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-进阶场景与优化
    • 常见问题与故障排查
    • 参考资料与延伸阅读

11-Go实现最小容器

学习目标

  • 使用 Go 语言实现一个最小容器
  • 掌握 syscall.SysProcAttr 的使用
  • 理解父子进程通信机制
  • 能够编译和运行容器程序
  • 掌握容器代码的调试技巧

前置知识

  • Go 语言基础
  • Linux 系统调用
  • 进程管理基础
  • 容器基础原理

一、项目结构

1.1 目录结构

container/
├── go.mod
├── main.go
├── container.go
├── namespace.go
├── cgroup.go
└── README.md

1.2 go.mod 文件

module container

go 1.21

require (
    golang.org/x/sys v0.15.0
)

二、核心实现

2.1 main.go - 主程序

package main

import (
    "flag"
    "fmt"
    "log"
    "os"
    "os/exec"
    "syscall"
)

func main() {
    var (
        rootfs = flag.String("rootfs", "", "Root filesystem path")
        cmd    = flag.String("cmd", "/bin/sh", "Command to run in container")
        args   = flag.String("args", "", "Command arguments")
    )
    flag.Parse()

    if *rootfs == "" {
        log.Fatal("rootfs is required")
    }

    // 检查 rootfs 是否存在
    if _, err := os.Stat(*rootfs); os.IsNotExist(err) {
        log.Fatalf("rootfs does not exist: %s", *rootfs)
    }

    // 解析命令参数
    command := *cmd
    commandArgs := []string{}
    if *args != "" {
        commandArgs = append(commandArgs, *args)
    }

    // 创建容器
    container := NewContainer(*rootfs, command, commandArgs)
    
    // 运行容器
    if err := container.Run(); err != nil {
        log.Fatalf("Failed to run container: %v", err)
    }
}

2.2 container.go - 容器核心

package main

import (
    "fmt"
    "os"
    "os/exec"
    "syscall"
    "golang.org/x/sys/unix"
)

type Container struct {
    Rootfs string
    Cmd    string
    Args   []string
}

func NewContainer(rootfs, cmd string, args []string) *Container {
    return &Container{
        Rootfs: rootfs,
        Cmd:    cmd,
        Args:   args,
    }
}

func (c *Container) Run() error {
    // 创建所有 namespace 的 flags
    flags := syscall.CLONE_NEWUTS |
             syscall.CLONE_NEWPID |
             syscall.CLONE_NEWNS |
             syscall.CLONE_NEWNET |
             syscall.CLONE_NEWIPC |
             syscall.CLONE_NEWUSER |
             syscall.CLONE_NEWCGROUP

    // 准备子进程命令
    cmd := exec.Command("/proc/self/exe", "child", c.Rootfs, c.Cmd)
    cmd.Args = append(cmd.Args, c.Args...)
    
    // 设置系统调用属性
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: flags,
        Unshareflags: syscall.CLONE_NEWNS,
    }

    // 设置标准输入输出
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    // 启动子进程
    if err := cmd.Start(); err != nil {
        return fmt.Errorf("failed to start child process: %v", err)
    }

    // 等待子进程完成
    return cmd.Wait()
}

// 子进程入口点
func runChild() {
    // 解析命令行参数
    if len(os.Args) < 4 {
        log.Fatal("child process requires rootfs, cmd, and args")
    }

    rootfs := os.Args[2]
    cmd := os.Args[3]
    args := os.Args[4:]

    // 设置容器环境
    if err := setupContainer(rootfs, cmd, args); err != nil {
        log.Fatalf("Failed to setup container: %v", err)
    }
}

