カテゴリー
Linux

【Docker Compose】build 時ではなく、run 時に設定ファイルに環境変数を埋め込む方法メモ

はじめに

php:7.2-apache をビルドするときに、 DocumentRoot を設定していました。特に疑問に感じていませんでしたけれどもあるとき、さまざまな Docker イメージを見ていると設定は build 時ではなく、 run 時に環境変数で設定していることに気が付きました。

DocumentRoot も run 時に設定したいと思いました。

そうするためには、次の手順で実現できると思いました。

  1. Dockerfile で行っていた設定ファイルの COPY を、不要ならやめる。
  2. docker-compose run 時に volumes で設定ファイルのディレクトリを共有することでコンテナに設定ファイルを渡してやる。
  3. コンテナに渡す設定ファイルの必要項目を変数化して run 時に埋め込む。

上記の方法のうち、 1. と 2. は簡単です。 3. が難しかったです><。

今回、応急処置的な一時しのぎ的なワークアラウンドではありますけれども、汎用的で Linux コマンドの勉強になりましたので、解決するまでの道筋を順番に記録に残していこうと思います。

参考ページ

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 の次に (&& でつなげて) もともとの 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 以外の場でも使えます♪

以上です。

コメントを残す