HiHuo
首页
博客
手册
工具
首页
博客
手册
工具
  • 手撸容器系统

    • 完整手撸容器技术文档系列
    • 01-容器本质与基础概念
    • 02-Namespace隔离机制
    • 03-CGroup资源控制
    • 04-Capabilities与安全机制
    • 05-容器网络原理
    • 06-网络模式与实现
    • 07-CNI插件开发
    • 08-RootFS与文件系统隔离
    • 09-OverlayFS镜像分层
    • 10-命令行手撸容器
    • 11-Go实现最小容器
    • 12-Go实现完整容器
    • 13-容器生命周期管理
    • 14-调试技术与工具
    • 15-OCI规范与标准化
    • 16-进阶场景与优化
    • 常见问题与故障排查
    • 参考资料与延伸阅读

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 语言实现最小容器,这是代码实践的开始!

Prev
09-OverlayFS镜像分层
Next
11-Go实现最小容器