インフラエンジニアがSlack スラッシュコマンド + API Gateway + Lambdaでサーバーレスボットを作った話

みんなのウェディングのインフラエンジニア横山です。 今回はAWSのサービスを利用したサーバーレスなデプロイボットの作り方についてお伝えします。

作成の動機

弊社には、rubotyで作られた、minaというデプロイ用のボットが存在しており、こちらのボットにSlack上で話しかけることで本番環境へのデプロイを行っていました。

今回は、このminaの置き換えとしてサーバーレスなデプロイボットが作れないか取り組みました。 作成の動機は以下の2点です。

  • ボットのメンテナンスコストの削減
  • ボットが稼働するサーバコストの削減

概要

全体の流れは以下の図の通りです。

まとめると、

  1. Slackのスラッシュコマンドを実行
  2. API Gatewayにリクエストが届く
  3. API Gatewayに紐づけられたLambdaでデプロイ処理が実行される
  4. デプロイされる

※AWS側のリソースはSAMで管理。

といった感じです。 以下では各ツールの概要と今回どのような用途に使っているのかについて説明します。

Slackのスラッシュコマンド

スラッシュコマンドは、Slackの標準機能です。 以下のように様々な操作のショートカットとして利用できます。

また、標準で用意されているコマンドだけでなく、自分でコマンドを作ることができ、指定したURLにPOST、GETリクエストを送ることができます。

用途

今回は/deployというコマンドを作成し、API Gatewayのエンドポイントに、以下のようなリクエストボディを持つリクエストを送信するようにしています。

token=XXXXXXXXXXXXXX
team_id=T0001
team_domain=example
channel_id=C2147483705
channel_name=test
user_id=U2147483697
user_name=Steve
command=/deploy
text=wedding+production
response_url=https://hooks.slack.com/commands/1234/5678

text部分に、スラッシュコマンド実行時の引数が記載されます。複数ある場合は+で結合されます。

API Gateway

API GatewayはAWSフルマネージドなAPI作成サービスです。
リソースごと、HTTPメソッドごとに対応する動作を指定していくだけでAPIの作成ができます。
例えば以下の例だと/に対してPOSTリクエストがきた際の動作について指定しています。

用途

今回は、スラッシュコマンドからのPOSTリクエストの受け取りに利用しています。 受け取ったリクエストは対応するLambdaに送られ、デプロイ処理が行われます。

Lambda

Lambdaはサーバーのプロビジョニングや管理なしでコードを実行できるサービスです。
対応言語はNode.js (JavaScript)、Python、Java (Java 8 互換)、C# (.NET Core)、および Goがあります。 今回は、普段利用しているRubyに近い言語ということでPythonを選択しました。

用途

今回はデプロイ処理の実行環境としてLambdaを利用しています。 Lambda上では、API Gatewayから受け取ったリクエストの内容を元に、CodeDeployやCodePipelineといったデプロイツールを実行します。
Lambda関数は以下の3種類に分けています。

  • DeployStarter
  • DeployExecutor
  • DeployLock

API Gatewayからのリクエストを受け取るDeployStarterと、実際のデプロイ処理を行うDeployExecutor、デプロイロックの処理を行うDeployLockに分けています。
分けた理由は、スラッシュコマンドの仕様で3秒以内にレスポンスを返さないとエラーになってしまうためです。 DeployStarterでの処理を、スラッシュコマンドから送られてくるトークンを元にした認証処理と、DeployExecutorまたはDeployLockの実行処理だけにすることで3秒以内にレスポンスを返せるようにしています。

AWS SAM

AWS SAM(AWS Serverless Application Model)とは、今回のようなサーバーレスアプリを作成、管理するのに適したツールです。
API GatewayもLambdaも設定項目が多く、構成管理しないまま使うと同様の構成を作り直す必要がある時に苦労します。
SAMを使うと簡単なyamlファイルで構成管理ができます。

用途

今回はAPI GatewayとLambdaの管理に利用しています。
以下のyamlファイルをaws cloudformationコマンドでpackageしてdeployすることで、APIGatwaway:1つとLambdaFunction:2つを持つCloudFormationのスタックが出来上がります。 コードの内容を変更した際も再度packageしてdeployすることですぐに変更を反映可能です。

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: deploy applications
Resources:
  Starter:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: deploy_starter.lambda_handler
      Runtime: python3.6
      FunctionName: DeployStarter
      CodeUri: ./source/deploy_starter/
      Description: deploy starter
      Role: arn:aws:iam::123456789:role/deploy_starter
      MemorySize: 128
      Timeout: 30
      Events:
        API:
          Type: Api
          Properties:
            Path: /
            Method: post
  Executor:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: deploy_executor.lambda_handler
      Runtime: python3.6
      FunctionName: DeployExecutor
      CodeUri: ./source/deploy_executor/
      Description: deploy executor
      Role: arn:aws:iam::123456789:role/deploy_executor
      MemorySize: 128
      Timeout: 30
      VpcConfig:
        SubnetIds:
          # [xxxxxxx-1,xxxxxxx-2]
          - 'subnet-xxxxxxxx'
          - 'subnet-xxxxxxxx'
        SecurityGroupIds:
          # [xxxxx, xxxxxx]
          - 'sg-xxxxxxxx'
          - 'sg-xxxxxxxx'

まとめ

スラッシュコマンド + API Gateway + Lambda を使うことで、インフランエジニアでも簡単にサーバーレスボットを作ることができました。 また、AWS SAMを使うことで、構成管理までお手軽にできてしまいました。
複雑なことをしないのであれば、専用のボットフレームワークではなく、今回作成したようなサーバーレスボットで十分であることがお分りいただけたかと思います。
機会がありましたら、皆さんも是非作ってみてください!