薄いブログ

技術の雑多なことを書く場所

なぜ cache-from を指定しなければいけないか

気になるツイートを見かけ, 僕も理由がわからなかったので調査してみました.

TL;DR

  • build 時に cache-from をつけないと dockerd がメモリ上に保持している情報からしか子イメージを取得しない
  • build では子イメージの情報をメモリ上に登録する
  • pull は子イメージの情報をメモリ上に登録しない
  • なので pull してきたイメージは cache-from をつけないとキャッシュ対象にならない

わかっていること

  • docker buildしたイメージは cache-from をつけなくても cache が効く
  • docker pullしたイメージは cache-from をつけないと cache が効かない
  • イメージが等しい条件

orisano.hatenablog.com

  • build の際にイメージが cache が使われる条件
    • 共通の親イメージを持ち, イメージが等しいなかで最も新しいもの

orisano.hatenablog.com

  • イメージが親イメージの情報のみを持つ連結リストの構造であること
    • すべてのイメージは親をたどるとscratchという特殊なイメージにたどり着くこと
    • dockerdが子イメージに伸びる辺をメモリ上に保持している

speakerdeck.com

未知のこと

  • build したイメージと pull したイメージの差分
  • cache-from した場合としなかった場合の差

ソースコードを読む

moby/moby 2019/12/3時点でのHEAD (3152f9436292115c97b4d8bb18c66cf97876ee75) を参照する. https://github.com/moby/moby/tree/3152f9436292115c97b4d8bb18c66cf97876ee75

docker/cli 2019/12/3時点でのHEAD (54d085b857e954a8fc431a9a400f11bc3308efc1) を参照する. https://github.com/docker/cli/tree/54d085b857e954a8fc431a9a400f11bc3308efc1

imageStoreについて

  • NewDaemon (docker/daemon/daemon.go) で imageService が作られる.
    • NewFSStoreBackend で FSStoreBackend を $root/image/$graphDriver/imagedb に作る
    • NewImageStore の中で restore を呼び fs にある情報から子イメージの情報を構築する
      • 親から子に辺を張る(メモリ上)

pullの実装について知る 実装

  • cliで認証, 検証, タグの解決を行う
  • 認証情報, 解決されたタグの情報を /images/create に POST する
  • postImagesCreate(import,pull共通)の処理が実行される (docker/api/server/router/image/image_routes.go)
  • ImageService.PullImage の処理が実行される (docker/daemon/images/image_pull.go)
  • ImageService.pullImageWithReference の処理が実行される (docker/daemon/images/image_pull.go)
    • config.ImageStore は distribution.NewImageConfigStoreFromStore(ImageService.imageStore)
    • config.ReferenceStore は ImageService.referenceStore
    • config.DownloadManager は ImageService.downloadManager
  • distribution.Pull が実行される (docker/distribution/pull.go)
  • newPuller によって作られた v2Puller の Pull が実行される (docker/distribution/pull_v2.go)
  • v2Puller の pullV2Repository が実行される (docker/distribution/pull_v2.go)
  • pullV2Tag が実行される (docker/distribution/pull_v2.go)
    • マニフェストを取得する
    • pullSchema2 が実行される (docker/distribution/pull_v2.go)
      • pullSchema2Layers が実行される (docker/distribution/pull_v2.go)
      • config.ImageStore に digest が存在するか確認してある場合はそちらを使う
      • pullSchema2Config で Config を取得する (docker/distribution/pull_v2.go)
      • config.DownloadManager がある場合は RootFS を Download メソッドによってダウンロードする
      • Config と Download した RootFS が等しいか検証する
      • config.ImageStore に Put する
        • ImageStore って名前だが ImageConfigStore
        • Put すると image.Store に対して Create を実行する
        • Create は子イメージの情報がない状態で追加する (docker/image/store.go)
    • config.ReferenceStore がある場合は, tag の内容が更新されたかを確認して反映する

buildの復習

  • 基本的な流れは here
  • build 時に imageStore を触るタイミングはイメージを commit するとき
    1. dispatch (docker/builder/dockerfile/evaluator.go)
    2. dispatchXXX (docker/builder/dockerfile/dispatcher.go)
    3. commit (docker/builder/internals.go)
    4. commitContainer (docker/builder/internals.go)
    5. CommitBuildStep (docker/daemon/images/image_commit.go)
    6. CommitImage (docker/daemon/images/image_commit.go)
  • container の id から取得した read write の layer の tar を読み込む
  • layerStore に登録する
  • 親イメージIDを取得する
  • 今の状態を image.Store で Create する
  • SetParent で parent という metadata ファイルに書く
  • 親がいる場合は更新する, 元の親の children から自分の id を削除する
  • 親の children に自分の id を登録する

cache-fromの挙動

  • MakeImageCache (docker/daemon/images/cache.go) で builder.ImageCache が作られる
  • cache-from が指定されていない場合は getLocalCachedImage を使う (docker/image/cache/cache.go)
    • メモリ上の children だけを参照して,キャッシュの対象を見つける
  • cache-from が指定されている場合は ImageCache.GetCache が呼ばれる (docker/image/cache/cache.go)
    • メモリ上の children だけを参照して,キャッシュの対象を見つける
      • キャッシュの対象が cache-from で指定したイメージの親イメージだった場合のみ使用される
    • 親イメージを Store から取得する
    • 親イメージと cache-from で指定したイメージで history が前方一致, RootFS の DiffID が前方一致しているイメージがキャッシュ対象になる

結論

  • cache-from をつけない build はメモリ上で保持している children しか参照しない
  • pull したイメージは親イメージ群に対して SetParent しないのでメモリ上で保持している children からは参照できない
  • よって pull したイメージは cache-from をつけないと build 時のキャッシュ対象にならない

なんでこんな仕様になっているんでしょうか, 詳しい人教えて下さい