在容器技术日常落地的过程中,镜像构建方式直接影响交付速度和运行安全。传统 Docker 构建依赖长期运行的 dockerd 守护进程,这种模式在共享主机或 CI Runner 中容易遇到权限冲突和资源争抢。img 是一个用 Go 编写的命令行工具,它把 BuildKit 的核心能力封装成无守护进程的构建器,让用户在没有任何容器守护程序的环境下也能产出标准镜像。

img 的底层构建原理与命名空间隔离
img 并不自己实现镜像层打包逻辑,而是引用了 moby/buildkit 前端库,将构建指令翻译成 LLB(Low-Level Build)中间语言,再由内置的 executor 在本地执行。最关键的一点是,img 使用 Linux 的 user namespace 把容器内的 uid 0 映射到宿主机的普通用户,从而在不申请真正 root 权限的情况下完成需要写系统目录、改用户态配置的步骤。这种方式绕开了 dockerd 必须监听 Unix socket 且常以 root 运行的束缚。
在 user namespace 之上,img 还借助 overlayfs 或 native snapshotter 管理分层文件系统。每一条 Dockerfile 指令产生的变更都会被记录为只读层,最终依据 OCI image-spec 合成 manifest 与 config。因为构建进程本身就是调用方用户,所以生成的镜像在权限归属上更清晰,不会出现 root 创建的层导致后续非 root 环境无法清理的问题。下面是一段在 Ubuntu 普通账号下使用 img 构建简单镜像的示例:
# 安装 img 二进制 curl -fL https://github.com/genuinetools/img/releases/download/v0.5.11/img-linux-amd64 -o /usr/local/bin/img chmod +x /usr/local/bin/img # 编写最小 Dockerfile cat > Dockerfile <<'EOF' FROM alpine:3.18 RUN adduser -D appuser USER appuser CMD ["sh"] EOF # 使用 img 构建并打标签 img build -t ipipp.com/demo/mini:latest .
上述命令中,即使在禁止 docker 命令的服务器上,只要内核开启 user namespace,就能顺利执行。对比 docker build 需要事先启动服务,img 的冷启动几乎只受二进制加载速度影响。对于临时 Runner 来说,省去守护进程初始化就意味着更短的排队和更低的失败率。
与传统 Docker 构建在权限和 CI 场景的对比
传统 Docker 构建要求执行者拥有访问 docker.sock 的权限,这在多租户 CI 平台中往往等于变相授予宿主机 root。img 把权限边界收缩到单个用户命名空间,配合 --state 与 --cache 参数还能把构建缓存放在用户目录,避免不同项目互相污染。下表列出了两者在几个关键维度的差异:
| 维度 | Docker 构建 | img 构建 |
|---|---|---|
| 守护进程 | 必须运行 dockerd | 无需任何守护进程 |
| 权限模型 | 常需 root 或 docker 组 | 普通用户加 user namespace |
| 缓存位置 | 由 dockerd 统一管理 | 可指定用户态目录 |
| 适用环境 | 开发机、特权节点 | 无特权容器、CI Runner |
在 GitHub Actions 的自托管 Runner 中,如果 Runner 本身以容器方式部署且没有特权模式,docker build 会直接报错。此时将 img 作为构建步骤的替代,不仅能通过,还能利用层缓存加速。需要注意的是,img 目前对多平台交叉构建的支持不如最新 Buildx 完善,若需同时出 amd64 与 arm64 镜像,应评估是否配合 QEMU 用户态模拟。
另一个常被忽略的点是镜像推送。img 内置了 registry 客户端,支持直接把产物推送到支持 OCI 的仓库。由于推送动作也运行在用户态,不会在宿主机留下匿名 volume,对磁盘运维更友好。以下示例展示如何免 sudo 推送到远程:
# 登录镜像仓库,凭证存入 ~/.config/img img login ipipp.com -u builder -p secretpass # 构建并推送 img build -t ipipp.com/demo/mini:latest -push .
从这段流程可以看出,整个构建加推送没有调用任何系统级服务,所有网络与文件操作都可被用户配额限制,非常适合在受限沙箱中做合规审计。
实操中的常见误区与分层优化建议
不少初次接触 img 的开发者会误以为它只是 docker 命令的别名,于是沿用 Docker 守护进程的思维,比如试图用 docker images 查看 img 产物。实际上 img 的本地索引存放在用户目录的 state 文件夹,必须通过 img ls 或 img inspect 查看。若强行混合使用,容易造成层数统计混乱。理解这一点是排错的第一步。
在编写 Dockerfile 时,为了充分发挥 img 的层缓存优势,应当把变动频率低的指令放在前面。由于 img 的 snapshotter 对 RUN 指令的内容哈希敏感,哪怕只是调整了注释顺序也可能让缓存失效。建议把依赖安装与源码拷贝分开,并利用 --build-arg 传递环境差异。示例展示带缓存优化的写法:
FROM node:18-alpine WORKDIR /app # 先拷贝依赖描述,利用缓存 COPY package.json package-lock.json ./ RUN npm ci --omit=dev # 再拷贝源码,源码变动不影响依赖层 COPY src ./src CMD ["node", "src/index.js"]
此外,img 对 .dockerignore 的支持与 Docker 基本一致,但部分旧版本在嵌套目录过滤上略有偏差。建议在 CI 脚本里显式打印构建上下文大小,确认无关文件未被打入。当镜像最终需要交给 Kubernetes 拉取时,只要仓库支持 OCI 分发,img 产出的镜像无需任何转换即可被 containerd 或 cri-o 直接识别,这也是它在云原生流水线里逐渐被采纳的根本原因。
imgbuild_container_imageOCI_image修改时间:2026-08-16 09:40:30