とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど

開催予定の勉強会

読者登録と各種SNSのフォローもよろしくお願いいたします。

コンテナイメージ「ubi-micro」を使ってマルチステージビルドを試す

前回に引き続き、今回もマルチステージビルドネタです。

devops-blog.virtualtech.jp

コンテナイメージを極力小さくする方法として、マルチステージビルドは有効な手段です。 例えば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によってメンテナンスされているというところが安心感があります。コンテナにとってコンテナイメージは非常に重要なものです。キチンとメンテナンスが行き届いたイメージを使うことで、安心してコンテナを使えます。