Operator SDKとEnvoyのgo-control-planeを使って、Blue-Greenデプロイメント機能を備えたCustom Resourceを作ってみた


BlueとGreenの2世代のDeploymentとEnvoy PodをまとめてデプロイするOperatorを作成し、Envoyの構成を動的に制御するxDSサーバの機能もそのOperatorに組み込めば、Blue-Green Deploymentを標準装備したOperatorが作れそうです。
Operatorのフレームワーク(Operator SDK)とxDSサーバのフレームワーク(go-control-plane)がGitHubに公開されており、どちらもGo言語なので頑張れば一体化できるかもしれません。
...ということで作ってみました。Operatorの構造を下図に示します。

作成したコードはGitHubに公開しました。そのコードをダウンロードしてビルドし、Blue-Green Deploymentのシナリオを実行する手順をここに書き残しておきます。
ビルド済のコンテナイメージがDockerhubにpublicで登録されているので、単に動作を再現するだけなら以下の「実行環境構築」の1,3,4はスキップしても大丈夫です。

ここではOperatorやxDSサーバの実装の詳細には言及しませんが、興味のあるかたは上記のArchitecture図をたよりにソースコードを覗いてみてください。Go初心者のad hocなプログラムなので、Goプログラミングの参考にはなりませんが、動いたという事実と処理の流れくらいは参考になると思います。

動作確認環境は次のとおりです。

Operator SDKはテンプレート(ソースコード)生成時にv0.17.2を使用した都合上、v0.17.2を前提としています(v0.18以上では互換性の問題が出るかもしれません)。他は最新バージョンでいいと思います。
go-control-planeはライブラリとして使用していますが、明示的にインストールする必要はありません。

実行環境構築

1. 前提ソフトウェアをインストール

上記の動作確認環境に必要なソフトウェアをインストールします(手順は各リンク先参照)。
Operator SDKはRELEASE_VERSION=v0.17.2とする他はリンク先の手順のとおりで大丈夫です。

2. GitHubリポジトリをダウンロード

git clone https://github.com/takeyan/bgdeploy-xds2.git

3. Operatorのコードおよびコンテナイメージをビルド

cd bgdeploy-xds2
export OPERATOR_IMAGE={image name and tag for your container registry}
operator-sdk build $OPERATOR_IMAGE
docker login to your registry
docker push $OPERATOR_IMAGE

4. Operatorのマニフェストファイルを編集

deploy/operator.yaml内でOperatorのイメージ名を参照しているので、上記で指定した名前に変更します。(初期状態ではtakeyan/bgdeploy-oprator:0.1.0となっています)

vi deploy/operator.yaml

5. 以下のマニフェストを適用

kubectl create -f deploy/service_account.yaml 
kubectl create -f deploy/role.yaml
kubectl create -f deploy/role_binding.yaml 
kubectl create -f deploy/crds/swallowlab.com_bgdeploys_crd.yaml
kubectl create -f envoy-configmap.yaml

動作確認シナリオ実行

Operatorはカスタムリソースのマニフェストを定期的にモニターし、マニフェストが変化したらマニフェストの定義に合わせてリソースの状態を変更します。マニフェストのtransitは起動するDeploymentの数を指示しており、OFFの時にはactiveに指定した方のDeploymentとServiceを、ONではblueとgreenの両方とも起動します。activeはEnvoyがリクエストを転送する相手も指示しています。
下図にシナリオの流れを示します。

1. Operatorをデプロイ

kubectl create -f deploy/operator.yaml 

2. BGDeployカスタムリソースをデプロイ

kubectl create -f bgdeploy_phase1.yaml

Operatorをデプロイした後初めてカスタムリソースをデプロイするタイミングでは、EnvoyのPodとService、およびOperatorのgrpcポートとhttpポートを公開するためのServiceが起動し、これらはその後稼動し続けます。さらにマニフェストの指定に従ってblueのDeploymentとServiceが起動します。

3. bgdeploy-svc-envoyのNodePort番号を確認

kubectl get svc

# kubectl get svc
NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                           AGE
bgdeploy-svc-envoy      NodePort    172.21.229.116   <none>        10000:32478/TCP                   15m

K8sクラスタ外部からEnvoyに対してHTTP Getを送付するのにNodePortを使用します。EnvoyのhttpリスニングポートにHTTPリクエストを送付すると、EnvoyがblueまたはgreenのServiceにそのリクエストを転送し、応答を返してくれます。

4. HTTP Getリクエストを送付

curl {worker nodeのIPアドレス}:{EnvoyのNodePort番号}/api/echo

# curl 10.242.0.5:32478/api/echo
{"api":"echo","nodename":"10.243.0.4","pod_ip":"172.17.5.151","podname":"bgdeploy-dep-blue-68bb89dbf4-glwkd","query_string":[""],"timestamp":"2020-08-27T11:44:31.500561"}

サンプルアプリは/api/echoというパスでHTTP Getリクエストを受け取ると、Pod名を含むJSONフォーマットのメッセージを返す仕掛けになっています。Pod名に含まれる"blue"または"green"で、Envoyがblue/greenのどちらに振り分けたかが確認できます。

以下のターミナル画面の上段ではcurlを、下段ではkubectl get podとkubectl get service(getall.sh内で呼び出し)をそれぞれ2秒間隔で実行し、マニフェストの更新によってpodとserviceの増減やアプリ応答が変化する様子をモニターします。以下の画面ではblueのpod(bgdeploy-dep-blue-xx-xx)とservice(bgdeploy-svc-blue)が起動しており、curlにはblueが応答していることがわかります。

5. マニフェストを更新し、応答がblueからgreenに変わることを確認

マニフェストのtransitをONに変更します。
kubectl apply -f bgdeploy_phase2.yaml


bgdeploy-phase2.yamlの適用によりtransit=ON, active=blueになると、greenのpodとserviceが起動し、blueとgreenの共存状態となりました。curlには引き続きblueが応答しています。

マニフェストのactiveをgreenに変更します
kubectl apply -f bgdeploy_phase3.yaml


bgdeploy-phase3.yamlの適用によりtransit=ON, active=greenになると、curlに対してgreenが応答するようになりました。podとserviceは引き続きblueとgreenが共存しています。

マニフェストのtransitをOFFに変更します
kubectl apply -f bgdeploy_phase4.yaml


bgdeploy-phase4.yamlの適用によりtransit=OFF, active=greenになると、blueのpodとserviceが停止しました。

まとめ

この記事ではBGDeployというカスタムリソースによるBlue-Green Deploymentの流れををご紹介しました。
OperatorとEnvoyの欲しいところだけをコンパクトに実装したいというニーズは、EdgeコンピューティングとEdge向け軽量K8sの普及に伴って広がってくると思います。どちらもプログラミングするとなると難しそうな印象ですが、フレームワークのおかげで敷居はずいぶん低くなっていると感じました。

この記事がOperatorやEnvoyに興味を持っている方の参考になれば幸いです。

以上です。