Trước khi đào quá sâu, bạn có thể thấy hữu ích để hiểu cách Linux triển khai hệ thống tệp lớp phủ. Tôi bao gồm một chút về điều này bài tập đầu tiên của phần xây dựng phần giới thiệu của tôi . Các ghi chú demo bao gồm từng lệnh tôi đang chạy và nó cho bạn ý tưởng về cách các lớp được hợp nhất và điều gì xảy ra khi bạn thêm / sửa đổi / xóa từ một lớp.
Điều này phụ thuộc vào việc triển khai, dựa trên hệ điều hành máy chủ của bạn và trình điều khiển biểu đồ đang được sử dụng. Tôi lấy ví dụ về HĐH Linux và Overlay2 vì đó là trường hợp sử dụng phổ biến nhất.
Nó bắt đầu bằng cách nhìn vào kích thước lưu trữ của lớp hình ảnh :
// 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
}
Trong đó có một cuộc gọi layerStores
mà chính nó là ánh xạ tới 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
}
Đi sâu vào layer.Store
thực hiện GetRWLayer
, có định nghĩa sau :
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
}
Theo đó để tìm cách Size
triển khai cho tham chiếu gắn kết, có chức năng này được đưa vào trình điều khiển biểu đồ cụ thể:
func (ml *mountedLayer) Size() (int64, error) {
return ml.layerStore.driver.DiffSize(ml.mountID, ml.cacheParent())
}
Nhìn vào trình điều khiển biểu đồ lớp phủ2 để tìm hàm 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))
}
Đó là cách gọi naiveDiff
mà thực hiện Kích thước trong gói 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
}
Sau đây archive.ChangeSize
chúng ta có thể thấy việc thực hiện này :
// 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
}
Tại điểm chúng ta đang sử dụng os.Lstat
để trả về một cấu trúc bao gồm Size
trên mỗi mục nhập là thêm hoặc sửa đổi cho mỗi thư mục. Lưu ý rằng đây là một trong một số đường dẫn có thể có của mã, nhưng tôi tin rằng đó là một trong những đường dẫn phổ biến hơn cho kịch bản này.