GitHub ActionsでECS Exec用のコンテナを起動してみた
ECSを使用していると、DBへの踏み台サーバーなどの用途でECSタスクを起動したくなる時があると思います。
毎回、手動でコマンドを実行し、ECSタスクを起動・接続するのがかなり面倒だったのでGitHub ActionsでECSタスクの起動と接続コマンドの表示を実装することにしました。
コードはこちら
name: ECS Exec
on:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
env:
AWS_REGION: [リージョン名]
AWS_ECS_CLUSTER: [ECSクラスター名]
AWS_ECS_TASK: [ECSタスク]
jobs:
run-ecs-task:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Get latest task definition revision
id: get-latest-task-def
env:
TASK_FAMILY: ${{ env.AWS_ECS_TASK }}
run: |
LATEST_TASK_DEF=$(aws ecs list-task-definitions --family-prefix ${{ env.TASK_FAMILY }} --sort DESC --max-items 1 --query "taskDefinitionArns[0]")
echo "latest_task_def=$LATEST_TASK_DEF" >> $GITHUB_OUTPUT
- name: Get ECS service network configuration
id: get-network-config
env:
CLUSTER_NAME: ${{ env.AWS_ECS_CLUSTER }}
SERVICE_NAME: ${{ env.AWS_ECS_TASK }}
run: |
NETWORK_CONFIG=$(aws ecs describe-services --cluster ${{ env.CLUSTER_NAME }} --services ${{ env.SERVICE_NAME }} --query "services[0].networkConfiguration.awsvpcConfiguration" --output json)
SUBNET=$(echo $NETWORK_CONFIG | jq -r '.subnets[0]')
SECURITY_GROUP=$(echo $NETWORK_CONFIG | jq -r '.securityGroups[0]')
echo "subnet=${SUBNET}" >> $GITHUB_OUTPUT
echo "security_group=${SECURITY_GROUP}" >> $GITHUB_OUTPUT
- name: Run ECS Task
id: run-ecs-task
env:
LATEST_TASK_DEF: ${{ steps.get-latest-task-def.outputs.latest_task_def }}
ECS_TASK_SUBNET: ${{ steps.get-network-config.outputs.subnet }}
ECS_TASK_SECURITY_GROUP: ${{ steps.get-network-config.outputs.security_group }}
CONTAINER_NAME: ${{ env.AWS_ECS_TASK }}
run: |
result=$(aws ecs run-task \
--cluster ${{ env.AWS_ECS_CLUSTER }} \
--launch-type FARGATE \
--enable-execute-command \
--task-definition ${{ env.LATEST_TASK_DEF }} \
--network-configuration 'awsvpcConfiguration={subnets=["${{ env.ECS_TASK_SUBNET }}"],securityGroups=["${{ env.ECS_TASK_SECURITY_GROUP }}"],assignPublicIp="DISABLED"}' \
--tags key=StartedBy,value=GitHubActions \
--overrides '{"containerOverrides": [{"name": "${{ env.CONTAINER_NAME }}", "command": ["sleep", "7200"]}]}' \
--count 1)
echo "Run Task result: $result"
- name: Echo ECS Exec command
id: echo-ecs-exec-command
env:
TASK_ARN: ${{ steps.run-ecs-task.outputs.task_arn }}
run: |
echo "ECSタスクへログインするコマンド aws ecs execute-command --cluster ${{ env.AWS_ECS_CLUSTER }} --task ${{ env.TASK_ARN }} --container ${{ env.AWS_ECS_TASK }} --interactive --command /bin/sh"
事前処理
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
AWSにアクセスするための認証情報を設定しています。
検証のため、簡易的にアクセスキーを使用していますが、IAMロール認証を推奨します。
最新のタスク定義を取得
- name: Get latest task definition revision
id: get-latest-task-def
env:
TASK_FAMILY: ${{ env.AWS_ECS_TASK }}
run: |
LATEST_TASK_DEF=$(aws ecs list-task-definitions --family-prefix ${{ env.TASK_FAMILY }} --sort DESC --max-items 1 --query "taskDefinitionArns[0]")
echo "latest_task_def=$LATEST_TASK_DEF" >> $GITHUB_OUTPUT
ECSタスクの起動に使用したいタスク定義の最新のARNを取得しています。
サブネット、セキュリティグループを取得
- name: Get ECS service network configuration
id: get-network-config
env:
CLUSTER_NAME: ${{ env.AWS_ECS_CLUSTER }}
SERVICE_NAME: ${{ env.AWS_ECS_TASK }}
run: |
NETWORK_CONFIG=$(aws ecs describe-services --cluster ${{ env.CLUSTER_NAME }} --services ${{ env.SERVICE_NAME }} --query "services[0].networkConfiguration.awsvpcConfiguration" --output json)
SUBNET=$(echo $NETWORK_CONFIG | jq -r '.subnets[0]')
SECURITY_GROUP=$(echo $NETWORK_CONFIG | jq -r '.securityGroups[0]')
echo "subnet=${SUBNET}" >> $GITHUB_OUTPUT
echo "security_group=${SECURITY_GROUP}" >> $GITHUB_OUTPUT
手動で設定してもいいですが、面倒なのでネットワーク設定も取得するようにしました。
これはクラスターにサービスタスクが稼働している前提です。
稼働しているサービスタスクの設定からサブネット、セキュリティグループの情報を取得しています。
タスク起動
- name: Run ECS Task
id: run-ecs-task
env:
LATEST_TASK_DEF: ${{ steps.get-latest-task-def.outputs.latest_task_def }}
ECS_TASK_SUBNET: ${{ steps.get-network-config.outputs.subnet }}
ECS_TASK_SECURITY_GROUP: ${{ steps.get-network-config.outputs.security_group }}
CONTAINER_NAME: ${{ env.AWS_ECS_TASK }}
run: |
result=$(aws ecs run-task \
--cluster ${{ env.AWS_ECS_CLUSTER }} \
--launch-type FARGATE \
--enable-execute-command \
--task-definition ${{ env.LATEST_TASK_DEF }} \
--network-configuration 'awsvpcConfiguration={subnets=["${{ env.ECS_TASK_SUBNET }}"],securityGroups=["${{ env.ECS_TASK_SECURITY_GROUP }}"],assignPublicIp="DISABLED"}' \
--tags key=StartedBy,value=GitHubActions \
--overrides '{"containerOverrides": [{"name": "${{ env.CONTAINER_NAME }}", "command": ["sleep", "7200"]}]}' \
--count 1)
echo "Run Task result: $result"
前述の処理で取得した最新のタスク定義、サブネット、セキュリティグループを使ってタスクを起動します。
ECS Execを使用するため、--enable-execute-command
をオプションに追加しています。
起動したコンテナを放置しても勝手に停止するように、--overrides '{"containerOverrides": [{"name": "${{ env.CONTAINER_NAME }}", "command": ["sleep", "7200"]}]}'
で起動時のコマンドをsleepに上書きしています。
コンテナに接続するコマンドを表示
- name: Echo ECS Exec command
id: echo-ecs-exec-command
env:
TASK_ARN: ${{ steps.run-ecs-task.outputs.task_arn }}
run: |
echo "ECSタスクへログインするコマンド aws ecs execute-command --cluster ${{ env.AWS_ECS_CLUSTER }} --task ${{ env.TASK_ARN }} --container ${{ env.AWS_ECS_TASK }} --interactive --command /bin/sh"
起動したタスクに接続するためのコマンドをログに表示します。
CloudShellなどからコマンドを実行してタスクに接続します。
slackなどに通知するようにしても良さそうです。
まとめ
こういうワークフローを作成しているとAWS CLIって本当になんでもできそうな気がしています。
もっと活用して、作業効率をどんどん上げていきたいと思います!
Discussion