作者选择了 Apache Software Foundation作为 Write for Donations计划的一部分接受捐款。
介绍
Docker Registry是一个管理存储和交付 Docker 容器图像的应用程序。注册表集中容器图像并缩短开发人员的构建时间。Docker 图像通过虚拟化保证了相同的运行时间环境,但构建图像可能需要大量的时间。例如,而不是单独安装依赖和包来使用 Docker,开发人员可以从包含所有必要组件的注册表下载压缩图像。
Docker 还有一个免费的公共注册表 Docker Hub,可以托管您自定义的 Docker 图像,但有些情况下,您不希望您的图像公开可用。
在本教程中,您将设置和保护自己的私人Docker注册表. 您将使用 Docker Compose来定义运行Docker应用程序和 Nginx的配置,将服务器流量从HTTPS转移到运行Docker容器。
前提条件
在您开始本指南之前,您将需要以下内容:
一个服务器将托管您的私人Docker注册表,另一个服务器将是您的客户端服务器。Docker和Docker-Compose安装在两个服务器上,仅需完成本教程的第一步才能安装Docker Compose。本教程解释了如何安装Docker作为其先决条件的一部分。
- Nginx安装在您的私人Docker注册表服务器上,如下 How to InstallDocker-Compose on Ubuntu 18.04教程。
- Nginx以您的私人Docker注册表上的名称加密,随后将通过[How to SecureDocker_Inc1使用Docker注册表的流量以确保您的Docker的任何部分被安裝到Docks(LINK2))。
步骤 1 – 安装和配置 Docker 注册表
Docker 命令行工具有助于启动和管理一个或两个 Docker 容器,但为了实现完整的部署,大多数在 Docker 容器内运行的应用程序需要其他组件并行运行,例如,许多 Web 应用程序包括一个 Web 服务器,如 Nginx,它提供应用程序的代码,一个解释的脚本语言,如 PHP,和一个数据库服务器,如 MySQL。
使用 Docker Compose,您可以编写一个.yml 文件来设置每个容器的配置和容器需要相互通信的信息,然后您可以使用docker-compose
命令行工具发出命令给构成您的应用程序的所有组件。
Docker 注册表本身是一个具有多个组件的应用程序,因此您将使用 Docker Compose 来管理您的配置. 要启动注册表的实例,您将设置一个 docker-compose.yml
文件来定义您的注册表将存储其数据的位置。
在您为托管私人 Docker 注册表创建的服务器上,您可以创建一个docker 注册表
目录,移动到它,然后使用以下命令创建一个数据
子文件夹:
1mkdir ~/docker-registry && cd $_
2mkdir data
使用文本编辑器创建docker-compose.yml
配置文件:
1nano docker-compose.yml
将以下内容添加到文件中,描述了 Docker 注册表的基本配置:
1[label docker-compose.yml]
2version: '3'
3
4services:
5 registry:
6 image: registry:2
7 ports:
8 - "5000:5000"
9 environment:
10 REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
11 volumes:
12 - ./data:/data
环境
部分将环境变量设置在Docker注册表容器中的路径 /data
. Docker注册表应用程序在启动时检查此环境变量,因此开始将其数据保存到 `/data’文件夹中。
但是,当您包含了卷: -. / 数据: / 数据
行时,Docker 将开始将该容器中的 / 数据
目录映射到您的注册表服务器上的 / 数据
。
端口
部分,配置为5000:5000
,告诉Docker在服务器上将端口5000
地图到运行容器中的端口5000
。
现在您可以启动 Docker Compose 来检查设置:
1docker-compose up
您将在输出中看到下载栏,显示 Docker 从 Docker 自己的注册表中下载 Docker 注册表图像。
1[secondary_label Output of docker-compose up]
2Starting docker-registry_registry_1 ... done
3Attaching to docker-registry_registry_1
4registry_1 | time="2018-11-06T18:43:09Z" 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.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2
5registry_1 | time="2018-11-06T18:43:09Z" level=info msg="redis not configured" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2
6registry_1 | time="2018-11-06T18:43:09Z" level=info msg="Starting upload purge in 20m0s" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2
7registry_1 | time="2018-11-06T18:43:09Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2
8registry_1 | time="2018-11-06T18:43:09Z" level=info msg="listening on [::]:5000" go.version=go1.7.6 instance.id=c63483ee-7ad5-4205-9e28-3e809c843d42 version=v2.6.2
您将在本教程中稍后发出没有提供 HTTP 秘密
警告消息,输出显示容器正在启动,输出的最后一行显示它已成功开始在端口5000
上收听。
默认情况下,Docker Compose 将继续等待您的输入,因此按CTRL+C
关闭您的 Docker 注册表容器。
您已经在端口5000
上设置了完整的Docker注册表,此时注册表将不会启动,除非您手动创建注册表。此外,Docker注册表没有内置的身份验证机制,因此它目前不安全,并且完全向公众开放。
步骤 2 — 设置 Nginx 端口转发
您已经在您的 Docker 注册表服务器上设置了 HTTPS 与 Nginx,这意味着您现在可以设置从 Nginx 到 `5000 端口的端口转发。
作为 How to Secure Nginx With Let's Encrypt的前提条件的一部分,您已经设置了包含您的服务器配置的 /etc/nginx/sites-available/example.com
文件。
使用您的文本编辑器打开此文件:
1sudo nano /etc/nginx/sites-available/example.com
查找现有的位置
线,它会看起来像这样:
1[label /etc/nginx/sites-available/example.com]
2...
3location / {
4 ...
5}
6...
您需要将流量转发到端口5000
,在那里您的注册表将运行。您还希望将请求标题附加到注册表中,这些标题会为每个请求和响应提供服务器的额外信息。
1[label /etc/nginx/sites-available/example.com]
2...
3location / {
4 # Do not allow connections from docker 1.5 and earlier
5 # docker pre-1.6.0 did not properly set the user agent on ping, catch "Go *" user agents
6 if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
7 return 404;
8 }
9
10 proxy_pass http://localhost:5000;
11 proxy_set_header Host $http_host; # required for docker client's sake
12 proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
13 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
14 proxy_set_header X-Forwarded-Proto $scheme;
15 proxy_read_timeout 900;
16}
17...
$http_user_agent
部分验证了客户端的Docker版本是否超过了1.5
,并确保UserAgent
不是一个Go
应用程序,因为您正在使用注册表的版本2.0
,所以不支持较旧的客户端。
保存和退出文件. 通过重新启动 Nginx 来应用更改:
1sudo service nginx restart
您可以通过运行注册表来确认 Nginx 正在将流量转发到端口 `5000’:
1cd ~/docker-registry
2docker-compose up
在浏览器窗口中,打开以下URL:
1https://example.com/v2
您将看到一个空的 JSON 对象,或者:
1{}
在您的终端中,您将看到类似于以下的输出:
1[secondary_label Output of docker-compose up]
2registry_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
3registry_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/
,这是您从您的浏览器发送请求的终端点。
现在您已经设置了端口转发,您可以继续改进您的注册表的安全性。
步骤三:设置身份验证
借助 Nginx 代理请求,您现在可以使用 HTTP 身份验证来保护您的注册表,以管理谁可以访问您的 Docker 注册表。 为了实现这一目标,您将创建一个具有htpasswd
的身份验证文件,并添加用户。
您可以通过运行以下操作来安装htpasswd
包:
1sudo apt install apache2-utils
现在,您将创建存储我们的身份验证凭证的目录,并将其更改为该目录。 $_
扩展到上一个命令的最后一个参数,在这种情况下 ~/docker-registry/auth
:
1mkdir ~/docker-registry/auth && cd $_
接下来,您将以以下方式创建第一个用户,将用户名
替换为您想要使用的用户名。B
标志指定了bcrypt
加密,比默认加密更安全。
1htpasswd -Bc registry.password username
<$>[注]
注: 若要添加更多用户,请在没有 -c 选项的情况下重新运行之前的命令(c
是为创建):
1htpasswd registry.password username
美元
接下来,您将编辑docker-compose.yml
文件,告诉Docker使用您创建的文件来验证用户。
1cd ~/docker-registry
2nano docker-compose.yml
您可以为您创建的auth/
目录添加环境变量和卷,通过编辑docker-compose.yml
文件来告诉Docker如何验证用户。
1[label docker-compose.yml]
2version: '3'
3
4services:
5 registry:
6 image: registry:2
7 ports:
8 - "5000:5000"
9 environment:
10 REGISTRY_AUTH: htpasswd
11 REGISTRY_AUTH_HTPASSWD_REALM: Registry
12 REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
13 REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
14 volumes:
15 - ./auth:/auth
16 - ./data:/data
对于REGISTRY_AUTH
,您已指定htpasswd
,即您正在使用的身份验证方案,并将REGISTRY_AUTH_HTPASSWD_PATH
设置为身份验证文件的路径。
现在,您可以通过运行注册表来验证您的身份验证是否正确,并检查它是否要求用户提供用户名和密码。
1docker-compose up
在浏览器窗口中,打开`https://example.com/v2′′。
输入用户名
和相应的密码后,您将再次看到{}
。您已经确认了基本的身份验证设置:注册表只在您输入正确的用户名和密码后返回了结果。
步骤 4 – 启动 Docker 注册表作为服务
你想确保你的注册表每次系统启动时都会启动. 如果有任何意外的系统故障,你想确保注册表在服务器重新启动时重新启动。
1nano docker-compose.yml
在注册表
下添加以下内容行:
1[label docker-compose.yml]
2...
3 registry:
4 restart: always
5...
您可以开始注册表作为一个背景过程,这将允许您退出ssh
会话并坚持这个过程:
1docker-compose up -d
随着您的注册表在背景中运行,您现在可以准备 Nginx 进行文件上传。
步骤 5 — 增加 Nginx 的文件上传大小
在您可以将图像推到注册表之前,您需要确保您的注册表能够处理大型文件上传,尽管Docker将大图像上传分为单独的层,但它们有时可能超过1GB
。
1sudo nano /etc/nginx/nginx.conf
找到http
部分,然后添加以下行:
1[label /etc/nginx/nginx.conf]
2...
3http {
4 client_max_body_size 2000M;
5 ...
6}
7...
最后,重新启动 Nginx 以应用配置更改:
1sudo service nginx restart
您现在可以将大图像上传到您的Docker注册表,而无需 Nginx 错误。
步骤 6 – 发布到您的私人 Docker 注册表
现在你已经准备好将图像发布到你的私人Docker注册表,但首先你必须创建一个图像. 对于本教程,你将创建一个简单的图像,基于来自Docker Hub的ubuntu
图像。 Docker Hub是一个公共托管的注册表,有很多预配置的图像,可以利用快速Dockerize应用程序。
从您的 客户端服务器创建一个小,空的图像来推到您的新注册表,-i
和-t
旗帜为您提供交互式容器访问:
1[environment second]
2docker run -t -i ubuntu /bin/bash
完成下载后,您将进入 Docker 提示,请注意以下root@
的容器 ID 会有所不同. 通过创建名为SUCCESS
的文件快速更改文件系统。
1[environment second]
2touch /SUCCESS
退出 Docker 容器:
1[environment second]
2exit
下面的命令会创建一个名为测试图像
的新图像,基于已经运行的图像以及您所做的任何更改。
承诺改变:
1[environment second]
2docker commit $(docker ps -lq) test-image
此时,该图像仅在本地存在,现在您可以将其推到您创建的新注册表。
1[environment second]
2docker login https://example.com
输入用户名
和相应的密码从前面。接下来,你将标记图像与私人注册表的位置,以推动它:
1[environment second]
2docker tag test-image example.com/test-image
将新标记的图像推到注册表:
1[environment second]
2docker push example.com/test-image
您的输出将看起来如下:
1[secondary_label Output]
2[environment second]
3The push refers to a repository [example.com/test-image]
4e3fbbfb44187: Pushed
55f70bf18a086: Pushed
6a3b5c80a4eba: Pushed
77f18b442972b: Pushed
83ce512daaf78: Pushed
97aae4540b42d: Pushed
10...
您已验证您的注册表处理用户身份验证,并允许身份验证的用户将图像推到注册表。
步骤 7 – 从您的私人 Docker 注册表中提取
返回您的注册表服务器,以便您可以测试从您的客户端服务器中提取图像,也可以从第三方服务器中测试。
使用您之前设置的用户名和密码登录:
1docker login https://example.com
您现在已经准备好拉动图像了,使用您在上一步标记的域名和图像名称:
1docker pull example.com/test-image
Docker将下载图像并将您返回提示. 如果您在注册表服务器上运行图像,您将看到您之前创建的SUCCESS
文件在那里:
1docker run -it example.com/test-image /bin/bash
列出您的文件在 bash 壳中:
1ls
您将看到您为此图像创建的SUCCESS
文件:
1SUCCESS bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
您已完成设置一个安全的注册表,用户可以推和拖动自定义图像。
结论
在本教程中,您设置了自己的私人Docker注册表,并发布了Docker图像。正如介绍中提到的,您还可以使用TravisCI(https://docs.travis-ci.com/user/docker/)或类似的CI工具来自动推送到私人注册表。通过将Docker和注册表应用于您的工作流程,您可以确保包含代码的图像在任何机器上产生相同的行为,无论是在生产中还是在开发中。