読者です 読者をやめる 読者になる 読者になる

放浪エンジニアのLog

少しハッピーになれる開発

Git Tagを利用したJenkinsからのデプロイ

こんにちは、カノです。
今回は Git で Tag 管理されたソースを Jenkins を使ってデプロイする方法についてです。
f:id:kno75:20160804213016p:plain

ん? Rsync を使わないのかって?
筆者は Rsync が嫌いです!
そのため、Rsync は使いません!!

なぜか?
Rsyncは事故が起こりやすく、一歩間違えれば大事故に繋がります。
dryrun をかければよいか?

答えはNoです!
なぜなら、Rsyncの実行前にdryrunをするルールがあるにも関わらず、事故は起こるからです。実際に事故が起こっている….
その他 Rsync を選ばない理由は多々ありますが、その理由だけで記事が埋まってしまいそうなので、そのお話は需要があれば別の機会に


そのため、今回はそもそも Rsync を使わず、Git の Tag を利用してデプロイの仕組みを考えてみる事にしました。

■ 前提条件

 ・Git でソース管理(Tag管理)をしている
 ・環境が テスト環境 と 本番環境 の2つ以上別れている
 ・デプロイするときは本番環境で手動作業は行わない
 ・Jenkins を使う

これだけ


■ イメージ

f:id:kno75:20160805102003p:plain
実際に絵にしてみると、とても簡単ですね。
では工程ごとに見ていきましょう


1. TagのPush

リリースを行う機能の開発が一通り完了し、Git へ Tag の push を行う。

こちらでは特別な作業は特に必要ないですね。
タグはバージョンなどで管理しておくと良いかもしれません。

例) Tag : v1.4.20
 ・メジャーバージョン (メイン機能の追加など、大きなバージョンアップを行う場合)
 ・マイナーバージョン (サブ機能の追加などを行う場合)
 ・リビジョン (バグの改修など、機能改善を行う場合)


2. Webhookを送る

まずはリポジトリの settings で Webhook で通知を送る( Jenkins さんへの)設定を行います。
検知するイベントでリリースを選択することで、リリース時に Jenkins でリクエストを受け取ることができます。
f:id:kno75:20160805111111p:plain

webhookの設定が完了したら、次はいよいよJenkinsさんの出番ですね!


3. Jenkins で pull してくる

ここからが Jenkins 内での作業 ( Job での動作 )になってきます。

Job の設定ですが、まずはJenkinsの新規Jobを作成し、そこへ基本的な Github の情報の設定を行います。
f:id:kno75:20160805111915p:plain

次に、ビルドの項目で「シェルの実行」を選択します
f:id:kno75:20160805112112p:plain

※ 最終的なデプロイのための Job (shell) は1個ですが、説明のため分割しております。

shell の中身 1:最新のソースを取得


# master へチェックアウトする(念のため)          
git checkout master
git fetch origin
# 最新のソースをpull
git pull origin master

こちらは何も特殊なことはしていませんね。
最新のソースを落としてきているだけです。


4. Tag から差分(リリース対象)の抽出

今回のデプロイ方法では、基本的にリリース対象となるファイルだけを判別しリリースを行います。

仕組みとしてはTag の差分を取るのですが、Git でdiff を取ると対象ファイルが「M:修正」、「A:追加」、「D:削除」のような識別が可能なため、デプロイではそれにあった動作を選択させることが可能なのです。

diff で取得できるイベントとそれに伴う動作

取得 記号 意味 shell での動作
M 修正があるファイル 強制上書き
A 新規追加ファイル 普通にリリース
D 削除 サーバ上から削除
C コピー 普通にリリース


実際に前回リリース時のTag と、最新のTagとの差分取得方法です。

shell の中身 2:差分取得


# タグの一覧の取得を行う
$LIST=(`git tag -l`); ← 全てのタグ一覧が取得される
# リリース対象のTag名の取得
$RELEASE_TAG=$LISt[${#LIST[@]} - 1]};   ← 最新のタグ(1番最後のタグ)を取得
# 前回リリースしたTag名の取得
$BEFORE_TAG=$LISt[${#LIST[@]} - 2]};   ← 前回のタグ(2番最後のタグ)を取得


# Tag の Diff を取る (種類単位での取得)
A_LIST=`git diff --name-status ${RELEASE_TAG} ${BEFORE_TAG} | grep "^[A]" | awk '{print $2}'`;
M_LIST=`git diff --name-status ${RELEASE_TAG} ${BEFORE_TAG} | grep "^[M]" | awk '{print $2}'`;
D_LIST=`git diff --name-status ${RELEASE_TAG} ${BEFORE_TAG} | grep "^[D]" | awk '{print $2}'`;


解説
A_LIST=`git diff --name-status ${RELEASE_TAG} ${BEFORE_TAG} | grep "^[A]" | awk '{print $2}'`;

簡単な shell 芸ですが、1つずつ見ていきましょう。

git diff --name-status ${RELEASE_TAG} ${BEFORE_TAG}  

こちらは Git Tag の差分を『 記号 + ファイル名(相対パス) 』で取得をしています。

grep "^[A]"                        

記号の A [新規追加] だけを抽出しています。

awk'{print $2}'                      

記号が余計なので、ファイル名だけを抽出しています。

分割して見ていくと、とても簡単ですね。


5. 差分のリリース

作業4で作成したTagの差分リストから、記号 (M:修正、A:追加、D:削除)に合わせてリリースしていきます。

※ 鉄則:デプロイは、既存サービスに影響のないものからしていきましょう!
  新規追加分、修正分、削除の順番に実行していくのが良いと思われます。

shell の中身 3:デプロイ


# リリース対象のベースパスの設定
$DEPLOY_BASE_DIR="/home/www/test"; ← DEPLOY_BASE_DIR は .git が置かれているディレクト
$DEPLOY_HOST="192.xx.xx.xx";    ← DEPLOY_HOST は本番環境のHost

# 新規追加分のリリース
for a_file in ${A_LIST[@]}; do
 SOURCE_PATH=${a_file};

 TARGET_PATH=${DEPLOY_BASE_DIR}${a_file};
 TARGET_DIR=`dirname ${TARGET_PATH}`;

 # ファイルのリリース(パーミッションは適切なものへ変えてください)
 scp ${SOURCE_PATH} ${DEPLOY_HOST}:${TARGET_PATH};chmod 755 ${TARGET_DIR}";
done


# 修正分のリリース
for m_file in ${M_LIST[@]}; do
 SOURCE_PATH=${m_file};

 TARGET_PATH=${DEPLOY_BASE_DIR}${m_file};
 TARGET_DIR=`dirname${TARGET_PATH}`;

 # ファイルのリリース
 scp ${SOURCE_PATH} ${DEPLOY_HOST}:${TARGET_PATH};
done


# 削除処理
for d_file in ${D_LIST[@]}; do
 TARGET_PATH=${DEPLOY_BASE_DIR}${d_file};
 TARGET_DIR=`dirname ${TARGET_PATH}`;

 ssh ${DEPLOY_HOST} "rm ${TARGET_PATH}";
done

ご覧の通り、 A:新規追加、M:修正、D:削除 などで処理を変えております。
また、リリースを行うファイル名などを管理しているため設定ファイル、画像ファイルなどの判別が可能となっており、ファイルによってのデプロイ順番なども管理できます(もちろんその分スクリプトを書きますが)。

更に、この Tag を活用したデプロイと逆の事を行えば、リストアも簡単に実装が可能です。

このように、デプロイの手法などを自分の環境に好きにカスタマイズできるので、皆さんも自分の環境にあったデプロイを実現してみてください。