PHP (CodeIgniter), Apache のProductをDocker Imageにする
業務でPHPアプリケーションを保守することになったのでとりまコンテナ化しました。 fpm × nginxで作りたかったけど業務上Apache縛りがあったのでモジュール版を使います。
2020/06/12 追記: fpm versionも書きました
ソフトウェアバージョン
software | version |
---|---|
PHP | 7.4.3 |
Apache | 2.4 |
CodeIgniter | 3.1.11 |
追加でやること
Dockerfile
## module php FROM php:7.4.3-apache-buster ## composer multi stage build ## コンテナ内で composer 叩きたい場合に設定する COPY --from=composer:1.9.3 /usr/bin/composer /usr/bin/composer ## apache, phpの設定ファイルをcopyする COPY ["php.ini-development", "apache2.conf", "000-default.conf", "ports.conf", "rewrite.conf", "deflate.conf", "fqdn.conf", "environment", "/tmp/"] ## project directory copy COPY ./sample-app /var/www/html/public/sample-app RUN apt-get update \ ## 必要なLibraryがあったら追加する && apt-get -y install vim unzip \ ## redis install. PHP5.6の場合は4.3.0を入れる && pecl install redis-5.2.0 \ && docker-php-ext-enable redis \ ## config file move && mv /tmp/php.ini-development "$PHP_INI_DIR/php.ini-development" \ && mv /tmp/apache2.conf /etc/apache2/apache2.conf \ && mv /tmp/000-default.conf /etc/apache2/sites-available/000-default.conf \ && mv /tmp/ports.conf /etc/apache2/ports.conf \ && mv /tmp/rewrite.conf /etc/apache2/mods-available/rewrite.conf \ && mv /tmp/deflate.conf /etc/apache2/mods-available/deflate.conf \ && mv /tmp/environment /etc/environment \ ## https://askubuntu.com/questions/256013/apache-error-could-not-reliably-determine-the-servers-fully-qualified-domain-n && mv /tmp/fqdn.conf /etc/apache2/conf-available/fqdn.conf \ && a2enconf fqdn \ ## apacheで環境変数を読み込む && echo "" >> /etc/apache2/envvars \ && echo "## read environment file" >> /etc/apache2/envvars \ && echo ". /etc/environment" >> /etc/apache2/envvars \ ## rewrite.confを設定する && ln -s /etc/apache2/mods-available/rewrite.conf /etc/apache2/mods-enabled/rewrite.conf \ && a2enmod rewrite \ ## directory権限をwww-dataにしてpermission && SGID変更 ## directory -> 770, file -> 660 && chown -R www-data:www-data /var/www/html/public \ && find /var/www/html/public -type d -exec chmod 770 {} \; \ && find /var/www/html/public -type f -exec chmod 660 {} \; \ && chmod -R 2770 /var/www/html/public \ ## php.ini file mv && mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" \ ## userを作成してwww-data groupに入れる && useradd webdev \ && usermod -g www-data webdev ## rootで起動しないのでwell-known portは使わない EXPOSE 9000 ## webdevでdocker 起動 USER webdev
config files
000-default.conf
# root userで動かさないためにwell-known portを使わないようにする <VirtualHost *:9000> ServerAdmin webmaster@localhost DocumentRoot /var/www/html/public/sample-app ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log json <Directory /var/www/html/public/sample-app> Options FollowSymlinks AllowOverride None Require all granted </Directory> </VirtualHost>
apache2.conf
# 前略... # ErrorLogFormatを Jsonにする ErrorLogFormat "{\"time\":\"%{%Y-%m-%dT%T}t.%{usec_frac}t%{%z}t\", \"function\": \"[%-m:%l]\", \"process\": \"%P\", \"file\": \"%F\", \"error\": \"%E\", \"message\": \"%M\"}" # 中略... # LogFormatを Jsonにする LogFormat "{\"time\":\"%{%Y-%m-%dT%T}t.%{usec_frac}t%{%z}t\", \"remoteIP\":\"%a\", \"host\":\"%V\", \"requestPath\":\"%U\", \"query\":\"%q\", \"method\":\"%m\", \"status\":\"%>s\", \"userAgent\":\"%{User-agent}i\", \"referer\":\"%{Referer}i\"}" json # 後略...
deflate.conf
# FilterProvider 使っています <IfModule mod_deflate.c> DeflateCompressionLevel 1 <IfModule mod_filter.c> FilterDeclare COMPRESS FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} =~ m#^text/#i" FilterProvider COMPRESS DEFLATE "%{CONTENT_TYPE} =~ m#^application/(atom\+xml|javascript|json|rss\+xml|xml|xhtml\+xml)#i" FilterChain COMPRESS FilterProtocol COMPRESS DEFLATE change=yes;byteranges=no </IfModule> </IfModule>
environment
## apacheで利用する環境変数をexportする export SAMPLE_APP_SERVER_NAME=${SAMPLE_APP_SERVER_NAME}
Dockerfileの以下の部分で/etc/environmentを読み込むようにしています。
&& echo "" >> /etc/apache2/envvars \ && echo "## read environment file" >> /etc/apache2/envvars \ && echo ". /etc/environment" >> /etc/apache2/envvars \
fqdn.conf
## 環境変数を読み込む ServerName ${SAMPLE_APP_SERVER_NAME}
php.ini-development
[Date] date.timezone = "Asia/Tokyo" [mbstring] mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese"
ports.conf
Listen 9000 <IfModule ssl_module> Listen 443 </IfModule> <IfModule mod_gnutls.c> Listen 443 </IfModule>
rewrite.conf
# CodeIgniter用のrewrite. index.phpをurlに入れなくても良いようにする <Directory /var/www/html/public/sample-app> <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php/$1 [L] </IfModule> </Directory>
docker-compose.yaml
version: "3" services: app: # 事前にbuildしておいたやつ image: sample-php-app:0.0.1-SNAPSHOT ports: - "9000:9000" environment: # apache env - SAMPLE_APP_SERVER_NAME=localhost:9000
PHPのLogもJsonで標準出力する
monologを入れて、CI_Log.phpのsubclass を作ります。 monologのJsonFormatterは日本語をescapeしてしまうので、encodeしないような拡張クラスを用意しています。
sample-app/application/core/MY_Log.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); use Monolog\Logger; use Monolog\Handler\StreamHandler; require_once dirname(__FILE__) . '/ExtendedJsonLogFormatter.php'; class MY_Log extends CI_Log { private $logger; public function __construct() { parent::__construct(); $log_channel = 'API'; $log_threshold_stdout = Logger::INFO; $formatter = new ExtendedJsonLogFormatter(); $this->logger = new Logger($log_channel); try { $log_handler = new StreamHandler('php://stdout', $log_threshold_stdout); $log_handler->setFormatter($formatter); $this->logger->pushHandler($log_handler); } catch (Exception $e) { die($e->getMessage()); } } public function write_log($level, $msg) { switch (strtoupper($level)) { case 'DEBUG': $this->logger->addDebug($msg); break; case 'INFO': $this->logger->addInfo($msg); break; case 'WARN': $this->logger->addWarning($msg); break; case 'ERROR': $this->logger->addError($msg); break; default: $this->logger->addError('log level error'); break; } return TRUE; } }
sample-app/application/core/ExtendedJsonLogFormatter.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); use Monolog\Formatter\JsonFormatter; class ExtendedJsonLogFormatter extends JsonFormatter { public function __construct() { parent::__construct(); } public function format(array $record) { return json_encode($record, JSON_UNESCAPED_UNICODE) . ($this->appendNewline ? "\n" : ''); } }