Atsushi2022の日記

データエンジニアリングに関連する記事を投稿してます

Docker パーサーディレクティブ

Parser directive

Parser directiveの実体はDockerfileの先頭行に記述する特殊なコメントで、Dockerfileの後続行の処理方法に影響する。

syntaxescapeの2種類のパーサーディレクティブが存在する。

syntax

実はDockerfile 内で使用される構文は交換可能で、syntaxで構文の処理方法を指定することができる。例えば、以下のように先頭行に# syntax=docker/dockerfile:1と記述することで、Dockerfile内でヒアドキュメントを使用できるようになる。

# syntax=docker/dockerfile:1
FROM golang:1.21 as build
WORKDIR /src
COPY <<-EOF /src/main.go
  package main

  import "fmt"

  func main() {
    fmt.Println("hello, world")
  }
EOF
RUN go build -o /bin/hello ./main.go

docker/dockerfileイメージにより、BuildKitを使用してイメージをビルドできるようになる。ちなみにBuildKitとは、コンテナをビルドするためのデーモンらしい。

BuildKit とは、コンテナをビルドするためのツールキットであり、 buildkitdというデーモンとbuildctlコマンドで構成されています。 Dockerの標準のビルドと比べて、BuildKitでビルドした場合には以下のようなメリットがあります。

  • マルチステージDockerfileの各ステージを並列ビルドできる
  • ビルドキャッシュをDockerHubなどに外部保存/再利用ができる
  • SSH接続でリモートのファイルを取得
  • 秘密鍵などのファイルをイメージ内に残さないようにマウント

また、BuildKitの一部機能はDocker 18.06以降のDocker Engineに統合されており、 Docker単体でもBuildKitの一部機能を利用することができます。

syntaxはDockerfile Frontendとも呼ばれる。syntaxで外部のDockerfile frontend(dockerfileというDockerイメージ)を指定することで、構文処理の方法を変更することができる。BuildKitやDocker Engineをアップグレードしなくても、最新のDockerfile frontendを使用できる。

BuildKitは不使用のビルドステージを検知し、スキップする。例えば、次のようなDockerfileがあると、最終イメージはstage2になる。stage2baseステージから作成され、stage1は使われないステージになっている。このときBuildKitはbasestage2のみ処理し、依存関係のないstage1はスキップされる。一方、レガシーのビルダーでは、stage1も含め全てのステージが処理される。

# syntax=docker/dockerfile:1
FROM ubuntu AS base
RUN echo "base"

FROM base AS stage1
RUN echo "stage1"

FROM base AS stage2
RUN echo "stage2"

escape

escapeは、エスケープ文字を指定するために使用するパサーディレクティブ。デフォルトのエスケープ文字はバックスラッシュ\で、変更したい場合に使用するが、あまり用途はない気がする。

# escape=\

参考

BuildKit Dockerfile Frontend

Custom Dockerfile syntax

docker/dockerfile

Docker BuildKitを理解する

BuildKit

BuildKitとは

Differences between legacy builder and BuildKit