Dockerfile の ARG を使って xdebug をインストールしたり,しなかったり した方法のメモ

## 結論

https://qiita.com/t_n/items/203cd3593427a35f7ed7 のように、 `shell` の `if 〜 then 〜 fi` を使う。

ただし、記事にある通りで、

> これはするべきではないです

と思います。が、今の所、この方法しか思いつかなかった。。というメモ。

具体例:

```Dockerfile
# ..snip..
COPY ./docker/workspace/usr/local/etc/php/conf.d/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
RUN if [ "${PHP_XDEBUG_INSTALL}" = "true" ]; then pecl install xdebug ;fi
RUN if [ ! "${PHP_XDEBUG_INSTALL}" = "true" ]; then rm -rf /usr/local/etc/php/conf.d/xdebug.ini ;fi
# ..snip..
```

## 実現したかったこと

- Dockerfile を使って、 PHP の動くコンテナを作成する
- ある条件(ARG で与えるパラメーター)に従って必要ならば xdebug をインストール
  - 具体的には、 `PHP_XDEBUG_INSTALL` が `"true"` なら xdebug をインストール
  - `"false"` なら xdebug をインストールせず、かつ、 `xdebug.so` の読み込みもしない
- Dockerfile 単独でビルドするときはパラメーターを与えないことで `PHP_XDEBUG_INSTALL` の値を自動的に `"false"` とする
- docker-compose でビルドするときは `PHP_XDEBUG_INSTALL` の値を `docker-compose.yml` に記載して `"true"` となるようにする
- かつ、 `xdebug` の各種パラメーターを ARG で与えるようにしたい

## なんで、こんな事を実現したかったか

- 作成した Docker Image を Production でも使えるようにしたかった
- かつ、 Production では xdebug は入れたくなかった
- でも、開発環境では、可能な限り Production と親しい状態に保ちたかった(xdebug は入れておきたかった)
- でも、 docker for mac 使ってる人もいるかもだし、個人的には docker-machine 使いたいしで、特に `xdebug.remote_host` の値とか各開発者の環境に依存するので、可能なら外部でコントロールしたい[^1]
- echo 使って xdebug.ini 出力しても良いけど、 Dockerfile 見にくいの嫌やし…
- なにより、あまり何も考えずにコマンド叩けば済むようにしたかった

## どうやって実現したか

前提：

- ベースイメージは `php:7.2-fpm-alpine3.7` を使ってます

### シンプルに `php:7.2-fpm-alpine3.7` で xdebug をインストールして有効化する方法

```Dockerfile
# ..snip..
RUN pecl install xdebug
RUN docker-php-ext-enable xdebug
# ..snip..
```

`pecl` でインストールして `docker-php-ext-enable` で有効にする。

これで、 `/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini` に以下のファイルが配置される。

```/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20170718/xdebug.so
```

`docker-php-ext-enable xdebug` を実行すると `/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini` が用意される。(便利やねー)


### `Dockerfile` で ARG を使う かつ デフォルト値を持たせる

```Dockerfile
# ..snip..
ARG PHP_XDEBUG_INSTALL="false"
# ..snip..
```

`ARG` で宣言してるので `docker build` 時に `--build-arg <KEY>=<VAL>` みたいに指定できるようになる。

`ARG PHP_XDEBUG_INSTALL="false"` としてデフォルト値である `"false"` を渡してるので、宣言しない場合は自動的に `"false"` となる。

`PHP_XDEBUG_INSTALL` を `"true"` としたければ、 `--build-arg PHP_XDEBUG_INSTALL=true` としたら大丈夫。(の、はず。。)

### `docker-compose.yml` で ARG を上書きする

```docker-compose.yml
# ..snip..
  workspace:
    build:
      context: ./
      dockerfile: docker/workspace/Dockerfile
      args:
        PHP_XDEBUG_INSTALL: "true"
# ..snip..
```

上記のように、 `args` で指定する。

この例であれば、 `PHP_XDEBUG_INSTALL` は `"true"` となる。

なお、 ARG は http://docs.docker.jp/engine/reference/builder.html#arg にある通り、ビルド時のみ有効な値となる。

### あまり考えずにコマンドを叩けばOKになった！

これで、何も考えずに `docker build` したら `PHP_XDEBUG_INSTALL` は `"false"`, `docker-compose build` したら `PHP_XDEBUG_INSTALL` は `"true"` という状況が実現できた。

### ARG の値を用いて条件分岐する

Dockerfile には if 文が無い。

ので、冒頭の記事のように RUN 命令と `shell` を組み合わせて実現する。

`PHP_XDEBUG_INSTALL` が `"true"` の時だけ `xdebug` をインストールして有効化したいならば、以下のように書けば事が足りる。

```Dockerfile
# ..snip..
RUN if [ "${PHP_XDEBUG_INSTALL}" = "true" ]; then \
    pecl install xdebug \
    && docker-php-ext-enable xdebug \
;fi
# ..snip..
```

ただし、これではデフォルトの `/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini` が出来上がるだけで、

> でも、 docker for mac 使ってる人もいるかもだし、個人的には docker-machine 使いたいしで、特に `xdebug.remote_host` の値とか各開発者の環境に依存するので、外部でコントロールしたい

これが実現できない。

### echo で実現してみる

```Dockerfile
# ..snip..
ARG PHP_XDEBUG_REMOTE_HOST="192.168.99.1"
# ..snip..
RUN if [ "${PHP_XDEBUG_INSTALL}" = "true" ]; then \
    pecl install xdebug \
    && docker-php-ext-enable xdebug \
    && echo "xdebug.remote_host=${PHP_XDEBUG_REMOTE_HOST}" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
;fi
# ..snip..
```

