Encuentre el código fuente para calcular el tamaño de una imagen acoplable

8

He oído que el número no es igual a todos los tamaños de capas que se suman dentro de una imagen. Y tampoco es el tamaño del espacio en disco que ocupa.

Ahora quiero verificar la lógica por código fuente (en este repositorio: https://github.com/docker/docker-ce ), ¡porque ver es creer! Pero después de navegar por el código durante mucho tiempo, descubrí que no podía encontrar el código informático real de tamaño imag.

Entonces, ¿qué función / archivo se usa la ventana acoplable para realizar la lógica de tamaño?

Steve
fuente

Respuestas:

13

Antes de profundizar demasiado, puede resultarle útil comprender cómo Linux implementa el sistema de archivos superpuesto. Incluyo un poco en esto el primer ejercicio de la sección de compilación de mi presentación de introducción . Las notas de demostración incluyen cada uno de los comandos que estoy ejecutando y le da una idea de cómo se fusionan las capas y qué sucede cuando agrega / modifica / elimina de una capa.


Esto depende de la implementación, según el sistema operativo host y el controlador de gráficos que se esté utilizando. Estoy tomando el ejemplo de un sistema operativo Linux y Overlay2 ya que ese es el caso de uso más común.

Comienza mirando el tamaño de almacenamiento de la capa de imagen :

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

En hay una llamada a la layerStoresque en sí es una asignación a la capa .

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

Profundizando en la layer.Storeimplementación GetRWLayer, existe la siguiente definición :

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
}

Después de eso para encontrar la Sizeimplementación de la referencia de montaje, existe esta función que se introduce en el controlador de gráfico específico:

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

Mirando el controlador de gráfico overlay2 para encontrar la función 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))
}

Es una llamada naiveDiffque implementa Size en el paquete 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 archive.ChangeSizecontinuación podemos ver esta implementación :

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

En ese momento estamos utilizando os.Lstatpara devolver una estructura que incluye Sizeen cada entrada que se agrega o modifica a cada directorio. Tenga en cuenta que esta es una de las varias rutas posibles que toma el código, pero creo que es una de las más comunes para este escenario.

BMitch
fuente
2
Muy instructivo Votado Además, he escuchado player.fm/series/devops-and-docker-talk/… , así que ... ¡trato de ayudar siempre que puedo!
VonC
0

Puedes obtener talla en

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

este código de función está aquí https://github.com/docker/docker-ce/blob/5a0987be93654b685927c2e5c2d18ac01022d20c/components/cli/cli/command/image/list.go

y obtiene el tamaño de este código https://github.com/docker/docker-ce/blob/524986b1d978e1613bdc7b0448ba2cd16b3988b6/components/cli/cli/command/formatter/image.go

y finalmente necesitas https://github.com/docker/docker-ce/blob/531930f3294c31db414f17f80fa8650d4ae66644/components/engine/daemon/images/images.go

Ryabchenko Alexander
fuente