Docker 构建在大 UID 的 useradd 步骤卡住并大量占用磁盘
Linux 镜像创建大 UID 账号时,lastlog 或 faillog 初始化可能产生巨大稀疏记录并拖慢层处理。使用支持该选项的 useradd --no-log-init,保留原身份并验证磁盘占用。
Docker 构建停在 RUN useradd,换成较小的 UID 就能继续,同时构建机磁盘占用明显上涨。若这一步使用了很大的数字 UID,需要检查账号创建时对 /var/log/lastlog、/var/log/faillog 的初始化,而不只是反复延长构建超时。
本文针对 Linux 镜像中支持 --no-log-init 的 useradd。不同基础镜像的账号工具、登录记录配置和构建器处理方式可能不同;大 UID 本身不意味着所有镜像都会卡住,普通的用户名冲突、UID 映射限制也不由这个选项解决。
卡住的位置未必仍在执行 useradd
登录记录按用户身份保存,较大的 UID 可能让相关记录文件具有很大的逻辑长度。这类文件可以是稀疏文件:逻辑上很长,实际分配的磁盘块较少。但镜像层归档处理稀疏内容时可能把空洞展开,从而大量消耗磁盘和时间。
因此,构建日志最后显示 RUN useradd,不能单凭这一行认定账号命令仍未退出;时间也可能耗在后续层处理。更换基础镜像后恰好不再发生,也不能证明问题一定来自某个 Ubuntu 版本。
先对照最近改动,确认是否刚把 -u 的值改成平台分配的大 UID,以及基础镜像里的 useradd 是否会初始化登录记录。保留原 Dockerfile、基础镜像标识和构建日志。已有失败构建造成磁盘紧张时,先取消这次构建并核对占用,不要继续并发重试制造更多中间数据。
在原创建命令上加 --no-log-init
对于不需要为容器应用账号初始化这些登录记录的镜像,在原来的 useradd 命令中增加该选项即可。下面是独立验证用的完整 Dockerfile,数值只是大 UID 示例,须替换成项目明确分配的身份:
FROM ubuntu:24.04
RUN useradd --no-log-init -m -s /bin/bash -N -u 1000123456 appuser
USER appuser
CMD ["id"]
--no-log-init 的短形式是小写 -l。它跳过该用户的 lastlog、faillog 初始化,不会取消账号创建,也不负责清理已经存在的巨大文件。这里 -m 仍创建 home,-N 表示不创建同名用户组、沿用基础镜像配置的默认组;真实应用如有明确 GID,应保留原有建组和 -g 设置。
不要为避开膨胀随意换一个较小 UID。身份通常还参与挂载目录权限或部署平台约束;改变它可能让构建通过,却使服务访问数据失败。修复目标是跳过不需要的登录记录初始化,保留已约定的运行身份。
Debian/Ubuntu 的 adduser 包装命令不能直接照搬这一选项。BusyBox 等其他账号工具也应按自身接口处理。先确认 Dockerfile 实际调用的是哪一个工具;不要因为选项不识别就放宽权限或改成 root 运行。
验证构建和账号语义
在有足够空间的独立构建环境使用新标签,保留原有可用镜像:
docker build -t uid-check:fixed .
docker run --rm uid-check:fixed
这两行针对上面的验证 Dockerfile,不挂载生产数据。预期构建完成,id 输出中 UID 与配置一致;同时观察镜像大小和构建机磁盘占用,不应再因这一项登录记录初始化持续增长。示例不承诺具体体积或耗时,不同基础镜像和构建器仍需分别核对。
应用镜像还要验证真实入口、home 与所需目录访问。id 成功仅说明账号和身份可用,不证明业务程序可写挂载目录。若构建仍在同一位置耗时,应继续确认空间、构建器日志以及其他命令,不能把所有无输出都归为稀疏文件。
处理旧构建残留时保留恢复依据
修改 Dockerfile 不会自动回收旧层或构建缓存,也不会缩小基础镜像本来就带有的巨大记录文件。不要在 Docker 数据目录内手工删除文件,更不要清空生产主机的 lastlog、faillog 来修一个镜像构建问题;那会改变宿主机的登录审计记录。
确认哪些中间结果属于失败构建、哪些镜像仍被服务或回退流程使用,再按现有 Docker 清理流程处理指定对象。没有完成这项核对时,不应直接执行全局清理或删除卷。
如果应用确实依赖登录记录初始化,应重新评估这个镜像内的账号设计,不能无条件关闭该行为。上线前保留旧镜像和创建配置;验证失败时恢复原部署,而不是在已满的构建机上反复重建原来的大 UID 步骤。回退应用版本也不会自动撤销构建产生的磁盘占用。


