💨

【チュートリアル】 AmazonEC2で作業して作る、ECSのためのphp動作環境

2025/01/23に公開

https://zenn.dev/catatsumuri/articles/9d1cd203776402

前回の続き

dockerコンテナでphpを動作させる環境について

これは主に2タイプある

  • apache2にphpモジュールを組込む手法 (1つのコンテナで完結する)
  • nginx + php-fpmで頑張る方法 (1つのコンテナで完結する方法もあるけど通常は2つのコンテナを使う)

現状のnginxの構成を確認後、nginxとphp用に分離させていく

前回まででの構成では

./Dockerfile
./index.html

このようにプロジェクトのルートに雑に展開するというシンプルな構成になっているが、まず、この構造を変更していく。

ディレクトリ構造を変更する

  • nginxというディレクトリを作り、Dockerfileを移動する
  • さらにpublicというディレクトリを作り、index.htmlを移動する

ここでさらにplacehold.jpというサービスでダミーの画像を作り、これをpublic/logo.pngとして保存する

curl 'https://placehold.jp/300x300.png?text=LOGO' -o public/logo.png

ここまでで以下のような構成を取るようにファイルを移動する事

.
├── nginx
│   └── Dockerfile       # Nginx用Dockerfile (ルートから移動)
└── public
    ├── index.html       # 静的HTMLファイル (ルートから移動)
    └── logo.png         # ダミーロゴ画像 (新規に追加)
作業ログ
admin@ip-172-31-29-222:~/my-ecs-project$ mkdir  public nginx
admin@ip-172-31-29-222:~/my-ecs-project$ ls
Dockerfile  index.html  nginx  public
admin@ip-172-31-29-222:~/my-ecs-project$ mv index.html  public/
admin@ip-172-31-29-222:~/my-ecs-project$ mv Dockerfile nginx/
admin@ip-172-31-29-222:~/my-ecs-project$ curl 'https://placehold.jp/300x300.png?text=LOGO' -o public/logo.png
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4339  100  4339    0     0  27440      0 --:--:-- --:--:-- --:--:-- 27636
admin@ip-172-31-29-222:~/my-ecs-project$ find
.
./nginx
./nginx/Dockerfile
./public
./public/logo.png
./public/index.html

nginx/Dockerファイルを書き換えてDockerイメージにpublicの内容をコピーする

nginx/Dockerfile
FROM nginx:alpine

COPY ./public /usr/share/nginx/html

EXPOSE 80

以上のように変更してビルドを試す

$ ls
nginx  public

このようにプロジェクトのトップディレクトリから以下のコマンドを実行する

docker build -t my-ecs-nginx -f ./nginx/Dockerfile .

イメージ名をmy-ecs-nginxに変更している。作成されたイメージはdocker image lsで見られる。

$ docker image ls
REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
my-ecs-nginx   latest    179f135ac531   2 minutes ago   50.9MB
nginx          alpine    f9d642c42f7b   8 weeks ago     50.9MB

前のが残っててもあんま気にしなくていい(掃除方法は調べるとすぐ出てくる)。

ここでmy-ecs-nginxが作成された事に注目する。

nginxのイメージからデフォルト設定をコピーし、編集後、再配置する

ここでpullしてきたnginxイメージの中に入っている /etc/nginx/conf.d/default.conf をコピーし、設定の雛形として利用する。dockerイメージからのコピーは以下のようにして行う事ができる。

docker run --rm -v $(pwd):/host nginx:alpine cp /etc/nginx/conf.d/default.conf /host/

dockerはイメージからコンテナーを生成して実行するのであるが、このコマンドはコンテナを一度だけ生成してコマンド実行後にコンテナーを消滅させる。ともあれこれでカレントディレクトリにdefault.confが存在しているはずである。

