前回に引き続き、今回もマルチステージビルドネタです。
コンテナイメージを極力小さくする方法として、マルチステージビルドは有効な手段です。 例えばGo言語のアプリケーションは次のようなDockerfileを作って、イメージを作成していたのではないかと思います。
# syntax=docker/dockerfile:1 FROM docker.io/golang:1.18 AS builder WORKDIR /usr/src/app COPY *.go ./ COPY go.mod ./ RUN go build main.go FROM docker.io/busybox:stable-glibc WORKDIR /root/ COPY --from=builder /usr/src/app/main ./ EXPOSE 8090 CMD [ "./main" ]
前回のブログ記事でも説明しましたが、1つ目のFROM行のブロックはGoのコンテナーイメージを使って、 アプリケーションバイナリーをビルドしています。
2つ目のブロックでは、1つ目のブロックで生成したバイナリーを2つ目のコンテナーに持ってきて、アプリケーションイメージとしてビルドしています。このイメージを使ってコンテナーを作るとmainというバイナリーが実行され、ポート8090で待ち受けします。
最終的な成果物のイメージはGo言語の実行環境が含まれない最小イメージの代表格であるbusyboxというイメージが使われます。同じような理由から、Alpine Linuxベースのイメージを使っている場合もあるかもしれません。
今回紹介するのはRed Hat Universal Base Images (UBI)イメージです。これはRed Hatが提供、メンテナンスするコンテナーイメージであり、制限なく利用および再配布可能なコンテナイメージになっています。 イメージによってはrpm,dnf,yumといった使い慣れたコマンドを使って、コンテナアプリケーションの構築が可能になります。
UBIイメージはかつてはRed Hatのレジストリーからダウンロードして使う形でしたが、現在はDocker Hubというポピュラーに使われるコンテナレジストリーでもオフィシャルイメージが提供されています。
[追記]
そのため、docker search ubi
みたいに実行すると、このような感じでUBIイメージが見つかります。
% docker search ubi NAME DESCRIPTION STARS OFFICIAL AUTOMATED redhat/ubi8 Red Hat Universal Base Image 8 58 redhat/ubi8-minimal Red Hat Universal Base Image 8 Minimal 28 ibmcom/websphere-liberty WebSphere Liberty multi-architecture images … 22 redhat/ubi8-micro Red Hat Universal Base Image 8 Micro 18 redhat/ubi8-init Red Hat Universal Base Image 8 Init 13 redhat/ubi9 Red Hat Universal Base Image 9 6 redhat/ubi9-micro Red Hat Universal Base Image 9 Micro 3 redhat/ubi9-minimal Red Hat Universal Base Image 9 Minimal 2 redhat/ubi9-init Red Hat Universal Base Image 9 Init 2
このイメージを使うには docker image pull redhat/ubi9-micro
などとすれば良いです。
UBI Base Image以外のアプリケーションが組み込み済みのイメージは引き続き、Red Hatのサイトからダウンロードできます。
UBIを使ったマルチステージビルド
Red Hat UBI 9のベースイメージは大きく4つのものが提供されています。ubi9 は使い勝手が同じバージョンのRHELに近いイメージであり、ubi9-init にはInitが含まれます。つまりサービスが動かせるイメージです。
ubi9-minimalは一見ubi9と同等に見えますが、microdnfという軽量版のパッケージマネージャが実装されています。これによってrpmパッケージを追加することができますが、使い勝手は通常のdnfと異なっており実装されていないオプションが存在します。ubi9-micro は最も小さいイメージです。パッケージマネージャなど、対話的に利用するようなソフトウェアは実装されていない分軽量ですが、なにかソフトウェアを導入したいと思ったときにちょっと大変です。
redhat/ubi9-init latest 99252fce3c48 3 weeks ago 244MB redhat/ubi9 latest 4bc05abf4ce5 4 weeks ago 227MB redhat/ubi9-minimal latest a5934dd8e7ad 4 weeks ago 136MB redhat/ubi9-micro latest 70eeda7622be 3 weeks ago 25.6MB
ubi-microについては、Red Hatさんのエンジニアブログの以下の記事がわかりやすかったので、ご覧ください。ブログではbuildahを使ったイメージのカスタマイズについて書かれています。ブログにも書かれていますが、ワンバイナリーでも動くアプリケーションの事項などに使うのがもっとも最適なイメージなのではないかと思います。
イメージサイズが小さいということは、導入されているバイナリーやライブラリーが少ないということです。脆弱性対策やイメージの可搬性のことを考えれば、小さいイメージの方が良いということです。ただその一方で、普段の使い勝手と異なると効率の面を考えると、プラスの面もあればマイナスの面もあると思います。
一体何を言っているのかというと、UBIイメージは一番小さいものでもbashが入っています。コンテナでアプリを動かすのが当たり前になればわざわざコンテナのシェルに入ってガシガシ作業することはあまりないかもしれませんが、使い慣れたシェルを使えるのはかなり大きいと思います。
コンテナーに含まれるbashのバージョンを確認してみると...次のようにバージョンが表示されました。
% docker container run -it redhat/ubi9-micro bash --version GNU bash, version 5.1.8(1)-release (aarch64-redhat-linux-gnu) Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
ちなみに「のようなもの」と書いたのは理由があって、コンテナに必要ではないと思われるパッケージは組み込みのパッケージマネージャからはダウンロードできないようになっているようで、RHELベースのコンテナーイメージとは異なるようです。
では、UBIを使ってイメージをビルドしてみましょう。先程のDockerfileをUBIイメージを使うように変更してみましょう。 本例ではgoの実行ファイルの他に何も導入はしないので、ubi9-microを使ってみましょう。
# syntax=docker/dockerfile:1 FROM docker.io/golang:1.18 AS builder WORKDIR /usr/src/app COPY *.go ./ COPY go.mod ./ RUN go build main.go FROM docker.io/redhat/ubi9-micro:latest WORKDIR /root/ COPY --from=builder /usr/src/app/main ./ EXPOSE 8090 CMD [ "./main" ]
コンテナイメージの作成は、現在動かしているアーキテクチャー向けのみで良ければいつものコマンドでイメージ作成できます。
% docker image build --compress -t localhost/golang-demo:latest -f Dockerfile-ubi9 .
作ったイメージは、Dockerでは次のように実行できます。
% docker container run -p 58090:8090 -d localhost/golang-demo:latest % curl http://localhost:58090/hello hello
ビルドしたイメージはDockerやPodmanの他、Kubernetesなどでも利用できます。 イメージを色々なCPUアーキテクチャー向けにビルドしたい場合は、multi-CPU architectureサポートのガイドのようにイメージをビルドします。一つのコマンドを実行するだけで、x86サーバーやARMサーバー、Raspberry Piを代表としたARMボードなどでコンテナーベースでアプリケーションを実行できるようになります。
https://docs.docker.com/desktop/multi-arch/
なによりUBIイメージは定期的にRed Hatによってメンテナンスされているというところが安心感があります。コンテナにとってコンテナイメージは非常に重要なものです。キチンとメンテナンスが行き届いたイメージを使うことで、安心してコンテナを使えます。