引言
Docker 仓库是一个管理 Docker 容器镜像存储和分发的应用。仓库集中管理容器镜像,并为开发者减少构建时间。Docker 镜像通过虚拟化保证相同的运行环境,但构建一个镜像可能需要大量的时间投入。例如,开发者不必单独安装依赖和包来使用 Docker,而是可以从仓库下载一个包含所有必要组件的压缩镜像。此外,开发者可以使用 TravisCI 等持续集成工具自动化推送镜像到仓库,以在生产和开发期间无缝更新镜像。
在本教程中,你将设置并保护你自己的私有 Docker 仓库。你将使用 Docker Compose 来定义配置,运行你的 Docker 容器,并使用 Nginx 将服务器流量从互联网转发到运行中的 Docker 容器。完成本教程后,你将能够将自定义 Docker 镜像推送到你的私有仓库,并从远程服务器安全地拉取镜像。
前提条件
完成本教程,你需要以下条件:
- 两台按照 Ubuntu 初始服务器设置指南设置的 Ubuntu 服务器,包括一个
sudo
非根用户和防火墙。一台服务器将托管你的私有 Docker 仓库,另一台将是你的客户端服务器。 - 两台服务器上都安装了 Docker,你可以通过遵循如何在 Ubuntu 上安装和使用 Docker 的步骤 1 和 2 来设置。
在托管服务器上,你需要设置:
-
在托管服务器上安装 Docker Compose,你可以通过遵循如何在 Ubuntu 上安装和使用 Docker Compose 的步骤 1 来设置。
-
在你的托管服务器上安装 Nginx,你可以通过遵循如何在 Ubuntu 上安装 Nginx 的步骤来设置。
-
为你的私有 Docker 仓库使用 Let’s Encrypt 保护 Nginx,你可以通过遵循如何在 Ubuntu 上使用 Let’s Encrypt 保护 Nginx 的教程来设置。确保在步骤 4 中将所有流量从 HTTP 重定向到 HTTPS。
- 一个注册的域名,解析到你用来托管私有 Docker 仓库的服务器。你将作为 Let’s Encrypt 先决条件的一部分来设置它。在本教程中,我们将称之为
your_domain
。
建议使用至少有以下配置的服务器:
4 核心的 CPU,4GB 的内存
选择服务器提供商
为了本教程的演示,我将以一个具体的云服务提供商为例,展示如何进行操作。选择哪个提供商根据个人偏好和需求来决定。以下步骤仅供参考,请根据实际需求选择配置。
购买云服务器
本示例中,我们选择了香港作为服务器区域。点击 云产品 → 云服务器 → 立即购买
选择操作系统
在创建服务器实例时,选择 Ubuntu 作为操作系统。连接到服务器
使用 X-shell 或偏好的 SSH 客户端,通过远程用户名和密码连接到服务器。成功连接后,将看到特定的欢迎信息,表明已成功登录。 - 一个注册的域名,解析到你用来托管私有 Docker 仓库的服务器。你将作为 Let’s Encrypt 先决条件的一部分来设置它。在本教程中,我们将称之为
第 1 步 — 安装和配置 Docker 仓库
在命令行上运行 Docker 对于初学者和测试容器很有用,但对于涉及多个并行运行容器的更大部署,可能会变得不便。
使用 Docker Compose,你可以通过编写一个 .yml
文件来设置每个容器的配置和信息,以便容器之间能够相互通信。你可以使用 docker compose
工具向你的应用程序的所有组件发出命令,并作为一个组来控制它们。
Docker 仓库本身是一个具有多个组件的应用,因此你将使用 Docker Compose 来管理它。要启动仓库的一个实例,你将设置一个 docker-compose.yml
文件来定义它以及你的仓库将存储其数据的磁盘位置。
你将在托管服务器上的一个名为 docker-registry
的目录中存储配置。通过运行以下命令创建它:
mkdir -p ~/docker-registry
cd ~/docker-registry
然后,创建一个名为 data
的子目录,你的仓库将在这里存储其镜像:
mkdir -p ~/docker-registry/data
通过运行以下命令创建并打开一个名为 docker-compose.yml
的文件:
touch ~/docker-registry/docker-compose.yml
nano ~/docker-registry/docker-compose.yml
添加以下内容,定义一个基本的 Docker 仓库实例:
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 -d
仓库容器及其依赖项将被下载并启动。
你可以按 CTRL+C
停止其执行。
在这一步中,你创建了一个 Docker Compose 配置,启动了一个在端口 5000
上监听的 Docker 仓库。在接下来的步骤中,你将在你的域名下暴露它并设置认证。
第 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`,运行以下命令:
```bash
curl -I https://your_domain/v2
然后,在浏览器窗口中,导航到你的域名并访问 v2
端点,如下所示:
https://your_domain/v2
浏览器将加载一个空的 JSON 对象:
{}
容器由于端口转发接收了请求,并返回了 {}
的响应。代码 200
表示容器成功处理了请求。
按终端中的 CTRL+C
停止其执行。
现在你已经设置了端口转发,你将增强仓库的安全性。
第 3 步 — 设置认证
Nginx 允许你为其管理的网站设置 HTTP 认证,你可以用它来限制对你的 Docker 仓库的访问。为了实现这一点,你将创建一个带有 htpasswd
的认证文件,并向其中添加用户名和密码组合,这些组合将被接受。这个过程将启用对你的仓库的认证。
你可以通过安装 apache2-utils
包来获取 htpasswd
工具。运行以下命令来安装它:
sudo apt-get update
sudo apt-get install apache2-utils
你将在 ~/docker-registry/auth
下存储带有凭据的认证文件。通过运行以下命令创建它:
mkdir -p ~/docker-registry/auth
cd ~/docker-registry/auth
创建第一个用户,将 username
替换为你想要使用的用户名。-B
标志命令使用 bcrypt
算法,这是 Docker 要求的:
htpasswd -Bbn username password > registry.password
输入密码时会提示。凭据组合将被追加到 registry.password
。
注意: 要添加更多用户,请在没有 -c
的情况下重新运行上一个命令:
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 -d
在你的浏览器中,刷新你的域名页面。系统将提示你输入用户名和密码。
在提供有效的凭据组合后,你将访问带有空 JSON 对象的页面:
{}
你已经成功认证并获得了对仓库的访问权限。按终端中的 CTRL+C
退出。
你的仓库现在是安全的,只有在认证后才能访问。你接下来将配置它作为后台进程运行,同时在重启后自动启动。
第 4 步 — 将 Docker 仓库作为服务启动
你可以确保仓库容器在系统每次启动时都会启动,或者在崩溃后,通过指示 Docker Compose 始终保持其运行。
打开 docker-compose.yml
进行编辑:
nano ~/docker-registry/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 仓库,而不会有 Nginx 阻止传输或出错。
第 6 步 — 发布到你的私有 Docker 仓库
现在你的 Docker 仓库服务器正在运行并接受大文件大小,你可以尝试将镜像推送到它。由于你没有现成的镜像可用,你将使用 Docker Hub 上的 ubuntu
镜像,这是一个公共 Docker 仓库,来测试这个。
在客户端服务器的新终端会话中,运行以下命令下载 ubuntu
镜像,运行它,并获取其 shell 访问:
docker run -it ubuntu /bin/bash
i
和t
标志给你提供了容器的交互式 shell 访问。
一旦进入,通过运行以下命令创建一个名为 SUCCESS
的文件:
touch SUCCESS
通过创建这个文件,你已经自定义了你的容器。你稍后将使用它来检查你使用的是否完全相同的容器。
通过运行以下命令退出容器 shell:
exit
现在从你刚刚自定义的容器创建一个新的镜像:
docker commit [CONTAINER_ID] your_domain/test-image
将 [CONTAINER_ID]
替换为你的容器 ID。
这个新镜像现在在本地可用,你将把它推送到你的容器仓库。首先,你必须登录:
docker login your_domain
当提示时,输入你在第 3 步中定义的用户名和密码凭据。
输出将是:
Login Succeeded
登录成功后,重命名创建的镜像:
docker tag your_domain/test-image your_domain/test-image:latest
最后,将新标记的镜像推送到你的仓库:
docker push your_domain/test-image: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 your_domain
尝试通过运行以下命令拉取 test-image
:
docker pull your_domain/test-image:latest
Docker 将下载镜像。使用以下命令运行容器:
docker run -it your_domain/test-image:latest /bin/bash
它将加载容器的 shell。
通过运行以下命令列出存在的文件:
ls
文件列表将包括你之前创建的 SUCCESS
文件,证实这个容器使用的是你创建的相同镜像:
退出容器 shell:
exit
你已经测试了推送和拉取镜像,并完成了设置一个安全的仓库,你可以用它来存储自定义镜像。
结论
在本教程中,你设置了你自己的私有 Docker 仓库并发布了一个 Docker 镜像到它。如引言中提到的,你还可以使用 TravisCI 或类似的 CI 工具自动化推送到私有仓库。