Docker Composeでdocker multi stage buildをつかってみた

少し前に、[Docker環境でcomposerのimageつかってupdateしてみる](https://qiita.com/yuya_sega/items/a8f2c3c58ae3762c0458)の記事を書かせていただきましたが、この構成だと、`docker-compose up`の際に毎回`composer update`が実行されて、時間かかって嫌だなぁと思ってました。。

その問題を解消するために、今回はDocker multi stage buildを使って`composer update`の起動タイミングを制御してみようと思います。

一式をGitHubに公開してますので、是非。
https://github.com/yuya-sega/docker-multi-stage-build-sample

# まず、できたものみてみる
##### docker/php/Dockerfile
今回作成したDockerfileはこちらです。
FROMが２つありますが、`dev`としてaliasつけているのが普段、開発で使用することを想定してるステージです。
`composer`としてaliasつけているのが、`composer update`を実行したいときに使用するステージです。


```dockerfile
FROM php:7.3-apache AS dev

CMD ["apache2-foreground"]

FROM dev AS composer

RUN apt-get update && apt-get install -y zip unzip
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer

CMD ["composer", "update", "--no-plugins", "--no-scripts"]
```

##### docker-compose.yml
Docker Composeファイルで主に注目すべきは、`target: ${PHP_CONTAINER_BUILD_TARGET}`の部分です。`target`に、先程Dockerfile内で命名したalias名を指定することによって、ビルド時のステージを切り替えることが可能となります。

```yml
version: "3.7"

services:
  php:
    build: 
      context: .
      dockerfile: ./docker/php/Dockerfile
      target: ${PHP_CONTAINER_BUILD_TARGET}
    volumes:
      - ./app:/var/www/html/
    ports:
      - "8010:80"
```

##### .env
docker-compose.ymlで参照している変数は、envファイルに定義してあります。

```config
PHP_CONTAINER_BUILD_TARGET=composer
```


# 実際に動かしてみる
#### `PHP_CONTAINER_BUILD_TARGET=composer`で動かす
composerを実行して動かす場合、up後コンテナは正常終了します。
するとapp配下にvenderフォルダなどが生成され、必要なライブラリがダウンロード完了した状態となります。

```console
$ docker-compose build && docker-compose up
Building php
Step 1/7 : FROM php:7.3-apache AS dev
 ---> 406c73effb13
Step 2/7 : CMD ["apache2-foreground"]
 ---> Running in 68f88f2b3ab8
Removing intermediate container 68f88f2b3ab8
 ---> f55426e4e3d8

Step 3/7 : FROM dev AS composer
 ---> f55426e4e3d8
Step 4/7 : RUN apt-get update && apt-get install -y zip unzip
 ---> Running in 1b2252f6568b
Get:1 http://security-cdn.debian.org/debian-security stretch/updates InRelease [94.3 kB]

~~ 略 ~~

php_1  | Writing lock file
php_1  | Generating autoload files
docker-multi-stage-build-sample_php_1 exited with code 0
$
```

#### `PHP_CONTAINER_BUILD_TARGET=dev`で動かす
次に、.envファイルの`PHP_CONTAINER_BUILD_TARGET`を`composer`から`dev`に書き換えます。
起動する際は、再度`docker-compose build`を実行する必要がありますのでご注意ください。

```console
$ docker-compose build && docker-compose up
Building php
Step 1/2 : FROM php:7.3-apache AS dev
 ---> 406c73effb13
Step 2/2 : CMD ["apache2-foreground"]
 ---> Using cache
 ---> f55426e4e3d8

Successfully built f55426e4e3d8
Successfully tagged docker-multi-stage-build-sample_php:latest
Recreating docker-multi-stage-build-sample_php_1 ... done
Attaching to docker-multi-stage-build-sample_php_1
php_1  | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 192.168.16.2. Set the 'ServerName' directive globally to suppress this message
php_1  | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 192.168.16.2. Set the 'ServerName' directive globally to suppress this message
php_1  | [Thu Sep 26 05:44:39.139258 2019] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.25 (Debian) PHP/7.3.3 configured -- resuming normal operations
php_1  | [Thu Sep 26 05:44:39.139583 2019] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
```

実行すると、コンテナは起動状態を維持したまま作業が進められます！
これで、何度コンテナをupしても、`composer update`が実行されず、`up`時間を短縮することができました

# 少し解説

### FROM dev AS composer
ここでは、エイリアス名`dev`で作成したステージをベースに、別のステージをつくるような指定となっています。
今回はわざわざcomposerのイメージを落としてくる必要もないと考え、このようにしてますが、以下のようにしてDockerfileをシンプルに書くことも可能です。

```dockerfile
FROM php:7.3-apache AS dev

CMD ["apache2-foreground"]

FROM composer:1.9.0 AS composer
WORKDIR /var/www/html
CMD ["composer", "update"]
```

### composerステージbuildしたあと、devステージでもbuildしなきゃいけない
`PHP_CONTAINER_BUILD_TARGET=composer`で`docker-compose build && docker-compose up`した後、
`PHP_CONTAINER_BUILD_TARGET=dev`で`docker-compose build && docker-compose up`しなければならない手順なのですが、devのビルド時は、composerでのビルドの際にできたキャッシュを利用したビルドとなるので、ビルドに時間がかからないため、今回はよしとしました。

```console
Building php
Step 1/2 : FROM php:7.3-apache AS dev
 ---> 406c73effb13
Step 2/2 : CMD ["apache2-foreground"]
 ---> Using cache  ← キャッシュ使ってますね！
 ---> f55426e4e3d8
```

# 課題
いくつか課題残ってると思うので、以下に箇条書きしてきます。

- 必要なときにcomposer updateしわすれるんじゃないか。。
- .envをいちいち書き換えるのがめんどくさい

# さいごに
multi stage buildは触ってみて全然難しいものでないと感じたので、今後はどんどん活用していきたいです。