项目源码:点击查看项目源码

镜像

前面我们用 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 layercontainer-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
}

转自:微信号 跟派大星学编程