GKEクラスタ内部であってもイベントを直接転送できるEventarcの意外な落とし穴

こんにちは。エンジニアの岩松です。たまにVisionalグループの軽音部でベースを弾いています。最近エフェクターを買ってみたのですが、欲しいものが次々と増えてきて困っています。

この記事はGCP(Google Cloud Platform) Advent Calendar 2023 15日目の記事となります。( Assured Tech Blog でアドベントカレンダーは初参加! 🎉 )

TL;DR

  • Eventarcを使えばイベントドリブンなリソース連携をマネージドかつ手軽に管理できる
  • イベント転送先にはGKEクラスタ内部のエンドポイントも直接指定できるのでPub/Subや Cloud StorageのイベントをPush型で処理しやすくなる
  • Terraform経由だとEventarcが作成ができず、関連リソースが🧟‍♂️になるバグと遭遇したが、サポートとやりとりを続けて解消できた

やりたかったこと

AssuredではGoogle Kubernetes Engine(GKE)によって複数のアプリケーションを管理しています。以前紹介したインフラ構成では書かれていませんが、API処理とは別に重たい処理を担当するアプリケーションも存在しています。(以下JobAppとします)

tech.assured.jp

APIからJobAppを呼び出すにあたり、可用性を考慮してPub/Subを挟んでいたのですが、Pull型の実装では実行速度やリソース効率に限度があるうえ、ビジネス要件の観点からもPush型の実装にできないかと考えていました。

flowchart LR
  A1["API1"]
  A2["API2"]
  B["JobApp"]
  PS["Pub/Sub"]

    subgraph GKE Cluster
    A1
    A2
    B
  end
  
  A1-->|Send|PS
  A2-->|Send|PS
  B-->|Pull|PS
  B-->B

これを

flowchart LR
  A1["API1"]
  A2["API2"]
  B["JobApp"]
  PS["Pub/Sub"]

    subgraph GKE Cluster
    A1
    A2
    B
  end
  
  A1-->|Send|PS
  A2-->|Send|PS
  PS-->|Push|B

こうしたい

しかし、今回利用しているGKEクラスタは限定公開であるため、これをセキュアな形で実現しようとすると意外と手間がかかります。JobAppに内部アクセス用のIPアドレス(VPC)を紐付ける。Pub/SubのPushイベントを処理するCloud Functionsを用意しサーバーレス VPC と紐づけ、内部用VPCと連携する。といった具合です。

flowchart LR
  A1["API1"]
  A2["API2"]
  B["JobApp"]
  PS["Pub/Sub"]
  CF["Cloud Functions"]
  SIP["Static IP Address"]

    subgraph GKE Cluster
    A1
    A2
    B
  end

  subgraph Serverless VPC
    CF
  end

  subgraph Internal VPC
    SIP
  end
  
  A1-->|Send|PS
  A2-->|Send|PS
  PS-->|🔒Push|CF
  CF-->|🔒Request|SIP
  B---SIP

真面目にやると多分こうなる

Pull型と比較してPush型を実現するのは正直めんどくさい…。ここで登場するのがEventarcです!

Eventarcとは

EventarcはGoogle Cloudが用意しているマネージドサービスであり、 Pub/SubやCloud StorageといったGoogle Cloudリソースまたはサードパーティリソース(Datadog等)からのイベントを起点に様々なエンドポイントを呼び出せる機能となります。AWSだとEventBridgeが近しい機能ですが、GKE(Kubernetes)内部のエンドポイントを直接指定できるという点が差分の一つかと思います。したがって前述のケースであっても、GKEの外側にエンドポイントを用意せず簡単にセキュアなPush型アーキテクチャを実現できるというわけです。

flowchart LR
  A1["API1"]
  A2["API2"]
  B["JobApp"]
  C["Eventarc Pod"]
  PS1["Pub/Sub"]
  PS2["Pub/Sub"]

    subgraph GKE Cluster
    A1
    A2
    B
    subgraph Eventarc managed
      C
    end
  end

    subgraph Eventarc managed namespace
    PS2["Pub/Sub"]
  end

  A1-->|Send|PS1
  A2-->|Send|PS1
  PS1-->|Push|PS2
  PS2-->C
  C-->B

