Dockerイメージのサイズを計算するためのソースコードを見つける


8

この数は、画像内に追加されるレイヤーのすべてのサイズと同じではないと聞きました。また、それが占有するディスク領域のサイズでもありません。

ここで、ソースコード(このリポジトリのhttps://github.com/docker/docker-ce)でロジックを確認したいと思います。しかし、コードを何度もナビゲートした後、実際のimag-sizeコンピューティングコードを見つけることができなかったことがわかりました。

では、Dockerはサイズロジックの実行にどの関数/ファイルを使用していますか?

回答:


13

深く掘り下げる前に、Linuxがオーバーレイファイルシステムをどのように実装するかを理解しておくと役立つ場合があります。イントロプレゼンテーションのビルドセクションの最初の演習について少し説明します。デモノートは、私が実行していることだし、それはあなたの層がマージされているかのアイデアを提供し、あなたが/変更/層からの削除追加したときに何が起こるの各コマンドが含まれています。


これは、使用しているホストOSとグラフドライバに基づいて、実装に依存します。Linux OSとOverlay2の例を取り上げます。それが最も一般的な使用例だからです。

まず、画像レイヤーのストレージサイズを確認します

// GetContainerLayerSize returns the real size & virtual size of the container.
func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) {
    var (
        sizeRw, sizeRootfs int64
        err                error
    )

    // Safe to index by runtime.GOOS as Unix hosts don't support multiple
    // container operating systems.
    rwlayer, err := i.layerStores[runtime.GOOS].GetRWLayer(containerID)
    if err != nil {
        logrus.Errorf("Failed to compute size of container rootfs %v: %v", containerID, err)
        return sizeRw, sizeRootfs
    }
    defer i.layerStores[runtime.GOOS].ReleaseRWLayer(rwlayer)

    sizeRw, err = rwlayer.Size()
    if err != nil {
        logrus.Errorf("Driver %s couldn't return diff size of container %s: %s",
            i.layerStores[runtime.GOOS].DriverName(), containerID, err)
        // FIXME: GetSize should return an error. Not changing it now in case
        // there is a side-effect.
        sizeRw = -1
    }

    if parent := rwlayer.Parent(); parent != nil {
        sizeRootfs, err = parent.Size()
        if err != nil {
            sizeRootfs = -1
        } else if sizeRw != -1 {
            sizeRootfs += sizeRw
        }
    }
    return sizeRw, sizeRootfs
}

layerStoresそれ自体がlayer.Storeへのマッピングである呼び出しがあります:

// ImageServiceConfig is the configuration used to create a new ImageService
type ImageServiceConfig struct {
    ContainerStore            containerStore
    DistributionMetadataStore metadata.Store
    EventsService             *daemonevents.Events
    ImageStore                image.Store
    LayerStores               map[string]layer.Store
    MaxConcurrentDownloads    int
    MaxConcurrentUploads      int
    MaxDownloadAttempts       int
    ReferenceStore            dockerreference.Store
    RegistryService           registry.Service
    TrustKey                  libtrust.PrivateKey
}

layer.Store実装について詳しく説明するGetRWLayerと、次の定義があります

func (ls *layerStore) GetRWLayer(id string) (RWLayer, error) {
    ls.locker.Lock(id)
    defer ls.locker.Unlock(id)

    ls.mountL.Lock()
    mount := ls.mounts[id]
    ls.mountL.Unlock()
    if mount == nil {
        return nil, ErrMountDoesNotExist
    }

    return mount.getReference(), nil
}

その後Size、マウント参照の実装を見つけるために、特定のグラフドライバーに入るこの関数があります。

func (ml *mountedLayer) Size() (int64, error) {
    return ml.layerStore.driver.DiffSize(ml.mountID, ml.cacheParent())
}

overlay2グラフドライバーを見て、DiffSize関数を見つけます

func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
    if useNaiveDiff(d.home) || !d.isParent(id, parent) {
        return d.naiveDiff.DiffSize(id, parent)
    }
    return directory.Size(context.TODO(), d.getDiffPath(id))
}

それは、graphDriverパッケージでSizenaiveDiff実装する呼び出しです:

func (gdw *NaiveDiffDriver) DiffSize(id, parent string) (size int64, err error) {
    driver := gdw.ProtoDriver

    changes, err := gdw.Changes(id, parent)
    if err != nil {
        return
    }

    layerFs, err := driver.Get(id, "")
    if err != nil {
        return
    }
    defer driver.Put(id)

    return archive.ChangesSize(layerFs.Path(), changes), nil
}

次に、archive.ChangeSizeこの実装を示します

// ChangesSize calculates the size in bytes of the provided changes, based on newDir.
func ChangesSize(newDir string, changes []Change) int64 {
    var (
        size int64
        sf   = make(map[uint64]struct{})
    )
    for _, change := range changes {
        if change.Kind == ChangeModify || change.Kind == ChangeAdd {
            file := filepath.Join(newDir, change.Path)
            fileInfo, err := os.Lstat(file)
            if err != nil {
                logrus.Errorf("Can not stat %q: %s", file, err)
                continue
            }

            if fileInfo != nil && !fileInfo.IsDir() {
                if hasHardlinks(fileInfo) {
                    inode := getIno(fileInfo)
                    if _, ok := sf[inode]; !ok {
                        size += fileInfo.Size()
                        sf[inode] = struct{}{}
                    }
                } else {
                    size += fileInfo.Size()
                }
            }
        }
    }
    return size
}

その時点で、各ディレクトリへの追加または変更である各エントリをos.Lstat含む構造体を返すために使用していますSize。これはコードが取るいくつかの可能なパスの1つですが、このシナリオではより一般的なパスの1つであると思います。


2
非常に有益です。賛成。さらに、player.fm / series / devops-and-docker-talk /…を聞いたことがあるので、できる限りサポートを提供します。
VonC

0
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.