Apache+PHP-FPMをDocker化

Docker

数年前までは、Apache+PHP-FPMの環境ってrpmインストールや自前コンパイルして構築していたのですが、頻繁にバージョンアップがあって管理が大変だったので、去年ぐらいにDocker管理化して自動化するようにしました。今回はその構築方法を紹介します。

まず、ベースとなるイメージですが、ゼロベースでなく、Docker hub公式のイメージを使います。それぞれドンピシャなイメージがあるので、それをもとに独自の設定を追加していきます。

まずはApacheですが「httpd:latest」を使います。こいつはDebian 10ベースのbuster-slimがベースになってます。

FROM httpd:latest

ARG PUID=300
ARG PGID=300

RUN groupadd -g ${PGID} webservd \
    && useradd -u ${PUID} -g webservd -d /home/webservd -m webservd \
    && mkdir -p /home/webservd/WebContent/ROOT /run/httpd

COPY ./conf/httpd.conf /usr/local/apache2/conf/httpd.conf
COPY ./conf/extra /usr/local/apache2/conf/extra

EXPOSE 80 443

# Declare volumes for mount point directories
VOLUME ["/usr/local/apache2/logs", "/home/webservd/WebContent/ROOT"]

Apacheの実行ユーザーとして「webservd」というユーザーを作ってます。あとは公式だとpidファイルが「/usr/local/apahe2/logs」に置かれる前提なので、「/run/httpd」を作ります。それとログファイルとドキュメントルートをマウントポイントとして定義します。あとはDockerファイルと同じ階層に「conf」フォルダ作ってそこに各種confファイルを入れときます。

続いてPHP-FPMのほうです。こちらは公式の「php-fpm:7.4」を使います。まだ8系はこなれていないので7系を使います。

FROM php:7.4-fpm

ARG PUID=300
ARG PGID=300

# Install some needed packages
RUN apt-get update && apt-get install -y \
        libbz2-dev \
        libgmp-dev \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
        libgd-dev \
        libicu-dev \
        libmagickwand-dev \
        libxslt-dev \
        libzip-dev \
  && ln -s /usr/include/x86_64-linux-gnu/gmp.h /usr/include/gmp.h \
  && docker-php-ext-configure gd --with-freetype --with-jpeg \
  && docker-php-ext-install -j$(nproc) bcmath bz2 calendar xsl gd gmp exif intl pdo_mysql mysqli opcache sockets zip \
  && pecl install imagick \
  && docker-php-ext-enable imagick

RUN groupadd -g ${PGID} webservd \
    && useradd -u ${PUID} -g webservd -d /home/webservd -m webservd \
    && mkdir -p /home/webservd/WebContent/ROOT
 
# Use the default production configuration
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

RUN { \
  echo 'output_handler = mb_output_handler'; \
  echo 'default_charset = UTF-8'; \  
  echo 'zend.multibyte = On'; \
  echo 'max_execution_time = 300'; \
  echo 'max_input_time = 300'; \
  echo 'memory_limit = 256M'; \
  echo 'post_max_size = 128M'; \
  echo 'upload_max_filesize = 128M'; \
  echo 'max_file_uploads = 20'; \
  echo 'date.timezone = Asia/Tokyo'; \
  echo 'session.gc_maxlifetime = 86400'; \
  echo 'mbstring.language = Japanese'; \
  echo 'mbstring.http_output = pass'; \
  echo 'mbstring.encoding_translation = Off'; \
  echo 'mbstring.detect_order = UTF-8,SJIS,EUC-JP,JIS,ASCII'; \
  echo 'mbstring.substitute_character = none'; \
  echo 'mbstring.func_overload = 0'; \
  echo 'mbstring.strict_detection = On'; \
  echo 'gd.jpeg_ignore_warning = 0'; \
  echo 'fastcgi.logging = 1'; \
  echo 'cgi.fix_pathinfo = 0'; \
  echo 'opcache.enable = 1'; \
  echo 'opcache.enable_cli = 1'; \
  echo 'opcache.memory_consumption = 256'; \
  echo 'opcache.interned_strings_buffer = 8'; \
  echo 'opcache.max_accelerated_files = 4000'; \  
  echo 'opcache.validate_timestamps = 1'; \
  echo 'opcache.revalidate_freq = 60'; \
  echo 'opcache.fast_shutdown = 1'; \
} > /usr/local/etc/php/conf.d/php-ini-overrides.ini
 
