4.构造镜像
4.1 使用busybox基本镜像构造容器
docker export -o busybox.tar c5a45bda498e(容器id)
可以起一个busybox的后台运行容器,通过export导出文件系统,查看下结构pivot_root
系统调用,用于改变root文件系统
int pivot_root(const char *new_root, const char *put_old);
会将原先的root文件系统move到put_old,然后以new_root作为调用进程的新的root文件系统pivot_root
和chroot
的区别:
pivot_root
把进程切换到一个新的root目录,对之前root文件系统的不再有依赖,这样你就能够umount原先的root文件系统。chroot
只是更改了root目录,还会依赖老的文件系统,主要使用的目的一般是为了限制用户的访问。pivot_root
相关代码:
func pivotRoot(root string) error { //这个root目录在之前通过cmd.Dir='/path/busybox'设置好了 // new_root 和put_old 必须不能同时存在当前root 的同一个文件系统中,需要通过--bind重新挂载一下 if err := syscall.Mount(root, root, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { return fmt.Errorf("mount --bind PWD PWD error ") } pivotDir := filepath.Join(root, old_root) if err := os.Mkdir(pivotDir, 0777); err != nil { return err } if err := syscall.PivotRoot(root, pivotDir); err != nil { return fmt.Errorf("privot_root error %v", err) } log.Infof("now change dir to root") if err := syscall.Chdir("/"); err != nil { return fmt.Errorf("chdir / %v", err) } // 更新下文件路径 pivotDir = filepath.Join("/", old_root) if err := syscall.Unmount(pivotDir, syscall.MNT_DETACH); err != nil { return fmt.Errorf("umount pivot_root dir %v", err) } return os.Remove(pivotDir)}复制代码
4.2 使用AUFS包装busybox
- 上一步构建好之后其实所有的操作都会影响busybox目录,需要将镜像和容器做隔离
- 大致的思路是在进入init进程进行pivotRoot之前,需要加上以下操作
- 启动容器时,将两个目录AUFS挂载到(ex:path/mnt),一个是read-wirte一个是read-only(一般是镜像文件,ex:busybox的文件系统)
- 退出时卸载并且删除read-write文件系统
- 流程如下
比较关键的就一步挂载AUFS
func CreateMountPoint(rootUrl string, mntUrl string) { if err := os.Mkdir(mntUrl, 0777); err != nil { log.Errorf("Mkdir dir %s error. %v", mntUrl, err) } dirs := "dirs=" + rootUrl + "writeLayer:" + rootUrl + "busybox" cmd := exec.Command("mount", "-t", "aufs", "-o", dirs, "none", mntUrl) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Errorf("%v", err) }}复制代码
4.3实现volumn数据卷
就是实现
docker run -v xxx:yyy
,支持挂载外部文件到容器中去
- 相较于4.2的实现,在启动容器时在mnt挂载完成后多了一步挂载volumn
func MountVolume(rootURL string, mntURL string, volumeURLs []string) { parentUrl := volumeURLs[0] if err := os.Mkdir(parentUrl, 0777); err != nil { log.Infof("Mkdir parent dir %s error. %v", parentUrl, err) } containerUrl := volumeURLs[1] containerVolumeURL := mntURL + containerUrl if err := os.Mkdir(containerVolumeURL, 0777); err != nil { log.Infof("Mkdir container dir %s error. %v", containerVolumeURL, err) } dirs := "dirs=" + parentUrl cmd := exec.Command("mount", "-t", "aufs", "-o", dirs, "none", containerVolumeURL) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Errorf("Mount volume failed. %v", err) }}复制代码
- 在退出时加一步umount操作,和挂载顺序相反。
- 大致流程
4.4 实现简单镜像打包(略)
实现类似
docker commit xxx
的功能
- 比较简单就是在容器运行时 tar 压缩下 path/mnt的文件