Kubernetes で Blue Green デプロイをやってみた
最近の仮想技術とクラウドサービスを使うと、アプリケーションのデプロイ作業もより安全に行える。その手法の一つが「ブルー・グリーン・デプロイメント」というモノで、コレを Kubernetes クラスタでやってみた、という話。
目次
Blue Green Deployment とは
ブルー・グリーン・デプロイメントとは、既存のサーバ環境を残したまま新たなサーバ環境を構築しておき、準備が整ったらロードバランサーで接続先のサーバ環境を新しい方に切り替える、という手法だ。既存環境を残したまま新環境を用意できるので、無停止デプロイできる上に、何か問題があったときに元の環境にロールバックしやすいのが特徴だ。
「既存のサーバ環境」を「ブルー」、「新たなサーバ環境」を「グリーン」、と表現したりするが、一度「グリーン」側のデプロイが終わったら、次のデプロイ時は 「グリーン」が「既存のサーバ環境」 と呼ばれ、新たにデプロイするのは「ブルー」環境、ということになる。
どうして青と緑なのかは、調べても出てこなかった。ただ、
- 「正系」と「副系」を決める アクティブ・スタンバイ構成 (アクティブ・パッシブ構成) とも違うし、
- 両方を同時に動かす アクティブ・アクティブ構成 (二重化・多重化・冗長化) とも違うし、
と思うと、「どっちも正系なんだけど普段使うのは片方」という意味合いを表現するには、青と緑が良かったのかも?
ブルー・グリーン・デプロイメントによく似た手法として、「イミュータブル・デプロイ (Immutable Deployment)」というモノがある。手順は同じなのだが、最後に「古くなった環境を捨てる」場合はイミュータブル・デプロイになる。ブルー・グリーン・デプロイは使っていない方の環境も残したままになっているので、何か問題があった時は古いバージョンに差し戻しやすくなる。しかし、使っていないとはいえ2環境を稼動させたままになるので、リソースを食う。
Kubernetes でブルー・グリーン・デプロイをやってみる
ブルー・グリーン・デプロイ自体は単なる手法なので、物理サーバでも行えることだが、今回は Kubernetes クラスタでコレを実現してみる。手順およびサンプルコードは以下の記事を参考にした。
1. 初回リリース
まずは初回リリース。ココでは「Blue」を「既存環境」とするため、以下のような Deployment と Service を kubectl apply
で適用して、Web アプリを稼動させておく。
deployment-blue.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-blue # Blue なデプロイメント名にしておく
spec:
replicas: 3
template:
metadata:
labels: # Blue なラベルを付けておく
name: nginx
version: blue
spec:
containers:
- name: nginx
image: nginx:v1 # このバージョンの Docker イメージを使う
ports:
- containerPort: 80
service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
name: nginx
spec:
type: LoadBalancer
selector: # Blue の Pod 群を指定する
name: nginx
version: blue
ports:
- name: http
port: 80
targetPort: 80
こんな感じ。replicas
の指定のとおり、Pod としては3台稼動している状態だ。Load Balancer Service が割り振る Pod 群を特定するために指定したラベルは、name
と version
プロパティを振っているが、この辺のラベル名や値はお好みで。実際はリリースするバージョン番号とかを書いた方が分かりやすいかも。
2. 「Green」環境をデプロイする
さて、この状態で、新しいバージョンをリリースしてみよう。
既存の nginx-deployment-blue
Deployment はそのまま保持するので、deployment-blue.yaml
ファイルを変更して適用するのではなく、このファイルをコピーして deployment-green.yaml
というファイルを作る。
deployment-green.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-green # Green なデプロイメント名にしておく
spec:
replicas: 3
template:
metadata:
labels: # Green なラベルを付けておく
name: nginx
version: green
spec:
containers:
- name: nginx
image: nginx:v2 # 新しいバージョンの Docker イメージを使う
ports:
- containerPort: 80
deployment-blue.yaml
との違いはコメントがある部分のみ。Deployment 名、Pod のラベル、使用する Docker イメージが違う。
コレを $ kubectl apply -f deployment-green.yaml
コマンドで適用し、デプロイする。
デプロイ後、$ kubectl get pod
で確認すると、Pod としては Blue 3台、Green 3台の合計6台が稼動した状態になる。Green 側の Web アプリもリクエストを待ち受けている状態にはなっているが、Load Balancer Service が Blue 側の Pod たちにしかリクエストを割り振らないので、Green 側が外部から呼び出されることはない状態だ。
ココまでで、Green としてデプロイした各 Pod が正常に動作しているか確認しておく。Load Balancer 経由で到達できないだけで、Green 側の直接 IP アドレスを叩いて動作確認してみたりはできるだろう。
通信先を Blue の Pod 群から Green の Pod 群に変える
さて、デプロイした Green の Pod 群が問題なさそうなら、Load Balancer Service の設定を変更して、Blue の Pod 群ではなく Green の Pod 群に通信を割り振るようにする。使用するのは先程使った service.yaml
だ。
service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
name: nginx
spec:
type: LoadBalancer
selector: # Green の Pod 群を指定する
name: nginx
version: green
ports:
- name: http
port: 80
targetPort: 80
変更したのは spec.selector
部分のみ。コレで $ kubectl apply -f service.yaml
コマンドを実行すれば、即座にリクエストが Green 側で処理されるようになる。Blue 側の Pod 群は稼動はしたままだが、Load Balancer 経由ではリクエストを受け取ることはなくなった。
イミュータブル・デプロイ : Blue 環境を捨てておく
あとはオマケ。ココまでだと、6台の Pod が動いたままになる。リリース直後は Blue 側にロールバックすることも考えて、環境を残しておいても良いが、しばらくしたらロールバックの備えも必要なくなるだろう。使っていないとはいえ多少リソースは食うし、システムログなんかは随時溜まっていく。時間が経つと、実際どの Pod がリクエストを処理しているのか、パッと見で分かりにくくなったりもするので、個人的には Blue 側の環境は最終的には削除したいと思う。
削除する場合は簡単で、$ kubectl delete deployment nginx-deployment-blue
と、Blue の Deployment 名を指定して Delete すれば OK。Blue の Pod 群も合わせて破棄される。呼ばれなくなった Pod 群を破棄しただけなので、利用されている Green 側の Deployment や Pod 群には影響なし。
この次のリリース時は、deployment-blue.yaml
を新バージョンに書き換えて適用していけば、Green から Blue に切り替えられる、というワケ。
以上
なんだか最終的にブルー・グリーン・デプロイではなくイミュータブル・デプロイをやってみた感じになったが、手順はとっても簡単だった。
Kubernetes だとこういう「サーバのデプロイ」みたいな作業が簡単に行えて素晴らしい。レガシーなオンプレ環境の頃は、「本番作業室」に入室して、リリース資材を共有サーバに中継して、本番環境にリモート接続して、手順書に沿ってリリース作業をして…みたいなことを、夜間に半日〜1日がかりでやっていたが、Kubernetes なら設定ファイルを書き換えて kubectl
コマンドを2・3回叩けば終わるからなぁ。本当に楽になった…。