func setupContainer(rootfs, cmd string, args []string) error {
    // 1. 重新挂载 /proc
    if err := unix.Mount("proc", "/proc", "proc", 0, ""); err != nil {
        return fmt.Errorf("failed to mount /proc: %v", err)
    }

    // 2. 重新挂载 /sys
    if err := unix.Mount("sysfs", "/sys", "sysfs", 0, ""); err != nil {
        return fmt.Errorf("failed to mount /sys: %v", err)
    }

    // 3. 重新挂载 /dev
    if err := unix.Mount("devtmpfs", "/dev", "devtmpfs", 0, ""); err != nil {
        return fmt.Errorf("failed to mount /dev: %v", err)
    }

    // 4. 创建必要设备文件
    if err := createDevices(); err != nil {
        return fmt.Errorf("failed to create devices: %v", err)
    }

    // 5. 切换根目录
    if err := pivotRoot(rootfs); err != nil {
        return fmt.Errorf("failed to pivot root: %v", err)
    }

    // 6. 重新挂载特殊文件系统
    if err := unix.Mount("proc", "/proc", "proc", 0, ""); err != nil {
        return fmt.Errorf("failed to remount /proc: %v", err)
    }

    if err := unix.Mount("sysfs", "/sys", "sysfs", 0, ""); err != nil {
        return fmt.Errorf("failed to remount /sys: %v", err)
    }

    if err := unix.Mount("devtmpfs", "/dev", "devtmpfs", 0, ""); err != nil {
        return fmt.Errorf("failed to remount /dev: %v", err)
    }

    // 7. 设置主机名
    if err := unix.Sethostname([]byte("container")); err != nil {
        return fmt.Errorf("failed to set hostname: %v", err)
    }

    // 8. 执行命令
    if err := syscall.Exec(cmd, append([]string{cmd}, args...), os.Environ()); err != nil {
        return fmt.Errorf("failed to exec command: %v", err)
    }

    return nil
}

func pivotRoot(newroot string) error {
    // 1. 绑定挂载 newroot
    if err := unix.Mount(newroot, newroot, "", unix.MS_BIND|unix.MS_REC, ""); err != nil {
        return fmt.Errorf("failed to bind mount newroot: %v", err)
    }

    // 2. 创建 put_old 目录
    putold := "/.oldroot"
    if err := os.MkdirAll(putold, 0700); err != nil {
        return fmt.Errorf("failed to create put_old directory: %v", err)
    }

    // 3. 执行 pivot_root
    if err := unix.PivotRoot(newroot, putold); err != nil {
        return fmt.Errorf("failed to pivot_root: %v", err)
    }

    // 4. 切换到新根目录
    if err := os.Chdir("/"); err != nil {
        return fmt.Errorf("failed to change working directory: %v", err)
    }

    // 5. 卸载原根目录
    if err := unix.Unmount(putold, unix.MNT_DETACH); err != nil {
        return fmt.Errorf("failed to unmount old root: %v", err)
    }

    // 6. 删除 put_old 目录
    if err := os.RemoveAll(putold); err != nil {
        return fmt.Errorf("failed to remove put_old directory: %v", err)
    }

    return nil
}

func createDevices() error {
    devices := []struct {
        name string
        mode uint32
        dev  int
    }{
        {"/dev/null", syscall.S_IFCHR | 0666, 0x0103},
        {"/dev/zero", syscall.S_IFCHR | 0666, 0x0105},
        {"/dev/random", syscall.S_IFCHR | 0666, 0x0108},
        {"/dev/urandom", syscall.S_IFCHR | 0666, 0x0109},
    }

    for _, device := range devices {
        if err := unix.Mknod(device.name, device.mode, device.dev); err != nil {
            return fmt.Errorf("failed to create device %s: %v", device.name, err)
        }
    }

    return nil
}

2.3 namespace.go - 命名空间管理

package main

import (
    "fmt"
    "os"
    "syscall"
    "golang.org/x/sys/unix"
)

// NamespaceConfig 命名空间配置
type NamespaceConfig struct {
    UTS   bool // 主机名隔离
    PID   bool // 进程隔离
    MNT   bool // 文件系统隔离
    NET   bool // 网络隔离
    IPC   bool // 进程间通信隔离
    USER  bool // 用户隔离
    CGROUP bool // 控制组隔离
}

// NewNamespaceConfig 创建默认命名空间配置
func NewNamespaceConfig() *NamespaceConfig {
    return &NamespaceConfig{
        UTS:    true,
        PID:    true,
        MNT:    true,
        NET:    true,
        IPC:    true,
        USER:   true,
        CGROUP: true,
    }
}

// GetCloneFlags 获取 clone 标志
func (nc *NamespaceConfig) GetCloneFlags() uintptr {
    var flags uintptr

    if nc.UTS {
        flags |= syscall.CLONE_NEWUTS
    }
    if nc.PID {
        flags |= syscall.CLONE_NEWPID
    }
    if nc.MNT {
        flags |= syscall.CLONE_NEWNS
    }
    if nc.NET {
        flags |= syscall.CLONE_NEWNET
    }
    if nc.IPC {
        flags |= syscall.CLONE_NEWIPC
    }
    if nc.USER {
        flags |= syscall.CLONE_NEWUSER
    }
    if nc.CGROUP {
        flags |= syscall.CLONE_NEWCGROUP
    }

    return flags
}

