Cgroups Namespace Rootfs Volume Docker容器

文章目录

docker容器

Cgroups 容器的资源限制

  • Linux Cgroups是Linux内核中用来为进程设置资源限制的一个重要功能
  • 主要的作用,就是限制一个进程组能够使用的资源上限,包括CPU、内存、磁盘、网络带宽
#Cgroup在Linux中是以文件和目录的方式组织在操作系统的/sys/fs/cgroup路径下
ls /sys/fs/cgroup
blkio  cpuacct      cpuset   freezer  memory   net_cls,net_prio  perf_event  systemd
cpu    cpu,cpuacct  devices  hugetlb  net_cls  net_prio          pids

#
mkdir /sys/fs/cgroup/cpu/test  #在子目录cpu中创建test控制组  创建就算是控制组了 
ls /sys/fs/cgroup/cpu/test   #查看系统自动创建的资源限制文件
cgroup.clone_children  cgroup.procs  cpuacct.usage         cpu.cfs_period_us  cpu.rt_period_us   cpu.shares  notify_on_release
cgroup.event_control   cpuacct.stat  cpuacct.usage_percpu  cpu.cfs_quota_us   cpu.rt_runtime_us  cpu.stat    tasks


#证明控制组的作用
cat /sys/fs/cgroup/cpu/test/cpu.cfs_quota_us
-1  #代表占用CPU时间没有限制
cat /sys/fs/cgroup/cpu/test/cpu.cfs_period_us
100000  #100000us=100ms  

#在100ms进程时间内能被分配到全部的CPU时间

echo 20000 > /sys/fs/cgroup/cpu/test/cpu.cfs_quota_us   #修改只能占用20ms

#写个后台死循环
while : ;do : ; done &
[1] 9028

top  #查看占用                                 100%
PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
9028 root      20   0  116876   1836    120 R  100.0  0.0   0:28.99 bash
#100%占用

echo 9028 > /sys/fs/cgroup/cpu/test/tasks  #将死循环PID写入到cpu子系统下的test控制组tasks文件中里面

#再次查看
top                                           #20%
PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
9028 root     20   0  116876   2028    312 R  20.0  0.1   0:36.80 bash

#可以发现  CPU占用率维持到了20%

kill 9028  #杀死进程

#cgroup的文件和目录操作如上

#docker中设置可以通过相关参数来指定
docker run -itd  --cpu-period 100000 --cpu-quota 20000 centos:latest /bin/bash
fcd2df2d8a2a34644e3d3593e8d4d7dca7ab85be962f2b6e4ded52a3b62e9675

#查看docker创建的控制组    docker目录下  对应的目录
cat /sys/fs/cgroup/cpu/docker/fcd2..../cpu.cfs_period_us
100000   #指定参数已经写入了
  • 容器中top命令显示宿主机信息的解决办法
    • 原因: top是从/prof/stats目录下获取数据
    • 解决: LXCFS来实现,将宿主机的/var/lib/lxcfs/proc/memoinfo文件挂载到Docker容器的/proc/meminfo。容器读取相应文件内容时,LXCFS的FUSE实现从容器对应的Cgroup中读取正确的内存限制,正确的资源约束设定;k8s环境下,可以以ds方式运行LXCFS
    • 摘自:极客时间评论区

Namespace 容器的隔离

  • 当用clone()创建新进程指定Mount Namespace,容器进程看见的文件系统还是跟宿主机完全一样的,原因是Mount Namespace修改的,是容器进程对文件系统挂载点的认知。只有当开启了新的Mount Namespace之后在容器内挂载才能得到”专属“容器的文件目录。
  • 容器创建用户进程
    1. 启用Linux Namespace配置
    2. 设置指定的Cgroups参数
    3. 切换进程的根目录(Change Root 即chroot) 一般优先使用pivot_root()
  • UnionFS能够将不同位置的目录联合挂载
#创建个容器
docker run -itd --name test nginx:latest
59f8758c017b81a2b3f668ff7d67a5dbacb43602528752f2831d5494c78817ea

#查看容器信息
docker inspect test
.....
"GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/fb61dc4ba16ed9e57cc5cd92a5f8d80266fc7b27971a7cda5327720288e25e3d-init/diff:/var/lib/docker/overlay2/1375b09370cf6bccf97620822a2e7515ea45370cab9eb5235459a36f718ede86/diff:/var/lib/docker/overlay2/3362fdc8d6f057d287ae570576641333facac0b34ff63dd244a89e6813aecd28/diff:/var/lib/docker/overlay2/0cecb59da6ca9ab957255ac95cc344866d39bdd845d78491e3b7ab6b4da78b65/diff",
                "MergedDir": "/var/lib/docker/overlay2/fb61dc4ba16ed9e57cc5cd92a5f8d80266fc7b27971a7cda5327720288e25e3d/merged",
                "UpperDir": "/var/lib/docker/overlay2/fb61dc4ba16ed9e57cc5cd92a5f8d80266fc7b27971a7cda5327720288e25e3d/diff",
                "WorkDir": "/var/lib/docker/overlay2/fb61dc4ba16ed9e57cc5cd92a5f8d80266fc7b27971a7cda5327720288e25e3d/work"
            },
            "Name": "overlay2"
        },
