Perl Hackers Hub

第61回GitHub ActionsとAmazon ECSを使ったDockerアプリケーションの自動デプロイ(1)

本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーは、はてなでマンガビューアの開発に携わり、電子工作によるガジェットを趣味で開発している野口啓介さんで、テーマは「GitHub ActionsとAmazon ECSを使ったDockerアプリケーションの自動デプロイ」です。

本稿のサンプルコードは、WEB+DB PRESS Vol.116のサポートサイトから入手できます。また、本稿はDocker[1]やdockercompose[2]の基本的な知識はあるものとします。

リモートの開発環境もDocker化

2013年3月のDockerの登場から7年が経ちました。周辺ツールの充実もあり、Web開発の現場にもDockerの恩恵が届くようになりました。ローカル開発環境がDocker化された人も多いでしょう。

しかし、ステージング環境などのチームで動作確認する場面では、まだDockerを使っていない人が多いと思います。そこで今回は、PerlアプリケーションをGitHub Actionsを用いてAmazon Elastic Container Service(以下、ECS)にデプロイし、チーム開発でも自動的にDockerを活用する方法をお伝えします。ECSは、DockerコンテナをAWSAmazon Web Services上で動かすマネージドサービスです。

Amazon ECS─⁠─クラウドで動作するマネージドコンテナサービス

(1)では、ECSを使ってPerlアプリケーションをAWS上に構築します。

ECSは、AWSで動作するマネージドなdockercomposeだとみなせます。docker-composeがそうであるように、ECSを動作させるためにはイメージや設定が必要です。ただし、ECSは多数のアクセスや障害にも耐え得る冗長構成を前提としているため、dockercomposeとは概念が若干異なります。

ECSについて詳しくは、公式ドキュメントを参照してください。

Amazon ECSにデプロイできるPerlアプリケーションを作ろう

本項では、Amazon ECSにデプロイできるPerlアプリケーションを作ります。

アプリケーションのディレクトリ構造

