Docker 18.09 新機能 (イメージビルド&セキュリティ)

Akihiro Suda
nttlabs
Published in
11 min readNov 8, 2018

--

NTTの須田です.Moby (≒Docker),BuildKitcontainerdなど,コンテナ関連のオープンソースソフトウェアのメンテナ (開発委員.コミッタとも.)を務めています.

本記事では,2018年11月8日にリリースされたDocker 18.09の新機能を紹介します.

BuildKitの正式統合

Dockerfileからコンテナイメージをビルドする機能である,docker build の新しいバックエンドとして,BuildKitがDocker 18.06より実験的に採用されていましたが,Docker 18.09にて,正式な機能に格上げされました.

BuildKitは,須田が2017年4月に提案した docker build並列化のpull requestを契機として,Docker社 Tõnis Tiigi氏の主導により,Moby傘下のオープンソースプロジェクトとして同年夏に発足しました.プロジェクト発足時より,須田もメンテナとして開発に参加しています.

BuildKitは,Dockerfileの各命令間の依存性をDAG (有向非巡回グラフ)として表現することにより,命令の並列実行や,正確なキャッシュ判定を実現します.

また,ビルドコンテキストの差分転送などの最適化もなされています.

高速化の程度は場合によりけりですが,Dockerを用いてDocker自体(厳密にはMoby)をビルドするベンチマークの場合,約2倍速くなるとの報告がなされています.

出典: Tõnis Tiigi氏のDockerCon US 2018 発表資料,23頁

LLB

BuildKitは,Dockerfileそのものをビルドするのではなく,Dockerfileからコンパイルされる中間言語であるLLBをビルドします. (clangがC言語を機械語に直接コンパイルするのではなく,LLVM IRを介するのに似ています.)

LLBは,Dockerfile以外のイメージ記述言語からコンパイルすることも可能です.例えば,HerokuやCloud Foundryで用いられている,BuildpacksのYAMLからLLBをコンパイルすることも可能です.YAMLの1行目にshebangのように # syntax = tonistiigi/pack と記述しておけば, docker build コマンドから直接YAMLを扱うことができます.

$ cat manifest.yaml
# syntax = tonistiigi/pack
---
applications:
- name: myapp
memory: 128MB
disk_quota: 256MB
random-route: true
buildpack: python_buildpack
command: python hello.py
$ docker build –f manifest.yml

なお, # syntax = ... で指定する文字列は,Dockerで予約されている文字列ではなく,LLBを出力するプログラム(フロントエンド)のコンテナイメージのreference文字列です.ですので,DockerfileでもBuildpacksでもない,独自のイメージ記述言語のフロントエンドをユーザが実装し,利用することも可能です.

Cache Mount

従来の docker build では,コンパイラやパッケージマネージャのキャッシュを有効に活用できませんでした.

例えば,次のようなDockerfileがあるとします:

FROM golang
...
RUN go build -o /foo ./pkg/foo

Goのコンパイラはキャッシュをコンテナ内の /root/.cache/go-build に保存しますが,従来の docker build では,Goのソースや,Dockerfileの RUN go build … 以前の行を書き換えるたびに,コンパイラのキャッシュを含むイメージレイヤを破棄しなくてはなりませんでした.

BuildKitでは, RUN --mount=type=cache 命令を用いることで,キャッシュを保持することができます.ただし, 今のところ非標準命令であるため,Dockerfileの1行目に # syntax = docker/dockerfile:experimental と記述する必要があります.

# syntax = docker/dockerfile:experimental
FROM golang
...
RUN --mount=type=cache,target=/root/.cache/go-build \
go build -o /foo ./pkg/foo

RUN --mount=type=cache 命令をうまく活用すると,従来の docker build より33倍以上速いビルドも可能です.

出典: Tõnis Tiigi氏のDockerCon US 2018 発表資料,46頁

RUN --mount=type=cache 命令は, apt などのパッケージマネージャのキャッシュの保持にも利用することができます.

# syntax = docker/dockerfile:experimental
FROM ubuntu
RUN rm -f /etc/apt/apt.conf.d/docker-clean; \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > \
/etc/apt/apt.conf.d/keep-cache
RUN \
--mount=type=cache,target=/var/cache/apt \
--mount=type=cache,target=/var/lib/apt \
apt update && apt install -y gcc

Secret Mount

従来の docker build では,Dockerfileの中から,プライベートなGitリポジトリや,S3などへアクセスすることがが困難でした.

