Dockerを用いたJenkinsの運用をしてみた話
河合 航平
この記事は RECRUIT MARKETING PARTNERS Advent Calendar 2015 の投稿記事です。
こんにちは、2015年新卒インフラエンジニアの河合です。アドベントカレンダー2015の3日目を担当します。現在の担当プロダクトは英語/英単語サプリです。そういえば10月末に英語サプリのiOS版がリリースされました。
英語サプリリリースに伴い、インフラ業務としては
- Terraformの運用の話
- Amazon EC2 Container Serviceで構築した話
などと色々なネタがあるのですが、本日はDockerを用いたJenkinsの運用をしてみた話をします。
Jenkinsと数ヶ月ほど戯れてみて…
Jenkinsと戯れてみて思ったのは以下のとおりです。
- 拡張性の代償として保守運用コストを要求する
- プラグインが豊富なので、設定が容易に済むことがある
- 各ジョブの設定のために手作業でやることが多い
- 手作業で行われたJenkins特有のジョブの設定方法を知らないと属人化されがち
Jenkins辛い運用パターン
担当プロダクトにおけるJenkinsの主な利用シーンは
- CI
- デプロイスクリプトジョブ
- cron batch
などがあるのですが、デプロイに限って言えば以下が該当します。
- Web deploy => gulpコマンドが必須
- ECS Container deploy => 独自コマンドはGoで動いている
とはいえ、初期状態だとそれぞれを動かすためのインストール作業が終わっていないために、シェルの実行の部分でgulpとかgo runとか書いてしまうとcommand not found
といったエラーが出てしまいます。そのため、以下のような作業が必要になります。
担当プロダクトはnode.js
, Golang
, java
などなど様々な要素が組み合わさっていることで成り立っているプロダクトです。やっぱりインストール作業は面倒なものですが、誰がメンテするんだよ状態(お前だよ)となりかねません。
問題点を整理すると…
- Jenkinsのホストに入ってインストールする作業が面倒1)Chef等で構築されている場合は除く
- インストールしたあとのメンテ作業が面倒
の2つがあると思います。
英語・英単語サプリファミリーチームにおけるJenkins運用ルール
JenkinsインスタンスにはJenkins以外何もインストールせず、ジョブの設定はDockerだけで完結するようにする
上記を原則とする運用ルールで進めました。
すると、〈1つのJenkins-Job = 1つのDocker-Imageで完結〉するために面倒なインストール作業をすることなく、全てdocker runだけで済むということになります。
具体例
こうすると先ほどの具体例として挙げた『GulpタスクをJenkins上で動かすためにnpm周りの環境をセッティングするぞ!』は、以下のようになるかと思います。
Before
- sshでJenkinsのインスタンスに入る
- EPELリポジトリの追加
- sudo yum install npm(バージョン把握)
- npm install -g gulp typescript bower dtsm
- Jenkinsのジョブの設定でシェルを書く
といったものになると思うのですが、これは次のようにすることが出来ます。
After
- Dockerfileを記載(以下にコード記載)
- docker push
- Jenkinsのジョブの設定で
docker pull
とdocker run
を書く
という流れになります。
FROM node:0.12.5
USER root
RUN npm install -g gulp typescript bower dtsm
ADD upload.sh /scripts/upload
RUN chmod +x /scripts/upload #アップロード作業
必要な項目がDockefileとしてコードで明文化されていることはすごくいいですね。
Jenkins + Docker Tips
Jenkins + Dockerで培ったちょっとしたTipsを以下に紹介します。
git clone後はvオプションでマウントするようにする
ワークスペース上にgit cloneしてきた後にそのコードをテスト/デプロイに使いたいときは普通にあると思うのですが、そこはDockerのvオプションを使って、ワークスペース上のコードをDockerコンテナ内にマウントできるようにします。
環境変数を上手く使う
Jenkins上で設定した環境変数を上手く使うためには、eオプションで環境変数をDockerコンテナに渡してあげることで柔軟に対応できるようにしたりします。
バージョン管理はDockerのimageタグで
我々はTerraformというHashiCorp社が作成したインフラ構成をテンプレートファイルに記述することでコードで管理できるオーケストレーションツールを利用しています。
git pushされたブランチにおけるインフラ構成差分チェックのためにterraform planというコマンドを実行することでインフラ構成の差分をチェックをするのですが、この作業もJenkins上で行っています。
Terraformのバージョンは良くも悪くもアップデートが激しいため、「Terraform plan用ジョブ」はどのTerraformバージョンなのかを明示的にしています。
#!/bin/bash
echo "branch is $branch"
# タグでどのバージョンか指定
terraform_registry="registry.dev.eitangosapuri.jp/terraform:0_6_4" #タグで指定
# image取得
docker pull $terraform_registry #取得
# 環境変数セッティング
ENVS="ACCESS_KEY SECRET_KEY REGION"
for env in $ENVS
do
ENV_OPTIONS="${ENV_OPTIONS} -e ${env}=`printenv $env`"
done
# docker run
docker run $ENV_OPTIONS -v /etc/jenkins/jenkins_home/jobs/$JOB_NAME/workspace/:/$JOB_NAME -w /$JOB_NAME $terraform_registry \
sh -c "cd dev && rake plan"
ちょっと具体例が悪かったかもしれないのですが、rubyのバージョンなどジョブの設定でどのバージョンを動かしているのかを、dockerのimageタグを明示的に書くようにしています。
結論
この運用方法にしてからJenkinsのホストに入って何かをインストールするという作業が一切なくなりました。1 Jenkinsビルドジョブ = 1Docker-Imageなので、メンテはJenkins上にあるDockerに関することぐらいで済みます。
シェルの実行のところでdocker runで済むものさえ作ってくれるのであれば、インフラエンジニア以外もみんなガシガシJenkinsいじってほしいです(願望)。
Jenkins属人化ダメゼッタイ。
脚注
↑1 | Chef等で構築されている場合は除く |
---|