实现Docker容器的底层技术-cgroup 和namespace浅析

引言

docker启动的容器本质上是Host中的一个进程,cgroup和namespace是最重要的两项技术,cgroup主要实现资源的限额而namespce则用来实现资源的隔离

1.cgroup

cgroup全称Control Group ,Linux操作系统通过cgroup可以设置进程使用CPU、内存、和IO资源的限额,比如可以在启动容器时通过:–cpu-shares、-m、–device-write-bps等启动参数来配置限额,其实这些底层就是在配置cgroup。

cgroup到底是啥东西么。可以通过host的目录来找到它

[root@VM_0_13_centos cgroup]# pwd
/sys/fs/cgroup

这里,咱们通过一个例子来深入理解它。

即通过 progrium/stress镜像来测试说明这个东西,启动这个镜像,run命令启动镜像时若本地没有该镜像则会自动去docker仓库去拉相应的镜像

[root@VM_0_13_centos ~]# docker run -it --cpu-shares 512 progrium/stress -c 1

再查看一下刚刚启动的容器ID,下面的ce348eec3e6b

[root@VM_0_13_centos cgroup]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
ce348eec3e6b        progrium/stress     "/usr/bin/stress --v…"   17 hours ago        Up 17 hours                                quirky_cartwright

然后在/sys/fs/cgroup/cpu/docker/目录中,

[root@VM_0_13_centos cgroup]# cd /sys/fs/cgroup/cpu/docker/

Linux会为每一个容器创建一个cgroup的目录,以容器长ID命名,如下所示:

[root@VM_0_13_centos docker]# ll
total 0
drwxr-xr-x 2 root root 0 Oct 29 17:12 ce348eec3e6b9b68f517279478eef3ba573fca4d81bcc511a34e65505d80f31b

感兴趣的,可以再看下这个容器下的cgroup内容:

[root@VM_0_13_centos ce348eec3e6b9b68f517279478eef3ba573fca4d81bcc511a34e65505d80f31b]# ll
total 0
-rw-r--r-- 1 root root 0 Oct 29 17:12 cgroup.clone_children
--w--w--w- 1 root root 0 Oct 29 17:12 cgroup.event_control
-rw-r--r-- 1 root root 0 Oct 29 17:12 cgroup.procs
-r--r--r-- 1 root root 0 Oct 29 17:12 cpuacct.stat
-rw-r--r-- 1 root root 0 Oct 29 17:12 cpuacct.usage
-r--r--r-- 1 root root 0 Oct 29 17:12 cpuacct.usage_percpu
-rw-r--r-- 1 root root 0 Oct 29 17:12 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Oct 29 17:12 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Oct 29 17:12 cpu.rt_period_us
-rw-r--r-- 1 root root 0 Oct 29 17:12 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 Oct 29 17:12 cpu.shares
-r--r--r-- 1 root root 0 Oct 29 17:12 cpu.stat
-rw-r--r-- 1 root root 0 Oct 29 17:12 notify_on_release
-rw-r--r-- 1 root root 0 Oct 29 17:12 tasks

目录中包含所有与cpu相关的cgroup配置,文件cpu.shares保存的就是 –cpu-shares的配置,值为512,

[root@VM_0_13_centos ce348eec3e6b9b68f517279478eef3ba573fca4d81bcc511a34e65505d80f31b]# cat cpu.shares 
512

同样的,/sys/fs/cgroup/memory/docker/sys/fs/cgroup/blkio/docker中保存的是内存以及Block IO的cgroup配置。

2.namespace

在每个容器中,我们都可以看到文件系统、网卡等资源,这些资源看上去是各容器之间互相隔离的,拿网卡来说,每个容器都会认为自己有一块独立的网卡,即使host上只有一块物理网卡,这种方式非常好,它使得每个容器更像一台独立的计算机。

Linux实现这种方式的技术是namespace,namespace管理着host中全局唯一的资源,并可以让每个容器都觉得只有自己在独立的占有该资源,换句话说,namespace实现了容器间的资源的隔离。

Linux使用了6种namespace,分别对应6种资源:Mount、UTS、IPC、PID、Network和User。

1.Mount namespace

Mount namespace 让容器看上去拥有整个文件系统。
容器有自己的根目录:“/”,可以执行mountumount命令,当然我们知道这些操作只有在当前容器中生效,不会影响到host和其他容器

2.UTS namespace

