はじめに
先日、プロダクション環境のEKSをupdateしました
その際に得られた知見を共有します
※ベストプラクティスである保証はできません
元々の環境
- EKS v1.20
- terraform管理
- Flux2 v0.13.0
- Github Enterpriseと同期
アップデート戦略
EKSのアップデート戦略については、以下の記事がとても参考になります
https://zenn.dev/nameless_gyoza/articles/how-to-update-eks-cluster-safely
ざっくり、以下の方法があります
- in-place update
- in-place update(control planeのみ) + node migration
- ブルーグリーンデプロイメント
- カナリアリリース
弊チームでは、以下の理由でカナリアリリースを採用しました
運用サービスのSLOが高い
→ 何かあった時、すぐ戻せる状態にしたい
→ 新規クラスターを作成し、切り替える方式が良さそう
→ ブルーグリーンはリスクが大きい (問題があるとサービスが止まる)
→ カナリアリリースだ!
しかし、in-place updateの方が圧倒的に楽です
具体的に、カナリア・ブルーグリーンは以下の手順が必要になります
- 各種リソース複製(リソース名・バージョンが若干異なるので、レビュー必要)
- terraformリソース
- CI config
- k8sマニフェスト
- 動作確認
- 切り替え
- 後片付け(旧リソース削除)
また、カナリアリリースにおける重み付けは、ALBとRoute53のどちらかで実施することになります
https://dev.classmethod.jp/articles/alb-blue-green-deployment/
弊チームではALBを切り替えたくない事情があるため、ALBで行うことにしました
事前準備
EKS最新バージョン確認
EKSの最新バージョンがいくつか調べます
日本語だと情報が古いことがあるので、英語にして確認しましょう(1敗)
https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions.html
作業時(2022年9月)、最新はv1.23.7
らしいです
apirVersion確認
k8sリソースのapiVersion
の差分を確認します
非推奨・削除を検出するツールであるPluto
を用います
https://kakakakakku.hatenablog.com/entry/2022/07/20/091424
Pluto インストール
$ brew install FairwindsOps/tap/pluto
$ pluto version
Version:5.10.6 Commit:0c3ca2eb1e34d03473948eb0dc82fc2cd6b8c9cf
Pluto 実行
$ pluto detect-files -d . -o wide --target-versions k8s=v1.23.7
NAME NAMESPACE KIND VERSION REPLACEMENT DEPRECATED DEPRECATED IN REMOVED REMOVED IN
xxx-pdb xxx PodDisruptionBudget policy/v1beta1 policy/v1 true v1.21.0 false v1.25.0
yyy yyy CronJob batch/v1beta1 batch/v1 true v1.21.0 false v1.25.0
PDBとCronJobが非推奨バージョンを用いてるみたいです
以下のようにk8sマニフェストを修正すると良さそうです
- PodDisruptionBudget
- policy/v1beta1 -> policy/v1
- CronJob
- batch/v1beta1 -> batch/v1
addon バージョン確認
addonのバージョンを、以下コマンドを用いて確認します
$ aws eks describe-addon-versions --kubernetes-version 1.23
上記結果をもとに、以下のように修正すると良さそうです
- kube-proxy
- v1.20.4-eksbuild.2 -> v1.23.7-eksbuild.1
- coredns
- v1.8.3-eksbuild.1 -> v1.8.7-eksbuild.2
- vpc_cni
- v1.7.10-eksbuild.1 -> v1.11.3-eksbuild.1
Flux2 バージョン確認
弊チームのk8s環境は、Flux2を用いて`GitHub Enterpriseと同期しています。
Flux2がずっとアップデートできていないので、これを機にアップデートしていきます
$ brew upgrade fluxcd/tap/flux
$ flux --version
flux version 0.33.0
ローカルで準備ができたら、あとは以下コマンドで生成したリソースをapplyするだけです
(新クラスター完成後に行います)
flux install \
--version=v0.33.0 \
--namespace=flux-system \
--components=source-controller,kustomize-controller,helm-controller \
--components-extra=image-reflector-controller,image-automation-controller \
--log-level=error \
--export > crd.yaml
また、GHE連携できないバグがあるみたいです
(v0.31.0の時に再現しました)
https://github.com/fluxcd/source-controller/issues/652
上記issueによると、source-controlle
のv0.22.0
から発生しているようです。
うまく連携できない方は、上記搭載前のv0.27.4
にすると良いかもしれません
また、私の環境だとv0.33.0
でv0.27.4
をflux intall
することができなかったので、
v0.27.4
をローカルインストールする方法を載せておきます
$ brew tap-new iwakirik/taps
$ brew extract fluxcd/tap/flux iwakirik/taps --version 0.27.4
$ brew install iwakirik/taps/flux@0.27.4
カナリア用リソース名の考察
カナリアリリースのために、切り替え先のリソースを作る必要があります
こちらの名前について、最初はprefixにcanary
を付けていました
(例: xxx_cluster -> xxx_cluster_canary)
しかし、これだと、*_canary
リソースが残ってしまう形になります
これを解消するには、リリースを終えた後に、以下のような作業が必要になります
方法1: カナリア用リソースを削除する方法
元クラスター→新クラスター→元クラスターで2回カナリアリリースを行うイメージです
- 元リソースを更新する
- EKS version
- apiVersion
- addon version
- flux2 version
- 元clusterの動作確認
- ALBの向き先を戻す(カナリアで?)
- 様子見
- 問題無さそうなら、カナリア用リリースの削除
方法2: カナリア用リソースを使い続ける方法
- 元リソースを削除する
- カナリア用リソースの名前・パラメータ・ディレクトリ構造を、元リソースに合わせる(戻す)
- terraform import等の不毛な作業が必要
はい、リリース後の作業が沢山になっちゃいます
リリース完了したら、旧リソースを削除するだけで終わりにしたいです。
つまり、カナリアリリース用のリソースだけが残っても自然な形にしたいです。
そのため、リソース名のprefixはversion名にすることにしました
(例: xxx_cluster -> xxx_cluster_v1_23)
これだと、今後のupdateも自然なリソース名にすることができます
(xxx_20221001
といった日付も良いと思います)
実作業
リソース複製
各種リソースを複製しつつ、準備編で挙げたルール・バージョンで変更します。
弊チームでは、ざっくり以下リソースを複製・追記しました
- aws_eks_cluster
- aws_eks_node_group
- aws_iam_openid_connect_provider
- aws_launch_template
- aws_eks_addon
- aws_lb_target_group
- aws_autoscaling_attachment
- 各種k8sマニフェスト(Flux2 crd, Datadog等のHelm Chart含む)
- 各種CI設定
また、リソース複製から動作までの間に、いくつかトラブルに遭遇しました。
そちらの詳細と解決方法は後の章にまとめてあります。
動作確認
前手順で作成したクラスターで動作確認を行います。
ざっくり、主要な機能が動作するかといったことを検証しました。
また、以下のようなリスナールールを用意すると、
カナリアリリースの重み付けの中でもアクセスが任意で行えて便利でした。
resource "aws_lb_listener_rule" "<リソース名>" {
listener_arn = aws_lb_listener.<リスナー名>.arn
action {
type = "forward"
target_group_arn = aws_lb_target_group.<ターゲット名>.arn
}
condition {
http_header {
http_header_name = "canary"
values = ["true"]
}
}
}
カナリアリリース
動作確認も終えたので、実際にカナリアリリースを行います。
以下の重み付け推移で、1日毎にリリースを行いました。
0.1% -> 1% ->10% -> 50% ->100%
リソース削除
100%リリースから数日経過し、安定稼働を確認したら、旧リソースを削除します
遭遇したトラブル
ここからは、今回遭遇したトラブルと解決方法をまとめていきます。
たくさんリソースを生成するわけですから、どこかでミスは起こりそうですね。
初歩的なものも多いですが、自分への戒めとして残しておきます。
アプリケーションが動かない
新しく生成したクラスターのsecurity groupが不足していたようです。
追加して対応しました。
エラー:configmaps “aws-auth” already exists
k8s関連リソースの複製中に起こったエラーです。
どうやら、元々terraform管理していたaws-auth
と、クラスター生成時に生成されるaws-auth
が衝突しちゃったようです。
とりあえず、手動削除して再applyで解決しようとします
$ aws eks update-kubeconfig --region <リージョン名> --name <クラスター名> --profile <プロファイル名>
$ kubectl delete cm aws-auth -n kube-system
error: You must be logged in to the server (Unauthorized)
また怒られちゃいました。
どうやら、デフォルトのaws-auth
では、EKSを作成したユーザーしか権限を持っていないようです。
弊チームでは、terraformをGitHub管理し、CircleCIでterraform applyしています。
そのため、CircleCI userでしかterraform applyできません。
CircleCIにsshして上記手順を行い、解消しました。
レスポンスタイム急上昇
アプリケーションの動作が取れた後、数分間レスポンスタイムが急上昇する問題が発生しました。
調査に時間をかけたり、サポートへ問い合わせたりしましたが、結局未解決でした…
原因が分からなかった大きな理由として、ログを有効化していなかった
所にあります
(サポートから、「ログがないから調査できない」との返答がありました)
アプリレイヤーではログを取得してますが、ALBではログは有効にしていませんでした。
少なくとも安定稼働までは、各種リソースでログを有効にするのが良さそうですね。
まとめ
以下の反省点がありました
- update戦略はちゃんと考える
- リソース複製方式は想像よりずっと大変!
- 安定稼働までの間、ログを取る
- 原因を特定できないリスク
この辺りを反省して、次の業務に活かしていきたいところです。
(マサカリあったら遠慮なくお願いします!)