如何在 Ubuntu 22.04 上设置私有 Docker 注册表
作者选择了 Write for DOnations 计划。
介绍
TravisCI,在生产和开发过程中无缝更新图像。
Docker Hub 是一个免费的公共注册表,可以托管您的自定义 Docker 映像,但在某些情况下您不希望您的映像公开可用。映像通常包含运行应用程序所需的所有代码,因此在使用专有软件时最好使用私有注册表。
在本教程中,您将设置并保护您自己的私有 Docker Registry。您将使用 Docker Compose 来定义运行 Docker 容器的配置,并使用 Nginx 将服务器流量从互联网转发到正在运行的 Docker 容器。完成本教程后,您将能够将自定义 Docker 镜像推送到您的私有注册表,并从远程服务器安全地拉取该镜像。
先决条件
要完成本教程,您需要具备以下条件:
- 按照 Ubuntu 22.04 初始服务器设置指南设置的两台 Ubuntu 22.04 服务器,包括
sudo
非根用户和防火墙。一台服务器将托管您的私有 Docker Registry,另一台将作为您的客户端服务器。 - 两台服务器上都安装了 Docker,您可以按照如何在 Ubuntu 22.04 上安装和使用 Docker 的第 1 步和第 2 步进行设置。
在主机服务器上,您需要设置:
- Docker Compose 安装在主机服务器上,您可以按照如何在 Ubuntu 22.04 上安装和使用 Docker Compose 的第 1 步进行设置。
- Nginx 安装在您的主机服务器上,您可以按照如何在 Ubuntu 22.04 上安装 Nginx 中的步骤进行设置。
- Nginx 在您的主机服务器上使用 Let's Encrypt 保护私有 Docker 注册表,您可以按照如何在 Ubuntu 22.04 上使用 Let's Encrypt 教程保护 Nginx 进行设置。确保在步骤 4 中将所有流量从 HTTP 重定向到 HTTPS。
- 一个已注册的域名,可解析为您用来托管私有 Docker Registry 的服务器。您将把它设置为 Let's Encrypt 先决条件的一部分。在本教程中,我们将其称为
your_domain
。
第 1 步 — 安装和配置 Docker 注册表
在命令行上运行 Docker 在启动和测试容器时很有用,但对于涉及并行运行的多个容器的更大部署,它可能会变得笨拙。
使用 Docker Compose,您编写一个
.yml
文件来设置每个容器的配置和容器相互通信所需的信息。您可以使用docker compose
工具向构成应用程序的所有组件发出命令,并将它们作为一个整体进行控制。Docker Registry 本身是一个包含多个组件的应用程序,因此您将使用 Docker Compose 来管理它。要启动注册表实例,您将设置一个
docker-compose.yml
文件来定义它以及注册表将在磁盘上存储其数据的位置。您会将配置存储在主机服务器上名为
docker-registry
的目录中。通过运行创建它:- mkdir ~/docker-registry
导航到它:
- cd ~/docker-registry
然后,创建一个名为
data
的子目录,您的注册表将在其中存储其图像:- mkdir data
通过运行以下命令创建并打开名为
docker-compose.yml
的文件:- nano docker-compose.yml
添加以下行,它们定义了 Docker Registry 的基本实例:
version: '3' services: registry: image: registry:latest ports: - "5000:5000" environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./data:/data
首先,您将第一个服务命名为
registry
,并使用最新版本将其映像设置到registry
。然后,在ports
下,将主机上的端口5000
映射到容器的端口5000
,这样您就可以向服务器上的端口5000
并将请求转发到注册表。在
environment
部分,您将REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
变量设置为/data
,指定应将其数据存储在哪个卷中。然后,在volumes
部分,将主机文件系统上的/data
目录映射到容器中的/data
,充当直通。数据实际上将存储在主机的文件系统中。保存并关闭文件。
您现在可以通过运行以下命令开始配置:
- docker compose up
注册表容器及其依赖项将被下载并启动。
Output[+] Running 2/2 ⠿ Network docker-registry_default Created 0.1s ⠿ Container docker-registry-registry-1 Created 0.1s Attaching to docker-registry-registry-1 docker-registry-registry-1 | time="2022-11-19T14:31:20.40444638Z" level=warning msg="No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable." go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version="v2.8.1+unknown" docker-registry-registry-1 | time="2022-11-19T14:31:20.404960549Z" level=info msg="redis not configured" go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version="v2.8.1+unknown" docker-registry-registry-1 | time="2022-11-19T14:31:20.412312462Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version="v2.8.1+unknown" docker-registry-registry-1 | time="2022-11-19T14:31:20.412803878Z" level=info msg="Starting upload purge in 52m0s" go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version="v2.8.1+unknown" docker-registry-registry-1 | time="2022-11-19T14:31:20.41296431Z" level=info msg="listening on [::]:5000" go.version=go1.16.15 instance.id=4fb8d420-eaf8-4a69-b740-bdc94fa52d91 service=registry version="v2.8.1+unknown" ...您将在本教程后面处理
No HTTP secret provided
警告消息。输出的最后一行表示它已成功启动,监听端口
5000
。您可以按
CTRL+C
停止它的执行。在此步骤中,您创建了一个 Docker Compose 配置,该配置启动一个侦听端口
5000
的 Docker Registry。在接下来的步骤中,您将在您的域中公开它并设置身份验证。第 2 步 — 设置 Nginx 端口转发
作为先决条件的一部分,您在域中启用了 HTTPS。要在那里公开您的安全 Docker 注册表,您需要配置 Nginx 以将流量从您的域转发到注册表容器。
您已经设置了
/etc/nginx/sites-available/your_domain
文件,其中包含您的服务器配置。通过运行打开它进行编辑:- sudo nano /etc/nginx/sites-available/your_domain
查找现有的
location
块:... location / { ... } ...
您需要将流量转发到端口
5000
,您的注册表将在此处侦听流量。您还希望将标头附加到转发到注册表的请求,它提供来自服务器的关于请求本身的附加信息。用以下行替换location
块的现有内容:... location / { # Do not allow connections from docker 1.5 and earlier # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) { return 404; } proxy_pass http://localhost:5000; proxy_set_header Host $http_host; # required for docker client's sake proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 900; } ...
if
块检查请求的用户代理并验证 Docker 客户端的版本是否高于 1.5 并且它不是Go
应用程序正在尝试访问。有关这方面的更多解释,您可以在 Docker 的注册表 Nginx 指南中找到nginx
标头配置。完成后保存并关闭文件。通过重新启动 Nginx 来应用更改:
- sudo systemctl restart nginx
如果您收到错误消息,请仔细检查您添加的配置。
要确认 Nginx 正确地将流量转发到端口
5000
上的注册表容器,请运行它:- docker compose up
然后,在浏览器窗口中,导航到您的域并访问
v2
端点,如下所示:https://your_domain/v2
浏览器将加载一个空的 JSON 对象:
{}
在您的终端中,您将收到类似于以下内容的输出:
Outputdocker-registry-registry-1 | time="2022-11-19T14:32:50.082396361Z" level=info msg="response completed" go.version=go1.16.15 http.request.host=your_domain http.request.id=779fe265-1a7c-4a15-8ae4-eeb5fc35de98 http.request.method=GET http.request.remoteaddr=87.116.166.89 http.request.uri="/v2" http.request.useragent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" http.response.contenttype="text/html; charset=utf-8" http.response.duration="162.546µs" http.response.status=301 http.response.written=39 docker-registry-registry-1 | 172.19.0.1 - - [19/Nov/2022:14:32:50 +0000] "GET /v2 HTTP/1.0" 301 39 "" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" docker-registry-registry-1 | 172.19.0.1 - - [19/Nov/2022:14:32:50 +0000] "GET /v2/ HTTP/1.0" 200 2 "" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" docker-registry-registry-1 | time="2022-11-19T14:32:50.132472674Z" level=info msg="response completed" go.version=go1.16.15 http.request.host=your_domain http.request.id=0ffb17f0-c2a0-49d6-94f3-af046cfb96e5 http.request.method=GET http.request.remoteaddr=87.116.166.89 http.request.uri="/v2/" http.request.useragent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" http.response.contenttype="application/json; charset=utf-8" http.response.duration=2.429608ms http.response.status=200 http.response.written=2 docker-registry-registry-1 | 172.19.0.1 - - [19/Nov/2022:14:32:50 +0000] "GET /favicon.ico HTTP/1.0" 404 19 "your_domain/v2/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"从最后一行,您知道向
/v2/
发出了GET
请求,这是您向其发送请求的端点。由于端口转发,容器收到了您发出的请求并返回了{}
的响应。代码200
表示容器成功处理了请求。按
CTRL+C
停止执行。现在您已经设置了端口转发,您将提高注册表的安全性。
第 3 步 — 设置身份验证
Nginx 允许您为其管理的站点设置 HTTP 身份验证,您可以使用它来限制对 Docker Registry 的访问。为此,您将使用
htpasswd
创建一个身份验证文件,并向其中添加将被接受的用户名和密码组合。该过程将启用对您的注册表的身份验证。您可以通过安装
apache2-utils
包来获取htpasswd
实用程序。通过运行这样做:- sudo apt install apache2-utils -y
您将在
~/docker-registry/auth
下存储带有凭据的身份验证文件。通过运行创建它:- mkdir ~/docker-registry/auth
导航到它:
- cd ~/docker-registry/auth
创建第一个用户,将
username
替换为您要使用的用户名。-B
标志命令使用 Docker 要求的bcrypt
算法:- htpasswd -Bc registry.password username
出现提示时输入密码。凭据组合将附加到
registry.password
。注意:要添加更多用户,请在不使用
-c
的情况下重新运行之前的命令:- htpasswd -B registry.password username
-c
创建一个新文件,因此删除它会更新现有文件。现在已经创建了凭据列表,您将编辑
docker-compose.yml
以命令 Docker 使用您创建的文件来验证用户。打开它进行编辑:- nano ~/docker-registry/docker-compose.yml
添加突出显示的行:
version: '3' services: registry: image: registry:latest ports: - "5000:5000" environment: REGISTRY_AUTH: htpasswd REGISTRY_AUTH_HTPASSWD_REALM: Registry REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./auth:/auth - ./data:/data
您已经添加了指定使用 HTTP 身份验证的环境变量,并提供了创建的文件
htpasswd
的路径。对于REGISTRY_AUTH
,您将htpasswd
指定为其值,这是您正在使用的身份验证方案,并将REGISTRY_AUTH_HTPASSWD_PATH
设置为身份验证文件的路径.REGISTRY_AUTH_HTPASSWD_REALM
表示htpasswd
领域的名称。您还安装了
./auth
目录以使该文件在注册表容器内可用。保存并关闭文件。您现在可以验证您的身份验证是否正常工作。首先,导航到主目录:
- cd ~/docker-registry
然后,通过执行以下命令运行注册表:
- docker compose up
在您的浏览器中,刷新您域的页面。系统会要求您提供用户名和密码。
提供有效的凭据组合后,您将访问带有空 JSON 对象的页面:
{}
您已成功通过身份验证并获得了对注册表的访问权限。在终端中按
CTRL+C
退出。您的注册表现在是安全的,只有在身份验证后才能访问。接下来,您将配置它作为后台进程运行,同时通过自动启动来适应重新启动。
第 4 步 — 启动 Docker Registry 即服务
您可以通过指示 Docker Compose 始终保持运行来确保注册表容器在每次系统启动或崩溃后启动。
打开
docker-compose.yml
进行编辑:- nano docker-compose.yml
将以下行添加到
registry
块:... registry: restart: always ...
将
restart
设置为always
可确保容器在重启后仍然存在。完成后,保存并关闭文件。您现在可以通过传入
-d
将注册表作为后台进程启动:- docker compose up -d
当你的注册表在后台运行时,你可以随意关闭这个 SSH 会话,终端和注册表不会受到影响。
由于 Docker 映像的大小可能非常大,接下来您将增加 Nginx 接受上传的最大文件大小。
第 5 步 — 增加 Nginx 的文件上传大小
在将图像推送到注册表之前,您需要确保您的注册表能够处理大文件上传。 Nginx 默认的文件上传大小限制是
1m
,这对于 Docker 镜像来说是远远不够的。要引发它,您将修改位于/etc/nginx/nginx.conf
的主 Nginx 配置文件。打开它进行编辑:
- sudo nano /etc/nginx/nginx.conf
将突出显示的行添加到
http
部分:... http { client_max_body_size 16384m; ... } ...
client_max_body_size
参数现在设置为16384m
,使最大上传大小等于 16GB。保存并关闭文件。
重新启动 Nginx 以应用配置更改:
- sudo systemctl restart nginx
在此步骤中,您更新了 Nginx 允许的文件大小。您现在可以将大图像上传到 Docker Registry,而 Nginx 不会阻止传输或出错。
第 6 步 — 发布到您的私有 Docker 注册表
现在您的 Docker Registry 服务器正在运行并接受大文件大小,您可以尝试将图像推送到它。由于您没有现成可用的图像,因此您将使用来自 Docker Hub(一个公共 Docker 注册中心)的
ubuntu
图像来测试它。在客户端服务器的新终端会话中,运行以下命令以下载
ubuntu
映像,运行它并访问其 shell:- docker run -t -i ubuntu /bin/bash
-i
和-t
标志使您可以通过交互式 shell 访问容器。进入后,通过运行以下命令创建一个名为
SUCCESS
的文件:- touch /SUCCESS
通过创建此文件,您已经自定义了您的容器。稍后您将使用它来检查您是否使用完全相同的容器。
运行以下命令退出容器 shell:
- exit
现在从您刚刚自定义的容器创建一个新图像:
- docker commit $(docker ps -lq) test-image
新图像现在在本地可用,您可以将其推送到您的容器注册表。首先,您必须登录:
- docker login https://your_domain
出现提示时,输入您在第 3 步中定义的用户名和密码凭据。
输出将是:
Output... Login Succeeded登录后,重命名创建的图像:
- docker tag test-image your_domain/test-image
最后,将新标记的图像推送到您的注册表:
- docker push your_domain/test-image
您将收到类似于以下内容的输出:
OutputUsing default tag: latest The push refers to a repository [your_domain/test-image] 1cf9c9034825: Pushed f4a670ac65b6: Pushed latest: digest: sha256:95112d0af51e5470d74ead77932954baca3053e04d201ac4639bdf46d5cd515b size: 736您已验证您的注册表通过登录处理用户身份验证,并且它允许经过身份验证的用户将图像推送到注册表。您现在将尝试从您的注册表中拉取图像。
第 7 步 — 从您的私有 Docker 注册表中拉取
现在您已经将镜像推送到您的私有注册表,您将尝试从中提取。
在主服务器上,使用您之前设置的用户名和密码登录:
- docker login https://your_domain
尝试通过运行以下命令拉取
test-image
:- docker pull your_domain/test-image
Docker 将下载图像。使用以下命令运行容器:
- docker run -it your_domain/test-image /bin/bash
它将加载容器的外壳。
通过运行列出存在的文件:
- ls
文件列表将包括您之前创建的
SUCCESS
文件,确认此容器使用您创建的相同图像:- SUCCESS bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
退出容器外壳:
- exit
您已经测试了推送和拉取图像并完成了可用于存储自定义图像的安全注册表的设置。
结论
在本教程中,您设置了自己的私有 Docker Registry 并向其发布了 Docker 镜像。如介绍中所述,您还可以使用 TravisCI 或类似的 CI 工具直接自动推送到私有注册表。
通过在您的工作流程中利用 Docker 容器,您可以确保包含代码的图像将在任何机器上产生相同的行为,无论是在生产中还是在开发中。有关编写 Docker 文件的更多信息,您可以访问关于最佳实践的官方文档。
- 一个已注册的域名,可解析为您用来托管私有 Docker Registry 的服务器。您将把它设置为 Let's Encrypt 先决条件的一部分。在本教程中,我们将其称为