// SetupNamespaces 设置命名空间
func SetupNamespaces(config *NamespaceConfig) error {
    // 设置 UTS namespace
    if config.UTS {
        if err := unix.Sethostname([]byte("container")); err != nil {
            return fmt.Errorf("failed to set hostname: %v", err)
        }
    }

    // 设置 PID namespace
    if config.PID {
        // 重新挂载 /proc
        if err := unix.Mount("proc", "/proc", "proc", 0, ""); err != nil {
            return fmt.Errorf("failed to mount /proc: %v", err)
        }
    }

    // 设置 MNT namespace
    if config.MNT {
        // 重新挂载特殊文件系统
        if err := unix.Mount("sysfs", "/sys", "sysfs", 0, ""); err != nil {
            return fmt.Errorf("failed to mount /sys: %v", err)
        }

        if err := unix.Mount("devtmpfs", "/dev", "devtmpfs", 0, ""); err != nil {
            return fmt.Errorf("failed to mount /dev: %v", err)
        }
    }

    // 设置 NET namespace
    if config.NET {
        // 启动 lo 接口
        if err := unix.SetsockoptString(unix.AF_INET, unix.SOL_SOCKET, unix.SO_BINDTODEVICE, "lo"); err != nil {
            return fmt.Errorf("failed to bind to lo: %v", err)
        }
    }

    return nil
}

// GetCurrentNamespaces 获取当前命名空间
func GetCurrentNamespaces() (map[string]string, error) {
    namespaces := make(map[string]string)
    
    nsTypes := []string{"uts", "pid", "mnt", "net", "ipc", "user", "cgroup"}
    
    for _, nsType := range nsTypes {
        nsPath := fmt.Sprintf("/proc/self/ns/%s", nsType)
        if target, err := os.Readlink(nsPath); err == nil {
            namespaces[nsType] = target
        }
    }
    
    return namespaces, nil
}

// PrintNamespaces 打印命名空间信息
func PrintNamespaces() error {
    namespaces, err := GetCurrentNamespaces()
    if err != nil {
        return err
    }
    
    fmt.Println("=== 当前命名空间 ===")
    for nsType, nsPath := range namespaces {
        fmt.Printf("%s: %s\n", nsType, nsPath)
    }
    
    return nil
}

2.4 cgroup.go - 控制组管理

package main

import (
    "fmt"
    "os"
    "path/filepath"
    "strconv"
)

// CGroupConfig 控制组配置
type CGroupConfig struct {
    MemoryMax string // 内存限制,如 "128M"
    CPUMax    string // CPU 限制,如 "50000 100000"
    PidsMax   int    // 进程数限制
}

// NewCGroupConfig 创建默认控制组配置
func NewCGroupConfig() *CGroupConfig {
    return &CGroupConfig{
        MemoryMax: "128M",
        CPUMax:    "50000 100000",
        PidsMax:   100,
    }
}

// CGroupManager 控制组管理器
type CGroupManager struct {
    config *CGroupConfig
    path   string
}

// NewCGroupManager 创建控制组管理器
func NewCGroupManager(config *CGroupConfig, containerID string) *CGroupManager {
    return &CGroupManager{
        config: config,
        path:   filepath.Join("/sys/fs/cgroup", containerID),
    }
}

// Create 创建控制组
func (cgm *CGroupManager) Create() error {
    // 创建控制组目录
    if err := os.MkdirAll(cgm.path, 0755); err != nil {
        return fmt.Errorf("failed to create cgroup directory: %v", err)
    }

    // 设置内存限制
    if err := cgm.setMemoryLimit(); err != nil {
        return fmt.Errorf("failed to set memory limit: %v", err)
    }

    // 设置 CPU 限制
    if err := cgm.setCPULimit(); err != nil {
        return fmt.Errorf("failed to set CPU limit: %v", err)
    }

    // 设置进程数限制
    if err := cgm.setPidsLimit(); err != nil {
        return fmt.Errorf("failed to set pids limit: %v", err)
    }

    return nil
}

