Perl Hackers Hub

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

この記事を読むのに必要な時間:およそ 3 分

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

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

注1)
執筆時点(2020年3月)で最新のDocker Engine 19.03.8を使用しています。
注2)
執筆時点で最新のdocker-compose 1.25.4を使用しています。

リモートの開発環境も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番ポートをアタッチしています。

注3)
執筆時点で最新のPlack 1.0047を使用しています。
注4)
執筆時点で最新のStarlet 0.31を使用しています。

著者プロフィール

野口啓介(のぐちけいすけ)

大学在学中に『はてなサマーインターン』に参加し,卒業後の2016年に株式会社はてなに入社。型のパワフルさを活かした開発が得意。

入社後はScalaによる『はてなブックマーク』のリプレイスメントプロジェクトや,『はてなブログ』の開発に参加。

アプリケーションエンジニアとしてバックエンド部分を主に担当しつつも,趣味で運用している自宅サーバで培ったインフラ寄りの知識でチームをサポートしている。

2018年8月より同社のマンガビューワの開発に携わっている。

GitHub:windymelt
Twitter:@windymelt
Hatena:id:Windymelt