Docker に Composer をインストールする際,Composer のバージョンアップのたびにハッシュ値を書き換えるのが面倒だったので,何か解決策はないかと調べていたところこちらの記事を発見しました。
正直これだけでいいじゃんという話ですが,内容が大変ミニマルでしたので,解説を交えて記事にさせていただきました。
結論
Dockerfile に以下を追記するだけで OK 。
COPY --from=composer /usr/bin/composer /usr/bin/composer
ただし multi-stage builds (マルチステージビルド) を使用するため,Docker のバージョンは >=17.05
が必須です。
以下で解説します。
メジャーな方法(Composer 公式)
Composer 公式 のインストール方法を Dockerfile に記述するのが一般的かと思います。
例:PHP-FPM に Composer をインストールしたイメージを作る Dockerfile
FROM php:7.3-fpm
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php -r "if (hash_file('sha384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
&& php composer-setup.php \
&& php -r "unlink('composer-setup.php');" \
&& mv composer.phar /usr/local/bin/composer
この方法の問題点
上記の方法の4行目内のハッシュ値が問題です。
php -r "if (hash_file('sha384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
このハッシュ値は Composer はバージョンアップごとに変更されるので(上記は2019/09/30現在),そのたびに Dockerfile も変更しなければなりません。
めんどうです。
解決法
Docker 公式の Composer イメージ を使用します。
Docker 17.05 から導入されたマルチステージビルドを使えば非常にシンプルに記述できます。
(すべて Docker 公式の Composer イメージのページの DESCRIPTION に記載されています)
- (optimal) create your own build image and install Composer inside it.
Note: Docker 17.05 introduced multi-stage builds, simplifying this enormously:
COPY --from=composer /usr/bin/composer /usr/bin/composer
以下の `:1.8.6` のようにタグでバージョン指定もできますし,常に最新がよければタグなしもしくは `:latest` を付ければ OK です。
```Dockerfile
COPY --from=composer:1.8.6 /usr/bin/composer /usr/bin/composer
マルチステージビルドとは?
With multi-stage builds, you use multiple
FROM
statements in your Dockerfile. EachFROM
instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image.
簡単にいうと,ひとつの Dockerfile で複数のビルドができ,各ビルドの段階で必要な部分だけコピーして(残りは破棄される),最終的に使用したいベースイメージにペーストできる機能です。
例:golang をビルドしたバイナリだけを Alpine Linux イメージにコピーして起動する
# golang をビルド
FROM golang:1.7.3 AS builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
# 上でビルドしたバイナリだけを Alpine Linux イメージにコピー・起動
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
さらに,イメージからコピーするだけであれば,下記のように簡略化できます。
例:Nginx のベースイメージ内の nginx.conf だけコピーしてくる
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
解決法の具体例
前述した PHP-FPM の例は以下のように書き直せます。
before (再掲)
FROM php:7.3-fpm
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php -r "if (hash_file('sha384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \
&& php composer-setup.php \
&& php -r "unlink('composer-setup.php');" \
after
FROM php:7.3-fpm
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
参考
謝辞
参考にさせていただいた記事「Dockerにcomposerをインストールする方法の正解」の hanhan 様に感謝いたします。