// AddProcess 添加进程到控制组
func (cgm *CGroupManager) AddProcess(pid int) error {
    cgroupProcsPath := filepath.Join(cgm.path, "cgroup.procs")
    return os.WriteFile(cgroupProcsPath, []byte(strconv.Itoa(pid)), 0644)
}

// Destroy 销毁控制组
func (cgm *CGroupManager) Destroy() error {
    return os.RemoveAll(cgm.path)
}

// setMemoryLimit 设置内存限制
func (cgm *CGroupManager) setMemoryLimit() error {
    memoryMaxPath := filepath.Join(cgm.path, "memory.max")
    return os.WriteFile(memoryMaxPath, []byte(cgm.config.MemoryMax), 0644)
}

// setCPULimit 设置 CPU 限制
func (cgm *CGroupManager) setCPULimit() error {
    cpuMaxPath := filepath.Join(cgm.path, "cpu.max")
    return os.WriteFile(cpuMaxPath, []byte(cgm.config.CPUMax), 0644)
}

// setPidsLimit 设置进程数限制
func (cgm *CGroupManager) setPidsLimit() error {
    pidsMaxPath := filepath.Join(cgm.path, "pids.max")
    return os.WriteFile(pidsMaxPath, []byte(strconv.Itoa(cgm.config.PidsMax)), 0644)
}

// GetStats 获取控制组统计信息
func (cgm *CGroupManager) GetStats() (map[string]string, error) {
    stats := make(map[string]string)
    
    // 读取内存使用情况
    if data, err := os.ReadFile(filepath.Join(cgm.path, "memory.current")); err == nil {
        stats["memory.current"] = string(data)
    }
    
    // 读取 CPU 使用情况
    if data, err := os.ReadFile(filepath.Join(cgm.path, "cpu.stat")); err == nil {
        stats["cpu.stat"] = string(data)
    }
    
    // 读取进程数
    if data, err := os.ReadFile(filepath.Join(cgm.path, "pids.current")); err == nil {
        stats["pids.current"] = string(data)
    }
    
    return stats, nil
}

// PrintStats 打印控制组统计信息
func (cgm *CGroupManager) PrintStats() error {
    stats, err := cgm.GetStats()
    if err != nil {
        return err
    }
    
    fmt.Println("=== 控制组统计信息 ===")
    for key, value := range stats {
        fmt.Printf("%s: %s", key, value)
    }
    
    return nil
}

三、完整实现

3.1 完整的 main.go

package main

import (
    "flag"
    "fmt"
    "log"
    "os"
    "os/exec"
    "syscall"
    "golang.org/x/sys/unix"
)

func main() {
    var (
        rootfs = flag.String("rootfs", "", "Root filesystem path")
        cmd    = flag.String("cmd", "/bin/sh", "Command to run in container")
        args   = flag.String("args", "", "Command arguments")
        memory = flag.String("memory", "128M", "Memory limit")
        cpu    = flag.String("cpu", "50000 100000", "CPU limit")
        pids   = flag.Int("pids", 100, "Process limit")
    )
    flag.Parse()

    if *rootfs == "" {
        log.Fatal("rootfs is required")
    }

    // 检查 rootfs 是否存在
    if _, err := os.Stat(*rootfs); os.IsNotExist(err) {
        log.Fatalf("rootfs does not exist: %s", *rootfs)
    }

    // 解析命令参数
    command := *cmd
    commandArgs := []string{}
    if *args != "" {
        commandArgs = append(commandArgs, *args)
    }

    // 创建容器
    container := NewContainer(*rootfs, command, commandArgs)
    
    // 设置资源限制
    container.SetResourceLimits(*memory, *cpu, *pids)
    
    // 运行容器
    if err := container.Run(); err != nil {
        log.Fatalf("Failed to run container: %v", err)
    }
}

type Container struct {
    Rootfs string
    Cmd    string
    Args   []string
    Memory string
    CPU    string
    Pids   int
}

func NewContainer(rootfs, cmd string, args []string) *Container {
    return &Container{
        Rootfs: rootfs,
        Cmd:    cmd,
        Args:   args,
    }
}

func (c *Container) SetResourceLimits(memory, cpu string, pids int) {
    c.Memory = memory
    c.CPU = cpu
    c.Pids = pids
}

