この記事から得られる知識
この記事を読むと、以下を "完全に理解" できます✌️
- Istioのアップグレード手法の種類について
- 安全なカナリア方式の仕組みについて
- この記事から得られる知識
- 01. はじめに
- 02. なぜ安全なアップグレードが必要なのか
- 03. アップグレード手法を説明する前に
- 04. アップグレード手法の概要
- 05. アップグレード手法の詳細
- 06. おわりに
- 記事関連のおすすめ書籍
01. はじめに
隠しません。
有吉弘行のサンデーナイトドリーマー は人生のバイブルです。
![]()
さて、最近の業務でIstio⛵️をひたすらアップグレードしています。
今回は、採用したアップグレード手法の紹介も兼ねて、Istioの安全なアップグレード手法の仕組みを記事で解説しました。
Istioのアップグレード手法には変遷があり、解説するのは執筆時点 (2023/02/26) で最新の 1.14 系のアップグレード手法です。
それでは、もりもり布教していきます😗
飛ばしていただいても大丈夫ですが、読んでもらえるとより理解が深まるはずです👍
02. なぜ安全なアップグレードが必要なのか
起こりうる問題
そもそも、なぜIstioで安全なアップグレードを採用する必要があるのでしょうか。
Istioで問題が起こると、Pod内のistio-proxyコンテナが正しく稼働せず、システムに大きな影響を与える可能性があります。
例えば、istio-proxyコンテナのPodへのインジェクションがずっと完了せず、アプリコンテナへの通信が全て遮断されるといったことが起こることがあります。

採用するべきアップグレード手法
執筆時点 (2023/02/26) では、Istiodコントロールプレーン (以降、Istiodとします) のアップグレード手法には、『インプレース方式』と『カナリア方式』があります。
また合わせてアップグレードが必要なIstio IngressGatewayには、その手法に『インプレース方式』があります。

今回の安全なアップグレード手法として、Istiodでは『カナリアアップグレード』、Istio IngressGatewayでは『インプレースアップグレード』を採用します。
03. アップグレード手法を説明する前に
カナリアリリースとは
Istiodのカナリアアップグレードが理解しやすくなるように、カナリアリリースから説明したいと思います。
カナリアリリースは、実際のユーザーにテストしてもらいながらリリースする手法です。
もしカナリアリリースをご存知の方は、 04. アップグレード手法の概要 まで飛ばしてください🙇🏻
カナリアリリースの手順
カナリアリリースは、一部のユーザーを犠牲にすることになる一方で、アプリを実地的にテストできる点で優れています。
手順を交えながら説明します。
(1) 新環境のリリース
旧環境のアプリを残したまま、新環境をリリースします。
この段階では、全てのユーザー (100%) を旧環境にルーティングします。

(2) 新環境への重み付けルーティング
ロードバランサーで重み付けを変更し、一部のユーザー (ここでは10%) を新環境にルーティングします。

(3) 実地的テストの実施
ユーザーの手を借りて新環境を実地的にテストします (例:該当のエラーメトリクスが基準値を満たすか) 。

(4) 重み付けの段階的変更
新環境に問題が起こらなければ、重み付けを段階的に変更し、最終的には全てのユーザー (100%) を新環境にルーティングします。

『カナリアリリース』の呼称の由来
カナリアリリースについては、その呼称の由来を知ると、より理解が深まります。
カナリアリリースは、20世紀頃の炭坑労働者の危機察知方法に由来します。
炭鉱内には有毒な一酸化炭素が発生する場所がありますが、これは無色無臭なため、気づくことに遅れる可能性があります。
そこで当時の炭鉱労働者は、一酸化炭素に敏感な『カナリア』を炭鉱内に持ち込み、カナリアの様子から一酸化炭素の存在を察知するようにしていたそうです。
つまり、先ほどの『犠牲になる一部のユーザー』が、ここでいうカナリアというわけです😨

04. アップグレード手法の概要
カナリアリリースを理解したところで、Istioの安全なアップグレード手法の概要を説明します。
おおよそ以下の手順からなります。
なお各番号は、05. アップグレード手法の詳細 の (1) 〜 (8) に対応しています。
(1) アップグレード前の検証
旧Istiodが稼働しています。
ここで、アップグレードが可能かどうかを検証しておきます。

(2) 新Istiodのインストール
新Istiod (discoveryコンテナ) をインストールします。

(3) Webhookの宛先のServiceの変更
新Istiodのistio-proxyコンテナをインジェクションできるように、Webhookの宛先のServiceを変更します。
この手順は重要で、後の (3) Webhookの宛先のServiceの変更 で詳細を説明しています。
(4) Istio IngressGatewayをインプレースアップグレード
Istio IngressGatewayをインプレースアップグレードします。

(5) 一部のNamespaceのistio-proxyコンテナをアップグレード
一部のNamespaceで、istio-proxyコンテナをカナリアアップグレードします。

