Jenkins でコンテナアプリの CI

プロジェクトでは Jenkins でビルド職人をやることが結構あります。Jenkins のジョブは Web UI でポチポチ設定するのが伝統でしたが、最近はビルドパイプラインを DSL で書けるようになって Infrastructure as Code 化が進んでいます*1

wiki.jenkins.io

ここ数年は Docker コンテナでアプリをリリースすることが増えており、コンテナイメージのビルド・破棄というステップがパイプラインに入ってきます。

以前作った Spring Boot の REST API のコンテナ化アプリを題材にパイプラインを構築してみました。

blog.kondoumh.com

github.com

このサンプルでは、docker-compose で Spring Boot の jar ファイルをコンテナに COPY して起動し Mongo DB のコンテナと連携させています。

パイプラインは以下のようなステップで流します。

  • ソースコードのチェックアウト (git clone)
  • パッケージング (maven package)
    • ビルド
    • 単体テスト
    • jar ファイル生成
  • コンテナビルドと起動 (docker-compose up)
  • 結合テストの実行
  • コンテナの停止と破棄 (docker-compose down)

アプリの単体テストはコンテナビルド前に実行し、結合テストはコンテナを起動してエンドポイントに対して E2E のテストを実行します。

pipeline {
    agent { node {label 'jnlp_agent'} }
    stages {
        stage('checkout') {
            // git clone
            steps {
                checkout([$class: 'GitSCM', branches: [[name: '*/master']],
     userRemoteConfigs: [[url: 'https://github.com/kondoumh/sb-sample-service.git']]])
            }
        }
        stage('package') {
            steps {
                // build, unit test, package
                sh './mvnw package'
            }
        }
        stage('build and run container') {
            steps {
                // build and run container
                sh 'docker-compose up -d'
                sleep 60
            }
        }
        stage('container integration test') {
            steps {
                // e2e test with curl
                echo 'conteiner integration test'
                sh 'curl -X POST "http://localhost:18888/api/user/" -H "accept: */*" -H "Content-Type: application/json" -d "{ \"id\": 1, \"name\": \"Mike\"}"'
                sh 'curl -X GET "http://localhost:18888/api/usr/1" -H "accept: */*"'
            }
        }
    }
    post {
        always {
            // shutdown container
            sh 'docker-compose down'
        }
    }
}

DSL なので Groovy 知らなくても大体読めると思います。agent {} 宣言で実行する Jenkins agent を指定します*2stages 内の stage が順次実行されます。post { always{} } に書かれた処理は stages の途中でエラーが発生しても必ず実行されます。stage は並列実行などもできます。

docker-compose でコンテナをビルド実行すると Spring アプリケーションの起動に時間がかかるため、sleep を入れてます。

結合テストは Java で書いてちゃんと assert すべきですが、簡単な動作確認を curl で書いて済ませました。

実行は Blue Ocean のモダンな画面でモニタリングできます*3

f:id:kondoumh:20190401211954p:plain

jenkins.io

Blue Ocean の画面でパイプラインを編集可能なプラグインもあるようです。

今回のように Docker コマンドを shell で叩いてもいいのですが Pipeline Plugin の DSL で書けるようにしてくれるプラグインもあります。

wiki.jenkins.io

イメージの取得ビルド、コンテナ内のプログラム実行、ジョブ終了後のコンテナ破棄をやってくれます。ただし、docker-compose には対応していないようです。残念。

Jenkins のエージェント自体もコンテナで動かすことができて、公式のイメージを利用できます。

https://hub.docker.com/r/jenkinsci/slave

Jenkins ではプラグインを使う必要がありますが、最近の CI ツールはネイティブにコンテナイメージを扱えるものが主流になっています。 Jenkins も Jenkins X というプロジェクトで、クラウドネイティブ化を目指しています。

jenkins-x.io

*1:Jenkinsfile という Groovy の DSL でコードとして管理できます。

*2:以前は slave と呼ばれてましたがポリコレ的に改名されました。

*3:Blue Ocean は必須ではありません。