Linux内核中的容器技术详解
引言
容器技术是现代云计算和DevOps的基础,Linux内核通过namespace和cgroups等机制实现了容器化隔离。本文将深入探讨Linux容器技术的底层实现原理,包括资源隔离、容器编排和容器安全等方面。
容器技术概述
1. 容器 vs 虚拟机
| 特性 | 容器 | 虚拟机 |
|---|---|---|
| 启动时间 | 秒级 | 分钟级 |
| 资源占用 | 低 | 高 |
| 隔离级别 | 进程级 | 硬件级 |
| 性能 | 接近原生 | 有损耗 |
2. 容器生态
- Docker:最流行的容器平台
- Podman:Docker的无守护进程替代品
- containerd:容器运行时
- runc:容器运行器
- Kubernetes:容器编排平台
Linux内核支持
1. 必需的内核配置
# 检查内核支持 grep -E "CONFIG_NAMESPACES|CONFIG_CGROOPS|CONFIG_OVERLAY_FS" /boot/config-$(uname -r) # 必需的配置选项 CONFIG_NAMESPACES=y CONFIG_UTS_NS=y CONFIG_IPC_NS=y CONFIG_PID_NS=y CONFIG_NET_NS=y CONFIG_CGROUPS=y CONFIG_CGROUP_NS=y CONFIG_OVERLAY_FS=y2. 内核模块
# 加载必要的模块 modprobe overlay modprobe br_netfilter # 检查已加载的模块 lsmod | grep -E "overlay|bridge|iptable"namespace隔离
1. namespace类型
#define CLONE_NEWNS 0x00020000 // Mount namespace #define CLONE_NEWUTS 0x04000000 // UTS namespace #define CLONE_NEWIPC 0x08000000 // IPC namespace #define CLONE_NEWPID 0x20000000 // PID namespace #define CLONE_NEWNET 0x40000000 // Network namespace #define CLONE_NEWUSER 0x10000000 // User namespace2. 创建namespace
#define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int child(void *arg) { printf("I'm in child namespace\n"); printf("PID in namespace: %d\n", getpid()); // 执行shell execl("/bin/bash", "/bin/bash", NULL); return 0; } int main() { char *stack; pid_t pid; // 分配栈空间 stack = malloc(4096 * 1024); if (!stack) return 1; // 创建PID namespace pid = clone(child, stack + 4096 * 1024, CLONE_NEWPID | CLONE_NEWUTS | SIGCHLD, NULL); if (pid < 0) { perror("clone"); return 1; } printf("Parent: child PID = %d\n", pid); waitpid(pid, NULL, 0); return 0; }3. Network namespace
# 创建network namespace ip netns add myns # 在namespace中执行命令 ip netns exec myns ip link list # 添加网络接口到namespace ip link add veth0 type veth peer name veth1 ip link set veth1 netns myns # 配置namespace中的接口 ip netns exec myns ip addr add 10.0.0.2/24 dev veth1 ip netns exec myns ip link set veth1 up # 删除namespace ip netns del myns # 查看所有namespace ip netns list ls /var/run/netns4. Mount namespace
# 创建mount namespace unshare --mount # 挂载proc mount -t proc proc /proc # 挂载sys mount -t sysfs sys /sys # 创建新的挂载点 mount -t tmpfs tmpfs /tmp # 查看挂载 mount cat /proc/self/mountinfocgroups资源控制
1. cgroups层次结构
cgroup root ├── system.slice ├── user.slice └── docker.slice ├── abc123.service └── def456.service2. cgroups v1
# 创建cgroup mkdir /sys/fs/cgroup/cpu/mycontainer # 设置CPU限制 echo 50000 > /sys/fs/cgroup/cpu/mycontainer/cpu.cfs_quota_us # 50% CPU echo 100000 > /sys/fs/cgroup/cpu/mycontainer/cpu.cfs_period_us # 设置内存限制 echo 512M > /sys/fs/cgroup/memory/mycontainer/memory.limit_in_bytes echo 256M > /sys/fs/cgroup/memory/mycontainer/memory.soft_limit_in_bytes # 设置IO限制 echo "8:0 wbps=10485760" > /sys/fs/cgroup/blkio/mycontainer/blkio.throttle.write_bps_device # 添加进程 echo <pid> > /sys/fs/cgroup/cpu/mycontainer/tasks # 查看统计 cat /sys/fs/cgroup/cpu/mycontainer/cpu.stat3. cgroups v2
# 检查cgroups v2 mount | grep cgroup2 # 创建cgroup mkdir /sys/fs/cgroup/mycontainer # 设置CPU限制 echo "max 50000 100000" > /sys/fs/cgroup/mycontainer/cpu.max # 设置内存限制 echo "max 512M" > /sys/fs/cgroup/mycontainer/memory.max # 设置IO限制 echo "8:0 rbps=max wbps=max" > /sys/fs/cgroup/mycontainer/io.max # 添加进程 echo <pid> > /sys/fs/cgroup/mycontainer/cgroup.procs4. libcgoup编程
#include <libcgroup.h> int main() { struct cgroup *cgroup; struct cgroup_controller *cgc; int ret; cgroup_init(); // 创建cgroup cgroup = cgroup_new_cgroup("mycontainer"); if (!cgroup) return 1; // 添加CPU控制器 cgc = cgroup_add_controller(cgroup, "cpu"); cgroup_controller_set_uint64(cgc, "cpu.cfs_quota_us", 50000); cgroup_controller_set_uint64(cgc, "cpu.cfs_period_us", 100000); // 添加内存控制器 cgc = cgroup_add_controller(cgroup, "memory"); cgroup_controller_set_uint64(cgc, "memory.limit_in_bytes", 512 * 1024 * 1024); // 创建cgroup ret = cgroup_create_cgroup(cgroup, 0); if (ret) { fprintf(stderr, "Failed to create cgroup\n"); return 1; } // 添加进程 ret = cgroup_add_task(cgroup, getpid()); // 清理 cgroup_free(cgroup); return 0; }Union文件系统
1. OverlayFS
OverlayFS将多个目录合并成单一的视图。
overlay ├── lower (只读) │ └── usr/ │ └── bin/ ├── upper (读写) │ └── var/ └── work (工作目录)2. OverlayFS挂载
# 创建目录 mkdir -p /mnt/overlay/{lower,upper,work,mnt} # lower层(镜像内容) mount -t overlay overlay -o lowerdir=/var/lib/docker/overlay2/lower,\ upperdir=/var/lib/docker/overlay2/upper,\ workdir=/var/lib/docker/overlay2/work \ /mnt/overlay/mnt3. OverlayFS在内核中
#include <linux/overlayfs.h> // OverlayFS通过VFS实现,不需要额外的系统调用 // 容器使用overlay存储驱动时,Docker自动处理挂载容器网络
1. 网络模型
- bridge:默认网络模式
- host:共享主机网络
- overlay:跨主机网络
- macvlan:为容器分配MAC地址
- none:禁用网络
2. Bridge网络
# 创建网桥 ip link add br0 type bridge ip addr add 172.17.0.1/16 dev br0 ip link set br0 up # 创建veth对 ip link add veth0 type veth peer name veth1 # 一端连接到网桥 ip link set veth0 master br0 ip link set veth0 up # 另一端移到容器namespace ip link set veth1 netns <container_pid> ip netns exec <container_pid> ip addr add 172.17.0.2/16 dev veth1 ip netns exec <container_pid> ip link set veth1 up3. iptables规则
# NAT规则 iptables -t nat -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE # 端口映射 iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80 # 过滤规则 iptables -A FORWARD -i docker0 -o docker0 -j ACCEPT容器存储
1. 存储驱动
- overlay2:当前推荐,性能好
- aufs:早期Docker使用
- devicemapper:块级存储
- btrfs:文件系统级存储
- zfs:高级存储功能
2. 数据卷
# 创建volume docker volume create myvolume # 挂载volume docker run -v myvolume:/data debian # 绑定挂载主机目录 docker run -v /host/path:/container/path debian # tmpfs挂载 docker run --tmpfs /run debian3. 存储驱动切换
# 查看当前存储驱动 docker info | grep "Storage Driver" # 修改存储驱动(编辑/etc/docker/daemon.json) { "storage-driver": "overlay2" }容器安全
1. Seccomp
# Docker默认seccomp配置 docker run --security-opt seccomp=default.json debian # 自定义seccomp docker run --security-opt seccomp=/path/to/profile.json debian # 无seccomp限制 docker run --security-opt seccomp=unconfined debian2. Capabilities限制
# 查看默认能力 docker run --rm debian cat /proc/self/status | grep Cap # 删除能力 docker run --cap-drop=NET_RAW debian # 添加能力 docker run --cap-add=NET_ADMIN debian # 完全删除能力 docker run --privileged debian3. AppArmor/SELinux
# AppArmor docker run --security-opt "apparmor=profile" debian # SELinux docker run --security-opt "label=type:container_file_t" debian # 禁用安全标签 docker run --security-opt "label=disable" debianrunc容器运行
1. runc概述
runc是OCI容器运行时的参考实现。
# 创建spec runc spec # 运行容器 runc run mycontainer # 列出容器 runc list # 停止容器 runc kill mycontainer # 删除容器 runc delete mycontainer2. spec文件
{ "ociVersion": "1.0.2", "process": { "terminal": true, "user": { "uid": 0, "gid": 0 }, "args": ["/bin/bash"], "cwd": "/" }, "root": { "path": "rootfs", "readonly": true }, "hostname": "container", "mounts": [ { "destination": "/proc", "type": "proc", "source": "proc" } ], "linux": { "namespaces": [ {"type": "pid"}, {"type": "network"}, {"type": "ipc"}, {"type": "uts"}, {"type": "mount"} ] } }容器编排
1. Kubernetes架构
- Control Plane:
- kube-apiserver
- etcd
- kube-controller-manager
- kube-scheduler
- Node:
- kubelet
- kube-proxy
- container runtime
2. Pod
Pod是Kubernetes最小的调度单位。
apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx:1.21 ports: - containerPort: 80 resources: limits: memory: "128Mi" cpu: "500m" requests: memory: "64Mi" cpu: "250m"3. Deployment
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.21 ports: - containerPort: 80结论
Linux容器技术通过namespace实现资源隔离,通过cgroups实现资源限制,通过overlayfs实现高效的镜像管理。这些内核机制共同构成了现代容器技术的基础。理解这些底层原理对于容器开发、运维和安全加固都有重要意义。