RUN { \
  echo 'user = webservd'; \
  echo 'group = webservd'; \
  echo 'pm = dynamic'; \
  echo 'pm.max_children = 60'; \
  echo 'pm.start_servers = 12'; \
  echo 'pm.min_spare_servers = 12'; \
  echo 'pm.max_spare_servers = 45'; \
  echo 'pm.max_requests = 100'; \
  echo 'env[HOSTNAME] = $HOSTNAME'; \
  echo 'env[TMP] = /tmp'; \
  echo 'env[TMPDIR] = /tmp'; \
  echo 'env[TEMP] = /tmp'; \
  echo 'request_terminate_timeout = 300'; \
  echo 'php_admin_value[memory_limit] = 256M'; \
} > /usr/local/etc/php-fpm.d/zzz-docker.conf

EXPOSE 9000

# Declare volumes for mount point directories
VOLUME ["/home/webservd/WebContent/ROOT"]

そのままだと「GD」などのモジュールがインストールされていないので、docker-php-extension-installerを使ってインストールします。「imagick」は「pecl」でインストールして「docker-php-ext-enable」で有効化します。デフォルトのphp.iniは作られていないので、product用の「php.ini-production」をphp.iniとして置きます。

あとはApacheと同じく実行ユーザーを作成し、php.iniの設定を上書きするためにconf.dに「php-ini-overrides.ini」を作成します。特にWordpressを運用する際はファイルアップロードの制限がデフォルトだと足りないので、その辺の設定も入れときます。

php-fpm.confのほうはphp-fpm.dにあるconfファイルをアルファベット順に読み込んでいくので「zzz-docker.conf」に設定を記述していきます。php.ini側と同じくファイルアップロードの制限回避のためにメモリの最大使用量と最大実行時間を増やしています。

そうしたら、それぞれ「apache2」、「php-fpm」フォルダにおいて、docker-compose.ymlを作ります。

version: '3.8'
services:
  apahce2:
   build:
      context: apache2/
    container_name: apache2
    expose:
    - 80
    - 443
    network_mode: host
    restart: unless-stopped
    volumes:
    - web_root:/home/webservd/WebContent/ROOT
    - /var/log/httpd:/usr/local/apache2/logs
    - /etc/localtime:/etc/localtime:ro
    environment:
    - TZ=Asia/Tokyo
    depends_on:
    - php-fpm
  php-fpm:
    build:
      context: php-fpm/
    container_name: php-fpm
    expose:
    - 9000
    network_mode: host
    restart: unless-stopped
    volumes:
    - web_root:/home/webservd/WebContent/ROOT
    - /etc/localtime:/etc/localtime:ro
    environment:
    - TZ=Asia/Tokyo
volumes:
  web_root:
    driver_opts:
      type: none
      device: /home/webservd/WebContent/ROOT
      o: bind

network_modeはオーバヘッドがないhostモードで実行して、ホストPCのフォルダをドキュメントルートとしてマウントします。トップレベルのvolumesでも上記の設定で定義すればコンテナとボリュームを削除してもホストPCのフォルダが削除されるということはないです。

これを「docker-compose up -d」で立ち上げておけば、Apache+PHP-FPMサーバーの完成です。ApacheやPHPのバージョンが上がっても、「docker-compose build」で簡単にビルドしなおせるので管理がとても楽になりました。

しばらくはこれでWordpressや諸々サービスのProxyとして運用していました。「OpenResty」という存在を知るまでは…

コメント

タイトルとURLをコピーしました