これはPHPアドベントカレンダー2017 10日目の記事です。
あまりの寒さに、毎日背中にホッカイロを貼り付けてます。hanhan1978です。
コンテナでの開発を続けていると、最初は開発出来たこと自体が嬉しくて、次はDockerfileの中身を最適化してビルド順を最適化してなるべくビルド時間が短くなるように工夫していきます。
ただ、工夫はDockerイメージのレイヤを複雑に重ねてしまうので、コンテナイメージ自体は大きくなってしまいます。今回はPHPのフレームワークであるLaravelのアプリケーションを例にとって、メンテナンス可能でかつ、コンテナイメージを小さくする方法について紹介します。
結果から
気の早い人のために、まず結果をのせておきます。
概要 | イメージサイズ | Dockerfile |
---|---|---|
1. 普通のLaravelコンテナ | 300MB | v0.0.1 |
2. dockerignore対応 | 352MB | v0.0.2 |
3. マルチステージビルド | 121MB | v0.0.3 |
4. マルチステージビルド - Nginxベース | 63.8MB | v0.0.4 |
2は増えてるじゃん!という気もしますが、最終的に63.8MBまで小さくすることが出来ました。詳細については、下の方を読んで見て下さい。
この記事で目指すこと
本ポストでは、メンテナンサビリティを考慮して、PHP及び、Nginxの公式コンテナイメージをベースにしつつ、極力小さいコンテナイメージを作る方法について解説します。
世の中には、専用のベースイメージを作って、とにかく小さいPHPコンテナを作ろうとしている方々もいます。それはそれで素晴らしい取組とは思うのですが、アプリケーションエンジニアであれば、ベースイメージよりもアプリケーションの構築に力を注ぎたい所です。
※PHPのミニマムイメージを作っている方のリポジトリ
GitHub - nubs/docker-php-minimal: A collection of Dockerfiles for different versions of a very barebones PHP.
なお、コンテナのサイズの大小がどうやって決まるのかについては、下記の記事が優しく解説しています。
ベースイメージのサイズ
まず、今回使う公式PHPイメージがベースにしているAlpine Linux
のコンテナイメージサイズを計ります。
1 | $ docker images alpine:3.6 |
3.97MBでした。Alpine Linux
は必要最低限の機能のみを収めたコンテナイメージですので、ここがコンテナイメージの最小値と考えてよいと思います。
次に、PHPコンテナ開発のベースイメージとして、php:7.2-fpm-alpine
のサイズを確認します。
1 | $ docker images php:7.2-fpm-alpine |
76.6MBです。Alpine Linux
のサイズを下回るのは大変そうですが、今回の目標としてとりあえず80MB程度を目安にします。
1. 普通のLaravelコンテナ
リンク先のDockerfileは、特に何の考慮もなく 7.2-fpm-alpine
のイメージをベースにして、必要なライブラリをどんどんインストールした状態です。
普通にDockerでPHPアプリを開発し始めた場合、こんなDockerfile
になるんじゃないかなと思います。
イメージサイズ
1 | $ docker images hanhan1978/docker-laravel55-skelton:0.0.1 |
見ての通りで、300MBもあります。地球に優しくないサイズです。CD1枚分に到達しようかという勢い。さっそくダイエットしていきましょう。
2. dockerignoreの設定
必ずしもダイエットにつながるとは限らないのですが、.dockerignore
を設定します。
これにより、ローカルに持っている余分なディレクトリをビルド時にコンテナ側に送信しなくなるので、少し省スペース化が期待できます。また、開発でしか必要としないライブラリとかもついでに削りましょう。
こんな内容で.dockerignore
ファイルを追加します。
1 | laravel/vendor |
composerのライブラリ群と、Laravel Mix
用のnode_modulesを除外対象にしています。
イメージサイズ
1 | $ docker images hanhan1978/docker-laravel55-skelton:0.0.2 |
ふ、増えてやがる…
予想外に成長してしまいました。これ結構あるあるで、ローカルのファイル群のが実は容量が小さかったり、インストールの段階でネットワーク不調でcomposerがソース落とし直したりされたりで、結構変動します。
.dockerignore
を追加すること自体は、アプリケーションとして正しいことではあると思うので、しょうがないので突き進みます。
3. マルチステージビルド
Use multi-stage builds | Docker Documentation
Dockerでは、version17.05以降でマルチステージビルドが使えます。要するにビルドで使うコンテナと、最終的な実行可能ファイルだけを詰め込んだコンテナを分けて使うことが出来ます。
まずLaravelアプリケーションの最終実行イメージにおいて、node_modules
は完全に邪魔です。必要なのは、npm run production
で出力される Javascriptやcssのファイルだけです。
そこで、マルチステージビルドを利用して、node系のモジュール群をコンテナから追い出してしまいます。
中身を見てもらうとわかりますが、ビルドが二段階に別れています。一つ目は公式npmコンテナをベースイメージにして、Laravel Mix
を実行しています。
抜粋 npmの部分
1 | FROM node:9.2 |
抜粋 次のコンテナで0番目のコンテナからファイルをコピー
1 | COPY --from=0 /var/laravel /var/www/laravel |
この書き方をすることで、Laravel Mix
実行後のアプリケーションディレクトリをコピーしてきます。--from=0
と書いてありますが、別名をつけることも可能です。詳しくは公式ドキュメントをどうぞ。
イメージサイズ
1 | $ docker images hanhan1978/docker-laravel55-skelton:0.0.3 |
ああ、もうこれで終わりでいいんじゃないかというくらい小さくなりました。
121MBですから、当初の1/3くらいです。流石マルチステージビルドです。最高!
しかし、まだ何かモニョリます。100MBを下回りたいというのが一つと、HTTPサーバとしてNginxをインストールしているのですが、phpのベースイメージ内にNginxをインストールするのはなんか嫌だなと。
そこで、マルチステージをもう一つ推し進めて、必要最低限のPHPの実行ファイル群だけを公式Nginxコンテナにコピーすることで、もう一つイメージを小さく出来ないか試します。
4. マルチステージビルド - Nginxベース
1 | $ docker images nginx:1.13-alpine |
nginxのベースイメージは15.5MB
ここにPHPの必要最低限の実行ファイルをコピーします。
ついでと言っては何ですが、vendor
配下のtest系のスクリプトも全部削除しました。本番で必要な物以外は全て消しましょう。
イメージサイズ
1 | $ docker images hanhan1978/docker-laravel55-skelton:0.0.4 |
最終的に63.8MBまで減らすことが出来ました。PHPの最低限の実行ファイルのみなので、後はアプリケーションのコードを減らす以外に無いかな…
最初の1/6くらいにはなりましたので、及第点というところでしょうか。
コンテナイメージが重いよ〜とお困りの方は、是非マルチステージビルドを有効活用して、コンテナのダイエットをされたらと思います。