10-命令行手撸容器
学习目标
- 通过命令行手动创建完整的容器环境
- 掌握容器创建的核心步骤和原理
- 能够配置网络、存储和资源限制
- 理解容器运行时的完整流程
- 掌握容器调试和故障排查
前置知识
- Linux 系统管理基础
- 网络配置基础
- 文件系统操作
- 进程管理基础
一、环境准备
1.1 系统要求
# 检查系统版本
cat /etc/os-release
# 检查内核版本
uname -r
# 需要 3.10+ 支持 namespaces 和 cgroups
# 检查必要工具
which ip iptables brctl
# 如果没有,安装:
# Ubuntu/Debian: apt-get install iproute2 iptables bridge-utils
# CentOS/RHEL: yum install iproute iptables bridge-utils
1.2 权限检查
# 检查当前用户权限
id
# 需要 root 权限或 sudo 权限
# 检查 cgroup 支持
ls /sys/fs/cgroup/
# 应该看到 cgroup 目录
# 检查 namespace 支持
ls /proc/self/ns/
# 应该看到各种 namespace 文件
1.3 网络准备
# 启用 IP 转发
echo 1 > /proc/sys/net/ipv4/ip_forward
# 永久启用 IP 转发
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
sysctl -p
️ 二、创建基础容器环境
2.1 准备 RootFS
#!/bin/bash
# 准备容器 RootFS
ROOTFS="/tmp/container-rootfs"
echo "=== 准备 RootFS: $ROOTFS ==="
# 1. 创建目录结构
mkdir -p $ROOTFS/{bin,usr,etc,proc,sys,dev,tmp,var,home,root}
# 2. 使用 busybox 创建最小系统
if [ ! -f "$ROOTFS/busybox" ]; then
echo "下载 busybox..."
wget -q https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64 -O $ROOTFS/busybox
chmod +x $ROOTFS/busybox
fi
# 3. 安装 busybox 命令
cd $ROOTFS
./busybox --install -s .
# 4. 创建必要文件
echo "创建系统文件..."
# 创建 passwd 文件
cat > etc/passwd << 'EOF'
root:x:0:0:root:/root:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/false
EOF
# 创建 group 文件
cat > etc/group << 'EOF'
root:x:0:
nogroup:x:65534:
EOF
# 创建主机名文件
echo "container" > etc/hostname
# 创建 hosts 文件
cat > etc/hosts << 'EOF'
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
EOF
# 创建 resolv.conf
cat > etc/resolv.conf << 'EOF'
nameserver 8.8.8.8
nameserver 8.8.4.4
EOF
# 5. 设置权限
chmod 644 etc/passwd etc/group etc/hostname etc/hosts etc/resolv.conf
chmod 755 bin usr etc proc sys dev tmp var home root
chmod 1777 tmp
echo "RootFS 准备完成"
2.2 创建网络环境
#!/bin/bash
# 创建容器网络环境
echo "=== 创建网络环境 ==="
# 1. 创建 Bridge
BRIDGE_NAME="cni0"
BRIDGE_IP="10.22.0.1/24"
# 检查 Bridge 是否已存在
if ! ip link show $BRIDGE_NAME >/dev/null 2>&1; then
echo "创建 Bridge: $BRIDGE_NAME"
ip link add name $BRIDGE_NAME type bridge
ip addr add $BRIDGE_IP dev $BRIDGE_NAME
ip link set $BRIDGE_NAME up
else
echo "Bridge $BRIDGE_NAME 已存在"
fi
# 2. 配置 NAT
echo "配置 NAT 规则..."
iptables -t nat -C POSTROUTING -s 10.22.0.0/24 -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s 10.22.0.0/24 -j MASQUERADE
# 3. 配置 FORWARD 规则
iptables -C FORWARD -i $BRIDGE_NAME -j ACCEPT 2>/dev/null || \
iptables -A FORWARD -i $BRIDGE_NAME -j ACCEPT
iptables -C FORWARD -o $BRIDGE_NAME -j ACCEPT 2>/dev/null || \
iptables -A FORWARD -o $BRIDGE_NAME -j ACCEPT
echo "网络环境创建完成"
echo "Bridge: $BRIDGE_NAME ($BRIDGE_IP)"
2.3 创建 veth pair
#!/bin/bash
# 创建 veth pair 并配置网络
CONTAINER_ID="container-$(date +%s)"
VETH_HOST="veth-${CONTAINER_ID: -6}"
VETH_CONT="eth0"
CONTAINER_IP="10.22.0.$(($(date +%s) % 200 + 2))"
echo "=== 创建 veth pair ==="
echo "容器 ID: $CONTAINER_ID"
echo "宿主机接口: $VETH_HOST"
echo "容器接口: $VETH_CONT"
echo "容器 IP: $CONTAINER_IP"
# 1. 创建 veth pair
ip link add $VETH_HOST type veth peer name $VETH_CONT
# 2. 将宿主机端连接到 Bridge
ip link set $VETH_HOST master $BRIDGE_NAME
ip link set $VETH_HOST up
# 3. 创建 Network Namespace
ip netns add $CONTAINER_ID
# 4. 将容器端移到 Network Namespace
ip link set $VETH_CONT netns $CONTAINER_ID
# 5. 在 Network Namespace 中配置网络
ip netns exec $CONTAINER_ID ip link set lo up
ip netns exec $CONTAINER_ID ip addr add $CONTAINER_IP/24 dev $VETH_CONT
ip netns exec $CONTAINER_ID ip link set $VETH_CONT up
ip netns exec $CONTAINER_ID ip route add default via 10.22.0.1
echo "veth pair 创建完成"
echo "容器 IP: $CONTAINER_IP"
三、创建完整容器
3.1 综合脚本
#!/bin/bash
# 创建完整的容器环境
set -e
# 配置参数
CONTAINER_ID="container-$(date +%s)"
ROOTFS="/tmp/container-rootfs"
BRIDGE_NAME="cni0"
BRIDGE_IP="10.22.0.1/24"
VETH_HOST="veth-${CONTAINER_ID: -6}"
VETH_CONT="eth0"
CONTAINER_IP="10.22.0.$(($(date +%s) % 200 + 2))"
echo "=== 创建容器: $CONTAINER_ID ==="
# 1. 准备 RootFS
echo "1. 准备 RootFS..."
if [ ! -d "$ROOTFS" ]; then
# 创建目录结构
mkdir -p $ROOTFS/{bin,usr,etc,proc,sys,dev,tmp,var,home,root}
# 下载并安装 busybox
if [ ! -f "$ROOTFS/busybox" ]; then
wget -q https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64 -O $ROOTFS/busybox
chmod +x $ROOTFS/busybox
fi
cd $ROOTFS
./busybox --install -s .
# 创建系统文件
cat > etc/passwd << 'EOF'
root:x:0:0:root:/root:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/false
EOF
cat > etc/group << 'EOF'
root:x:0:
nogroup:x:65534:
EOF
echo "container" > etc/hostname
echo "127.0.0.1 localhost" > etc/hosts
echo "nameserver 8.8.8.8" > etc/resolv.conf
chmod 644 etc/passwd etc/group etc/hostname etc/hosts etc/resolv.conf
chmod 755 bin usr etc proc sys dev tmp var home root
chmod 1777 tmp
fi
# 2. 创建网络环境
echo "2. 创建网络环境..."
# 创建 Bridge
if ! ip link show $BRIDGE_NAME >/dev/null 2>&1; then
ip link add name $BRIDGE_NAME type bridge
ip addr add $BRIDGE_IP dev $BRIDGE_NAME
ip link set $BRIDGE_NAME up
fi
# 配置 NAT
iptables -t nat -C POSTROUTING -s 10.22.0.0/24 -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s 10.22.0.0/24 -j MASQUERADE
# 配置 FORWARD
iptables -C FORWARD -i $BRIDGE_NAME -j ACCEPT 2>/dev/null || \
iptables -A FORWARD -i $BRIDGE_NAME -j ACCEPT
iptables -C FORWARD -o $BRIDGE_NAME -j ACCEPT 2>/dev/null || \
iptables -A FORWARD -o $BRIDGE_NAME -j ACCEPT
# 3. 创建 veth pair
echo "3. 创建 veth pair..."
ip link add $VETH_HOST type veth peer name $VETH_CONT
ip link set $VETH_HOST master $BRIDGE_NAME
ip link set $VETH_HOST up
# 4. 创建 Network Namespace
echo "4. 创建 Network Namespace..."
ip netns add $CONTAINER_ID
# 5. 配置容器网络
echo "5. 配置容器网络..."
ip link set $VETH_CONT netns $CONTAINER_ID
ip netns exec $CONTAINER_ID ip link set lo up
ip netns exec $CONTAINER_ID ip addr add $CONTAINER_IP/24 dev $VETH_CONT
ip netns exec $CONTAINER_ID ip link set $VETH_CONT up
ip netns exec $CONTAINER_ID ip route add default via 10.22.0.1
# 6. 创建 cgroup
echo "6. 创建 cgroup..."
CGROUP_DIR="/sys/fs/cgroup/$CONTAINER_ID"
mkdir -p $CGROUP_DIR
# 设置资源限制
echo 128M > $CGROUP_DIR/memory.max
echo 50000 100000 > $CGROUP_DIR/cpu.max
echo 100 > $CGROUP_DIR/pids.max
# 7. 启动容器
echo "7. 启动容器..."
echo "容器 ID: $CONTAINER_ID"
echo "容器 IP: $CONTAINER_IP"
echo "RootFS: $ROOTFS"
echo ""
# 进入容器
ip netns exec $CONTAINER_ID \
unshare -U -p -m -i -C -f \
--mount-proc \
chroot $ROOTFS /bin/sh << 'EOF'
echo "=== 进入容器环境 ==="
echo "主机名: $(hostname)"
echo "进程 ID: $$"
echo "IP 地址: $(ip addr show eth0 | grep inet)"
echo "挂载点:"
mount | head -5
echo "文件系统:"
ls -la /
echo ""
echo "容器启动完成!"
echo "输入 'exit' 退出容器"
EOF
# 8. 清理
echo "8. 清理资源..."
ip netns del $CONTAINER_ID
ip link del $VETH_HOST 2>/dev/null || true
rm -rf $CGROUP_DIR
echo "容器清理完成"
3.2 分步执行
3.2.1 步骤1:准备环境
# 1. 准备 RootFS
./prepare-rootfs.sh
# 2. 创建网络环境
./create-network.sh
# 3. 创建 veth pair
./create-veth.sh
3.2.2 步骤2:启动容器
# 1. 创建所有 namespace
unshare -U -p -m -n -i -C -f bash << 'EOF'
echo "=== 进入隔离环境 ==="
# 2. 重新挂载 /proc
mount -t proc proc /proc
# 3. 切换根目录
mount --bind /tmp/container-rootfs /tmp/container-rootfs
cd /tmp/container-rootfs
mkdir -p oldroot
pivot_root . oldroot
umount -l /oldroot
# 4. 重新挂载特殊文件系统
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
# 5. 设置主机名
hostname container
# 6. 启动容器
exec /bin/sh
EOF
四、容器调试
4.1 网络调试
# 1. 查看网络接口
ip netns exec $CONTAINER_ID ip addr show
# 2. 查看路由表
ip netns exec $CONTAINER_ID ip route show
# 3. 测试连通性
ip netns exec $CONTAINER_ID ping -c 3 8.8.8.8
# 4. 查看 Bridge 状态
brctl show $BRIDGE_NAME
# 5. 查看 NAT 规则
iptables -t nat -L POSTROUTING
4.2 文件系统调试
# 1. 查看挂载点
ip netns exec $CONTAINER_ID mount
# 2. 查看文件系统
ip netns exec $CONTAINER_ID ls -la /
# 3. 查看进程信息
ip netns exec $CONTAINER_ID ps aux
# 4. 查看系统信息
ip netns exec $CONTAINER_ID cat /proc/version
ip netns exec $CONTAINER_ID cat /proc/cpuinfo
4.3 资源限制调试
# 1. 查看 cgroup 配置
cat /sys/fs/cgroup/$CONTAINER_ID/memory.max
cat /sys/fs/cgroup/$CONTAINER_ID/cpu.max
cat /sys/fs/cgroup/$CONTAINER_ID/pids.max
# 2. 查看资源使用情况
cat /sys/fs/cgroup/$CONTAINER_ID/memory.current
cat /sys/fs/cgroup/$CONTAINER_ID/cpu.stat
cat /sys/fs/cgroup/$CONTAINER_ID/pids.current
# 3. 测试资源限制
# 在容器中运行内存密集型任务
ip netns exec $CONTAINER_ID dd if=/dev/zero of=/tmp/test bs=1M count=200
五、容器测试
5.1 基础功能测试
#!/bin/bash
# 容器基础功能测试
echo "=== 容器基础功能测试 ==="
# 1. 测试网络连通性
echo "1. 测试网络连通性..."
ip netns exec $CONTAINER_ID ping -c 3 8.8.8.8
if [ $? -eq 0 ]; then
echo " 网络连通性测试通过"
else
echo " 网络连通性测试失败"
fi
# 2. 测试文件系统
echo "2. 测试文件系统..."
ip netns exec $CONTAINER_ID ls -la /
if [ $? -eq 0 ]; then
echo " 文件系统测试通过"
else
echo " 文件系统测试失败"
fi
# 3. 测试进程隔离
echo "3. 测试进程隔离..."
CONTAINER_PID=$(ip netns exec $CONTAINER_ID ps aux | grep -v grep | wc -l)
HOST_PID=$(ps aux | grep -v grep | wc -l)
if [ $CONTAINER_PID -lt $HOST_PID ]; then
echo " 进程隔离测试通过"
else
echo " 进程隔离测试失败"
fi
# 4. 测试主机名隔离
echo "4. 测试主机名隔离..."
CONTAINER_HOSTNAME=$(ip netns exec $CONTAINER_ID hostname)
if [ "$CONTAINER_HOSTNAME" = "container" ]; then
echo " 主机名隔离测试通过"
else
echo " 主机名隔离测试失败"
fi
# 5. 测试资源限制
echo "5. 测试资源限制..."
MEMORY_LIMIT=$(cat /sys/fs/cgroup/$CONTAINER_ID/memory.max)
if [ "$MEMORY_LIMIT" = "134217728" ]; then
echo " 内存限制测试通过"
else
echo " 内存限制测试失败"
fi
echo "=== 测试完成 ==="
5.2 性能测试
#!/bin/bash
# 容器性能测试
echo "=== 容器性能测试 ==="
# 1. 网络性能测试
echo "1. 网络性能测试..."
ip netns exec $CONTAINER_ID iperf3 -s &
IPERF_PID=$!
sleep 2
iperf3 -c $CONTAINER_IP -t 10
kill $IPERF_PID 2>/dev/null
# 2. 文件系统性能测试
echo "2. 文件系统性能测试..."
ip netns exec $CONTAINER_ID dd if=/dev/zero of=/tmp/test bs=1M count=100
ip netns exec $CONTAINER_ID rm /tmp/test
# 3. CPU 性能测试
echo "3. CPU 性能测试..."
ip netns exec $CONTAINER_ID time dd if=/dev/zero of=/dev/null bs=1M count=1000
echo "=== 性能测试完成 ==="
六、容器清理
6.1 清理脚本
#!/bin/bash
# 清理容器环境
echo "=== 清理容器环境 ==="
# 1. 清理 Network Namespace
echo "1. 清理 Network Namespace..."
for ns in $(ip netns list | grep container-); do
echo "删除 namespace: $ns"
ip netns del $ns
done
# 2. 清理 veth 接口
echo "2. 清理 veth 接口..."
for veth in $(ip link show | grep veth- | cut -d: -f2 | tr -d ' '); do
echo "删除 veth: $veth"
ip link del $veth 2>/dev/null || true
done
# 3. 清理 Bridge
echo "3. 清理 Bridge..."
if ip link show cni0 >/dev/null 2>&1; then
echo "删除 Bridge: cni0"
ip link del cni0
fi
# 4. 清理 iptables 规则
echo "4. 清理 iptables 规则..."
iptables -t nat -D POSTROUTING -s 10.22.0.0/24 -j MASQUERADE 2>/dev/null || true
iptables -D FORWARD -i cni0 -j ACCEPT 2>/dev/null || true
iptables -D FORWARD -o cni0 -j ACCEPT 2>/dev/null || true
# 5. 清理 cgroup
echo "5. 清理 cgroup..."
for cg in /sys/fs/cgroup/container-*; do
if [ -d "$cg" ]; then
echo "删除 cgroup: $cg"
rmdir $cg 2>/dev/null || true
fi
done
# 6. 清理 RootFS
echo "6. 清理 RootFS..."
if [ -d "/tmp/container-rootfs" ]; then
echo "删除 RootFS: /tmp/container-rootfs"
rm -rf /tmp/container-rootfs
fi
echo "=== 清理完成 ==="
七、验证检查清单
基础功能
- [ ] 能够手动创建 RootFS
- [ ] 能够配置容器网络
- [ ] 能够创建 veth pair
- [ ] 能够配置资源限制
容器运行
- [ ] 能够启动完整容器
- [ ] 能够验证网络连通性
- [ ] 能够验证文件系统隔离
- [ ] 能够验证进程隔离
调试技能
- [ ] 能够调试网络问题
- [ ] 能够调试文件系统问题
- [ ] 能够调试资源限制问题
- [ ] 能够进行性能测试
清理维护
- [ ] 能够清理容器资源
- [ ] 能够清理网络配置
- [ ] 能够清理存储资源
- [ ] 能够进行环境重置
相关链接
- 02-Namespace隔离机制 - 进程隔离技术
- 03-CGroup资源控制 - 资源限制技术
- 05-容器网络原理 - 网络基础原理
- 08-RootFS与文件系统隔离 - 文件系统隔离技术
下一步:让我们学习 Go 语言实现最小容器,这是代码实践的开始!