タグ: PHP-FPM

Apache & PHP-FPM 管理スクリプト (引数対応版)。

Webサーバを運用する上で役立ている

  1. Apacheで有効になっているサイトの設定情報を表示
  2. 構文を確認
  3. 問題が無ければApache再起動
  4. 再起動後にステータスを表示する

スクリプト。非常に便利なものでしたが、VPS新調に際してPHP-FPMを導入。

そこで、

  1. PHP-FPMも構文チェックを行う
  2. 他、引数による処理

を改訂しました。(元ネタ:ChatGPT、リファクタリングはdeepseekとGemini)

スクリプト内容

  • apache2-check.sh
#!/bin/bash

#================================================================
# Apache & PHP-FPM 管理スクリプト (引数対応版)
#================================================================
#
# 機能:
# 1. Apacheで有効になっているサイトの設定情報を表示します。
# 2. ApacheとPHP-FPMの設定ファイルの構文をチェックします。
# 3. ApacheとPHP-FPMの再起動を個別に確認し、実行します。
# 4. 再起動後に各サービスのステータスを表示します。
#
# 引数:
#  -y : 確認プロンプトをすべてスキップし、自動で'yes'と応答します。
#  -a : Apacheのみを対象とします。
#  -p : PHP-FPMのみを対象とします。
#  -h : このヘルプを表示します。
#
#================================================================

# --- 設定項目 ---

# Apacheのサイト設定が格納されているディレクトリ
SITES_DIR="/etc/apache2/sites-enabled"

# 操作対象のPHPバージョン (例: "8.3", "8.2", "7.4")
PHP_VERSION="8.3"

# --- 設定ここまで ---


# --- スクリプトの動作を制御するフラグ ---
AUTO_YES=false
RESTART_APACHE=true  # デフォルトでは両方実行
RESTART_PHP=true     # デフォルトでは両方実行
EXCLUSIVE_MODE=false # -a または -p が指定されたかを判定するフラグ

# --- ヘルプ表示関数 ---
usage() {
    echo "Usage: $(basename "$0") [-y] [-a] [-p] [-h]"
    echo "  -y : 確認プロンプトをすべてスキップし、自動で'yes'と応答します。"
    echo "  -a : Apacheのみを対象とします。"
    echo "  -p : PHP-FPMのみを対象とします。"
    echo "  -h : ヘルプを表示します。"
    echo "引数なしの場合は、ApacheとPHP-FPMの両方が対象となります。"
    exit 0
}

# --- 引数解析 ---
while getopts "yaph" opt; do
  case $opt in
    y)
      AUTO_YES=true
      ;;
    a)
      if ! $EXCLUSIVE_MODE; then
          # -a or -p が初めて指定された場合、排他モードにしてデフォルトをリセット
          RESTART_APACHE=false
          RESTART_PHP=false
          EXCLUSIVE_MODE=true
      fi
      RESTART_APACHE=true
      ;;
    p)
      if ! $EXCLUSIVE_MODE; then
          # -a or -p が初めて指定された場合、排他モードにしてデフォルトをリセット
          RESTART_APACHE=false
          RESTART_PHP=false
          EXCLUSIVE_MODE=true
      fi
      RESTART_PHP=true
      ;;
    h)
      usage
      ;;
    \?)
      # 不正なオプション
      usage
      ;;
  esac
done

# PHP-FPMのサービス名とコマンド名を変数から生成
PHP_FPM_SERVICE="php${PHP_VERSION}-fpm"
PHP_FPM_COMMAND="php-fpm${PHP_VERSION}"

#
# サービスを再起動し、ステータスを表示する関数
#
restart_and_check_service() {
    local service_name="$1"
    local service_label="$2" # 表示用の名前
    local confirm_action="n"

    if [ "$AUTO_YES" = true ]; then
        confirm_action="y"
        echo "${service_label} を再起動します... (-yオプションにより自動確認)"
    else
        read -p "${service_label} を再起動しますか? (y/n): " confirm_action
    fi

    if [[ "$confirm_action" =~ ^[Yy]$ ]]; then
        if [ "$AUTO_YES" = false ]; then
          echo "--- ${service_label} を再起動します... ---"
        fi
        if ! systemctl restart "$service_name"; then
            echo "エラー: ${service_label} の再起動に失敗しました。"
        else
            echo "${service_label} が正常に再起動されました。"
            echo
            echo "==== ${service_label} ステータス ===="
            systemctl status "$service_name" --no-pager
            echo "=================================="
        fi
    else
        echo "${service_label} の再起動はキャンセルされました。"
    fi
    echo
}

# スクリプトを root ユーザーで実行しているかチェック
if [ "$EUID" -ne 0 ]; then
    echo "エラー: このスクリプトは root 権限で実行する必要があります。"
    exit 1
fi