ここで、カナリアリリースのような重み付けがなく、カナリアアップグレードの『カナリア』という呼称に違和感を持つ方がいるかもしれません。
これについては、全てのNamespaceの
istio-proxyコンテナを一斉にアップグレードするのではなく、段階的にアップグレードしていく様子を『カナリア』と呼称している、と個人的に推測しています。
もし『カナリアアップグレード』の由来をご存じの方は、ぜひ教えていただけると🙇🏻
(6) ユーザの手を借りたテスト
ユーザーの手を借りて、実地的にテストします (例:該当のエラーメトリクスが基準値以下を満たすか) 。

(7) istio-proxyコンテナの段階的アップグレード
新Istiodのistio-proxyコンテナに問題が起こらなければ、他のNamespaceでもistio-proxy
コンテナを段階的にカナリアアップグレードしていきます。
一方でもし問題が起これば、Namespaceのistio-proxyコンテナとIstio IngressGatewayをダウングレードします。

(8) 旧Istiodのアンインストール
最後に、旧Istiodをアンインストールします。

05. アップグレード手法の詳細
istioctl コマンドを使用したアップグレード
ここからは、04. アップグレード手法の概要 を深ぼっていきます。
今回は、ドキュメントで一番優先して記載されている istioctl コマンドを使用した手順 を説明します。
なお各番号は、04. アップグレード手法の概要 の (1) 〜 (8) に対応しています。
もちろん、
istioctlコマンド以外のツール (例:helmコマンド、helmfileコマンド、ArgoCD) を使用してもアップグレードできます。
細かな手順が異なるだけで、アップグレード手法の概要は同じです🙆🏻
前提
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
...
istio.io/revラベル値のエイリアスについてistio.io/revラベル値は、どんなエイリアスでもよいです。
よくあるエイリアスとして
defaultやstableを使用します👍
さらに、マニフェストに書き起こすと以下のようになっています。
apiVersion: v1 kind: Namespace metadata: name: foo labels: istio.io/rev: default
このistio.io/revラベルがあることにより、そのNamespaceのPodにistio-proxyコンテナを自動的にインジェクションします。
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
Istio IngressGateway
Istio IngressGatewayはIstiodとは異なるNamespaceで動いており、インプレースアップグレードします。

Istio IngressGatewayはistio-proxyコンテナを持ちます。
$ kubectl get deployment -n istio-ingress NAME READY UP-TO-DATE AVAILABLE AGE istio-ingressgateway 1/1 1 1 47s
セキュリティのベストプラクティスでは、IstiodとIstio 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がアップグレード要件を満たしているかを検証します。
istioctl x precheckコマンド
istioctl x precheckコマンドを実行し、アップグレード要件を検証します。
問題がなければ、istioctlコマンドはNo issue ...の文言を出力します。
$ 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 x precheckコマンドはエラー文言を出力します。
例えば、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がこれを検知します。
MutatingWebhookConfigurationにはdefaultに対応するIstioのリビジョンが定義されており、kube-apiserverが特定のIstioのバージョンのServiceにWebhookを送信可能になります🎉
(2) 新Istiodのインストール
ここで実施すること
それでは、新Istiodをインストールします。
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!
revisionキーを使用したカナリアアップグレードでは、2つの先のマイナーバージョンまでアップグレードできます。
例えば、現在のIstioが
1.14.6であるなら、1.16系まで対応しています👍
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) Istio IngressGatewayをインプレースアップグレード
ここで実施すること
Webhookの宛先が1-15-4のServiceに変わったところで、Istio IngressGatewayをインプレースアップグレードします。
kubectl rollout restartコマンド
kubectl rollout restartコマンドを実行し、Istio 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コマンドについてkubectl getコマンドの代わりに、istioctl proxy-statusコマンドを使用して、アップグレードの完了を確認してもよいです。
今の状況は以下の通りです👇

Istio IngressGatewayのアップグレード時、マイクロサービスへのインバウンド通信が遮断されてしまうと思った方がいるかもしれません。
この点については、DeploymentがローリングアップデートでIstio IngressGatewayのPodを入れ替えるため、安心していただいて問題ありません🙆🏻
(5) 一部のNamespaceのistio-proxyコンテナをアップグレード
ここで実施すること
続けて、一部のNamespaceのistio-proxyコンテナをアップグレードします。
Podの再作成により、新Istiodのistio-proxyコンテナがインジェクションされるため。istio-proxyコンテナをアップグレードできます。
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コマンドについてkubectl getコマンドの代わりに、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に完全に入れ替わったため、アップグレードは完了です。
今の状況は以下の通りです👇

実は、他にもアンインストールしているものがあるのですが、話をわかりやすくするために、今回は言及していません🙇🏻
06. おわりに
Istioを安全にアップグレードするカナリア方式とその仕組みをもりもり布教しました。
Istioへの愛が溢れてしまいました。
これからIstioを採用予定の方は、Istioを安全にアップグレードするために十分に準備しておくことをお勧めします👍

