容器的实现原理

容器的本质

容器的本质是一个进程。容器技术的核心功能,就是通过约束和修改进程的动态表现,从而为其创造出一个“边界”。

对于docker等大多数linux容器来说,cgroups技术是用来制造约束的主要手段,而namespace技术则是用来修改进程视图的主要方法。

首先创建一个容器试试:

docker run -it busybox /bin/sh
/ #
/ # ps
PID  USER   TIME COMMAND
  1 root   0:00 /bin/sh
  10 root   0:00 ps

从上面可以看到,docker里最开始执行的/bin/sh,这就是容器内部的第一号进程(pid=1),而这个容器一共只有2个进程,也就是说,docker隔离在了一个跟宿主机完全不同的世界当中。
本来我们在宿主机上运行一个/bin/sh程序,操作系统会给它分配一个进程编号,pid=100,现在docker把这个/bin/sh程序运行在一个容器当中,使用一个“障眼法”,让它永远看不到其他的进程,让它误以为自己就是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。

namespace实现了容器间资源的隔离。linux使用6中namespace,分别对应6中资源:Mount, UTS, IPC, PID,Network和User。

这些就是linux容器最基本的实现原理。docker容器听起来很玄,实际上就是在创建容器进程时,指定了这个进程所需要启动的一组namespace参数。这样,容器就只能看到当前namespace所限定的资源、文件、设备、状态和配置等。而对于主机以及其他不想管的程序,它就完全看不到了。

为了更好的理解容器的特性,这里讨论容器的底层实现技术。
cgroup和namespace是最重要的两种技术。cgroup实现资源限制,namespace实现资源隔离。

cgroup

linux操作系统通过cgroup可以设置进程使用CPU、内存和IO资源的限额。前面提到的–cpu-shares,-m,–device-write-bps实际就是在配置cgroup。
在/sys/fs/cgroup/cpu/docker目录中,linux会为每个容器创建一个cgroup目录,以容器的长ID命名。

7a19c745df04b7b7e68332d422c4a6a5.png

namespace

namespace实现了容器间资源的隔离。
linux使用6中namespace,分别对应6中资源:Mount, UTS, IPC, PID,Network和User。

Mount namespace

Mount namespace让容器看上去拥有整个文件系统。

UTS namespace

UTS ns让容器拥有自己的hostname。容器的hostname默认是它的短ID,可以通过-h或 --hostname参数来设置。

IPC namespace

IPC namespace让容器拥有自己的共享内存和信号量,实现进程间通信。

PID namespace

Network namespace

让容器拥有自己独立的网卡、ip、路由等资源。

User namespace

让容器能够管理自己的用户,host不能看到容器中创建的用户。

你可能感兴趣的:(docker,docker学习笔记)