新着記事

Viewing posts for the category nginx

nginx+FPMの環境のタイムアウトの解決法の備忘録

前回、記事を書いたPHPをnginx+FPMの環境で、実際にwebアプリを作成。
ページが重くなる場合にタイムアウトが発生、その原因が複数の理由のため解決に時間がかかったのでその備忘録。

さきに原因を書くと
  1. phpの実行時間によるタイムアウト
  2. ブラウザとnginxのタイムアウト
  3. nginxとphp-fpmのタイムアウト
と3つの箇所でタイムアウトが発生していた。

まずは最初に表示された
504 Gateway Timeout
を解消する。
phpのタイムアウト時間max_execution_timeを設定して確認。
まだ504エラーが表示されるので、つぎにnginxのsend_timeoutkeepalive_timeoutを設定。
この設定の追加で504エラーは表示されなくなったが、別のエラーがnginxから表示されるようになった。
An error occurrerd. Sorry, the page you are looking for is currently unavailable. Please try again later.

このエラーメッセージがnginxとphp-fpmのタイムアウトだと気づくのに時間がかかった。
これの対応はnginxの設定にfastcgi_connect_timeoutfastcgi_read_timeoutfastcgi_read_timeoutを追加することで解消できた。

最終的にnginx.confphp.iniに下記の設定を追加しています。
nginx.conf
http {
send_timeout 300; # クライアントへの応答のタイムアウト時間
keepalive_timeout 300; # クライアントとの接続をキープする時間

fastcgi_connect_timeout 300; # nginxとphp-fpmの接続を確立するためのタイムアウト時間
fastcgi_send_timeout 300; # nginxからphp-fpmへのリクエスト送信のタイムアウト時間
fastcgi_read_timeout 300; # php-fpmからの応答のタイムアウト時間
}
php.ini
max_execution_time = 600 # phpのスクリプトの実行時間

webサーバーとアプリケーション・サーバーを分けた場合、その間のタイムアウトも気にしなければいけなかった。

PHPをnginx+FPMの環境で動作させる

古いPHPのWebアプリの改修を行うことになりました。
まずはメンテナンス性の確保のためにオレオレフレームワークからLaravelに載せ替える。
また、1台サーバーを使用して動かしていたが、あまり大きなアプリではないので、kubernetesで動作させることにします。
そして合わせて将来的な技術投資としてWebサーバーをApacheからnginx + FastCGI Process Manager(FPM)を試してみることにしました。
その際のdokcerとkubernetesの設定の備忘録になります。

nginxのconfファイルの作成

まずはLaravelとphp-fpm用にしたnginxのconfファイルを作成。
default.conf
server {
    listen       80;
    listen  [::]:80;
    server_name  example.com;

    access_log  /var/log/nginx/host.access.log;
    error_log  /var/log/nginx/host.error.log;

    location / {
        root   /usr/share/nginx/html;
        index  index.php index.html index.htm;
        # リクエストされたファイルが存在しなければ、Laravelのフロントコントローラーに内部リダイレクトさせる
        try_files $uri /index.php?$query_string;
    }

    # 400番台のエラーページの設定
    error_page  404              /404.html;

    # 500番台のエラーページの設定
    # 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;
    }

    # Apacheの設定なので今回はコメントアウトのまま
    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # FastCGIの設定
    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
        # Laravelのルートディレクトリ
        root           /var/www/html/public;
        # nginxからphp-fpmに受け渡すIPアドレスとポート番号の設定
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        # 全てのリクエストをLaravelのフロントコントローラーで実行させる
        fastcgi_param  SCRIPT_FILENAME  $document_root/index.php;
        include        fastcgi_params;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}
ここでの注意点はnginxからphp-fpmに受け渡すIPアドレスとポート番号の設定
ローカル環境で動作させる場合はdokcerのphp-fpmのコンテナを指定してください。
fastcgi_pass   php-fpm:9000;
kubernetesで動作させる場合は1pod内でnginxのコンテナとphp-fpmのコンテナを動作させるため127.0.0.1:9000で受け取ることができます

nginxのDockerfileの作成

Docker Hubから最新のものを使用。
Dockerfile
FROM nginx:1.23.3

COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80 443

STOPSIGNAL SIGQUIT

CMD ["nginx", "-g", "daemon off;"]

php-fpmのDockerfileの作成

こちらもDocker Hubから最新のものを使用。
注意点はnginxがアクセスできるようにLaravelのプロジェクトを配置しているディレクトリの所有ユーザーを変更すること。
アクセスしてくるのはnginxなので所有ユーザーをwww-dataに変更しておかないと404エラーになります。
Dockerfile
FROM php:8.1.16-fpm

#PHPの拡張機能のインストール
RUN apt update \
    && apt install -y libonig-dev libxml2-dev libcurl4-openssl-dev libssl-dev libzip-dev \
    && docker-php-ext-install pdo pdo_mysql mysqli simplexml curl phar zip ftp \
    && pecl install xdebug redis \
    && docker-php-ext-enable xdebug redis

# nginxがアクセスできるように所有ユーザーを変更
COPY project /var/www/html
RUN chown -R www-data:www-data /var/www/html

EXPOSE 9000

ENTRYPOINT ["docker-php-entrypoint"]

CMD ["php-fpm"]

kubernetesの設定の作成

kubernetesに反映させるdeployment、service、ingressを作成します。
deployment.yaml
1pod内で動作させるためcontainersにnginxとphp-fpmのコンテナを記載します。
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-deployment
  labels:
    app: example
spec:
  replicas: 1
  selector:
    matchLabels:
      app: example
  template:
    metadata:
      labels:
        app: example
    spec:
      containers:
        - image: php-fpm:latest
          name: example-php-fpm
          ports:
            - containerPort: 9000
              protocol: TCP
        - image: nginx:latest
          name: example-nginx
          ports:
            - containerPort: 80
              protocol: TCP
service.yaml
外からのアクセスはnginxが受けるのでポートは80番。
kind: Service
apiVersion: v1
metadata:
  name: example-service
spec:
  type: NodePort
  selector:
    app: example
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
spec:
  rules:
    - host: example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: example-service
                port:
                  number: 80

kubernetesに反映を実行。 これで動作できました。
Search