func (c *Container) Run() error {
    // 创建所有 namespace 的 flags
    flags := syscall.CLONE_NEWUTS |
             syscall.CLONE_NEWPID |
             syscall.CLONE_NEWNS |
             syscall.CLONE_NEWNET |
             syscall.CLONE_NEWIPC |
             syscall.CLONE_NEWUSER |
             syscall.CLONE_NEWCGROUP

    // 准备子进程命令
    cmd := exec.Command("/proc/self/exe", "child", c.Rootfs, c.Cmd)
    cmd.Args = append(cmd.Args, c.Args...)
    
    // 设置系统调用属性
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: flags,
        Unshareflags: syscall.CLONE_NEWNS,
    }

    // 设置标准输入输出
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    // 启动子进程
    if err := cmd.Start(); err != nil {
        return fmt.Errorf("failed to start child process: %v", err)
    }

    // 创建控制组
    cgroupConfig := &CGroupConfig{
        MemoryMax: c.Memory,
        CPUMax:    c.CPU,
        PidsMax:   c.Pids,
    }
    
    cgroupManager := NewCGroupManager(cgroupConfig, fmt.Sprintf("container-%d", cmd.Process.Pid))
    if err := cgroupManager.Create(); err != nil {
        return fmt.Errorf("failed to create cgroup: %v", err)
    }
    
    // 将进程添加到控制组
    if err := cgroupManager.AddProcess(cmd.Process.Pid); err != nil {
        return fmt.Errorf("failed to add process to cgroup: %v", err)
    }

    // 等待子进程完成
    return cmd.Wait()
}

// 子进程入口点
func runChild() {
    // 解析命令行参数
    if len(os.Args) < 4 {
        log.Fatal("child process requires rootfs, cmd, and args")
    }

    rootfs := os.Args[2]
    cmd := os.Args[3]
    args := os.Args[4:]

    // 设置容器环境
    if err := setupContainer(rootfs, cmd, args); err != nil {
        log.Fatalf("Failed to setup container: %v", err)
    }
}

func setupContainer(rootfs, cmd string, args []string) error {
    // 1. 重新挂载 /proc
    if err := unix.Mount("proc", "/proc", "proc", 0, ""); err != nil {
        return fmt.Errorf("failed to mount /proc: %v", err)
    }

    // 2. 重新挂载 /sys
    if err := unix.Mount("sysfs", "/sys", "sysfs", 0, ""); err != nil {
        return fmt.Errorf("failed to mount /sys: %v", err)
    }

    // 3. 重新挂载 /dev
    if err := unix.Mount("devtmpfs", "/dev", "devtmpfs", 0, ""); err != nil {
        return fmt.Errorf("failed to mount /dev: %v", err)
    }

    // 4. 创建必要设备文件
    if err := createDevices(); err != nil {
        return fmt.Errorf("failed to create devices: %v", err)
    }

    // 5. 切换根目录
    if err := pivotRoot(rootfs); err != nil {
        return fmt.Errorf("failed to pivot root: %v", err)
    }

    // 6. 重新挂载特殊文件系统
    if err := unix.Mount("proc", "/proc", "proc", 0, ""); err != nil {
        return fmt.Errorf("failed to remount /proc: %v", err)
    }

    if err := unix.Mount("sysfs", "/sys", "sysfs", 0, ""); err != nil {
        return fmt.Errorf("failed to remount /sys: %v", err)
    }

    if err := unix.Mount("devtmpfs", "/dev", "devtmpfs", 0, ""); err != nil {
        return fmt.Errorf("failed to remount /dev: %v", err)
    }

    // 7. 设置主机名
    if err := unix.Sethostname([]byte("container")); err != nil {
        return fmt.Errorf("failed to set hostname: %v", err)
    }

    // 8. 执行命令
    if err := syscall.Exec(cmd, append([]string{cmd}, args...), os.Environ()); err != nil {
        return fmt.Errorf("failed to exec command: %v", err)
    }

    return nil
}

