文章目录
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之后在容器内挂载才能得到”专属“容器的文件目录。
- 容器创建用户进程
- 启用Linux Namespace配置
- 设置指定的Cgroups参数
- 切换进程的根目录(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文件来遮挡只读层的文件
- 可读写层(rw)
当要修改只读层的内容 就会从上往下寻找修改的文件,复制到可读写层,再进行修改 这就是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创建镜像 容器中挂载的目录在可读写层始终是空的