容器底层技术
一、Docker基本架构
- 服务端
Docker服务端也就是Docker daemon,一般在宿主机后台运行,接收客户端请求,并处理。
Docker 服务端默认监听本地的unix:///var/run/Docker.sock 套接字,只允许本地root用户或Docker 用户组成员访问,可通过-H
此参数来修改监听方式
Docker 服务端 -H 0.0.0.0:1234
- 客户端
用户不能与服务器直接交互,用户使用的Docker 可执行命令就是客户端程序。
客户端发命令,服务端返回,客户端结束退出,用户执行新指令时需要再次调用客户端程序,同样,客户端默认通过本地的unix:///var/run/Docker.sock套接字向服务端发命令,如果服务端不在默认监听地址,用户在执行命令时指定服务端地址。
假设服务端监听本地的tcp连接1234端口tcp://127.0.0.1:1234,只有通过-H
参数指定了正确的地址信息才能连接服务端。
[root@wangliukun ~]# docker -H tcp://127.0.0.1:1234 version
二、Namespace
1、Namespace介绍
Namespace----linux系统中,容器用来实现“隔离”的技术。
Namespace 技术实际上修改了应用进程看待整个计算机的“视图”,即应用进程的“视线”被操作系统做了限制,只能“看到”某些指定内容。
运行centos7
[root@wangliukun ~]# docker run -it centos /bin/bash
[root@8f822fa5baa2 /]# ps
PID TTY TIME CMD
1 pts/0 00:00:00 bash
15 pts/0 00:00:00 ps
[root@8f822fa5baa2 /]#
两个进程分别为/bin/sh以及ps命令,在进程增加时,会分配PID,对于进程,每当开启一个进程,分配PID之后docker会施加一个假象,屏蔽他之前的进程,使得当前进程PID为1,但实际上,其PID还是开始时分配的PID
通过宿主机查看容器进程:
[root@wangliukun ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b68b20c1f3ae centos "/bin/bash" 4 minutes ago Up 10 seconds suspicious_stonebraker
[root@wangliukun ~]# ps aux | grep b68
root 2460 0.0 0.6 720304 12688 ? Sl 11:24 0:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id b68b20c1f3ae3c6b37bf5655a368bce67a62d33b1cb2973a962fb959117d6d7c -address /run/containerd/containerd.sock
root 2583 0.0 0.0 112824 988 pts/0 S+ 11:26 0:00 grep --color=auto b68
可看到,进程号2460
2、Namespace的类型
- 1、Mount Namespace
将一个文件系统的顶层目录与另一个文件系统的子目录关联,子目录为挂载点,过程称为挂载
- 2、UTS Namespace
提供主机名和域名的隔离,使子进程有独立的主机名和域名
- 3、IPC Namespace
UNIX与Linux下进程间通讯方式
- 4、PID Namespace
隔离进程ID,可嵌套,有父子关系,父Namespace可以看到子Namespace进程的进程信息
- 5、Network Namespace
每个容器拥有独立的网络设备,IP地址,IP路由,/proc/net目录,端口号等使一个host上多个容器实现在网络上隔离
- 6、User Namespace
隔离User权限相关的Linux资源
在不同的 User Namespace 中,同样一个用户的 User ID 和 Group I 可以不一样。也就是说,个用户可以在父 User Namespace 中是普通用户,在子User Namespace 中是超级用户。
3、深入了解Namespace
# -d 表示使容器在后台运行
[root@wangliukun ~]# docker run -it -d centos
793f1efe420575d9275bc4a61bfb18e2c0b65d397cc446f3d12540ed21bd1fab
[root@wangliukun ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
793f1efe4205 centos "/bin/bash" 15 seconds ago Up 4 seconds naughty_leavitt
[root@wangliukun ~]#
#查看正在运行的容器进程号
[root@wangliukun ~]# docker inspect --format '{{ .State.Pid }}' 793f1efe4205
1868
[root@wangliukun ~]#
#查看宿主机的/proc文件,可以看到这个 1868 进程的所有 Namespace 对应的文件
[root@wangliukun ~]# ls -l /proc/1868/ns/
总用量 0
lrwxrwxrwx. 1 root root 0 4月 7 18:42 ipc -> ipc:[4026532503]
lrwxrwxrwx. 1 root root 0 4月 7 18:42 mnt -> mnt:[4026532501]
lrwxrwxrwx. 1 root root 0 4月 7 18:38 net -> net:[4026532506]
lrwxrwxrwx. 1 root root 0 4月 7 18:42 pid -> pid:[4026532504]
lrwxrwxrwx. 1 root root 0 4月 7 18:42 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 4月 7 18:42 uts -> uts:[4026532502]
[root@wangliukun ~]#
#可以看到,一个进程的每种 Namespace 都在它对应的/proc/[进程号]/ns下有一个对应的虚拟文件并日链接到一个真实的 Namespace 文件。
#有了这样的文件,就可以对 Namespace 做一些实质性的操作。
#这个操作依赖一个名为setns()的Linux系统调用
4、Namespace的劣势
- 隔离不彻底
- 有些资源和对象不能被Namespace化
- 安全问题
三、Cgroups
1、简介
- Cgroups(Control Groups)是Linux下用于对一个或一组进程进行资源控制和监控的机制
- 可以对诸如CPU使用时间、内存、磁盘I/O等进程所需资源进行限制
- 不同资源的具体管理工作由相应的Cgroup子系统(Subsystem)来实现
- 针对不同类型的资源限制,只要将限制策略在不同的子系统上进行关联即可
- Cgroups在不同的系统资源管理子系统中,以层级树(Hierarchy)的方式来组织管理:每个Cgroup都可以包含其他的子Cgroup,因此子Cgroup能使用的资源除了受本Cgroup配置的资源参数限制外,还受到父Cgroup设置的资源限制
Cgroups用于对Linux中的进程做统一的监控和资源管理.
Cgroups也分为不同的子系统,不同的子系统会控制不同的资源.一个进程所需的资源大约可以分为:CPU、内存、Disk I/O.这些资源都可以被Cgroups管理起来.这些资源在Cgroups中被称为Subsystem.
进程是一个树状结构.Cgroups也采用了类似的结构,叫做Hierarchy.
2、Cgroups的限制能力
- blkio:该子系统为块设备设定输入输出限制
- cpu: 限制进程的 cpu 使用率。
- cpuacct :该子系统可以自动生成 cgroups 中的任务所使用的 cpu 报告。
- cpuset: 为cgroups中的进程分配单独的cpu节点或者内存节点。
- memory: 限制进程的memory使用量。
- devices: 控制进程能够访问某些设备。
- net_cls: 标记cgroups中进程的网络数据包,然后可以使用tc模块(traffic control)对数据包进行控制。
- net_prio: 限制进程网络流量的优先级。
- huge_tlb: 限制HugeTLB的使用。
- freezer:挂起或者恢复cgroups中的进程。
- ns: 控制cgroups中的进程使用不同的namespace。
3、实例验证
创建目录(控制组)
[root@wangliukun /]# cd /sys/fs/cgroup/cpu
[root@wangliukun cpu]# ll
总用量 0
-rw-r--r--. 1 root root 0 4月 7 18:22 cgroup.clone_children
--w--w--w-. 1 root root 0 4月 7 18:22 cgroup.event_control
-rw-r--r--. 1 root root 0 4月 7 18:22 cgroup.procs
-r--r--r--. 1 root root 0 4月 7 18:22 cgroup.sane_behavior
-r--r--r--. 1 root root 0 4月 7 18:22 cpuacct.stat
-rw-r--r--. 1 root root 0 4月 7 18:22 cpuacct.usage
-r--r--r--. 1 root root 0 4月 7 18:22 cpuacct.usage_percpu
-rw-r--r--. 1 root root 0 4月 7 18:22 cpu.cfs_period_us
-rw-r--r--. 1 root root 0 4月 7 18:22 cpu.cfs_quota_us
-rw-r--r--. 1 root root 0 4月 7 18:22 cpu.rt_period_us
-rw-r--r--. 1 root root 0 4月 7 18:22 cpu.rt_runtime_us
-rw-r--r--. 1 root root 0 4月 7 18:22 cpu.shares
-r--r--r--. 1 root root 0 4月 7 18:22 cpu.stat
drwxr-xr-x. 3 root root 0 4月 7 18:38 docker
-rw-r--r--. 1 root root 0 4月 7 18:22 notify_on_release
-rw-r--r--. 1 root root 0 4月 7 18:22 release_agent
drwxr-xr-x. 60 root root 0 4月 7 19:03 system.slice
-rw-r--r--. 1 root root 0 4月 7 18:22 tasks
drwxr-xr-x. 2 root root 0 4月 7 18:22 user.slice
[root@wangliukun cpu]# mkdir container
[root@wangliukun cpu]# cd container/
[root@wangliukun container]# ll
总用量 0
-rw-r--r--. 1 root root 0 4月 7 19:05 cgroup.clone_children
--w--w--w-. 1 root root 0 4月 7 19:05 cgroup.event_control
-rw-r--r--. 1 root root 0 4月 7 19:05 cgroup.procs
-r--r--r--. 1 root root 0 4月 7 19:05 cpuacct.stat
-rw-r--r--. 1 root root 0 4月 7 19:05 cpuacct.usage
-r--r--r--. 1 root root 0 4月 7 19:05 cpuacct.usage_percpu
-rw-r--r--. 1 root root 0 4月 7 19:05 cpu.cfs_period_us
-rw-r--r--. 1 root root 0 4月 7 19:05 cpu.cfs_quota_us
-rw-r--r--. 1 root root 0 4月 7 19:05 cpu.rt_period_us
-rw-r--r--. 1 root root 0 4月 7 19:05 cpu.rt_runtime_us
-rw-r--r--. 1 root root 0 4月 7 19:05 cpu.shares
-r--r--r--. 1 root root 0 4月 7 19:05 cpu.stat
-rw-r--r--. 1 root root 0 4月 7 19:05 notify_on_release
-rw-r--r--. 1 root root 0 4月 7 19:05 tasks
[root@wangliukun container]#
可以看到,新创建的目录下系统自动生成该子系统的资源限制文件
执行将本将cpu占满
[root@wangliukun container]# while : ; do : ; done &
[1] 1986
执行死循环,cpu达100%,进程号1986
top查看cpu使用情况
[root@wangliukun container]# top
top - 19:11:04 up 48 min, 2 users, load average: 0.80, 0.28, 0.14
Tasks: 115 total, 2 running, 113 sleeping, 0 stopped, 0 zombie
%Cpu(s): 27.5 us, 0.2 sy, 0.0 ni, 72.0 id, 0.0 wa, 0.0 hi, 0.2 si, 0.0 st
#cpu使用100%
KiB Mem : 1862812 total, 1226584 free, 296140 used, 340088 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 1412160 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1986 root 20 0 115668 608 180 R 100.0 0.0 1:34.88 bash
。。。。。。。。
查看container下的文件
[root@wangliukun container]# cat cpu.cfs_quota_us
-1
[root@wangliukun container]# cat cpu.cfs_period_us
100000
[root@wangliukun container]#
可知,container控制组cpu无限制,cpu period默认100000(100ms)
通过修改文件可进行资源限制
向container中 cpu.cfs_quota_us 写入20ms
[root@wangliukun container]# echo 20000 >> ./cpu.cfs_quota_us
在每个100ms里控制组限制进程只能使用20ms的cpu时间,也就是只能使用20%的cpu带宽
把被限制的进程号写入tasks文件
[root@wangliukun container]# echo 1986 >> tasks
查看效果
[root@wangliukun container]# top
top - 19:21:43 up 59 min, 2 users, load average: 1.19, 0.96, 0.58
Tasks: 116 total, 2 running, 114 sleeping, 0 stopped, 0 zombie
%Cpu(s): 5.3 us, 0.2 sy, 0.0 ni, 94.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
#这里cpu只有20%
KiB Mem : 1862812 total, 1225428 free, 297236 used, 340148 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 1411036 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1986 root 20 0 115668 608 180 R 20.0 0.0 11:08.07 bash
4、Cgroups的劣势
-
复杂性:Cgroups的配置和管理相对复杂。用户需要了解如何配置不同的资源限制,包括CPU、内存、磁盘I/O等,并且这些配置通常需要直接在Linux系统上进行,涉及到底层的系统设置和管理。
-
不直观性:Cgroups的配置和状态信息通常存储在
/sys/fs/cgroup
目录下,这些信息的格式和解读方式对于普通用户来说可能不够直观。用户需要具备一定的Linux系统知识和经验,才能有效地使用和理解Cgroups。 -
兼容性问题:不同的Linux发行版可能对Cgroups的实现和支持有所差异,这可能导致在不同系统之间迁移容器时出现兼容性问题。此外,随着Linux内核的更新和发展,Cgroups的接口和特性也可能发生变化,需要用户保持对最新技术的关注和学习。
-
性能开销:虽然Cgroups提供了强大的资源控制和监控能力,但它也引入了一定的性能开销。这是因为Cgroups需要对进程进行额外的管理和监控操作,这些操作可能会消耗一定的CPU和内存资源。在大量使用容器的环境中,这种性能开销可能会更加明显。
-
限制能力有限:虽然Cgroups可以对进程组的资源进行限制,但某些资源(如网络带宽)的限制可能不够精确或灵活。此外,Cgroups对于一些特定的系统调用和内核功能可能无法进行有效的限制和控制。
四、Docker文件系统
1、容器可读可写层工作原理
首先,Docker文件系统是由多个分层组成的,每个分层包含一个只读的镜像层以及一个可写的层(也称为容器层)。这种设计使得Docker能够高效地管理容器和镜像,同时保持镜像的不可变性。
其次,当Docker容器启动时,Docker会在只读镜像之上创建一个可写层。这个可写层用于存储容器运行时产生的所有写操作,如创建、更改和删除文件等。所有对容器内文件系统的修改操作都会在可写层中进行,从而确保原始镜像的内容不会被修改。
此外,UnionFS提供了一种将多个不同文件系统联合成一个逻辑文件系统的方式。在Docker中,UnionFS使得多个镜像层(只读层)和一个可写层能够联合成一个统一的文件系统视图,容器可以看到一个完整的、统一的文件系统。
这种设计带来了几个关键好处。首先,它实现了容器的可读可写特性,使得容器内的文件系统既可以被读取也可以被修改。其次,由于所有的写操作都在可写层中进行,原始镜像的内容得以保持不变,从而确保了镜像的不可变性。这种不可变性使得镜像可以被共享、分发和重用,而无需担心被意外修改。
最后,写时复制(Copy-on-Write,简称CoW)技术也在Docker文件系统中发挥重要作用。它表示只在需要写时才去复制,针对已有文件的修改场景,使用CoW可以有效提高磁盘的利用率。当容器中的进程进行写操作时,写操作会发生在可写层上,而不会影响到原始的只读镜像。这种按需分配磁盘空间的方式进一步提高了存储效率。
2、Docker存储驱动
Docker提供了多种不同的存储驱动选项,以满足不同的使用情境和需求,包括但不限于以下几种:
-
OverlayFS:这是Docker默认的存储驱动,在大多数Linux发行版中都可用。
-
AUFS:是文件级的存储驱动。
-
Device mapper:是映射框架机制。
-
Btrfs:也是文件级存储驱动。
-
ZFS:一种全新的文件系统。
Docker存储驱动采用插件式的架构,允许用户根据不同的需求选择合适的存储后端。其核心任务是将容器的文件系统操作映射到宿主机上的文件系统,以提供持久性和数据隔离,同时负责管理镜像的存储和分发。
上一章:Docker容器
下一章:Docker 数据卷