4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

iRidgeAdvent Calendar 2019

Day 10

AWS Chatbotを使ってSlackからALBのログを検索する

Last updated at Posted at 2019-12-09

この記事はiRidge Advent Calendar 2019 10日目の記事です。

先日AWS Chatbotを利用してSlackからAWSコマンドを発行する機能のβ版が発表されました。

もともとAWS Chatbotを利用してCost ExplorerのBudgets AlertをSlackに流すくらいのことはやっていたのですが、コマンドが実行できることでやれることがより増えそうだなと思い、今回はよく業務で行っていたALBのログ検索を行い結果を取得するのをSlackで完結できるかを試してみました。

今回やること

前提

Athenaのテーブル作成、Partitionの作成は完了している前提で書いてます。テーブルの作成等はAWSの下記のドキュメントが参考になると思います。

AWS Chatbotを設定する

まずはChatbotの設定をしていきます。

  1. workspaceの設定をします。Chat clientSlackにしてConfigure clientを押すとworkspaceにアクセスするための権限を求めてくるので、問題なければ許可します。

    スクリーンショット 2019-12-09 10.38.46.png

  2. Chatbotを利用するchannelを選びます。

    スクリーンショット 2019-12-09 10.33.38.png

  3. Chatbotが利用するIAM Roleを定義します。ある程度まとまったPolicyがテンプレートとして準備されているようです。作成したタイミングでは下記のテンプレートが準備されていました。

    • Notification Permission: CloudWatchからメトリックグラフを取得するのを許可する権限
    • Read-only command permissions: Readのコマンドを許可する権限
    • Lambda-invoke command permissions: サポートされているクライアントでLambdaの呼び出しを許可する権限
    • AWS Support command permissions: support APIを呼び出しを許可する権限

    スクリーンショット 2019-12-09 10.33.55.png

    今回は、Lambdaの呼び出しだけで良かったので、Lambda-invoke command permissionsのテンプレートを利用しました。Defaultだと、すべてのLambdaに対するinvokeが許可されているようなので、適宜呼び出せる関数は制御したほうが良さそうです。下記がテンプレートで付与されたLambdaの呼び出し用のIAM Policyです。

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "lambda:invokeAsync",
                    "lambda:invokeFunction"
                ],
                "Resource": [
                    "*"
                ]
            }
        ]
    }
    

あとSNSの設定項目がありますが、今回は不要なので割愛します。CloudWatchのAlarmや、CostExplorerのBudgetsに関する通知を送るときは設定が必要になってきます。

Lambdaを作る

Slack から呼び出すLambdaを作成します。Lambdaの中では下記の処理を実装しました。

  • Athenaのクエリを実行、結果をS3に出力させる
  • 配置された結果ファイルに対するpresigned-urlを取得する

下記コードです。テーブル等は適当なものに変更してるのでそのままじゃ動かないです。

サンプルコード
main.py
import boto3
import time

from urllib.parse import urlparse

athena_client = boto3.client('athena')
s3_client = boto3.client('s3')


query_format = '''
select * from <table>
limit 10
'''


def execute():
    response = athena_client.start_query_execution(
        QueryString=query_format,
        ResultConfiguration={
            'OutputLocation': 's3://<S3 bucket name to output result>',
        }
    )
    return response['QueryExecutionId']


def get_output_location(query_execution_id):
    while True:
        response = athena_client.get_query_execution(
            QueryExecutionId=query_execution_id
        )
        print(response['QueryExecution']['Status']['State'])
        if response['QueryExecution']['Status']['State'] in ['QUEUED', 'RUNNING']:
            time.sleep(5)
            continue
        if response['QueryExecution']['Status']['State'] == 'SUCCEEDED':
            return response['QueryExecution']['ResultConfiguration']['OutputLocation']
        print(response)
        return None


def get_presigned_url(location):
    parsed_s3path = urlparse(location)
    print(parsed_s3path)
    response = s3_client.generate_presigned_url(
                    'get_object',
                    Params={
                        'Bucket': parsed_s3path.netloc,
                        'Key': parsed_s3path.path.replace('/','')
                    },
                    ExpiresIn=3600)
    return response


def lambda_handler(context, event):
    query_execution_id = execute()
    output_location = get_output_location(query_execution_id)
    if output_location is None:
        print("Query Failed!!")
        return "Query Failed!!"
    presigned_url = get_presigned_url(output_location)
    print(presigned_url)
    return presigned_url


if __name__ == "__main__":
    context = {}
    event = {}
    lambda_handler(context, event)


