マルチステージビルドを使ってDockerにcomposerをインストールする
PHPのパッケージ管理システムであるcomposerをDockerにインストールする際にマルチステージビルドを使うといい感じだったのでメモ。
まずは普通にインストール
composerの公式ドキュメントに従い、PHPを使ってインストールしていく getcomposer.org
Dockerfileに記述するとこんな感じ
FROM php:7.4-cli php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" php -r "if (hash_file('sha384', 'composer-setup.php') === '795f976fe0ebd8b75f26a6dd68f78fd3453ce79f32ecb33e7fd087d39bfeb978342fb73ac986cd4f54edd0dc902601dc') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" php composer-setup.php php -r "unlink('composer-setup.php');"
しかしこれだと、composerのバージョンが変わるとハッシュ値が変更されてしまい、インストールが失敗してしまう。 となると、毎回composerのバージョンが上がる度にハッシュ値を書き換える必要がある。
マルチステージビルドを使ったインストール
composerの公式のDockerイメージがあるので、マルチステージビルドを使用して以下のように書くことができる、
FROM php:7.4-cli COPY --from=composer /usr/bin/composer /usr/bin/composer
COPY --from=composer /usr/bin/composer /usr/bin/composer
の部分で、composerのイメージの中から/usr/bin/composer
だけをPHPコンテナの中にコピーでき、毎回バージョンが上がる度にハッシュ値を書き換えなくても良くなる。
そもそもマルチステージビルドとは?
Docker 17.05からの機能で、docker buildを複数のビルドにして実行することができる。
これによって、ビルドするだけの一時的なイメージを作るステージ、デプロイ用のイメージ作るステージを分離することができる。
これで何が嬉しいかというと、例えばビルド済みのイメージから必要なファイルだけをデプロイ用のイメージに取り込むことによってデプロイ用のイメージのサイズを削減することが出来たりする。
goのプログラムをビルドして試してみる
hello worldを表示させるためのプログラムを以下のように記述
hello.go
package main import "fmt" func main() { fmt.Println("hello world") }
作成したプログラムをビルドするステージ、プログラムを実行するステージに分けて以下のようにDockerfileを記述する。
# ビルドするだけの一時的なイメージを作るステージ FROM golang:alpine AS build-stage COPY . /work WORKDIR /work RUN go build hello.go # ビルド済みのバイナリファイルを実行するステージ FROM busybox COPY --from=build-stage /work/hello /usr/local/bin/hello ENTRYPOINT ["/usr/local/bin/hello"]
COPY --from=build-stage
とすることで、直前のステージで作り出された生成内容を、単純に新たなステージにコピーしている。
こうすることで、プログラム実行するイメージには、直前のステージでビルドしたバイナリファイルだけをコピーすることができ、無駄のないイメージを作成できることができる。