はじめに
こんにちは、技術本部SRE部カート決済SREブロックの遠藤・金田です。
普段はSREとしてZOZOTOWNのカート決済機能のリプレイスや運用を担当しています。本記事では自作のコマンドラインツールをSlack + AWS Chatbot + AWS Lambdaを使用してChatOps化した事例をご紹介します。「日々の運用業務をコマンドラインツールを実装して効率化したものの今ひとつ広まらない」「非エンジニアにも使えるようにしたい」と考えている方の参考になれば幸いです。
目次
背景・課題
私たちの日々の運用業務の1つに過熱登録という作業があります。
福袋や限定品など、ユーザーから大量のアクセスが来る商品のことを弊社では「過熱商品」と呼んでいます。過熱商品は販売開始のタイミングで大量のアクセスが発生します。ZOZOTOWNのカート機能では過熱商品の販売によりアクセスが急増した際にも過熱商品ではない商品の購入に支障が出ないようにシステムを構築しています。
詳しくは以下のテックブログをご覧ください。
「過熱登録」とは、上記で紹介している、カート投入された商品が過熱商品であるかを判断するためのデータベース(以下:DB)に、その商品情報を登録する作業を指します。
このプロセスでは、商品ごとに一意のIDを用いてDB内に登録していきます。
過熱登録に使用するIDは商品のサイズや色毎に発行されるIDを使用しており、1つの商品に紐づく全てのIDをDBから抽出しそれらを個別に登録する必要があるため、手作業で行うと手間のかかる面倒な作業でした。
上記の課題を解決するため、DBから関連情報を抽出し一括で過熱登録するコマンドラインツールをGo言語で実装し運用していましたが、以下のような別の課題が見えてきました。
- ツールを使用するための事前準備が必要
- 本番環境に接続する権限が必要
- 実行履歴が残らない
- コマンドラインツールのため非エンジニア向けではない
これらの課題を解決する手法としてChatOpsの採用を決め実装しました。
ChatOpsとは
ChatOpsとは、Slackなどのチームコラボレーションツールと自動化を組み合わせたもので、システム運用における運用業務を自動化するものです。ChatOpsはDevOps文化と密接に関わっており、作業の透明性や効率性を高めることができます。
AWS ChatBotとは
AWS ChatBotとは、Slackなどのチャットツールと連携してAWSリソースを管理、監視、操作するためのAWSサービスです。
以下のような特徴が挙げられます。
- 通知とアラート:AWSのサービスやリソースに関するイベントやアラートをリアルタイムでチャットツールに通知できます。
- アクションの実行:ChatBotを使用してAWSリソースを操作できます。awsコマンドで実施できることは一通り実行できます。
- アクセス制御:IAMを使用して、特定のチャンネル、ユーザーにのみAWSリソースへのアクセスを許可します。これによりセキュリティを維持しながらチーム全体でのAWSリソースの管理が可能になります。
また、2024/2時点で以下の3つのチャットツールと連携が可能です。
- Amazon Chime
- Slack
- Microsoft Teams
構成
ChatOpsのシステム構成は以下の通りです。
ChatOps用のSlackチャンネルからワークフローを実行し、対象の商品IDなど必要な情報を入力します。新しいスレッドが作成され、AWS ChatBotに対してコマンドが実行されます。ChatBot経由でAWS Lambdaを実行し、過熱登録ツールが実行されます。
AWS ChatBot
ZOZOではチャットツールとしてSlackを使用しているため、本記事ではSlackとChatBotの連携方法について記載します。
はじめに、SlackとAWS ChatBotを連携する必要があり、Slackワークスペースに対して、AWS ChatBotアプリのアクセスを許可します。
その後、AWS ChatBotのリソースを作成します。
AWS ChatBotのCloudFormationは非常にシンプルです。WorkspaceIdとChannelIdを指定して、チャンネル単位で連携します。AWS ChatBotには後段のAWS Lambdaを実行するRoleを付与しています。
Resources: ZozoCartChatOpsChatbot: Type: AWS::Chatbot::SlackChannelConfiguration Properties: SlackWorkspaceId: !Ref TargetWorkspaceId SlackChannelId: !Ref TargetChannelId ConfigurationName: !Ref ConfiguredChannelName IamRoleArn: !GetAtt ZozoCartChatOpsChatbotRole.Arn LoggingLevel: ERROR ZozoCartChatOpsChatbotRole: Type: AWS::IAM::Role Properties: RoleName: zozo-chatops-chatbot AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - chatbot.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: zozo-chatops-chatbot-policies PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - 'lambda:invokeAsync' - 'lambda:invokeFunction' Resource: - !GetAtt ZozoChatOpsLambda.Arn
チャットツール側の設定
ChatBotリソースが作成されたら、ChatBotを利用したいSlackチャンネル上で以下のコマンドを実行しAWSアプリを追加します。
/invite @aws
これでSlackからAWS ChatBotを利用する準備が整いました。
Slackの場合、パブリックチャンネル/プライベートチャンネルのどちらでも連携可能です。権限設定については後述しますが、管理をシンプルにするためプライベートチャンネルを採用し、作業者のみをチャンネルに招待して利用を開始しました。
Slack Workflow
Slackにはワークフローという自動化機能があり、社内でも多く活用しています。作業をより簡単にするため、ChatOpsでもSlackワークフローを使って簡易化を検討しました。
自動化する作業ごとにワークフローを作成します。ワークフローを実行するとスレッド上でawsコマンドが投稿され、AWS ChatBotへ命令を渡すことができます。aws lambda invoke
コマンドを実行して任意のLambda関数を実行します。
Lambda
ChatOpsから任意の処理を実行する基盤としてAWS Lambdaを採用しました。選定理由としては以下が挙げられます。
- ChatBotから実行できワークフローで指定したパラメータを渡せる
- 今後様々な用途でChatOpsを利用することを考え、柔軟に実装できる
- コストが安い
ChatOpsは運用者がコマンドを実行しない限り発動しないため、実行時間での課金であるAWS LambdaはChatOpsの実行基盤として相性が良いです。
ChatBotを経由して以下のコマンドを実行することでLambda関数を実行します。
@aws lambda invoke --payload '{JSON形式で過熱商品IDなどを渡す}' --function-name [Lamda関数名] --invocation-type Event --region ap-northeast-1
--payload
オプションでLamdbaへ任意のパラメータを渡すことができるため、ワークフロー内で入力された情報をLambdaに渡すことができます。
実装のポイント
ChatBotのアクセス制御
AWS ChatBotの権限設定には2つの設定範囲があります。
- Channel Role
- User Role
Channel Roleは、SlackチャンネルごとにIAM Roleを付与します。そのSlackチャンネルに参加するメンバーは同じ権限でChatBotにコマンドを送ることができます。
User Roleは、Slackユーザーごとに個別のIAM Roleを付与できます。チャンネル内のメンバーごとに、異なる権限を与えたい場合にはこちらが適しています。
理想としては、ChatOps用のSlackチャンネルをパブリックチャンネルにして、一部のメンバーのみが過熱登録できる状態が望ましいです。そのためUser Roleを使いたいと考えましたが、後述する問題がありSlackチャンネルはプライベートチャンネルにしてChannel Roleを使って運用しています。
User Roleの運用方法
ChatBotの設定でUser Roleを有効化することで、Slackユーザーごとに個別のIAMロールを付与できます。
しかし、User Roleの設定は各ユーザーがAWSのマネジメントコンソールにアクセスして、ChatBotの画面からSlackへ連携する必要があります。
この連携設定はCloudFormationで管理できません。AWSへの権限がある程度必要になるため、AWSへの権限を持っていないユーザーはそもそもこの設定ができません。
また、設定時に付与するIAM Roleを自分で設定する必要があり、統制を取ることが難しいと感じました。そのため、一旦プライベートチャンネルとChannel Roleでの運用としています。
ガードレールポリシー
上記のロール設定とは別にAWS ChatBotにはガードレールポリシーという設定があります。ガードレールポリシーで権限を制限することで、その範囲を超えて権限が付与されることを防ぎます。
ガードレールポリシーはデフォルトでAdministratorAccessとなっているため、制限がない状態です。ChatBotからアクセスする想定がないリソースはここで権限を制限しておくと安心です。
これにより、誤ってAWS ChatBotに強い権限が付与されてしまう事故を防ぐことができます。
コマンドラインツールのLambda関数化
コマンドラインツールについて
過熱登録用コマンドラインツール(以下、過熱登録ツールと記載します)についても簡単に触れておきたいと思います。
過熱登録の作業を効率化するために実装したGo言語製ツールで、機能毎にサブコマンドとして実装しています。
機能(サブコマンド) | 処理内容 |
---|---|
商品情報の取得 | 指定されたIDに紐づく商品情報をDBから取得して出力する。 |
過熱登録 | 1つの商品に紐づく全てのIDをDBから取得して過熱登録用のAPIサーバに登録リクエストを行う。 |
過熱商品の検知と自動登録 | カート投入のログから過熱商品の発生を検知して自動で過熱登録する。ArgoWorkflowから定期的に実行する。 |
local環境での実行とPod起動コマンドの共通化
過熱登録ツールの各機能は主にDBサーバからの情報取得とEKS上で稼働しているAPIサーバへのリクエストで構成されます。
アクセス権限やDB接続情報などの秘密情報を管理する観点から、DBサーバやAPIサーバへのアクセスはlocal環境で実行する過熱登録ツールからは行わず、EKS上にPodを起動して行う構成としています。
Pod起動に使用するコンテナイメージは過熱登録ツールのバイナリを/usr/local/bin
に配置したイメージをECRに登録して使用しています。
以下のDockerファイルでイメージをビルドしています。
FROM golang:1.21.4 WORKDIR /go/src/hotitem-tool COPY . . RUN go mod tidy # GOOSとGOARCHの設定以外はソースコードも含めlocal環境用のビルドと同一の設定でビルド RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /usr/local/bin/hotitem-tool . CMD ["hotitem-tool"]
過熱登録ツールはlocal環境で起動されたらPod起動の処理を行い、コンテナ環境で起動されたら各種サーバへのアクセスを行いビジネスロジックを実行する構成になっています。
local環境用/コンテナ環境用といったようにツールの実装を分けてしまうと管理が煩雑になってしまいます。そのため過熱登録ツール内部で起動された環境がlocal環境か/コンテナ環境かを判別して、Podを起動するか/ビジネスロジックを実行するかを分岐しています。
下記は商品情報の取得処理を実行する場合のツール起動コマンドとPod起動設定です。
local環境でのコマンド実行例です。
hotitem-tool get-goodsinfo --all 1234
上記コマンドで実行した場合、過熱登録ツールは以下のマニフェストに相当するspec設定でclient-goを利用してPodを起動します。
apiVersion: v1 kind: Pod name: hotitem-tool namespace: <namespace> spec: containers: - name: hotitem-tool image: <ECRにpushした過熱登録ツールコンテナイメージのURI> command: - hotitem-tool - get-goodsinfo - --all - 1234
ツール起動時に指定されたサブコマンド/オプション/引数の値を全て引き継いでコンテナの起動コマンドを設定してPodを起動しています。Pod起動によってコンテナ環境で実行された過熱登録ツールは、commandに設定されたサブコマンドや引数に応じたビジネスロジックを実行します。
このような構成にすることで実装を分けずにツール単体でクライアント/サーバ型のアプリケーションと近い構成を実現しています。
cgroupによる実行環境の判定
ツールがどの環境で起動されたかの判定にはcgroupを利用しています。
cgroup(Control Group)はLinuxカーネルの機能の1つで、システムリソースを使用するプロセスをグループ化し、グループに対してのリソース使用量を制限できる機能です。cgroupはコンテナの実現に重要な役割を担っておりKubernetesにおいてもnodeリソースを管理するために利用されています。
/proc/<pid>/cgroup
を確認すると<pid>
のプロセスがどのコントロールグループに属しているかがわかるようになっています。
下記は開発環境のEKSで稼働しているコンテナのcgroupを実際に出力してみた結果です。コンテナのメインプロセスはpid:1で起動されるためpidは1を指定しています。
/ # cat /proc/1/cgroup 11:cpuset:/kubepods.slice/kubepods-pod1f9bc0a5_a5bc_4707_b55e_f9accda568af.slice/cri-containerd-7e3aab58c075f4d2b7e00a7f91d5bc9ed827fb141f0cc2a95ab25083c71d0f6e.scope 10:hugetlb:/kubepods.slice/kubepods-pod1f9bc0a5_a5bc_4707_b55e_f9accda568af.slice/cri-containerd-7e3aab58c075f4d2b7e00a7f91d5bc9ed827fb141f0cc2a95ab25083c71d0f6e.scope 9:freezer:/kubepods.slice/kubepods-pod1f9bc0a5_a5bc_4707_b55e_f9accda568af.slice/cri-containerd-7e3aab58c075f4d2b7e00a7f91d5bc9ed827fb141f0cc2a95ab25083c71d0f6e.scope 8:pids:/kubepods.slice/kubepods-pod1f9bc0a5_a5bc_4707_b55e_f9accda568af.slice/cri-containerd-7e3aab58c075f4d2b7e00a7f91d5bc9ed827fb141f0cc2a95ab25083c71d0f6e.scope 7:perf_event:/kubepods.slice/kubepods-pod1f9bc0a5_a5bc_4707_b55e_f9accda568af.slice/cri-containerd-7e3aab58c075f4d2b7e00a7f91d5bc9ed827fb141f0cc2a95ab25083c71d0f6e.scope 6:net_cls,net_prio:/kubepods.slice/kubepods-pod1f9bc0a5_a5bc_4707_b55e_f9accda568af.slice/cri-containerd-7e3aab58c075f4d2b7e00a7f91d5bc9ed827fb141f0cc2a95ab25083c71d0f6e.scope 5:memory:/kubepods.slice/kubepods-pod1f9bc0a5_a5bc_4707_b55e_f9accda568af.slice/cri-containerd-7e3aab58c075f4d2b7e00a7f91d5bc9ed827fb141f0cc2a95ab25083c71d0f6e.scope 4:blkio:/kubepods.slice/kubepods-pod1f9bc0a5_a5bc_4707_b55e_f9accda568af.slice/cri-containerd-7e3aab58c075f4d2b7e00a7f91d5bc9ed827fb141f0cc2a95ab25083c71d0f6e.scope 3:devices:/kubepods.slice/kubepods-pod1f9bc0a5_a5bc_4707_b55e_f9accda568af.slice/cri-containerd-7e3aab58c075f4d2b7e00a7f91d5bc9ed827fb141f0cc2a95ab25083c71d0f6e.scope 2:cpu,cpuacct:/kubepods.slice/kubepods-pod1f9bc0a5_a5bc_4707_b55e_f9accda568af.slice/cri-containerd-7e3aab58c075f4d2b7e00a7f91d5bc9ed827fb141f0cc2a95ab25083c71d0f6e.scope 1:name=systemd:/kubepods.slice/kubepods-pod1f9bc0a5_a5bc_4707_b55e_f9accda568af.slice/cri-containerd-7e3aab58c075f4d2b7e00a7f91d5bc9ed827fb141f0cc2a95ab25083c71d0f6e.scope / #
cgroupは階層構造を持っており、上記の出力でも確認できる通りKubernetesで起動されるコンテナのcgroupは必ずkubepods.slice/
というユニットに属します。
以上のことを利用し、過熱登録ツールでは以下の実装で実行環境を判定しています。
func IsInPods() bool { _, err := os.Stat("/proc/1/cgroup") if err != nil { return false } return grep("(kubepods)", "/proc/1/cgroup") } func grep(pattern, filename string) bool { file, err := os.Open(filename) if err != nil { return false } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() if match, _ := regexp.MatchString(pattern, line); match { return true } } if err := scanner.Err(); err != nil { return false } return false }
ツールのコードを再利用したLambda関数の実装
過熱登録に必要な機能は、過熱登録ツールに実装済みであるため、実装済みの機能はChatOps用に再実装することなくそのまま活かしたい、ソースコードも同一リポジトリで管理したいと考えました。
Go言語でLambda関数を作成するにはaws-lambda-goを使って以下のようなmain関数とハンドラ関数を実装します。
package main import ( "context" lambdago "github.com/aws/aws-lambda-go/lambda" ) type Option struct { Name string Value string } type Options []Option // 引数で受け取るペイロードは独自形式で定義できます type Payload struct { Command string Options Options Arguments string } func main() { lambdago.Start(handler) } func handler(ctx context.Context, event Payload) (string, error) { switch event.Command { case "get-goodsinfo": return executeGetGoodsInfoCommand(event) case "regist": return executeRegistCommand(event) } return "", nil } 〜略〜
実装が完了したら以下の手順でLambda関数を作成します。
- コンテナイメージをビルド
- ECRにpush
- Lambda関数の作成オプションでコンテナイメージを選択し、ECRにpushしたコンテナイメージのURIを設定
過熱登録ツールがlocal環境側で行う処理は、EKSにPodを起動して実行結果を受け取り出力することだけです。
Lambda関数化しても行う処理は同様で、EKSにPodを起動して実行結果を受け取り出力できれば良いです。そのためmain関数をコマンドラインツール用の実装からLambda関数用の実装に切り替えることができれば、既存実装をそのまま活かしてLambda関数化できます。
main関数の差し替え
Go言語には実行対象の環境毎に実装を切り替えることができるビルド制約(ビルドタグとも呼ばれます)という機能があります。
ビルド制約は基本的にはOS/ARCHといったビルド時に設定する実行環境の情報を使ってビルドに含む/含まないといった制御ができる機能ですが、独自タグの定義による制御もできます。
本記事ではLambda関数用のビルドに含みたいファイルは、ファイルの先頭に //go:build lambda_handler
というビルドタグを定義します。元のmain関数が実装してあるファイルの先頭にはLambda関数用のビルド時に含まれないように//go:build !lambda_handler
を定義します。
そしてgo build
コマンドのオプションに-tags lambda_handler
を設定することで独自タグの定義が有効になりmain関数の差し替えを実現しています。
Lambda関数用のコンテナイメージをビルドするDockerfileは以下のように記述しています。
FROM golang:1.21.4 AS lambda-builder WORKDIR /go/src/hotitem-tool COPY . . RUN go mod tidy RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /usr/local/bin/hotitem-tool -tags lambda_handler . # Lambda関数実行時にawsコマンドでeks clusterの設定をするためaws-cliのイメージをベースにする FROM --platform=linux/amd64 amazon/aws-cli:2.14.1 AS lambda-handler WORKDIR /etc/ # awsコマンドでeks clusterのconfig設定をした後にハンドラ関数を実行するshellをentrypointに設定 COPY startup.sh . RUN chmod 755 startup.sh COPY --from=lambda-builder /usr/local/bin/hotitem-tool . ENTRYPOINT ["/bin/sh", "-c", "./startup.sh"]
startup.sh
の実装です。ハンドラ関数からPod起動ができるようにツール実行前にtokenを取得してKubeConfigを更新しています。
#!/bin/sh if [ -z "$AccountID" ] || [ -z "$ClusterName" ]; then echo "Error: environment variables are not set." exit 1 fi export KUBECONFIG=/tmp/kubeconfig aws eks get-token --cluster-name "arn:aws:eks:ap-northeast-1:$AccountID:cluster/$ClusterName" aws eks update-kubeconfig --name "$ClusterName" ./hotitem-tool "$@"
過熱登録ツールは各機能をサブコマンドで実装しており、main関数を実装しているファイルはコマンドの定義だけです。そのためmain関数を実装しているファイルのみ差し替えることで既存実装に手を加えることなくLambda関数化できました。
実行結果の通知方法
過熱登録ツールは元々コマンドラインツールとして実装した関係で、実行結果の出力は標準出力で行っています。ChatOps化するにあたって実行結果はSlackに通知する必要がありました。
しかし、ChatBotの欠点として、タイムアウト値を10秒から変更できないことが挙げられます。
処理に10秒以上かかる場合、ChatBotによってレスポンスを返すことができないため、処理の中でWebhookによってレスポンスを通知するという方法をとりました。
そのため、AWS Lambdaの実行は-invocation-type
オプションで非同期(Event)としています。
Lambda関数の標準出力は自動的にCloudWatchのロググループに/aws/lambda/<LambdaFuntionName>
の形式で保存されます。
CloudWatchAlerm/AmazonSNS/AWS Chatbotを組み合わせてSlack通知する方法もありますが、本記事では以下の理由からツール側でSlack通知する方針にして実装しました。
- 元々ArgoWorkflowで実行した処理結果のSlack通知をツール側で実装済みだった
- Slack通知する際のフォーマットをツール側で定義・管理したい
Slack通知する実装は既にツール側に存在していましたが、Lambda関数から実行した場合、実行結果を標準出力ではなくSlack通知へ切り替える実装をする必要があります。
真っ先に思いつく方法は各機能の引数にLambdaからの実行かどうかのフラグを持たせて標準出力とSlack通知を分岐させる方法です。しかしフラグを用いた方法だと既存実装に手を加える必要があり修正箇所も多くなるため、本記事では別の方法を採用しました。
具体的には以下のような標準出力をキャプチャする処理を実装し、ハンドラ関数のはじめにInit関数の呼び出しと終了時にPostする処理を呼び出すようにしています。
こうすることで各機能の実装に手を加えることなく標準出力をSlack通知に差し替えています。
標準出力をキャプチャする実装
//go:build lambda_handler package lambda import ( "bytes" "fmt" "io" "os" "regist-hotitems/slack" // Slackへのpostとフォーマットする関数を実装した独自モジュール ) type StdoutCapture struct { orgStdout *os.File bufChan chan string writer *os.File reader *os.File } func (s *StdoutCapture) Init() error { var err error s.orgStdout = os.Stdout s.reader, s.writer, err = os.Pipe() if err != nil { return err } os.Stdout = s.writer s.bufChan = make(chan string) go func() { var b bytes.Buffer io.Copy(&b, s.reader) s.bufChan <- b.String() }() return nil } func (s *StdoutCapture) Close() { s.writer.Close() s.reader.Close() os.Stdout = s.orgStdout } func (s *StdoutCapture) PostWebhookForGetGoodsInfo(title, url string) error { s.writer.Close() s.reader.Close() os.Stdout = s.orgStdout // 標準出力をパースしてSlack通知用のフォーマットに変換する関数の呼び出し mes, err := slack.GenerateLambdaGetGoodsInfoNotificationMessage(title, <-s.bufChan) if err != nil { return fmt.Errorf("failed to generate message: %w", err) } if err := slack.PostWebhook(url, mes); err != nil { return fmt.Errorf("failed to post webhook: %w", err) } return nil }
ハンドラ関数の先頭で標準出力をキャプチャする処理を初期化して、defer
で終了時にSlackへpostする処理を実行する。
//go:build lambda_handler package main import ( 〜略〜 "regist-hotitems/lambda" ) 〜略〜 func handler(ctx context.Context, event Payload) (string, error) { switch event.Command { case "get-goodsinfo": return executeGetGoodsInfoCommand(event) case "regist": return executeRegistCommand(event) } return "", nil } func executeGetGoodsInfoCommand(event Payload) (string, error) { capture := lambda.StdoutCapture{} if err := capture.Init(); err != nil { return "", fmt.Errorf("failed to init stdout capture: %w", err) } defer capture.PostWebhookForGetGoodsInfo("get-goodsinfo result", os.Getenv("WebhookURL")) 〜略〜
標準出力差し替えの詳細についてはZOZO AdventCalender 2023に投稿した記事で解説しているので、興味がある方はご覧ください。
ChatOps化の効果
すでに本番環境の運用をChatOpsに切り替えており、ChatOps化したことであらゆるメリットを実感しています。
環境構築や準備が不要
今までは、運用業務を実施するメンバーがローカル環境を構築する必要がありました。運用ツールをアップデートするたびに、ツールをPullして再ビルドする必要がありましたがそれらが不要になりました。
本番環境の権限が不要
今までは、本番環境に対する権限を作業者に付与する必要がありましたが、AWS ChatBotを活用することで、本番環境の権限を直接払い出す必要がなくなりました。
また、作業に必要な権限が明確になる効果もあると感じました。
実行履歴が残る
誰がいつ何の作業をしたかを自動的に履歴として残すことができます。
また、そのままチャット上で会話に繋がるため、作業の共有やコミュニケーションが促される効果もあると感じました。
さいごに
本記事で紹介した過熱登録ツールを皮切りに、今後も様々な運用作業をChatOps化していこうと思います!
ZOZOTOWNでは、SREとして自動化やインフラ構築に携わってくれるメンバーを募集しています!