Kubernetes で Blue Green デプロイをやってみた

最近の仮想技術とクラウドサービスを使うと、アプリケーションのデプロイ作業もより安全に行える。その手法の一つが「ブルー・グリーン・デプロイメント」というモノで、コレを Kubernetes クラスタでやってみた、という話。

目次

Blue Green Deployment とは

ブルー・グリーン・デプロイメントとは、既存のサーバ環境を残したまま新たなサーバ環境を構築しておき、準備が整ったらロードバランサーで接続先のサーバ環境を新しい方に切り替える、という手法だ。既存環境を残したまま新環境を用意できるので、無停止デプロイできる上に、何か問題があったときに元の環境にロールバックしやすいのが特徴だ。

「既存のサーバ環境」を「ブルー」「新たなサーバ環境」を「グリーン」、と表現したりするが、一度「グリーン」側のデプロイが終わったら、次のデプロイ時は 「グリーン」が「既存のサーバ環境」 と呼ばれ、新たにデプロイするのは「ブルー」環境、ということになる。

どうして青と緑なのかは、調べても出てこなかった。ただ、

と思うと、「どっちも正系なんだけど普段使うのは片方」という意味合いを表現するには、青と緑が良かったのかも?

ブルー・グリーン・デプロイメントによく似た手法として、「イミュータブル・デプロイ (Immutable Deployment)」というモノがある。手順は同じなのだが、最後に「古くなった環境を捨てる」場合はイミュータブル・デプロイになる。ブルー・グリーン・デプロイは使っていない方の環境も残したままになっているので、何か問題があった時は古いバージョンに差し戻しやすくなる。しかし、使っていないとはいえ2環境を稼動させたままになるので、リソースを食う。

Kubernetes でブルー・グリーン・デプロイをやってみる

ブルー・グリーン・デプロイ自体は単なる手法なので、物理サーバでも行えることだが、今回は Kubernetes クラスタでコレを実現してみる。手順およびサンプルコードは以下の記事を参考にした。

1. 初回リリース

まずは初回リリース。ココでは「Blue」を「既存環境」とするため、以下のような Deployment と Service を kubectl apply で適用して、Web アプリを稼動させておく。

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
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 群を特定するために指定したラベルは、nameversion プロパティを振っているが、この辺のラベル名や値はお好みで。実際はリリースするバージョン番号とかを書いた方が分かりやすいかも。

2. 「Green」環境をデプロイする

さて、この状態で、新しいバージョンをリリースしてみよう。

既存の nginx-deployment-blue Deployment はそのまま保持するので、deployment-blue.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 だ。

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回叩けば終わるからなぁ。本当に楽になった…。