取得できたconfig
default.conf
server {
    listen       80;
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

このように比較的シンプルな設定になっている。

これを nginx/ ディレクトリへと移動しておく

cp default.conf nginx/
sudo rm default.conf

(コンテナからコピーすると権限が権限揃ってないので、これを変更するのが面倒だったのでcopyして消しただけ)

現状

.
├── nginx
|   ├── index.html
│   └── default.conf     # Nginxコンテナーからコピーしたオリジナルのdefault.conf
└── public
    ├── index.html       # 静的HTMLファイル
    └── logo.png         # ダミーロゴ画像

nginx/default.confの確認し、phpを実行するための設定に変更する

ここで設定を変更する。まずphp関連の設定のコメントを外し、以下のようにする

nginx/default.conf
    location ~ \.php$ {
        root           /usr/share/nginx/html; # PHPスクリプトのルートディレクトリ
        fastcgi_pass   php-fpm:9000;          # PHP-FPMサービス名とポート
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include        fastcgi_params;       # 必須のFastCGIパラメータをインクルード
    }

ここでパラメーターの変更などは特に解説しないが、fastcgi_pass php-fpm:9000の指定に着目すると、php-fpmというhostのポート9000に通信の指定を行っている。すなわち、php-fpmという名前で通信できるホストを別途作る必要があるという事になる。

fastcgi_param SCRIPT_FILENAME document_rootfastcgi_script_name; の解説

元の設定

fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;

ここでは、PHPスクリプトが /scripts ディレクトリ以下に存在すると仮定している。$fastcgi_script_name はリクエストされたスクリプト名(例えば: /index.php)を表しており、
つまり、この設定では、すべてのPHPスクリプトを /scripts ディレクトリの中から探そうとしている

修正後の設定

fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;

$document_root は現在のリクエストで設定された root ディレクティブの値(例: /usr/share/nginx/html)を表しており$fastcgi_script_name は先述の通り、リクエストされたスクリプト名(例: /index.php)を表す。

この設定により、PHPスクリプトの実際のフルパスが root で指定したドキュメントルートに基づいて動的に解決される、ここでは

 root   /usr/share/nginx/html;

の設定により /usr/share/nginx/html からサーチされる。すなわち、このパスに基いてfastCGIの先を作らなければならない。、といっても今はそのサーチ先を作っていないので何を言ってるのかよくわからないはずなので、ここでは記憶に留めておくこと。

ダミーロゴを使うようにpublic/index.htmlを変更する

public/index.htmlの内容を以下のように変更する。

public/index.html
<!DOCTYPE html>
<html>
<head>
  <title>My First Web Page</title>
</head>
<body>
  <h1>Hello, ECS Fargate!</h1>
  <p>This is my first containerized web page.</p>
  <img src="logo.png"> <!-- これ -->
</body>
</html>

imgタグを追加しただけ

phpinfoを実行するスクリプトを作る

さらに public/info.phpphpinfo() するだけの簡単なphpファイルを作る。

public/info.php
<?php phpinfo();

ここで public/ 以下は現状、以下のようになっているはずだ

.
├── nginx
|   ├── index.html
│   └── default.conf     # Nginxコンテナーからコピーしたオリジナルのdefault.conf
└── public
    ├── index.html       # 静的HTMLファイル
    ├── logo.png         # ダミーロゴ画像
    └── info.php         # phpinfo()を実行するたけのもの

設定を含めnginxにコピーするDockerfile

nginx/Dockerfile
FROM nginx:alpine

COPY ./public /usr/share/nginx/html
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf


EXPOSE 80

ここまでのファイル

nginx/Dockerfile
nginx/Dockerfile
FROM nginx:alpine

COPY ./public /usr/share/nginx/html
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf


EXPOSE 80
nginx/defaults.conf
nginx/default.conf
server {
    listen       80;
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
        #root           html;
        root           /usr/share/nginx/html;
        fastcgi_pass   php-fpm:9000;
        fastcgi_index  index.php;
        #fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

確実にビルドする

設定を変更なりした時にビルドプロセスを忘れる事がよくあるので、現状のファイル構成に従って以上のファイルを確実にビルドしておく。

docker build -t my-ecs-nginx -f ./nginx/Dockerfile .

これにより

  • nginxの設定が更新される
  • public/の内容がnginx内 /usr/share/nginx/html 以下にコピーされる

なお、この段階だとphp-fpmが解決できないため、nginxは起動できなくなる

phpの実行環境を整える

ここでは2つ目のコンテナである、php-fpmを作成する。これを構成するために、まずphp-fpm/ というディレクトリを作成する

mkdir php-fpm

この状態で php-fpm/Dockerfile を作成する

public以下をphp-fpmにコピーする

php-fpm/Dockerfile
FROM php:8.4-fpm
COPY ./public /usr/share/nginx/html

ここでpublicの内容を php-fpmでも /usr/share/nginx/html にコピーしている。これは先述した

/usr/share/nginx/html からサーチされる。すなわち、このパスに基いてfastCGIの先を作らなければならない。

この設定によるものである。

確実にビルドする

この段階でphp-fpmコンテナもビルドする

docker build -t my-ecs-php-fpm -f ./php-fpm/Dockerfile .

docker-composeの設定

今はnginxとphp-fpmのコンテナをそれぞれ2つdocker runで起動したが、これらの2コンテナを相互に通信させるためにはdocker-composeを使うのが楽。

前段の記事でこのツールの導入は済んでいるはずであるが、まだの場合は

sudo apt install docker-compose

で導入して欲しい

既存のdockerプロセスの停止

docker stop $(docker ps -q)

一括で停止できる。
IDを指定する事も可能だが、他のプロセスで止めたら駄目なものが無いならこれで停止すると楽であろう。

docker-compose.ymlの記述

docker-composedocker-compose.ymlというファイルを参照してその設定に従ってコンテナーを起動するツールだ。従ってこれを作成する必要がある。

docker-compose.yml
services:
  php-fpm:
    image: my-ecs-php-fpm
    networks:
      - app-network

  nginx:
    image: my-ecs-nginx
    ports:
      - "80:80"
    depends_on:
      - php-fpm
    networks:
      - app-network

networks:
  app-network:

このように設定する。

この状態のまま

docker-compose up -d

で起動する。

疎通を確認する

docker-compose up -d

で起動できる

この状態でコンテナのシェルに入れるため、pingなどを行い名前解決と疎通を確認しておくと良いだろう。

nginx → php-fpm

docker-compose exec nginx sh

でコンテナに入り

ping nginx

とすると

admin@ip-172-31-29-222:~/my-ecs-project$ docker-compose exec nginx sh
/ # ping php-fpm
PING php-fpm (172.21.0.2): 56 data bytes
64 bytes from 172.21.0.2: seq=0 ttl=64 time=0.134 ms
^C
--- php-fpm ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.134/0.134/0.134 ms

このように応答がある事を確認しておく

php-fpm → nginx

こちらは

docker-compose exec php-fpm sh

でシェルに入り(こっちはbashでもok)

apt update && apt install -y iputils-ping

した後

ping nginx
admin@ip-172-31-29-222:~/my-ecs-project$ docker-compose exec php-fpm sh
# pingが入っていない
# type ping
ping: not found
# apt update
# apt install iputils-ping
# ping nginx
PING nginx (172.18.0.3) 56(84) bytes of data.
64 bytes from my-ecs-project_nginx_1.my-ecs-project_app-network (172.18.0.3): icmp_seq=1 ttl=64 time=0.058 ms
^C
--- nginx ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.058/0.058/0.058/0.000 ms
# exit

nginx側にはpingが入ってなかったためaptでiputils-pingを導入した。

再度起動

準備が整ったので、最新イメージで起動するようにdocker-composeをdownしてup

docker-compose down
docker-compose up -d

この状態で諸々確認してみよう


htmlの確認。ロゴも含めてok


phpの確認。phpinfo()の出力ok

このようにphpinfoが正しく表示されている。これに関してメカニズムを見ていこう

それぞれのコンテナーのファイルを確認してみる

まずnginxの方。これは以下のコマンドでシェルに入れるのだった

docker-compose exec nginx sh

コピー先の /usr/share/nginx/html にcdしてみる

/ # cd /usr/share/nginx/html/
/usr/share/nginx/html # ls
50x.html    index.html  info.php    logo.png

このように

  • index.html
  • info.php
  • logo.png
  • 50x.html

の4ファイルが見える。ここで、info.phpを削除してみよう

rm info.php

このようにしてもinfo.phpは確認できるはずだ

しかし、logo.pngを例えば削除すると

rm logo.png

消滅する(ブラウザーによりcacheが効いてる可能性は高いのですが)

ではexitして今度はphp-fpmに入ろう

docker-compose exec php-fpm bash
$ docker-compose exec php-fpm bash
root@abfa9786edd9:/var/www/html# cd /usr/share/nginx/html/
root@abfa9786edd9:/usr/share/nginx/html# ls
index.html  info.php  logo.png

このように移動すると、こちらは3ファイルある

ここでindex.htmllogo.pngを削除してみる

それでもphpは普通に見えるはずだ


大丈夫

ただし、info.phpを削除するとこれが消滅する

以上の事から理解できる事

  • nginxはphp以外のファイルをサーブする
  • php-fpmはphp関連のファイルのみサーブしている

このようになっている。例えば、laravelなどのフレームワークをサービスする場合はこの概念が非常に重要になってくる。これは次回以降記事にする。

awsへのデプロイ

ここまで割と記事が長くなってしまったのでawsへのデプロイは次回以降とする

Discussion