叢集映象中的overlay2使用剖析

語言: CN / TW / HK

sealer是阿里巴巴開源的基於kuberentes的叢集映象開源技術,可以把整個叢集整體打包。

sealer中叢集映象的儲存和docker映象很像,也採用了寫時複製的技術來提升複用性。

本文聊一下overlay2。

寫時複製

COPY on Write在很多地方都會用到,比如git的儲存,去修改一個檔案並非真的修改,

而是把檔案拷貝出來修改,讀的時候讀取上層的修改層,同樣比如在ceph的後段儲存

等系統中都存在應用。好處是可以非常方便的進行回滾,以及儲存複用,比如給虛擬機器

做快照,那我們並不需要把整個系統盤複製一份,而只需要用指標指一下快照的地方就好。

容器映象同理,寫時複製可以讓多個應用共享一個基礎映象。

叢集映象中的寫時複製

叢集映象把整個叢集打包,用寫時複製的方法可以幫助分散式應用共享k8s基礎映象。

以及可以很方便的把各種分散式應用映象進行融合。

作業系統中使用overlay2

mount -t overlay overlay -o lowerdir=./lower,upperdir=./upper,workdir=./work ./merged

overlay檔案系統分為lowerdir、upperdir、merged, 對外統一展示為merged,uperdir和lower的同名檔案會被upperdir覆蓋

workdir必須和upperdir是mount在同一個檔案系統下, 而lower不是必須的

mount -t overlay overlay -o lowerdir=/lower1:/lower2:/lower3,upperdir=/upper,workdir=/work /merged

lowerdir可以是多個目錄(前面覆蓋後面),如果沒有upperdir,那merged是read only.

overlay只支援兩層,upper檔案系統通常是可寫的;lower檔案系統則是隻讀,這就表示著,當我們對 overlay 檔案系統做任何的變更,都只會修改 upper 檔案系統中的檔案

來個實際操作:

overlay2
├── lower1
│   ├── a
│   └── b
├── lower2
│   └── a
├── merged
├── upper
│   └── c
└── work
mount -t overlay overlay -o lowerdir=lower1:lower2,upperdir=upper,workdir=work merged

以上overlay2下面的幾個目錄就被合併了

對只在 lower 有的檔案寫時,則會做一個copy_up 的操作,先從 lower將檔案拷貝一份到upper,同時為檔案建立一個硬連結。此時可以看到 upper 目錄下生成了兩個新檔案,寫的操作只對從lower 複製到 upper 的檔案生效,而 lower 還是原檔案

刪除 lower 和 upper 都有的檔案時,upper 的會被刪除,在 upper 目錄下建立一個 ‘without’ 檔案,而 lower 的不會被刪除

程式碼實現

有了上述理論基礎,我們先看一段docker裡面的原始碼

	if len(mountData) > pageSize {
		opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work"))
		mountData = label.FormatMountLabel(opts, mountLabel)
		if len(mountData) > pageSize {
			return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData))
		}

		mount = func(source string, target string, mType string, flags uintptr, label string) error {
			return mountFrom(d.home, source, target, mType, flags, label)
		}
		mountTarget = path.Join(id, "merged")
	}

	if err := mount("overlay", mountTarget, "overlay", 0, mountData); err != nil {
		return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
	}

mountTarget就是目標目錄, mountData是: fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", string(lowers), path.Join(id, "diff"), path.Join(id, "work"))

可以看到SDK的使用和命令列很像

我們可以做一個封裝:

package mount

import (
	"syscall"
)

func mount(device, target, mType string, flag uintptr, data string) error {
	if err := syscall.Mount(device, target, mType, flag, data); err != nil {
		return err
	}

	// If we have a bind mount or remount, remount...
	if flag&syscall.MS_BIND == syscall.MS_BIND && flag&syscall.MS_RDONLY == syscall.MS_RDONLY {
		return syscall.Mount(device, target, mType, flag|syscall.MS_REMOUNT, data)
	}
	return nil
}

func unmount(target string, flag int) error {
	return syscall.Unmount(target, flag)
}

主要是syscall.Mount這個系統呼叫,引數基本和命令列一一對應

sealer中overlay2原始碼地址 kubernetes一鍵安裝

sealer叢集整體打包!