はじめに
こんにちは、Sreake 事業部 佐藤慧太@(SatohJohn) です。
以下の docker build check という機能について、検証をし、Google Cloud の Cloud Build に組み込んで見る。ということをやってみました。
https://www.docker.com/ja-jp/blog/introducing-docker-build-checks/
Docker のドキュメントには以下のようにまとまっております。
https://docs.docker.com/build/checks/
上記記事では、開発者が Dockerfile の作成、編集に対してとても苦労していることがわかっていると紹介されています。
開発者との会話の中で、多くの人がコンテナイメージを構築するためのベストプラクティスを学び、それに従うのに苦労していることがわかりました。 2024 の「アプリケーション開発の現状」レポートによると、Docker ユーザーの 35% が、Dockerfile の作成と編集を上位 3 つのタスクの 1 つとして挙げています。しかし、回答者の 55%は、Dockerfileの作成がサポートとして最も選択されているタスクであると報告しています。
https://www.docker.com/ja-jp/blog/introducing-docker-build-checks/
その意味でこのツールは Dockerfile の lint check をおこない、上記の課題に対し、既存のベストプラクティスから学べるようにしたものです。
Dockerfile の linter としては以下のようにすでにツールが存在しています。
- hadolint https://github.com/hadolint/hadolint
これらと違い、docker build check は見て分かる通り docker の機能として組み込まれており、ツールを別途インストールする必要がない、というのが大きな特徴となります。
そして、 docker build の —-check
オプションは build 自体をしないので、とても実行が早くおわるというのも特徴です。
利用するための Docker のバージョンは 1.8 以上、 Buildx バージョン 0.15.0 以上です。
実装
今回利用するものは、私が作ったものと言うよりは、最近アプリケーション開発に利用した https://github.com/google/mesop というフレームワークで Deploy時に利用する Dockerfile を使います。
https://google.github.io/mesop/guides/deployment/#dockerfile
FROM python:3.12.4-bullseye
RUN apt-get update && \\
apt-get install -y \\
# General dependencies
locales \\
locales-all && \\
# Clean local repository of package files since they won't be needed anymore.
# Make sure this line is called after all apt-get update/install commands have
# run.
apt-get clean && \\
# Also delete the index files which we also don't need anymore.
rm -rf /var/lib/apt/lists/*
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8
# Install dependencies
RUN pip install poetry \\
&& poetry config virtualenvs.create false
COPY ./pyproject.toml ./poetry.lock* ./
RUN poetry install
# Create non-root user
RUN groupadd -g 900 mesop && useradd -u 900 -s /bin/bash -g mesop mesop
USER mesop
# Add app code here
COPY . /srv/mesop-app
WORKDIR /srv/mesop-app/src
# Run Mesop through gunicorn. Should be available at localhost:8080
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "main:me"]
上記の Dockerfile を build すると以下のように warning がでてきます。
docker build .
3 warnings found (use docker --debug to expand):
- LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 16)
- LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 17)
- LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 18)
ENV の記載方法を更新したほうが良い、との警告ですね。 build 自体は成功しています。詳細を見たい場合は —debug
オプションを付けます。
docker --debug build .
3 warnings found:
- LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 16)
Legacy key/value format with whitespace separator should not be used
More info: <https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/>
Dockerfile:16
--------------------
14 | rm -rf /var/lib/apt/lists/*
15 |
16 | >>> ENV LC_ALL en_US.UTF-8
17 | ENV LANG en_US.UTF-8
18 | ENV LANGUAGE en_US.UTF-8
--------------------
- LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 17)
Legacy key/value format with whitespace separator should not be used
More info: <https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/>
Dockerfile:17
--------------------
15 |
16 | ENV LC_ALL en_US.UTF-8
17 | >>> ENV LANG en_US.UTF-8
18 | ENV LANGUAGE en_US.UTF-8
19 |
--------------------
- LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 18)
Legacy key/value format with whitespace separator should not be used
More info: <https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/>
Dockerfile:18
--------------------
16 | ENV LC_ALL en_US.UTF-8
17 | ENV LANG en_US.UTF-8
18 | >>> ENV LANGUAGE en_US.UTF-8
19 |
20 | # Install dependencies
--------------------
この build 時に warning があった際にエラーとしたい場合はファイルの先頭に # check=error=true
をつけます。
https://docs.docker.com/build/checks/#fail-build-on-check-violations
ビルドのチェック違反は、デフォルトでは終了コード 0 の警告として報告されます。Dockerfile
https://www.docker.com/ja-jp/blog/introducing-docker-build-checks/check=error=true
のディレクティブを使用して、違反が報告されたときにビルドが失敗するように Docker を設定できます。これにより、ビルド チェックが実行された後、実際のビルドが実行される前に、ビルドでエラーが発生します。
後に紹介をしますが、コマンドのオプションでも、可能です
docker build --build-arg "BUILDKIT_DOCKERFILE_CHECK=skip=LegacyKeyValueFormat" .
docker desktop での実行
本題になりますが、上記までの場合は build を行いチェックがされました。再度になりますが —check
オプションを付けると build されないため高速にチェックができます。
docker build --check .
[+] Building 1.3s (3/3) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 979B 0.0s
=> [internal] load metadata for docker.io/library/python:3.12.4-bullseye 1.3s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
WARNING: LegacyKeyValueFormat - <https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/>
"ENV key=value" should be used instead of legacy "ENV key value" format
Dockerfile:15
--------------------
13 | rm -rf /var/lib/apt/lists/*
14 |
15 | >>> ENV LC_ALL en_US.UTF-8
16 | ENV LANG en_US.UTF-8
17 | ENV LANGUAGE en_US.UTF-8
--------------------
WARNING: LegacyKeyValueFormat - <https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/>
"ENV key=value" should be used instead of legacy "ENV key value" format
Dockerfile:16
--------------------
14 |
15 | ENV LC_ALL en_US.UTF-8
16 | >>> ENV LANG en_US.UTF-8
17 | ENV LANGUAGE en_US.UTF-8
18 |
--------------------
WARNING: LegacyKeyValueFormat - <https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/>
"ENV key=value" should be used instead of legacy "ENV key value" format
Dockerfile:17
--------------------
15 | ENV LC_ALL en_US.UTF-8
16 | ENV LANG en_US.UTF-8
17 | >>> ENV LANGUAGE en_US.UTF-8
18 |
19 | # Install dependencies
--------------------
このとき、Dockerfile に# check=error=true
をつけていなくても、終了コードは 1 が返ってきます。
Unlike a regular build, if any violations are reported when using the
https://www.docker.com/ja-jp/blog/introducing-docker-build-checks/--check
flag, the command exits with a non-zero status code.
Cloud Build での実行
私が Google Cloud が好きだから(大事)というのもありますが、Cloud Build でやるパターンも考えます。
Cloud Build の設定については省きます。ファイルはとても簡易的なものを用意しました。
steps:
- id: docker-file-check-app
name: "gcr.io/cloud-builders/docker"
args: ["build", ".", "--check"]
waitFor: ["-"]
- id: build-app
name: "gcr.io/kaniko-project/executor:latest"
args:
- --destination=${LOCATION}-docker.pkg.dev/$PROJECT_ID/app/main:latest
- --cache=true
- --cache-ttl=6h
waitFor: ["docker-file-check-app"]
options:
logging: CLOUD_LOGGING_ONLY
timeout: 60s
gcr.io/cloud-builders/docker を使って、check をして成功すれば、アプリケーションを build するというものです。サンプルとして作成しているだけですので、build 時にチェックすることでもやりたいことは達成できるとは思いますが、今回はあくまでも —-check
について動かすと言うものにしています。
これを動かすと —-check
option が見つからずエラーになります。
当たり前ですが、docker のバージョンが違うためです。私が実行した際には gcr.io/cloud-builders/docker を見てみると latest は 20.10.24
と同じになっており、docker version も 20.10.24
になっております
そのため、現状CIとしていれるのであれば 確かめるとしたら docker のバージョンを更新するのが良さそうです。
steps:
- id: docker-file-check-app
name: "gcr.io/cloud-builders/docker:24.0.9"
args: ["build", "--check", "."]
waitFor: ["-"]
- id: build-app
name: "gcr.io/kaniko-project/executor:latest"
args:
- --destination=${LOCATION}-docker.pkg.dev/$PROJECT_ID/app/main:latest
- --cache=true
- --cache-ttl=6h
waitFor: ["docker-file-check-app"]
options:
logging: CLOUD_LOGGING_ONLY
timeout: 60s
もう一度動かすとチェックされている事がわかります。
lint で言われた部分を修正してすると、0 が返り、build が進みます。
ENV LC_ALL=en_US.UTF-8
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US.UTF-8
補足
対応してほしくないものについては skip をさせることができます。
例えば、先程の Dockerfile の先頭に # check=skip=LegacyKeyValueFormat
と記載すると警告を skip できます。警告の種類については以下にまとまっております。
https://docs.docker.com/reference/build-checks/
また、Dockerfile に記載するのではなく、以下のように CSV 文字列を build-arg
オプションに渡し実行することもできます。
docker build --check --build-arg "BUILDKIT_DOCKERFILE_CHECK=skip=LegacyKeyValueFormat" .
まとめ
warning のある Dockerfile があった場合、以下のようにまとめることができます。
error=true | 終了コード | build時間 | |
---|---|---|---|
docker build | false | 0 | build するため長い |
docker build | true | 0以外 | build するため長い? |
(docker desktop では docker build —check と同じ動きをする) | |||
docker build —check | false | 0以外 | 短い |
docker build —check | true | 0以外 | 短い |
これを見ると分かる通り、CI のような高速に回したい機能については docker build の —check
オプションは有用かと思います。
しかし、実際、利用してみたところ docker build check で確認できるものは hadolint よりも少ないという認識です。例えば、 https://hadolint.github.io/hadolint/ で触れられている Always tag the version of an image explicitly
のような点については check してくれません。
また、以下記事で触れていただいているような COPY
の利用についても対応されているようには見えません。
https://future-architect.github.io/articles/20240726a/
この様に、まだ発展段階と思いますが、導入自体はとても簡単ですし、まず一度試してみるのが良いかと思います。