UUUM攻殻機動隊

UUUMのエンジニアによる技術ブログです

dockerあれこれ

Dockerを使う上で、少し解りずらいけど理解したらDockerと少し親しくなれるようなところをピックアップしてみようと思います。

Docker imageって何?

設定ファイルとファイルシステムのdiffから構成される

例えば下記のようなシンプルなDockerfileからビルドしたイメージを docker inspect [image] で中身を一部抜粋すると

FROM alpine
RUN apk --no-cache add  git
CMD ["/bin/echo", "hoge"]
[
    {
        "Id": "sha256:167ac9be494e05feadc5096832622cf9b5b70acaa62ac6e17f61de624e6eb2fb",
        "RepoTags": [
            "sample1:latest"
        ],
        "RepoDigests": [],
        "Parent": "sha256:efcaf1953f1fe27f5e9372fae521da68a506da7352ce8d17f3c94a09fdaa5ec4",
         ...
        "DockerVersion": "17.03.1-ce",
        "Author": "",
        "Config": {
            ...
            "Cmd": [
                "/bin/echo",
                "hoge"
            ],
            ...
        },
        ...
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:3fb66f713c9fa9debcdaa58bb9858bd04c17350d9614b7a250ec0ee527319e59",
                "sha256:bfd51f3d31f5ed500e02975f8207a2f1f604ecd85126da84614fbe93e8d7aed8"
            ]
        }
    }
]

こんな感じになります。

ちなみに alpine はとてもサイズが小さいLinux OSです。

alpinelinux.org

CMDで指定した内容が Config の Cmd にあって、ファイルシステムのdiff(RootFS Layers)に、alpineの分と、apk で gitをインストールした内容の2つ分入っていることが解ります。

イメージのレイヤーを確認する docker history [image] を実行すると

167ac9be494e        6 minutes ago       /bin/sh -c #(nop)  CMD ["/bin/echo" "hoge"]     0 B                 
efcaf1953f1f        23 hours ago        /bin/sh -c apk --no-cache add  git              21.5 MB             
a41a7446062d        11 days ago         /bin/sh -c #(nop)  CMD ["/bin/sh"]              0 B                 
<missing>           11 days ago         /bin/sh -c #(nop) ADD file:ce33aabbc5f370e...   3.97 MB

inspectの Parent のところが historyの1つ前のイメージ efcaf1953f1f と一致していますね。 これで何となくイメージの内容が解るようになりましたね。

Copy On Write

次はファイルシステムの diff はイメージ間で共有されるというところを見ていきます。 さっきのinspectの内容の

"Layers": [
   "sha256:3fb66f713c9fa9debcdaa58bb9858bd04c17350d9614b7a250ec0ee527319e59",
   "sha256:bfd51f3d31f5ed500e02975f8207a2f1f604ecd85126da84614fbe93e8d7aed8"
]

の部分です。

apk で git をインストールしていた箇所を、ADDでファイルをコンテナに追加する処理に書き換えて試してみます

FROM alpine
ADD hoge.txt huga.txt
CMD ["/bin/echo", "hoge"]

で build して inspect した結果

"Layers": [
  "sha256:3fb66f713c9fa9debcdaa58bb9858bd04c17350d9614b7a250ec0ee527319e59",
  "sha256:77b5680dd209091028af983ee403527c3ff6d45933599e6e6d7caecfca4c7bcd"
]

1つの3fb66f … が同じで、2つめの値は違うということが見ると解りますね。 ということで、1つめの diffのハッシュが alpine のもので、2つめのハッシュはそれぞれ、apk add git の内容と、 hoget.txt の内容ということが解ると思います。

これでファイルシステムの内容が共有されるということが解りましたね。

build時のキャッシュについて

親イメージから辿っていき、命令文が完全に一致するものがあれば、新しくイメージを作らずにそれを使う

build時に既存のイメージを流用できるようなケースは、新しいイメージを作らずに既存のイメージが流用されます。

例えばさっきの1つめのDockerfileを最初にビルドすると

Sending build context to Docker daemon 99.33 kB
Step 1/3 : FROM alpine
 ---> a41a7446062d
Step 2/3 : RUN apk --no-cache add  git
 ---> Running in 0b1a3015df35
fetch http://dl-cdn.alpinelinux.org/alpine/v3.6/main/x86_64/APKINDEX.tar.gz

...

OK: 25 MiB in 17 packages
 ---> efe320ae020a
Removing intermediate container 0b1a3015df35
Step 3/3 : CMD /bin/echo hoge
 ---> Running in f0829ddf04a5
 ---> 1313f4ef40dd
Removing intermediate container f0829ddf04a5
Successfully built 1313f4ef40dd

で、Dockerfileを変更せずに再度ビルドすると

Sending build context to Docker daemon 99.33 kB
Step 1/3 : FROM alpine
 ---> a41a7446062d
Step 2/3 : RUN apk --no-cache add  git
 ---> Using cache
 ---> efe320ae020a
Step 3/3 : CMD /bin/echo hoge
 ---> Using cache
 ---> 1313f4ef40dd
Successfully built 1313f4ef40dd

となり、Using cache と出ていて、イメージのハッシュ値も一致していることが解ります。 このようにハッシュが使用されるルールは下記です。

  1. 親イメージから子に向かって処理は進められ、対象レイヤーの親イメージが持つ子イメージの中で、コマンドの内容が完全に一致しているものがあればそれが使用されます。(関係ないスペースが入ってもキャッシュは利用されなくなります)

  2. ADD, COPY 命令の場合は、ファイルの中身のチェックサムが一致するとキャッシュが利用されます。

なので、ADD, COPY命令の場合は、Dockerfile上の命令は変わっていなくても、ファイルの内容が更新された場合はキャッシュは使用されずに新しくイメージを作ってくれます。

CMD, ENTRYPOINT について

CMDとENTRYPOINTって解りにくいと思うので説明したいと思います。

2つの形式

まず書式としては2つあります

  1. shell方式 echo hoge
  2. exec方式 ["/bin/echo", "hoge"]

これらは同じ、hoge と出力するものです。 これらの方式とCMD, ENTYPOINTの挙動の違いが少し複雑です

CMDについて

イメージ対してゆるいデフォルトコマンドを指定する

  • run時に実行される
  • 上書き可能
ENTRYPOINTについて

イメージ起動時のコマンドを指定する

  • 同じようにRUN時に実行される
  • CMDと違って上書きはできない

ちょっとわかりづらくて説明もよく解らないので、実際の動きを見てみると

FROM alpine
RUN apk --no-cache add  git
CMD ["/bin/echo", "bbb"]
ENTRYPOINT ["/bin/echo", "aaa"]

を build して run すると、

aaa /bin/echo bbb

となります。 この挙動を見ると、ENTRYPOINTがあると、そっちのほうが優先され、CMDのexec形式で書いたものは引数として渡されているのが解ると思います

run時に ccc を引数として渡すと

aaa ccc

となり、CMDで指定したものが上書きされます。

次にENTYPOINTの書式をshell形式にして試してみます。

FROM alpine
RUN apk --no-cache add  git
CMD ["/bin/echo", "bbb"]
ENTRYPOINT echo aaa

これで run すると、引数有り無しにかかわらず、aaa が表示されるだけになり、CMDの内容も run の引数も影響がなくなってしまいます。 若干解りづらいですが、このあたりの挙動を理解しておくと、Dockerfileを書くときのストレスが減少できると思います。

UUUMのシステム開発ではDockerの恩恵をうけた快適な開発スタイルを実践しています^^

参考にさせて頂いたサイト