创建容器需要用到的工具/知识
- Namespace linux内核提供的功能,用来隔离一系列的资源,如PID,网络,User Id,文件系统等,使进程拥有自己独立的资源视图。有三个主要的系统调用:
clone(), 创建容器化进程,根据系统调用参数来判断哪些namespace被创建,该进程的子进程会包含进这些namespace中
unshare(), 将进程移出某个namespace
setns(), 将进程加入到某个namespace中 - Cgroups 限制进程的各种资源,如cpu,内存,io等
- overlayfs 联合文件系统,是一种轻量级的、基于联合的文件系统,它支持分层方法来组合多个目录,最终呈现给进程一个统一的文件视图。
- volume 通过挂载的方式将容器其中的数据持久化到宿主机上。
- 网络虚拟化 通过bridge,veth,iptables等,让容器可以访问外部网络
Namespace
- Mount Namespace, 隔离文件系统的挂载点,可以让不同的进程在不同的文件系统视图下工作,互不干扰
- PID Namespace, 隔离进程id,不同进程的namespace中可以用相同的id号。每个容器都有自己的PID空间,看不到容器外面的进程。
- Network Namespace, 隔离网络接口、IP地址,路由表、端口等网络资源。
- UTS Namespace, 隔离主机名和域名,每个容器可以设置自己的主机名。
- IPC Namespace, 隔离进程间通信, 保证不同容器的IPC通信不受干扰。
- User Namespace, 隔离用户或用户组ID,使得进程可以拥有不同的用户ID空间。在容器中一个用户可以映射成root用户,但在主机上,实际是一个普通用户。
- Cgroup Namespace,隔离控制组,使得进程对cgroup的修改仅限于自己的cgroup空间。
Linux资源隔离命令
Linux 资源隔离
unshare --uts --ipc --pid --mount --user --fork /bin/sh
Options:
-m, --mount[=<file>] unshare mounts namespace(隔离进程看到的挂载点视图,之后需要通过pivot_root切换根目录视图)
-u, --uts[=<file>] unshare UTS namespace (hostname etc)
-i, --ipc[=<file>] unshare System V IPC namespace(隔离进程间通信)
-n, --net[=<file>] unshare network namespace(网络设备、IP地址端口等)
-p, --pid[=<file>] unshare pid namespace(隔离PID,需要重新挂载proc,否则ps命令显式的是主机的进程)
-U, --user[=<file>] unshare user namespace (隔离用户和用户组。缺少user空间的隔离,会导致在执行pivot_root,umount old rootfs时,丢失宿主机的devpts的mount point,原因不明)
-C, --cgroup[=<file>] unshare cgroup namespace
-T, --time[=<file>] unshare time namespace
Cgroups(v1)
创建并挂载cgroup
mkdir cgroup-test #创建一个目录
sudo mount -t cgroup -o none,name=cgroup-test cgroup-test ./cgroup-test #挂载cgroup到上面创建的目录上,挂载之后会生成一些默认文件
默认情况下cgroup挂载在/sys/fs/cgroup目录下,可以在该目录下的对应资源目录下通过创建目录来创建控制组,然后在控制组下配置资源,子cgroup会继承父cgroup的资源限制配置(cgroup.clone_children=1)。
创建自己的cgroup,如: mkdir -p /sys/fs/cgroup/cpu/pid
删除指定cgroup子目录:rmdir /sys/fs/cgroup/cpu/pid (task和cgrpu.procs文件需要没有进程才能删除)
可以模拟压力测试cgroup配置
stress-ng --cpu 1 --cpu-load 50 --vm 1 --vm-bytes 200M --timeout 30s
cgroup常用的控制文件
tasks 将进程的pid按行写入该文件,将该文件中的进程分配到特定的cgroup控制组中,进程消亡后会自动从这个文件清除。
cgroup.procs 和tasks文件一样
cpu.share 设置 CPU shares,1024 是 1 个 CPU 的默认权重,512 表示 0.5 个 CPU 的权重(相对权重,如果没有其他进程竞争,还是会独占cpu)
cpu.cfs_period_us 设置一个 CPU 时间周期的长度(以微秒为单位),通常是 100ms(即 100000 微秒)
cpu.cfs_quota_us 设置在一个时间周期内,cgroup 允许使用的最大 CPU 时间(以微秒为单位)。值为 -1 表示不限制 cfs_quota_us/cfs_period_us表示cpu占用的时间比例
cpuset.cpus 将进程绑定到某几个cpu核心上,如1,3
cpuset.mems 指定任务可以使用的内存节点集合,适用于 NUMA,没有特殊需求,可以简单设为0
memory.soft_limit_in_bytes 用于配置内存的软限制,会优先回收超过软件的内存但不会强制执行,系统会根据压力动态调节内存使用
memory.limit_in_bytes 超出这个限制的内存请求会触发 OOM 杀手(Out of Memory),终止进程
overlayFS
挂载overlayFS之后,镜像作为只读层,容器对文件的修改,不会影响镜像。如果多个lowerdir都有同名文件,那么靠前的文件优先级更高,在统一视图层看到的会是优先级最高的文件的结果。如果upperdir也有同名文件,那么会显示upperdir的文件的结果。
mount -t overlay overlay -o lowerdir=lower1:lower2:lower3,upperdir=uppdir,workdir=work mergedir
1)lowerdir, 对应ausf的只读目录,可以挂载多个
2)upperdir, 对应ausf的读写目录,对lowdir的修改,会通过CoW(写时复制)复制到updderdir再修改
3)workdir, 工作目录,挂载时会被清空
3)mergedir, 挂载目录,即lowerdir和uperdir的合并视图
volume
比如,需要将容器中的/home/data目录数据支持化,只需要将/home/data这个目录挂载到宿主机上,假如挂载到宿主机的/home/test/data目录,实际的容器挂载路径会是mergedir目录(overlay的合并视图)。命令如下:
mount --bind /home/test/data mergedir/home/data
网络虚拟化设备
veth pair(虚拟以太网设备对)是一种虚拟网络接口,它由两个接口组成,每个接口可以在不同的网络命名空间或同一命名空间中适用,通常用于将不同的网络命名空间链接起来。veth pair 由两个接口互连,数据包从一个接口进入,从另一个接口出来。
Bridge 连接多个网络设备,在容器中通常通过网桥把多个容器连接起来形成一个局域网环境。
#开启IP转发功能
echo 1 > /proc/sys/net/ipv4/ip_forward
#创建一个网桥
ip link add br0 type bridge
#给网桥添加ip
ip addr add 172.19.0.1/24 dev br0
#上面这个命令似乎会自动添加一个路由,如果没有可以手动配置
route add -net 172.19.0.0/24 dev br0 #将172.19.0.0/24这个网段的数据包路由到br0这个接口上
#启用网桥
ip link set br0 up
#创建两对veth
ip link add veth0 type veth peer veth1
ip link add veth2 type veth peer veth3
#把其中两对veth的一个放到网桥上
ip link set veth0 master br0
ip link set veth2 master br0
#启动放到网桥上的veth设备
ip link set veth0 up
ip link set veth2 up
#创建两个network namespace
ip netns add ns1
ip netns add ns2
#把剩下的两个veth放到ns1和ns2上
ip link set veth1 netns ns1
ip link set veth3 netns ns2
#给放到ns1网络命名空间的虚拟网络接口配置ip和路由,并启用
ip netns exec ns1 ip addr add 172.19.0.2/24 dev veth1
ip netns exec ns1 ip link set veth1 up
ip netns exec ns1 ip link set lo up
ip netns exec ns1 ip route add default via 172.19.0.1
#给放到ns2网络命名空间的虚拟网络接口配置ip和路由,并启用
ip netns exec ns2 ip addr add 172.19.0.3/24 dev veth3
ip netns exec ns2 ip link set veth3 up
ip netns exec ns2 ip link set lo up
ip netns exec ns2 ip route add default via 172.19.0.1
#禁止转发
iptables -P FORWARD DROP
#如果iptables默认禁止转发,需要让两个容器通信,需要配置iptable
iptables -A FORWARD -i br0 -o br0 -j ACCEPT
#如果iptables默认禁止转发,需要访问外网,需要配置NAT,和转发规则
iptables -t nat -A POSTROUTING -s 172.19.0.0/24 -o eth0 -j MASQUERADE
iptables -A FORWARD -i br0 -o eth0 -j ACCEPT #允许br0的流量转发到eth0
iptables -A FORWARD -o br0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # 允许已经建立连接的外部网络返回的流量
删除路由
ip route del 172.19.0.0/24
删除net namespace
ip netns delete ns1
删除veth
ip link delete veth0
删除birdge
ip link delete br0