Inside of LOVOT

GROOVE X 技術ブログ

クラスタ式年遷宮: Autopilot へ移行の巻

この記事は、Groove Xアドベントカレンダー2022 14日目の記事です。

こんばんは、junya です。

先週、酷い熱が出てしまい、12/14 投稿予定の記事がすっかり遅くなってしまいました。 今回は、この夏から秋に取り組んだ GKE クラスタAutopilot への移行について紹介します。

Autopilot とは?

GCP 上のマネージドな Kubernetes サービスです。

コントロールプレーンの管理に加えて、GKE がノードを自動で管理してくれます。また、設定次第で Anthos Service Mesh と呼ばれるマネージドな Istio も管理してくれます。

クラスタ式年遷宮とは?

意図してか何なのか、2年に1回くらいの頻度で k8s クラスタを引っ越ししています。

  • 2018年夏頃: 初代クラスタ構築
  • 2020年夏頃: 共有 VPC 用にネットワークを再設計してクラスタを引っ越し
  • 2022年夏頃: Autopilot へ引っ越し

社内ではこれを伊勢神宮の儀式になぞらえ、クラスタ式年遷宮と呼んでいます。2024年頃にまた引っ越すんだろな、きっと。

式年遷宮の背景

今回の式年遷宮のきっかけは、Istio on GKE のサービス終了(2022年9月末まで)でした。 はじめはマイグレーションで対応しようと思っていたのですが、初期の頃の設定のゴミが残っていたりで移行が思いの外大変そうだったので、まるっとクラスタごと立て直して、引っ越すことにしました。

クラスタの構築手順

詳細は省きますが、以下の手順で新しいクラスタを構築しました。

  • クラスタ用に subnet を作成
  • Docker イメージの管理を Container Registry から Artifact Registry へ移行
  • プロジェクトで fleet を有効化
  • GCP Console でクラスタを作成
    • オプションで Anthos Service Mesh (マネージドな Istio) を有効化
  • クラスタのサービスアカウントに Artifact Registry への Read 権限を付与
  • 古いクラスタから Secret を移行
  • Namespace で istio injection を有効化

Autopilot への移行で発生した課題と解決策

Autopilot への移行には色々と罠があり、サポート契約を結んでいる クラウドエース さんや Google と連携しながら、1つずつ課題を解決していきました。ハマりポイントと解決策を紹介します。

コンソールで表示されるコマンドが実行できない

GCP のコンソールって結構便利で、GUI でリソースを設定しようとすると、それと等価なコマンドを表示してくれるじゃないですか。 なのに何故か、表示されたコマンドを実行しても失敗します。gcloud container clusters create-auto--labels オプションを与えることになっているんですが、実際のコマンドにはそのオプションが存在しない(2022年12月現在)ので、諦めて GUI 上でクラスタを作成しました。

ノードが1つも作られない

共有VPC上で Autopilot を構成したのですが、以下のような Warning が発生して Pod の作成に失敗しました。

kube-system 28s Warning FailedScheduling pod/antrea-controller-horizontal-autoscaler-76dc654676-wg688 no nodes available to schedule pods

確認したところ、クラスタを作成した共有VPCのサブネットで、「限定公開のGoogleアクセス (Private Google Access)」が無効になっていて、サブネット内から GoogleAPI にアクセスできないのが原因でした。「限定公開の Google アクセス」を有効化して、無事に解決しました。

IP アドレスが枯渇して新しい Pod が作られない

以下のような Warning が発生して、 Pod が ContainerCreating から先へ進まなくなりました。

Warning FailedCreatePodSandBox 4m21s (x77 over 100m) kubelet (combined from similar events): Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "2ffd683f686d0c849515ecca19a3ebd7c013dcb6569c085844aca234738d12dd": failed to allocate for range 0: no IP addresses available in range set: 10.8.65.193-10.8.65.254

こちらはプラットフォーム側のバグ で、対策済みの 1.22.15-gke.100 に手動アップグレードすることで解決しました。

完全マネージドの場合、こういう問題が起きた時にノードを自前では調べられないのが歯がゆいと同時に、プラットフォーム側が調査して解決してくれるのが頼もしくもあります。

LimitRanges を参照してくれない

コンテナのデフォルトのリソース設定を LimitRanges で管理していたのですが、 Autopilot で割当後のリソースを見ると、設定値を考慮してくれていませんでした。調査を依頼したところ、これは GKE Autopilot の仕様で、 LimitRanges は機能しないとのことでした。

アプリケーションへのリクエストがキャンセルされてしまう

開発環境・ステージング環境では正常に動作していたアプリケーションを、本番環境で istio injection を有効にして動かしたところ、 Go の context canceled が多発して、多数のリクエストをさばけなくなりました。試行錯誤したところ、 istio sidecar に割り当てている CPU が足りていないのが原因で、 sidecar への CPU の割当を増やすことで解決しました。

当てられるべき以上のリソースが割り当てられる

Autopilot で以下のような Annotation を用いて sidecar のリソースを制御していたのですが、

sidecar.istio.io/proxyCPU: "120m"
sidecar.istio.io/proxyCPULimit: "120m"
sidecar.istio.io/proxyMemory: "150Mi"
sidecar.istio.io/proxyMemoryLimit: "150Mi"

Autopilot で割り当てられるべきリソース 以上のリソースを確保してしまい、余計なコストがかかってしまいました。

本来は Pod における要求リソースの合計に対して、 CPU は 250 mCPU 単位、メモリは 0.5GiB 単位で繰り上げられるのですが、 Annotation によって istio sidecar が生成されるタイミングが Autopilot の制御に対して遅延するため、アプリケーションコンテナの作成時と、サイドカーのコンテナの作成時の2段階で Autopilot のリソース補正が走ってしまっていました。

こちらはサポートに相談し、 Customizing Injection の機能によって、 Annotation ではなく以下のように Deployment として明示的にコンテナを指定することで、解決しました。

spec:
  containers:
  - name: istio-proxy
    resources:
      limits:
        memory: 5Gi
      requests:
        cpu: 200m
        memory: 5Gi

クラスタ管理の費用がかかりすぎる問題

ここまでの設定で一通り動くようにはなったのですが、今まではざっくりとノードを確保して Pod を沢山動かしていたのが、Pod 単位でリソースが厳密に管理されるようになり、しかも、Pod ごとの最低リソースが 250m CPU, 0.5 GiB ということもあって、クラスタ管理の費用が爆増してしまいました。特に、リクエスト負荷の低い、開発環境やステージング環境のクラスタでの費用の増大が顕著でした。

その対策の1つとして、以下のような設定を Deployment に追加して Spot Pod を使うようにしました。

terminationGracePeriodSeconds: 25
affinity:
  nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: cloud.google.com/gke-spot
            operator: In
            values:
              - "true"

Spot インスタンスを利用することで、単価が30%になる想定で、その効果はこれから確認するところです。

今後の予定

まだまだ Autopilot や Service Mesh のポテンシャルを活かしきれていないので、勉強してこれからもっと活用していきたいなと思ってます。 また、 Workload Identity の導入も進めたいです。

こんな感じで k8s クラスタを管理しているのですが、アプリケーション開発と兼任で3~4人で回している状態なので、一緒に開発してくれるインフラエンジニア・バックエンドエンジニアの方を絶賛募集中です。

ご興味ある方は、是非お気軽にご応募下さい!

recruit.jobcan.jp