一応、これで実現できる。

でも、 xdebug の他のパラメーターもここに書くの？なんか嫌じゃない？(増えてきたら)見にくいし…

### というわけで、どうするか

- 事前に `xdebug.ini` を COPY で `/usr/local/etc/php/conf.d/xdebug.ini` へ転送する
  - そのため、 `docker-php-ext-enable xdebug` は行わない
- `xdebug.ini` は、変数を使って初期化できるように書いておく
- `PHP_XDEBUG_INSTALL` が `"true"` 以外なら
  - COPY した `/usr/local/etc/php/conf.d/xdebug.ini` を `rm -rf /usr/local/etc/php/conf.d/xdebug.ini` で削除する

例えば、 xdebug.ini は以下のような感じにデフォルト値設定しておくとか。

```./xdebug.ini
[xdebug]
zend_extension=xdebug.so
xdebug.default_enable=${PHP_XDEBUG_DEFAULT_ENABLE}
xdebug.remote_enable=${PHP_XDEBUG_REMOTE_ENABLE}
xdebug.remote_port=${PHP_XDEBUG_REMOTE_PORT}
xdebug.remote_host=${PHP_XDEBUG_REMOTE_HOST}
xdebug.remote_autostart=${PHP_XDEBUG_REMOTE_AUTO_START}
xdebug.remote_connect_back=${PHP_XDEBUG_REMOTE_CONNECT_BACK}
xdebug.idekey=${PHP_XDEBUG_IDEKEY}

; profiler
xdebug.profiler_enable = ${PHP_XDEBUG_PROFILER_ENABLE}
xdebug.profiler_output_dir = ${PHP_XDEBUG_PROFILER_OUTPUT_DIR}
```

で、 Dockerfile はと言うと以下のような感じで用意する。

```Dockerfile
# ..snip..
ARG PHP_XDEBUG_DEFAULT_ENABLE="Off"
ARG PHP_XDEBUG_REMOTE_ENABLE="On"
ARG PHP_XDEBUG_REMOTE_PORT="9001"
ARG PHP_XDEBUG_REMOTE_HOST="127.0.0.99"
ARG PHP_XDEBUG_REMOTE_AUTO_START="Off"
ARG PHP_XDEBUG_REMOTE_CONNECT_BACK="Off"
ARG PHP_XDEBUG_IDEKEY="PHPSTORM"
ARG PHP_XDEBUG_PROFILER_ENABLE="Off"
ARG PHP_XDEBUG_PROFILER_OUTPUT_DIR="/tmp"

ENV PHP_XDEBUG_DEFAULT_ENABLE ${PHP_XDEBUG_DEFAULT_ENABLE}
ENV PHP_XDEBUG_REMOTE_ENABLE ${PHP_XDEBUG_REMOTE_ENABLE}
ENV PHP_XDEBUG_REMOTE_PORT ${PHP_XDEBUG_REMOTE_PORT}
ENV PHP_XDEBUG_REMOTE_HOST ${PHP_XDEBUG_REMOTE_HOST}
ENV PHP_XDEBUG_REMOTE_AUTO_START ${PHP_XDEBUG_REMOTE_AUTO_START}
ENV PHP_XDEBUG_REMOTE_CONNECT_BACK ${PHP_XDEBUG_REMOTE_CONNECT_BACK}
ENV PHP_XDEBUG_IDEKEY ${PHP_XDEBUG_IDEKEY}
ENV PHP_XDEBUG_PROFILER_ENABLE ${PHP_XDEBUG_PROFILER_ENABLE}
ENV PHP_XDEBUG_PROFILER_OUTPUT_DIR ${PHP_XDEBUG_PROFILER_OUTPUT_DIR}
# ..snip..
## PHP_XDEBUG_INSTALL の値に関わらずとりあえず事前に COPY
COPY ./docker/workspace/usr/local/etc/php/conf.d/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
## PHP_XDEBUG_INSTALL が true なら pecl install xdebug するけど、 docker-php-ext-enable xdebug は行わない
RUN if [ "${PHP_XDEBUG_INSTALL}" = "true" ]; then pecl install xdebug ;fi
## PHP_XDEBUG_INSTALL が true ではないなら、 COPY した /usr/local/etc/php/conf.d/xdebug.ini を rm する
RUN if [ ! "${PHP_XDEBUG_INSTALL}" = "true" ]; then rm -rf /usr/local/etc/php/conf.d/xdebug.ini ;fi
# ..snip..
```

当然ながら、 `RUN if 〜 then 〜 fi` の中で COPY 命令は使えない(RUN 命令なので)ので、事前に COPY して、不要ならば削除する方針とした。

## さいごに

作業した記憶から書き起こしてるのと、実際に自分の書いたコードとは一部違うので、誤植とか書き間違いがあったらごめんなさい。

他に、もっと効率的に書けるよ！とか、こんなの Docker way とは違うよ！みたいな事、あるかと思います！教えてくださいm(_ _)m

-----

[^1]: `docker-compose.yml` を gitignore に入れて、 `docker-compose.yml.template` とかをリポジトリに入れて各人でローカルの `docker-compose.yml` を編集してもらう方法とか、 `.env` を使う方法(その場合は `ARG` でのコントロールじゃなくなるかも。)とか、色々あるけどとりあえず割愛。