自己动手写Docker系列 -- 4.3实现volume数据卷
简介
在上篇中对容器和镜像实现了进一步的文件隔离,是容器内的修改不影响到宿主机。本篇中将实现docker中的volume,提供持久化存储能力
源码说明
同时放到了Gitee和Github上,都可进行获取
- Gitee: https://gitee.com/free-love/docker-demo
- GitHub: https://github.com/lw1243925457/dockerDemo
本章节对应的版本标签是:4.3,防止后面代码过多,不好查看,可切换到标签版本进行查看
代码实现
在上篇中,我们实现了容器和镜像的文件隔离,在容器内的修改不会影响到宿主机内
但我们也会有一些持久化的存储,在容器中操作后,想要保存下来,便于后序查看或者重启后进行加载,对应docker中的 -v 参数
这里的原理还是同上篇中的一样,也是使用文件挂载的方式,不同于上篇的是,这个-v的挂载只卸载卷,但不删除文件,这样文件就保留了下来
代码也不是太复杂,直接上代码,相比较书中的代码,稍微做了一些结构上的调整和优化
新增 -v 命令参数
我们在RunCommand中增加-v命令参数,和docker中的-v一样
需要注意的是,目前暂时单数据卷挂载,还不能像docker一样提供多个-v,但影响不大
var RunCommand = cli.Command{Name: "run",Usage: `Create a container with namespace and cgroups limit mydocker run -ti [command]`,Flags: []cli.Flag{......// 添加-v标签cli.StringFlag{Name: "v",Usage: "volume",},},/*这里是run命令执行的真正函数1.判断参数是否包含command2.获取用户指定的command3.调用Run function 去准备启动容器*/Action: func(context *cli.Context) error {......volume := context.String("v")run.Run(tty, cmdArray, resConfig, volume)return nil},
}
上面加参数传入了Run函数中,在Run函数中,我们将其继续传递到进程启动的初始化进程和退出时的清理函数中
func Run(tty bool, cmdArray []string, config *subsystem.ResourceConfig, volume string) {......mntUrl := pwd + "/mnt/"rootUrl := pwd + "/"// 传入初始化进程中parent, writePipe := container.NewParentProcess(tty, rootUrl, mntUrl, volume)if err := parent.Start(); err != nil {log.Error(err)// 如果fork进程出现异常,但有相关的文件已经进行了挂载,需要进行清理,避免后面运行报错时,需要手工清理deleteWorkSpace(rootUrl, mntUrl, volume)return}......log.Infof("parent process run")_ = parent.Wait()// 传入退出时的清理函数中deleteWorkSpace(rootUrl, mntUrl, volume)os.Exit(-1)
}
创建容器文件系统
在进程初始化函数中,会创建容器文件系统,和上篇文件中一样,我们只是在newWorkSpace函数中新增一个函数,来挂载持久化数据卷即可
回顾下,这个核心函数是功能大致如下:
1.创建只读层
2.创建容器读写层
3.创建挂载点并将只读层和读写层挂载到挂载点上
下面我们要增加的是:
4.在容器内创建对应的数据卷,并将其挂载到挂载点上
我们这步新增的需要在第三步后面,因为需要挂载点已经准备就绪
func newWorkSpace(rootUrl, mntUrl, volume string) error {if err := createReadOnlyLayer(rootUrl); err != nil {return err}if err := createWriteLayer(rootUrl); err != nil {return err}if err := createMountPoint(rootUrl, mntUrl); err != nil {return err}// 在容器内创建对应的数据卷,并将其挂载到挂载点上if err := mountExtractVolume(mntUrl, volume); err != nil {return err}return nil
}
挂载数据卷的具体处理如下:
1.如果参数有效才进行挂载操作:空直接返回;参数错误则报错
2.如果宿主机中的文件路径不存在,需要进行创建(书中是使用mkdir,这样如果多级目录时,上级目录没有时会报错,这里mkdirall递归创建)
3.在容器读写层创建对应到容器内的文件
4.将宿主机文件进行挂载
具体实现如下:
func mountVolume(mntUrl string, volumeUrls []string) error {// 如果宿主机文件目录不存在则创建parentUrl := volumeUrls[0]exist, err := pathExist(parentUrl)if err != nil && !os.IsNotExist(err) {return err}if !exist {// 使用mkdir all 递归创建文件夹if err := os.MkdirAll(parentUrl, 0777); err != nil {return fmt.Errorf("mkdir parent dir err: %v", err)}}// 在容器文件系统内创建挂载点containerUrl := mntUrl + volumeUrls[1]if err := os.Mkdir(containerUrl, 0777); err != nil {return fmt.Errorf("mkdir container volume err: %v", err)}// 把宿主机文件目录挂载到容器挂载点dirs := "dirs=" + parentUrlcmd := exec.Command("mount", "-t", "aufs", "-o", dirs, "none", containerUrl)cmd.Stdout = os.Stdoutcmd.Stderr = os.Stderrif err := cmd.Run(); err != nil {return fmt.Errorf("mount volume err: %v", err)}return nil
}func pathExist(path string) (bool, error) {_, err := os.Stat(path)if err == nil {return true, err}return false, err
}
容器退出时的清理
在上篇中清理动作是直接卸载了挂载点,并删除了读写层
我们这次的数据卷是需要持久化保存的,只需要进行将挂载点卸载即可
具体实现如下:
func deleteWorkSpace(rootUrl, mntUrl, volume string) {// 这里在删除挂载点之前,把数据卷卸载即可// 后面的删除挂载点和删除读写层后,不会影响宿主机的文件unmountVolume(mntUrl, volume)deleteMountPoint(mntUrl)deleteWriteLayer(rootUrl)
}
unmountVolume 具体实现如下:
func unmountVolume(mntUrl string, volume string) {if volume == "" {return}volumeUrls := strings.Split(volume, ":")if len(volumeUrls) != 2 || volumeUrls[0] == "" || volumeUrls[1] == "" {return}// 卸载容器内的 volume 挂载点的文件系统containerUrl := mntUrl + volumeUrls[1]cmd := exec.Command("umount", containerUrl)cmd.Stdout = os.Stdoutcmd.Stderr = os.Stderrif err := cmd.Run(); err != nil {log.Errorf("ummount volume failed: %v", err)}
}
运行测试
编译一波代码,在容器内创建一个文件
➜ dockerDemo git:(main) ✗ go build mydocker/main.go
➜ dockerDemo git:(main) ✗ ./main run -ti -v /root/volumn/test:/test sh
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"all command is : sh","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"parent process run","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"init come on","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"current location: /home/lw/code/go/dockerDemo/mnt","time":"2022-03-20T10:15:04+08:00"}
{"level":"info","msg":"find path: /bin/sh","time":"2022-03-20T10:15:04+08:00"}
/ # ls
bin dev etc home main proc root sys test tmp usr var
/ # touch /test/test.txt
/ # ls /test/
test.txt
我们新开一个sh看看宿主机的情况,可以看到文件在宿主机中也有
➜ ~ ls /root/volumn/test
test.txt
然后我们退出容器,可以看到当前运行目录下的文件已经被清理掉了
/ # exit
➜ dockerDemo git:(main) ✗ ll
总用量 4.6M
drwxr-xr-x 12 root root 4.0K 3月 17 06:17 busybox
drwxrwxr-x 2 lw lw 4.0K 3月 18 20:45 docs
drwxrwxr-x 3 lw lw 4.0K 3月 7 04:55 example
-rw-rw-r-- 1 lw lw 382 3月 12 10:18 go.mod
-rw-rw-r-- 1 lw lw 2.0K 3月 12 10:18 go.sum
-rw-rw-r-- 1 lw lw 12K 3月 12 10:18 LICENSE
-rwxr-xr-x 1 root root 4.6M 3月 20 10:15 main
drwxrwxr-x 6 lw lw 4.0K 3月 12 10:20 mydocker
-rw-rw-r-- 1 lw lw 473 3月 12 10:18 README.md
我们再次去查看宿主机的文件,发现依旧存在,目的达成
➜ ~ ls /root/volumn/test
test.txt
自己动手写Docker系列 -- 4.3实现volume数据卷相关推荐
- 自己动手写Docker系列 -- 5.7实现通过容器制作镜像
简介 在上篇中我们实现了rm命令,删除存在的容器,本篇中,将完善之前的文件系统隔离,实现容器与容器之间的文件系统隔离 源码说明 同时放到了Gitee和Github上,都可进行获取 Gitee: htt ...
- 自己动手写Docker系列 -- 6.3 手动配置容器网络(下)
简介 网络部分较为复杂,本篇先利用之前写好的基础容器和网桥部分,加上手工给容器配置网络,让其容器与外部网络部分功能正常,为后面程序编写打下基础 源码说明 同时放到了Gitee和Github上,都可进行 ...
- 【无标题】自己动手写Docker系列 -- 6.3 手动配置容器网络(上)
简介 网络部分较为复杂,本篇先利用之前写好的基础容器和网桥部分,加上手工给容器配置网络,让其容器与宿主机网络部分功能正常,为后面程序编写打下基础 源码说明 同时放到了Gitee和Github上,都可进 ...
- 自己动手写Docker系列 -- 3.1构造实现run命令版本的容器
简介 通过对前面Linux的Namespace.Cgroups.Union File System的学习,对Docker实现的基础知识有了一点点了解,接下来就跟着作者开始编写 源码说明 同时放到了Gi ...
- 【Docker系列 Swarm】 swarm volume 数据持久化
Docker Swarm volume 数据持久化 volume 是将宿主级的目录映射到容器中,以实现数据持久化. 可以用两种方式来实现: volume 默认模式:工作节点宿主机数据同步到容器内. v ...
- 自己动手写Docker系列 -- 4.1使用busybox创建容器
简介 目前docker demo中还是使用的系统原有proc,不怎么纯净,本篇中使用busybox来更换docker demo的系统挂载点 源码说明 同时放到了Gitee和Github上,都可进行获取 ...
- 自己动手写Docker系列 -- 5.4实现进入容器的namespace,exec命令
简介 在上篇中我们实现了将容器后台运行,本篇中我们将实现docker的ps命令,查看当前正在运行中的容器列表 源码说明 同时放到了Gitee和Github上,都可进行获取 Gitee: https:/ ...
- 自己动手写Docker系列 -- 5.2实现查看运行中的容器
简介 在上篇中我们实现了将容器后台运行,本篇中我们将实现docker的ps命令,查看当前正在运行中的容器列表 源码说明 同时放到了Gitee和Github上,都可进行获取 Gitee: https:/ ...
- 自己动手写Docker系列 -- 5.1实现容器的后台运行
简介 在前几篇中,我们已经构建了一个基础的镜像,本篇开始做一些进阶的功能,下面就是实现docker中的-d命令,让容器能够后台运行 源码说明 同时放到了Gitee和Github上,都可进行获取 Git ...
最新文章
- java linux路径 home_根据linux自带的JDK,配置JAVA_HOME目录
- Flask-hello程序
- 滴滴基于 Flink 的实时数仓建设实践
- Selenium + Grid + Testng并发运行用例
- Android开发之AlertDialog设置左右边距的间接办法
- java中你知道这四种代码块吗?
- JS滚动条位置,顶部,底部,触发事件
- 小程序 | 获取用户头像信息接口改进:getUserInfo的使用
- java中有cin格式吗,C中std :: cin对象的规则是什么?
- 高德地图上线武汉千家商超信息 可预约团购、查营业时间和电话
- 《Linux4.0设备驱动开发详解》笔记--第五章:Linux文件系统与设备文件
- DRDS 柔性事务漫谈
- 海康VisionMaster定位任务
- 我的世界服务器显示fps,我的世界提升fps的方法 低配玩家必备秘籍
- java的dataset怎么用,C# DataSet的基本用法
- 腾讯支持html5吗,WebQQ全面升级支持IE9 充分运用HTML5优势
- 等保测评机构推荐证书撤销,纳入国家认证体系
- 250部世界公认的经典大片
- 使用原汁原味的Java 语言
- Discuz对不起,您安装的不是正版应用的解决办法
热门文章
- 为bootstrap+angularJs打造的表格代码生成器
- 在运行时切换 WinForm 程序的界面语言 System.ComponentModel.ComponentResourceManager .ApplyResources...
- 关于IE的RegExp.exec
- 一位Erlang程序员的自白
- mysql 8创建远程访问用户以及连接mysql速度慢的解决方法
- win10系统更新补丁时进度条一直卡在0%不动的解决方案
- 【白皮书分享】2020年度薪酬白皮书.pdf(附下载链接)
- 组卷积(group convolution)
- AJAX框架衣柜推拉门设计,带镜子的推拉门衣柜如何设计好看
- 三维图像处理_【图像处理】用于三维物体检测的三维骨干网络