Docker容器挂载宿主机docker.sock推镜像到DockerHub报401授权失败
挂载/var/run/docker.sock仅能让容器内的Docker客户端与宿主机Docker守护进程建立通信,Docker Hub的认证凭证默认存储在发起操作的客户端侧用户目录下,而非保存在Docker守护进程中。
你在宿主机直接执行docker push能成功,是因为宿主机上的Docker客户端会自动读取当前宿主机用户~/.docker/config.json中存储的Docker Hub登录凭证,随请求发送给守护进程完成鉴权。而容器内执行推送操作时,容器内的Docker客户端/sbt构建插件无法访问宿主机上的这份凭证文件,发起push请求时没有携带有效认证信息,最终触发401授权失败错误。
从报错输出的多架构manifest导出、BuildKit构建日志特征可以判断,你使用的sbt docker:publish底层调用BuildKit完成构建推送,BuildKit对凭证的读取逻辑更严格,完全依赖发起构建请求的客户端侧提供的认证信息,不会主动读取守护进程侧的任何配置,进一步放大了凭证缺失的问题。
- 对Docker认证凭证的存储位置存在认知偏差:误将Docker守护进程当作凭证存储主体,实际上守护进程只负责校验客户端传来的凭证,本身不持久化存储用户的Docker Hub登录信息。
- 启动容器时仅挂载了Docker套接字,未同步挂载Docker客户端所需的认证配置文件,导致容器内的构建流程无法获取有效推送凭证。
- 未考虑容器内执行用户与凭证路径的匹配问题:如果容器内执行sbt命令的用户不是root,就算挂载了凭证文件,只要路径和对应用户的
~/.docker目录不匹配,依然会读不到凭证。
- 启动容器时额外挂载只读的Docker认证配置文件,路径匹配容器内运行进程的用户家目录。如果容器内默认用root用户执行ENTRYPOINT,启动命令调整为:
docker run -v /var/run/docker.sock:/var/run/docker.sock \ -v $HOME/.docker/config.json:/root/.docker/config.json:ro \ bitcoin-s-build:latest
注意:如果你的宿主机Docker配置使用了凭证助手(credential helper)存储加密凭证,而非将凭证明文写入
config.json,直接挂载配置文件会因为容器内没有对应凭证助手程序而无法解密凭证。这种场景下有两种可选方案:一是在宿主机重新执行docker login时临时注释掉config.json中的credsStore字段,让凭证明文写入配置文件再挂载;二是容器启动后先手动执行docker login在容器内完成认证,再触发构建推送流程。
- 为避免BuildKit读取凭证时路径识别错误,可以在你的Dockerfile中增加环境变量,显式指定Docker配置目录:
# 放在ENTRYPOINT指令之前即可 ENV DOCKER_CONFIG=/root/.docker
- 正式执行构建前可以先进入容器做连通性校验:执行
docker ps确认能正常列出宿主机上的运行容器,执行docker push推送一个本地测试镜像确认认证正常,再触发sbt构建任务,减少排查成本。
内容的提问来源于stack exchange,提问作者Chris Stewart