func pivotRoot(newroot string) error {
    // 1. 绑定挂载 newroot
    if err := unix.Mount(newroot, newroot, "", unix.MS_BIND|unix.MS_REC, ""); err != nil {
        return fmt.Errorf("failed to bind mount newroot: %v", err)
    }

    // 2. 创建 put_old 目录
    putold := "/.oldroot"
    if err := os.MkdirAll(putold, 0700); err != nil {
        return fmt.Errorf("failed to create put_old directory: %v", err)
    }

    // 3. 执行 pivot_root
    if err := unix.PivotRoot(newroot, putold); err != nil {
        return fmt.Errorf("failed to pivot_root: %v", err)
    }

    // 4. 切换到新根目录
    if err := os.Chdir("/"); err != nil {
        return fmt.Errorf("failed to change working directory: %v", err)
    }

    // 5. 卸载原根目录
    if err := unix.Unmount(putold, unix.MNT_DETACH); err != nil {
        return fmt.Errorf("failed to unmount old root: %v", err)
    }

    // 6. 删除 put_old 目录
    if err := os.RemoveAll(putold); err != nil {
        return fmt.Errorf("failed to remove put_old directory: %v", err)
    }

    return nil
}

func createDevices() error {
    devices := []struct {
        name string
        mode uint32
        dev  int
    }{
        {"/dev/null", syscall.S_IFCHR | 0666, 0x0103},
        {"/dev/zero", syscall.S_IFCHR | 0666, 0x0105},
        {"/dev/random", syscall.S_IFCHR | 0666, 0x0108},
        {"/dev/urandom", syscall.S_IFCHR | 0666, 0x0109},
    }

    for _, device := range devices {
        if err := unix.Mknod(device.name, device.mode, device.dev); err != nil {
            return fmt.Errorf("failed to create device %s: %v", device.name, err)
        }
    }

    return nil
}

四、编译和运行

4.1 编译程序

# 1. 初始化 Go 模块
go mod init container

# 2. 下载依赖
go mod tidy

# 3. 编译程序
go build -o container main.go

# 4. 检查编译结果
ls -la container

4.2 准备 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
    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 .

# 3. 创建系统文件
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

echo "RootFS 准备完成"

4.3 运行容器

# 1. 运行容器
sudo ./container -rootfs /tmp/container-rootfs -cmd /bin/sh

# 2. 运行容器并指定参数
sudo ./container -rootfs /tmp/container-rootfs -cmd /bin/ls -args "-la /"

# 3. 运行容器并设置资源限制
sudo ./container -rootfs /tmp/container-rootfs -cmd /bin/sh -memory 256M -cpu "100000 100000" -pids 50

五、调试技巧

5.1 调试模式

// 添加调试模式
var debug = flag.Bool("debug", false, "Enable debug mode")

func (c *Container) Run() error {
    if *debug {
        fmt.Printf("Starting container with rootfs: %s\n", c.Rootfs)
        fmt.Printf("Command: %s %v\n", c.Cmd, c.Args)
    }
    
    // ... 其他代码
}

5.2 日志记录

import "log"

func setupContainer(rootfs, cmd string, args []string) error {
    log.Printf("Setting up container with rootfs: %s", rootfs)
    
    // 1. 重新挂载 /proc
    log.Printf("Mounting /proc")
    if err := unix.Mount("proc", "/proc", "proc", 0, ""); err != nil {
        return fmt.Errorf("failed to mount /proc: %v", err)
    }
    
    // ... 其他代码
}

5.3 错误处理

func (c *Container) Run() error {
    // 启动子进程
    if err := cmd.Start(); err != nil {
        return fmt.Errorf("failed to start child process: %v", err)
    }
    
    // 等待子进程完成
    if err := cmd.Wait(); err != nil {
        if exitError, ok := err.(*exec.ExitError); ok {
            return fmt.Errorf("child process exited with code %d: %v", exitError.ExitCode(), err)
        }
        return fmt.Errorf("failed to wait for child process: %v", err)
    }
    
    return nil
}

六、验证检查清单

基础功能

  • [ ] 能够编译 Go 程序
  • [ ] 能够准备 RootFS
  • [ ] 能够运行容器
  • [ ] 能够验证容器隔离

代码理解

  • [ ] 理解 syscall.SysProcAttr 的使用
  • [ ] 理解父子进程通信机制
  • [ ] 理解 pivot_root 的实现
  • [ ] 理解控制组的配置

调试技能

  • [ ] 能够使用调试模式
  • [ ] 能够添加日志记录
  • [ ] 能够处理错误
  • [ ] 能够进行性能测试

相关链接

  • 10-命令行手撸容器 - 命令行实践
  • 12-Go实现完整容器 - 完整功能实现
  • 14-调试技术与工具 - 调试技术详解

下一步:让我们学习 Go 实现完整容器,这是功能完整的实现!

Prev
10-命令行手撸容器
Next
12-Go实现完整容器