自己动手写Docker系列 -- 5.1实现容器的后台运行

自己动手写Docker 一.容器简介 1.容器与虚拟机 2.容器的用途 3.Docker版本 二.基础技术 1.Namespace 1.1 UTS Namespace 1.2 IPC Namespace 1.3 PID Namespace 1.4 Mount Namespace 1.5 User Namesapce 1.6 Network Namespace 2.Cgroups 2.1 Cgroups中... 阅读详情

简介

在前几篇中,我们已经构建了一个基础的镜像,本篇开始做一些进阶的功能,下面就是实现docker中的-d命令,让容器能够后台运行

源码说明

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

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

代码实现

在Docker早期版本,所有的容器init进程都是从dockerdaemon这个进程fork出来的。

这也就会导致一个众所周知的问题,如果dockerdaemon挂掉,那么所有的容器都会宕掉,这给升级docker daemon带来很大的风险。

后来,Docker使用了containerd,也就是现在的runC,便可以实现即使daemon挂掉,容器依然健在的功能了

我们并不想去实现一个daemon,因为这和容器的关联不是特别大,而且,查看Docker的运行引擎runC可以发现,runC也提供一种detach功能,可以保证在runC退出的情况下容器依然可以运行。

因此,我们将会使用detach功能去实现创建完成容器后,mydocker就会退出,但是容器依然继续运行的功能。

容器,在操作系统看来,其实就是一个进程。当前运行命令的mydocker 是主进程,容器是被当前mydocker进程fork出来的子进程。

子进程的结束和父进程的运行是一个异步的过程,即父进程永远不知道子进程到底什么时候结束。

如果创建子进程的父进程退出,那么这个子进程就成了没人管的孩子,俗称孤儿进程。

为了避免孤儿进程退出时无法释放所占用的资源而僵死,进程号为1的进程init就会接受这些孤儿进程。

这就是父进程退出而容器进程依然运行的原理。

虽然容器刚开始是由当前运行的mydocker进程创建的,但是当mydocker进程退出后,容器进程就会被进程号为1的init进程接管,这时容器进程还是运行着的,这样就实现了mydocker退出、容器不宕掉的功能。

原理大致就如上,对应的实现代码如下:

1.首先增加-d的命令选项,并传入run中

var RunCommand = cli.Command{
	Name:  "run",
	Usage: `Create a container with namespace and cgroups limit mydocker run -ti [command]`,
	Flags: []cli.Flag{
		......
		// 添加-d标签
		cli.BoolFlag{
			Name:  "d",
			Usage: "detach container",
		},
	},
	Action: func(context *cli.Context) error {
		if len(context.Args()) < 1 {
			return fmt.Errorf("missing container command")
		}
		var cmdArray []string
		for _, arg := range context.Args() {
			cmdArray = append(cmdArray, arg)
		}
		tty := context.Bool("ti")
		detach := context.Bool("d")
		resConfig := &subsystem.ResourceConfig{
			MemoryLimit: context.String("mem"),
			CpuShare:    context.String("cpuShare"),
			CpuSet:      context.String("cpuSet"),
		}
		volume := context.String("v")
		run.Run(tty, detach, cmdArray, resConfig, volume)
		return nil
	},
}

2.在run中,如果detach不为true则一直等待父进程退出,不反之,则父进程退出,docker进行成为孤儿进程,让init进程进行接管

func Run(tty, detach bool, cmdArray []string, config *subsystem.ResourceConfig, volume string) {
	.....

	log.Infof("parent process run")
	// 和书中稍微有点不一样,我们这里直接判断detach即可
	// 在我们使用docker的时候,-dit是可以共存的,所以我们优先判断detach
	if !detach {
		_ = parent.Wait()
		deleteWorkSpace(rootUrl, mntUrl, volume)
	}
	os.Exit(-1)
}

3.对文件空间初始化的改造

在完成上面的代码后,是可以运行了,但如果我们强杀进程后,其挂载点和可写层并没有进行清理

查看了书中提供的整个源码,应该是后面的rm命令实现的

