Найти исходный код для вычисления размера docker изображения - PullRequest
8 голосов
/ 23 апреля 2020

Я слышал, что число не равно всем размерам слоев, складывающихся вместе внутри изображения. И это также не размер дискового пространства, которое это занимает.

Теперь я хочу проверить логи c по исходному коду (в этом репо: https://github.com/docker/docker-ce), потому что видение - это вера! Но после долгих перемещений по коду я обнаружил, что не могу найти настоящий вычислительный код размера изображения.

Итак, какая функция / файл docker используется для выполнения logi c размера?

Ответы [ 2 ]

13 голосов
/ 26 апреля 2020

Прежде чем копать слишком глубоко, может оказаться полезным понять, как Linux реализует оверлейную файловую систему. Я включил немного в это первое упражнение в разделе моего построения презентации . Демонстрационные заметки включают в себя все команды, которые я выполняю, и они дают вам представление о том, как объединяются слои, и что происходит при добавлении / изменении / удалении из слоя.


Это зависит от реализации, в зависимости от используемой операционной системы и используемого драйвера графа. Я беру пример Linux ОС и 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, который сам по себе является отображением на слой. Магазин :

// 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 для ссылки на монтирование, есть эта функция , которая попадает в спецификацию c драйвер графа:

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))
}

То, что вызывает naiveDiff which реализует Size в пакете graphDriver :

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 для каждой записи, которая является добавлением или изменением для каждого каталога. Обратите внимание, что это один из нескольких возможных путей, по которым идет код, но я считаю, что это один из наиболее распространенных путей для этого сценария.

0 голосов
/ 26 апреля 2020

Вы можете получить размер в

$ docker image ls
REPOSITORY  TAG                 IMAGE ID            CREATED             SIZE
nginx       1.12-alpine         24ed1c575f81        2 years ago        15.5MB

этот код функции здесь https://github.com/docker/docker-ce/blob/5a0987be93654b685927c2e5c2d18ac01022d20c/components/cli/cli/command/image/list.go

и получает размер из этого кода https://github.com/docker/docker-ce/blob/524986b1d978e1613bdc7b0448ba2cd16b3988b6/components/cli/cli/command/formatter/image.go

и наконец вам нужно https://github.com/docker/docker-ce/blob/531930f3294c31db414f17f80fa8650d4ae66644/components/engine/daemon/images/images.go

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...