Screaming Loud

日々是精進

Slackから始めるChatOps

先日発表したChatOps事例のスライドを共有します。


www.slideshare.net

連携のハマりどころ

この発表でちゃんと伝えてないので補足です。


APIGatewayとLambdaの連携

APIGatewayを使うにあたりSlack連携で、GETリクエストを受け取るのは簡単なんですが、POSTリクエストが実はちょっと面倒なんですね。
p18でも書いてますが、Slackから飛んでくるリクエストがJsonではなくFormなんです。

Slackから飛ぶForm内容は、以下のような感じなんですが、

token=xxxxxxxxxxxxx
team_id=T0001
team_domain=example
channel_id=C1234567
channel_name=test
user_id=U1234567
user_name=Steve
command=/weather
text=its\ time\ to\ wake\ up
response_url=https://hooks.slack.com/commands/1234/5678

これがAPIGatewayだとTemplateマッピングってシロモノを使わなきゃできない感じなんです。(と思っていた。)

対処方法としては、

  1. テンプレートマッピングを使う
  2. lambdaプロキシを使う
テンプレートマッピング使い方

テンプレートマッピングに関しての使い方は公式に詳しく書いてあるので、こちらを参照にしてみてください。
API Gateway API リクエストとレスポンスペイロードのマッピングテンプレートのリファレンス - Amazon API Gateway

実際自分が作ったものだと
APIGateway→メソッドを選択→統合リクエストを選択→本文マッピングテンプレートで以下のようなテンプレートを記載しました。
ContentTypeは"application/x-www-form-urlencoded"と記載。

{ "body": $input.json("$") }

これは、formのインプットに対して、bodyをキーにしたjsonに変換するという処理です。
実際受け取るときは、

{ "body": {"token":"xxxxxxxxxxxxx", "team_id":"T0001", ... } }

みたいな感じに変換されてLambdaに渡されます。

Lambdaプロキシを使う

テンプレートマッピングって独自仕様すぎてわかりづらいですよね。
そこでlambdaブロキシを使うと楽できます。
"Lambda プロキシ統合の使用"にチェックを入れると以下のようなリクエストが渡されます。

{
    'body': 'token=xxxxxxxxxxxxx&team_id=T0001&team_domain=example&channel_id=C1234567&channel_name=test&user_id=U1234567&user_name=Steve&command=/weather&text=its\ time\ to\ wake\ up&response_url=https://hooks.slack.com/commands/1234/5678',
    'resource': '/start-stop-api',
    'requestContext': {
        'resourceId': 'xxxx',
        'apiId': 'xxxxxx',
        'resourcePath': '/test',
        'httpMethod': 'POST',
        'requestId': 'xxxxxxxx',
        'accountId': 'xxxxxxxx',
        'identity': {
            'apiKey': None,
            'userArn': None,
            'cognitoAuthenticationType': None,
            'accessKey': None,
            'caller': None,
            'userAgent': 'Slackbot 1.0 (+https://api.slack.com/robots)',
            'user': None,
            'cognitoIdentityPoolId': None,
            'cognitoIdentityId': None,
            'cognitoAuthenticationProvider': None,
            'sourceIp': '', // AWSのIP
            'accountId': None
        },
        'stage': 'dev'
    },
    'queryStringParameters': None,
    'httpMethod': 'POST',
    'pathParameters': None,
    'headers': {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Via': '1.1 xxxxxxxxxxxxxxx.cloudfront.net (CloudFront)',
        'Accept-Encoding': 'gzip,deflate',
        'CloudFront-Is-SmartTV-Viewer': 'false',
        'CloudFront-Forwarded-Proto': 'https',
        'X-Forwarded-For': '',
        'CloudFront-Viewer-Country': 'US',
        'Accept': 'application/json,*/*',
        'User-Agent': 'Slackbot 1.0 (+https://api.slack.com/robots)',
        'Host': 'xxxxx.execute-api.ap-northeast-1.amazonaws.com',
        'X-Forwarded-Proto': 'https',
        'X-Amz-Cf-Id': 'xxxxxx',
        'CloudFront-Is-Tablet-Viewer': 'false',
        'X-Forwarded-Port': '443',
        'CloudFront-Is-Mobile-Viewer': 'false',
        'CloudFront-Is-Desktop-Viewer': 'true'
    },
    'stageVariables': None,
    'path': '/test',
    'isBase64Encoded': False
}

これを使えば、テンプレートマッピングなんかしなくても、bodyにformの値が入ってくれるので、パースできちゃいます。

CloudWatchのログ設定

自分は、グループの作成に関しては、以下のような名前で手動で実施しました。
/aws/lambda/{lambda名}

AMIに以下の権限を付与してあげれば、作成されます。

{
            "Effect": "Allow",
            "Action": [
                "cloudwatch:GetMetricStatistics",
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
TIPS

lambda自体のTIPSですが、boto3は重いです。
なので、pythonコードとしてimport するときは、

from boto3 import resource

みたいな感じで、必要のないimportを無くすように心がけましょう。

lambdaは実行時間課金なので、無駄な処理は極力削除!!



ということで、ChatOpsどんどんやっていきましょー。