Encontre o código-fonte para calcular o tamanho de uma imagem do docker

8

Ouvi dizer que o número não é igual a todos os tamanhos de camadas que se juntam dentro de uma imagem. E também não é o tamanho do espaço em disco que ocupa.

Agora eu quero verificar a lógica pelo código fonte (neste repositório: https://github.com/docker/docker-ce ), porque ver é acreditar! Mas, depois de navegar o código por muito tempo, descobri que não conseguia encontrar o código de computação de tamanho de imagem real.

Então, qual função / arquivo a janela de encaixe é usada para executar a lógica do tamanho?

Steve
fonte

Respostas:

13

Antes de ir muito fundo, talvez seja útil entender como o Linux implementa o sistema de arquivos de sobreposição. Incluo um pouco sobre isso o primeiro exercício da seção de compilação da minha apresentação de introdução . As notas de demonstração incluem cada um dos comandos que estou executando e dão uma idéia de como as camadas são mescladas e o que acontece quando você adiciona / modifica / exclui de uma camada.


Isso depende da implementação, com base no sistema operacional host e no driver gráfico sendo usado. Estou usando o exemplo de sistema operacional Linux e Overlay2, pois esse é o caso de uso mais comum.

Começa observando o tamanho de armazenamento da camada de imagem :

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

Existe uma chamada na layerStoresqual é um mapeamento para a camada .

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

Indo para a layer.Storeimplementação de GetRWLayer, há a seguinte definição :

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
}

Depois disso, para encontrar a Sizeimplementação da referência de montagem, existe esta função que entra no driver gráfico específico:

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

Olhando para o driver gráfico overlay2 para encontrar a função 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))
}

Isso é chamado, naiveDiffque implementa Size no pacote 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
}

A seguir archive.ChangeSize, podemos ver esta implementação :

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

Nesse ponto, estamos usando os.Lstatpara retornar uma estrutura que inclui Sizeem cada entrada uma adição ou modificação em cada diretório. Observe que esse é um dos vários caminhos possíveis que o código segue, mas acredito que seja um dos mais comuns para esse cenário.

BMitch
fonte
2
Muito instrutivo. Votado. Além disso, ouvi o player.fm/series/devops-and-docker-talk/… , então ... tento ajudar sempre que posso!
VonC 26/04
0

Você pode obter tamanho em

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

este código de função está aqui https://github.com/docker/docker-ce/blob/5a0987be93654b685927c2e5c2d18ac01022d20c/components/cli/cli/command/image/list.go

e obtém o tamanho desse código https://github.com/docker/docker-ce/blob/524986b1d978e1613bdc7b0448ba2cd16b3988b6/components/cli/cli/command/formatter/image.go

e, finalmente, você precisa https://github.com/docker/docker-ce/blob/531930f3294c31db414f17f80fa8650d4ae66644/components/engine/daemon/images/images.go

Ryabchenko Alexander
fonte