而我们在实际使用docker的过程中,好像不删除容器,直接使用容器ID重启后,上次的文件是有进行保存的,还存在

所有我们对原来的文件空间初始化进行改造,让我们强杀容器进程后,能够再次启动进程,而不是报文件夹已存在的错误

如下所示,当挂载点和读写层不存在时,我们才进行创建

func createWriteLayer(rootUrl string) error {
	writeUrl := rootUrl + "writeLayer/"
	exist, err := pathExist(writeUrl)
	if err != nil && !os.IsNotExist(err) {
		return err
	}
	if !exist {
		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文件夹作为挂载点
	exist, err := pathExist(mntUrl)
	if err != nil && !os.IsNotExist(err) {
		return err
	}
	if !exist {
		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
}

运行测试

我们如书中的例子,运行一个top命令来进行测试

➜  dockerDemo git:(main) ✗ go build mydocker/main.go
➜  dockerDemo git:(main) ✗ ./main run -d top
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-21T05:58:44+08:00"}
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-21T05:58:44+08:00"}
{"level":"info","msg":"all command is : top","time":"2022-03-21T05:58:44+08:00"}
{"level":"info","msg":"parent process run","time":"2022-03-21T05:58:44+08:00"}
➜  dockerDemo git:(main) ✗ ps -ef |grep top
root       69016       1  0 05:58 pts/0    00:00:00 top
root       69044    5508  0 05:58 pts/0    00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox top
➜  dockerDemo git:(main) ✗

可以看到启动起来后就退出了,没有进入交互命令,查看top进程时,其父进程id是1

Docker:进入容器(处于后台运行的容器) 容器启动时如果指定了-d参数,在后台运行容器,或者通过docker start重启已经停止的容器。注:使用attach进入容器后,如果在容器中执行exit或ctrl+d,会停止容器。注:通过这种方式进入容器后,如果在容器中执行exit或ctrl+d,不会停止容器。2.docker exec -it 容器ID /bin/bash。1.docker attach 容器ID。 阅读详情

相关推荐

[全志T113]:ST7701 86屏幕驱动

[全志T113]:ST7701 86屏幕驱动

LINKKEEE的博客 3141

技术分享 | 如何让上千容器同时“存活“

最近接到一个需求,用户可以申请创建一个或者多个 docker 容器,容器要一直存在,用户不管过了多长时间都可以访问,而且用户产生的数据一直存在,换而言之就是要做到容器持久化,也就是说我们要提供一个小型的服务器,但是久而久之服务器资源就会被容器占满,就只能扩容了,我要做的就是在尽可能节省服务器的情况下,提供尽可能多的 docker 容器。 简要思路 在用户使用一段时间后,把用户产生的差异数据持久化,然后停止容器,当用户再次访问,通过拦截访问地址去新建容器并把持久化数据放到新的容器中,然后重定向,用户就可以访

ShowMeBug的博客 1186

医学图像处理入门:如何用Python将CT灰度图转换为伪彩色图像(附代码)

本文详细介绍了如何使用Python将CT灰度图转换为伪彩色图像,提升医学图像的可读性和诊断效率。通过OpenCV和Matplotlib等工具,实现灰度值到彩色空间的映射,特别适用于突出显示特定组织密度差异。文章包含完整代码示例和临床实用技巧,适合医学影像分析初学者和Python开发者。

gitlab7runner的博客 325

Docker容器技术

Docker镜像是通过Dockerfile定义的,Dockerfile是一个文本文件,其中包含了一系列构建镜像所需的指令和配置,比如基础镜像、环境变量、依赖项等。Linux容器是一种轻量级的虚拟化技术,它利用Linux内核的功能,通过进程隔离、文件系统隔离、网络隔离等机制,使得应用程序可以在独立、安全的运行环境中运行,而与宿主机和其他容器隔离开来。:Docker守护进程是运行在宿主机上的系统服务,负责接收和处理Docker客户端的请求,管理容器的创建、运行和销毁,以及镜像的构建、存储和分发。

stu_kk的博客 612

WIFI基础入门--802.11g--增强速率物理层ERP--15

WIFI基础入门--802.11--增强速率物理层--191.802.11g的组件保护机制 1.802.11g的组件 802.11g其实是将好几种物理层规范合而为一。虽然统称为增强速率物理层(Extended Rate PHY简称ERP),但是实际上ERP有好几种不同的方式: ERP-DSSS与ERP-CCK 这两种模式向下兼容于原始直接序列规范(1Mbps与2Mbps),以及所提到的802.11...

香蕉一号 4617

Haclon案例-找出图中面积最大的圆

自带的函数,求得圆的面积和圆心点坐标,然后,用本章所学习的选择算子和循环算子找出面积最大的圆。的图片,试着给它加上不同种类的噪声,然后找出其中面积最大的圆,并将圆的面积标注在其圆心位置。可知,除了六个圆形之外,还有一些微小的噪声点,采用了开运算和特征选择的方法去除噪声。1. 该图片为一灰度图,对其进行二值化,二值化后如图。4. 在面积最大圆的圆心坐标位置写上该圆的面积。

qq_20660115的博客 896

Java引入docker-api(二),进阶操作之redis创建

步骤如下: 创建hostconfig对象 获取dockerClient对象 调用创建容器方法 运行容器 直接上代码,每段代码有详细注释 //获取客户端在引入docker-api(一)中有相关代码,这里不多余做粘贴 DockerClient client = dockerConfig.getClient(); //容器名字 String containerName = "redis-test"; //宿主机挂载conf地址 String confPath = "/User/tomcat/redis.co

zzztimes的博客 2511

使用AI编写测试用例——详细教程

现阶段AI还不能完全替代人工测试用例编写,但如果把AI当做一个提高效率的工具,它将会是一个很强大的辅助。

公众号|软件测试老吴 3万+

java对接docker简单示例

以上示例代码演示了如何使用Docker Java客户端库执行一些常见的Docker操作,例如列出本地镜像、创建容器并运行命令、获取容器日志等。请根据你的实际需求修改代码中的对象和参数,以适应你的Docker环境和操作。请注意,在使用Docker Java客户端库时,你需要根据实际情况设置Docker的连接信息,例如Docker服务器的地址、认证信息等。以下是一个示例代码,展示了如何使用Java对接Docker并执行一些常见的操作: 首先,确保已经在项目中添加了Docker Java客户端库的依赖。

牛肉胡辣汤 2260

Windows Pychram部署Unet++过程

windows系统 在pycharm平台上配置并跑通Unet网络 这里我用的是B站课程里的资源《唐宇迪Unet图像分割语义分割实战》

ArcGis_Niu的博客 803

【Unity】【2D】用连续图片生成动画Animation的简明步骤

本文介绍了Unity中使用2D连续图片制作2D动画Animation的简明步骤。

O青草O的博客 Unity和游戏制作知识分享 714

自己动手写 Docker 系列文章总览

本文章系列起源于:《自己动手写 Docker》,编程实践方得真知,虽然大部分代码书中都有,但还是遇到了不少困难,下面是对于自己写的 Docker Demo 的总览

github_35735591的博客 2268

1 初识智能化测试

非常感谢您可以和我一起来聊一聊智能化测试的一些事。智能化测试是一个新鲜又老旧的问题,说新鲜是因为很多人当听到智能化测试都会联想到人工智能、机器学习、深度学习等高大上的技术,很多时候觉得离我们的实际工作还很远;说老旧,是因为智能化测试的一些技术的发展在行业里面已经很久了,例如符号执行、静态分析等技术已经有很长的历史了。近些年,随着测试技术的的飞速发展,智能化测试也有了越来越多的实践,优秀的开源项目慢慢的被行业推行并且落地。那么在这里我们就一起来聊聊智能化测试以及智能化测试好的思路、实践、方法以及技术落地过程。

Criss@陈磊 2623

自己动手写一个仿Docker虚拟容器

自己动手写一个仿Docker虚拟容器 本项目参照书籍《自己动手写Docker》 作者:陈显鹭(花名:遥鹭)-阿里云高级研发工程师等 项目地址:https://gitee.com/ShengHua666/can 项目环境:Linux 虚拟机 Ubuntu20.04 内核5.10.x,Go 1.17.1 项目运行: go mod init Bocker go mod tidy go build mydocker/main.go mv main can cp can /bin/ mv busybox.tar

huahua66123的博客 1343

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

自己动手写Docker系列 -- 5.2实现查看运行中的容器

在上篇中我们实现了将容器后台运行,本篇中我们将实现docker的ps命令,查看当前正在运行中的容器列表

github_35735591的博客 1216

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

自己动手写Docker系列 -- 5.5实现容器停止

在上篇中我们实现了通过exec命令,重新进入了后台运行中的容器,本篇将实现stop命令,将运行中的容器停止

github_35735591的博客 576

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

自己动手写Docker系列 -- 5.6实现删除容器

在上篇中我们实现了stop命令,停止正在运行的容器,本篇将实现rm命令,删除已经停止的容器

github_35735591的博客 311

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

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

github_35735591的博客 748

【Docker系列】大神我自己直接用Go动手写Docker

文章目录一.容器简介1.容器与虚拟机2.容器的用途3.Docker版本二.基础技术1.Namespace查看进程的namespace信息1.1 UTS Namespace1.2 IPC Namespace1.3 PID Namespace1.4 Mount Namespace1.5 User Namesapce1.6 Network Namespace2.Cgroups2.1 Cgroups中的3个组件2.2 Cgroup文件系统三.构造容器1.proc文件系统2.mydocker文件分布结构3.实现run

weixin_54707168的博客 859

《自己动手写Docker》学习笔记1

《自己动手写Docker》学习笔记1 1 前言 由于本人毕业设计与云原生领域相关,因此最近在学习Docker相关知识,《自己动手写Docker》涵盖了Docker底层的各类知识,还提供了各类实验及代码Demo,是入门云原生的不错之书。本文主要是记录本人的实验过程以及一些总结感悟。 本文所涉及的实验环境: Vmware Workstation 16 搭建Ubuntu20.04环境 Linux内核为5.10.x Go版本1.17.1 2 第一章 容器与开发语言 2.1 Docker Docker是一个开源工具,

huahua66123的博客 994

自己动手写Docker系列 -- 5.3实现logs命令查看容器日志

在上篇中我们实现了查看正在运行中的容器列表,本章节我们来实现logs命令,来查看正在运行中的容器的运行日志

github_35735591的博客 815

Docker——退出而不关闭容器

一般情况下,进入docker容器后如果退出容器,容器就会变成Exited的状态,那么如何退出容器让容器不关闭呢? 如果要正常退出不关闭容器,请按Ctrl+P+Q进行退出容器,这一点很重要,请牢记! 以下示例为退出容器但不关闭容器 [root@localhost ~]# docker attach c600c4519fc8 [root@c600c4519fc8 /]# exit exit [root...

qq_36274515的博客 1万+

自己动手写Docker系列 -- 5.4实现进入容器的namespace,exec命令

在上篇中我们实现了将容器后台运行,本篇中我们将实现docker的ps命令,查看当前正在运行中的容器列表

github_35735591的博客 659

自己动手写Docker系列 -- 4.1使用busybox创建容器

目前docker demo中还是使用的系统原有proc,不怎么纯净,本篇中使用busybox来更换docker demo的系统挂载点

github_35735591的博客 2050

自己动手写Docker系列 -- 5.8实现容器制定环境变量运行

在上篇中我们实现了将不同的容器独立进行隔离,并进行独立打包,本篇中将实现启动容器时指定环境变量,让容器内运行的程序可以使用外部传递的环境变量

github_35735591的博客 480
上一篇: 自己动手写Docker系列 -- 4.4实现简单镜像打包
下一篇: 自己动手写Docker系列 -- 5.2实现查看运行中的容器
_萧_
博客等级 码龄10年 55粉丝 157原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值