本連載では第一線の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コンテナをAWS(Amazon Web Services )上で動かすマネージドサービスです。
Amazon ECS──クラウドで動作するマネージドコンテナサービス
(1)では、ECSを使ってPerlアプリケーションをAWS上に構築します。
ECSは、AWSで動作するマネージドなdockercomposeだとみなせます。docker-composeがそうであるように、ECSを動作させるためにはイメージや設定が必要です。ただし、ECSは多数のアクセスや障害にも耐え得る冗長構成を前提としているため、dockercomposeとは概念が若干異なります。
ECSについて詳しくは、公式ドキュメント を参照してください。
Amazon ECSにデプロイできるPerlアプリケーションを作ろう
本項では、Amazon ECSにデプロイできるPerlアプリケーションを作ります。
アプリケーションのディレクトリ構造
今回は、PSGI(Perl 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)はこちら 。>
特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現!
特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう
特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、NFT