作为Write for DOnations计划的一部分,作者选择了免费和开源基金来接受捐赠。
介绍
Docker Registry是一个管理存储和交付 Docker 容器镜像的应用程序。注册表集中容器映像并减少开发人员的构建时间。Docker 镜像通过虚拟化保证相同的运行时环境,但构建镜像可能需要大量时间投资。例如,开发人员无需单独安装依赖项和包来使用 Docker,而是可以从包含所有必要组件的注册表下载压缩映像。此外,开发人员可以使用持续集成工具(例如TravisCI)自动将图像推送到注册表,以在生产和开发期间无缝更新图像。
Docker 还有一个免费的公共注册表Docker Hub,它可以托管您的自定义 Docker 镜像,但在某些情况下,您不希望您的镜像公开可用。映像通常包含运行应用程序所需的所有代码,因此在使用专有软件时最好使用私有注册表。
在本教程中,您将设置并保护您自己的私有 Docker 注册表。您将使用Docker Compose定义运行 Docker 容器的配置,并使用 Nginx 将服务器流量从互联网转发到正在运行的 Docker 容器。完成本教程后,您将能够将自定义 Docker 映像推送到您的私有注册表,并从远程服务器安全地拉取该映像。
先决条件
- 按照Ubuntu 20.04 初始服务器设置指南设置了两台 Ubuntu 20.04 服务器,包括
sudo
非 root 用户和防火墙。一台服务器将托管您的私有 Docker Registry,另一台将作为您的客户端服务器。 - 在两台服务器上安装了由以下泊坞窗步骤1和2中如何安装和使用码头工人在Ubuntu 20.04。
- 在上安装多克尔撰写主机服务器通过以下步骤1中如何安装和使用泊坞撰写在Ubuntu 20.04。
- 按照如何在 Ubuntu 20.04 上安装 Nginx 中的步骤在您的主机服务器上安装 Nginx。
- 按照如何在 Ubuntu 20.04 上使用 Let’s Encrypt 来保护 Nginx教程,Nginx在您的服务器上使用 Let’s Encrypt 保护私有 Docker Registry 。确保在步骤 4中将所有流量从 HTTP 重定向到 HTTPS 。
- 解析为您用于私有 Docker 注册表的服务器的域名。您将把它设置为 Let’s Encrypt 先决条件的一部分。在本教程中,我们将其称为
your_domain
.
第 1 步 – 安装和配置 Docker Registry
命令行上的 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:2
ports:
- "5000:5000"
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
- ./data:/data
首先,您命名第一个 service registry
,并将其图像设置为registry
, version 2
。然后,在 下ports
,5000
将主机上的端口映射到5000
容器的端口。这允许您向5000
服务器上的端口发送请求,并将请求转发到注册表。
在该environment
部分中,您将REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
变量设置为/data
,指定应将其数据存储在哪个卷中。然后,在该volumes
部分中,您/data
将主机文件系统上的目录映射到/data
容器中,作为传递。数据实际上将存储在主机的文件系统上。
保存并关闭文件。
您现在可以通过运行以下命令启动配置:
- docker-compose up
注册表容器及其依赖项将被下载并启动。
OutputCreating network "docker-registry_default" with the default driver
Pulling registry (registry:2)...
2: Pulling from library/registry
e95f33c60a64: Pull complete
4d7f2300f040: Pull complete
35a7b7da3905: Pull complete
d656466e1fe8: Pull complete
b6cb731e4f93: Pull complete
Digest: sha256:da946ca03fca0aade04a73aa94b54ff0dc614216bdd1d47585f97b4c1bdaa0e2
Status: Downloaded newer image for registry:2
Creating docker-registry_registry_1 ... done
Attaching to docker-registry_registry_1
registry_1 | time="2021-03-18T12:32:59.587157744Z" 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.11.2 instance.id=119fe50b-2bb6-4a8d-902d-dfa2db63fc2f service=registry version=v2.7.1
registry_1 | time="2021-03-18T12:32:59.587912733Z" level=info msg="redis not configured" go.version=go1.11.2 instance.id=119fe50b-2bb6-4a8d-902d-dfa2db63fc2f service=registry version=v2.7.1
registry_1 | time="2021-03-18T12:32:59.598496488Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.11.2 instance.id=119fe50b-2bb6-4a8d-902d-dfa2db63fc2f service=registry version=v2.7.1
registry_1 | time="2021-03-18T12:32:59.601503005Z" level=info msg="listening on [::]:5000" go.version=go1.11.2 instance.id=119fe50b-2bb6-4a8d-902d-dfa2db63fc2f service=registry version=v2.7.1
...
您将No HTTP secret provided
在本教程后面解决警告消息。请注意,输出的最后一行显示它已成功开始侦听端口5000
。
您可以按CTRL+C
停止其执行。
在这一步中,您已经创建了一个 Docker Compose 配置,它启动了一个侦听端口的 Docker Registry 5000
。在接下来的步骤中,您将在您的域中公开它并设置身份验证。
第 2 步 – 设置 Nginx 端口转发
作为先决条件的一部分,您已在您的域中启用 HTTPS。要在那里公开您的安全 Docker 注册表,您只需要配置 Nginx 以将流量从您的域转发到注册表容器。
您已经设置了包含服务器配置的文件。通过运行打开它进行编辑:/etc/nginx/sites-available/your_domain
- sudo nano /etc/nginx/sites-available/your_domain
查找现有location
块:
...
location / {
...
}
...
您需要将流量转发到 port 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
尝试访问的应用程序。有关这方面的更多解释,您可以nginx
在Docker 的注册表 Nginx 指南中找到标头配置。
完成后保存并关闭文件。通过重新启动 Nginx 应用更改:
- sudo systemctl restart nginx
如果出现错误,请仔细检查您添加的配置。
要确认 Nginx 正确地将流量转发到 port 上的注册表容器5000
,请运行它:
- docker-compose up
然后,在浏览器窗口中,导航到您的域并访问v2
端点,如下所示:
https://your_domain/v2
您将看到一个空的 JSON 对象:
{}
在您的终端中,您将收到类似于以下内容的输出:
Outputregistry_1 | time="2018-11-07T17:57:42Z" level=info msg="response completed" go.version=go1.7.6 http.request.host=cornellappdev.com http.request.id=a8f5984e-15e3-4946-9c40-d71f8557652f http.request.method=GET http.request.remoteaddr=128.84.125.58 http.request.uri="/v2/" http.request.useragent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7" http.response.contenttype="application/json; charset=utf-8" http.response.duration=2.125995ms http.response.status=200 http.response.written=2 instance.id=3093e5ab-5715-42bc-808e-73f310848860 version=v2.6.2
registry_1 | 172.18.0.1 - - [07/Nov/2018:17:57:42 +0000] "GET /v2/ HTTP/1.0" 200 2 "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7"
您可以从最后一行看到GET
请求是/v2/
从您的浏览器发送到的端点,即您向其发送请求的端点。容器从端口转发收到您发出的请求,并返回{}
. 200
输出最后一行的代码表示容器成功处理了请求。
按CTRL+C
停止其执行。
现在您已经设置了端口转发,您将继续提高注册表的安全性。
第 3 步 – 设置身份验证
Nginx 允许您为其管理的站点设置 HTTP 身份验证,您可以使用它来限制对 Docker Registry 的访问。为此,您将创建一个身份验证文件,htpasswd
并向其中添加将被接受的用户名和密码组合。
您可以htpasswd
通过安装该apache2-utils
软件包来获取该实用程序。运行:
- sudo apt install apache2-utils -y
您将带有凭据的身份验证文件存储在~/docker-registry/auth
. 通过运行创建它:
- mkdir ~/docker-registry/auth
导航到它:
- cd ~/docker-registry/auth
创建第一个用户,替换username
为您要使用的用户名。该-B
标志命令使用bcrypt
Docker 要求的算法:
- htpasswd -Bc registry.password username
出现提示时输入密码,凭据组合将附加到registry.password
.
注意:要添加更多用户,请在不使用 的情况下重新运行上一个命令-c
,这会创建一个新文件:
- htpasswd -B registry.password username
既然已创建凭据列表,您将进行编辑docker-compose.yml
以命令 Docker 使用您创建的文件对用户进行身份验证。通过运行打开它进行编辑:
- nano ~/docker-registry/docker-compose.yml
添加突出显示的行:
version: '3'
services:
registry:
image: registry:2
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 as a Service
通过指示 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
. 通过运行打开它进行编辑:
- sudo nano /etc/nginx/nginx.conf
找到该http
部分,并添加以下行:
...
http {
client_max_body_size 16384m;
...
}
...
该client_max_body_size
参数现在设置为16384m
,使最大上传大小等于 16GB。
完成后保存并关闭文件。
重新启动 Nginx 以应用配置更改:
- sudo systemctl restart nginx
您现在可以将大图像上传到您的 Docker Registry,而 Nginx 不会阻止传输或出错。
第 6 步 – 发布到您的私有 Docker 注册表
现在您的 Docker Registry 服务器已启动并正在运行,并且可以接受大文件大小,您可以尝试向其推送映像。由于您没有任何现成的ubuntu
图像,您将使用来自公共 Docker 注册表 Docker Hub的图像进行测试。
从您的第二个客户端服务器,运行以下命令来下载ubuntu
映像,运行它,并访问它的 shell:
- docker run -t -i ubuntu /bin/bash
在-i
和-t
标志给你交互shell访问到容器中。
进入后,创建一个SUCCESS
通过运行调用的文件:
- touch /SUCCESS
通过创建此文件,您已经自定义了您的容器。您稍后将使用它来检查您使用的是完全相同的容器。
运行以下命令退出容器外壳:
- 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
您将收到类似于以下内容的输出:
OutputThe push refers to a repository [your_domain/test-image]
420fa2a9b12e: Pushed
c20d459170d8: Pushed
db978cae6a05: Pushed
aeb3f02e9374: Pushed
latest: digest: sha256:88e782b3a2844a8d9f0819dc33f825dde45846b1c5f9eb4870016f2944fe6717 size: 1150
您已验证您的注册表通过登录处理用户身份验证,并允许经过身份验证的用户将图像推送到注册表。您现在将尝试从您的注册表中提取图像。
第 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 文件的更多信息,您可以访问有关最佳实践的官方文档。