如何解决 Docker 的 'pull access denied' 错误

DockerBeginner
立即练习

介绍

Docker 是一个强大的容器化平台,它简化了应用程序的部署和管理。然而,用户在尝试拉取 Docker 镜像时,有时可能会遇到“pull access denied”(拉取访问被拒绝)的错误。本教程将全面指导你理解、排除故障并解决这个常见的 Docker 问题。

在整个实验(Lab)过程中,你将学习 Docker 镜像注册表(Docker image registries)的工作原理,了解为什么会出现访问被拒绝的错误,并培养解决身份验证问题的实用技能。通过本教程的学习,你将能够自信地处理容器化工作流程中的 Docker 镜像访问问题。

理解 Docker 注册表和基本镜像拉取

在探讨“pull access denied”(拉取访问被拒绝)错误之前,让我们先了解 Docker 注册表以及镜像拉取的工作原理。

什么是 Docker 注册表?

Docker 注册表是 Docker 镜像的存储系统。它允许你推送(上传)和拉取(下载)容器镜像。Docker Hub 是默认的公共注册表,但还有许多其他的注册表,包括组织用来存储专有镜像的私有注册表。

首先,让我们检查 Docker 是否已正确安装在你的系统上。打开一个终端并运行:

docker --version

你应该看到类似如下的输出:

Docker version 20.10.21, build baeda1f

从 Docker Hub 拉取公共镜像

现在,让我们尝试从 Docker Hub 拉取一个简单的公共镜像。拉取镜像的基本语法是:

docker pull [registry/][username/]repository[:tag]

让我们拉取官方的 Alpine Linux 镜像,它体积小且常用:

docker pull alpine:latest

你应该看到类似如下的输出:

latest: Pulling from library/alpine
c158987b0551: Pull complete
Digest: sha256:bc41182d7ef5ffc53a40b044e725193bc10142a1243f395ee852a8d9730fc2ad
Status: Downloaded newer image for alpine:latest
docker.io/library/alpine:latest

这确认了你可以成功拉取公共镜像。让我们通过列出你所有的 Docker 镜像来验证镜像是否已下载:

docker images

你应该在列表中看到 alpine 镜像:

REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
alpine       latest    9c6f07244728   2 weeks ago    5.54MB

Docker 镜像命名约定

理解 Docker 镜像命名约定对于解决访问问题至关重要:

  • Registry:注册表所在的 hostname(默认是 Docker Hub)
  • Username/Organization:拥有 repository 的账户
  • Repository:镜像的名称
  • Tag:镜像的特定版本(默认是 "latest")

例如,在 docker.io/nginx:1.21 中,registry 是 docker.io,repository 是 nginx,tag 是 1.21

当你没有指定 registry 时,Docker 假定你正在使用 Docker Hub。当你没有指定 username 或 organization 时,Docker 会在 "library" 命名空间中查找,该命名空间包含官方镜像。

遇到“Pull Access Denied”(拉取访问被拒绝)错误

现在你已经了解了 Docker 镜像拉取的基础知识,让我们来探讨“pull access denied”(拉取访问被拒绝)错误。当你尝试拉取你无权访问的镜像时,通常会发生此错误。

创建一个场景来遇到此错误

让我们尝试拉取一个不存在或私有的镜像,以故意触发“pull access denied”(拉取访问被拒绝)错误。我们将尝试拉取一个虚构的私有镜像:

docker pull labex/private-repo:latest

你应该看到类似如下的错误消息:

Error response from daemon: pull access denied for labex/private-repo, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

此错误发生的原因可能是:

  1. 该 repository 不存在
  2. 该 repository 存在但为私有,并且你未通过身份验证
  3. 你已通过身份验证,但没有权限访问此 repository

理解 Docker 注册表的身份验证

Docker 使用一个简单的身份验证系统来处理私有注册表。在拉取私有镜像之前,你需要使用 docker login 命令进行身份验证:

docker login [registry-url]

如果未提供 registry URL,Docker 假定你正在登录 Docker Hub。

让我们尝试登录 Docker Hub(如果你有自己的 Docker Hub 帐户,可以使用它,或者只是查看提示,然后按 Ctrl+C 取消):

docker login

你将看到一个提示,要求你输入用户名和密码:

Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username:

由于我们在此练习中实际上不需要登录,你可以按 Ctrl+C 取消登录过程。

“Pull Access Denied”(拉取访问被拒绝)错误的常见原因

“pull access denied”(拉取访问被拒绝)错误可能由于多种原因而发生:

  1. Repository 名称不正确:你可能拼错了 repository 的名称
  2. Repository 不存在:你尝试访问的 repository 不存在
  3. 需要身份验证:repository 是私有的,需要登录
  4. 权限不足:你已通过身份验证,但没有访问权限
  5. 速率限制:Docker Hub 限制未通过身份验证用户的拉取次数

检查 Docker 守护程序日志

在排除访问问题时,检查 Docker 守护程序日志通常很有帮助:

sudo journalctl -u docker | tail -n 20

