自己动手写Docker系列 -- 5.7实现通过容器制作镜像

本文介绍了如何在Docker中实现容器间的文件系统隔离,通过在可写层和挂载层增加以容器名为名的目录来达到隔离效果。文章详细阐述了代码实现过程,包括在容器启动时创建隔离目录和修改`commit`命令以打包特定容器的可写层。读者可以参考Gitee和GitHub上的源码进行学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

在上篇中我们实现了rm命令,删除存在的容器,本篇中,将完善之前的文件系统隔离,实现容器与容器之间的文件系统隔离

源码说明

同时放到了Gitee和Github上,都可进行获取

本章节对应的版本标签是:5.7,防止后面代码过多,不好查看,可切换到标签版本进行查看

代码实现

实现该功能的主要思路如下:

在以前的文章:自己动手写Docker系列 – 4.2使用AUFS包装busybox

实现了容器文件系统与宿主机文件的隔离,但目前为止,所有的容器都是使用的同一个目录,容器与容器之间存在相互影响

本篇文章目的就是为了消除这边影响,实现容器与容器之间的文件系统也进行隔离

实现思路:

以前的文件系统如下:

  • 只读层:busybox系统,这个只能读,是系统的基础
  • 可写层:writerLayer,这个是容器内部的可写层,能进行对应的修改
  • 挂载层:mnt,挂载外部的文件系统,类似于虚拟机的文件共享

要实现容器间的文件系统隔离,就是在可写层和挂载层再加一层,以容器名称进行隔离,也就是:

  • 只读层:不变
  • 可写层:再加容器名为名的目录,进行隔离,也就是 writeLayer/{容器名称}
  • 挂载层:再加容器名为名的目录,进行隔离,也就是 mnt/{容器名称}

文件系统进行隔离后,我们commit的时候,对应的对容器可写层进程打包即可

根据思路,代码实现也比较简单,自己理清思路后,很快便能进行改造实现

修改容器启动时可写层和挂载层,以容器名进行隔离

在容器启动的时候,获取当前的容器名,用于构建相关的隔离目录

func Run(tty, detach bool, cmdArray []string, config *subsystem.ResourceConfig, volume, containerName string) {
   
   
	// 容器容器名
	id, containerName := getContainerName(containerName)

	pwd, err := os.Getwd()
	if err != nil {
   
   
		log.Errorf("Run get pwd err: %v", err)
		return
	}
	mntUrl := pwd + "/mnt/"
	rootUrl := pwd + "/"
	// 传入初始化进程,初始化工作空间
	parent, writePipe := container.NewParentProcess(tty, containerName, rootUrl, mntUrl, volume)
	if err := parent.Start(); err != nil {
   
   
		log.Error(err)
		// 如果fork进程出现异常,但有相关的文件已经进行了挂载,需要进行清理,避免后面运行报错时,需要手工清理
		// 删除容器工作空间进行改造
		deleteWorkSpace(rootUrl, mntUrl, volume, containerName)
		return
	}

    // 记录容器信息进行改造
    containerName, err = recordContainerInfo(parent.Process.Pid, cmdArray, id, containerName)
    if err != nil {
   
   
       log.Errorf("record contariner info err: %v", err)
       return
	}
	
	......

	log.Infof("parent process run")
	if !detach {
   
   
		_ = parent.Wait()
		// 删除容器工作空间进行改造
		deleteWorkSpace(rootUrl, mntUrl, volume, containerName)
		// 删除容器信息进行改造
		deleteContainerInfo(containerName)
	}
	os.Exit(-1)
}

获取容器名

func getContainerName(containerName string) (string, string) {
   
   
	id := randStringBytes(10)
	if containerName == "" {
   
   
		containerName = id
	}
	return id, containerName
}

生成容器信息

func recordContainerInfo(pid int, cmdArray []string, id, containerName string) (string, error) {
   
   
	createTime := time.Now().Format("2000-01-01 00:00:00")
	command := strings.Join(cmdArray, " ")
	containerInfo := &container.ContainerInfo{
   
   
		ID:         id,
		Pid:        strconv.Itoa(pid),
		Command:    command,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值