简单地说,UTS namespace让容器有自己的hostname,默认情况下,容器的hostname是他的短ID,可以通过-h或者 --hostname启动参数设置。

3.IPC namespace

IPC namespace让容器拥有自己的共享内存和信号量(semaphore)来实现进程间通信,而不会与host和其他容器的IPC混在一起。

4.PID namespace

容器在host中是以进程的方式运行的,例如当前host中运行了两个容器,

[root@VM_0_13_centos docker]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
ce348eec3e6b        progrium/stress     "/usr/bin/stress --v…"   17 hours ago        Up 17 hours                                quirky_cartwright
3127d82847fb        ruibaby/halo        "java -Djava.securit…"   6 months ago        Up 6 months         0.0.0.0:80->8090/tcp   halo

两个容器的短ID:ce348eec3e6b3127d82847fb
通过 ps axf 可以查看容器进程

[root@VM_0_13_centos docker]# ps axf | grep docker
 4869 pts/1    S+     0:00  |       \_ grep --color=auto docker
 9551 pts/3    Sl+    0:01          \_ docker run -it --cpu-shares 512 progrium/stress -c 1
13028 ?        Sl     6:41  \_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/3127d82847fb813da2f8da2de1ccb46dac59238df170bcf6aa3b978f16e0b07c -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
 9731 ?        Sl     0:01  \_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/ce348eec3e6b9b68f517279478eef3ba573fca4d81bcc511a34e65505d80f31b -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
11412 ?        Ssl   68:50 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
13023 ?        Sl     0:15  \_ /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.2 -container-port 8090

可以看出 13028进程ID对应容器:3127d82847fb9731进程ID对应容器:ce348eec3e6b

所有容器的进程都挂在dockerd进程下,并且容器也有自己的子进程,可以进入到容器中查看容器的子进程。

[root@VM_0_13_centos docker]# docker exec -it ce348eec3e6b bash
root@ce348eec3e6b:/# ps axf
  PID TTY      STAT   TIME COMMAND
    7 pts/1    Ss     0:00 bash
   14 pts/1    R+     0:00  \_ ps axf
    1 pts/0    Ss+    0:00 /usr/bin/stress --verbose -c 1
    6 pts/0    R+   1034:13 /usr/bin/stress --verbose -c 1
root@ce348eec3e6b:/# 

5.Network namespace

Network namespace让容器拥有自己的独立的网卡、IP、路由等资源,后面单独写篇来详细了解。

6.User namespace

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

[zerah@VM_0_13_centos ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
93448d822113        ubuntu:v1           "bash"                   9 minutes ago       Up 9 minutes                               eloquent_herschel
3127d82847fb        ruibaby/halo        "java -Djava.securit…"   6 months ago        Up 6 months         0.0.0.0:80->8090/tcp   halo
[zerah@VM_0_13_centos ~]$ docker exec -it 93448d822113 bash
root@93448d822113:/# useradd bob     
root@93448d822113:/# whoami
root
root@93448d822113:/# su bob 
$ whoami
bob

然后在host中新打开一个窗口,测试一下host能不能看到容器中的用户

[root@VM_0_13_centos ~]# su bob
su: user bob does not exist

在容器中创建了用户zerah,但host中并不会创建相应的用户

镜像常用操作命令

  • images: 显示镜像列表
  • history:显示镜像构建历史
  • commit:从容器创建新镜像
  • build :从dockerfile 构建镜像
  • tag:给镜像打tag
  • pull:从registry 下载镜像
  • push:将镜像上传到registry
  • rmi:删除docker host中的镜像
  • search:搜索docker hub中的镜像

容器常用操作命令:

  • create:创建容器
  • run:运行容器
  • pause:暂停容器
  • unpause:取消暂停的容器,使其继续运行
  • stop:发送SIGTERM停止容器
  • kill:发送SIGKILL快速停止容器
  • start:启动容器
  • restart:重启容器
  • attach:进入到容器启动进程的终端
  • exec:在容器中启动新进程,通常使用“-it” 参数
  • logs:显示容器启动进程的控制台输出,用“-f”持续打印
  • rm:从磁盘中删除容器

注:删除多个已停止的容器:

[root@VM_0_13_centos ~]# docker rm -v $(docker ps -aq -f status=exited)

附录:

Ubuntu下添加,删除,修改,查看用户和用户组:http://www.sunaiwen.com/?p=127

你可能感兴趣的:(docker)