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

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

简介

在上篇中我们实现了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,
### Docker Compose 部署 MySQL 5.7 的配置示例 以下是通过 `docker-compose` 配置并部署 MySQL 5.7 的具体方法: #### 基本配置文件结构 Docker Compose 使用 YAML 文件来定义服务及其依赖关系。对于 MySQL 5.7,可以通过创建名为 `docker-compose.yml` 的文件实现。 ```yaml version: '3.8' services: db: image: mysql:5.7 container_name: mysql_5_7_container restart: always environment: MYSQL_ROOT_PASSWORD: rootpassword MYSQL_DATABASE: testdb MYSQL_USER: user MYSQL_PASSWORD: password ports: - "3306:3306" volumes: - ./data:/var/lib/mysql networks: - app-network networks: app-network: driver: bridge ``` #### 参数说明 - **image**: 定义使用的镜像版本为 `mysql:5.7`[^4]。 - **container_name**: 设置容器名称为 `mysql_5_7_container`。 - **restart**: 当主机重启时自动启动该容器- **environment**: 配置环境变量以初始化数据库实例,包括根密码 (`MYSQL_ROOT_PASSWORD`) 和其他用户/数据库设置。 - **ports**: 将宿主机的端口映射到容器内部的默认 MySQL 端口 (3306)[^4]。 - **volumes**: 数据持久化存储路径,将本地目录挂载至容器内的 `/var/lib/mysql` 路径。 - **networks**: 创建一个新的网络用于服务间通信。 #### 启动服务 完成上述配置后,运行以下命令即可启动服务: ```bash docker-compose up -d ``` 此命令将以分离模式启动服务,并在后台运行容器[^3]。 #### 修改权限 如果遇到权限问题(例如无法执行 `docker-compose`),可按照如下方式调整文件权限: ```bash chmod +x /usr/local/bin/docker-compose ``` 或者如果是通过 YUM 安装,则需调整以下路径的权限: ```bash chmod +x /usr/libexec/docker/cli-plugins/docker-compose ``` 以上操作确保了 `docker-compose` 可正常执行[^2]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值