本文仅作为学习记录,非商业用途,侵删,如需转载需作者同意。
一、进程
容器本身没有价值,有价值的是容器编排。
进程:一个程序运行起来后的计算机执行环境的总和。 一个程序被执行起来,它就从磁盘上的二进制文件,变成了计算机内存中的数据,寄存器里的值,堆栈中的指令,被打开的文件,以及各种设备的状态的信息集合。
容器的核心技术:通过约束和修改进程的动态表现,从而为其创造出一个边界。
Cgroups 技术是用来制造约束的主要手段;
Namespace 技术用来修改进程视图的主要手段
==
在容器中可以看到进程的PID 是从1开始的,这种技术就是Linux 里的Namespace 机制。
Namespace的使用方式:它只是Linux 创建进程的一个可选参数,Linux中的创建进程的可选参数是clone() ,比如:
int pid = clone(main_function, stack_size, SIGCHLD, NULL);
这个系统调用就会为我们创建一个新的进程,并且返回它的进程号pid。
而当我们用clone() 系统调用创建一个新的进程时,就可以在参数中指定CLONE_NEWPID
参数,比如:
int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
这时新创建的进程将会看到一个全新的进程空间,在这个进程空间里,它的PID 是1 。
之所以说看到,因为这是个障眼法,在宿主机真实的进程空间里,这个进程的PID还是真实的数值,比如100。
我们还可以多次执行上面的clone() 调用,这样会创建出多个PID Namespace,而每个Namespace里的进程都会认为自己是当前容器里的 1 号进程。每个容器中的进程既看不到宿主机里真正的进程空间,也看不到其他Namespace里的具体情况。
除了刚才用到的PID Namespace,Linux操作系统还提供了Mount、UTS、IPC、Network 和User 这些Namespace,用来对各种不同的进程上下文进程障眼法操作。
比如:
Mount Namespace 用来让隔离的进程只看到当前 Namespace 里的挂载点信息;
Network Namespace 用于让被隔离进程看到当前Namespace里的网络设备和配置。
这就是Linux 容器最基本的实现原理了。
所以实际是在创建容器进程时,指定了这个进程所需要启动的一组Namespace参数。
这样容器就只能看到当前Namespace 所限定的资源,文件,设备,或者配置等信息。
对于宿主机和其他不相关的信息就看不到了。
容器只是一种特殊的进程
二、总结
上图中的左边是虚拟机:
Hypervisor 是虚拟机最主要的部分,通过硬件虚拟化功能,模拟出了运行一个操作系统所需的各种硬件,比如CPU,内存,I/O设备等,在虚拟的硬件上安装了一个新的操作系统 Guest OS。
这样用户的进程就可以运行在这个虚拟的机器中,能看到的只有Guest OS的文件和目录,以及这个机器里的虚拟设备,这样就能将不同的应用程序隔离。
上图中的右边,使用Docker Engine 替换了Hypervisor,这也是为什么很多人把Docker 项目称为轻量级虚拟化技术的原因。
这样的说话并不严谨
理解了Namespace的工作方式之后,在使用Docker 的时候,并没有一个真正的Docker 容器运行在宿主机里面,Docker 项目帮助用户启动的,还是原来的应用程序,只不过在创建这些进程的时候,Docker 为他们加上了各种各样的Namespace参数。
这时,这些进程就会觉得自己是各自 PID Namespace 里的第1号进程,只能看到各自Mount Namespace 里挂载的目录和文件,只能访问到各自Network Namespace 里的网络设备,就仿佛运行在一个个容器里面,与世隔绝。
这种解释说话,用障眼法形容就很贴切了。
三、评论
1、
问题:
为什么clone()时 还有线程呢 是写错了吗 还是有线程的启动啊
回复:
严格说,clone()是线程操作,但linux 的线程是用进程实现的
2、
问题:
老师有个问题一直困扰,容器是一个单进程,那比如我有一个镜像里面集成了jdk, netstat, ping等,虽然这个容器启动时里面是一个java进程,但是我可以进到容器里面执行各种命令,比如netstat等,那这些命令在容器的运行过程中是在运行的吗?
回复:
是在运行的。但它们不受docker的控制,就像野孩子。所以单进程意思不是只能运行一个进程,而是只有一个进程是可控的。
3、
问题:
1 用 namespace 框住app。
2 PID, UTS, network, user, mount, IPC, cgroup
虽然本质上理解,namespace 和 cgroup 是内核特性,容器本质上就是一个加了限定参数的进程,效果上看,图画的也没毛病。
这么看,是不是说容器的安全性,也就是隔离性,就是没办法达到虚拟机的级别呢?
回复:
没错,已经进门了哈
4、
问题:
进一步,1. 如果容器镜像os支持某硬件的驱动,但是宿主机os如果不支持该硬件驱动的话,是否也白搭
2. 是否可以理解为 镜像只是提供了一套镜像文件系统中的各种文件,而各种内核相关的模块或者特性支持,完全依赖于宿主机?
回复:
说的没毛病。
5、
问题:
第一个问题,我感觉docker engine 最好虚线标识,表示他只是一种启动时用,运行时并不需要,真实进程是直接run在host os上
回复:
听起来不错哦
6、
问题:
在容器内,除了pid=1的进程,其他进程是不受docker控制的。
这个控制具体指什么呢?其他进程和pid=1的进程关系又是什么呢?
回复:
是你通过exec进去之后启动的后台进程,不受控制。控制指的是它们的回收和生命周期管理。
7、
问题:
镜像的运行对host os有要求吗? 比如打包的镜像原来的kernel和运行时的kernel不一致,又或者镜像的发行版是centos,能运行在host为ubuntu的机器上吗
回复:
只要应用本身对内核没要求,那就完全没问题
8、
问题:
有一个问题 所以通过pid 可以判断自己是在docker里面还是在宿主机上。怎么通过命令行知道自己现在是在container里面还是在外面?
回复:
有很多种方法。比如查看/proc/1/cgroup下的文件结构
9、
问题:
如果运行的容器是一个os ubuntu,那么在这ubuntu里面运行的进程,和这个ubuntu是什么关系 和主机OS是什么关系?谢谢
回复:
使用ubuntu里的文件,使用宿主机上的内核。
10、
问题:
老师有个问题一直困扰,容器是一个单进程,那比如我有一个镜像里面集成了jdk, netstat, ping等,虽然这个容器启动时里面是一个java进程,但是我可以进到容器里面执行各种命令,比如netstat等,那这些命令在容器的运行过程中是在运行的吗?
作者回复
是在运行的。但它们不受docker的控制,就像野孩子。所以单进程意思不是只能运行一个进程,而是只有一个进程是可控的。
老师接着这个问题有几个问题
- 这些不被docker管理的进程在哪里能看得到?这些进程是否会很占用硬件资源?比如netstat
- 关于mac上运行的docker,比如容器进程依赖linux内核的版本4.2才能正确运行(镜像的内核版本是4.2的),那么既然docker只是用资源隔离的形式运行的。那我在mac起的这个容器时是否能正确运行?他会加载完整的linux操作系统吗?
回复: ps同样可以看到。win mac docker上跑其实都是个虚拟机
11、
问题:
虚拟化和容器的最大区别可以理解这个吗:1:虚拟化是同一台物理机不同的操作系统隔离应用进程2:但是容器是同一个操作系统的不同进程隔离。 简单来说就是:一个是操作系统级别隔离,一个是进程间隔离。
回复:
实际上是 硬件级别 的隔离 VM可是有虚拟硬件的。
12、
问题:
张老师我再追问一问题:在容器内,除了pid=1的进程,其他进程是不受docker控制的。那么如果在一个pod(容器,单pod单容器场景)中以CMD方式启动脚本(该脚本封装了多个启动程序,实测脚本自身的pid=1)。并且还配置了pod的prestop,poststart(是为了执行类似注册的脚本,一次执行而已,不是后台进程)。想请问这种方式下pod的prestop,poststart是如何控制的?实测是生效的,想知道其中的缘由?
回复:
它们当然都是pid 1进程的子进程
13、
问题:
可以理解为虚拟机虚拟的是硬件,docker虚拟的是操作系统吗?
回复:
docker啥都没虚拟。
14、
问题:
老师提到win mac docker上跑其实都是个虚拟机,可以运行不同平台的容器,如mac上运行ubuntu的容器。那么容器所需要的内核在哪里?是虚拟机的内核吗?还有这个虚拟机是不是这个正在运行的docker软件,也就是使用docker的虚拟机的内核?
回复:
容器用的就是这个虚拟机的内核。docker软件负责给你启动这个虚拟机。
15、
问题:
在docker出来之前是怎么用容器的,有想到把操作系统包进去吗?容器技术又是基于什么原因出现的呢?
回复:
那时候各家有各家的方法,也打包操作系统,叫rootfs,请关注后面镜像部分的讲解。
16、
作者回复:
咱们这里默认都说的是linux容器。资源分配是内核统一管理的,不管你的pid是几。
17、
问题:
docker是一个基于内核提供的资源隔离机制而开发实现的客户端管理工具,这样理解没错吧?
回复:
docker本身是有daemon的,但你说的没错,你完全可以开发一个操作runc的没daemon的docker
18、
问题:
在容器内,除了pid=1的进程,其他进程是不受docker控制的,如果这样的话,请问pod生命周期中poststart,prestop这些钩子还能用吗?
回复:
所以不要把hook写成后台进程
19、
问题:
如果容器中跑的是4.x的虚拟kernel环境,宿主机真实是3.x的内核,那么是否有些4.x的内核新功能如果要用到的话是不可行的?
回复:
用不了