今回は、PSGIPerl Web Server Gateway Interfaceアプリケーションを使用します。完成時のディレクトリ構造は次のとおりです(次節以降で作成するもの含んでいます⁠⁠。アプリケーション周りのファイルはapplication以下に、それ以外のファイルはプロジェクトルートに配置します。

$ tree -a
.
├── .github
│     └── workflows
│           ├── aws.yml
│           └── perl.yml
├── application
│     ├── Dockerfile
│     ├── app.psgi
│     ├── cpanfile
│     ├── cpanfile.snapshot
│     └── generate-cpanfile-snapshot.sh
├── docker-compose-ecs.yml
├── docker-compose.yml
└── task-definition.json

PSGIアプリケーションの作成

PSGIアプリケーションは定型文を返すだけのシンプルなもので、app.psgiの内容は次のとおりです。

my $app = sub {
  return [
    200,
    [ 'Content-Type' => 'text/plain' ],
    [ 'Hello, Plack!' ],
  ];
};

Dockerfileの作成

Dockerfileの内容は次のとおりです。

FROM perl:5.30

RUN mkdir /app
WORKDIR /app

COPY cpanfile /app/cpanfile
COPY cpanfile.snapshot /app/cpanfile.snapshot
RUN cpanm Carton

COPY app.psgi /app/app.psgi

RUN carton install --deployment

CMD [ \
  "carton", "exec", "--", "plackup", \
  "--app", "app.psgi", "--server", "Starlet", \
  "--port", "80", "--host", "0.0.0.0" \
  ]

Cartonをインストールし、必要なライブラリをインストールしたあとで、plackup[3]を実行してStarletサーバ[4]を起動しています。Dockerfile中ではcpanfile.snapshotを変更したくないため、Cartonは--deploymentオプション付きで起動しています。

docker-compose.ymlの作成と動作確認

手もとでも起動できることを確認します。

まず、docker-compose.ymlを作成します。

version: "3"
services:
  app:
    build: ./application
    ports:
      - "8000:80"

docker-compose.ymlをプロジェクトルートに配置した状態で、次のコマンドで動作確認を行います。

$ docker-compose up
Building app
(省略)
Successfully built c471b02b6fdb
(省略)
app_1 | Plack::Handler::Starlet: Accepting connections at
http://0.0.0.0:80/

無事に動作しました。のちほどイメージをECRにpushするために、イメージID(ここではc471b02b6fdbを控えておきましょう。

Amazon ECRへのイメージのpush

ECSでアプリケーションを動かすためには、DockerイメージがAmazon Elastic Container Registry(以下、ECR)にpushされている必要があります。

あらかじめECRにリポジトリを作成しておき、次のコマンドでpushします。

$ docker tag \
  イメージID \
  AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/リポジトリ名:latest
$ docker push \
  AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/リポジトリ名:latest

docker-compose-ecs.ymlの作成

次項でECSにデプロイするために、docker-compose.ymlを改変したdocker-compose-ecs.ymlを作成します。

version: "3"
services:
  app:
    image: AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaw
s.com/リポジトリ名:latest(実際は1行)
    ports:
      - "80:80"

イメージをビルドする代わりに、ECRのイメージを参照しています。また、今回ECRで使用するawsvpcインタフェースではコンテナポートとホストポートが一致している必要があるため、ホストに80番ポートをアタッチしています。

Amazon ECSへデプロイしよう

では、作成したPerlアプリケーションをECSにデプロイしましょう。ECSでは、アプリケーションはタスク定義を用いて定義します。

ECSにおけるタスク定義は、docker-composeにおけるdocker-compose.ymlに該当する概念です。タスク定義は、タスクがどのようなコンテナを持ち、どのようなDockerイメージを使うのか、メモリやCPUリソースをどの程度割り当てるのかなどを定義します。そのタスク定義をインスタンス化し、ECSの基盤上に動作させたものがタスクです。

以降では、ECSにタスクをデプロイする方法を解説します。

ecs-cliコマンドのインストール

タスクのECSへのデプロイは、今回はAWSが提供しているecs-cliコマンドを用いて行います。公式ドキュメントを参考に、ecs-cliコマンド[5]をインストールしておいてください。

プロファイルの設定

次に、ecs-cliコマンドが使うプロファイルを設定し、AWSにアクセスできるようにします。

$ ecs-cli configure profile \
  --profile-name プロファイル名 \
  --access-key $AWS_ACCESS_KEY_ID \
  --secret-key $AWS_SECRET_ACCESS_KEY
INFO[0000] Saved ECS CLI profile configuration プロファイル名.

ECSクラスタの作成

ECSクラスタを事前に作成しましょう。既存のクラスタを使う場合は、この作業は不要です。

$ ecs-cli up --vpc VPCのID \
  --subnets サブネットID[,サブネットID...] \
  --launch-type FARGATE \
  --region ap-northeast-1 \
  --cluster クラスタ名

このクラスタをecs-cliコマンドのデフォルトとして使うことを指定します。

$ ecs-cli configure \
  --cluster クラスタ名 \
  --default-launch-type FARGATE \
  --region ap-northeast-1 \
  --config-name クラスタ設定に付ける名前

Amazon ECSタスク定義の作成

そして、ecs-cliコマンドがどのクラスタをコンテナの起動先に指定するのか、どの起動タイプを選ぶのかをまとめた設定を、ecs-params.ymlに記述します。

version: 1
task_definition:
  # ↓あらかじめ実行ロールを作成しておくこと
  task_execution_role: ecsTaskExecutionRole
  ecs_network_mode: awsvpc
  task_size:
    mem_limit: 0.5GB
    cpu_limit: 256
run_params:
  network_configuration:
    awsvpc_configuration:
      subnets:
        - "subnet-xxxxxx"
      security_groups:
        - "sg-xxxxxxxxxxxx"
      assign_public_ip: ENABLED

これで、ECSにタスクをデプロイし、パブリックIPアドレスを経由してアクセスする準備が整いました。

いざデプロイ

次のコマンドを実行すると、PerlアプリケーションがECSクラスタにデプロイされます。

$ ecs-cli compose \
  -f docker-compose-ecs.yml \
  --project-name サービスに付ける名前 service up \
  --create-log-groups \
  --cluster-config クラスタ設定名 \
  --ecs-profile プロファイル名

コンテナが動作しているかを確認しましょう。

$ ecs-cli compose \
  --project-name サービスに付けた名前 service ps \
  --cluster-config クラスタ設定名 \
  --ecs-profile プロファイル名
Name      State    Ports  TaskDefinition  Health
..../app  RUNNING  xxx.yyy.zzz.aaa:80->80/tcp  app:1  UNKNOWN

公開IPアドレスが表示されていればアプリケーションはECS上で動作しています。

$ curl http://xxx.yyy.zzz.aaa/
Hello, Plack!

<続きの(2)こちら。>

WEB+DB PRESS

本誌最新号をチェック!
WEB+DB PRESS Vol.130

2022年8月24日発売
B5判/168ページ
定価1,628円
(本体1,480円+税10%)
ISBN978-4-297-13000-8

  • 特集1
    イミュータブルデータモデルで始める
    実践データモデリング

    業務の複雑さをシンプルに表現!
  • 特集2
    いまはじめるFlutter
    iOS/Android両対応アプリを開発してみよう
  • 特集3
    作って学ぶWeb3
    ブロックチェーン、スマートコントラクト、NFT

おすすめ記事

記事・ニュース一覧