鏡像
前面我們用
namespace
和cgroup
構建了一個簡單的容器,但是我們可以發現容器內的目錄還是當前運行程序的目錄,這裏就缺少了鏡像這麼一個重要的特性。這裏我們先用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
文件夾下,可以看到這時mnt
和writeLayer
文件夾都是空的,只有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
}