这将显示 Docker 系统日志的最后 20 行,其中可能包含有关访问被拒绝错误的更多信息。

解决“Pull Access Denied”(拉取访问被拒绝)错误

现在我们了解了导致“pull access denied”(拉取访问被拒绝)错误的原因,让我们学习如何解决它们。我们将根据最常见的原因,逐步介绍几种解决方案。

解决方案 1:验证 Repository 名称和 Tag

访问错误最常见的原因之一是简单地使用了不正确的 repository 名称或 tag。始终仔细检查你的镜像名称是否有拼写错误。

让我们尝试使用特定 tag 拉取一个有效的镜像:

docker pull nginx:1.21.0

输出应该显示成功的拉取:

1.21.0: Pulling from library/nginx
a330b6cecb98: Pull complete
b847ebd0aed4: Pull complete
543e2db69aaf: Pull complete
... (more lines)
Digest: sha256:2f1cd90e00fe2a0aa8969938c6a4135443ac6c7e50d255a54b57ba1a21086ce3
Status: Downloaded newer image for nginx:1.21.0
docker.io/library/nginx:1.21.0

解决方案 2:使用 Registry 进行身份验证

如果你尝试访问私有 repository,你需要首先进行身份验证:

docker login [registry-url]

成功身份验证后,Docker 将凭据存储在 ~/.docker/config.json 的配置文件中。让我们检查此文件是否存在:

ls -la ~/.docker/

如果你之前已登录,你应该看到 config.json 文件被列出。

解决方案 3:检查 Registry 速率限制

Docker Hub 对拉取施加了速率限制:

  • 匿名用户:每 6 小时每个 IP 地址 100 次拉取
  • 已通过身份验证的用户:每 6 小时每个帐户 200 次拉取

如果你遇到了速率限制,请进行身份验证以增加你的限制:

docker login

解决方案 4:使用显式的 Registry URL

有时,指定完整的 registry URL 可以帮助解决访问问题:

docker pull docker.io/library/ubuntu:20.04

这种显式格式有助于 Docker 正确识别要连接的 registry。

通过拉取另一个公共镜像来测试你的访问权限

让我们通过拉取不同的镜像来验证你是否仍然可以拉取公共镜像:

docker pull hello-world

你应该看到确认成功拉取的输出:

Using default tag: latest
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:6e8b6f026e0b9c419ea0fd02d3905dd0952ad1feea67543f525c73a0a790fefb
Status: Downloaded newer image for hello-world:latest
docker.io/library/hello-world:latest

现在运行 hello-world 容器以验证一切是否正常工作:

docker run hello-world

你应该看到来自 Docker 的欢迎消息:

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

故障排除清单

当你遇到“pull access denied”(拉取访问被拒绝)错误时,请遵循此清单:

  1. 验证镜像名称和 tag 是否正确
  2. 检查 repository 是否存在(在 Docker Hub 上搜索)
  3. 如果 repository 是私有的,请进行身份验证
  4. 检查速率限制问题
  5. 检查 Docker 守护程序日志以获取详细的错误消息
  6. 验证与 registry 的网络连接

使用私有 Registry

在许多实际场景中,你需要使用私有 Docker registry。这些 registry 需要身份验证和仔细的凭据管理。让我们学习如何有效地使用它们。

私有 Registry 的类型

有几种可用的私有 registry 选项:

  1. Docker Hub 私有 Repositories:Docker Hub 上的私有 repository
  2. Docker Registry:Docker 的开源 registry 实现
  3. **Docker Trusted Registry (DTR)**:Docker Enterprise 的一部分
  4. 第三方 registry:例如 AWS ECR、Google Container Registry、GitHub Container Registry 等

设置用于测试的简单本地 Registry

为了学习,让我们设置一个本地 Docker registry。这将帮助你理解私有 registry 的工作原理:

docker run -d -p 5000:5000 --name registry registry:2

此命令在端口 5000 上启动一个私有 registry 容器。如果成功,你应该看到一个容器 ID 输出。

将镜像推送到你的本地 Registry

让我们修改一个现有的镜像并将其推送到我们的本地 registry:

  1. 首先,使用我们的本地 registry 地址标记一个现有的镜像:
docker tag nginx:1.21.0 localhost:5000/my-nginx:v1
  1. 将镜像推送到本地 registry:
docker push localhost:5000/my-nginx:v1

你应该看到显示推送进度的输出:

The push refers to repository [localhost:5000/my-nginx]
72a69066d2fe: Pushed
1e7cb45d18ab: Pushed
c8db6be2bb1a: Pushed
... (more layers)
v1: digest: sha256:... size: 1570
  1. 现在让我们删除本地镜像并尝试从我们的 registry 中拉取它:
docker image rm localhost:5000/my-nginx:v1
docker pull localhost:5000/my-nginx:v1

你应该看到镜像从你的本地 registry 中成功拉取。

使用 Registry 身份验证

对于生产环境中的私有 registry,你需要正确处理身份验证。登录私有 registry 时,Docker 会将凭据存储在你的配置文件中。

