自分だけが使うDockerfileがずっと「かんぜんにりかいした」レベルで、Hadolintに怒られ続けて1年ぐらい経った気がするので、これを機にリファレンスを読み直して再入門しよう、というお話。
新しい発見も結構あったので、備忘録も兼ねて気になった部分だけピックアップ。
長くなりそうなので、まず第1弾。
コメントや命令前のスペース
次の例は間違いではないが、空白は保持されないので “discouraged” (推奨されない)とのこと。
# this is a comment-line
RUN echo hello
RUN echo world
個人的には RUN
コマンドの一部をコメントアウトする時はインデントに合わせているが、それぐらいなら問題ないのではないかな。
パーサディレクティブ
個人的新要素。どう処理するかについての特殊なコメントで、拡張や文字環境の差による問題を解決するもの。
以下の注意点がある:
- 重複できない
- 他のコメント(未定義ディレクティブを含む)の後に書くと、ただのコメント扱いになる
FROM
の後でもただのコメント扱い
今のところ、 syntax
と escape
の2つが定義されている。
syntax
イメージ情報を何のsyntaxで記述するかについて。BuildKitバックエンドが有効な場合に使える。
Docker Buildxを使うと自動で有効になる。もしくは DOCKER_BUILDKIT=1
にする。
Docker公式では「docker/dockerifle:1
」が推奨されている。
この部分のバージョニングはsemverなので、breaking changeが入らない限りはmajorは上がらない、ということなのだろう。
これを使うと、Dockerfileの独自拡張ができたり、YAMLとかをDSLに使うオレオレ構文で書けたりする。
ある範囲(エコシステム)に特化したビルドシステムの構築に使われてる印象。
例
Mockerfile (YAML)
#syntax=r2d4/mocker
apiVersion: v1alpha1
images:
- name: demo
from: ubuntu:16.04
package:
repo:
- deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8
- deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial edge
gpg:
- https://bazel.build/bazel-release.pub.gpg
- https://download.docker.com/linux/ubuntu/gpg
install:
- bazel
- python-dev
- ca-certificates
- curl
- build-essential
- git
- gcc
- python-setuptools
- lsb-release
- software-properties-common
- docker-ce=17.12.0~ce-0~ubuntu
external:
- src: https://storage.googleapis.com/kubernetes-release/release/v1.10.0/bin/linux/amd64/kubectl
dst: /usr/local/bin/kubectl
- src: https://github.com/kubernetes-sigs/kustomize/releases/download/v1.0.8/kustomize_1.0.8_linux_amd64
dst: /usr/local/bin/kustomize
sha256: b5066f7250beb023a3eb7511c5699be4dbff57637ac4a78ce63bde6e66c26ac4
- src: https://storage.googleapis.com/kubernetes-helm/helm-v2.10.0-linux-amd64.tar.gz
dst: /tmp/helm
install:
- install /tmp/helm/linux-amd64/helm /usr/local/bin/helm
- src: https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-217.0.0-linux-x86_64.tar.gz
dst: /tmp
sourceMopy (YAML)
#syntax=cmdjulian/mopy:v1
apiVersion: v1
python: 3.9.2
build-deps: [ libopenblas-dev, gfortran, build-essential ]
labels:
fizz: buzz
foo: ${fizz}
envs:
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python
indices:
- url: https://mirrors.sustech.edu.cn/pypi/simple
pip:
- numpy==1.22
- slycot
- git+https://user:[email protected]/moskomule/anatome.git@dev
- git+ssh://[email protected]/RRZE-HPC/pycachesim.git
- ./requirements.txt
project: my-python-app/
sourceFROM python:3.9.2 AS builder
RUN mkdir /build
WORKDIR /build
RUN --mount=type=cache,target=/var/cache/apt \
--mount=type=cache,target=/var/lib/apt \
apt update && \
apt install -y git-lfs libopenblas-dev gfortran build-essential
ENV PIP_NO_WARN_SCRIPT_LOCATION=0 \
PIP_USER=1 \
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python \
PYTHONPYCACHEPREFIX="$HOME/.pycache" \
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" \
PIP_DISABLE_PIP_VERSION_CHECK=1
RUN --mount=type=cache,target=/root/.cache \
--mount=type=ssh,required=true \
--mount=type=bind,source=./requirements.txt,target=/tmp/0requirements.txt \
pip install \
--retries 2 \
--extra-index-url https://mirrors.sustech.edu.cn/pypi/simple \
-r /tmp/0requirements.txt \
numpy==1.22 slycot git+https://user:[email protected]/moskomule/anatome.git@dev git+ssh://[email protected]/RRZE-HPC/pycachesim.git
RUN find /root/.local/lib/python*/ -name 'tests' -exec rm -r '{}' + && \
find /root/.local/lib/python*/site-packages/ -name '*.so' -exec sh -c 'file "{}" | grep -q "not stripped" && strip -s "{}"' \; && \
find /root/.local/lib/python*/ -type f -name '*.pyc' -delete && \
find /root/.local/lib/python*/ -type d -name '__pycache__' -delete
FROM gcr.io/distroless/python3:nonroot@sha256:ddc151b6cb50be22362cfbe1c9e073570b06925162a908eb8321dede8c209997
LABEL org.opencontainers.image.description="autogenerated by mopy" \
moby.buildkit.frontend="mopy" \
mopy.version="v1" \
mopy.python.version="3.9.2" \
mopy.sbom="[\"numpy==1.22\", \"slycot\", \"git+https://github.com/moskomule/anatome.git@dev\", \"git+ssh://[email protected]/RRZE-HPC/pycachesim.git\", \"./requirements.txt\"]" \
fizz="buzz" \
foo="buzz"
ENV PYTHONUNBUFFERED=1 \
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
COPY --from=builder --chown=nonroot:nonroot /root/.local/ /home/nonroot/.local/
COPY --chown=nonroot:nonroot ./my-python-app/ /home/nonroot/my-python-app
ENTRYPOINT [ "python" ]
WORKDIR /home/nonroot/my-python-app
CMD [ "main.py" ]
sourceCargo Wharf (Rust)
# syntax = localhost:10395/denzp/cargo-wharf-frontend:local
[workspace]
members = [
"binary-1",
"binary-2",
"lib-1",
]
[workspace.metadata.wharf.builder]
image = "rust"
[workspace.metadata.wharf.output]
image = "debian:stable-slim"
workdir = "/root"
user = "root"
pre-install-commands = [
{ shell = "echo 'pre-install' > /custom-setup", display = "My custom pre-install command" },
]
post-install-commands = [
{ shell = "echo 'post-install' > /custom-post-setup", display = "My custom post-install command" },
]
sourceNix
# syntax = ghcr.io/reproducible-containers/buildkit-nix:v0.1.0@sha256:c727e0efc2a3aa23bbd31404701b5eee420ada1f08c7d4e21d666f24804355b6
{
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
# See https://ryantm.github.io/nixpkgs/languages-frameworks/rust/
app = pkgs.rustPlatform.buildRustPackage {
name = "rust-httpserver";
cargoSha256 = "N8HCmBEiIX5G3F2OQH5IvkzpwhCJVpR51TB86gV9IAo=";
src = ./.;
};
in rec {
defaultPackage = pkgs.dockerTools.buildImage {
name = "rust-httpserver";
tag = "nix";
contents = [ pkgs.bash pkgs.coreutils app ];
config = {
Cmd = [ "rust-httpserver" ];
ExposedPorts = { "80/tcp" = { }; };
};
};
});
}
sourceescape
PowerShellのエスケープ文字は「`
」(backtick) であって「\
」(backslash) ではない。
特にWindowsではbackslashはドライブやディレクトリのデリミタなので、Dockerfileのデフォルト(backslash)のままだと RUN
とかで複数行に書きたい時におかしくなってしまう。
FROM microsoft/nanoserver
# ↓この行末の"\"がエスケープ文字とみなされてしまう
COPY testfile.txt c:\\
RUN dir c:\
# 「COPY testfile.txt c:\RUN dir c:」になってしまう
backtickをエスケープ文字として明示すれば回避できる。
# escape=`
FROM microsoft/nanoserver
COPY testfile.txt c:\
RUN dir c:\
トップレベルARG
(※ あくまで自分がそう呼んでるだけです)
Multi-stage buildsが導入され、 FROM
が複数書けるようになったのはいいが、共通の ARG
をどう指定するか。
実は最初の FROM
の前にグローバルな ARG
が置ける。
逆に FROM
の後(stage内)にある ARG
はそのstage内がスコープになる。
つまり、FROM
にはトップレベル ARG
がそのまま使えるが、stage内の RUN
とかには別途受け皿となる ARG
を置かないといけない。
# トップレベルARG
ARG VERSION=1.34.1
# FROMにはそのまま使える
FROM busybox:$VERSION
# FROMの後(stage内)では受け皿となるARGが別途必要。名前は同じで良いっぽい
ARG VERSION
RUN echo $VERSION > image_version
Predefined ARGs
ARG
は基本的に前もって宣言しておかないと使えないが、宣言しなくても使える ARG
がいくつか用意されている。
HTTP_PROXY
/http_proxy
HTTPS_PROXY
/https_proxy
FTP_PROXY
/ftp_proxy
NO_PROXY
/no_proxy
ALL_PROXY
/all_proxy
実は ARG
は docker history
で見えてしまうのもあって、秘匿しておきたい情報の受け渡しには向かない。
しかし、private repositoryのアクセス先などをbuild時に渡すこともある。
上記のPredefinedな ARG
をそのまま使えば、 docker history
には現れなくなる。
ただし ARG
として明示してしまうとPredefinedの扱いではなくなり、 docker history
で見えるようになるので注意が必要。
ヒアドキュメント
docker/dockerfile:1.4
で追加された。
RUN
と COPY
限定でヒアドキュメントが使える。
しかもshebangも指定できる。ちょっとビックリ。
# syntax=docker/dockerfile:1
FROM python:3.6
RUN <<EOT
#!/usr/bin/env python
print("hello world")
EOT
第1弾はひとまずここまで。
これら以外にも気になったところはあるので、それらはベストプラクティスも絡めて第2弾以降に。
RUN
の引数--mount
--network
COPY --link
- Dockerfileをstdinで渡す
- レイヤー生成の変更点
apt-get clean
の自動実行
など…