....
"RootFS": {
            "Type": "layers",
            "Layers": [   #nginx镜像使用的rootfs由3层组成
                "sha256:3c816b4ead84066ec2cadec2b943993aaacc3fe35fcd77ada3d09dc4f3937313",
                "sha256:787822cf1b17268c7ad2114a7f58fb2743387a5e183815f3f0f1a8a109d33e07",
                "sha256:89decbdf7fb7815678e6f46c9035cdc5c04aeaa1ef63469403d777d5fd14abb8"
            ]
},

#镜像会被联合挂载到一个目录下

Rootfs 容器的一致性

  • 容器的rootfs由三部分组成
    • 可读写层(rw)
      • 增量操作都在这一层
    • init层(ro+wh)
      • -init结尾的层 在只读和读写层之间,专门用来存放/etc/hosts、/etc/resolv.conf等信息
    • 只读层(ro+wh)
      • 最底层,readonly+whiteout 实现删除操作时,会在可读写层创建一个whiteout文件来遮挡只读层的文件

当要修改只读层的内容 就会从上往下寻找修改的文件,复制到可读写层,再进行修改 这就是COW(copy-on-write)

  • UnionFS类别和不同宿主机环境下的使用

    • aufs, device mapper, btrfs, overlayfs, vfs, zfs

      系统 UnionFS
      centos overlayfs , device mapper
      ubuntu overlayfs, aufs
      suse btrfs
      solaris vfs,zfs

Docker exec是如何进入容器内的

查看容器PID 查看宿主机下容器PID所属的Namespace

docker run -itd --name test nginx:latest

docker inspect --format '{{.State.Pid}}' test   #查看容器PID
11132

ls -l /proc/11132/ns  #查看宿主机proc文件,看到容器进程所有的Namespace

total 0
lrwxrwxrwx 1 root root 0 Feb 23 19:50 ipc -> ipc:[4026532170]
lrwxrwxrwx 1 root root 0 Feb 23 19:50 mnt -> mnt:[4026532168]
lrwxrwxrwx 1 root root 0 Feb 23 19:09 net -> net:[4026532173]
lrwxrwxrwx 1 root root 0 Feb 23 19:50 pid -> pid:[4026532171]
lrwxrwxrwx 1 root root 0 Feb 23 19:50 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Feb 23 19:50 uts -> uts:[4026532169]

docker exec 的原理,就是创建一个新的进程,选择加入到某个进程已有的Namespace中,从而达到‘进入’这个进程所在容器的目的

#使用exec进入test容器
docker exec -it test /bin/bash

#再开一个终端
ps -aux | grep /bin/bash #查看进程
root     31254  1.6  0.4 251356 16976 pts/0    Sl+  20:06   0:00 docker exec -it test /bin/bash
root     31282  0.3  0.0  18124  1896 pts/1    Ss+  20:06   0:00 /bin/bash


ls -l /proc/31282/ns #查看exec创建的进程Namespace
total 0
lrwxrwxrwx 1 root root 0 Feb 23 20:06 ipc -> ipc:[4026532170]
lrwxrwxrwx 1 root root 0 Feb 23 20:06 mnt -> mnt:[4026532168]
lrwxrwxrwx 1 root root 0 Feb 23 20:06 net -> net:[4026532173]
lrwxrwxrwx 1 root root 0 Feb 23 20:06 pid -> pid:[4026532171]
lrwxrwxrwx 1 root root 0 Feb 23 20:06 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Feb 23 20:06 uts -> uts:[4026532169]

#发现和上面的Namespace是一样的   这就是exec的原理了

Docker Volume

Volume机制,允许你将宿主主机上指定的目录或者文件,挂载到容器里面进行读取和修改操作

#挂载到/test但是没有指定,默认创建/var/lib/docker/volumes/[VOLUME_ID]/_data挂载
docker run -v /test ...
#指定宿主机的/home挂载到容器的/test上
docker run --name test -v /home:/test ....   :Z ..... #:Z安全标签

docker volume ls    #查看Volume的ID

docker exec -it test /bin/bash
touch /test/1.txt  #容器内创建 

ls /var/lib/docker/volumes/[VOLUME_ID]/_data/ #查看
1.txt  #有了

挂载的时候,“容器进程”已经创建,即已经有了Mount Namespace ,再执行挂载,所有宿主机是看不见挂载的 这就保证了容器的隔离性不会被Volume打破

“容器进程”指Docker创建容器初始化进程(dockerinit)不是应用进程(Entrypoint + Cmd) dockerinit复制完成根目录的准备,挂载设备和目录,配置hostname等容器初始化操作,然后通过execv()系统调用让应用进程(Entrypoint + Cmd)来取代自己成为容器里的PID=1的进程

commit创建镜像 容器中挂载的目录在可读写层始终是空的

上一篇:如何优雅的升级内核?


下一篇:“懂情感,有大脑”的机器人来了