Bevor Sie zu tief graben, ist es möglicherweise hilfreich zu verstehen, wie Linux das Overlay-Dateisystem implementiert. Ich füge ein wenig dazu die erste Übung des Build-Abschnitts meiner Intro-Präsentation hinzu . Die Demo-Notizen enthalten alle Befehle, die ich ausführe, und geben Ihnen eine Vorstellung davon, wie Ebenen zusammengeführt werden und was passiert, wenn Sie eine Ebene hinzufügen / ändern / löschen.
Dies hängt von der Implementierung ab, basierend auf Ihrem Host-Betriebssystem und dem verwendeten Grafiktreiber. Ich nehme das Beispiel eines Linux-Betriebssystems und von Overlay2, da dies der häufigste Anwendungsfall ist.
Zunächst wird die Speichergröße der Bildebene betrachtet :
// 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
}
In gibt es einen Aufruf, layerStores
dem selbst eine Zuordnung zur Ebene zugeordnet ist .
// 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
}
Bei der layer.Store
Implementierung für GetRWLayer
gibt es die folgende Definition :
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
}
Um die Size
Implementierung für die Mount-Referenz zu finden, gibt es diese Funktion , die in den spezifischen Grafiktreiber gelangt:
func (ml *mountedLayer) Size() (int64, error) {
return ml.layerStore.driver.DiffSize(ml.mountID, ml.cacheParent())
}
Schauen Sie sich den Grafiktreiber overlay2 an, um die DiffSize-Funktion zu finden :
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))
}
Das ist ein Aufruf, naiveDiff
der Size im graphDriver-Paket implementiert :
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
}
Im Folgenden sehen archive.ChangeSize
wir diese Implementierung :
// 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
}
An diesem Punkt geben wir os.Lstat
eine Struktur zurück, die Size
in jedem Eintrag ein Hinzufügen oder Ändern zu jedem Verzeichnis enthält. Beachten Sie, dass dies einer von mehreren möglichen Pfaden ist, die der Code einschlägt, aber ich glaube, dass dies einer der häufigsten für dieses Szenario ist.