七天用 Go 写个 docker(第四天)
项目源码:点击查看项目源码
镜像
前面我们用
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个文件夹,
mntwriteLayerbusybox,我们将writeLayerbusybox这两个文件夹挂载到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
}
文章会首发于我微信公众号上,扫码关注,及时获取最新内容

本作品采用《CC 协议》,转载必须注明作者和本文链接
关于 LearnKu
推荐文章: