- 01. はじめに
- 02. Istioのアップグレード手法を説明する前に
- 03. アップグレード手法の概要
- 04. アップグレード手法の詳細
- 05. おわりに
01. はじめに
隠しません。
有吉弘行のサンデーナイトドリーマーが生きがいです。
さて今回は、Istioの安全なアップグレード手法の仕組みに関する記事を投稿しました🚀
執筆時点 (2023/02/26) では、IstioのIstiodコントロールプレーン (以降、Istiodとします) のアップグレード手法には、『インプレース方式』と『カナリア方式』があります。
また合わせてアップグレードが必要なIstioのIngressGatewayにも、その手法に『インプレース方式』と『カナリア方式』があります。
今回の安全なアップグレード手法として、Istiodでは『カナリアアップグレード』、IngressGatewayでは『インプレースアップグレード』を採用します。
それでは、Istioの安全なアップグレード手法の仕組みをもりもり布教しようと思います😗 (沼のまわりに餌をまく)
↪️ 参考:
02. Istioのアップグレード手法を説明する前に
カナリアリリースとは
Istiodのカナリアアップグレードが理解しやすくなるように、カナリアリリースから説明したいと思います。
カナリアリリースは、実際のユーザーにテストしてもらいながらリリースする手法です。
もしカナリアリリースをご存知の方は、 03. アップグレード手法の概要 まで飛ばしてください🙇🏻♂️
カナリアリリースの手順
カナリアリリースは、一部のユーザーを犠牲にすることになる一方で、アプリを実地的にテストできる点で優れています。
手順を交えながら説明します。
↪️ 参考:CanaryRelease
【1】
旧環境のアプリを残したまま、新環境をリリースします。
この段階では、全てのユーザー (100
%) を旧環境にルーティングします。
【2】
ロードバランサーで重み付けを変更し、一部のユーザー (ここでは10
%) を新環境にルーティングします。
【3】
ユーザーの手を借りて新環境を実地的にテストします (例:該当のエラーメトリクスが基準値を満たすか) 。
【4】
新環境に問題が起こらなければ、重み付けを段階的に変更し、最終的には全てのユーザー (100
%) を新環境にルーティングします。
『カナリアリリース』の呼称の由来
カナリアリリースについては、その呼称の由来を知ると、より理解が深まります。
カナリアリリースは、20世紀頃の炭坑労働者の危機察知方法に由来します。
炭鉱内には有毒な一酸化炭素が発生する場所がありますが、これは無色無臭なので、気づくことに遅れる可能性があります。
そこで当時の炭鉱労働者は、一酸化炭素に敏感な『カナリア』を炭鉱内に持ち込み、カナリアの様子から一酸化炭素の存在を察知するようにしていたそうです。
つまり、先の『犠牲になる一部のユーザー』が、ここでいうカナリアというわけです😨
↪️ 参考:
03. アップグレード手法の概要
手順
カナリアリリースについて理解したところで、Istioの安全なアップグレード手法の概要を説明します。
おおよそ以下の手順からなります。
【1】
旧Istiodが稼働しています。
【2】
新Istiod (discovery
コンテナ) をインストールします。
【3】
新Istiodのistio-proxy
コンテナをインジェクションできるように、Webhookの宛先のServiceを変更します。
この手順は重要で、後のistioctl tag set
コマンドの箇所で詳細を説明しています。
【4】
IngressGatewayをインプレースアップグレードします。
【5】
一部のNamespaceで、istio-proxy
コンテナをカナリアアップグレードします。
ここで、カナリアリリースのような重み付けがなく、カナリアアップグレードの『カナリア』という呼称に違和感を持つ方がいるかもしれません。
これについては、全てのNamespaceのistio-proxy
コンテナを一斉にアップグレードするのではなく、段階的にアップグレードしていく様子を『カナリア』と呼称している、と個人的に推測しています。
もし『カナリアアップグレード』の由来をご存じの方は、教えていただきたいです🙇🏻♂️
【6】
ユーザーの手を借りて、実地的にテストします (例:該当のエラーメトリクスが基準値以下を満たすか) 。
【7】
新Istiodのistio-proxy
コンテナに問題が起こらなければ、他のNamespaceでもistio-proxy
コンテナを段階的にカナリアアップグレードしていきます。
一方でもし問題が起これば、Namespaceのistio-proxy
コンテナとIngressGatewayをダウングレードします。
【8】
最後に、旧Istiodをアンインストールします。
↪️ 参考:Istio / Canary Upgrades
04. アップグレード手法の詳細
手順
ここからは、03. アップグレード手法の概要 を深ぼっていきます。
ヤサイニンニクアブラマシマシな説明になってしまったので、ここまでを食べ切れた方のみ進むことをお勧めします🥺
今回は、ドキュメントで一番優先して記載されているistioctl
コマンドを使用した手順を説明します。
もちろん、他のツール (例:Helm、ArgoCD) を使用してもアップグレードできます。
細かな手順が異なるだけで、アップグレード手法の概要に関しては同じです🙆♂️
それでは、03. アップグレード手法の概要 の【1】〜【8】に対応させながら説明していくゾ。
前提
Namespace
まず最初に、前提となる状況を設定しておきます。
各Namespaceのistio.io/rev
ラベルにdefault
が設定されているとします。
$ kubectl get namespace -L istio.io/rev
NAME STATUS AGE REV
foo Active 34d default
bar Active 34d default
baz Active 34d default
istio-ingress Active 34d default
...
マニフェストに書き起こすと以下のようになっています。
apiVersion: v1 kind: Namespace metadata: name: foo labels: istio.io/rev: default
エイリアスはどんな値でも問題なく、よくあるエイリアスとしてdefault
やstable
などを使用します。
このistio.io/rev
ラベルがあることで、そのNamespaceのPodにistio-proxy
コンテナを自動的にインジェクションします。
istio-proxy
コンテナのインジェクションについては、こちら記事で説明しており、もし気になる方はこちらもよろしくどうぞ🙇🏻♂️
Istiod
すでに1-14-6
のIstiodが動いており、1-15-4
にカナリアアップグレードします。
IstiodはDeployment配下のPodであり、このPodはIstiodの実体であるdiscovery
コンテナを持ちます。
$ kubectl get deployment -n istio-system -l app=istiod NAME READY UP-TO-DATE AVAILABLE AGE istiod-1-14-6 1/1 1 1 47s # 1-14-6
IngressGateway
IngressGatewayはIstiodとは異なるNamespaceで動いており、インプレースアップグレードします。
IngressGatewayはistio-proxy
コンテナを持ちます。
$ kubectl get deployment -n istio-ingress NAME READY UP-TO-DATE AVAILABLE AGE istio-ingressgateway 1/1 1 1 47s
補足として、セキュリティのベストプラクティスでは、IstiodとIngressGatewayは異なるNamespaceで動かすことが推奨されています。
マイクロサービス
各Namespaceでマイクロサービスが動いています。
マイクロサービスのPodはistio-proxy
コンテナを持ちます。
$ kubectl get deployment -n foo NAME READY UP-TO-DATE AVAILABLE AGE foo 2/2 1 1 47s ...
$ kubectl get deployment -n bar NAME READY UP-TO-DATE AVAILABLE AGE bar 2/2 1 1 47s ..
$ kubectl get deployment -n baz NAME READY UP-TO-DATE AVAILABLE AGE baz 2/2 1 1 47s ...
【1】 アップグレード前の検証
ここで実施すること
アップグレード前に、現在のKubernetes Clusterがアップグレード要件を満たしているかを検証します。
↪️ 参考:Before you upgrade
istioctl x precheck
コマンド
istioctl x precheck
コマンドを実行し、アップグレード要件を検証します。
$ istioctl x precheck ✅ No issues found when checking the cluster.Istiois safe to install or upgrade! To get started, check out https://istio.io/latest/docs/setup/getting-started/
問題がなければ、istioctl
コマンドはNo issue ...
の文言を出力します。
もし、問題がある場合、istioctl
コマンドはエラー文言を出力します。
例えば、Istioのistio-proxy
コンテナのインジェクションではkube-apiserverと通信する必要があります。
そのため、kube-apiserverのバージョンが古すぎるせいでIstioが非対応であると、エラーになります。
kubectl get
コマンド
▼ IstiodのDeployment
kubectl get
コマンドを実行し、現在のIstiodのバージョンを確認します👀
まずはIstiodのDeploymentを確認すると、1-14-6
のDeploymentがあります。
$ kubectl get deployment -n istio-system -l app=istiod NAME READY UP-TO-DATE AVAILABLE AGE istiod-1-14-6 1/1 1 1 47s # 1-14-6
istio-proxy
コンテナのインジェクションの仕組みでいうと、以下の赤枠の要素です👇
▼ Webhookの宛先のService
次に、 Serviceを確認すると、1-14-6
のServiceがあります。
$ kubectl get service -n istio-system -l app=istiod NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istiod-1-14-6 ClusterIP 10.96.93.151 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 109s # 1-14-6
このServiceは、kube-apiserverからIstiodへのWebhookを仲介することにより、istio-proxy
コンテナのインジェクションを可能にします。
istio-proxy
コンテナのインジェクションの仕組みでいうと、以下の赤枠の要素です👇
▼ 宛先のServiceを決めるMutatingWebhookConfiguration
最後に、MutatingWebhookConfigurationを確認すると、istio-revision-tag-<エイリアス>
とistio-sidecar-injector-<リビジョン番号>
のMutatingWebhookConfigurationがあります。
$ kubectl get mutatingwebhookconfigurations NAME WEBHOOKS AGE istio-revision-tag-default 2 114s # カナリアアップグレード用 istio-sidecar-injector-1-14-6 2 2m16s # インプレースアップグレード用のため今回は言及しない
istio-proxy
コンテナのインジェクションの仕組みでいうと、以下の赤枠の要素です👇
これらのうち、前者 (istio-revision-tag-<エイリアス>
) をカナリアアップグレードのために使用します。
このMutatingWebhookConfigurationは、Webhookの宛先のServiceを決めるため、結果的にistio-proxy
コンテナのバージョンを決めます。
ここで、MutatingWebhookConfigurationのistio.io/rev
ラベルとistio.io/tag
ラベルの値も確認しておきます。
$ kubectl get mutatingwebhookconfiguration istio-revision-tag-default -o yaml \ | yq '.metadata.labels' ... istio.io/rev: 1-14-6 istio.io/tag: default ...
istio.io/rev
ラベルはIstiodのバージョン、istio.io/tag
ラベルはこれのエイリアスを表しています。
また、.webhooks[].namespaceSelector
キー配下のistio.io/rev
キーの検知ルールを確認します。
$ kubectl get mutatingwebhookconfiguration istio-revision-tag-default -o yaml \ | yq '.webhooks[]' ... namespaceSelector: matchExpressions: - key: istio.io/rev operator: In values: - default ...
合わせて、.webhooks[].clientConfig.service
キー配下のServiceを名前を確認します。
$ kubectl get mutatingwebhookconfiguration istio-revision-tag-default -o yaml \ | yq '.webhooks[].clientConfig' ... service: name: istiod-1-14-6 ...
整理すると、Namespaceでistio.io/rev
ラベルにdefault
を設定しておけば、MutatingWebhookConfigurationがそれを検知し、特定のIstioのバージョンのServiceにWebhookを送信できるようになっています。
↪️ Istio / Safely upgrade the Istio control plane with revisions and tags
【2】 新Istiodのインストール
ここで実施すること
それでは、新Istiodをインストールします。
↪️ 参考:Control plane
istioctl version
コマンド
新しくインストールするIstiodのバージョンは、istioctl
コマンドのバージョンで決まります。
そこで、istioctl version
コマンドを実行し、これのバージョンを確認します。
$ istioctl version client version: 1.15.4 # アップグレード先のバージョン control plane version: 1.14.6 # 現在のバージョン data plane version: 1.14.6
istioctl install
コマンド
カナリアアップグレードの場合、istioctl install
コマンドを実行します。
ドキュメントではrevision
キーの値がcanary
ですが、今回は1-15-4
とします。
この値は、Istioが使用する様々なKubernetesリソースの接尾辞や、各種リソースのistio.io/rev
ラベルの値になります。
$ istioctl install --set revision=1-15-4 WARNING: Istio is being upgraded from 1.14.6 -> 1.15.4 WARNING: Before upgrading, you may wish to use 'istioctl analyze' to check for IST0002 and IST0135 deprecation warnings. ✅ Istio core installed ✅ Istiod installed ✅ Ingress gateways installed ✅ Installation complete Thank you for installing Istio 1.15. Please take a few minutes to tell us about your install/upgrade experience!
kubectl get
コマンド
▼ IstiodのDeployment
kubectl get
コマンドを実行し、istioctl install
コマンドで何をインストールしたのかを確認します👀
まずはIstiodのDeploymentを確認すると、1-15-4
というDeploymentが新しく増えています。
$ kubectl get deployment -n istio-system -l app=istiod NAME READY UP-TO-DATE AVAILABLE AGE istiod-1-14-6 1/1 1 1 47s # 1-14-6 istiod-1-15-4 1/1 1 1 47s # 1-15-4
接尾辞の1-15-4
は、revision
キーの値で決まります。
この段階では、旧Istiodと新Istioが並行的に稼働しており、kube-apiserverはまだ旧Istiodと通信しています
今の状況は以下の通りです👇
▼ Webhookの宛先のService
次に Webhookの宛先のServiceを確認すると、istiod-1-15-4
というServiceが新しく増えています。
$ kubectl get service -n istio-system -l app=istiod NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istiod-1-14-6 ClusterIP 10.96.93.151 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 109s # 1-14-6 istiod-1-15-4 ClusterIP 10.104.186.250 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 87s # 1-15-4
この段階では、まだWebhookの宛先はistiod-1-14-6
のServiceです。
今の状況は以下の通りです👇
▼ Webhookの宛先のServiceを決めるMutatingWebhookConfiguration
最後にMutatingWebhookConfigurationを確認すると、istio-sidecar-injector-1-15-4
というMutatingWebhookConfigurationが新しく増えています。
$ kubectl get mutatingwebhookconfigurations NAME WEBHOOKS AGE istio-revision-tag-default 2 114s # カナリアアップグレードで使用する istio-sidecar-injector-1-14-6 2 2m16s istio-sidecar-injector-1-15-4 2 2m16s
カナリアアップグレードでは、istio-revision-tag-<エイリアス>
のMutatingWebhookConfigurationを使用します。
今の状況は以下の通りです👇
※ 実は、他にもインストールしているものがあるのですが、話をわかりやすくするために、今回は言及していません🙇🏻♂️
【3】 Webhookの宛先のServiceの変更
ここで実施すること
この手順では、エイリアスのistio.io/tag
ラベルはそのままに、istio.io/rev
ラベルの値を変更します。
さらに、Webhookの宛先のServiceを変更します。
↪️ 参考:
istioctl tag set
コマンド
istioctl tag set
コマンドを実行し、istio.io/rev
ラベルの値と宛先のServiceを変更します。
$ istioctl tag set default --revision 1-15-4 --overwrite
実行後に、もう一度MutatingWebhookConfigurationを確認すると、istio.io/rev
ラベルの値が変わっています。
$ kubectl get mutatingwebhookconfiguration istio-revision-tag-default -o yaml \ | yq '.metadata.labels' ... istio.io/rev: 1-15-4 istio.io/tag: default ...
また、Webhookの宛先のServiceも変わっています。
$ kubectl get mutatingwebhookconfiguration istio-revision-tag-default -o yaml \ | yq '.webhooks[].clientConfig' ... service: name: istiod-1-15-4 ...
これらにより、Webhookの宛先が1-15-4
のServiceとなるため、1-15-4
のistio-proxy
コンテナをインジェクションできるようになります。
今の状況は以下の通りです👇
【4】 IngressGatewayをインプレースアップグレード
ここで実施すること
Webhookの宛先が1-15-4
のServiceに変わったところで、IngressGatewayをインプレースアップグレードします。
↪️ 参考:In place upgrade
kubectl rollout restart
コマンド
kubectl rollout restart
コマンドを実行し、IngressGatewayをインプレースアップグレードします。
$ kubectl rollout restart deployment istio-ingressgateway-n istio-ingress
再作成したPodのイメージを確認してみると、istio-proxy
コンテナを1-15-4
にアップグレードできています。
$ kubectl get pod bar -n bar -o yaml | yq '.spec.containers[].image' docker.io/istio/proxyv2:1.15.4 # istio-proxyコンテナ
補足として、istioctl proxy-status
コマンドを使用して、アップグレードの完了を確認してもよいです。
今の状況は以下の通りです👇
なお、IngressGatewayのアップグレード時、マイクロサービスへのインバウンド通信が遮断されてしまうと思った方がいるかもしれません。
この点については、DeploymentがローリングアップグレードでIngressGatewayのPodを入れ替えるため、安心していただいて問題ありません🙆♂️
【5】 一部のNamespaceのistio-proxy
コンテナをアップグレード
ここで実施すること
続けて、一部のNamespaceのistio-proxy
コンテナをアップグレードします。
Podの再作成により、新Istiodのistio-proxy
コンテナがインジェクションされるため。istio-proxy
コンテナをアップグレードできます。
↪️ 参考:Data plane
kubectl rollout restart
コマンド
前提にあるように、Namespaceには foo
bar
baz
があります。
kubectl rollout restart
コマンドを実行し、bar
のistio-proxy
コンテナからアップグレードします。
$ kubectl rollout restart deployment bar -n bar
再作成したPodのイメージを確認してみると、istio-proxy
コンテナを1-15-4
にアップグレードできています。
$ kubectl get pod bar -n bar -o yaml | yq '.spec.containers[].image' bar-app:1.0 # マイクロサービス docker.io/istio/proxyv2:1.15.4 # istio-proxyコンテナ
補足として、istioctl proxy-status
コマンドを使用して、アップグレードの完了を確認してもよいです。
今の状況は以下の通りです👇
【6】 ユーザの手を借りたテスト
ここで実施すること
Istioを部分的にアップグレードしたところで、アップグレードが完了したNamespaceをテストします。
ユーザーの手を借りて実地的にテストします (例:該当のエラーメトリクスが基準値を満たすか) 。
今の状況は以下の通りです👇
もし問題が起こった場合
もし問題が起こった場合、1-14-6
にダウングレードしていきます。
istioctl tag set
コマンドを実行し、istio.io/rev
ラベルの値を元に戻します。
$ istioctl tag set default --revision 1-14-6 --overwrite
その後、kubectl rollout restart
コマンドの手順を実行し、istio-proxy
コンテナをダウングレードしてきます。
【7】 istio-proxy
コンテナの段階的なアップグレード
ここで実施すること
先のNamespaceで問題が起こらなければ、残ったNamespace (foo
、baz
、...) のistio-proxy
コンテナも段階的にアップグレードしていきます。
kubectl rollout restart
コマンド
同様にkubectl rollout restart
コマンドを実行し、istio-proxy
コンテナからアップグレードします。
$ kubectl rollout restart deployment foo -n foo $ kubectl rollout restart deployment baz -n baz ...
最終的に、全てのNamespacemのistio-proxy
コンテナが新しくなります。
今の状況は以下の通りです👇
【8】 旧Istiodのアンインストール
ここで実施すること
最後に、旧Istiodのアンインストールします。
istioctl uninstall
コマンド
istioctl uninstall
コマンドを実行し、旧Istiodをアンインストールします。
$ istioctl uninstall --revision 1-14-6 ✅ Uninstall complete
今の状況は以下の通りです👇
kubectl get
コマンド
▼ IstiodのDeployment
kubectl get
コマンドを実行し、istioctl uninstall
コマンドで何をアンインストールしたのかを確認します👀
まずはIstiodのDeploymentを確認すると、1-14-6
というDeploymentが無くなっています。
$ kubectl get deployment -n istio-system -l app=istiod NAME READY UP-TO-DATE AVAILABLE AGE istiod-1-15-4 1/1 1 1 47s # 1-15-4
▼ Webhookの宛先のService
次に Webhookの宛先のServiceを確認すると、istiod-1-14-6
というServiceが無くなっています。
$ kubectl get service -n istio-system -l app=istiod NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istiod-1-15-4 ClusterIP 10.104.186.250 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 87s # 1-15-4
▼ 宛先のServiceを決めるMutatingWebhookConfiguration
最後にMutatingWebhookConfigurationを確認すると、istio-sidecar-injector-1-14-6
というMutatingWebhookConfigurationが無くなっています。
$ kubectl get mutatingwebhookconfigurations NAME WEBHOOKS AGE istio-revision-tag-default 2 114s # 次のカナリアアップグレードでも使用する istio-sidecar-injector-1-15-4 2 2m16s
これで、新Istiodに完全に入れ替わったため、アップグレードは完了です。
今の状況は以下の通りです👇
※ 実は、他にもアンインストールしているものがあるのですが、話をわかりやすくするために、今回は言及していません🙇🏻♂️
05. おわりに
Istioの安全なアップグレード手法の仕組みをもりもり布教しました。
Istioへの愛が溢れてしまいました。
Istioのアップグレードの異常がシステムに与える影響力は非常に大きく、様々な問題 (体験談:istio-proxy
コンテナのPodへのインジェクションがずっと完了せず、アプリコンテナを作成できない) が起こる可能性があります😇
これからIstioを採用予定の方は、Istioを安全にアップグレードするために十分に準備しておくことをお勧めします👍