从零实现一个容器运行时:Docker的核心设计

📅 2026/7/7 4:56:48
从零实现一个容器运行时:Docker的核心设计
前言你有没有想过Docker是怎么做到让一个程序看起来像在独立系统里运行的隔离是怎么实现的Docker的核心技术是 Namespace Cgroups 联合文件系统。今天我们用C语言从零实现一个容器运行时的核心功能· Namespace隔离PID、NET、IPC、UTS、Mount· Cgroups资源限制CPU、内存· Rootfs切换chroot/pivot_root· 容器生命周期管理· 镜像分层联合文件系统---一、容器运行时核心原理1. 容器技术栈┌─────────────────────────────────────────────────────────────┐│ 容器 ││ ┌─────────────────────────────────────────────────────┐ ││ │ Namespace隔离 │ ││ │ PID | NET | IPC | UTS | Mount | User │ ││ └─────────────────────────────────────────────────────┘ ││ ┌─────────────────────────────────────────────────────┐ ││ │ Cgroups资源限制 │ ││ │ CPU | Memory | Disk I/O | Network I/O │ ││ └─────────────────────────────────────────────────────┘ ││ ┌─────────────────────────────────────────────────────┐ ││ │ Rootfs (chroot/pivot_root) │ ││ │ /bin /etc /usr /var /lib ... │ ││ └─────────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────────────┘2. 核心概念概念 说明Namespace 资源隔离让容器看到独立的PID、网络等Cgroups 资源限制控制CPU、内存使用上限Rootfs 容器的根文件系统联合文件系统 镜像分层存储OverlayFSrunc 容器运行时实际执行者---二、完整代码实现1. 基础数据结构c#include stdio.h#include stdlib.h#include string.h#include unistd.h#include pthread.h#include sys/types.h#include sys/wait.h#include sys/mount.h#include sys/stat.h#include sys/prctl.h#include sched.h#include signal.h#include errno.h#include fcntl.h#include dirent.h#include grp.h#include linux/capability.h#define MAX_CONTAINERS 100#define MAX_CMD_LEN 256#define MAX_ROOTFS_LEN 512#define STACK_SIZE (1024 * 1024)// 容器状态typedef enum {CONTAINER_CREATED 0,CONTAINER_RUNNING,CONTAINER_PAUSED,CONTAINER_STOPPED,CONTAINER_EXITED} container_state_t;// 容器配置typedef struct container_config {char name[64];char rootfs[MAX_ROOTFS_LEN];char cmd[MAX_CMD_LEN];char **argv;int argc;// 资源限制int cpu_limit; // CPU核心数int memory_limit_mb; // 内存限制(MB)// 隔离配置int isolate_pid;int isolate_net;int isolate_ipc;int isolate_uts;int isolate_mount;int isolate_user;// 网络配置char veth_host[32];char veth_container[32];char bridge[32];char hostname[64];} container_config_t;// 容器实例typedef struct container {char id[64];container_config_t config;container_state_t state;pid_t pid;int pty_master;int pty_slave;time_t start_time;time_t stop_time;int exit_code;struct container *next;} container_t;// 容器运行时typedef struct container_runtime {container_t *containers;int container_count;char data_dir[256];pthread_mutex_t mutex;int running;} container_runtime_t;2. Namespace隔离c// 创建Namespace标志#define NAMESPACE_FLAGS \(CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWIPC | \CLONE_NEWUTS | CLONE_NEWNS | CLONE_NEWUSER)// 容器进程入口int container_main(void *arg) {container_config_t *config (container_config_t*)arg;// 设置hostnameif (config-hostname[0]) {sethostname(config-hostname, strlen(config-hostname));}// 挂载proc文件系统mount(proc, /proc, proc, 0, NULL);mount(sysfs, /sys, sysfs, 0, NULL);mount(tmpfs, /dev, tmpfs, 0, NULL);mount(devpts, /dev/pts, devpts, 0, NULL);// 切换根目录if (chdir(config-rootfs) ! 0) {perror(chdir);return 1;}if (chroot(config-rootfs) ! 0) {perror(chroot);return 1;}// 执行容器命令execvp(config-argv[0], config-argv);perror(execvp);return 1;}// 创建容器进程pid_t create_container_process(container_config_t *config) {char *stack malloc(STACK_SIZE);if (!stack) return -1;// 创建子进程在隔离环境中pid_t pid clone(container_main, stack STACK_SIZE,SIGCHLD | NAMESPACE_FLAGS, config);if (pid 0) {perror(clone);free(stack);return -1;}free(stack);return pid;}3. Cgroups资源限制c// Cgroup路径void cgroup_path(char *path, const char *controller, const char *name) {snprintf(path, 256, /sys/fs/cgroup/%s/%s, controller, name);}// 创建Cgroupint cgroup_create(const char *name) {char path[256];char controllers[][16] {cpu, memory, pids};for (int i 0; i 3; i) {cgroup_path(path, controllers[i], name);if (mkdir(path, 0755) 0 errno ! EEXIST) {perror(mkdir cgroup);return -1;}}return 0;}// 设置CPU限制int cgroup_set_cpu(const char *name, int cpu_limit) {char path[256];cgroup_path(path, cpu, name);char filepath[512];snprintf(filepath, sizeof(filepath), %s/cpu.cfs_quota_us, path);FILE *fp fopen(filepath, w);if (!fp) return -1;// 每个CPU核心100000us乘以核心数fprintf(fp, %d, cpu_limit * 100000);fclose(fp);// 设置周期snprintf(filepath, sizeof(filepath), %s/cpu.cfs_period_us, path);fp fopen(filepath, w);if (fp) {fprintf(fp, 100000);fclose(fp);}return 0;}// 设置内存限制int cgroup_set_memory(const char *name, int memory_limit_mb) {char path[256];cgroup_path(path, memory, name);char filepath[512];snprintf(filepath, sizeof(filepath), %s/memory.limit_in_bytes, path);FILE *fp fopen(filepath, w);if (!fp) return -1;fprintf(fp, %d, memory_limit_mb * 1024 * 1024);fclose(fp);return 0;}// 添加进程到Cgroupint cgroup_add_process(const char *name, pid_t pid) {char controllers[][16] {cpu, memory, pids};for (int i 0; i 3; i) {char path[256], filepath[512];cgroup_path(path, controllers[i], name);snprintf(filepath, sizeof(filepath), %s/cgroup.procs, path);FILE *fp fopen(filepath, w);if (!fp) return -1;fprintf(fp, %d, pid);fclose(fp);}return 0;}4. 容器管理c// 创建容器运行时container_runtime_t *runtime_create(const char *data_dir) {container_runtime_t *r malloc(sizeof(container_runtime_t));memset(r, 0, sizeof(container_runtime_t));strcpy(r-data_dir, data_dir);r-running 1;pthread_mutex_init(r-mutex, NULL);mkdir(data_dir, 0755);printf([运行时] 初始化完成数据目录: %s\n, data_dir);return r;}// 生成容器IDvoid generate_container_id(char *buf) {snprintf(buf, 64, %d-%ld, getpid(), time(NULL));}// 创建容器container_t *runtime_create_container(container_runtime_t *r,container_config_t *config) {pthread_mutex_lock(r-mutex);container_t *c malloc(sizeof(container_t));generate_container_id(c-id);memcpy(c-config, config, sizeof(container_config_t));c-state CONTAINER_CREATED;c-pid 0;c-start_time 0;c-stop_time 0;c-exit_code 0;c-next r-containers;r-containers c;r-container_count;pthread_mutex_unlock(r-mutex);printf([容器] 创建: %s (%s)\n, c-id, config-name);return c;}// 启动容器int runtime_start_container(container_runtime_t *r, const char *id) {pthread_mutex_lock(r-mutex);container_t *c r-containers;while (c) {if (strcmp(c-id, id) 0) break;c c-next;}if (!c) {pthread_mutex_unlock(r-mutex);return -1;}if (c-state CONTAINER_RUNNING) {pthread_mutex_unlock(r-mutex);return -2;}// 创建Cgroupcgroup_create(c-id);if (c-config.cpu_limit 0) {cgroup_set_cpu(c-id, c-config.cpu_limit);}if (c-config.memory_limit_mb 0) {cgroup_set_memory(c-id, c-config.memory_limit_mb);}// 创建容器进程pid_t pid create_container_process(c-config);if (pid 0) {pthread_mutex_unlock(r-mutex);return -3;}// 添加到Cgroupcgroup_add_process(c-id, pid);c-pid pid;c-state CONTAINER_RUNNING;c-start_time time(NULL);pthread_mutex_unlock(r-mutex);printf([容器] 启动: %s (PID: %d)\n, c-id, pid);return 0;}// 停止容器int runtime_stop_container(container_runtime_t *r, const char *id, int signal) {pthread_mutex_lock(r-mutex);container_t *c r-containers;while (c) {if (strcmp(c-id, id) 0) break;c c-next;}if (!c) {pthread_mutex_unlock(r-mutex);return -1;}if (c-state ! CONTAINER_RUNNING) {pthread_mutex_unlock(r-mutex);return -2;}if (kill(c-pid, signal) 0) {pthread_mutex_unlock(r-mutex);return -3;}c-state CONTAINER_STOPPED;c-stop_time time(NULL);pthread_mutex_unlock(r-mutex);printf([容器] 停止: %s (信号: %d)\n, c-id, signal);return 0;}// 删除容器int runtime_remove_container(container_runtime_t *r, const char *id) {pthread_mutex_lock(r-mutex);container_t *c r-containers;container_t *prev NULL;while (c) {if (strcmp(c-id, id) 0) break;prev c;c c-next;}if (!c) {pthread_mutex_unlock(r-mutex);return -1;}if (c-state CONTAINER_RUNNING) {pthread_mutex_unlock(r-mutex);return -2;}if (prev) {prev-next c-next;} else {r-containers c-next;}r-container_count--;free(c);pthread_mutex_unlock(r-mutex);printf([容器] 删除: %s\n, id);return 0;}5. 联合文件系统OverlayFS模拟c// 联合挂载模拟OverlayFSint setup_overlay_rootfs(const char *lower_dir, const char *upper_dir,const char *work_dir, const char *merged_dir) {// 创建目录mkdir(upper_dir, 0755);mkdir(work_dir, 0755);mkdir(merged_dir, 0755);char options[512];snprintf(options, sizeof(options),lowerdir%s,upperdir%s,workdir%s,lower_dir, upper_dir, work_dir);if (mount(overlay, merged_dir, overlay, 0, options) 0) {perror(mount overlay);return -1;}printf([Overlay] 挂载: %s %s - %s\n, lower_dir, upper_dir, merged_dir);return 0;}// 镜像拉取模拟int pull_image(const char *image_name, const char *target_dir) {char path[256];snprintf(path, sizeof(path), %s/%s, target_dir, image_name);mkdir(path, 0755);// 创建基本rootfs结构mkdir(path, 0755);mkdir(path, 0755);mkdir(path, 0755);mkdir(path, 0755);mkdir(path, 0755);// 创建基本的/etc/hostschar hosts_path[512];snprintf(hosts_path, sizeof(hosts_path), %s/etc/hosts, path);FILE *fp fopen(hosts_path, w);if (fp) {fprintf(fp, 127.0.0.1 localhost\n);fclose(fp);}printf([镜像] 拉取: %s - %s\n, image_name, path);return 0;}6. 测试代码cvoid test_runtime() {printf( 容器运行时测试 \n\n);container_runtime_t *r runtime_create(./containers);// 配置容器container_config_t config;memset(config, 0, sizeof(config));strcpy(config.name, test-container);strcpy(config.rootfs, ./rootfs);strcpy(config.cmd, /bin/sh);config.argv malloc(sizeof(char*) * 3);config.argv[0] /bin/sh;config.argv[1] -c;config.argv[2] echo Hello from container! sleep 10;config.argc 3;config.cpu_limit 1;config.memory_limit_mb 256;strcpy(config.hostname, container);// 创建容器container_t *c runtime_create_container(r, config);// 启动容器runtime_start_container(r, c-id);sleep(2);// 显示状态printf(\n 容器状态 \n);printf(ID: %s\n, c-id);printf(状态: %d\n, c-state);printf(PID: %d\n, c-pid);// 停止容器sleep(5);runtime_stop_container(r, c-id, SIGTERM);// 删除容器runtime_remove_container(r, c-id);free(config.argv);free(r);}int main() {test_runtime();return 0;}---三、编译和运行bashgcc -o runtime runtime.c -lpthread./runtime---四、Docker vs 本实现特性 本实现 DockerNamespace隔离 ✅ ✅Cgroups限制 ✅ ✅联合文件系统 模拟 OverlayFS容器生命周期 基础 完整镜像管理 基础 完整网络管理 无 Bridge/Host/Overlay镜像仓库 无 Docker Hub---五、总结通过这篇文章你学会了· 容器的核心技术Namespace Cgroups· 进程隔离实现· 资源限制CPU/内存· 根文件系统切换· 容器生命周期管理容器运行时是云原生的基石。掌握它你就理解了Docker、Podman、CRI的底层设计。下一篇预告《从零实现一个Kubernetes调度器Pod调度与资源分配》---评论区分享一下你对容器技术的理解