docker系列1—docker隔离和限制技术
- 本篇文章主要回答了以下几个问题
- Docker和进程的关系是什么
- Docker如何在各容器共享内核的情况下,拥有他自己的PID空间以及如何实现资源隔离
docker 与进程
- docker使用起来给人的感觉是像虚拟机,但是docker和虚拟机是有本质的区别的
- 虚拟机在运行一个虚拟机操作系统的时候,宿主机操作系统之上,在一个分配好的完整磁盘空间,安装、运行操作系统,(操作系统是一个软件,这相当于又运行了一个操作系统软件),这显然是隔离的
- 而docker在启动一个容器之后,那个容器(效果和虚拟机里面的一个系统相似)是作为一个操作系统的一个进程在宿主机中存在的,作为一个进程存在,也就意味着多个容器之间或者说,容器和其他进程之间都共享者操作系统的内核,换而言之,在docker启动容器的代价是启动进程的代价,对比虚拟机启动一个虚拟机内的系统就要运行一套新的系统内核来说,开销大幅度降低。当然,作为一个取代虚拟机的产品,虚拟机的资源隔离,比如文件隔离,进程ID隔离,用户名隔离这些东西要在容器作为一个进程运行的基础上完成,本文将在之后分析实现资源隔离的两个技术Namespace和Cgroups技术
- docker虽然消耗小,但是也有他的局限性,如下
- 既然是作为一个进程存在,那么也就意味着容器中的系统必须在和他相同内核或者兼容的内核的宿主机上运行,像Windows上的docker运行linux或者linux上的docker 运行windows都是不行的,那低内核版本的linux想要运行高内核版本的linux,也是不行的
NameServer
-
这个是Linux系统的技术,可以实现对各种资源的隔离,如PID Namespace,Mount Namespace(只让被隔离的进程看到当前Namespace里的挂载点信息),Network(只让被隔离进程看到当前Namespace里的网络设备和配置)和User这种资源,这些NameServer,Linux系统通过提供了系统调用api来让别人调用,docker是通过调用这些系统调用来实现各种资源的隔离的
-
下面拿PID Namespace来举例子
-
容器作为一个进程存在,和宿主机的其他进程处于一个平等竞争的关系,所以他也有一个宿主机分配的PID,比如是1005。
-
docker run -it busybox /bin/sh,比如这一条命令,把容器运行起来之后,你进去容器查看进程的情况,你会发现有一个PID为1的进程和ps进程(ps -ef里的那个ps),而在宿主机里看,PID为1005,可以发现这个docker容器实现了和宿主机进程隔离开来的一个效果,这种效果是通过在被隔离应用的进程空间做了改动,让这些进程只能看到被修改过之后的进程编号
-
int pid = clone(main_function, stack_size, SIGCHLD, NULL);
-
上面的代码块是一个系统调用,调用后,系统会帮我们创建一个新的进程,并且返回这个进程的PID
-
int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
-
多了那个CLONE_NEWPID,那这个进程由于是系统管的,那个进程能看到的东西也是系统管的,在这个进程里面运行的时候通过系统调用获得的PID那就是修改后的,其他的Namespace也类似,通过这个例子我想说明的是,这些Namespace是通过Linux的机制去实现的。
-
CGroup
-
在默认的情况下,是不对进程进行资源限制的,进程所利用的计算资源会不断的变化,那为了限制好计算资源,来达到类似虚拟机那样的计算资源隔离的效果,CGroup被docker用于实现这个目标
-
首先,CGroup是一个系统调用,先来看看他的用法,他是通过Linux中的某个目录里面文件的变动去调用这个功能的
-
$ mount -t cgroup # 用于显示各种资源,表示了对不同硬件资源的操控调用的目录所在处 cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct) cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio) cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset) cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event) cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
-
上面的代码块中,/sys/fs/cgroup/cpu,像这个就是调用cpu资源的入口,对于不同进程的CPU限制的调用,我们可以在这个目录下创建一个子目录,对应一个线程,如下
cd /sys/fs/cgroup/cpu mkdir test # 创建了文件夹之后马上进去看 cd test ll test drwxr-xr-x 2 root root 0 Sep 12 22:20 ./ dr-xr-xr-x 7 root root 0 Sep 12 22:20 ../ -rw-r--r-- 1 root root 0 Sep 12 22:20 cgroup.clone_children -rw-r--r-- 1 root root 0 Sep 12 22:20 cgroup.procs -r--r--r-- 1 root root 0 Sep 12 22:20 cpuacct.stat -rw-r--r-- 1 root root 0 Sep 12 22:20 cpuacct.usage -r--r--r-- 1 root root 0 Sep 12 22:20 cpuacct.usage_percpu -rw-r--r-- 1 root root 0 Sep 12 22:20 cpu.cfs_period_us -rw-r--r-- 1 root root 0 Sep 12 22:20 cpu.cfs_quota_us -rw-r--r-- 1 root root 0 Sep 12 22:20 cpu.shares -r--r--r-- 1 root root 0 Sep 12 22:20 cpu.stat -rw-r--r-- 1 root root 0 Sep 12 22:20 notify_on_release -rw-r--r-- 1 root root 0 Sep 12 22:20 tasks #可以发现系统自动帮我们创建好了这些东西,那么我们应该如何去使用呢
对于cpu来说,有两个比较关键的文件,一个是cpu.cfs_quota_us,另外一个是cpu.cfs_period_us,这两个一般搭配起来使用,表示的是每cpu.cfs_period_us的时间里,cpu只能占用cpu.cfs_quota_us的时间,让这些配置生效还需要在tasks里面指定被限制的线程的PID,在这个cpu里面的子文件夹或者递归的子文件夹只要创建了了一个文件夹,这些文件都是会在每一个文件夹出现的,但是没有在tasks里面指定任务的id的话就不会生效,那么大概可以猜想,docker生成一个容器,获得他的pid之后,通过操作这些目录,就可以轻松达到限制资源的目的
-