Jenkins
docker
JenkinsPipeline
0

初めての Jenkins と Declarative Pipeline

初めての 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: 今までの設定が正しいとレポジトリを選択出来る。
  • 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' というエラーが出てしまう。
  • 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

    • git のバージョンが古いとこのエラーが出る らしい。
    • また、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: ビルドコマンド
  • 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 が有効になる。

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 に書きたい。参考になりそうな情報:

  • Pipeline: Multibranch

  • Pipeline: SCM Step

  • Snippet Generator