Jenkins2をコード化しよう - その1:Ansibleでプロビジョニングする
今回は、CIサーバとして広く使われているJenkinsをAnsibleでコード化するための手順を紹介します。
背景:Jenkinsサーバがカオスだった
配属された現場で使用されていたJenkinsサーバが誰もメンテンナンスできていない状態でしたので、「完璧にコード化案件ですねこれは」と頑張ってみました。
困ったことに、複数存在するJenkinsサーバのそれぞれにインストールされているJenkins PluginやOSコマンドが異なっていましたし、 Jenkins自体もHomebrew経由でインストールしているものもあれば、直接Jarファイルをダウンロードしたものもあり、「これは再現性がないぞ」と思いました。 更に、Jenkinsたちはオンプレのサーバに立っていて、バックアップも取られていません。
これでは、サーバが死んだら開発が止まってしまいますよね。 このような状況はきっと他にもあるのだろうと思ったので、Tipsをまとめてみました。
Ansibleを使ってJenkinsコード化する
以降では、JenkinsをAnsible化するにあたってのポイントを aisible-playbook
の yml
形式で順に説明します。
環境情報
- Ansible
2.3.1
- プロビジョニング対象のOS
- AWS EC2の
AmazonLinux2
(CentOS7がベースになっています)
- AWS EC2の
- インストールするJenkins
2.164.2
Jenkinsのインストール
Jenkinsの起動に必要なJava8をインストールした後、Jenkinsのyumリポジトリを追加します。
インストールには公開鍵が必要なので、取得を行った後にJenkinsを yum
でインストールします。
1- name: yumリポジトリを有効にします
2 shell: amazon-linux-extras enable corretto8
3- name: OpenJDK 8(corretto) をインストールします
4 yum:
5 name: java-1.8.0-amazon-corretto-devel
6 state: present
7- name: jenkinsのyumリポジトリを追加します
8 get_url:
9 url: http://pkg.jenkins-ci.org/redhat-stable/jenkins.repo
10 dest: /etc/yum.repos.d/jenkins.repo
11- name: 公開鍵をインストールします
12 rpm_key:
13 key: http://pkg.jenkins-ci.org/redhat-stable/jenkins-ci.org.key
14- name: jenkinsをインストールします
15 yum:
16 name: "jenkins-2.164.2-1.1"
17 state: present
Jenkinsの起動設定の変更
Jenkinsの起動引数を変更したい場合には、いくつか方法はありますが、今回は /etc/sysconfig/jenkins
ファイルを差し替えます。
ここでは ${JENKINS_HOME}
の場所や、待受ポート、SSL証明書へのパス、Jenkinsの起動引数の追加等が行えます。
1- name: jenkinsの起動引数を変更します
2 copy:
3 src: "../files/etc/sysconfig/jenkins"
4 dest: /etc/sysconfig/jenkins
5 owner: root
6 group: root
7 mode: 0600
セッションタイムアウトを伸ばしたければ、 JENKINS_ARGS="--sessionTimeout=120"
といった要領です。
Jenkinsを起動
Jenkinsを起動します。AmazonLinux2(CentOS7)から systemd
になっているので、デーモンに登録しておきます。
1- name: jenkinsを起動します
2 systemd:
3 name: jenkins
4 state: started
5 enabled: yes
Jenkinsが起動すると初期化処理に時間がかかるので、ログイン画面が表示されるまで待ちましょう。
1- name: Jenkinsが起動するまでwaitします
2 uri:
3 url: http://localhost:8080/login
4 status_code: 200
5 timeout: 10
6 register: jenkins_service_status
7 retries: 15
8 delay: 20
9 until: >
10 'status' in jenkins_service_status and
11 jenkins_service_status['status'] == 200
Jenkins ユーザに sudo権限を付与
Jenkinsをインストールすると、Jenkinsユーザが誕生します。
Jenkinsユーザに何らかの理由で sudo
させたいのであれば、以下のように sudoers.d
配下のファイルに追記しておきます。
1- name: jenkinsユーザにsudo権限を付与します
2 lineinfile:
3 dest: "/etc/sudoers.d/users"
4 owner: root
5 group: root
6 mode: 0440
7 state: present
8 create: yes
9 line: "jenkins ALL=(ALL) NOPASSWD:ALL"
10 validate: 'visudo -cf %s'
セットアップウィザードを無効にする
Jenkinsを初回起動した時にセットアップウィザードが表示されます。
これを無効化するために、 ${JENKINS_HOME}/jenkins.install.InstallUtil.lastExecVersion
ファイルを作成してしまいましょう。
ファイルの中身にはJenkinsのバージョン番号(今回は 2.164.2
)が記載されていればOKです。
1- name: セットアップウィザードを無効化します
2 copy:
3 content: "2.164.2"
4 dest: /var/lib/jenkins/jenkins.install.InstallUtil.lastExecVersion
5 owner: jenkins
6 group: jenkins
7 mode: 0644
アップグレードウィザードを無効にする
同様にしてアップグレードウィザードも無効にします。アップグレードウィザードは ${JENKINS_HOME}/jenkins.install.UpgradeWizard.state
です。
1- name: アップグレードウィザードを無効化します
2 copy:
3 content: "2.164.2"
4 dest: /var/lib/jenkins/jenkins.install.UpgradeWizard.state
5 owner: jenkins
6 group: jenkins
7 mode: 0644
Jenkinsのセキュリティ設定を一時的にオフにする
以降の処理でJenkins2のセキュリティ設定がプロビジョニングの邪魔をすることがあるので、一時点にオフにします。
${JENKINS_HOME}/config.xml
を変更します。
1- name: セキュリティ無効化します
2 copy:
3 src: "../files/var/lib/jenkins/config.xml.unsecure"
4 dest: /var/lib/jenkins/config.xml
5 owner: jenkins
6 group: jenkins
7 mode: 0644
ここは xml
を置き換える という少し泥臭い方法を取ります。
1<?xml version='1.1' encoding='UTF-8'?>
2<hudson>
3 <disabledAdministrativeMonitors/>
4 <version>2.164.2</version>
5 <installStateName>RUNNING</installStateName>
6 <numExecutors>60</numExecutors>
7 <mode>NORMAL</mode>
8 <!-- CLI実行のためにセキュリティ設定をOFFにする ココから -->
9 <useSecurity>false</useSecurity>
10 <authorizationStrategy class="hudson.security.AuthorizationStrategy$Unsecured"/>
11 <securityRealm class="hudson.security.SecurityRealm$None"/>
12 <!-- ココまで -->
13 <disableRememberMe>false</disableRememberMe>
14 <projectNamingStrategy class="jenkins.model.ProjectNamingStrategy$DefaultProjectNamingStrategy"/>
15 <workspaceDir>${JENKINS_HOME}/workspace/${ITEM_FULL_NAME}</workspaceDir>
16 <buildsDir>${ITEM_ROOTDIR}/builds</buildsDir>
17 <jdks/>
18 <viewsTabBar class="hudson.views.DefaultViewsTabBar"/>
19 <myViewsTabBar class="hudson.views.DefaultMyViewsTabBar"/>
20 <clouds/>
21 <scmCheckoutRetryCount>0</scmCheckoutRetryCount>
22 <views>
23 <hudson.model.AllView>
24 <owner class="hudson" reference="../../.."/>
25 <name>all</name>
26 <filterExecutors>false</filterExecutors>
27 <filterQueue>false</filterQueue>
28 <properties class="hudson.model.View$PropertyList"/>
29 </hudson.model.AllView>
30 </views>
31 <primaryView>all</primaryView>
32 <slaveAgentPort>21000</slaveAgentPort>
33 <label></label>
34 <crumbIssuer class="hudson.security.csrf.DefaultCrumbIssuer">
35 <excludeClientIPFromCrumb>false</excludeClientIPFromCrumb>
36 </crumbIssuer>
37 <nodeProperties/>
38 <globalNodeProperties/>
セキュリティ設定を変更したら、再起動で設定ファイルの再読込をします。
1- name: jenkinsを再起動します
2 systemd:
3 name: jenkins
4 state: restarted
5 enabled: yes
Jenkins Pluginをインストールする
Jenkinsのプラグインをインストールするには Jenkins CLIを使うのがオススメです。
JenkinsプラグインのIDはAnsibleの vars/
配下のファイルに書き出すなどしてループでインストールしてしまうのが良いでしょう。
セットアップウィザードをスキップしてしまっているので、セットアップウィザードでデフォルトでインストールするプラグインは このタイミングでインストールすると良いでしょう。
プラグインのインストール終了後のJenkins再起動も忘れずに行います。
1- name: jenkins-cliをインストールします
2 shell: wget http://localhost:8080/jnlpJars/jenkins-cli.jar -P /usr/lib/jenkins/
3- name: jenkins pluginをjenkins-cli経由でインストールします
4 shell: java -jar /usr/lib/jenkins/jenkins-cli.jar -s http://localhost:8080 install-plugin {{ item.plugin_name }}
5 with_items: "{{ jenkins_plugins }}"
6- name: jenkinsを再起動します
7 systemd:
8 name: jenkins
9 state: restarted
10 enabled: yes
Jenkinsの起動スクリプトを配置する
Jenkinsは起動したタイミングで、${JENKINS_HOME}/init.groovy.d/
配下にあるGroovyスクリプトを実行させることができます。
Groovyスクリプトを使うことで、 ${JENKINS_HOME}
配下にxmlをひたすらコピーするソリューションを防ぐことができます。
${JENKINS_HOME}/init.groovy.d
ディレクトリはデフォルトで存在しないので、ディレクトリを作成しておきます。
1- name: jenkins起動時のHOOK Groovy Scriptディレクトリを作成します
2 file:
3 dest: "/var/lib/jenkins/init.groovy.d/"
4 state: directory
5 owner: jenkins
6 group: jenkins
7 mode: 0700
Groovyスクリプトの書き方については、「Jenkins2をコード化しよう - その2:Jenkinsの起動時にプログラム(Groovy Hook Script)を動かす」 を参考にしてください。
xmlファイルを配置する
JenkinsやJenkinsプラグインの設定は xml
ファイルで管理されています。
必要に応じて ${JENKINS_HOME}
配下に置きましょう。
1- name: Slackの設定をコピーします
2 copy:
3 src: "../files/var/lib/jenkins/jenkins.plugins.slack.SlackNotifier.xml"
4 dest: /var/lib/jenkins/jenkins.plugins.slack.SlackNotifier.xml
5 force: yes
6 owner: jenkins
7 group: jenkins
8 mode: 0755
Jenkinsのセキュリティ設定をオンに戻す
プラグインをインストールするためにオフにしたセキュリティ設定を元にもどします。
1- name: セキュリティ有効化します
2 copy:
3 src: "../files/var/lib/jenkins/config.xml.secure"
4 dest: /var/lib/jenkins/config.xml
5 owner: jenkins
6 group: jenkins
7 mode: 0644
Jenkinsインストール時の ${JENKINS_HOME}/config.xml
の内容に戻してあげましょう。
サンプルは以下のような感じです。
1<?xml version='1.1' encoding='UTF-8'?>
2<hudson>
3 <disabledAdministrativeMonitors/>
4 <version>2.164.2</version>
5 <installStateName>RUNNING</installStateName>
6 <numExecutors>60</numExecutors>
7 <mode>NORMAL</mode>
8 <useSecurity>false</useSecurity>
9 <authorizationStrategy class="hudson.security.FullControlOnceLoggedInAuthorizationStrategy">
10 <denyAnonymousReadAccess>true</denyAnonymousReadAccess>
11 </authorizationStrategy>
12 <securityRealm class="hudson.security.HudsonPrivateSecurityRealm">
13 <disableSignup>true</disableSignup>
14 <enableCaptcha>false</enableCaptcha>
15 </securityRealm>
16 <disableRememberMe>false</disableRememberMe>
17 <projectNamingStrategy class="jenkins.model.ProjectNamingStrategy$DefaultProjectNamingStrategy"/>
18 <workspaceDir>${JENKINS_HOME}/workspace/${ITEM_FULL_NAME}</workspaceDir>
19 <buildsDir>${ITEM_ROOTDIR}/builds</buildsDir>
20 <jdks/>
21 <viewsTabBar class="hudson.views.DefaultViewsTabBar"/>
22 <myViewsTabBar class="hudson.views.DefaultMyViewsTabBar"/>
23 <clouds/>
24 <scmCheckoutRetryCount>0</scmCheckoutRetryCount>
25 <views>
26 <hudson.model.AllView>
27 <owner class="hudson" reference="../../.."/>
28 <name>all</name>
29 <filterExecutors>false</filterExecutors>
30 <filterQueue>false</filterQueue>
31 <properties class="hudson.model.View$PropertyList"/>
32 </hudson.model.AllView>
33 </views>
34 <primaryView>all</primaryView>
35 <slaveAgentPort>21000</slaveAgentPort>
36 <label></label>
37 <crumbIssuer class="hudson.security.csrf.DefaultCrumbIssuer">
38 <excludeClientIPFromCrumb>false</excludeClientIPFromCrumb>
39 </crumbIssuer>
40 <nodeProperties/>
41 <globalNodeProperties/>
Jenkinsの再起動
最後にJenkinsを再起動して終了です。
1- name: jenkinsを再起動します
2 systemd:
3 name: jenkins
4 state: restarted
5 enabled: yes
注意点
プロビジョニングに時間がかかる
Jenkinsのマシンイメージのコード化にあたってプロビジョニングの所要時間を計測したところ、
インスタンスタイプが c5.xlarge
で15分ほどかかりました。特に、プラグインインストールの所で時間がかかっているようでした。
xml編集という泥臭い作業は残る
コード化するにあたって、xmlを編集する という苦行は避けたかったのですが、全回避はできなかったです。 Jenkinsの設定をymlでかけるようにするための Configuration as Code Plugin というものがありますが、各プラグインがConfiguration as Code Plugin に対応する必要があるため、対応していないプラグインを使いたい場合には厄介です。