IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 自己动手写Docker系列 -- 4.2使用AUFS包装busybox -> 正文阅读

[系统运维]自己动手写Docker系列 -- 4.2使用AUFS包装busybox

简介

在上篇中实现了使用宿主机busybox目录作为文件的根目录,但在容器内的对文件的操作仍会影响到宿主机的目录,本篇中实现进一步的容器和镜像隔离

源码说明

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

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

代码编写

本章中直接借鉴书中的代码即可,基本可以运行

首先修改Run命令启动入口,在其中传入相关的路径参数和在容器退出时候清理相关文件

func Run(tty bool, cmdArray []string, config *subsystem.ResourceConfig) {
	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, rootUrl, mntUrl)
	if err := parent.Start(); err != nil {
		log.Error(err)
		return
	}

	cgroupManager := cgroup.NewCgroupManager("mydocker-cgroup")
	defer cgroupManager.Destroy()
	if err := cgroupManager.Apply(parent.Process.Pid); err != nil {
		log.Errorf("cgroup apply err: %v", err)
		return
	}
	if err := cgroupManager.Set(config); err != nil {
		log.Errorf("cgoup set err: %v", err)
		return
	}

	sendInitCommand(cmdArray, writePipe)

	log.Infof("parent process run")
	_ = parent.Wait()
	// 在容器退出的时候删除相关
	deleteWorkSpace(rootUrl, mntUrl)
	os.Exit(-1)
}

// 将运行参数写入管道
func sendInitCommand(array []string, writePipe *os.File) {
	command := strings.Join(array, " ")
	log.Infof("all command is : %s", command)
	if _, err := writePipe.WriteString(command); err != nil {
		log.Errorf("write pipe write string err: %v", err)
		return
	}
	if err := writePipe.Close(); err != nil {
		log.Errorf("write pipe close err: %v", err)
	}
}

func deleteWorkSpace(rootUrl, mntUrl string) {
	deleteMountPoint(mntUrl)
	deleteWriteLayer(rootUrl)
}

func deleteMountPoint(mntUrl string) {
	cmd := exec.Command("umount", mntUrl)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		log.Errorf("deleteMountPoint umount %s err : %v", mntUrl, err)
	}
	if err := os.RemoveAll(mntUrl); err != nil {
		log.Errorf("deleteMountPoint remove %s err : %v", mntUrl, err)
	}
}

func deleteWriteLayer(rootUrl string) {
	writeUrl := rootUrl + "writeLayer/"
	if err := os.RemoveAll(writeUrl); err != nil {
		log.Errorf("deleteMountPoint remove %s err : %v", writeUrl, err)
	}
}

Docker会在删除容器的时候,把容器对应的Write Layer和Container-init Layer删除,而保留镜像所有的内容。本节中,在容器退出的时候会删除Write Layer。DeleteWorkSpace函数包括DeleteMountPoint和DeleteWriteLayer。
首先,在DeleteMountPoint函数中umount mnt目录。
然后,删除mnt目录。
最后,在DeleteWriteLayer函数中删除writeLayer文件夹。这样容器对文件系统的更改就都已经抹去了。

下面是创建只读层和可写层的代码:

修改NewParentProcess

func NewParentProcess(tty bool, rootUrl, mntUrl string) (*exec.Cmd, *os.File) {
	readPipe, writePipe, err := os.Pipe()
	if err != nil {
		log.Errorf("create pipe error: %v", err)
		return nil, nil
	}

	cmd := exec.Command("/proc/self/exe", "init")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
	}
	if tty {
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}

	// 将管道的一端传入fork的进程中
	cmd.ExtraFiles = []*os.File{readPipe}
	if err := newWorkSpace(rootUrl, mntUrl); err != nil {
		log.Errorf("new work space err: %v", err)
		return nil, nil
	}
	cmd.Dir = mntUrl
	return cmd, writePipe
}

func newWorkSpace(rootUrl string, mntUrl 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
	}
	return nil
}

// 我们直接把busybox放到了工程目录下,直接作为容器的只读层
func createReadOnlyLayer(rootUrl string) error {
	busyboxUrl := rootUrl + "busybox/"
	exist, err := pathExist(busyboxUrl)
	if err != nil {
		return err
	}
	if !exist {
		return fmt.Errorf("busybox dir don't exist: %s", busyboxUrl)
	}
	return nil
}

// 创建一个名为writeLayer的文件夹作为容器的唯一可写层
func createWriteLayer(rootUrl string) error {
	writeUrl := rootUrl + "writeLayer/"
	if err := os.Mkdir(writeUrl, 0777); err != nil {
		return fmt.Errorf("create write layer failed: %v", err)
	}
	return nil
}

func createMountPoint(rootUrl string, mntUrl string) error {
	// 创建mnt文件夹作为挂载点
	if err := os.Mkdir(mntUrl, 0777); err != nil {
		return fmt.Errorf("mkdir faild: %v", err)
	}
	// 把writeLayer和busybox目录mount到mnt目录下
	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 {
		return fmt.Errorf("mmt dir err: %v", err)
	}
	return nil
}

func pathExist(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, err
	}
	return false, err
}

NewWorkSpace函数是用来创建容器文件系统的,它包括CreateReadOnlyLayer、CreateWriteLayer和CreateMountPoint。
CreateReadOnlyLayer函数新建busybox文件夹,将busybox.tar解压到busybox目录下,作为容器的只读层。
CreateWriteLayer函数创建了一个名为writeLayer的文件夹,作为容器唯一的可写层。
在CreateMountPoint函数中,首先创建了mnt文件夹,作为挂载点,然后把writeLayer目录和busybox目录mount到mnt目录下。
最后,在NewParentProcess函数中将容器使用的宿主机目录/root/busybox替换成/root/mnt。

运行测试

我们运行容器后,在里面创建一个文件:

?  dockerDemo git:(main) ? ./main run -ti sh
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-18T20:38:46+08:00"}
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-18T20:38:46+08:00"}
{"level":"info","msg":"all command is : sh","time":"2022-03-18T20:38:46+08:00"}
{"level":"info","msg":"parent process run","time":"2022-03-18T20:38:46+08:00"}
{"level":"info","msg":"init come on","time":"2022-03-18T20:38:46+08:00"}
{"level":"info","msg":"current location: /home/lw/code/go/dockerDemo/mnt","time":"2022-03-18T20:38:46+08:00"}
{"level":"info","msg":"find path: /bin/sh","time":"2022-03-18T20:38:46+08:00"}
/ # touch /tmp/test.txt
/ # ls /tmp/
test.txt

然后我们在宿主机上查看相关文件夹中的内容:

?  dockerDemo git:(main) ls busybox/tmp 
?  dockerDemo git:(main) ? ls mnt/tmp/         
test.txt

我们可以看到在busybox中并没有对应的修改,只修改了我们的只读层

然后我们使用exit退出容器,在退出容器后,宿主机上面的只读层会对应的删除

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 21:35:42  更:2022-03-21 21:39:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 0:28:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码