鍵ファイルを COPY ( ADD )すればいいだけのように思われるかも知れませんが,このような方法は危険です.鍵のファイルを後で RUN rm しても, COPY 命令に対応するイメージレイヤのtarの中には,鍵ファイルが残るためです.鍵ファイルを含むイメージを,不特定多数のユーザが参照可能なレジストリにpushすると,GitやS3などのアクセス権限を奪取される恐れがあります.

COPYした鍵ファイルがイメージに残存することを防ぐには,マルチステージビルドや, docker build --squash を注意深く用いる必要があります.

BuildKitでは, RUN --mount=type=secret 命令を用いることで,鍵ファイルをイメージに残さず安全にマウントすることができます.

# syntax = docker/dockerfile:experimental
FROM ...
RUN --mount=type=secret,id=ssh,target=/root/.ssh/id_rsa git clone ...

上の例のDockerfileは, docker build --secret id=ssh,src=$HOME/.ssh/id_rsa のように,任意の鍵ファイルを紐付けてビルドすることが出来ます.

SSH Mount

上記の RUN --mount=type=secret 命令は,パスフレーズ付きのSSH秘密鍵には向かない問題があります. ビルドコンテナには標準入力が接続されず,パスフレーズを入力できないためです.

パスフレーズ付きのSSH秘密鍵については,代わりに RUN --mount=type=ssh 命令を使用することができます.

# syntax = docker/dockerfile:experimental
FROM ...
RUN --mount=type=ssh git clone ...

docker build 実行前に ssh-agent を起動することで,パスフレーズを入力することが可能です.

$ eval $(ssh-agent)
$ ssh-add ~/.ssh/id_rsa
Enter passphrase for /home/penguin/.ssh/id_rsa: (パスフレーズを入力)
$ docker build --ssh default=$SSH_AUTH_SOCK .

BuildKitの有効化

BuildKitを有効化するには, docker buildコマンド実行前に環境変数DOCKER_BUILDKIT=1 を設定しておく必要があります.あるいは, /etc/docker/daemon.json{"features":{"buildkit":true}} と記述しても有効化できます.

図の通り,docker build コマンドの出力が従来と大きく変わっていれば,BuildKitが有効になっています.

左: 従来の `docker build` / 右: BuildKitを有効にした `docker build`

リモートDockerホストへのSSH接続

dockerコマンドは,リモートのDockerホストにREST APIを用いて接続することが出来ます.Dockerホストが攻撃者に乗っ取られることを防ぐには,TLSを有効化したり,ポートへアクセス可能なIPアドレスを絞ったりする必要があります.しかしながら,こうした設定は容易ではありません.実際,Dockerホストの設定不備を突いて仮想通貨を発掘するマルウェアが確認されています.

Docker 18.09では,須田が2017年より提案してきた,SSHを用いたリモートDockerホスト接続機能採用されています.SSHは,リモートのLinuxホストに接続する最も一般的な方法であり,TLSに比べて簡単に設定することが出来ます.

この機能は,環境変数 DOCKER_HOST=ssh://ユーザ@ホスト を設定した状態で docker コマンドを実行すると利用することが出来ます.単に ssh -l ユーザ ホスト -- docker コマンドを実行する場合と異なり,クライアントの ~/.docker/config.json に保存されているレジストリ認証情報や,ビルドコンテキストにアクセスすることが可能です.

その他の変更点

Docker 18.09からは,以下のストレージドライバが非推奨(deprecated)となっています:

  • devicemapper
  • overlay

これらのストレージドライバは,将来のリリースでは削除される予定となっています.また, aufs ドライバについても,Docker 19.03を目処に非推奨とすることが予定されています.ユーザは,可能な限り早期に overlay2 ドライバなどに乗り換えることが推奨されています. overlay2 ドライバへの乗り換え方については Dockerのドキュメントをご覧ください.

今後のリリース

Docker 17.03より18.06まではedge版が毎月,stable版が3ヶ月毎にリリースされていましたが,18.09以降は,6ヶ月毎のリリースが予定されています.

今後のリリースへ向け,Docker・Mobyコミュニティでは主に以下の作業に取り組んでいます:

おわりに

私たちNTTは,オープンソースコミュニティで共に活動する仲間を募集しています.ぜひ弊社 ソフトウェアイノベーションセンタ紹介ページ及び,採用情報ページをご覧ください.

--

--