要使用私有 registry 进行身份验证:

docker login [registry-url]

身份验证后,你可以正常拉取镜像:

docker pull [registry-url]/[repository]:[tag]

出于安全原因,完成后你应该注销:

docker logout [registry-url]

安全地存储 Registry 凭据

对于自动化系统,安全地存储凭据非常重要。你可以使用:

  1. Docker 凭据助手
  2. 带有 docker login 的环境变量
  3. Docker secrets(在 swarm 模式下)

让我们检查当前的凭据存储:

cat ~/.docker/config.json | grep -v auth

你应该看到有关你的 Docker 设置的配置详细信息。

清理

让我们停止并删除我们的测试 registry:

docker stop registry
docker rm registry

这将删除我们为测试创建的本地 registry 容器。

防止访问问题的最佳实践

在你学会了如何解决“拉取访问被拒绝”错误后,让我们来探索一下未来如何避免这些问题。

使用完全限定的镜像名称

始终使用完全限定的镜像名称以避免歧义:

docker pull docker.io/library/ubuntu:20.04

这清楚地表明了你试图访问的注册表、仓库和标签。

设置凭证助手 (Credential Helpers)

Docker 凭证助手可以安全地存储你的注册表凭证。为你的操作系统安装合适的助手:

对于 Ubuntu,你可以使用基于 pass 的凭证助手:

sudo apt-get update
sudo apt-get install -y pass

然后生成一个 GPG 密钥(出于演示目的,你可以按 Enter 键接受默认设置):

gpg --generate-key

使用你的 GPG 密钥 ID 初始化 pass(请替换为前一个输出中的实际密钥 ID):

pass init "Your GPG Key ID"

安装 Docker 凭证助手:

sudo apt-get install -y docker-credential-pass

配置默认注册表设置

你可以在 Docker daemon 配置文件中配置默认注册表设置。让我们创建一个简单的配置:

sudo mkdir -p /etc/docker
echo '{
  "registry-mirrors": ["https://registry-mirror.example.com"]
}' | sudo tee /etc/docker/daemon.json

注意:这只是一个示例。如果需要,你应将镜像 URL 替换为真实的 URL。

使用 Docker Compose 进行一致性部署

Docker Compose 有助于确保跨环境的一致性镜像引用。让我们创建一个简单的 docker-compose.yml 文件:

mkdir -p ~/project/compose-demo
cd ~/project/compose-demo

现在创建一个 docker-compose.yml 文件:

cat > docker-compose.yml << 'EOF'
version: '3'
services:
  web:
    image: nginx:1.21.0
    ports:
      - "8080:80"
  redis:
    image: redis:6.2
EOF

首先,让我们确保你的系统上安装了 Docker Compose:

docker compose version

如果未安装 Docker Compose,你可能需要安装它。在 Ubuntu 上,你可以使用以下命令安装:

sudo apt-get update
sudo apt-get install -y docker-compose-plugin

使用此文件,你可以通过一个命令启动两个服务:

docker compose up -d

你应该会看到显示容器正在创建的输出:

Creating network "compose-demo_default" with the default driver
Creating compose-demo_web_1   ... done
Creating compose-demo_redis_1 ... done

验证服务是否正在运行:

docker compose ps

你应该会看到两个服务都处于“Up”状态。

清理你的 Docker 环境

让我们通过停止并移除容器来清理我们的环境:

docker compose down
cd ~/project

这会停止并移除我们使用 Docker Compose 创建的容器。

最佳实践总结

  • 始终使用完全限定的镜像名称
  • 在拉取私有镜像之前进行身份验证
  • 设置安全的凭证存储
  • 使用 Docker Compose 进行一致性部署
  • 定期审计你的 Docker 配置
  • 使用镜像摘要 (image digests) 进行不可变引用
  • 为注册表访问实现适当的网络配置

通过遵循这些最佳实践,你将最大限度地减少“拉取访问被拒绝”错误,并创建一个更可靠的容器化环境。

总结

在这个实验中,你已经学会了如何理解、排除故障和解决 Docker 中的“pull access denied”(拉取访问被拒绝)错误。你现在拥有以下方面的实践经验:

  • 理解 Docker registry 和镜像拉取基础知识
  • 识别“pull access denied”(拉取访问被拒绝)错误的常见原因
  • 通过适当的身份验证和故障排除来解决访问问题
  • 使用私有 registry,包括设置本地测试 registry
  • 实施最佳实践以防止将来出现访问问题

这些技能对于在你的 Docker 环境中顺利进行容器部署和管理至关重要。当你继续使用 Docker 时,你会发现,适当的访问管理是容器操作的一个基本方面,尤其是在具有私有 registry 的企业环境中。

记住解决访问问题的关键步骤:

  1. 验证镜像名称和 tag
  2. 检查身份验证要求
  3. 查看 Docker 守护程序日志
  4. 确保适当的网络连接
  5. 应用凭据管理方面的最佳实践

你现在已经掌握了在你的容器化工作流程中自信地处理 Docker 镜像访问挑战的知识。