🔥

AWS Lightsail ContainersにGitHub Actionsでデプロイする。

2020/11/22に公開

AWS Lightsail Containersとは

Lightsail Containersとは2020年11月に発表されたAWS Lightsailの新機能です。Dokcerイメージを直接起動することができ、自動的にHTTP/HTTPSのエンドポイントが付与されます。コンテナをスケールアウトさせることもできます。GCPのCloudRunに似ています。CloudRunと比べると同時に複数のコンテナを起動できるというところが大きな違いです。

Lightsail Containersの公式記事
https://aws.amazon.com/jp/blogs/news/lightsail-containers-an-easy-way-to-run-your-containers-in-the-cloud/

Lightsail Containersへのデプロイ方法

Lightsail Containersには2つのデプロイ方法があります。残念ながら、Elastic Container Registryなどのプライベートリポジトリを直接指定する方法は今のところ用意されていないようです。(2020年11月時点)

  • 公開リポジトリのコンテナを指定してデプロイする
  • aws lightsail push-container-image を使って登録したイメージを指定してデプロイする

GitHub Actionsからデプロイする

おおまかな流れとしては、下記になります。

  1. Dockerイメージをビルドする
  2. aws lightsail push-container-image でイメージをLightsail Containersに登録する
  3. aws lightsail create-container-service-deployment で登録済みのイメージをデプロイする

起動時パラメータのファイル化

現実のシステムでは起動時に複雑な設定をすることになると思うので、CIで実行するコマンドラインを単純にできるように、コマンドラインパラメータをJSONで指定できるようにします。AWSのコマンドでは--generate-cli-skeleton--cli-input-jsonのオプションを使ってJSONでパラメータを渡すことができますので、これを活用します。また、JSON内にCI上でしか取得できない値を変数として記述する必要があるため、ここではERBを使ったJSONテンプレートからCI上で最終的なJSONファイルを出力できるようにします。

コンテナを起動するコマンドのJSONは下記のコマンドで出力できます。

aws lightsail create-container-service-deployment --generate-cli-skeleton

下記のように設定用のJSONのスケルトンが出力されます。

{
    "serviceName": "",
    "containers": {
        "KeyName": {
            "image": "",
            "command": [
                ""
            ],
            "environment": {
                "KeyName": ""
            },
            "ports": {
                "KeyName": "HTTPS"
            }
        }
    },
    "publicEndpoint": {
        "containerName": "",
        "containerPort": 0,
        "healthCheck": {
            "healthyThreshold": 0,
            "unhealthyThreshold": 0,
            "timeoutSeconds": 0,
            "intervalSeconds": 0,
            "path": "",
            "successCodes": ""
        }
    }
}

これに必要なパラーメータを埋めていきます。serviceNameはコマンドラインパラメータとして指定するので削除します。

下記はRuby on Railsを起動する例です。変数となる部分はERBの書式に則っています。
ここではファイル名をcontainer.json.erbとします。

container.json.erb
{
    "containers": {
        "api": {
          "image": "<%= image %>",
            "command": [
                "rails",
                "server",
                "-e",
                "production"
            ],
            "environment": {
                "RAILS_LOG_TO_STDOUT": "true",
		"RAILS_MASTER_KEY": "<%= master_key %>",
                "SOME_ENV_VARS": "HOGEHOGE"
            },
            "ports": {
                "3000": "HTTP"
            }
        }
    },
    "publicEndpoint": {
        "containerName": "api",
        "containerPort": 3000,
        "healthCheck": {
           "healthyThreshold": 2,
           "unhealthyThreshold": 2,
           "timeoutSeconds": 3,
           "intervalSeconds": 5,
           "path": "/api/v1/health",
           "successCodes": "200-499"
        }
    }
}

GitHub Actionsのワークフローを作る

こちらもRuby on Railsの場合の例です。

GitHubのSecretsに下記が必要です。

  • AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY : AWS Lightsailのコマンドの実行に必要な権限を持ったAWSアクセスキー
  • RAILS_MASTER_KEY : Ruby on Railsのcredentialsを復号するためのキー情報
name: deploy rails

on:
  push:
    branches:
      - master

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Install Utilities
      run: sudo apt-get install -y jq ruby
    - name: Install AWS Client and LightsailControl Plugin
      run: |
        curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
        unzip awscliv2.zip
        sudo ./aws/install
        aws --version
        curl "https://s3.us-west-2.amazonaws.com/lightsailctl/latest/linux-amd64/lightsailctl" -o "lightsailctl"
        sudo mv "lightsailctl" "/usr/local/bin/lightsailctl"
        sudo chmod +x /usr/local/bin/lightsailctl
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ap-northeast-1
    - name: Build Docker Image
      run: |
        docker build -t example-image:latest .
    - name: Push and Deploy
      env:
        RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
      run: |
        service_name=container-service-1
        aws lightsail push-container-image \
          --region ap-northeast-1 \
          --service-name ${service_name} \
          --label api \
          --image example-image:latest
        aws lightsail get-container-images --service-name ${service_name} | jq --raw-output ".containerImages[0].image" > image.txt
        erb image=$(cat image.txt) master_key=$RAILS_MASTER_KEY container.json.erb > container.json
        aws lightsail create-container-service-deployment --service-name ${service_name} --cli-input-json file://$(pwd)/container.json

ポイントは最後のPush and Deployのジョブにまとまっており、下記の流れになっています。
(ここではLightsail Containersのサービス名が container-service-1 である想定です。)

  1. aws lightsail push-container-image でビルドしたDockerイメージを登録する。
  2. 登録したイメージのLightsailにおけるイメージ名を取得する必要があるため、aws lightsail get-container-images でイメージの一覧を取得してイメージ名を抜き出しています。JSONで情報が返ってくるためjqコマンドで情報を抜き出します。
  3. イメージ名やSecretsの情報を使って設定テンプレートcontainer.json.erbから起動用の設定ファイルcontainer.jsonを作成します。
  4. aws lightsail create-container-service-deploymentcontainer.jsonを指定してデプロイを実行します。

Discussion