Content Addressable DockerイメージとRegistry2.0
Docker 1.6: Engine & Orchestration Updates, Registry 2.0, & Windows Client Preview | Docker Blog
Docker1.6が出た.コンテナやイメージのラベリング(RancherOSの“Adding Label Support to Docker 1.6”がわかりやすい)や,Logging Driversといった新機能が追加された.今回のリリースで自分的に嬉しいのはDockerイメージがContent-addressableになったこと(#11109).
今までDocker Regitryを介したイメージのやりとりはイメージの名前とタグ(e.g., tcnksm/golang:1.2
)しか使うことができなかった.タグはイメージの作成者によって付与されるのもであり,同じタグであっても必ず同じイメージが利用できるという保証はなかった(Gitでいうとコミットハッシュが使えず,タグのみしか使えないという状況).
Docker1.6と同時に発表されたRegistry2.0(docker/distribution)によりイメージにユニークなID(digest
)が付与されるようになり,確実に同じイメージを参照することができるようになった(immutable image references).
使ってみる
DockerHubはすでにRegistry2.0になっているのですぐにこの機能は使える.が,今回は自分でPrivate Registryを立ててこの機能を試してみる(環境はboot2docker on OSX).
まずはRegistryを立てる.v1と同じようにDockerイメージが提供されている.
$ docker run -p 5000:5000 registry:2.0
簡単なDockerfile
を準備してtcnksm/test-digest
イメージをビルドする.
FROM busybox
$ docker build -t $(boot2docker ip):5000/tcnksm/test-digest:latest .
images
コマンドで確認する.--digests
オプションをつけるとdigest
が表示されるようになる.Gitと同じように考えると直感とズレるかもしれないがbuild
するだけではdigest
は生成されない.
$ docker images --digests
REPOSITORY TAG DIGEST IMAGE ID CREATED VIRTUAL SIZE
192.168.59.103:5000/tcnksm/test-digest latest <none> 8c2e06607696 3 days ago 2.433 MB
Registryにpush
してみる.push
するとdigest
が生成される.
$ docker push $(boot2docker ip):5000/tcnksm/test-digest:latest
...
Digest: sha256:e4c425e28a3cfe41efdfceda7ccce6be4efd6fc775b24d5ae26477c96fb5eaa4
生成したイメージを削除しdigest
を使ってイメージをpull
してみる.NAME:TAG
ではなくNAME@DIGEST
という形式で指定する.
$ docker rmi $(boot2docker ip):5000/tcnksm/test-digest:latest
$ docker pull $(boot2docker ip):5000/tcnksm/test-digest@sha256:e4c425e28a3cfe41efdfceda7ccce6be4efd6fc775b24d5ae26477c96fb5eaa4
images
コマンドで確認する.今回はdigest
が表示されているのが確認できる.
$ docker images --digests
REPOSITORY TAG DIGEST IMAGE ID CREATED VIRTUAL SIZE
192.168.59.103:5000/tcnksm/test-digest <none> sha256:e4c425e28a3cfe41efdfceda7ccce6be4efd6fc775b24d5ae26477c96fb5eaa4 8c2e06607696 3 days ago 2.433 MB
Dockerfile
Dockerfile
のFROM
でのイメージ名の指定にもdigest
は使える.気がついたら元のイメージ更新されていて完成イメージが意図しないものになっていたということが避けられる.
FROM 192.168.59.103:5000/tcnksm/test-digest@sha256:e4c425e28a3cfe41efdfceda7ccce6be4efd6fc775b24d5ae26477c96fb5eaa4
$ docker build .
Step 0 : FROM 192.168.59.103:5000/tcnksm/test-digest@sha256:e4c425e28a3cfe41efdfceda7ccce6be4efd6fc775b24d5ae26477c96fb5eaa4
---> 8c2e06607696
Successfully built 8c2e06607696
イメージの更新
Dockerfile
を編集して新しいイメージをbuild
する.
FROM busybox
MAINTAINER tcnksm
$ docker build -t $(boot2docker ip):5000/tcnksm/test-digest:latest .
Registryにpush
する.すると今度は異なるdigest
が生成される.
$ docker push $(boot2docker ip):5000/tcnksm/test-digest:latest
...
Digest: sha256:4675f7a9d45932e3043058ef032680d76e8aacccda94b74374efe156e2940ee5
仕組み
簡単に仕組みを説明する.digest
は手元で生成されるわけではない.push
してRegistry側で生成される.
まずclientはイメージと共にImage ManifestをRegistryに送る(署名する).Image ManifestはそのDocker Imageの内容をJSONで定義したもの.Golangのstructでいうと以下のようなものでイメージの名前やタグ,FSレイヤーといった情報が書かれる(Manifestはここに定義されている).
type ManifestData struct {
Name string `json:"name"`
Tag string `json:"tag"`
Architecture string `json:"architecture"`
FSLayers []*FSLayer `json:"fsLayers"`
History []*ManifestHistory `json:"history"`
SchemaVersion int `json:"schemaVersion"`
}
APIを叩くとManifestの中身を見ることができる.
$ curl $(boot2docker ip):5000/v2/tcnksm/test-digest/manifests/latest
そしてRegistryは以下の関数内でリクエストされたManifestを元にdigest
を生成する(registry/handlers/images.go).
// PutImageManifest validates and stores and image in the registry.
func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http.Request)
Docker-Content-Digest
ヘッダでそれをclientに送る(API doc)
202 Accepted
Location: <url>
Content-Length: 0
Docker-Content-Digest: <digest>
Regitryが異なったら…?
送るManifestは同じなのでRegistryが違っても同じdigest
が付与される.digest
はRegistryをまたがってユニークになる.
上で作成したイメージをDockerHubにpush
してみる.同じdigest
が付与される.
$ docker build -t tcnksm/test-digest:latest .
$ docker push tcnksm/test-digest:latest
...
Digest: sha256:e4c425e28a3cfe41efdfceda7ccce6be4efd6fc775b24d5ae26477c96fb5eaa4
Registry2.0
Faster and Better Image Distribution with Registry 2.0 and Engine 1.6 | Docker Blog
docker/distributionは新しいRegistryの実装で,APIやセキュリティなど今までのRegistryの問題を解決しようとしている.今まではPythonで実装されていたがGo言語で再実装されている.
特徴的なのは,
- イメージManifestの再定義(Image Manifest Version 2, Schema 1) - #8093を参照.セキュリティの改善が主な目的.
- APIの刷新(Docker Registry HTTP API V2, #Detail)- URIの改善,Manifest V2を利用できるようにする,Push/Pullが途中で死んでも終わったところから再開できるようにする,などなど(詳しく見てないけどclientはGo言語のinterfaceとして定義されていたので自分で独自のものをつくれる…?)
- バックエンドのストレージをPluggable化(Docker-Registry Storage Driver)- 現在は,インメモリ,ファイルシステム,S3,Azure Blob Storageが選択できる.Go言語のinterfaceとして定義されてるので自分で実装することもできる.
- Webhookの実装(Notifications)- Push/Pullといったイベントが発生するごとに設定したendopointにリクエストを送ることができる.
あとまだスケルトンしかないがdist
コマンドというものを作ろうとしている(dist).これはDockerデーモンなしでDockerイメージのpull/pushを行うコマンド.Dockerの少し嫌な部分としてrumtimeとイメージのダウンロードが分かれていないというのがあったが,それをここで解決しようとしているっぽい.