KubernetesでBlue-Green Deploymentしてみる

今回はサービスを本番で運用していくときに欲しくなるBlue-Green DeploymentをKubernetesでやってみます。

TL;DR

  • Serviceのselectorを更新するやり方だと10分程度BlueとGreenがまざる
  • Istioを使用すれば瞬時に100%のトラフィックを切り分けられるのでBlue-Green Deploymentができる

環境

  • GKE 1.9.7-gke.0

2種類のAPI

BlueとGreenを見分けるために、自身の色を返すAPIを用意します。

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "blue-api")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":80", nil)
}

そして、このAPIが稼働するDeploymentがこれです。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: blue-api
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: color-api
        version: blue
    spec:
      containers:
      - name: blue-api
        image: asia.gcr.io/hoge/color-api:blue
        ports:
        - containerPort: 80
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: green-api
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: color-api
        version: green
    spec:
      containers:
      - name: green-api
        image: asia.gcr.io/hoge/color-api:green
        ports:
        - containerPort: 80

ServiceでB/Gしてみる

KubernetesはServiceでアクセスするPodを見つけているので、そこの設定を書き換えればB/Gできそうです。

apiVersion: v1
kind: Service
metadata:
  name: service-color-api
spec:
  selector:
    app: color-api
    version: green
#     version: blue
  type: NodePort
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    name: http

versionをblueとgreenで変えてみた結果、このようになりました。

f:id:akaimo3:20180522222711p:plain

ちょっとわかりづらいですね。
点線の時間で切り替えを行い、Blue(上)からGreen(下)にトラフィックが切り替わるようにServiceを書き換えました。
Serviceの設定を更新したらすべてのトラフィックがGreenに行ってほしいところですが、10分ほど両方のPodにアクセスが行ってしまっています。

これではAPIのバージョニングができていないと不整合がおきてしまい正しく動かない恐れがあります。

なにか設定が漏れているのかもしれません。
原因を知っていれば教えてほしいです...

余談ですが、切り替えた直後に前の色のPodを削除することで、瞬時に全てのトラフィックを新しい色に流すことができます。

瞬時に100%のトラフィックを切り替えたいので別のアプローチとしてサービスメッシュのIstioを導入してみます。

IstioでB/Gする

まずはIstioの導入をします。
公式ドキュメントのステップ2まででIstioの導入は完了です。

Istioで経路の制御をするので、ServiceはBlueにもGreenにも通信ができるようにしておきます。

apiVersion: v1
kind: Service
metadata:
  name: service-color-api
  labels:
    app: color-api
spec:
  selector:
    app: color-api
  type: NodePort
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    name: http

Istioを使用する場合はIstio用のIngressを通さないと経路制御などができないので、少し設定に変更を入れます。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: color-ingress
  annotations:
    kubernetes.io/ingress.class: "istio"
spec:
  backend:
    serviceName: service-color-api
    servicePort: 80

最後に本命の経路制御の設定をいれます。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: color-api
spec:
  destination:
    name: service-color-api
  precedence: 1
  route:
  - labels:
      version: green

今回もselectorを書き換えることで制御できます。
定期的なアクセスがある状態で切り替えるとこのようになりました。

f:id:akaimo3:20180523104428p:plain

取得できるメトリクスがCPU利用率しかなかったので、あまりいいグラフにはなっていませんが、切り替えた直後からすべてのトラフィックが新しいほうに流れています。
Prometheusを導入後に今度はアクセス数をグラフ化して追記したいとおもいます。

まとめ

サービスメッシュを入れるとトラフィックの正確な制御ができます。
しかし、制御のためのプロキシも追加されてしまうため、余計なリソースを使うことにもなってしまいます。

デメリットも存在しますが、Istioを入れてしまえばB/Gだけでなくカナリアリリースもできるようになるので、
リリースを正確に行うためにIstioを導入するのもありかもしれません。