Docker
Docker
Introduction
Linux Namespace
简介
Linux Namespace 是 Linux 提供的一种内核级别环境隔离的方法。Unix 中有一个叫chroot 的系统调用(通过修改根目录把用户 jai l到一个特定目录下),chroot 提供了一种简单的隔离模式:chroot 内部的文件系统无法访问外部的内容。Linux Namespace 在此基础上,提供了对 UTS、IPC、mount、PID、network、User 等的隔离机制。
Linux 下的超级父亲进程的 PID 是1,所以,同 chroot 一样,如果我们可以把用户的进程空间 jail 到某个进程分支下,并像 chroot 那样让其下面的进程 看到的那个超级父进程的 PID为1,于是就可以达到资源隔离的效果了(不同的 PID namespace 中的进程无法看到彼此)
主要是三个系统调用
clone**() – 实现线程的系统调用,用来创建一个新的进程,并可以通过设计上述参数达到隔离。unshare**() – 使某进程脱离某个 namespacesetns**() – 把某进程加入到某个 namespace
clone() 系统调用
编译运行程序验证
UTS Namespace
运行程序,子进程的 hostname 变成了 container
IPC Namespace
IPC全称 Inter-Process Communication,是 Unix/Linux 下进程间通信的一种方式,IPC 有共享内存、信号量、消息队列等方法。所以为了隔离,需要把 IPC 给隔离开来,这样只有在同一个 Namespace 下的进程才能相互通信。IPC 需要有一个全局的 ID,Namespace 需要对这个 ID 隔离,不能让别的 Namespace 的进程看到。
启动 IPC 隔离,需要在调用 clone 时加上 CLONE_NEWIPC 参数
先创建一个 IPC 的 Queue,全局 Queue ID 是0
运行程序验证 IPC Queue 是否隔离
PID Namespace
运行程序验证
PID 为1的作用: PID 为1的进程是 init,地位非常特殊。作为所有进程的父进程,有很多特权(比如:屏蔽信号等),还会为检查所有进程的状态.如果某个子进程脱离了父进程(父进程没有wait它),那么 init 就会负责回收资源并结束这个子进程,所以要做到进程空间的隔离,首先要创建出 PID 为1的进程,最好就像 chroot 那样,把子进程的PID在容器内变成1.
但是在子进程的 shell 里输入 ps,top 等命令,上述程序还是可以看得到所有进程。说明并没有完全隔离。这是因为,像 ps, top 这些命令会去读 /proc 文件系统,因为 /proc 文件系统在父进程和子进程都是一样的,所以这些命令显示的东西都是一样的。因此,还需要对文件系统进行隔离.
Mount Namespace
启用 mount namespace 并在子进程中重新 mount /proc 文件系统
运行程序验证
User Namespace
User Namespace 主要是用了 CLONE_NEWUSER 的参数。使用了这个参数后,内部看到的 UID 和 GID 已经与外部不同了,默认显示为65534。那是因为容器找不到其真正的 UID,所以设置上了最大的 UID(其设置定义在 /proc/sys/kernel/overflowuid)。
要把容器中的 uid 和真实系统的 uid 给映射在一起,需要修改 /proc/pid/uid_map 和 /proc/pid/gid_map 这两个文件。这两个文件的格式为:
其中:
第一个字段 ID-inside-ns 表示在容器显示的 UID 或 GID,
第二个字段 ID-outside-ns 表示容器外映射的真实的 UID 或 GID。
第三个字段表示映射的范围,一般填1,表示一一对应。 比如,把真实的 uid=1000映射成容器内的 uid=0
再比如下面的示例:表示把 namespace 内部从0开始的 uid 映射到外部从0开始的 uid,其最大范围是无符号32位整形
需要注意的是:
写这两个文件的进程需要这个 namespace 中的 CAP_SETUID (CAP_SETGID)权限(可参看Capabilities)
写入的进程必须是此 user namespace 的父或子的 user namespace 进程。
另外需要满如下条件之一:1)父进程将 effective uid/gid 映射到子进程的 user namespace 中,2)父进程如果有 CAP_SETUID/CAP_SETGID 权限,那么它将可以映射到父进程中的任一 uid/gid。
上面的程序,用了一个 pipe 来对父子进程进行同步,为什么要这样做?因为子进程中有一个execv 的系统调用,这个系统调用会把当前子进程的进程空间给全部覆盖掉,我们希望在 execv 之前就做好 user namespace 的 uid/gid 的映射,这样,execv 运行的 /bin/bash 就会因为我们设置了 uid 为0的 inside-uid 而变成#号的提示符。
运行程序
虽然容器内是 root 用户,但其实容器的 /bin/bash 进程是以一个普通用户 ubuntu 运行的,容器的安全性得到提高. User Namespace 是以普通用户运行,但是别的 Namespace 需要 root 权限,那么,如果我要同时使用多个 Namespace 时,先用一般用户创建 User Namespace,然后把这个一般用户映射成 root,在容器内用 root 来创建其它的 Naemespace。
Network Namespace
一般用 ip 命令创建 Network Namespace. 注意: 宿主机可能是 VM 主机,物理网卡可能是一个可以路由 IP 的虚拟网卡. ![[Pasted image 20240213223106.png]]
docker 容器中,使用 ip link show 或 ip addr show 查看当前宿主机的网络情况
如何模拟以上情况:
docker 网络原理与以上方式有两点区别:
Docker 的 resolv.conf 没有用这样的方式,而是用了 [[Docker#Mount Namespace|Mount Namespace]]
另外,docker 是用进程的 PID 来做 Network Namespace 的名称的。
为运行的 docker 容器新增网卡,比如为正在运行的docker容器,增加一个 eth1的网卡,并给了一个静态的可被外部访问到的 IP 地址。
需要把外部的“物理网卡”配置成混杂模式,这样这个 eth1 网卡就会向外通过 ARP 协议发送自己的 Mac 地址,然后外部的交换机就会把到这个 IP 地址的包转到“物理网卡”上,因为是混杂模式,所以 eth1就能收到相关的数据,一看包是发给自己的那么就收到。这样,Docker容器的网络就和外部通了。
Linux Cgroup
Linux CGroup 全称 Linux Control Group, 是 Linux 内核的一个功能,用来限制、 控制与分离一个进程组群的资源(如 CPU、内存、磁盘输入输出等)。
Linux CGroupCgroup 可让您为系统中所运行任务(进程)的用户定义组群分配资源 — 比如 CPU 时间、系统内存、网络带宽或者这些资源的组合。您可以监控您配置的 cgroup,拒绝 cgroup 访问某些资源,甚至在运行的系统中动态配置您的 cgroup。 主要提供以下功能:
Resource limitation: 限制资源使用,比如内存使用上限以及文件系统的缓存限制。
Prioritization: 优先级控制,比如:CPU 利用和磁盘 IO 吞吐。
Accounting: 一些审计或一些统计,主要目的是为了计费。
Control: 挂起进程,恢复执行进程。
使用 cgroup,系统管理员可更具体地控制对系统资源的分配、优先顺序、拒绝、管理和监控。可更好地根据任务和用户分配硬件资源,提高总体效率.
隔离一个进程集合(比如:nginx 的所有进程),并限制他们所消费的资源,比如绑定 CPU的核。
为这组进程分配其足够使用的内存
为这组进程分配相应的网络带宽和磁盘存储限制
限制访问某些设备(通过设置设备的白名单)
Ubuntu 中查看 cgroup mount
或者使用 lssubsys 命令
如果没有可自己 mount
在 /sys/fs/cgroup 各个子目录 make dir
CPU Limit
模拟非常吃 CPU 的程序
限制自定义 group 的 CPU
线程代码示例
Memory Limit
模拟耗内存程序(不断的分配内存,每次512个字节,每次休息一秒)
限制内存
IO Limit
测试模拟 IO 速度
创建一个 blkio(块设备IO) 的 cgroup
限制进程 IO 速度
Cgroup Subsystem
blkio — 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等等)。
cpu — 这个子系统使用调度程序提供对 CPU 的 cgroup 任务访问。
cpuacct — 这个子系统自动生成 cgroup 中任务所使用的 CPU 报告。
cpuset — 这个子系统为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点。
devices — 这个子系统可允许或者拒绝 cgroup 中的任务访问设备。
freezer — 这个子系统挂起或者恢复 cgroup 中的任务。
memory — 这个子系统设定 cgroup 中任务使用的内存限制,并自动生成内存资源使用报告。
net_cls — 这个子系统使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。
net_prio — 这个子系统用来设计网络流量的优先级
hugetlb — 这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统。
Cgroup 相关术语
任务(Tasks):就是系统的一个进程。
控制组(Control Group):一组按照某种标准划分的进程,比如官方文档中的Professor和Student,或是WWW和System之类的,其表示了某进程组。Cgroups中的资源控制都是以控制组为单位实现。一个进程可以加入到某个控制组。而资源的限制是定义在这个组上,就像上面示例中我用的haoel一样。简单点说,cgroup的呈现就是一个目录带一系列的可配置文件。
层级(Hierarchy):控制组可以组织成hierarchical的形式,既一颗控制组的树(目录结构)。控制组树上的子节点继承父结点的属性。简单点说,hierarchy就是在一个或多个子系统上的cgroups目录树。
子系统(Subsystem):一个子系统就是一个资源控制器,比如CPU子系统就是控制CPU时间分配的一个控制器。子系统必须附加到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。Cgroup的子系统可以有很多,也在不断增加中。
Docker Engine
Install
Storage
Overview
Volumes
Bind mounts
tmpfs mounts
Storage drivers
Btrfs
OverlayFS
ZFS
containerd snapshotters
Networking
Overview
Networking drivers
Bridge
Overlay
Host
IPvlan
Macvlan
None
Container
自定义网络模式
Daemon
Docker Build
Build images
Multi-stage builds
Use multi-stage builds
Name build stages
Dockerfile
Example
others
Docker Compose
[[docker-compose.yml|Archery Docker Compose]]
Reference:
Last updated