ChatOps - TECH PLAY - TECH PLAY

TECH PLAY

ChatOps

イベント

該当するコンテンツが見つかりませんでした

マガジン

技術ブログ

はじめに こんにちは、EC基盤開発本部SRE部カート決済SREブロックの金田です。テックリードとして、ZOZOTOWNのカート決済機能のリプレイスや運用を担当しています。 カート決済SREブロックでは、Slackを起点としたChatOpsで運用作業の自動化に取り組んでいます。以前の記事で紹介したAWS Chatbotベースの基盤を約2年運用する中で、アクセス制御とツール追加コストの課題が見えてきました。本記事ではその課題と、Argo Eventsを軸にChatOps基盤を再構築した取り組みを紹介します。 目次 はじめに 目次 背景・課題 課題1: アクセス制御が粗い 課題2: ツール追加のハードルが高い 新基盤の要件 新しいChatOps基盤の全体像 Slackの制約と信頼チェーンによる認可 制約1: Workflowの投稿からは実行者が分からない 制約2: 3秒ルールとリトライ 制約3: 署名検証には生のリクエストボディが必要 制約4: Request URLはSlack Appごとに1つ 信頼チェーンで実行者を特定する Gatekeeperによる認可、承認、監査の一元化 SSMポリシーによる宣言的な認可 危険な操作への承認フロー 許可も記録する構造化監査ログ なぜArgo Eventsなのか Argo Eventsを本番運用に載せる設計判断 EventBusは高可用性をあえて求めない 監視の組み込み Sensorはルーティングだけ、実行権限はexecuteだけ K8s Jobによるツール実行機構 PodTemplateでツールのJob定義を配布する マルチテナント設計とツール追加の体験 共通の入口と、テナントごとの実行スタック ツールに渡す標準ペイロード ツール追加はSSMポリシーとマニフェストだけ 導入の効果 まとめ 背景・課題 カート決済SREブロックでは、過熱商品(発売開始時などにアクセスが急激に集中する人気商品)の登録などの運用作業をSlackから実行できるようにしています。従来の基盤は「Slack Workflow → AWS Chatbot → AWS Lambda」という構成でした。非エンジニアでもSlack Workflowのフォームに入力するだけで運用ツールを実行できます。構成の詳細や、コマンドラインツールをLambda関数化した工夫は以下の記事で紹介しています。 techblog.zozo.com 本記事は、この構成を約2年運用した先の話です。ツールと利用者が増え、この先さらに運用業務のツール化を進めるにあたって、次の2つの課題が無視できなくなりました。 課題1: アクセス制御が粗い AWS Chatbotのアクセス制御は、チャネルに紐付くIAMロールとガードレールポリシーが基本です。前回の記事でも当時できる範囲の制御を紹介しましたが、次の要素は仕組みとして実現できませんでした。 「どのチャネルで、誰が、どのコマンドを」実行できるかという、コマンド単位かつユーザー単位の認可 削除などの危険な操作に対する、実行前の承認フロー 「誰が何を実行し、なぜ許可されたのか」を機械的に追跡できる監査ログ 実行したコマンドはSlackのメッセージとして残ります。しかし、それは人が目視で追える履歴であって、拒否された実行や許可の判断根拠まで含めて検索や集計ができる監査ログではありません。運用ツールの中にはデータを書き換える操作も含まれるため、利用者が増えるほどこの粗さがリスクになっていきました。 課題2: ツール追加のハードルが高い AWS Chatbotから実行できるのはLambdaをはじめとするAWSリソースの操作だけです。運用ツールをChatOpsに載せるには、1コマンドで完結するような簡単な処理でも、ツールごとにLambda関数を実装する必要がありました。これが新しいツールを導入する際の障壁になっていました。 さらに、カート決済SREブロックの運用ツールにはEKS上のJobとして動かしたいものもあります。コンテナイメージとして完成しているツールでも、ChatOpsに載せるためだけにLambdaへ移植するのは本末転倒です。実行形態がLambdaに縛られていることが、この障壁をさらに高くしていました。 新基盤の要件 課題を整理して、新しいChatOps基盤に求める要件を次のように定めました。 分類 要件 認可 コマンド、ユーザー、チャネルの単位で実行可否を制御できる 承認 危険な操作は、権限を持つ承認者の承認を経てから実行される 監査 許可も拒否も、すべての実行判断が構造化ログとして残る 実行形態 K8s JobとLambdaのどちらでもツールを実装できる UX 非エンジニアは従来と同じSlack Workflowのフォームから実行できる 拡張性 ツールの追加や利用チームの拡大時に、基盤側の変更が不要である 新しいChatOps基盤の全体像 この要件を満たすために構築した、新基盤の全体像を示します。 図の中心は、EKSの手前に置いた Gatekeeper Lambda です。認証、認可、承認、監査を一手に担う関門です。その先のEKSでは、 Argo Events が認可済みのイベントを受けてツールを起動します。 1リクエストの流れを追うと次のようになります。 利用者がSlack Workflowのフォームに入力して送信すると、WorkflowがBotとして所定のチャネルにコマンドを投稿する Slack AppがEvents APIでこの投稿を受け取り、API Gateway経由でGatekeeper Lambdaに配送する Gatekeeperが署名検証、実行者の特定、ポリシー評価を済ませ、許可した場合のみペイロードを正規化してEKSのinternal ALBへ転送する ALBの先にいるArgo EventsのWebhook EventSourceがイベントを受け、EventBusへ流す Sensorがイベントの種別と承認要否を見てK8s Jobを作成する。承認が必要なコマンドは承認リクエストを投稿するJob(dispatch)へ、それ以外は実行を担うJob(execute)へ振り分ける ツール本体がK8s JobまたはLambdaとして実行され、結果をSlackのスレッドに通知する この流れはWorkflow経由のものです。エンジニア向けには、Botへの直接メンションとSlash Commandの経路もあり、いずれも同じAPI GatewayとGatekeeperを通ります。本記事では、非エンジニアも使うWorkflow経由を軸に説明します。 設計の方針は次の通りです。認証、認可、承認、監査は、実行基盤であるEKSの手前のGatekeeperで一元化します。EKS側はArgo Eventsでイベントを受けて実行に専念します。Slackとの接点は単一のSlack Appに集約し、ツールを追加してもSlack App、API Gateway、Gatekeeperには手を入れない構造にします。 この分担により、Argo Events以降には「誰が何を実行するか」が確定した正規化済みイベントだけを流します。SlackプロトコルとのやりとりはGatekeeperで完結しているため、EventSourceはSlackの署名を検証する必要がありません。Gatekeeperと共有するBearerトークンで、Gatekeeper自身を認証するだけで済みます。 Slackの制約と信頼チェーンによる認可 設計で最初に向き合ったのは、Slackプラットフォーム特有の制約です。 制約1: Workflowの投稿からは実行者が分からない Slack Workflowが投稿したメッセージの送信者は、実行した人ではなくWorkflowのBotです。Events APIで受け取るイベントの user フィールドを見ても、そこにいるのはBotであって「フォームに入力した人」ではありません。コマンド単位かつユーザー単位の認可には実行者の特定が必須なので、これは基盤の成立に関わる制約です。 制約2: 3秒ルールとリトライ SlackのEvents APIは、3秒以内に応答がなければ配送失敗とみなしてリトライします。Gatekeeperの中で重い処理はできません。そこで、EKSへの転送タイムアウトを2秒に設定し、認可判断と転送だけを行って即座に応答を返す構造にしました。また、リトライによる同一イベントの再配送に備えて、Gatekeeperで重複を排除しています。 制約3: 署名検証には生のリクエストボディが必要 Slackのリクエスト署名は、タイムスタンプとリクエストボディを連結した文字列から計算されます。リクエストの認証は API GatewayのLambda Authorizer へ分離するのが定石ですが、Authorizerにはリクエストボディが渡されません。つまりSlackの署名検証はAuthorizerでは実現できません。そのため認証を分離せず、署名検証から認可までをGatekeeperという1つのLambdaに集めています。 制約4: Request URLはSlack Appごとに1つ SlackがAppへリクエストを届ける先のURL(Request URL)は、機能ごとにApp全体で1つです。Events API、Slash Commands、Interactivity(承認ボタン)のいずれも、テナント別に別のURLを設定できません。そこで全テナント共通の単一エンドポイント POST /chatops ですべてを受け、どのチームのコマンドかの解決はGatekeeperがchannel-mapで行う構造にしました。経路ごとにペイロードの形式は異なりますが、Gatekeeperがパースして同じ正規化ペイロードに変換します。 一方、環境の軸では同じ制約に悩まされます。dev、stg、prdでURLを分けられないため、Slack Appは環境別に作ります。Appの複製にはApp Manifestが使え、設定ファイルをリポジトリ管理すればレビューも可能です。さらに、Slash Commandの名前はワークスペース内でグローバルです。そのためdevとstgのコマンド名には環境サフィックスを付け(例: /example-tool-dev )、Gatekeeperがコマンドの解釈時にサフィックスを除去します。 つまり「単一Slack App」とは、同一環境内のテナント間で1つという意味です。テナント軸は1つのAppに集約し、環境軸はAppごとに分離します。この線引き自体が、Slackプラットフォームの制約から導かれた設計です。 信頼チェーンで実行者を特定する 制約1で挙げた「実行者が分からない」問題への答えが、信頼チェーンです。 Workflowのフォームには実行者を示す入力欄があり、投稿されるメッセージ本文に実行者のユーザーIDが埋め込まれます。ただし、本文に書かれた実行者を無条件に信じると、任意のユーザーが他人になりすませてしまいます。そこで「本文中の実行者情報を信頼してよい条件」を、次の連鎖として構成しました。 Slackの署名検証により、イベントがSlackから来たことを保証する イベントの bot_id が、事前に登録した「信頼できるWorkflow」のものと完全一致することを確認する 要求されたコマンドが、そのWorkflowに許可されたコマンドの範囲内であることを確認する ここまで通って初めて、本文中の実行者情報を「Workflowのフォームが埋め込んだ値」として信頼する bot_id はWorkflowごとに払い出される識別子で、そのWorkflow以外は同じ bot_id で投稿できません。人間が手打ちで偽装したメッセージは投稿者が人間のuser_idになるため、Bot投稿とは必ず区別できます。実行者の情報は、Workflowのテンプレートに埋め込んだ「このワークフローを開始した人」という変数でSlackが自動的に挿入します。フォームの入力者はこの値を偽装できません。そしてWorkflowの編集コラボレーターを管理者に限定することで、テンプレート自体の改変も防いでいます。この運用条件までを含めて、実行者情報の信頼が成立します。 一方、Botを経由しない人間の直接メンションでは、イベントの user をそのまま実行者として使います。この経路では本文中の実行者情報を信頼しません。経路によって、何を信頼するかを切り替えています。 この認可はfail-closedに設計しています。 bot_id が未登録なら拒否、Workflowに許可されていないコマンドなら拒否、実行者情報が取れなければ拒否です。拒否はすべて理由コード( untrusted_bot 、 requester_missing など)付きでログに残します。 なお、移行にあたって既存のSlack Workflowは作り直さず、投稿先の差し替えで対応しました。Workflowを作り直すと bot_id が変わってしまうためです。 bot_id を登録制にする設計は、こうした運用上の注意点とセットになります。 Gatekeeperによる認可、承認、監査の一元化 信頼チェーンで実行者が特定できたら、次はポリシー評価です。Gatekeeperが1つのリクエストに対して行う処理は、順に次の通りです。 Slack署名の検証と、リトライによる重複リクエストの排除 チャネルIDからのテナント解決 信頼チェーンによる実行者の特定 ポリシー評価(チャネル、コマンド、ユーザー、グループ、Workflow) 承認要否の判定と、正規化ペイロードのEKSへの転送 1〜3はここまでに説明した通りです。残るは4と5、すなわちポリシーによる認可と承認フロー、そしてその記録です。 SSMポリシーによる宣言的な認可 認可のルールはAWS Systems Manager Parameter Store(以下、SSM)に置いています。構造は2段階です。 1つ目は /chatops/channel-map で、SlackのチャネルIDからテナント(チーム)とチャネルラベルを解決します。チャネルIDという環境依存の値を持つのはこのマップだけで、各ツールのポリシーはラベルだけを参照します。 { " C0123456789 ": { " tenant ": " zozo-cart ", " label ": " ops " } } 2つ目は /chatops/policy/{テナント}/{ツール} で、ツールごとに次を宣言します。 コマンドごとの許可チャネル(ラベル)と、許可ユーザーおよび許可グループ(SlackのUser Groupを利用) コマンドごとの承認要否と承認者 ツールの実行形態(K8s JobまたはLambda)と失敗時の連絡先 { " invoke ": { " type ": " k8s-job " } , " failure_contact ": " <!subteam^S0AAAAAAA> ", " commands ": { " info ": { " allowed_channels ": [ " ops " ] , " allowed_users ": " * " } , " update ": { " allowed_channels ": [ " ops " ] , " allowed_groups ": [ " S0BBBBBBB " ]} , " delete ": { " allowed_channels ": [ " ops " ] , " allowed_groups ": [ " S0BBBBBBB " ] , " requires_approval ": true , " approver_groups ": [ " S0CCCCCCC " ] , " allow_self_approval ": false } } , " trusted_workflows ": [ { " bot_id ": " B0XXXXXXXXX ", " allowed_commands ": [ " update " ]} ] } コマンドのキーは、ツールが実際に受け付けるサブコマンド名と一致させます。ツール側も宣言にないコマンドを拒否するため、ずれているとGatekeeperの認可を通ったコマンドさえツールが弾いてしまいます。信頼チェーンで使うWorkflowの bot_id も、このポリシーの trusted_workflows で登録します。 ユーザー単位の認可は、個人ID( allowed_users )とSlack User Group( allowed_groups )のORで判定します。メンバーの異動のたびにポリシーを書き換えずに済むよう、基本はUser Groupで宣言し、GatekeeperがSlack APIでメンバーを展開して判定します。この展開に失敗した場合は、許可側に倒さず拒否します(fail-close)。判断材料が欠けた状態で通すと、認可そのものが形骸化するためです。 ポリシーの追加や変更はSSMパラメータの更新だけで済み、Gatekeeperのデプロイは不要です。ただし、誰でも更新できるわけではありません。ポリシーはCloudFormationで管理し、変更にはPRレビューを必須にしています。信頼チェーンの起点になる trusted_workflows への bot_id の登録も、このレビューを通ります。認可ルールを書き換える手段が野放しでは、認可の仕組み全体が意味を失うためです。 GatekeeperはSSMとSlack APIのスロットリングを避けるため、ポリシーとUser Groupを5分間キャッシュします。変更の反映が最大5分遅れることは、運用上許容できるトレードオフと判断しました。この遅延は、緊急のアクセス権限の剥奪でも同じだけ発生します。即時に止める必要があれば、Lambdaの実行環境を入れ替えてキャッシュごと破棄できます。 危険な操作への承認フロー ポリシーで承認が必要と宣言されたコマンドは、即座には実行されません。まずdispatch Jobが、実行内容と承認ボタンを含むメッセージをSlackに投稿します。承認者がボタンを押すと、そのコールバックが再びGatekeeperに届きます。Gatekeeperは押した人がポリシー上の承認者であることを検証し、DynamoDBを使ったワンショット制御で同じ承認が二度実行されないことを保証してから、実行イベントをEKSへ転送します。 承認の細部もポリシーで宣言できます。承認には有効期限( approval_ttl_minutes )があり、期限を過ぎた承認リクエストは失効します。実行を依頼した本人による自己承認を許すかどうか( allow_self_approval )も制御でき、危険な操作では自己承認を禁止しています。 承認のコールバックも入口はコマンド投稿と同じGatekeeperです。認可の判断ロジックが1か所に集まっているため、「承認ボタンを押せる人の検証が漏れる」といった抜け道が生まれにくい構造になっています。 許可も記録する構造化監査ログ Gatekeeperは、拒否だけでなく許可した実行も構造化ログとして記録します。テナント、チャネル、コマンド、実行者、経路(Workflow経由または直接メンション)、判断結果と理由コードがワンレコードに収まっています。そのため「先月このコマンドを実行したのは誰か」「特定ユーザーの実行が拒否された理由は何か」をログ検索だけで答えられます。 { " event ": " chatops_allowed ", " team ": " zozo-cart ", " channel_id ": " C0123456789 ", " command ": " example-tool update ", " user_id ": " U001 ", " via ": " workflow/B0XXXXXXXXX " } { " event ": " chatops_denied ", " team ": " zozo-cart ", " channel_id ": " C0123456789 ", " command ": " example-tool delete ", " user_id ": " U003 ", " reason ": " user_not_allowed " } 拒否ログだけでは監査になりません。インシデント調査で本当に知りたいのは「誰が実行できたのか」だからです。許可の記録を含めて初めて、実行の全体像を後から再構成できます。 もう1つの設計点は網羅性です。Gatekeeperのすべてのレスポンス経路は、許可、拒否、リトライ破棄、転送失敗といったいずれかのイベント種別で必ずログに残ります。「ログに現れない実行」は構造上存在しないという性質が、監査ログとして信頼するための前提です。 なぜArgo Eventsなのか 入口の関門はこれで揃いました。次はEKS側、受け取ったイベントを実行する基盤です。Argo Eventsは、Kubernetes上でイベント駆動の処理を実現するためのツールです。次の3つのコンポーネントで構成されます。 EventSource : Webhookなどでイベントを受信してEventBusへ流す EventBus : イベントを配送するメッセージバス(NATS JetStreamを使用) Sensor : イベントをフィルタし、条件に合致したらトリガー(K8s Jobの作成など)を発火する 採用の決め手は次の3点です。 イベントを受けてK8s Jobを起動する仕組みを、自作せずに宣言的なマニフェストで実現できる EventSource、EventBus、SensorがすべてKubernetesリソースなので、既存のGitOps(Flux)の管理に乗る ZOZOではArgo WorkflowsのCronWorkflowを運用済みで、Argoエコシステムの運用知見がある これに加えて、他チームの案件でArgo Events自体の導入がすでに決まっており、クラスタにはコントローラも導入済みでした。技術的な適合とは別の要素ですが、この状況も採用を後押ししました。 Argo Eventsを本番運用に載せる設計判断 そのArgo Eventsを本番の実行基盤として運用するうえで、設計判断がいくつかありました。 EventBusは高可用性をあえて求めない EventBusはNATS JetStreamで構成しています。ストリームには300秒の重複排除窓を設定しており、再送があってもJobが二重に走らない構造です。Slackのリトライは手前のGatekeeperで破棄済みのため、この窓が受け持つのはArgo Events内部の再配送だけです。 レプリカ数は、本番も含めて1にしています。運用コマンドの実行基盤は、決済のようなミッションクリティカルなデータパスではありません。EventBusが短時間止まっても、利用者がコマンドを打ち直せば回復できます。 むしろ判断が要ったのは永続化です。JetStreamはPersistentVolumeへの永続化を構成できますが、この基盤では設定せずemptyDirのまま使っています。EventBusを通るのは、EventSourceが受けてからSensorがJobを作るまでのごく短命なイベントだけです。承認待ちのような長寿命の状態はDynamoDB側が持つため、JetStreamには残りません。そしてPVを付けても「EventBusの再起動中に届いたコマンド」は受けられないので、取りこぼしはゼロにならず、EBSのアタッチを挟むぶん復帰はかえって遅くなります。どちらの失敗でも受付通知が返らないため、利用者は気付いて再実行できます。それなら復帰が速い構成を選ぶ、という判断です。 PodDisruptionBudgetはクラスタポリシーが存在を要求するため置いていますが、 minAvailable: 0 でevictionを妨げない値にしています。単一レプリカで止まることを受け入れる方針を、PDBの値でも一貫させています。 なお、これはテナント単位の判断です。EventBusを含むArgo Eventsのリソース一式はテナントごとに独立しているため、高い可用性が必要なチームは、自分のテナントだけレプリカ数や永続化を引き上げられます。 監視の組み込み EventBus、EventSource、SensorはいずれもPrometheus形式のメトリクスを公開しています。このメトリクスをDatadogのAutodiscoveryアノテーションでスクレイプして、NATSとArgo Eventsの状態を既存の監視基盤に載せています。イベントの滞留や配送失敗を、他のサービスと同じダッシュボードとアラートの体系で扱えます。 Sensorはルーティングだけ、実行権限はexecuteだけ Sensorのフィルタは、イベント種別と承認要否によるルーティングだけです。承認が必要なコマンドは承認リクエストを投稿するdispatch Jobへ、承認不要なコマンドと承認済みのコールバックは実行を担うexecute Jobへ振り分けます。認可はGatekeeperで完結済みなので、Sensorに認可のロジックはありません。 実行(Lambda invokeとK8s Jobの作成)をexecute Jobに一本化しているのは、実行権限を1か所に集約するためです。Lambdaを呼べるIAMロールとJobを作れるRBACは、execute JobのServiceAccountだけに与えます。承認リクエストを投稿するだけのdispatch Jobは、実行権限を一切持ちません。なお、dispatchとexecuteは2つのサブコマンドを持つ1つのバイナリで、ペイロードの型とSlack通知の実装を共有しています。 K8s Jobによるツール実行機構 PodTemplateでツールのJob定義を配布する ツールをK8s Jobとして実行するには、「どんなPodを起動するか」の定義が必要です。この定義を v1 PodTemplate リソースとしてテナントのnamespaceに配布し、実行時にexecute Jobが PodTemplate を読んでJobを組み立てる方式にしました。 検討のポイントは、このテンプレートをどこに置いておくかでした。テンプレートもGitOps(Flux)の管理に乗せたいので、Kubernetesのオブジェクトとしてクラスタ上に置けるリソースが優先候補になります。素直に kind: Job のマニフェストを置く案が最初に浮かびますが、Jobは作成された時点で1回実行されるリソースなので、配布と同時に動いてしまい成立しません。 suspend: true のCronJobなら起動せずに置いておけます。ただし、suspendの解除ミスで意図せず動いてしまう危うさが残ります。PodTemplateは単体では何も実行しないKubernetesネイティブのリソースで、スキーマ検証も効きます。「実行されないテンプレート」という意図を、運用ルールではなく構造で表現できます。 Jobレベルの設定はテンプレート側に持たせず、実行機構側で決めています。リトライはさせず( backoffLimit: 0 )、完了したJobは1時間で自動削除します( ttlSecondsAfterFinished: 3600 )。例外は実行時間の上限( activeDeadlineSeconds )で、これはツールごとに変えたい値です。ただしPodTemplateが持てるのはPodのspecだけで、Jobレベルのフィールドは書けません。そこで、この値はPodTemplateのannotationで宣言してもらい、実行機構がJobを組み立てる際に読み取って反映する形にしました。テナントが管理するのはPodの中身だけ、Jobとしての振る舞いは基盤が統一する、という責務の線引きです。 テナントが配布するPodTemplateのマニフェストは、次のような形です(抜粋)。 apiVersion : v1 kind : PodTemplate metadata : name : zozo-cart-chatops-template-example-tool annotations : # JobレベルのactiveDeadlineSeconds(実行機構がJob specに設定する) chatops.zozo.com/job-active-deadline-seconds : "900" template : metadata : labels : app : zozo-cart-chatops-tool spec : restartPolicy : Never containers : - name : example-tool image : example-tool:<tag> # イメージのフルURIはoverlayのpatchが与える command : - example-tool - chatops env : - name : TOOL_ENV value : "" # 環境名もoverlayのpatchが与える # DB認証などのシークレットはsecretKeyRefで注入する なお、マニフェストにシークレットの生の値は置きません。実体はAWS Secrets Managerにあり、それをExternal SecretsがKubernetes Secretへ同期し、Podは secretKeyRef で参照します。 実行機構がPodTemplateからJobを組み立てる部分の実装は、次のような形です。 // Jobレベルの機構ポリシー。ツールによらず共通の値のため固定する const ( jobBackoffLimit int32 = 0 // リトライは利用者の再実行に任せる jobTTLSecondsAfterFinished int32 = 3600 // 完了したJobの掃除 ) // ツール別に調整するJobレベルの実行時間上限(テンプレートのannotationで宣言する) const activeDeadlineAnnotation = "chatops.zozo.com/job-active-deadline-seconds" template, err := client.CoreV1().PodTemplates(namespace).Get(ctx, templateName, metav1.GetOptions{}) // ...(annotationから実行時間上限を読み取り、Pod specを検証する) job := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ GenerateName: "chatops-" + tool + "-" , }, Spec: batchv1.JobSpec{ BackoffLimit: ptr.To(jobBackoffLimit), TTLSecondsAfterFinished: ptr.To(jobTTLSecondsAfterFinished), ActiveDeadlineSeconds: ptr.To(activeDeadline), // annotation由来 Template: corev1.PodTemplateSpec{ ObjectMeta: podMeta, Spec: *template.Template.Spec.DeepCopy(), }, }, } マルチテナント設計とツール追加の体験 共通の入口と、テナントごとの実行スタック この仕組みは、カート決済SREブロック専用ではなく、社内の複数チームで使える共通基盤として設計しています。Slack App、API Gateway、Gatekeeperは全テナント共通です。一方、EKS側のEventSource、EventBus、Sensor、Jobの一式は、テナントのnamespaceごとに独立しています。Gatekeeperがchannel-mapで解決したテナントに応じて転送先を切り替えるため、あるテナントのイベントが他のテナントの実行スタックに流れることはありません。 ツールに渡す標準ペイロード 実行機構からツールへは、次の形の標準ペイロードを渡します。 { " kind ": " chatops-tool ", " tool ": " example-tool ", " command ": " update ", " args ": [ " ... " ] , " requester ": " U12345678 ", " channel ": " C12345678 ", " team ": " zozo-cart ", " ts ": " 1756... " } 引数( args )の解釈はツール自身が行います。基盤側がツールごとの引数仕様を知る構造にすると、ツールを追加するたびに基盤へ変更が波及するためです。ツールはこのペイロードを受け取れる形にさえなっていれば、K8s JobでもLambdaでも構いません。 Lambdaツールの場合は、実行機構のJobがこのペイロードを渡してLambda関数をinvokeします。旧基盤から移行したLambdaツールは、関数やIAMロール、VPC設定をそのまま維持して、標準ペイロードを受理する改修だけでトリガー経路を差し替えられました。実行形態の自由とは、新しいツールの選択肢が増えることだけでなく、既存のLambda資産を作り直さずに済むことでもあります。 ツール追加はSSMポリシーとマニフェストだけ 新しいツールをChatOpsに追加する手順は次の2つだけです。 SSMに /chatops/policy/{テナント}/{ツール} のポリシーを追加する(コマンド、許可、承認、実行形態の宣言) K8s Jobツールの場合は PodTemplate のマニフェストを追加する。Lambdaツールの場合は標準ペイロードを受理するようにする Slack App、API Gateway、Gatekeeperのコードやリソースには一切手を入れません。従来はツールごとにLambdaの新設が必須でしたが、今はコンテナイメージとして動くツールならPodTemplateを書くだけでChatOpsに載ります。 導入の効果 移行によって、冒頭の課題は次のように解消されました。 コマンド、ユーザー、チャネルの単位の認可と、危険操作への承認フローが基盤の機能として動いている 許可を含むすべての実行判断が構造化ログとして残り、監査とインシデント調査で使える状態になった 非エンジニアの利用者は、従来と同じWorkflowフォームのUXのまま移行できた ツールの実行形態がK8s JobとLambdaの2択になり、追加コストが下がった まとめ 本記事では、AWS Chatbotで運用していたChatOps基盤を、Argo EventsとGatekeeper Lambdaを軸にした構成へ再構築した事例を紹介しました。実行者を直接特定できないWorkflow経由の投稿に対しては、信頼チェーンで認可を成立させました。この信頼チェーンと、実行基盤の手前での認可、承認、監査の一元化が設計の要点です。Slackを入口とした運用基盤の構築や、Argo Eventsの本番運用を検討している方の参考になれば幸いです。今後は実行時権限のさらなる最小化など、基盤の堅牢化を進めていきたいと考えています。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com
LY Corporationの技術カンファレンス Tech-Verse 2026 の公式記事です。はじめにこんにちは。社内クラウドサービス Verda および社内モニタリングツール IMON に In...
2026年6月29日(月)、技術カンファレンス「Tech-Verse 2026」(以下、本カンファレンス)を、オンラインにて開催いたします。本カンファレンスは、LINEヤフーおよび海外法人を含むグルー...

動画

該当するコンテンツが見つかりませんでした

書籍