容器两大核心技术资源隔离、资源限制

我们只要提起容器技术就都会想到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 为进程设定内存使用限制

 

posted @ 2018-09-26 15:59  大胖猴  阅读(2661)  评论(0编辑  收藏  举报