シ〜らかんす

プログラミングとか、カメラとか。

Github ActionsからAWS SSMのRun Commandを使う

動作環境

やりたいこと

mainブランチへのマージをトリガーに、EC2上で git pull コマンドを実行して最新のコードを反映させたい

というのが今回のやりたいことです。

前提

  • EC2がRunning状態である
  • ec2-userでgitコマンドが使える状態にある
  • githubリポジトリのSecretsの設定にAWSのクレデンシャル情報を登録済みである

どうやるか

今回は、github actionsと、AWS Systems Managerを使用して、EC2インスタンス上でコマンドを実行させてみようと思います。

SSM Agentのセットアップ

まず最初のステップは、EC2にSSM Agentをインストールすることです。

インスタンスの種類によってはデフォルトでインストールされているようですが、今回利用するRed Hat Enterprise Linux 8のインスタンスには入っていないので、入れる必要があります。

公式ドキュメントのこちらのページRHELでのインストール方法が記されていますので、これの通りやります。

RHEL8系のインストール手順を見ると、まずは事前準備としてpython2か3が入っている必要があるようなので、入れます。

$ sudo yum install python3.9

続いて、SSMのインストーラーをダウンロードします。

$ sudo dnf install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm

無事完了すると、SSM Agentのサービスプロセスが起動していることを確認できます。

$ sudo systemctl status amazon-ssm-agent
● amazon-ssm-agent.service - amazon-ssm-agent
   Loaded: loaded (/etc/systemd/system/amazon-ssm-agent.service; enabled; vendor preset: disabled)
   Active: active (running) since Sun 2021-08-15 23:41:54 JST; 42s ago
 Main PID: 20343 (amazon-ssm-agen)
    Tasks: 13 (limit: 4821)
   Memory: 27.3M
   CGroup: /system.slice/amazon-ssm-agent.service
           ├─20343 /usr/bin/amazon-ssm-agent
           └─20397 /usr/bin/ssm-agent-worker

IAM Roleの作成

続いて、EC2に付与するIAMロールの作成を行います。

management consoleからやります。

IAMのコンソールからロールの作成に行き、ユースケースとしてEC2を選択します。

f:id:sas-surfer0jonny:20210816000504p:plain

続いて、ポリシーを付与する画面で、「AmazonEC2RoleforSSM」と検索して、出てきたポリシーを付与します。

f:id:sas-surfer0jonny:20210816000617p:plain

あとは、お好きな名前とdescriptionを書いてRoleを作成して、EC2インスタンスに付与してください。

AWSコンソール上でRun Commandする

github actionsの設定に入る前に、SSM Run Commandが動くかどうかの確認と、github actions上で実行するコマンドを明らかにしておきましょう。

そのために、AWSコンソール上からRun Commandを実行し、git pull コマンドを実行させてみます。

AWS SSMのコンソールの左のナビゲーションの中に、「Run Command」があるので、そこから「コマンドの実行」へ移ります。 f:id:sas-surfer0jonny:20210816091911p:plain

f:id:sas-surfer0jonny:20210816092023p:plain

コマンドドキュメントとして「AWS-RunShellScript」を選びます。

f:id:sas-surfer0jonny:20210816092222p:plain

コマンドのパラメータの入力欄に、実行したいコマンドを書き込みます。

注意点として、SSMのRun Commandはrootユーザーで実行されてしまうので、ec2-userでgitの設定を行なっている場合はうまく動きません。

git pullコマンドを実行するときは、sudo -u ec2-user git pull とすることでec2-userとして実行させましょう。

f:id:sas-surfer0jonny:20210816094505p:plain

次に、ターゲットとして、コマンドを実行したいEC2を選びます。 タグやリソースグループでの指定も行えるようですが、今回はインスタンスを手動で選択します。

f:id:sas-surfer0jonny:20210816092917p:plain

SSMのRun Commandのコンソールでは、親切にもAWS CLIで同じRun Commandを実行する際のコマンドを自動で生成してくれていますので、控えておきましょう。

f:id:sas-surfer0jonny:20210816093327p:plain

実行し、しばらくすると成功か失敗かわかります。

github actions上からRun Commandを実行する

すでにSSM Run Commandを実行するコマンドはわかっているので、あとはGithub Actionsから実行してやるだけです。

.github/workflows/ 配下にyamlを作成し、以下のように書きます。

name: Deploy to Sandbox
on:
  push:
    branches:
      - main

env:
  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Execute SSM Run Command
        id: exec
        run: |
          export RESPONSE=$(aws ssm send-command --document-name "AWS-RunShellScript" --document-version "1" --targets '[{"Key":"InstanceIds","Values":["i-078b1fde3fc75d7fb"]}]' --parameters '{"workingDirectory":[""],"executionTimeout":["3600"],"commands":["cd /home/ec2-user/grpc-sample/", "sudo -u ec2-user git pull"]}' --timeout-seconds 600 --max-concurrency "50" --max-errors "0" --region ap-northeast-1)
          export COMMAND_ID=$(echo $RESPONSE | jq .Command.CommandId)
          echo "::set-output name=commandId::${COMMAND_ID}"

      - name: Check Run Command Result
        run: |
          bash -x ./.github/scripts/check_command_result.sh ${{ steps.exec.outputs.commandId }}

「Execute SSM Run Command」のステップでRun Commandを実行した後に、「Check Run Command Result」のステップで、Run Commandの成否をチェックしています。

シェルスクリプトで実行していて、そのコードは以下の通りです。

#!/bin/bash

function succeeded () {
  status=$(aws ssm get-command-invocation --command-id $1 --instance-id ご自身のEC2インスタンスIDに書き換えてください | jq .Status)
  if [ $status = "Success" ]; then
    echo true
  else
    echo false
  fi
}

commandId=$1
i=1

while [ $i -le 10 ];
do
  succeeded=$(succeeded $commandId)
  if [ $succeeded ]; then
    exit 0
  fi
  i=$($i + 1 )
  sleep 3
done

exit 1