Does not provide fallback content when JavaScript is not available. このサイトを視聴するにはJavaScriptを有効にしてください。PHP WebアプリケーションのDocker本番運用を考える | blog.potproject.net

PHP WebアプリケーションのDocker本番運用を考える

またまたDockerの話になるのですが、 割と構築を考えていて色々ググったりしてベストプラクティスを探ったりしています。 やはりWebアプリケーションのDocker運用は開発環境をそろえる為、のレベルならいいですが、 本番で使おうとすると実際はロギングや監視、ファイルの外出し、今はもうk8s前提の設計など考えること盛りだくさんで、完璧にできている所はかなり少ないんじゃないかと思います。 Dockerは使っているけどもコンテナに後から設定入れてるとか、開発環境だけだったりとかだったり。

で、PHPでのWebアプリケーションをDockerを本番稼働する際に考えていることで、ものすごく気になる点が一つあり・・・。 それの試行錯誤の記事です。

基本の構成

PHPでのWebアプリケーションの構成は、まあこんな感じだと大体思います。

  • PHP(php-fpm)
  • nginx

nginxとPHPで分離して、別々のプロセスで動かす。 後はDBやらキャッシュサーバやらいろいろ挟まるわけですが・・・ 特にモダンなPHPなら大体これが当たり前ではないでしょうか。

これをそのままDockerで管理しようとすると、こうなるわけです。 まあ今なら当たり前のように書きますがdocker-composeを使用した構成です。

docker-compose.yml

version: '3'
services:
  web:
      image: nginx:latest
      ports:
          - "127.0.0.1:80:80"
      depends_on:
          - php
  php:
      build: ./ #php:7-fpmにソースコードを追加したDockerfile

site.conf(Nginx Conf)

server {
    index index.php index.html;
    server_name localhost;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/code/;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        fastcgi_index index.php;
        fastcgi_pass php:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ .*\.(jpg|gif|png|css|js|ico|woff) {
        expires 3d;
    }
}

実際これで問題ないように見えますし、問題なく動作するように思えます。 しかしこの構成には問題があり、いや挙動的にには正常なのですが・・・

phpコンテナに入っているソースコードでは、staticなファイルをロードできない点です。

nginxとphpが同居している場合であれば、当然このような書き方になるのですが、 Dockerの場合はコンテナが別、つまり完全に動作する部分が分けられています。中身も別物です。

nginxの設定を見ると、phpファイルはfastcgi_passを見ているため、phpコンテナからファイルが読み込まれます。

でもそれ以外のファイルは、php-fpmを介さないため、nginxコンテナから取得されます。

この点で、「ソースコードにPHPファイルとstaticファイルが共存している」となっていると、この問題にぶち当たります。

問題の回避

この問題を回避するには、phpコンテナとnginxコンテナ両方に置くととりあえず解決します。

docker-compose.yml

version: '3'
services:
  web:
      build: ./ #nginx:latestにソースコードを追加したDockerfile
      ports:
          - "127.0.0.1:80:80"
      depends_on:
          - php
  php:
      build: ./ #php:7-fpmにソースコードを追加したDockerfile

しかし、これももこれでなんか綺麗な解決法ではないな・・・とちょっと思いますね。 容量も増えますし、なんかまたがっている感じがすごく気持ち悪い。

また、管理しなくてはならないdockerfileも増えます。 でもこれ以外に方法はないように思えるんですよね・・・。誰かいい方法があれば教えてくれ・・・。

妥協案

nginxとphpが一緒になったコンテナを使う

これもまあ悪いとも良いとも言えない解決法でしょうか。 nginxとphpの分離が難しいので、分離していないコンテナを使う。 こうすることで当然問題は解決しますが、Dockerの「1コンテナ = 1プロセス」のベストプラクティスに反してしまうという問題があります・・・。

既存のコンテナを使うなら、richarvey/nginx-php-fpmが使いやすくていいと思いました。 スターもドキュメントも共に多く、メンテナンスもされています。 Kubernetes Guideもあったりと、割と本番でも行けるようになっている感じはします。

php-apacheを使用する

もうphp-fpm/nginx構成をあきらめて、大人しく公式のphp-apacheを使うという選択。 これもありです。php-apacheは単一プロセスで動き、公式でサポートされているという安心感もあります。 WordPressのDockerもこれを使っていたり、PHPのWebアプリケーションはこれがベターな感じ。 しかし、nginxをあきらめるしかないのかという気持ちはするんですよね。 この上にまたnginxを載せるのもいいですが、それもそれで負担は上がるし・・・うーむ・・・?

完璧を求めるとDockerは辛い

Dockerは開発環境の構築も、本番環境の構築もどちらもなんとかなる今や万能のコンテナ技術です。 しかし、PHPのDockerは開発環境の方法はたくさん出てくるのに、 本番環境のベストプラクティスが全く出てこないので色々調べて書きました。

正直自分は、ベストプラクティスを維持したければ、Dockerfileを開発と本番で分けるしかないんじゃないかなあと思ってます。 (k8sを前提とした環境なら、podファイルは最初から分けられるのでいいんですが) しかしまだまだ新しい技術なので、これからもどんどんこういった部分はアプリケーション側もサポートするようになり、 成熟していくのでしょうかね?という気持ちです。