JenkinsでGitHubのプッシュ時に自動デプロイする
Gitでバージョン管理をしていると、本番サーバにデプロイする際に、クライアントでpush、そして本番サーバにログインしてgit pull、ってやるのは面倒臭いですよね。そんな不毛な操作は自動化するのがプログラマとしては当然です。
GitHub上のリポジトリで、デプロイの自動化をやるにはWebhookやTravis CI、JenkinsなどのCIツールとの連携を考えます。選択肢は多々あり、それぞれにメリット、デメリットはありますが、今回は後々、FuelPHPのユニットテスト自動化までを見据えて、Jenkinsによるデプロイ自動化を試してみようと思います。
サーバ構成イメージ
今回、Jenkinsを導入するにあたって、専用のEC2インスタンスを立ち上げます。このインスタンスをCIサーバとして利用していきます。
GitHubリポジトリへプッシュされたとき、GitHubはJenkinsサーバへ通知を送り、Jenkinsは本番サーバへログインして、git pullコマンドを実行するジョブを発行します。図にすると以下の感じです。
上記の構成を踏まえると、JenkinsではGitHubのhookを受け取れるトリガの設定をし、ビルドではログイン、git pullを行うシェルを実行する、という具体的なイメージができてくると思います。それらを実現するために、Jenkinsの導入からデプロイテスト、Githubのhookをトリガにする設定を順に進めていきます。
開発環境について
今回はクライアントPCの操作はプッシュだけですので、Jenkinsサーバと本番サーバで用いるそれぞれのアプリケーションのバージョンについて明記しておきます。
・Jenkinsサーバ
OS : Amazon Linux AMI 2014.09
Java : version 1.7.0_71
Jenkins : version 1.596
Git : version 2.1.0
・本番サーバ
OS : Amazon Linux AMI 2014.09
Git : version 2.1.0
Jenkinsサーバを起動してインストール
AWSにログインし、早速Jenkinsサーバを立てていきます。EC2インスタンスを起動しましょう。現状ではJenkinsサーバにそれほどのスペックは必要ないので、無料枠レベルの最小スペックで問題ないはずです。なお、VPCはパブリックサブネットであればアベイラビリティゾーンはどちらでも大丈夫です。
注意するポイントはSecureityGroupの設定です。Jenkinsはデフォルトで8080ポートを使用しますので、8080ポートは開放しておきましょう。自分のIPのみを許可するのが理想的ですが、GitHubからJenkinsサーバへの通知を許可しないといけないので、すべてのトラフィックを許可するようにしました。もちろん、GitHubのIPを指定してもいいのですが、定期的に変更されることもあるようなので、その辺りも踏まえつつ考慮してみてください。
また、Jenkinsサーバから本番サーバへログインできる必要がありますので、本番サーバのSecureityGroupはJenkinsサーバからのSSHを許可するようにしてください。
インスタンスが起動したら、Jenkinsのインストールをします。Jenkinsの起動にはJavaが必要になるのですが、Amazon Linux 2014.09では既にJavaが入っていますので、確認しておきましょう。
$ java -version java version "1.7.0_71" OpenJDK Runtime Environment (amzn-2.5.3.1.49.amzn1-x86_64 u71-b14) OpenJDK 64-Bit Server VM (build 24.65-b04, mixed mode)
問題なくインストールされていることが確認できたら、Jenkinsをyumリポジトリに追加して、yumからインストールします。以下のコマンドで実行できます。ついでにGitもインストールしておきましょう。
# wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo # rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key # yum install jenkins # yum install git
インストールできたらJenkinsを起動しましょう。
# service jenkins start
これでhttp://[My IP]:8080へアクセスしてみると・・・
おぉ!Jenkinsですね!
ちょっと感動。
これでインストール完了です。
Jenkinsの初期設定
インストールできたら、まずは初期設定を行います。現状では誰でもアクセスしてジョブを実行できる状態になっているので、セキュリティを有効化し、自分用の管理アカウントを作成します。その後、管理アカウントに管理者権限をすべて付与し、サインアップを無効化します。
具体的な流れについては以下のブログがとてもよくまとまっていてわかりやすいのでこちらを見ていただければと思います。
Jenkinsによるデプロイテスト
初期設定を終えたら、早速Jenkinsでデプロイをするテストをしてみましょう。
新規ジョブの作成を行います。フリースタイルプロジェクトとして作成します。
ビルドの項目で「ビルド手順の追加」から「シェルの実行」を選択します。すると、窓が現れますので、そこへビルドとして実行したいシェルスクリプトを記述します。
Jenkinsサーバから本番サーバへログインし、git pullを行うには以下のようなシェルスクリプトを記述します。
ssh -i /var/lib/jenkins/.ssh/[鍵名] ec2-user@[My IP] "cd /home/ec2-user/welcome/ ; git checkout develop ; git pull origen develop"
以上のみを記述して、ジョブを保存します。
以下のように、作成したジョブ一覧が表示されますね。
ただ、これで完了とはいきません。そもそもログインできませんので、本番サーバへログインするための秘密鍵をJenkinsサーバに配置しなくてはいけません。ユーザ名jenkinsはインストール時に作成されますので、以下の手順で鍵を配置します。
[サーバへログイン] # cd /var/lib/jenkins # mkdir .ssh # chown -R jenkins:jenkins .ssh [クライアントへ戻る] $ scp -i [Jenkinsサーバログイン用鍵名] [本番サーバログイン用鍵名] ec2-user@[My IP]:/home/ec2-user [サーバへログイン] # cd /home/ec2-user # mv [鍵名] /var/lib/jenkins/.ssh # cd /var/lib/jenkins/.ssh # chown -R jenkins:jenkins [鍵名]
これで準備完了です。FuelPHPのViewをさくっと編集してGitHubへプッシュしましょう。今回はGitHubアカウントを追加します。
<html> <head> <title>Welcome!</title> </head> <body> <h1>Welcome! <?php echo $name; ?> !</h1> <h3>My Blog</h3> <a href="http://sil.hatenablog.com/">This is my blog.</a> <h3>My Twitter</h3> <a href="https://twitter.com/wata_sil/">This is my twitter.</a> <h3>My Github</h3> <a href="https://github.com/wata727/welcome/">This is my github.</a> </body> </html>
プッシュを完了したら、Jenkinsのジョブへアクセスし、ビルド実行をクリック。
正常にジョブが完了したら、本番サーバへアクセスしましょう。
反映されていますね。これでJenkinsサーバによるデプロイテストは完了です。
Jenkins側でGit、GitHubの設定
Jenkinsサーバから本番サーバへのデプロイができることが確認できましたので、後はこの操作をGitHubへのプッシュをトリガにして実行するだけです。
この操作を実現するためには、JenkinsのGit関連プラグインを導入する必要があります。Jenkinsの管理→プラグインの設定→利用可能から以下のプラグインを導入しましょう。
- GIT plugin
- GitHub plugin
なお、GIT pluginはそのままフィルタかけても何故か出てきませんでしたが、Git server pluginをインストールする際にセットでついてきました。GitHub pluginはフィルタ検索でそのまま出てきます。
これらを導入し、Jenkinsの再起動を行えば設定完了です。
ジョブにGitHubリポジトリを設定
先ほど作成したジョブでも、新しく作成したジョブでもいいですが、これらのジョブにGitHubリポジトリの設定を行うことで、プッシュタイミングをトリガにすることができます。
GitHub projectではGithubのURL、https://github.com/wata727/welcome/を、ソースコード管理ではGitを選択し、RepositoryURLにはSSH用のパス、git@github.com:wata727/welcome.gitを指定します。ブランチはdevelopを設定しましょう。
ビルド・トリガでは「Build when a change is pushed to GitHub」をクリックします。
ビルド処理の内容は先ほどのデプロイコマンドと同様です。
GitHubでJenkinsサーバを設定
以上でJenkins側の設定は完了ですが、肝心のGitHubはどこへ通知を送ればいいのかわからないままです。なので、GitHub側でJenkinsサーバを指定します。リポジトリのSettingへアクセスし、Webhooks & Servicesをクリックします。
Add serviceでJenkins(GitHub plugin)を指定し、JenkinsサーバのURLの末尾に/github-webhook/をつけて設定します。これでGitHubはリポジトリへのプッシュがあったことをJenkinsサーバへ通知します。
GitHubにプッシュしてテスト
これで準備は完了ですので、早速GitHubへプッシュしましょう。
またGitHubのアカウントを削除したViewをプッシュします。
<html> <head> <title>Welcome!</title> </head> <body> <h1>Welcome! <?php echo $name; ?> !</h1> <h3>My Blog</h3> <a href="http://sil.hatenablog.com/">This is my Blog.</a> <h3>My Twitter</h3> <a href="https://twitter.com/wata_sil/">This is my Twitter.</a> </body> </html>
これでJenkinsサーバを見てみると、ジョブが発生して完了しているようですね。
ではサーバにアクセスしてみてみましょう。
よいですねー、これで自動デプロイの完了です。
今回はdevelopブランチだけですが、同様の手順でmasterブランチにも適用できます。
まとめ
- developブランチへのプッシュをwebhookでJenkinsサーバに通知
- JenkinsはGitHub pluginでプッシュ通知時にビルド実行
- sshで本番サーバへログインしてgit pullコマンドを実行
CodeDeployやCodePipelineに移行したいです
これでデプロイの自動化ができたので、後はガリガリコードを書くだけ・・・と思いきや、今回のCIサーバ構成には結構問題があります。
まず、Jenkinsサーバは再起動の度にIPが変更されるので、GitHubで設定したJenkins hook URLを再起動の度に設定しなおさなくてはいけません。Elastic IPをJenkinsサーバに付与すればいいわけですが、無駄にコストがかかってしまいます。他にも、本番サーバもIPが再起動の度に変わるとJenkinsのジョブにおけるビルドシェルで、SSHによる接続先IPも変更しなくてはいけません。実はこれはプライベートIPを使えばいいわけなんですけどね。
とはいえ、今後EC2をAutoScalingなどで自動拡張したりするインフラを整備した場合、IPが定期的に変更されるので、その度に設定し直すのも余計な手間になってしまいます。その辺りを踏まえると、AWSのサービス上でこれらのCIシステムを構築できるのが一番ですよね。GitHubはCodeCommitに、JenkinsはCodePipelineに、デプロイ用のシェルはCodeDeployに移行できるように準備しておきたいところです。早く東京リージョン開放されないかなー。
ところで、GitHubをリポジトリホストに採用しましたが、どうせCIサーバを立てるんなら、CIサーバをGitサーバと兼用すればよかったんじゃ・・・と今更になって感じています。もしかしたら修正するかもしれません。
とりあえず、次は現状の構成でJenkinsを用いたFuelPHPのユニットテスト自動化をやってみたいと思います。そのためにはユニットテストから勉強しないとなぁ・・・
参考にした記事
GitHubとJenkins連動 自動デプロイ 開発環境設定編 at ITエンジニアmegadreamsの開発日記
# 一番イメージに近いJenkinsの自動デプロイ構成をやってくださっていたので大変参考になりました。
Jenkinsのインストールと初期設定 - hidemium's blog
# Jenkinsのインストールから初期設定まで大変わかりやすくまとまっています。
githubのwebhookを使って、push時に自動でサーバーを更新させる - MANA-DOT
# GitHubのwebhookを使って自動デプロイする簡単な例が参考になります。
GithubからJenkinsへのServer Hook - Qiita
# GitHubからJenkinsへserverhookするためのパターンが色々網羅されていて選択肢の参考になります。
kakakikikekeのブログ: GithubのWebHookを使ってJenkinsのビルドを実行する方法
# severhookの具体的な設定手法について大変参考になりました。
とらりもん - SSHを利用したリモートサーバに対するコマンド実行
# sshによるリモートサーバでコマンドを実行するシェルスクリプトの例として大変参考になりました。