はじめに
2024/11に、ZOZOTOWNのマイクロサービスが稼働するプラットフォーム基盤上において、IstioのVersionをv1.21.4からv1.23.2にUpgradeしました。
本記事では、ZOZOにおけるIstio Upgradeの方針やその際の実施内容を紹介します。
ZOZOでのIstio Upgrade方針
Canary Upgrade
以前は、Control Planeやistio-ingressgatewayのUpgradeはIstio Operatorを活用したin-place upgrade手法を採用していました。しかし、in-place upgradeは手軽であるものの、マイナーバージョンを2つ以上一気にUpgradeすることがサポートされておらず、Istioの各バージョンのEOLが短いことから頻繁にアップグレード作業が必要なため、運用コストの問題がありました。また、たとえばzozo-api-gatewayのistio-ingressgatewayにおいて、もしもアップグレードに伴う障害が起きた場合はマイクロサービス側へのリクエストがすべてエラーになってしまう可能性がありました。
そこで、Canary upgrade with external traffic shifting (advanced)を参考に、ALB加重ルーティングを利用したControl Planeとistio-ingressgatewayのCanary Upgradeを採用することにしました。これにより、アップグレード作業の運用コストを抑え、万が一Upgradeに問題があった場合も影響を最小限にとどめることができます。
なお、Data Plane(Sidecarのistio-proxyがInjectされているマイクロサービス側)は、ZOZOではFlaggerを用いたProgressive DeliveryによりCanary Releaseが可能なため、特別な考慮は必要ありません。
Upgradeするタイミング
現在利用しているバージョンより2バージョン新しいバージョンがリリースされたら、Upgradeを実施します。
EOLを遵守しつつも、Upgrade頻度をなるべく下げて、工数を抑えるためです。
対象環境と順序
ZOZOでは、主にdev/stg/prdの3環境が存在します。当然ながら、すべての環境をUpgradeします。また、dev/stgもCanary Upgradeで実施します。dev/stgはin-placeでも問題ないのですが、prdと同じ手順で実施して問題がないことを確認してから、prdをUpgradeするためです。
今回のUpgrade実施内容
今回実施した、v1.21.4からv1.23.2へのUpgrade内容を以下に示します。
事前作業
リリースノートの確認
以下のドキュメントをすべて確認し、今回のUpgradeにより、問題となる変更がないことを確認しました。
- announcing
- https://istio.io/latest/news/releases/1.21.x/announcing-1.21.5/
- https://istio.io/latest/news/releases/1.21.x/announcing-1.21.6/
- https://istio.io/latest/news/releases/1.22.x/announcing-1.22/
- https://istio.io/latest/news/releases/1.22.x/announcing-1.22.1/
- https://istio.io/latest/news/releases/1.22.x/announcing-1.22.2/
- https://istio.io/latest/news/releases/1.22.x/announcing-1.22.3/
- https://istio.io/latest/news/releases/1.22.x/announcing-1.22.4/
- https://istio.io/latest/news/releases/1.22.x/announcing-1.22.5/
- https://istio.io/latest/news/releases/1.23.x/announcing-1.23/
- https://istio.io/latest/news/releases/1.23.x/announcing-1.23.1/
- https://istio.io/latest/news/releases/1.23.x/announcing-1.23.2/
- change-notes
- upgrade-notes
ただし、Istio Operatorが正式にv1.23でdeprecatedとなり、v1.24以降では利用できなくなるため、次回のUpgrade時には代替方法の検討が必要であることが分かりました。
IstioとKubernetesの対応バージョン確認
予定しているIstioバージョンがKubernetesバージョンをサポートしていることを確認しました。Istio Upgrade作業時のEKSクラスターのversionは1.29なので、問題ありませんでした。
https://istio.io/latest/docs/releases/supported-releases/#support-status-of-istio-releases
istioctlによるprecheck
Upgrade前にistioctl x precheck
を実行し、問題がないことを確認しました。
>istioctl x precheck --context {CONTEXT_NAME}
✔ No issues found when checking the cluster. Istio is safe to install or upgrade!
To get started, check out https://istio.io/latest/docs/setup/getting-started/
全体周知
slackで関係各所に、Istio Upgradeを実施する旨と予定スケジュールを事前に周知しました。
Upgrade作業
dev/stg/prd環境ごとに、6StepsでUpgradeを実施しました。
Step1: 新しいバージョンのControl Planeリソースとistio-ingressgatewayの作成
新しいバージョンのControl Planeリソースを作成しました。具体的には、以下のリソースです。
- ClusterRole
- ClusterRoleBinding
- Deployment
- Service
- ServiceAccount
- CustomResourceDefinition
リソースのmanifestは以下のコマンドを実行して取得しました。ただし、古いバージョンのmanifest上でCRDのみ削除する必要があります。削除しないと、同一名のCRDが両バージョンで重複してしまうからです。
$ curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.23.2 sh -
$ istio-1.23.2/bin/istioctl operator dump --revision 1-23-2 > istio-operator.yaml
動作確認として、istio-operator-1-23-2のPodで想定外のエラーログがないことを確認しました。
続いて、新しいバージョンのistio-ingressgateway関連のリソースを作成しました。具体的には、以下のリソースです。
- IstioOperator
- istiodのPod
- 各種マイクロサービスのistio-ingressgatewayのPod
- すべてのマイクロサービスでistio-ingressgatewayが存在するわけでなく、EKSクラスター外部から一次受けする可能性があるマイクロサービスにのみ用意しています
動作確認として、各Podで想定外のエラーログがないことを確認しました。
Step2: 新しいバージョンのistio-ingressgatewayに10%加重ルーティング
Ingressリソース(つまり、ALB)のannotationを更新し、新しいバージョンのistio-ingressgatewayに10%の加重ルーティングを行いました。以下は、zozo-api-gatewayのIngressリソースの例です。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: "internal-api-gateway-ingress"
annotations:
alb.ingress.kubernetes.io/actions.forward-multiple-tg: >
{ "Type":"forward", "ForwardConfig": {"TargetGroups": [ {"ServiceName":"api-gateway-istio-ingressgateway-1-21-4","ServicePort":"80","Weight":90},{"ServiceName":"api-gateway-istio-ingressgateway-1-23-2","ServicePort":"80","Weight":10} ] }}
Datadog Metrics Explorerを確認し、10%のリクエストが新しいバージョンに流れていることを確認しました。想定外のエラーが発生していないことも確認しました。
Step3: 新しいバージョンのistio-ingressgatewayに100%加重ルーティング
Ingressのannotationを更新し、新しいバージョンのistio-ingressgatewayに100%の加重ルーティングを行いました。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
alb.ingress.kubernetes.io/actions.forward-multiple-tg: |
{ "Type":"forward", "ForwardConfig": {"TargetGroups": [ {"ServiceName":"api-gateway-istio-ingressgateway-1-23-2","ServicePort":"80","Weight":100} ] }}
同じく、Datadog Metrics Explorerを確認し、100%のリクエストが新しいバージョンに流れていることを確認しました。想定外のエラーが発生していないことも確認しました。
Step4: NamespaceのRevisionラベルを修正する
Data Planeのnamespaceのmetadata.labels.isito.io/rev
に新しいバージョンのRevisionを指定しました。以下は、zozo-api-gatewayのNamespaceリソースの例です。
apiVersion: v1
kind: Namespace
metadata:
name: api-gateway
labels:
istio.io/rev: 1-23-2 # here
Step5: Data Planeリソースを再起動する
Data PlaneのDeploymentのannotationを更新し、Progressive Deliveryを実施します。
以下は、zozo-api-gatewayのDeploymentリソースの例です。
apiVersion: apps/v1
kind: Deployment
namespace: api-gateway
metadata:
name: api-gateway
spec:
template:
metadata:
annotations:
last-canary-retry-timestamp: "2024-11-06T19:02:00+0000" # here
...
Podが生まれ変わり、SidecarにInjectされているistio-proxyが新しいバージョンのものになったことを確認しました。
Podが作成される際に、KubernetesのAdmission Controllerがnamespaceに付与されているistio.io/rev
ラベルの値を読み取り、そのrevisionのIstio Control Planeに接続して、そのバージョンのistio-proxyをPodにInjectしています。
Step6: 古いバージョンのControl Planeとistio-ingressgatewayを削除する
まず、古いバージョンのリソースをマニフェスト上から削除しました。
ZOZOではFluxによるGitOpsを導入しているのですが、ZOZOではIstio関連のリソースはFlux対象外のため、自動では削除されません。したがって、以下のリソースを手動で削除しました。
- istiooperator
- clusterrolebinding
- clusterrole
- service
- serviceaccount
- istio-ingressgateway
- deployment
- service
- horizontalpodautoscaler
- poddisruptionbudget
- serviceaccount
- istiod
- poddisruptionbudget
- clusterrolebinding
- clusterrole
- MutatingWebhookConfiguration
- ValidatingWebhookConfiguration
以下のコマンドを実行し、古いバージョンのリソースが残っていないことを確認しました。
$ k get all -A | grep 1.21.4
$ k get clusterrolebinding,clusterrole,pdb,mutatingwebhookconfiguration,validatingwebhookconfiguration,sa -A| grep istio | grep 1.21.4
トラブル
CRDを削除してしまった
dev環境のUpgrade時に、誤ってstg/prd環境のistioperatorのCRDを削除してしまいました。本来は、古いバージョンのCRDをmanifestから削除する際には、同時に新しいバージョンのCRDを追加するべきだったのですが、stg/prdではそれが抜けていました。ZOZOのプラットフォーム基盤を構成管理するGitHubリポジトリでは、baseディレクトリで管理し、各環境でkustomizationによるoverlayをする構成になっています。dev環境のUpgradeのPull Request(以降、PR)では、baseからCRDを削除していたにもかかわらず、devの新しいバージョンのCRDしか追加していませんでした。このようなPRを作成してしまった理由は、前回のUpgrade時のPRをそのまま模倣してしまったからです。前回までのUpgradeでは、ZOZOのplatform基盤ではKubernetesのpruneを利用しない方式としていました。したがって、manifestからリソースを削除しても自動では削除されず、全環境のUpgradeが終わってからまとめて削除する手筈でした。しかしながら、今回のIstio Upgrade作業の少し前くらいからpruneを利用する方式に変わっており、baseのマニフェストからCRDを削除したタイミングで、クラスター上のCRDリソースに対して削除処理が走ってしまいました。
ただし、実際には、CRが残っていたため、CRDは削除中の状態(deletionTimestampが設定されているが存在する状態)で留まってくれていました。CRDが削除されなかったので、istio-ingressgatewayなどのPodはかろうじて残っており、サービス影響はありませんでした。不幸中の幸いです。しかしながら、その影響でistio-ingressgatewayなどのリソースを変更することができない状態になってしまいました。この状態を解消するためにIstio OperatorのCRとCRDを安全に再作成することにしました。実施した復旧手順は以下です。
- baseディレクトリを使用せずに、各環境のディレクトリでControl Planeのすべてのリソースを定義し、manifest上でstg/prdの1-21-4のCRDを追加した。
- istio-operatorのdeploymentのreplicasを0に変更し、Podを削除した。
kubectl scale deployment istio-operator-1-21-4 --replicas=0 -n istio-operator
- IstioOperatorのCRとCRDを削除した
- 具体的には、IstioOperator CRのfinalizersを空のスライスにした。
kubectl patch istiooperator istio-control-plane-1-21-4 -n istio-system -p '{"metadata":{"finalizers":[]}}' --type=merge
- 削除されてしばらくすると、自動でCRとCRDが起動する。これは、手順1で追加したmanifest上の1-21-4のCRDをFluxが検知し、CRDを作成するため。
Step6でistiooperatorが削除されない
Step6の手順で、古いバージョンのistiooperatorリソースを手動で削除したのですが、削除されませんでした。これは、kubectl edit
で該当リソースのfinalizerの値を空のスライスにすることで削除されるようになりました。ただし、deleteコマンドを実行してから、少し時間(数分ほど)待ってからeditしないと、予期せずゴミリソースが残ってしまうため、注意が必要でした。
いずれにせよ、今後はIstio Operatorがdeprecatedになるため、あまり気にする必要はなくなります。
最後に
本記事では、ZOZOでのIstio Upgradeの方針ややり方、そして今回のUpgrade実施内容について紹介しました。
Istioの運用をされている方の参考になれば幸いです。