【チュートリアル】 AmazonEC2で作業してnginxとphp-fpmのイメージをECSで動作させる
前回の続き
前回までで、nginx
とphp-fpm
の環境をローカルに構築し、docker-compose
にてそれぞれ起動してnginx側
からphp-fpm
と通信し、phpinfo()
を出力させる事に成功している。
今回はこの構成をどうやってECSで動作させるかを徹底解説していく。
ECRにローカルリポジトリをプッシュ
現在、記事通りに行ってくると、ECR
には最初の記事で作成したnginx
でシンプルなhtmlを表示するためのイメージリポジトリが残っているはずだ。今回はこれに加えてnginx
とphp-fpm
用の2つのリポジトリを追加でECR上に作成する。
ローカルの状態確認
現在のローカルの状態は以下の通りである。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
my-ecs-php-fpm latest f06015aafd46 7 hours ago 504MB
my-ecs-nginx latest 610939fb6090 7 hours ago 50.9MB
...
そうした時に
my-ecs-nginx
my-ecs-php-fpm
という2つの名前のイメージがあるのでそれぞれECRにリポジトリを作成する
ECR上に2つのリポジトリを作成する
ここでの手順は最初の記事に書いてあるので、必要であれば参照
practice/my-ecs-nginx
practice/my-ecs-php-fpm
という2つのリポジトリを追加する
設定項目がほとんど無いため、この手順は難しくないはず。
nginxのconfigを変更
docker-composeではphp-fpm:9000
で通信が可能であったが、構成にもよるが、ECSで1タスクの中で2コンテナを動作させる事を目指すとした場合、これは双方localhostで通信しなくてはならない。
構築した事が無いと何の話かわからないかもしれないのだが、すなわち今現在nginxのconfigでphp-fpmへの通信はTCPのphp-fpm:9000
を指定しているが、これ「ではなく」 localhost:9000
に変更しないといけないという事になる。
location ~ \.php$ {
root /usr/share/nginx/html;
# fastcgi_pass php-fpm:9000; # <----------------- これは無効化
fastcgi_pass localhost:9000; # <----------------- PHP-FPMサービス名とポート
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
このように変更してビルドする
tag付け後push
ここでも作業自体は前回の記事に書いてあるので詳細は割愛するが
コマンドが複雑になってきているため、以下のようなスクリプトを用意した
#!/bin/bash
# 設定
AWS_REGION="ap-northeast-1"
AWS_ACCOUNT_ID="***************" # 置き換える事
ECR_REPOSITORY_PREFIX="practice"
NGINX_TAG="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_PREFIX/my-ecs-nginx:latest"
PHP_FPM_TAG="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_PREFIX/my-ecs-php-fpm:latest"
# AWS ECRにログイン
aws ecr get-login-password --region "$AWS_REGION" | docker login --username AWS --password-stdin "$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com"
# Nginx
docker build -t my-ecs-nginx -f ./nginx/Dockerfile . --no-cache
echo "Tagging and pushing my-ecs-nginx as $NGINX_TAG..."
# tag
docker tag my-ecs-nginx:latest "$NGINX_TAG"
# push
docker push "$NGINX_TAG"
# PHP-FPM
# build
docker build -t my-ecs-php-fpm -f ./php-fpm/Dockerfile . --no-cache
echo "Tagging and pushing my-ecs-php-fpm as $PHP_FPM_TAG..."
# tag
docker tag my-ecs-php-fpm:latest "$PHP_FPM_TAG"
# push
docker push "$PHP_FPM_TAG"
echo "All images have been pushed successfully!"
このスクリプトを使うにせよ、使わないにせよ、ECRにイメージが2つpushされている事が重要である。
タスク定義の修正
再度タスク定義の画面に戻ってみよう
するとここでは
既にmy-ecs-project-task-def
というタスク定義を作成しているはずで、今回はこのタスク定義を変更していく。これは主にイメージの指定の変更になる。
新しいリビジョンを作成する
新しいリビジョンの作成
コンテナ1の変更
このように、コンテナ1は/practice/my-ecs-nginx
に変更している
ECSサービスの更新
ここで順々に手繰っていき「サービスの更新」を押す
ここで最新のリビジョンにセットして必要なタスクを1にする
新たに割り当てられたIPアドレスを確認する
タスクが正常に起動してきたら、当該タスクIDをクリックして
パブリックIPを確認する
そこにアクセスすると
新しく画像付きのwebページが表示されるはずだ
しかし
phpは何も設定していないため、localhost:9000と通信が出来ずエラーとなる。これを解消する
php-fpmコンテナを追加する
ここでECSタスクは1つのElastic Network Interface (ENI) を持つため、タスク内の全てのコンテナが 同じネットワーク名前空間 を共有するという特性がある。これを利用し、同一コンテナ上にphp-fpmを組むとlocalhostで通信できるようになる。以降この設定を行う
コンテナ1の下にある「コンテナの追加」を押す
php-fpmのECRへのURIを記述し、必須コンテナを「はい」にする
以上を行い更新する。ここではドキュメントをごちゃごちゃ書いたり消したりしたせいでrev6になった
サービスの再更新
タスク定義の新リビジョンを追加したら常にサービスを再度更新する必要がある。
筆者の環境ではrev6が最新だったので、そうしている
リビジョンを更新するとまたタスクが自動的に新しいものと置き換わる
自動更新された、これには結構時間かかる
新しいIPアドレスでphpinfoを確認する
このデプロイメントで新しいIPアドレスが割り当てられるから
このトップページとか
phpの起動を確認しておく
まとめ
駆け足で行ってきたが、ここまでで3記事あるけど結構複雑化してきたはずだ。手順をしっかりまとめる必要がある
- ローカルでnginxとphp-fpmのイメージを作成
- docker-composeで確認
- 2つのECRリポジトリを作成
- php-fpmの通信をlocalhostへと変更
- localhostに設定を変更したイメージを再度作成
- ローカルで2つのイメージをタグ付け
- ECRにプッシュ
- タスク定義の更新
- サービスの更新(nginxのみ)
- php-fpmを含むタスク定義の更新
- サービスの更新(nginx+php-fpm)
- 確認
ざっと箇条書きにしただけでも、こんなにもある。
本記事ではphp-fpmを含まないタスク定義を作って最初に失敗してみせているが、そこを一発でうまくやったとしても手順の複雑みはそれほど解消しないので、やはり1つ1つ丁寧にメモを残しつつ設定していく以外手が無い。
しかもこれはまだdevelopment環境で行っている話なのでproduction環境だとVPCの作成から何から諸々あるので、何度も実行して馴染ませていく以外無いかもしれない。
ただし
慣れてくるとwebコンソールからほとんど作業しなくなる。awsコマンドを使ったりだとか設定情報をコードに落としていくいわゆるIaC
で管理するのでもう少し楽になるが、それでも結局手でおこなっている事の置き換えなので手で作れないと当然、Iac
を完全に理解するのは難しいだろう。
次回
これを踏まえてもう少し環境を整理しつつ、laravelのデプロイメントを行っていく事にする。
ただし、まだローテクでの更新になるかと思う。
補足
この記事では、1タスクに Nginx
と PHP-FPM
の 2つのコンテナを詰め込んでいるが、プロダクション環境ではこの構成は分離のメリットを活かせない場合が多い。
実際のところ、Nginx
は静的コンテンツのみを配信している。このケースでは index.html
や logo.png
などが対象で、処理が非常に軽いことが想像できるはずだ。
一方で、動的コンテンツの生成を担当する PHP-FPM
は、PHP による動的処理を行うため、ヘビーな処理が集中する。その結果、Nginx
側の負荷が低くても、PHP-FPM
側の負荷が高まった場合には PHP-FPM
タスクのみを増加させる(これをスケールアウトと言う)構成が合理的である。
しかし、この2つをまとめてしまうと、タスク全体としてのスケーリングが必要となり、Nginx
のリソースを無駄に増やすことになる。これではコストパフォーマンスの悪化に繋がる。
高負荷なサービスを運用する場合、Nginx
と PHP-FPM
を分離し、PHP-FPM
側のみを柔軟にスケーリングできる構成を常に意識することが重要である。
このような事をapache2でやるには、やれるんだけどApache2自体が比較的重厚で多機能なため、近年nginxが積極的に採用されている理由がここにあったりもする。(ただし開発用途等でオールインワンのサーバーを用意するとかいう場合は便利なので積極的に使っていい)
Discussion