APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

激闘!Slack API 3秒の悪夢 ~遅延がもたらす失敗の影~

はじめに

こんにちは、クラウド事業部の清水(雄)です。

AWSの運用効率化にあたってChatOpsツールを開発しました。
「AWSコンソールを触ることなく」「チャットツールで運用作業を簡略化する」をコンセプトとしたツールです。

今回はSlack APIを用いて開発したのですが
Slack API は3秒以内にレスポンスを返さないと失敗扱いになります。
そこに至るまでの四苦八苦攻略法について記載させて頂きます。

※ツール紹介の記事はこちら↓
techblog.ap-com.co.jp ※ツールダウンロードリンクはこちら↓
www.ap-com.co.jp

目次

何故気づいた?

当初こんな構成で作ってみて
「lambdaは15分以内であれば処理が可能だし、そもそもIAMユーザ作成ってすぐ終わるっしょ!」って思って実行した所、、

玉砕!

でも実はAWS側(IAMユーザ作成)の処理は成功していたんです。

理由

Slackの公式(翻訳)にはこんな記載が、、、
元のリクエストが送信されてから 3000 ミリ秒以内に Slack によって受信される必要があります。
受信しないと、operation_timeout エラーがユーザーに表示されます。
リクエストのペイロードを検証できなかった場合、アプリは代わりにエラーを返し、リクエストを無視する必要があります。

つまり「3秒以内にStatus code 200を返さないといけません。」

※該当ページははこちら↓
api.slack.com

ChatGPTが教えてくれた解決策

ChatGPTに聞くとこんな回答が

「たかがIAMユーザ作成で3秒以内に返せないの」とも思ったのですが
よく考えてみると
1. Slackを開いているクライアント側のNWの速度が遅かった場合
2. AWS CLIもレスポンスが遅い時があったり、、
など3秒以内に返せない理由はいくつか考えられた為

今回はChatGPTが提示した「3. 非同期処理」を実装することにしました。

攻略法:非同期処理の実装

こんな構成で作り直しました。

それぞれのfunctionはこんな感じ(Python3)
※それぞれ簡略版です。これだけでは動かないので試したい場合はダウンロードを!
www.ap-com.co.jp

↓function1 ※正式版にはSlackAPIのクレデンシャルや引数チェックが含まれています。

import json
import boto3
from urllib.parse import parse_qs
import datetime
import os

def lambda_handler(event, context):
    
    # 別のLambda関数に渡すパラメータを作成する
    params = parse_qs(event['body'])
    text = params['text'][0]
    payload = {'text': params['text'][0]}
    
    #Slackレスポンスする内容の定義
    words = [e for e in text.split(' ')]
    user_name = words[0]
    group_name = words[1]
    password = words[2]


    # 別のLambda関数を非同期で呼び出す
    client = boto3.client('lambda')
    function_name = 'slack-iam-user-create-excute-handler'
    invocation_type = 'Event'
    response = client.invoke(
        FunctionName=function_name,
        InvocationType=invocation_type,
        Payload=json.dumps(payload)
    )
    
    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": "application/json"
        },
        "body": json.dumps(payload)
    }

↓function2 ※正式版にはポリシーアタッチやグループ追加、クレデンシャル発行も含まれてます。

import boto3

def create_iam_user(user_name):
    """
    IAMユーザーを作成する
    """
    client = boto3.client('iam')
    response = client.create_user(
        UserName=user_name
    )
    return response


def lambda_handler(event, context):
    text = event['text']
    words = [e for e in text.split(' ')]
    user_name = words[0]
    password = words[2]
    sts_client = boto3.client('sts')
    account_id = sts_client.get_caller_identity()['Account']
    policy_arn = f"arn:aws:iam::{account_id}:policy/Customer_IAM_MFA_Policy"

    # IAMユーザーの作成
    create_user_response = create_iam_user(user_name)
    print("IAMユーザーが作成されました:", create_user_response)

    return {
        'statusCode': 200,
        'body': 'IAMユーザーが作成されました'
    } 

関数の呼び出しでresponseに必要な値を代入してあげるだけでいけました。
別関数に値を渡す際はdict形式で渡さないといけないようです。

おわりに

非同期処理といえばSQSって思ってたのですが
案外functionからfunctionをキックするだけでよかったでのシンプルな構成となりました。
引数を別functionへ渡す処理を初めて書いたので勉強になりました!
3秒問題いつかAPI側でゆるくなるといいなぁ

お知らせ

APCはAWSセレクトティアサービスパートナー認定を受けております。

その中で私達クラウド事業部はAWSなどのクラウド技術を活用したSI/SESのご支援をしております。

https://www.ap-com.co.jp/service/utilize-aws/

また、一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。

www.ap-com.co.jp

本記事の投稿者: y-shimizu
AWSをメインにインフラ系のご支援を担当しています。 https://www.credly.com/users/giiiiiyu777/badges