Eventarc「…きこえますか…JobApp…今…GKEクラスタ内部に直接呼びかけています…」

Eventarcを作成するとGKEクラスター内に自動でnamespaceおよびPod、専用のPub/Sub Subscriberが作られます。「ちょっとボタンをポチポチするだけでこんな設定できるなんで、Eventarc便利すぎる…!」ということで本番環境への適用を考慮しTerraformの設定を始めたところで落とし穴が発生…! 😱

発生事象

EventarcはTerraformで管理することも可能で、比較的簡単に設定を記述することができます。 ところが実際にEventarcを作成しようとすると実行が終わらない…。

永遠に終わらないapply

これはおそらくEventarcに設定するサービスアカウントの権限が足りていなかったことが原因だったと思われ、操作を強制終了すれば問題はありませんでしたが

がリソースとして残ってしまいました。しかも、これらは通常の削除操作を受け付けてくれません。トリガーに削除操作を行うと長い時間をかけた後に「不明なエラーです。」というエラーを返してくるだけ、Kubernetes namespaceは削除操作を受け付けたフリで終わるという状態です。🧟‍♂️

Terminatingなnamespaceが残り続ける

削除操作後の様子。deletedとは。

テクニカルサポートの活用

残ったリソースが操作できなくなってしまい詰んでしまったのでGoogle Cloudのテクニカルサポートを活用していくことにしました。テクニカルサポート問い合わせ後の流れは以下となります。

  • Assuredチーム: 問い合わせ開始、状況を説明
  • サポートチーム: 簡単にリソースが削除できないと判明
  • サポートチーム: Eventarcスペシャリストへ調査を依頼
  • Eventarcスペシャリスト: Eventarc内部での問題であることが判明
  • Eventarc開発チーム: トリガーを削除できるように修正対応いただく
  • サポートチーム: Kuberenetes namespaceの削除方法を教示いただく

サポートからのメッセージに返信し忘れているとケース(問い合わせ)がクローズされてしまうので、素早くレスポンスしていく必要があります。こういったやりとりにおいてはオリバーさんはじめ、チームのみんなにも助けてもらいました。 🙇

現在状況と問題の解消手法

本記事の執筆時点(2023/12/15)では適切に権限を付与していれば問題なくTerraformでEventarcトリガーを作成できます。しかし、当初の事象と同様に権限が不足した状態で操作を行うとKubernetes namespaceが残り続ける問題が依然発生します(トリガーは消えるようになった)。この場合は以下の手順でnamespaceを削除できます。

$ kubectl get ns [NAMESPACE] -o yaml > [NAMESPACE].yml
$ vim [NAMESPACE].yml # spec.finalizer を削除
$ kubectl proxy # 以下別タブ等で操作
$ curl -H "Content-Type: application/yaml" -X PUT --data-binary @[NAMESPACE].yml http://127.0.0.1:8001/api/v1/namespaces/[NAMESPACE]/finalize

おわりに

こういった問題こそありましたが、結果的にEventarcの問題が修正される形となったのでGoogle Cloudの改善に貢献できて良かったです。今回作られたEventarcも問題なく稼働しており、管理すべきリソースがシンプルにまとまっているので、マネージドサービスの恩恵を受けられている実感があります。イベント転送先はGKEに限らずCloud FunctionやCloud Runにもできるので、今後もどんどん活用していきたいと考えています。

一方、事象の発端においては必要な操作権限の確認不足が問題として挙げられます。「操作が中断されるはずだから問題ない」で済ませず、事前にちゃんと確認していく必要があると感じました。Google Cloudリソースの内部仕様ももっと勉強していきたいと思います。💪

参考資料

cloud.google.com

registry.terraform.io

cloud.google.com

cloud.google.com