# 1. 有効なサイト設定とドメインを表示
# このセクションは引数に関わらず常に表示される情報として実行します
echo "==== 有効なサイト設定ファイル ===="
if [ -z "$(ls -A "$SITES_DIR" 2>/dev/null)" ]; then
    echo "サイト設定が存在しません。"
else
    shopt -s nullglob
    for site in "$SITES_DIR"/*; do
        echo "設定ファイル: $(basename "$site")"
        entries=$(grep -hi -E "^\s*(ServerName|ServerAlias)\s+" "$site" | sed -E 's/^[[:blank:]]+//;s/[[:blank:]]*#.*//' | awk '{
            original_directive = $1
            directive = tolower(original_directive)
            proper_directive = (directive == "servername") ? "ServerName" : \
                               (directive == "serveralias") ? "ServerAlias" : original_directive
            for (i=2; i<=NF; i++) {
                domain = tolower($i)
                sub(/[;,]*$/, "", domain)
                gsub(/^[[:blank:]]+|[[:blank:]]+$/, "", domain)
                if (domain) {
                    printf "%s %s\n", proper_directive, domain
                }
            }
        }' | sort -u)
        if [ -z "$entries" ]; then
            echo "  ※ ServerName/ServerAliasが定義されていません"
        else
            echo "$entries" | sed 's/^/  /'
        fi
        echo
    done
    shopt -u nullglob
fi
echo "=================================="
echo

# 2. 構文チェック
echo "--- 構文チェック ---"
SYNTAX_OK=true

# Apache構文チェック
if [ "$RESTART_APACHE" = true ]; then
    echo "Apache の構文をチェックしています..."
    if ! apachectl configtest 2>&1 | grep -q "Syntax OK"; then
        echo "エラー: Apacheの構文エラーが検出されました。"
        apachectl configtest # エラー詳細を再表示
        SYNTAX_OK=false
    else
        echo "Apacheの構文は正常です。"
    fi
    echo
fi

# PHP-FPMの存在確認と構文チェック
PHP_FPM_ENABLED=false
if [ "$RESTART_PHP" = true ]; then
    if systemctl list-units --type=service --all | grep -q "${PHP_FPM_SERVICE}.service"; then
        if command -v "$PHP_FPM_COMMAND" &>/dev/null; then
            PHP_FPM_ENABLED=true
            echo "${PHP_FPM_SERVICE} の構文をチェックしています..."
            if ! "$PHP_FPM_COMMAND" -t 2>&1 | grep -q "test is successful"; then
                echo "エラー: ${PHP_FPM_SERVICE} の構文エラーが検出されました。"
                "$PHP_FPM_COMMAND" -t # エラー詳細を再表示
                SYNTAX_OK=false
            else
                echo "${PHP_FPM_SERVICE} の構文は正常です。"
            fi
        else
            echo "警告: ${PHP_FPM_SERVICE} サービスは存在しますが、${PHP_FPM_COMMAND} コマンドが見つかりませ
ん。PHP-FPMのチェックはスキップします。"
            RESTART_PHP=false # 実行フラグをオフにする
        fi
    else
        echo "情報: ${PHP_FPM_SERVICE} サービスが見つかりません。PHP-FPM関連の処理はスキップします。"
        RESTART_PHP=false # 実行フラグをオフにする
    fi
    echo
fi

# 構文エラーがあれば処理を中断
if [ "$SYNTAX_OK" = false ]; then
    echo "構文エラーが検出されたため、処理を中断します。"
    exit 1
fi
echo "--------------------"
echo

# 3. サービスの再起動
if [ "$RESTART_APACHE" = false ] && [ "$RESTART_PHP" = false ]; then
    echo "再起動対象のサービスがありません。"
    exit 0
fi

# Apacheの再起動
if [ "$RESTART_APACHE" = true ]; then
    restart_and_check_service "apache2" "Apache"
fi

# PHP-FPMの再起動
if [ "$RESTART_PHP" = true ] && [ "$PHP_FPM_ENABLED" = true ]; then
    restart_and_check_service "$PHP_FPM_SERVICE" "$PHP_FPM_SERVICE"
fi

スクリプトの動き

  1. sudo bash apache2-check.shとすることで
  2. A@acheを起動するか、PHP-FPMを起動するかの2択が生まれます。
  3. -yオプションの場合は全てyにしてプロンプトを省略します。
  4. -aでApacheのみの再起動-pはPHP-FPMのみの再起動

と、柔軟かつやりやすいスタイルへと仕上がりました。

PHP8.3をPHP-FPMで動かすための設定

概要

Nextcloud等のLAMP環境で利用するPHPを、PHP-FPM (FastCGI Process Manager) を使ってApacheと連携させる形でインストール・設定します。
php-fpmを利用すると、PHPの処理がApacheのプロセスから分離されるため、パフォーマンスの向上や、より柔軟なリソース管理が期待できます。