Athenaに対してクエリを投げるときはstart_query_executionでクエリを実行したあと、get_query_executionを実行して結果の状態を把握する必要があるみたいでした。とりあえずLoopの中で5秒おきに実行するようにしていますが、実行時間が長いクエリだとLambdaのコストが増えるため微妙だなと思っています。SNSとかSQSを間に挟んで待ち時間を関数の外に出せば節約できそうですが、今回は一旦Lambda関数内でsleepさせることにしました。

Lambdaをデプロイする

Apexを使ってLambdaをデプロイしました。(よく使っているので)
ディレクトリ構成は下記のような感じです。

tree
aws-chatbot/
├── functions
│   └── athena-query-executor
│       └── main.py
└── project.json
project.json
{
  "name": "aws-chatbot",
  "description": "",
  "runtime": "python3.7",
  "memory": 128,
  "timeout": 300,
  "handler": "main.lambda_handler",
  "role": "arn:aws:iam::<Your AWS Account ID>:role/<Your role name>",
  "nameTemplate": "{{.Function.Name}}",
  "environment": {}
}

デプロイは下記コマンドで行います。

deploy
apex deploy -r ap-northeast-1 -p <your profile>

Lambdaに割り当てるIAM RoleにはAmazonAthenaFullAccessAmazonS3ReadOnlyAccessを割り当てました。本当は権限を絞っていきたいんですが、Athenaの権限が思ったより複雑だったので検証の段階では動かすことを優先しました。ちゃんと運用するときはしっかり理解した上での設定が必要だなと感じています。

SlackからLambdaを呼び出す

Lambdaがデプロイできたら、いよいよSlackからLambdaを呼び出してみます。

Chatbotの設定画面で設定したchannelに/invite @aws でchatbotのユーザーを招待します。
ユーザーが招待できたら次の内容をchannelに投下してLambdaを実行します。

@aws lambda invoke --function-name <Your function name> --region ap-northeast-1

スクリーンショット 2019-12-09 12.05.48.png

実行すると本当に実行してよいかを聞かれます。(スクリーンショットの Would you like~ あたり。) YES とするとLambdaの実行が始まります。
Lambdaの実行が完了すると戻り値として、Payload,ExecutedVersion,StatusCodeが返却されます。
どうやらPayloadはbase64エンコードされた状態で返却されるようです。。。(マスクしているので見えませんがbase64エンコードされた状態となっています。)想定ではここでpresined-urlをクリックしてAthena実行結果のcsvをゲットする算段だったのですが、ローカルでbase64デコードする一手間が挟まってしまいました。。
しかし、デコードした結果のURLからは正しくcsvを取得することができました。

まとめ

今回Chatbotを使ってみた所感です。

ChatOps環境を作りやすくなった

AWS Chatbotを使うとAWS連携がより手軽にできるようになったと感じました。以前AWS Chatbotが出る前にもSlackでスラッシュコマンド等を作ったことがあり、そのときはAPI Gateway + Lambdaを作成してバックエンドを作成したのですが、API Gatewayの設定が地味にめんどくさかった記憶があります。今回Chatbotで直接AWSコマンドを実行可能になり、API Gateway等を作る手間は削減されたのかなと感じます。個人的にオペレーション用に作るものはなるべく単純にしておきたいという思いがあるので、その点は少しスッキリしてよかったかなと思います。

権限管理は慎重に

SlackからAWSへの操作をできるようになるため、Chatbotに付与する権限は用途ごとにきっちり絞ったほうがよいなと感じました。DefaultのIAMポリシーテンプレートをそのまま使うのではなく、実行できる関数を絞っておくなどして、不用意な事故を防ぐ必要があるなと感じます。Chatbotに限った話ではないですが、ここで使うIAMにも必要最小限の権限を付与するというのを意識しておけば良さそうです。

個人の環境に左右されにくくなる

特定の操作をSlack上でできるので、よく行う運用作業をいくつかまとめられれば誰でもすぐログ調査等ができるようになる点はいいなと思いました。各個人に払い出されている権限、ツールのバージョン等を統一せずに調査等、必要な操作環境を提供できる点は良さそうです。

戻り値のPayloadをbase64デコードした状態で表示してほしい

今回試してみた結果Payloadがbase64エンコードされて返ってくることがわかりました。。base64デコードした状態で表示してくれれば戻ってきたpresigned-urlから直接ファイルがダウンロードできるのに、、、。ちなみに、AWS CLIでlambda invokeを実行するとoutfileに指定したファイルに戻ってきた結果が書き込まれるのですが、それはbase64デコードされた状態となっていました。

呼び出せるコマンドはまだ少ない?

当初Athenaのクエリ実行コマンドを、直接Slackから呼び出そうとしたのですが下記のエラーが出ました。まだ対応しているコマンドが限られているのかもしれません。今後呼び出せるコマンドが増えるといいなと思っています。

I can't run the command athena start-query-execution because it isn't enabled.

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?