Feedforce Developer Blog

フィードフォース開発者ブログ

Docker BuildKit の --secret フラグでイメージビルド時の秘匿情報を環境変数経由で渡せるようになっていた

こんにちは、id:daido1976 です。入社してもうすぐ 3 年が経ちます。

Docker BuildKit の --secret フラグについて、公式ドキュメントでの説明や関連する日本語記事はすでにいくつかあるのですが、2020/12/08 にリリースされた Docker Engine 20.10.0 で「秘匿情報を環境変数経由で渡せるようになっていたこと」への言及が見当たらなかったので書かせていただきます。*1(個人的にはとても嬉しいアップデートでした…!)

リリースノートの該当箇所は以下です。

See. https://docs.docker.com/engine/release-notes/#20100

  • buildkit: secrets: allow providing secrets with env moby/moby#41234 docker/cli#2656 moby/buildkit#1534
    • Support --secret id=foo,env=MY_ENV as an alternative for storing a secret value to a file.
    • --secret id=GIT_AUTH_TOKEN will load env if it exists and the file does not.

要点

  • Docker Engine 20.10.0 までは --secret id=my_secret,src=my_secret.txt のようにファイル経由でしか秘匿情報を渡せなかった
  • 20.10.0 からは --secret id=my_env,env=MY_ENV のように環境変数経由で秘匿情報を渡せるようになった
  • 上記の --secret id=my_env,env=MY_ENV にはショートハンドが用意されており --secret id=MY_ENV とも書ける(この場合 Dockerfile 側でも idMY_ENV になる)

使い方

基本的な使い方は 公式ドキュメント に書いてあるのでそちらに説明を譲ります。

以下は主に上記の 要点 と公式ドキュメントに記載のない仕様を説明するための最小のサンプルコードです。

こんな Dockerfile があったとして、

# syntax = docker/dockerfile:1.2
FROM alpine

# 1. docker build 時に `id` を明示的に指定する場合(例: `--secret id=my_env1,env=MY_ENV1` )
RUN --mount=type=secret,id=my_env1 cat /run/secrets/my_env1
# 2. docker build 時に `id` を明示的に指定しない場合(例: `--secret id=MY_ENV2`) 
RUN --mount=type=secret,id=MY_ENV2 cat /run/secrets/MY_ENV2
# 3. `dst` を指定した場合(`dst` を指定しないとデフォルトで `/run/secrets{id}` に秘匿情報が入ります)
RUN --mount=type=secret,id=MY_ENV3,dst=/foobar cat /foobar

# 補足: key 名の指定は実はいろいろできるらしい
# https://github.com/moby/buildkit/blob/v0.8.2/frontend/dockerfile/instructions/commands_runmount.go#L154-L216

以下のように docker build すると、環境変数経由で秘匿情報を渡すことができます。

$ export MY_ENV1='my_env1!!!' && export MY_ENV2='my_env2!!!' && export MY_ENV3='my_env3!!!'
# 複数の秘匿情報を渡すには --secret フラグを複数使えば良い
$ DOCKER_BUILDKIT=1 docker build --secret id=my_env1,env=MY_ENV1 --secret id=MY_ENV2 --secret id=MY_ENV3 --no-cache --progress=plain .

# ...
#8 [2/4] RUN --mount=type=secret,id=my_env1 cat /run/secrets/my_env1
#8 sha256:d32e4958843414fb006b5dbe7c259112f1bd481d17d84098c3d84f499793cb3b
#8 0.252 my_env1!!!#8 DONE 0.3s

#9 [3/4] RUN --mount=type=secret,id=MY_ENV2 cat /run/secrets/MY_ENV2
#9 sha256:4cc22ee3d7f39180f4f4f8167deaa481e852b2fc577c89a234fe88b9852952ba
#9 0.301 my_env2!!!#9 DONE 0.4s

#10 [4/4] RUN --mount=type=secret,id=MY_ENV3,dst=/foobar cat /foobar
#10 sha256:91f97f55e633c8ed675126abefa015110265a7fbd40e4aeaf88204dcb912cb26
#10 0.283 my_env3!!!#10 DONE 0.3s
# ...

Docker Compose で --secret フラグが使えないことへの対策

余談ですが、Docker Compose には BuildKit が統合されているものの、2021年3月現在 --secret フラグは利用できません。(対応中らしき PR はある)

私のチームではワークアラウンドとして、開発環境でも docker-compose build ではなく docker build を使う方法を取りました。

Docker Compose では docker-compose.yml で指定しているイメージと同名のイメージがローカルにあればそちらを使ってくれるので、以下のような docker-compose.yml を書いて、

version: '3'

services:
  app:
    build: .
    image: my-app
    # ...
$ DOCKER_BUILDKIT=1 docker build -t my-app --secret id=MY_ENV .

上記のように docker build で同名のイメージを事前に作っておいて docker-compose から使う、という流れになります。

参考記事

*1:BuildKit や --secret フラグってそもそも何?という方は最下部の参考記事に詳しくまとまっておりますのでぜひご覧ください。