为了构建一套高灵活性且高性能的容器服务,笔者近期研究了利用Gentoo Linux进行 Docker 容器云的搭建。本文从存储层次设计入手,逐步介绍 Docker 引擎安装、btrfs 存储驱动配置、NVIDIA GPU 加速支持,以及本地 Registry 镜像缓存的完整搭建过程。

Gentoo Linux 是一款优秀的Linux发行版。在众多发行版中属于比较“复杂”的风格。 造成这一印象的主要原因是其高度自定义甚至有些繁琐的手动安装过程,另一个原因是以 portage 为中心的同样高度自定义的包管理体系。

安装过程以一篇官方文档为中心展开。安装者需要根据文档提示逐渐理清需求并付诸实施。 详尽的步骤每个阶段都有明确的目标。从最初的“准备好安装介质”到后面“准备好重启系统”,一直处于一种准备好做某事(Ready to do)状态。 因此,安装者的心智负担一直处于可以承受的程度。安装过程会有挫败,又没有脱离文档的控制范围。 这是一种很好的模式,某个流行的发行版甚至会出现一种情况,比如安装后无法启动X时,用户不能立刻找到查看报错日志的方法并着手进行修复。 综合来说,安装过程的学习曲线虽然不平滑但并不陡峭。

portage 系统利用 USE 和 Keywords 等变量对庞大的软件仓库进行定制化,实现 rpm 或 deb 为中心的软件仓库难以实现的精细化管理。 真正的学习难度在于理解各类软件对 USE 标签的适配。 而 Gentoo 官方 wiki 网站提供了几乎所有常见应用场景和软件的文档。

安装手册 https://wiki.gentoo.org/wiki/Handbook:AMD64/zh-cn

下载 安装介质镜像 stage3

对了,甚至不需要专门的gentoo安装介质,你可以用其它linux发行版的liveCD完成安装。

存储层次设计

本机的容器云架构依赖一套清晰分层的存储体系,核心思路是按数据用途隔离、按工作负载选择文件系统。

所有数据位于一个 7.3T 磁盘的 LVM 卷组 meowium 上。使用 LVM 而非直接分区,是为了各存储卷可以独立在线扩容或迁移,后续增加硬盘时只需 vgextend 即可扩展任意逻辑卷。

三个逻辑卷各有专攻:

挂载点 大小 文件系统 用途 选择原因
/var/lib/docker 256G btrfs Docker 镜像和容器层 COW 子卷机制匹配分层模型,压缩节省 AI 镜像空间
/data/cache 512G ext4 registry 镜像缓存 纯顺序读写的大块存储,无 COW 开销,简单可靠
/data/models 1.0T ext4 AI 模型和数据集 大文件稳定存储,无需快照和压缩

其中 btrfs 的 COW 特性与 Docker 镜像层模型天然匹配——每个层对应一个子卷,而 zstd 压缩对动辄数 GB 的基础镜像效果明显。缓存和模型数据若放在 COW 文件系统上反而会引入写放大和碎片化,因此使用 ext4 更为合适。

安装 Docker 引擎

完成了系统的准备工作,接下来安装 Docker 容器运行时。

安装 Docker 包

Docker 在 Gentoo 中的安装以 Portage 的 USE 标志体系为核心,通过配置 USE 来选择所需的存储驱动和功能特性。

echo "=app-containers/docker-28.2.2 ~amd64" >> /etc/portage/package.accept_keywords/docker
echo "=app-containers/docker-cli-28.2.2 ~amd64" >> /etc/portage/package.accept_keywords/docker

echo "app-containers/docker btrfs overlay2 seccomp systemd cuda -device-mapper" >> /etc/portage/package.use/docker

emerge --ask app-containers/docker app-containers/docker-cli

USE 标志说明:

标志 用途
btrfs btrfs 存储驱动支持
overlay2 overlay2 存储驱动支持
seccomp 安全沙箱
systemd systemd 服务集成
cuda NVIDIA CUDA GPU 支持
-device-mapper 禁用 devicemapper(已不再推荐)

配置内核选项

Docker 依赖内核的若干特性,Gentoo 的自编译内核需要确保以下选项已启用:

CONFIG_NAMESPACES=y          # 容器隔离
CONFIG_CGROUPS=y             # 资源限制
CONFIG_CGROUP_PIDS=y         # 进程数限制
CONFIG_OVERLAY_FS=y          # overlay2 文件系统
CONFIG_BRIDGE=y              # 容器网络
CONFIG_SECCOMP=y             # 安全沙箱
CONFIG_SECCOMP_FILTER=y      # seccomp 过滤
CONFIG_BTRFS_FS=y            # btrfs 存储驱动
CONFIG_BTRFS_FS_POSIX_ACL=y  # btrfs ACL 支持

可以通过以下命令验证:

zgrep -E 'NAMESPACES|CGROUPS|CGROUP_PIDS|OVERLAY_FS|BRIDGE|SECCOMP|BTRFS_FS' /proc/config.gz

配置 btrfs 存储驱动

在存储层次设计中,我们为 Docker 单独划分了 btrfs 分区。Docker 的 btrfs 驱动将每个镜像层和容器层映射为独立的子卷(subvolume),利用 COW 实现零成本的层复制和快照。这种机制让容器启动几乎瞬时完成,同时多个容器共享相同镜像层时只占用一份磁盘空间。

/etc/conf.d/docker 中指定存储驱动和数据目录:

DOCKER_OPTS="--storage-driver btrfs --data-root /var/lib/docker"

