七天用Go寫個docker(第四天)

鏡像

前面我們用namespacecgroup構建了一個簡單的容器,但是我們可以發現容器內的目錄還是當前運行程序的目錄,這裏就缺少了鏡像這麼一個重要的特性。這裏我們先用docker拉一個最精簡的鏡像busybox,它是一個集合了非常多unix工具的箱子,提供了一個非常完整而且小巧的系統。

# 拉取busybox
docker pull busybox
# 運行
docker run -d busybox top -b

# 查看容器ID
docker ps

# 導出容器
docker export -o busybox.tar 


可以看到我們成功導出了容器內容,下面我解壓縮一下,然後將此文件夾作爲容器的只讀層。

mkdir busybox && tar -xvf busybox.tar -C busybox/

注意: 後面爲了方便,我們把 busybox.tar 放到 /root 路徑下

docker在啓動容器時,會新建2個layer,write layer和container-layer,write layer 是容器的唯一可讀寫層,而container-layer是爲容器新建的只讀層,這裏我們將busybox作爲這一層,讀寫層,我們創建一個writeLayer文件夾作爲這一層,然後將這兩個文件夾掛載到同一個文件夾下,然後將此文件夾作爲容器的啓動目錄即可,這個文件夾我們命名爲mnt

Linux掛載文件夾

這裏我們有3個文件夾,mnt writeLayer busybox,我們將writeLayer busybox這兩個文件夾掛載到 mnt文件夾下,可以看到這時 mntwriteLayer文件夾都是空的,只有busybox文件夾下有東西,我們掛載之後再看一下

# 掛載
 mount -t aufs -o dirs=/root/writeLayer:/root/busybox none /root/mnt

可以看到,mnt已經出現了 busybox 文件夾裏面的東西,這時我們在 writeLayer 文件夾下創建個新的文件夾,然後看看會發生什麼

可以清楚的看到,我們對writeLayer做的操作會映射到mnt文件夾下,docker也是以這種方式來實現你每次修改容器內的東西之後並不會對鏡像產生影響,因爲你做的操作都是writeLayer層的,而鏡像是在 busybox,也就是container-init層。

Go實現掛載

func NewWorkSpace(rootPath string, mntPath string, volume string) error {
	// 1. 創建只讀層
	err := createReadOnlyLayer(rootPath)
	if err != nil {
		logrus.Errorf("create read only layer, err: %v", err)
		return err
	}
	// 2. 創建讀寫層
	err = createWriteLayer(rootPath)
	if err != nil {
		logrus.Errorf("create write layer, err: %v", err)
		return err
	}
	// 3. 創建掛載點,將只讀層和讀寫層掛載到指定位置
	err = CreateMountPoint(rootPath, mntPath)
	if err != nil {
		logrus.Errorf("create mount point, err: %v", err)
		return err
	}
	return nil
}

簡單來講就三步,第一步創建只讀層,第二步創建讀寫層,第三步將兩者掛載到同一個文件夾下,具體實現也比較簡單,就是創建文件夾罷了,這裏重點看下怎麼掛載吧

func CreateMountPoint(rootPath string, mntPath string) error {
	_, err := os.Stat(mntPath)
	if err != nil && os.IsNotExist(err) {
		err := os.MkdirAll(mntPath, os.ModePerm)
		if err != nil {
			logrus.Errorf("mkdir mnt path, err: %v", err)
			return err
		}
	}

	dirs := fmt.Sprintf("dirs=%s%s:%s%s", rootPath, common.WriteLayer, rootPath, common.BusyBox)
	cmd := exec.Command("mount", "-t", "aufs", "-o", dirs, "none", mntPath)
	if err := cmd.Run(); err != nil {
		logrus.Errorf("mnt cmd run, err: %v", err)
		return err
	}
	return nil
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章