Docker 18.09 新機能 (イメージビルド&セキュリティ)
NTTの須田です.Moby (≒Docker),BuildKit,containerdなど,コンテナ関連のオープンソースソフトウェアのメンテナ (開発委員.コミッタとも.)を務めています.
本記事では,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倍速くなるとの報告がなされています.
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倍以上速いビルドも可能です.
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ホストへの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コミュニティでは主に以下の作業に取り組んでいます:
RUN --mount=type=cache
などの新Dockerfile命令の正式採用- 非rootユーザでDocker daemonを実行可能とする機能 (Rootless モード)
- containerdとの重複コードの除去,軽量化
おわりに
私たちNTTは,オープンソースコミュニティで共に活動する仲間を募集しています.ぜひ弊社 ソフトウェアイノベーションセンタ紹介ページ及び,採用情報ページをご覧ください.