启动后 Docker 会自动为每个层创建 btrfs 子卷,可以通过以下命令查看:

btrfs subvolume list /var/lib/docker

btrfs 的 zstd 压缩对 AI 镜像效果显著。本机配置为 compress=zstd:3,在压缩比和性能之间取得了良好的平衡。

启动 Docker 服务

# systemd
systemctl enable --now docker

# OpenRC
rc-update add docker default
rc-service docker start

验证 Docker 运行

docker info
docker run hello-world
usermod -aG docker $USER  # 非 root 用户加入 docker 组

配置 GPU / AI 支持

为什么使用 nvidia-container-toolkit? NVIDIA GPU 是当前 AI/ML 训练推理的事实标准。nvidia-container-toolkit 是 NVIDIA 官方提供的容器 GPU 透传方案,它自动探测宿主机的驱动版本和 CUDA 运行时,免去手动映射 /dev/nvidia* 设备节点的繁琐工作。搭配 Docker 的 --gpus all 参数,即可在容器内获得接近原生的 GPU 算力。本机使用的 RTX 5080(16GB VRAM,CUDA 13.2)对于多数主流模型(SD、LLaMA 量化版等)的推理和微调已经足够。

安装 nvidia-container-toolkit

emerge --ask app-containers/nvidia-container-toolkit

配置 NVIDIA 运行时

/etc/docker/daemon.json 中添加 NVIDIA 运行时:

{
    "runtimes": {
        "nvidia": {
            "args": [],
            "path": "nvidia-container-runtime"
        }
    }
}

本机的完整 daemon.json 还配置了存储驱动和国内网络所需的代理:

{
    "experimental": true,
    "storage-driver": "btrfs",
    "proxies": {
        "http-proxy": "http://172.17.0.1:64540",
        "https-proxy": "http://172.17.0.1:64540"
    },
    "runtimes": {
        "nvidia": {
            "args": [],
            "path": "nvidia-container-runtime"
        }
    }
}

验证 GPU 加速容器

docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi

搭建本地 Registry 镜像加速

在国内网络环境下,直接从 Docker Hub、NVCR、GHCR 拉取镜像速度慢且不稳定。本地搭建 registry 代理缓存(pull-through proxy cache)后,首次拉取通过代理缓慢下载,后续同一镜像从本地缓存直接返回,速度从分钟级降到秒级。

三个 registry 实例共享 /data/cache(在存储层次设计中规划的 512GB ext4 分区),TTL 设为 720 小时(30 天),兼顾缓存命中率和磁盘空间。

启动 Registry 代理容器

mkdir -p /data/cache/registry

# Docker Hub 代理
docker run -d --restart unless-stopped --name registry-hub \
  -v /data/cache/registry:/var/lib/registry \
  -e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io \
  -e REGISTRY_PROXY_TTL=720h \
  -e HTTP_PROXY=http://172.17.0.1:64540 \
  registry:3

# NVIDIA NGC 代理
docker run -d --restart unless-stopped --name registry-nvidia \
  -v /data/cache/registry:/var/lib/registry \
  -e REGISTRY_PROXY_REMOTEURL=https://nvcr.io \
  -e REGISTRY_PROXY_TTL=720h \
  -e HTTP_PROXY=http://172.17.0.1:64540 \
  registry:3

# GHCR 代理
docker run -d --restart unless-stopped --name registry-github \
  -v /data/cache/registry:/var/lib/registry \
  -e REGISTRY_PROXY_REMOTEURL=https://ghcr.io \
  -e REGISTRY_PROXY_TTL=720h \
  -e HTTP_PROXY=http://172.17.0.1:64540 \
  registry:3

注意使用 registry:3 镜像而非 registry:2,因为 v2 已停止维护且不支持 REGISTRY_PROXY_TTL 等配置项。以上容器内部监听 5000 端口,通过 Docker 内部网络通信,无需映射到宿主机,下面用 nginx 做统一入口。

配置 Nginx 反向代理

创建一个 nginx 配置,按 Host 请求头路由到对应的 registry 后端:

# /srv/registry/nginx.conf
worker_processes 2;

events { worker_connections 1024; }

http {
    server {
        listen 80 default_server;
        server_name registry-1.docker.io registry.local;

        location / {
            proxy_pass http://registry-hub:5000;
            proxy_set_header Host registry-1.docker.io;
        }
    }

    server {
        listen 80;
        server_name nvcr.io;

        location / {
            proxy_pass http://registry-nvidia:5000/;
            proxy_set_header Host nvcr.io;
        }
    }

    server {
        listen 80;
        server_name ghcr.io;

        location / {
            proxy_pass http://registry-github:5000/;
            proxy_set_header Host ghcr.io;
        }
    }
}

启动 nginx 代理容器:

docker run -d --restart unless-stopped --name registry-proxy \
  -p 80:80 \
  -v /srv/registry/nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx:alpine

配置 Docker 使用本地镜像

添加本地 DNS 解析:

echo "127.0.0.1 registry.local" >> /etc/hosts

daemon.json 中配置镜像加速:

{
    "registry-mirrors": ["http://registry.local"],
    "insecure-registries": ["registry.local"]
}

重启 Docker 后生效。

验证缓存效果

# 拉取一个未缓存的大型镜像
docker pull pytorch/pytorch:latest

# 查看缓存内容
curl -s http://registry.local/v2/_catalog | python3 -m json.tool

首次拉取会从上游下载,再次拉取同一镜像时将从本地缓存直接返回。

下一篇介绍容器运行在 Gentoo 上的实际情况。