初めての Jenkins と Declarative Pipeline
勉強のために手元の Ubuntu 16.04 に Jenkins を入れてみた。仕事でたまに使っているが自分で設定するのは初めてだ。最新版はかなり変わってて面白かった。特に Jenkinsfile というテキストファイルで設定を記述する所と、最初から build agent docker を使う事前提になっている(?) のが興味深い。また、オプションで Blue Ocean プラグイン というのを使うと CircleCI のようなイマドキな画面に早変わりする。人気のある OSS 製品が陳腐化するというのは珍しくないが、こうして進歩を止めない姿勢は素晴らしい。
apt でインストール
Download の説明に従う。自動更新が使える Jenkins Debian packages が楽そう。
Jenkins の公開鍵を apt-key で apt に保存する。
wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add -
Jenkins のエントリを sources.list.d に追加
sudo sh -c 'echo "deb https://pkg.jenkins.io/debian-stable binary/" > /etc/apt/sources.list.d/jenkins.list'
インストール
sudo apt-get update
sudo apt-get install -y jenkins
これで自動的に 8080 ポートで起動する。ポート番号を変えたい場合は /etc/init.d/jenkins の HTTP_PORT を変更する。
あと、どうやら docker を使っているらしいので docker グループに入れておく。
gpasswd -a jenkins docker
sudo systemctl restart jenkins
余談だが dpkg -L jenkins
でパッケージの中を見ると起動スクリプトが /etc/init.d/jenkins
にある。最近の自動起動は systemd に変わったはずなのでどういう仕組なのかなと思ったら、スクリプトの LSB ヘッダという物を使って systemd-sysv-generator で systemd service を自動生成しているらしい。
Post-installation setup wizard
初回起動時だけ色々設定が必要。インストールが完了してブラウザで 8080 ポートにアクセスすると、初期化画面が表示される。
- Unlock Jenkins: 指示に従って
/var/lib/jenkins/secrets/initialAdminPassword
にあるパスワードを打ち込む。 - Customizing Jenkins with plugins: インストールするプラグインを選べるらしいが、良くわからないので Install suggested plugins にした。
- Creating the first administrator user: ユーザ名とパスワードを入れる。OS のユーザ名とかとは関係なく、Jenkins 内だけのユーザ管理。
- あとで Manage Jenkins > Configure Global Security > でユーザ管理方法を選べる。
Pipeline の練習
- New Item を選ぶ。
- 適当にアイテムに名前を付ける。
- Multibranch Pipeline を選ぶ
- Add Source で適当にビルドしたいプロジェクトを選ぶ
- Github のプライベートレポジトリにアクセスしたい時、GitHub personal access tokens を使う。
- Personal access tokens のページで Generate new token を押す
- scope: "repo" を指定して生成
- Credentials: Add
- Kind: Username and password
- Username: github ユーザ名
- Password: 作った Github personal access token
- この内容はメニューの Credentials で編集出来る。
- Owner: github のユーザ名や Organization name を入れる。
- Repository: 今までの設定が正しいとレポジトリを選択出来る。
- Github のプライベートレポジトリにアクセスしたい時、GitHub personal access tokens を使う。
- Scan Repository Triggers
- Periodically if not otherwise run: チェックして、更新頻度を入れる。
- Jenkins がファイアウォール内にある時は webhook を受け取れないので、ポーリングして更新を知る。
- Save
これで何かが走るが特に何も起こらない。そこでレポジトリに Jenkinsfile という名前の以下のようなファイルを作ると Docker image の node 上で npm --version
を実行する。
pipeline {
agent { docker 'node:8.9.4' }
stages {
stage('build') {
steps {
sh 'npm --version'
}
}
}
}
この文法は Groovy という物らしいが、知らなくても使える。この Jenkinsfile を編集する事でビルド環境を作るが、多少引っかかる部分があったので以下に書く。
トラブルシューティング
- レポジトリが private な submodule を持っている場合。
- Jenkins の git 機能で submodule の振る舞いを変える事が出来る。
- Branch Sources > Behaviors > Advanced sub-modules behaviors
- Use credentials from default remote of parent repository
-
Error: EACCES: permission denied, mkdir '/.npm'
というエラーが出てしまう。- https://stackoverflow.com/questions/42743201/npm-install-fails-in-jenkins-pipeline-in-docker/42957034
- 環境変数に
HOME=/work
等を追加すれば良い。
- Github のビルド結果のリンクが
unconfigured-jenkins-location
になってしまう。- Jenkins > Configure System > Jenkins Location > Jenkins URL: を設定し直す
- デフォルトで正しい値になっていると思うが、特に設定を変えなくても Save ボタンを押すだけで直る。
- npm update で
fatal: unable to look up current user in the passwd file: no such user
-
また、HOME を適当に
.
とかにしていても出るので注意。git config --global user.name hoge # unable to look up のバグ回避 git config --global user.email hoge@example.com # unable to look up のバグ回避
- because the document's frame is sandboxed and the 'allow-scripts' permission is not set. が出る。
-
Manage Jenkins > Script Console で以下を入力 (セキュリティ上問題があるらしい)
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")
-
Docker について
上にサラッと書いたが、このサンプルは docker を使う事前提になっている。プロジェクトをビルドするためには対象ごとに色々な種類やバージョンの開発環境が必要があるのだが、混ざると危険だ。そこで、docker を使ってプロジェクト専用の環境を用意すると管理が楽だ。具体的には例のように agent { docker 'node:6.3' }
と記述すると node 6.3 が入った開発環境(Docker イメージ)が出来る。こうした開発環境を自分で作っても良いし、誰かが作った出来合いのやつを Docker Hub からもらってもよい。例えば 6.3 の代わりに node:8.9.4
のように指定すると別のバージョンの node でビルド出来る。
docker ビルド環境は手元の環境と異なるので Jenkinsfile を書く前にちゃんとビルド出来るか試したい。そこでこんな風に手動で docker でビルドしてみた。
$ git clone git@example.com:example/example.git
$ docker run -it --rm -u $(id -u) -e HOME=/work -w /work -v $PWD/example:/work node:8.9.4 bash
I have no name!@f748fa069894:~$ git config --global user.name hoge # unable to look up のバグ回避
I have no name!@f748fa069894:~$ git config --global user.email hoge@example.com # unable to look up のバグ回避
I have no name!@f748fa069894:~$ npm install
I have no name!@f748fa069894:~$ npm run build
docker run の引数は以下のとおり
-
-i
: STDIN を開く -
-t
: pseudo-TTY を割り当てる -
--rm
: 実行後コンテナ削除 -
-u
: 実行ユーザを指定 -
-e
: 環境変数を指定 -
-w
: 実行ディレクトリを指定 -
-v
: ホスト位置:コンテナ位置 でホストのディレクトリをコンテナから見えるようにする。 - イメージ名
- 実行コマンド
もうちょっと実用的な Jenkinsfile
実際に使っている Jenkinsfile を示します。Cordova というツールを使って Android アプリを作るための物です。たったこれだけですが、十分実用になります。
pipeline {
agent {
docker {
image 'beevelop/android-nodejs:v8.9.4'
args '-e HOME=.'
}
}
triggers {
cron('H 15 * * * ')
}
stages {
stage('build') {
steps {
sh 'npm --version'
sh 'npm install'
sh 'npm run build'
sh 'npx cordova build'
}
}
}
post {
always {
archiveArtifacts artifacts: 'platforms/android/build/outputs/apk/debug/*.apk', fingerprint: true
}
}
}
- トップレベルは pipeline と書かれたブロック。中に agent, stages, steps, post がある。
- agent: Pipeline が実行される環境を書く。
- ここではビルド環境 Docker image として適当に検索して出てきた android-nodejs というのを使っている。有り難い。Cordova が入ったイメージもあったが、Cordova は package.json でローカルにインストールした方が確実に動くので Cordova 本体は無い方が良かった。
- stages: ビルド手順全体
- stage: ビルド手順を分けるのに使う。
- steps: ビルドコマンド
- ここでは簡単に
npm install; npm run build; npx cordova build
を sh で実行している。 - https://jenkins.io/doc/pipeline/steps/ に実行可能な step が記述されている。
- ここでは簡単に
- steps: ビルドコマンド
- stage: ビルド手順を分けるのに使う。
- post: stage 終了後実行する step を書く。
- always: 無条件に実行。
- success: にすると stages が成功した時だけ実行。
- archiveArtifacts というのを使うとビルド結果を Jenkins からダウンロード出来るようになります。
- environment: 環境変数を設定。
- pipeline や stage の中に記述する。
- Jenkins 環境から credentials() でパスワードを渡せる。
- options: プラグイン全体の設定。
- pipeline や stage の中に記述する。
- buildDiscarder: 保存回数の指定
- timeout: タイムアウト設定
- parameters: パラメータの設定
- triggers: Pipeline を実行するタイミング
- webhook で実行するタイプの Pipeline では不要。
- この例では、webhook 以外に定期的にビルドしたいので追加した。
-
cron('H 15 * * * ')
のようにすると、毎日 15 時の適当な時間にビルドが走る。
- tools: ビルドで使うツールを指定すると自動インストールされる。
- maven, jdk, gradle を指定出来る。
- ツールは事前に Manage Jenkins > Global Tool Configuration で設定する必要がある。
- input: stage 内に記述すると、実行中に停止して何か入力出来るらしい。
- when: stage を実行するかどうか条件に応じて決められるらしい。
- pararell: stage を並行して実行出来る。
小技
- 常に最新の submodule でビルドする。この設定で submodule を commit しなくても最新になります。
- Configure > Branch Sources:
- Update tracking submodules to tip of branch: check
-
git submodule update --remote
が有効になる。
-
- Update tracking submodules to tip of branch: check
- Configure > Branch Sources:
Jenkinsfile の文法チェック
レポジトリにいちいちコミットしないと Jenkinsfile の文法チェックが出来ないというのでは面倒なので、Jenkins は API による文法チェックの機能がある。
JENKINS_URL=(Jenkins の URL)
JENKINS_CRUMB=`curl "$JENKINS_URL/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,\":\",//crumb)"`
curl -H $JENKINS_CRUMB -F "jenkinsfile=<Jenkinsfile" $JENKINS_URL/pipeline-model-converter/validate
参考
宿題
-
Multibranch Pipeline だと submodule の設定のような細かいチェックアウト情報は Jenkinsfile ではなく Web で設定する。Jenkinsfile で設定する方法もありそうな物だが分からなかった。理想はレポジトリと Jenkins のパスだけを Web で指定して残りは全部 Jenkinsfile に書きたい。参考になりそうな情報: