容器两大核心技术资源隔离、资源限制
我们只要提起容器技术就都会想到Docker,Docker是原dotCloud公司的项目,Docker项目出现了一个简单不起眼的技术叫容器镜像,而Docker项目的出现解决了应用打包这个原容器技术的中的难题,这就是为什么Docker项目刚刚开源不久,就带领dotCloud公司在PaaS领域脱颖而出。而dotCloud公司在2013年底改名为Docker公司,而Docker也成为了容器的代理名词。
docker安装
# step 1: 安装必要的一些系统工具 sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # Step 2: 添加软件源信息 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # Step 3: 更新并安装 Docker-CE sudo yum makecache fast sudo yum -y install docker-ce # Step 4: 开启Docker服务 sudo service docker start # 注意: # 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。例如官方并没有将测试版本的软件源置为可用,你可以通过以下方式开启。同理可以开启各种测试版本等。 # vim /etc/yum.repos.d/docker-ce.repo # 将 [docker-ce-test] 下方的 enabled=0 修改为 enabled=1 # # 安装指定版本的Docker-CE: # Step 1: 安装指定版本的docker-ce-selinux # yum install https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch.rpm # Step 2: 查找Docker-CE的版本: # yum list docker-ce.x86_64 --showduplicates | sort -r # Loading mirror speeds from cached hostfile # Loaded plugins: branch, fastestmirror, langpacks # docker-ce.x86_64 17.03.1.ce-1.el7.centos docker-ce-stable # docker-ce.x86_64 17.03.1.ce-1.el7.centos @docker-ce-stable # docker-ce.x86_64 17.03.0.ce-1.el7.centos docker-ce-stable # Available Packages # Step 3: 安装指定版本的Docker-CE: (VERSION 例如上面的 17.03.0.ce.1-1.el7.centos) # sudo yum -y install docker-ce-[VERSION]
认识容器技术
容器的核心技术就是通过约束和修改进程的动态表现,对于Docker等大数容器技术来说,都是通过Cgroup技术是用来制造约束的主要手段,而Namespace技术则是用来修改进程试图的主要方法。
Namespace 技术
Namespace技术其实只是Linux创建新进程的一个可选参数。在Linux创建线程的系统调用是clone(),例如:
int pid = clone(main_function,...
极客时间版权所有: https://time.geekbang.org/column/article/14642
int pid = clone(main_function,...
极客时间版权所有: https://time.geekbang.org/column/article/14642
int pid = clone(main_function, stack_size, SIGCHLD, NULL);
而用clone()创建新进程的时候,就可以在参数中指定CLONE_NEWPID参数,例如:
int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);
这时候,我们可以看到一个全新的进程空间,在这个进程空间里,它的PID是1,但是在宿主机的真实进程空间里,这个进程的PID还是真实数值,这就是Namespace的“障眼法”。而除了我们刚刚用到的PID Namespace,Linux还提供了Mount、UTS、IPC、Network 和User 这些Namespace。这就是Linux容器的基本实现原理了。
Docker听起来很高大上,实际上是容器进程时,指定了一组Namespace参数,这样容器内只能看到当前Namespace所限定的资源、文件、设备、状态等。而宿主机相关资源是看不到的。所以说容器其实是一种特殊的进程而已。
我们通过下面的指令验证一下
$ docker run -it busybox /bin/sh Unable to find image 'busybox:latest' locally latest: Pulling from library/busybox 8c5a7da1afbc: Pull complete Digest: sha256:cb63aa0641a885f54de20f61d152187419e8f6b159ed11a251a09d115fdff9bd Status: Downloaded newer image for busybox:latest / # ps PID USER TIME COMMAND 1 root 0:00 /bin/sh 7 root 0:00 ps / #
上面指令的意思就是我们通过busybox镜像启动一个容器 并执行/bin/sh命令,我们在容器里执行一下ps指令,可以看到在Docker里最开始执行的/bin/sh,就是这个容器内的1号进程,而这个容器里一共只有两个进程运行,这意味着这两个进程已经被Docker隔离在与宿主机不同的世界里。
Cgroup 技术
Linux Cgroups是Linux内核中用来为进程设置资源限制的重要功能,但是我们为什么要做资源限制呢?我们通过Namespace创建的容器通过障眼法的把PID在容器看到的是1号进程,但是在宿主机上的实际进程是100号,但是100号进程与宿主机其他所有进程是平等竞争的关系。这就意味着容器里的1号进程可以把所有宿主机资源吃光,也可以被宿主机上其他其他进程占用。显然这样的设计是不合理的。所以我们通过Cgroup来限制进程对资源的使用情况进行限制,主要作用就是限制一个进程组能够使用的资源上线,例如:CPU、内存、磁盘、网络带宽等。下面我们通过一组实例来验证一下限制功能。
在Linux中,Cgroup给用户暴露出来的操作接口是文件系统,既它以文件和目录的方式组织操作系统 /sys/fs/cgroup 路径下。通mount -t cgroup来显示出来
mount -t cgroup cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer) cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct) cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids) cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory) cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb) 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/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset) cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
可以看到在/sys/fs/cgroup下面有cpu、memory、blkio等这样的子目录,这都是能被Cgroup进行资源限制的资源类型,而这些子目录下就是这些类型的限制方法,例如:对CPU子目录来说,我们可以看到如下几个配置文件
$ ls /sys/fs/cgroup/cpu cgroup.clone_children cpuacct.usage cpuacct.usage_percpu_sys cpuacct.usage_user cpu.rt_period_us cpu.stat cgroup.procs cpuacct.usage_all cpuacct.usage_percpu_user cpu.cfs_period_us cpu.rt_runtime_us notify_on_release cpuacct.stat cpuacct.usage_percpu cpuacct.usage_sys cpu.cfs_quota_us cpu.shares tasks
我们可以用cpu.cfs_quota_us和cpu.rt_period_us来配合使用限制CPU使用情况,可以用来限制进程在长度为cpu.rt_period的一段时间,只能被分配到总量为cpu.cfs_quota的CPU时间。我们通过下面例子展示。首先我们需要在对应的子目录下创建一个目录,我们进入/sys/fs/cgroup/cpu,执行如下指令:
$ cd /sys/fs/cgroup/cpu $ mkdir container $ ls container/ cgroup.clone_children cpuacct.usage cpuacct.usage_percpu_sys cpuacct.usage_user cpu.rt_period_us cpu.stat cgroup.procs cpuacct.usage_all cpuacct.usage_percpu_user cpu.cfs_period_us cpu.rt_runtime_us notify_on_release cpuacct.stat cpuacct.usage_percpu cpuacct.usage_sys cpu.cfs_quota_us cpu.shares tasks
我们可以看到在创建的container目录下会自动生成该cpu子目录下的资源限制文件。
下面我们执行一个死循环脚本,让他把CPU资源吃到100%,根据输出,我们可以看到这个脚本的PID是17436
$ while : ; do : ; done & [1] 17436
通过top命令查看cpu资源使用情况
$ top .... %Cpu13 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st .....
我们看一下container目录下的cpu.cfs_quota_us和cpu.rt_period_us的值
$ cd container/ $ cat cpu.cfs_quota_us -1 $ cat cpu.cfs_period_us 100000
接下来,我们可以通过修改这些文件的内容来设置限制
极客时间版权所有: https://time.geekbang.org/column/article/14653
给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式...
极客时间版权所有: https://time.geekbang.org/column/article/14653
接下来我们通过修改这两个文件的内容来设置CPU限制。
向container目录下的cpu.cfs_quota_us文件中写入20ms (20000 us)
$ echo 20000 > cpu.cfs_quota_us
上述命令意味着在每100ms的时间里,被该控制组限制的进程只能使用20ms的CPU时间,也就是说这个进程只能使用到20% 的CPU带宽。
接下来,我们把被限制的进程的PID写入container目录下tasks文件,上面设置就对该PID生效。
$ echo 17436 > /sys/fs/cgroup/cpu/container/tasks
我们用top指令看一下
$ top .... %Cpu13 : 20.3 us, 0.0 sy, 0.0 ni, 79.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st ....
可以看到CPU资源讲到20%
除了CPU子系统外,Cgroup对其他子系统都有资源限制的方法,例如:
- blkio 为块设备设置I/O限制,一般用户磁盘等设备
- cpuset 为进程分配单独的CPU核和对应的内存节点
- memory 为进程设定内存使用限制