こんにちは。streampackのfadoです。厳しい暑さが続いておりますがみなさん、いかがお過ごしでしょうか。まだまだ油断は禁物ですのでうがいと手洗いを忘れないようにしましょう。さて、今回は表題にも書きました通り、AWS Systems Manager
を利用し、AWS Lambda
及びAmazon API Gateway
と連携したシステムでAmazon EC2
インスタンス上でコマンドを実行させる方法を簡単に説明していきたいと思います。
注意事項
- Linuxコマンド及び
AWS Lambda
,Amazon API Gateway
,AWS Systems Manager
の知見がある方を対象にしています。 AWS Lambda
はPython3で記載しています。- ffmpegのRTMP送信先は
AWS Elemental MediaLive
->AWS Elemental MediaPackage
にしていますがスコープ外ですのでセットアップ等は割愛させていただきます。
リソース
- Amazon EC2 (以下 EC2)
- AWS Systems Manager(以下 SSM)
- AWS Lambda (以下 Lambda)
- Amazon API Gateway(以下 API Gateway)
- AWS IAM (IAM ロールとIAM ポリシー)
手順
EC2
- OSは
Amazon Linux 2
を使っています。AWS Systems Manager
エージェントとffmpegが動く環境であればどのOSでも問題ありません。 AmazonSSMManagedInstanceCore
ポリシーがアタッチされたIAM ロール
を対象EC2
インスタンスにつけます。AmazonSSMManagedInstanceCore
はEC2
インスタンスがSSM
のメイン機能を取り扱えるように許可してくれるポリシーです。- 例ではffmpegコマンドを利用しますので事前にインストールしておきましょう。
- 動画はフリー素材を使っています。
SSM
AWS Systems Manager
コンソールにて「インスタンスとノード」 -> 「マネージドインスタンス」のタブで対象EC2インスタンスが登録されていることを確認します。表示に出ていない場合はEC2
にアタッチされているロールにAmazonSSMManagedInstanceCore
ポリシーが漏れている可能性があります。
Lambda
Lambda
のIAM ロール
にAWS Systems Manager
のssm:SendCommand
とssm:ListCommandInvocations
アクションを利用しているのでこの二つのアクションが含まれるポリシーを付けます。Lambda
のIAM ロール
専用のポリシーにしたいので、インランポリシーでの作成になります。
関数コードは次の通りです。
lambda_handler.py
import boto3 import time import json apigw = boto3.client('apigateway') ssm = boto3.client('ssm') ## 対象インスタンスを定義 instance_id = ['i-XXXXXXXXX'] ## 実行するコマンドを定義 start_cmd = "/usr/local/bin/ffmpeg -re -stream_loop -1 -i /home/ec2-user/media/bbb_sunflower_1080p_30fps_normal.mp4 -c:v copy -c:a aac -flags +loop-global_header -f flv rtmp://XX.XX.XX.XX:1935/test/live < /dev/null > /tmp/ffmpeg.log 2>&1 &" stop_cmd = "/usr/bin/killall ffmpeg" def lambda_handler(event, context): try: ## API GatewayのURL pathによって実行するコマンドを区別 api_path = event['path'] if "start" in api_path: cmd_to_execute = {'commands': [start_cmd]} elif "stop" in api_path: cmd_to_execute = {'commands': [stop_cmd]} else: return "No matches,exiting..." ### send-commandを実行 response = ssm.send_command( DocumentName = "AWS-RunShellScript", InstanceIds = instance_id, Comment = 'Executing ffmpeg command...', TimeoutSeconds = 600, Parameters = cmd_to_execute ) command_id = response['Command']['CommandId'] ## send_commandのCommandIdid値が取得できるまで数秒かかることもあるため5秒間隔をおく time.sleep(5) ## ステータスを取得 list_invocations = ssm.list_command_invocations( CommandId = command_id, Details = True ) ### Api Gatewayに渡すレスポンスを定義 body = list_invocations['CommandInvocations'][0]['Status'] return { 'statusCode': 200, 'headers': {'Content-Type': 'application/json'}, 'body': body } except Exception as e: raise e
詳細な説明は省きますが上記はAPI Gateway
から送られてきたURLを判別し、対象EC2
インスタンスに対しstartまたはstopコマンドの実行命令を送ります。コマンド実行が成功ならSuccess,失敗ならFailedのレスポンスが戻ってくる流れとなっています。
API Gateway
次のように設定します。リソース名は分かりやすいものにしましょう。
尚、メソッドはGETではなくPOSTにします。APIのURLを叩くだけで実行されないようにするためです。
プロトコル | メソッド | リソース名 |
---|---|---|
REST | POST | /start |
REST | POST | /stop |
メソッドを設定する際に「Lambda プロキシ統合の使用」にチェックを入れます。これはAPI Gateway
が受信リクエストをLambda
関数の入力eventパラメータにマッピングさせるためです。
検証
startコマンド
1.設定したstart用APIでPOSTメソッドを用いて実行します。「Success」レスポンスが返ってきたら問題なくコマンドが実行されたことになります。「Failed」レスポンスの場合は、EC2インスタンスのamazon-ssm-agent.logをご確認ください。
$ curl -X POST https://xxxxxxx.execute-api.ap-northeast-1.amazonaws.com/ffmpeg/start Success
2.EC2
インスタンス上でffmpegプロセスが起動できていることを確認できました。
$ ps axu|grep ffmpeg root 3119 9.9 1.0 91388 21100 ? S 01:23 0:00 /usr/local/bin/ffmpeg -re -stream_loop -1 -i /home/ec2-user/media/bbb_sunflower_1080p_30fps_normal.mp4 -c:v copy -c:a aac -flags +loop-global_header -f flv rtmp://54.249.218.4:1935/test/live ec2-user 3121 0.0 0.1 110536 2236 pts/0 S+ 01:23 0:00 grep --color=auto ffmpeg
3.配信が問題なくできていることも確認できました。
(配信はスコープ外なのでできていなくてもffmpegプロセスさえ動いていれば問題ありません)
stopコマンド
1.設定したstop用APIでPOSTメソッドを用いて実行します。「Success」レスポンスが返ってきたら問題なくコマンドが実行されたことになります。「Failed」レスポンスの場合は、EC2インスタンスのamazon-ssm-agent.logをご確認ください。
$ curl -X POST https://xxxxxxx.execute-api.ap-northeast-1.amazonaws.com/ffmpeg/stop Success
2.EC2
インスタンス上でffmpegプロセスが起動できていることを確認できました。
$ ps axu|grep ffmpeg ec2-user 3108 0.0 0.1 110536 2236 pts/0 S+ 01:22 0:00 grep --color=auto ffmpeg
SSM
からのコマンド実行に問題がないかEC2
インスタンスのログでも確認することができます。
/var/log/amazon/ssm/amazon-ssm-agent.logの一部
}, "documentStatus": "Success", "documentTraceOutput": "", "runtimeStatus": { "aws:runShellScript": { "status": "Success", "code": 0, "name": "aws:runShellScript", "output": "", "startDateTime": "2020-09-17T16:08:17.477Z", "endDateTime": "2020-09-17T16:08:17.488Z", "outputS3BucketName": "", "outputS3KeyPrefix": "", "stepName": "", "standardOutput": "", "standardError": "" }
結論
AWS Systems Manager
,AWS Lambda
とAmazon API Gateway
を活用すればEC2
インスタンス上でコマンドを簡単に実行させることができます。今回は単純な処理を例に挙げて説明しましたが踏み込んだコマンドを実行するスクリプトを用意すればさらに高度な処理ができ、かつその実行結果を表示させることも可能です。それではまた次回!
参考文献
https://aws.amazon.com/jp/systems-manager/
https://aws.amazon.com/jp/lambda/
https://aws.amazon.com/jp/api-gateway/
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html