久しぶりにJenkinsでCI環境作ったところ、Jenkins2になったことで以前と比べてだいぶ導入方法が変わってたので、忘れないためのメモです。
Jenkinsとは何か? なぜJenkins ?
Jenkins は自動化を導入するソフトウェアで、CIやCDを実現するためのツールとして使われます。同じカテゴリのサービスに、Travis CIやCircle CIがあります。TravisがGitHubのみ、Circle CIがGitHubやBitbucket Cloudのみを対象としているのに対して、Jenkinsは対象の制限が無いのが強みです(オンプレでもOK)。
個人的には、コードを GitHub で管理してるなら、Jenkinsの学習コストや運用コストを考えると、Circle CIやTravis CIなどの CIサービスを使ったほうがいいと思います。
オンプレでBitBucketサーバを立てたりして、Circle CIが使えないときは Jenkins を使わざるを得ません。でも昔よりは運用コストが低くなってるのかなと思います。
この記事で達成すること
- dockerを使ってJenkinsをインストール
- nginx を使って Port 80 -> 8080 にリバースプロキシーする
- Node.js のプロジェクトのPipelineを作成する。2分ごとにポーリングして監視するように設定
前提
- OS は Linux or Mac
- Jenkins からも dockerコンテナを起動して、dockerコンテナの中で処理させる
dockerをインストールする
Jenkins公式が配布しているdockerイメージを使ってJenkinsを起動するのが、2018年現在だと一番簡単なセットアップ方法だと思いますので、この方法でセットアップします。
Docker store https://store.docker.com/search?type=edition&offering=community から Docker Community Edition をインストールしてください
Jenkinsの導入/起動
まず Jenkins のデータを格納するディレクトリを作成します。
mkdir $HOME/jenkins_home
jenkinsci/blueocean という公式が配布しているdockerイメージを使います。
以下のコマンドを実行すると、まだdockerイメージをダウンロードしてないときはダウンロードしてから、dockerコンテナを起動します。
sudo docker run \
-u root \
--rm \
-d \
-p 8080:8080 \
-v $HOME/jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
jenkinsci/blueocean
-
-u root
: Jenkinsからdockerコンテナを起動するため、rootユーザーで実行します。 -
—-rm
: docker コンテナ終了時に、自動的にコンテナを削除します。Jenkinsの状態はすべて jenkins_home ディレクトリに格納されるため、コンテナ終了時に削除しても問題ないです。 -
-d
: バックグラウンド (detachモード)で実行します -
-p 8080:8080
: ホストマシンのポートとコンテナのポートをマッピングします。先の数字がホストマシンのポートで、後ろの数字がコンテナのポートです。 -
-v $HOME/jenkins_home:/var/jenkins_home
: ホストマシンの $HOME/jenkins_home ディレクトリをコンテナの /var/jenkins_home ディレクトリにマップします。ここにJenkinsのデータが格納されます。 -
-v /var/run/docker.sock:/var/run/docker.sock
: docker.sock をコンテナにマップすることで、コンテナの中から新たにdockerコンテナを起動できるようになります。
nginxでリバースプロキシする
(手元の環境で動かすだけならこの手順は不要です。)
80番ポートで受けたリクエストを8080番ポートに転送します。
以下のような転送設定でリクエストを転送できます。
upstream jenkins {
server 127.0.0.1:8080 fail_timeout=0;
}
server {
listen 80;
server_name my-ci.com; # 実際のドメインを指定してください。手元で試すだけなら localhost で良いです。
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://jenkins;
}
}
上記は80番ポートでアクセスを受け付ける時の設定例です。SSLを使用したときの設定例はこちらにあります。
https://wiki.jenkins.io/display/JENKINS/Jenkins+behind+an+NGinX+reverse+proxy
Post-installation setup wizard
JenkinsをセットアップしたURLにブラウザでアクセスします。
(localhostにセットアップしているとして、 リバースプロキシを設定したならhttp://localhost/ 、 設定してないなら http://localhost:8080/ )
準備ができるとAdministrator passwordを求められます。
画面に書かれてるように、initialAdminPassword というファイルにパスワードが記載されてます。
cat $HOME/jenkins_home/secrets/initialAdminPassword
コマンドを実行することでパスワードを取得できます。
これをブラウザのAdministrator password入力欄に貼り付けて、Continueをクリックします。
次にプラグインをインストールする画面が表示されます。
こだわりがなければ 「Install suggested plugins」 を選んで、おすすめプラグインをインストールします。
プラグインがダウンロード/インストールされます。これには少し時間がかかります。
次に
- 初期ユーザの情報を登録します。ユーザ名、パスワードなどのフィールドを入力して保存します。
- Jenkins URLの設定をします。実際に利用するURLを設定してください。手元で試すだけなら
http://localhost:8080/
などで良いです。
これでJenkinsのインストールは完了です。
Node.js のプロジェクトのPipelineを作成する
現在のJenkinsは、Pipelineという仕組みを使って処理内容を定義します。
Pipeline とは continuous delivery (CD) pipeline のJenkinsにおける表現です。CD pipelineは継続的デリバリーを実現するためにいくつかの処理を行いますが、Pipelineはそれらの処理を可視化したり、コードで定義する機能を提供します。
Pipelineの定義はJenkinsfileという設定ファイルで行います。JenkinsfileをSCMのリポジトリに含めておくことで、Jenkinsはリポジトリに含まれるJenkinsfileを見つけてPipelineを作成します。
ですので、Jenkinsにどういう処理をさせるかはJenkinsfileを書いておけば良く、画面から処理内容を設定する必要はなくなりました。JenkinsもTravis CIやCircle CIと同じようにファイルで処理を設定できるようになったということですね。
Jenkinsfileを含んだ、Node.jsのサンプルプロジェクトを用意しました。
https://github.com/kmdsbng/jenkinsfile_sample
このプロジェクトを監視するPipelineを作っていきます。
Open Blue Oceanリンクを選びます。
Create a new Pipeline ボタンを選びます。
Gitを選びます。(たぶんGitHubを選んでもいける。その時はユーザ、パスワードを入力しないといけなかったはず。)
gitリポジトリのURLを入力すると、SSH公開鍵が表示されます。これをGitHubに登録します。
これで、リポジトリに含まれたJenkinsfileに従って監視が開始されます。
Jenkinsfileの内容を見てみましょう。
pipeline {
agent {
docker {
image 'node:8.12-alpine'
}
}
triggers { pollSCM('H/2 * * * *') }
stages {
stage('Npm Build') {
steps {
sh 'npm install && npm run build'
}
}
}
post {
success {
echo 'I succeeeded!'
}
unstable {
echo 'I am unstable :/'
}
failure {
echo 'I failed :('
}
}
}
このJenkinsfileが設定していること。
- 2分毎にリポジトリの変更を監視
- node:8.12-alpine イメージを使って dockerを起動しそこで処理を行う。
- npmでビルドする
ファイルの記述を見れば、やってることがだいたいわかりますね。
次に、わざとビルドエラーを起こしてみましょう。コンパイルエラーが起こるようにプログラムを修正してからpushしてみます。
ビルド時にエラーが検知され、エラー内容のログを見ることができます。
プログラムを修正して push しなおすと、エラーがなくなったことを検知しました。
Jenkinsの設定の不備でビルドが失敗したときなどは、Branches画面から再度CIを実行することができます。
Jenkinsの終了
sudo docker ps
コマンドで、Jenkinsの Container ID を確認して、
sudo docker stop <Container ID>
コマンドを実行します。
最新のJenkinsに更新
最新のJenkinsに更新するには、sudo docker pull jenkinsci/blueocean
して、Jenkinsを再起動すればOK。
まとめ
Jenkinsfile を使って設定することで、これまでの Jenkins のように画面から設定するよりは、設定の手間が下がったと感じます。ただ、Jenkinsfile もそんなに明確に書けるというわけではなく、情報が少ないし、概念もわりと複雑な感じで、別の学習コストはかかるなあという印象です。
Jenkinsの運用方針として、あまりJenkinsのプラグインを使用せず、できるだけ Jenkinsfile で設定できることで目的を達成したほうが良いかなと考えてます。
リポジトリさえ指定すればCIの設定が完了する、という構成にしておいたほうが、Jenkinsサーバを立てるときの運用コストが減りそうなので。
参考資料
Jenkinsfileの書き方については、Jenkins自体に有益なドキュメントが格納されています。
Global Variable Reference
JENKINS_URL/pipeline-syntax/globals
ブランチ名などの Jenkinsfileで利用できるグローバル変数の説明です。
Directive Generator
JENKINS_URL/directive-generator/
Jenkinsfileのディレクティブ文字列のジェネレータです。
Steps Reference
JENKINS_URL/pipeline-syntax/html
Jenkinsfileの書式の資料です。
Installing Jenkins
Jenkinsのインストール方法を説明しています。この記事で紹介した docker を使ったインストール方法に加えて、パッケージをダウンロードしてインストールする手順なども説明しています。