作者选择了阿帕奇软件基金会接受捐赠作为为捐款而写程序。
Docker 注册表是一个管理存储和交付 Docker 容器镜像的应用程序。注册表集中容器映像并减少开发人员的构建时间。 Docker 镜像通过虚拟化保证相同的运行时环境,但构建镜像可能需要大量时间投入。例如,开发人员可以从包含所有必要组件的注册表下载压缩映像,而不是单独安装依赖项和包来使用 Docker。此外,开发人员可以使用持续集成工具自动将图像推送到注册表,例如TravisCI,在生产和开发过程中无缝更新图像。
Docker 还有一个免费的公共注册表,码头工人中心,它可以托管您的自定义 Docker 映像,但在某些情况下您不希望您的映像公开可用。映像通常包含运行应用程序所需的所有代码,因此在使用专有软件时最好使用私有注册表。
在本教程中,您将设置并保护您自己的私有 Docker 注册表。你将使用Docker 组合定义运行 Docker 应用程序和 Nginx 的配置,以将服务器流量从 HTTPS 转发到正在运行的 Docker 容器。完成本教程后,您将能够将自定义 Docker 映像推送到您的私有注册表,并从远程服务器安全地拉取该映像。
在开始本指南之前,您需要满足以下条件:
- 通过以下方式设置两台 Ubuntu 18.04 服务器Ubuntu 18.04 初始服务器设置指南,包括 sudo 非 root 用户和防火墙。一台服务器将托管您的私有 Docker 注册表,另一台服务器将是您的client server.
- 按照以下步骤在两台服务器上安装 Docker 和 Docker-Compose如何在 Ubuntu 18.04 上安装 Docker-Compose教程。您只需完成本教程的第一步即可安装 Docker Compose。本教程解释了如何安装 Docker 作为其先决条件的一部分。
- 按照以下步骤将 Nginx 安装在您的私有 Docker 注册表服务器上如何在 Ubuntu 18.04 上安装 Nginx.
- Nginx 通过 Let’s Encrypt 在您的服务器上为私有 Docker 注册表进行保护,具体方法如下如何使用 Let’s Encrypt 保护 Nginx。确保在步骤 4 中将所有流量从 HTTP 重定向到 HTTPS。
- 解析为您用于私有 Docker 注册表的服务器的域名。您将把它设置为 Let’s Encrypt 先决条件的一部分。
Docker 命令行工具对于启动和管理一两个 Docker 容器非常有用,但是,为了完全部署在 Docker 容器内运行的大多数应用程序,需要其他组件并行运行。例如,许多 Web 应用程序由提供应用程序代码的 Web 服务器(如 Nginx)、解释性脚本语言(如 PHP)和数据库服务器(如 MySQL)组成。
使用 Docker Compose,您可以编写一个.yml
文件来设置每个容器的配置以及容器相互通信所需的信息。然后您可以使用docker-compose
命令行工具向构成应用程序的所有组件发出命令。
Docker Registry 本身是一个具有多个组件的应用程序,因此您将使用 Docker Compose 来管理您的配置。要启动注册表实例,您将设置一个docker-compose.yml
文件来定义注册表将存储其数据的位置。
在您创建的用于托管私有 Docker 注册表的服务器上,您可以创建一个docker-registry
目录,移入其中,然后创建一个data
子文件夹包含以下命令:
-
mkdir〜/ docker-registry&& cd $_
-
mkdir data
使用文本编辑器创建docker-compose.yml
配置文件:
将以下内容添加到该文件中,该内容描述了 Docker 注册表的基本配置:
docker-compose.yml
version: '3'
services:
registry:
image: registry:2
ports:
- "5000:5000"
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
volumes:
- ./data:/data
The environment
部分使用路径在 Docker 注册表容器中设置环境变量/data
。 Docker注册表应用程序在启动时检查此环境变量,然后开始将其数据保存到/data
folder.
但是,由于您已包括volumes: - ./data:/data
行,Docker 将开始映射/data
该容器中的目录/data
在您的注册服务器上。最终结果是 Docker 注册表的数据全部存储在~/docker-registry/data
在注册服务器上。
The ports
部分,带有配置5000:5000
,告诉 Docker 映射端口5000
在服务器上要端口5000
在运行的容器中。这允许您向端口发送请求5000
在服务器上,并将请求转发到注册表应用程序。
您现在可以启动 Docker Compose 来检查设置:
您将在输出中看到下载栏,显示 Docker 从 Docker 自己的注册表下载 Docker 注册表映像。一两分钟内,您将看到类似于以下内容的输出(版本可能有所不同):
docker-compose up 的输出
Starting docker-registry_registry_1 ... done
Attaching to docker-registry_registry_1
registry_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
registry_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
registry_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
registry_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
registry_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
您将解决No HTTP secret provided
本教程后面的警告消息。输出显示容器正在启动。输出的最后一行显示它已成功开始侦听端口5000
.
默认情况下,Docker Compose 将继续等待您的输入,因此请点击CTRL+C
关闭您的 Docker 注册表容器。
您已经在端口上设置了完整的 Docker 注册表侦听5000
。此时,除非您手动启动,否则注册表将不会启动。此外,DockerRegistry没有任何内置的身份验证机制,因此它目前不安全并且完全向公众开放。在以下步骤中,您将解决这些安全问题。
You already have HTTPS set up on your Docker Registry server with Nginx, which means you can now set up port forwarding from Nginx to port 5000
. Once you complete this step, you can access your registry directly at example.com.
As part of the How to Secure Nginx With Let’s Encrypt prerequisite, you have already set up the /etc/nginx/sites-available/example.com
file containing your server configuration.
使用文本编辑器打开此文件:
-
sudo nano /etc/nginx/sites-available/example.com
找到现有的location
线。它看起来像这样:
/etc/nginx/sites-available/example.com
...
location / {
...
}
...
您需要将流量转发到端口5000
,您的注册表将在其中运行。您还希望将标头附加到注册表的请求中,该标头为每个请求和响应提供来自服务器的附加信息。删除内容location
部分,并将以下内容添加到该部分:
/etc/nginx/sites-available/example.com
...
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;
}
...
The $http_user_agent
部分验证客户端的 Docker 版本是否在上面1.5
,并确保UserAgent
不是一个Go
应用。由于您使用的是版本2.0
注册表中,不支持旧客户端。欲了解更多信息,您可以找到nginx
标头配置在Docker 注册表 Nginx 指南.
保存并退出文件。通过重新启动 Nginx 应用更改:
您可以确认 Nginx 正在将流量转发到端口5000
通过运行注册表:
-
cd〜/ docker-registry
-
docker 撰写 up
在浏览器窗口中,打开以下 url:
https://example.com/v2
您将看到一个空的 JSON 对象,或者:
{}
在您的终端中,您将看到类似于以下内容的输出:
docker-compose up 的输出
registry_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
输出的最后一行表示容器已成功处理请求。
现在您已经设置了端口转发,您可以继续提高注册表的安全性。
通过正确处理 Nginx 请求,您现在可以使用 HTTP 身份验证来保护您的注册表,以管理谁有权访问您的 Docker 注册表。为了实现这一点,您将创建一个身份验证文件htpasswd
并向其中添加用户。 HTTP 身份验证可以通过 HTTPS 连接快速设置和保护,注册表将使用 HTTPS 连接。
您可以安装htpasswd
通过运行以下命令进行打包:
-
sudo apt installapache2-utils
现在,您将创建存储我们的身份验证凭据的目录,并更改为该目录。$_
扩展为上一个命令的最后一个参数,在本例中~/docker-registry/auth
:
-
mkdir〜/ docker-registry / auth&& cd $_
Next, you will create the first user as follows, replacing username
with the username you want to use. The -B
flag specifies bcrypt
encryption, which is more secure than the default encryption. Enter the password when prompted:
- htpasswd -Bc registry.password username
Note:要添加更多用户,请重新运行上一个命令(不带 -c 选项)(c
用于创建):
- htpasswd registry.password username
接下来,您将编辑docker-compose.yml
文件告诉 Docker 使用您创建的文件来验证用户身份。
-
cd〜/ docker-registry
-
nanodocker-compose.yml
您可以添加环境变量和卷auth/
您创建的目录,通过编辑docker-compose.yml
文件告诉 Docker 您希望如何对用户进行身份验证。将以下突出显示的内容添加到文件中:
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
For REGISTRY_AUTH
,您已指定htpasswd
,这是您正在使用的身份验证方案,并设置REGISTRY_AUTH_HTPASSWD_PATH
到认证文件的路径。最后,REGISTRY_AUTH_HTPASSWD_REALM
表示的名称htpasswd
realm.
现在,您可以通过运行注册表并检查它是否提示用户输入用户名和密码来验证身份验证是否正常工作。
In a browser window, open https://example.com/v2
.
After entering username
and the corresponding password, you will see {}
once again. You’ve confirmed the basic authentication setup: the registry only returned the result after you entered the correct username and password. You have now secured your registry, and can continue to using the registry.
您希望确保注册表在系统启动时启动。如果出现任何不可预见的系统崩溃,您需要确保注册表在服务器重新启动时重新启动。打开docker-compose.yml
:
在下面添加以下行内容registry:
:
docker-compose.yml
...
registry:
restart: always
...
您可以将注册表作为后台进程启动,这将允许您退出ssh
会话并持久化该过程:
当您的注册表在后台运行时,您现在可以准备 Nginx 来上传文件。
在将映像推送到注册表之前,您需要确保注册表能够处理大文件上传。尽管 Docker 将大图像上传分成单独的层,但有时它们可能会结束1GB
。默认情况下,Nginx 的限制为1MB
关于文件上传,因此您需要编辑配置文件nginx
并将最大文件上传大小设置为2GB
.
-
sudo nano/etc/nginx/nginx.conf
找出http
部分,并添加以下行:
/etc/nginx/nginx.conf
...
http {
client_max_body_size 2000M;
...
}
...
最后,重新启动 Nginx 以应用配置更改:
现在,您可以将大图像上传到 Docker 注册表,而不会出现 Nginx 错误。
您现在已准备好将映像发布到您的私有 Docker 注册表,但首先您必须创建一个映像。在本教程中,您将基于以下内容创建一个简单的图像ubuntu
来自 Docker Hub 的图像。 Docker Hub 是一个公共托管的注册表,具有许多预配置的映像,可用于快速 Dockerize 应用程序。使用ubuntu
图像,您将测试向注册表的推送和拉取。
从你的client服务器,创建一个小的空图像以推送到您的新注册表,-i
and -t
标志使您可以交互式 shell 访问容器:
-
docker run -t -iubuntu /bin/bash
下载完成后,您将进入 Docker 提示符,请注意您的容器 IDroot@
会有所不同。通过创建一个名为的文件来快速更改文件系统SUCCESS
。在下一步中,您将能够使用此文件来确定发布过程是否成功:
退出 Docker 容器:
以下命令创建一个名为的新图像test-image
基于已经运行的图像以及您所做的任何更改。在我们的例子中,添加/SUCCESS
文件包含在新图像中。
提交更改:
-
docker commit $(docker ps -lq)测试图像
此时,该图像仅存在于本地。现在您可以将其推送到您创建的新注册表中。登录到您的 Docker 注册表:
-
docker login https://example.com
Enter the username
and corresponding password from earlier. Next, you will tag the image with the private registry’s location in order to push to it:
-
docker tag test-image example.com/test-image
将新标记的图像推送到注册表:
-
docker push example.com/test-image
您的输出将类似于以下内容:
Output
The push refers to a repository [example.com/test-image]
e3fbbfb44187: Pushed
5f70bf18a086: Pushed
a3b5c80a4eba: Pushed
7f18b442972b: Pushed
3ce512daaf78: Pushed
7aae4540b42d: Pushed
...
您已验证您的注册表处理用户身份验证,并允许经过身份验证的用户将图像推送到注册表。接下来,您将确认您也能够从注册表中提取映像。
返回到您的注册表服务器,以便您可以测试从您的注册表中提取映像client服务器。也可以从第三个服务器进行测试。
使用您之前设置的用户名和密码登录:
-
docker login https://example.com
现在您已准备好提取图像。使用您在上一步中标记的域名和图像名称:
-
docker pull example.com/test-image
Docker 将下载映像并返回到提示符。如果您在注册表服务器上运行该映像,您将看到SUCCESS
您之前创建的文件在那里:
-
docker run -it example.com/test-image /bin/bash
列出 bash shell 中的文件:
你会看到SUCCESS
您为此图像创建的文件:
SUCCESS bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
您已经完成了安全注册表的设置,用户可以向其中推送和拉取自定义映像。
在本教程中,您设置了自己的私有 Docker 注册表,并发布了 Docker 映像。正如简介中提到的,您还可以使用TravisCI或类似的 CI 工具来自动直接推送到私有注册表。通过在工作流程中利用 Docker 和注册表,您可以确保包含代码的映像将在任何机器上产生相同的行为,无论是在生产中还是在开发中。有关编写 Docker 文件的更多信息,您可以阅读此内容Docker教程解释过程。