※筆者の好みでaptitudeを用いています。適宜、aptに読み替えてください。

実施した環境

  • Ubuntu 24.04
  • Apache 2.4
  • MySQL 8

さっくりとした手順

  1. レポジトリを追加します。
  2. PHPおよびPHP-FPMパッケージをインストールします。
  3. Apacheの連携用モジュールを設定します。
  4. PHPのパフォーマンス設定(OPcache等)を行います。
  5. 各Webサイト(バーチャルホスト)でPHP-FPMを有効化します。
  6. サービスを再起動し、動作を確認します。

手順詳細

レポジトリ追加とアップデート

最新のPHPバージョンを利用するため、ondrej/phpリポジトリを追加します。

  • PHPレポジトリの追加
sudo add-apt-repository ppa:ondrej/php
  • パッケージ全体のアップデート
sudo aptitude update

PHPパッケージのインストール

PHP本体と、php-fpm、そしてWebアプリケーションで一般的に必要とされる拡張機能をインストールします。

  • PHP本体とFPM
sudo aptitude install php8.3 php8.3-fpm
  • 周辺モジュール (APCuとMemcached)
sudo aptitude install memcached php8.3-apcu
  • Webアプリに必要な周辺モジュール (MySQLを使う場合)
sudo aptitude install php8.3-{opcache,pdo,bcmath,calendar,ctype,fileinfo,ftp,gd,intl,json,ldap,mbstring,mysql,posix,readline,sockets,bz2,tokenizer,zip,curl,iconv,phar,xml,imagick,gmp,redis-server}
  • インストール確認
php -v

PHP 8.3.xのように、インストールしたバージョンが表示されることを確認します。

Apache連携モジュールの設定

php-fpmでPHPを動かすため、従来のmod_phpを無効化し、代わりにphp-fpmと通信するためのproxy_fcgiモジュールを有効化します。

  • mod_phpを無効化
sudo a2dismod php8.3
  • 必要なモジュールを有効化
sudo a2enmod proxy_fcgi setenvif

OPcacheとAPCuの有効化

Nextcloudなどのアプリケーションでは、パフォーマンス向上のためこれらのキャッシュ設定が推奨(あるいは必須)となります。

  • 設定ファイル待避
sudo mv /etc/php/8.3/mods-available/opcache.ini /path/to/backup/directory/opcache.ini.$(date +%Y%m%d)
sudo mv /etc/php/8.3/mods-available/apcu.ini /path/to/backup/directory/apcu.ini.$(date +%Y%m%d)

任意のバックアップディレクトリを指定します。(筆者環境/etc/conf_backup)

  • 設定ファイル差し替え
cat <<- __EOF__ | sudo tee /etc/php/8.3/mods-available/opcache.ini > /dev/null
; configuration for php opcache module
; priority=10
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.memory_consumption=256
opcache.save_comments=1
opcache.revalidate_freq=1
__EOF__
cat <<- __EOF__ | sudo tee /etc/php/8.3/mods-available/apcu.ini > /dev/null
extension=apcu.so
[apcu]
apc.enabled=1 apc.shm_size=32M apc.ttl=7200 apc.enable_cli=1 apc.serializer=php
__EOF__

※ メモリ量などは環境に合わせます。

各WebサイトでのPHP-FPM連携設定

各WebサイトのApache設定ファイル(.conf)で、PHPへのリクエストをphp-fpmに渡すように設定します。この設定がなければPHPスクリプトをサーバが解釈せず、動きません。

  • 設定例 (/etc/apache2/sites-available/hoge.confなど)
    <VirtualHost>ブロックの中に、以下の<FilesMatch>ブロックを追記します。
    <VirtualHost *:443>
        ServerName hoge.example.com
        # ...

        <FilesMatch \.php$>
            # SetHandlerで、phpファイルのリクエストをFastCGIプロキシに渡す
            SetHandler "proxy:unix:/var/run/php/php8.3-fpm.sock|fcgi://localhost/"
        </FilesMatch>

        # ...
    </VirtualHost>

このSetHandlerディレクティブが、Apacheに来たPHPへのリクエストを、バックグラウンドで動いているphp-fpmのプロセスに転送する役割を担います。

サービスの再起動と動作確認

設定を反映させるため、Apacheとphp-fpmの両方を再起動します。

  • サービス再起動
sudo systemctl restart php8.3-fpm.service
sudo systemctl restart apache2.service
  • 動作確認
    mod_phpは無効化されているため、a2queryではdisabledと表示されるのが正しい状態です。代わりに、php-fpmサービスが稼働していることを確認します。
systemctl status php8.3-fpm.service
`active (running)`と表示されていれば、PHP-FPMの導入と設定は完了です。

Powered by WordPress & Theme by Anders Norén