はじめに
php:7.2-apache をビルドするときに、 DocumentRoot を設定していました。特に疑問に感じていませんでしたけれどもあるとき、さまざまな Docker イメージを見ていると設定は build 時ではなく、 run 時に環境変数で設定していることに気が付きました。
DocumentRoot も run 時に設定したいと思いました。
そうするためには、次の手順で実現できると思いました。
- Dockerfile で行っていた設定ファイルの COPY を、不要ならやめる。
- docker-compose run 時に volumes で設定ファイルのディレクトリを共有することでコンテナに設定ファイルを渡してやる。
- コンテナに渡す設定ファイルの必要項目を変数化して run 時に埋め込む。
上記の方法のうち、 1. と 2. は簡単です。 3. が難しかったです><。
今回、応急処置的な一時しのぎ的なワークアラウンドではありますけれども、汎用的で Linux コマンドの勉強になりましたので、解決するまでの道筋を順番に記録に残していこうと思います。
参考ページ
- Using environment variables in nginx configuration
- envsubstを使ってDockerで設定ファイルに環境変数を埋め込めこむ汎用的なパターン – Qiita
- > Docker使ってるとできるだけイメージは共通化して、設定ファイルはマウントで差し込み、環境ごとの設定差分は環境変数で埋め込む方向になってきます。
> しかしながら、設定ファイルに環境変数を直接埋め込めるかは対象のソフトウェアの実装次第で、例えばfluentdは設定ファイルで環境変数を直接埋め込めるけど、nginxは環境変数を直接埋め込めなかったりします。
> で、よくやるのは entrypoint.sh みたいなのを作って起動時にsedするという原始的な方法もあるけど、もうちょっとスマートな方法として、 envsubst という軽量なテンプレートエンジンを使うことで、設定ファイルに汎用的に環境変数を埋め込むことができて便利なので紹介したい。 - 低機能でポータブルなテンプレートエンジンが欲しい→そこでenvsubstですよ – Qiita
- docker-compose run 時に command で指定すべき apache の起動コマンドを確認。それは “apache2-foreground”
1. Dockerfile で行っていた設定ファイルの COPY を、不要ならやめる。
ARG PHP_VERSION FROM php:${PHP_VERSION}-apache ARG TZ=UTC ENV TZ ${TZ} RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN apt-get update && apt-get install -y \ libssl-dev \ openssl \ ssl-cert \ unzip \ zlib1g-dev \ && docker-php-ext-install \ pdo_mysql \ zip \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && a2enmod rewrite \ && a2enmod ssl \ && a2ensite default-ssl COPY ./php.ini /usr/local/etc/php/ COPY ./sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf COPY ./sites-available/default-ssl.conf /etc/apache2/sites-available/default-ssl.conf RUN curl -s http://getcomposer.org/installer | php && \ mv composer.phar /usr/local/bin/composer
COPY ./sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf
COPY ./sites-available/default-ssl.conf /etc/apache2/sites-available/default-ssl.conf
の部分で、書き換えた Apache の設定ファイルを渡して、ビルドをしていました。これらの行を削除したとき、もともとこれらのファイルがあるのであれば問題なくビルド後のイメージでコンテナの起動とウェブサーバとしての動作がされます。
もともとこれらのファイルありましたっけ?あったような気がします。あやふやですので実際にこれらの COPY 行を削除して確かめてみました。
結果、問題ありませんでした♪これで次に進めますね♪
2. docker-compose run 時に volumes で設定ファイルのディレクトリを共有することでコンテナに設定ファイルを渡してやる。
... 略 ... services: php_apache: build: context: ./php_apache args: PHP_VERSION: ${PHP_VERSION} TZ: ${WORKSPACE_TIMEZONE} environment: - TZ=${WORKSPACE_TIMEZONE} ports: - 80:80 - 443:443 volumes: - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER} - ./env/php_apache/sites-available.template:/etc/apache2/sites-available.template networks: - backend ... 略 ...
docker-compose.yml のあるディレクトリを起点として、 ./env/php_apache/sites-available.template ディレクトリに、 000-default.conf.template 、 default-ssl.conf.template とファイルをリネームして置きました。
あとは、これを run 時にコンテナに送り込んでやればよいですね♪
最初は、 volumes: に、 - ./env/php_apache/sites-available.template/000-default.conf.template:/etc/apache2/sites-available/000-default.conf.template
とファイル単位で送り込もうとしたのですけれども、うまくいきません。。。
あれ、、、ファイル単位での共有もできたような気がしたのですけれども、、、><。
ですので、上述したような、ディレクトリ単位の共有といたしました。共有先のディレクトリを sites-available.template とし、既存の sites-available としませんでした。
それは、既存のディレクトリを共有した場合、既存のディレクトリのコンテナ側のファイルが、ホスト側に残ってしまいます。これを避けたいためです。
3. コンテナに渡す設定ファイルの必要項目を変数化して run 時に埋め込む。
これは、既存の Docker には用意されていない機能です。そこでワークアラウンドな対処が必要となります。このために必要なことが次です。
- イメージに evnsubst をインストールする。
- コンテナ起動時のコマンドオプションで環境変数を設定ファイルに埋め込む
docker container run [OPTIONS] IMAGE [COMMAND] [ARG...]
の COMMAND オプションで行う。- docker-compose.yml の command: で行う。
イメージに evnsubst をインストールする。
に従い、次のようにしました。
まず、php:7.2-apache イメージは、 debian:stretch-slim イメージを元に作られています。
そこで、 apt-get install gettext-base
のやり方でインストールします。
ARG PHP_VERSION FROM php:${PHP_VERSION}-apache ARG TZ=UTC ENV TZ ${TZ} RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN apt-get update && apt-get install -y \ gettext-base \ libssl-dev \ openssl \ ssl-cert \ unzip \ zlib1g-dev \ && docker-php-ext-install \ pdo_mysql \ zip \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && a2enmod rewrite \ && a2enmod ssl \ && a2ensite default-ssl COPY ./php.ini /usr/local/etc/php/ RUN curl -s http://getcomposer.org/installer | php && \ mv composer.phar /usr/local/bin/composer
ビルド後、コンテナを起動して入り、 envsubst -h
でヘルプが表示されることを確認しました!よいですね♪
コンテナ起動時のコマンドオプションで環境変数を設定ファイルに埋め込む
- 環境変数を埋め込むテンプレート (設定ファイル) に変数を設定する。
- docker-compose.yml の command: で envsubst を実行する。
- テンプレートに変数展開したくない文字列を含む場合、 envsubst の引数として展開対象の引数を指定する。
- envsubst の次に (&& でつなげて) もともとの Dockerfile の CMD で指定されているコマンドを実行する。
環境変数を埋め込むテンプレート (設定ファイル) に変数を設定しました。
テンプレートに埋め込んだ変数は DOCUMENT_ROOT です。埋め込むには ${DOCUMENT_ROOT}
と記述しました。
さて、ここで気になるのが、もともとテンプレートに書かれている ${APACHE_LOG_DIR}
です。このような文字列がある場合は、 envsubst 実行時にそのまま残すように一工夫しなければなりません。
... 略 ... DocumentRoot ${DOCUMENT_ROOT} <Directory ${DOCUMENT_ROOT}> ... 略 ... ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined ... 略 ...
... 略 ... DocumentRoot ${DOCUMENT_ROOT} ... 略 ... ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined ... 略 ...
docker-compose.yml の command: で envsubst を実行する。
テンプレートに変数展開したくない文字列を含む場合、 envsubst の引数として展開対象の引数を指定します。例えば、次です。
- 低機能でポータブルなテンプレートエンジンが欲しい→そこでenvsubstですよ – Qiita のテンプレート中に変数展開したくない文字列を含んでいる場合
envsubst の次に (&& でつなげて) もともとの Dockerfile の CMD で指定されているコマンドを実行します。これは、 Dockerfile 毎に異なりますので、調べますと、 apache2-foreground
でした。
また、イメージには DOCUMENT_ROOT という環境変数はもともとはありませんので、 run 時に指定してやります。
以上をふまえて、次のようになりました。
... 略 ... services: php_apache: build: context: ./php_apache args: PHP_VERSION: ${PHP_VERSION} TZ: ${WORKSPACE_TIMEZONE} environment: - DOCUMENT_ROOT=${DOCUMENT_ROOT} - TZ=${WORKSPACE_TIMEZONE} ports: - 80:80 - 443:443 volumes: - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER} - ./env/php_apache/sites-available.template:/etc/apache2/sites-available.template command: > /bin/bash -c " envsubst '$$DOCUMENT_ROOT' < /etc/apache2/sites-available.template/000-default.conf.template > /etc/apache2/sites-available/000-default.conf && envsubst '$$DOCUMENT_ROOT' < /etc/apache2/sites-available.template/default-ssl.conf.template > /etc/apache2/sites-available/default-ssl.conf && exec apache2-foreground" networks: - backend ... 略 ...
これで完成です!
docker-compose up -d
後、コンテナに入って設定ファイルを確認してみますと、DOCUMENT_ROOT の値が展開されていました♪
おわりに
Docker において環境ごとの設定の差は、コンテナ起動時に環境変数で渡す、という方向はとても便利に感じます。
環境変数をどれだけコンテナの設定に反映できるかは、イメージの実装次第で、環境変数で渡しても設定が変えられない場合に、今回あたってしまいました><。
Apache や Nginx など設定ファイルが大きくなるようなソフトウェアの場合、仕方がないとも思います。なんでもかんでも環境変数で設定できるようにしようと思った場合、非常に沢山の環境変数を用意し変えればならなくなります。それに、設定ファイルに階層等の構造がある場合、環境変数のみでは対応できない場合もあるかと思います。
それでも、ワークアラウンドでもよいので対応してみたかったため、今回の投稿となりました。
今回は、 envsubst を知れたのが大きかったです。 Docker 以外の場でも使えます♪
以上です。