作者选择了 自由和开源基金作为 写给捐款计划的一部分接受捐款。
介绍
Docker Registry是一个管理存储和交付 Docker 容器图像的应用程序。注册表集中容器图像并缩短开发人员的构建时间。Docker 图像通过虚拟化保证相同的运行时间环境,但构建图像可能需要大量的时间。例如,而不是单独安装依赖和包来使用 Docker,开发人员可以从包含所有必要组件的注册表下载压缩图像。
Docker Hub是一个免费的公共注册表,可以托管您自定义的Docker图像,但有些情况下,您不希望您的图像公开可用。
在本教程中,您将设置和保护自己的私人Docker注册表. 您将使用Docker Compose(https://docs.docker.com/compose/)来定义运行Docker容器和Nginx的配置,将服务器流量从互联网转移到运行Docker容器。
前提条件
要完成本教程,您将需要以下内容:
- 两个 Ubuntu 22.04 服务器是按照 Ubuntu 22.04 初始服务器设置指南设置的,包括一个
sudo
非根用户和一个防火墙. 一个服务器将 托管您的私人 Docker 注册表,另一个将是您的 客户端服务器。
在 host 服务器上,您需要设置:
- Docker Compose 安装在 host 服务器上,您可以通过遵循 How To Install and Use Docker Compose on Ubuntu 22.04的步骤 1 来设置。
- Nginx 安装在您的 host 服务器上,您可以通过遵循 How To Secure Nginx with Let's Encrypt on Ubuntu 22.04的步骤来设置。
- Nginx 在您的 host 服务器上使用 Let's Encrypt 加密,您可以通过遵循 How To Secure Nginx with Let's Encrypt on Ubuntu 22.04 教程来设置。
步骤 1 – 安装和配置 Docker 注册表
在命令行上运行Docker在启动和测试集装箱时是有用的,但在涉及多个集装箱并行运行的更大部署时,它可能会变得模糊。
使用 Docker Compose,您可以编写一个.yml 文件来设置每个容器的配置和容器需要相互通信的信息。
Docker 注册表本身是一个具有多个组件的应用程序,所以您将使用 Docker Compose 来管理它. 要启动注册表的实例,您将设置一个 docker-compose.yml
文件来定义它以及您的注册表将存储其数据的磁盘位置。
您将将配置存储在主机服务器上的名为docker-registry
的目录中。
1mkdir ~/docker-registry
导航它:
1cd ~/docker-registry
然后,创建一个名为数据
的子目录,您的注册表将存储其图像:
1mkdir data
创建并打开名为 'docker-compose.yml' 的文件,运行:
1nano docker-compose.yml
添加以下行,这些行定义了 Docker 注册表的基本实例:
1[label ~/docker-registry/docker-compose.yml]
2version: '3'
3
4services:
5 registry:
6 image: registry:latest
7 ports:
8 - "5000:5000"
9 environment:
10 REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
11 volumes:
12 - ./data:/data
首先,您将第一个服务命名为注册表
,并将其图像设置为注册表
,使用最新版本。然后,在端口
下,您将主机上的端口5000
地图到容器的端口5000
,这将允许您将请求发送到服务器上的端口5000
,并将请求发送到注册表。
在环境
部分中,您将REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
变量设置为/data
,指定它应该存储数据的体积。
保存并关闭文件。
您现在可以通过运行开始配置:
1docker compose up
注册表容器及其依赖将下载并启动。
1[secondary_label Output]
2[+] Running 2/2
3 ⠿ Network docker-registry_default Created 0.1s
4 ⠿ Container docker-registry-registry-1 Created 0.1s
5Attaching to docker-registry-registry-1
6docker-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"
7docker-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"
8docker-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"
9docker-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"
10docker-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"
11...
您将在本教程中稍后解决没有提供 HTTP 秘密
的警告消息。
输出的最后一行表示它已经成功启动,听到端口5000
。
您可以按「CTRL + C」來停止執行。
在此步骤中,您创建了一个 Docker Compose 配置,该配置将启动 Docker 注册表在端口5000
上聆听。
步骤 2 — 设置 Nginx 端口转发
作为先决条件的一部分,您在您的域中启用了 HTTPS. 要将您的安全 Docker 注册表暴露在那里,您需要配置 Nginx 来将流量从您的域转移到注册表容器。
您已经设置了包含您的服务器配置的 /etc/nginx/sites-available/your_domain
文件。
1sudo nano /etc/nginx/sites-available/your_domain
查找现有的位置
块:
1[label /etc/nginx/sites-available/your_domain]
2...
3 location / {
4 ...
5 }
6...
您需要将流量转发到端口5000
,您的注册表将听取流量。您还希望将标题附加到向注册表转发的请求中,该请求由服务器提供有关请求本身的额外信息。
1[label /etc/nginx/sites-available/your_domain]
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...
如果
块检查请求的用户代理,并验证 Docker 客户端的版本高于 1.5,并且它不是试图访问的Go
应用程序。
完成后保存并关闭文件. 通过重新启动 Nginx 来应用更改:
1sudo systemctl restart nginx
如果您收到错误消息,请双重检查您添加的配置。
要确认 Nginx 正在正确地将流量转移到您的注册表容器上的端口5000
,请运行它:
1docker compose up
然后,在浏览器窗口中,导航到您的域,并访问v2
终端,如下:
1https://your_domain/v2
浏览器会加载一个空的 JSON 对象:
1[environment third]
2{}
在您的终端中,您将收到类似于以下的输出:
1[secondary_label Output]
2docker-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
3docker-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"
4docker-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"
5docker-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
6docker-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"
从最后一行,你知道一个GET
请求被发送到/v2/
,这是你发送请求的终端。
按CTRL+C
停止执行。
现在你已经设置了端口转发,你将提高你的注册表的安全性。
步骤三:设置身份验证
Nginx 允许您为其管理的网站设置 HTTP 身份验证,您可以使用它来限制访问您的 Docker 注册表. 为了实现这一点,您将创建一个使用 `htpasswd’ 的身份验证文件,并添加用户名和密码组合,这些组合将被接受. 该过程将允许对您的注册表进行身份验证。
您可以通过安装apache2-utils
包来获取htpasswd
实用程序。
1sudo apt install apache2-utils -y
您将存储身份验证文件与凭证在 ~/docker-registry/auth
下。
1mkdir ~/docker-registry/auth
导航它:
1cd ~/docker-registry/auth
创建第一个用户,用你想要使用的用户名替换用户名
。B
旗下命令使用bcrypt
算法, Docker 需要:
1htpasswd -Bc registry.password username
提示时输入密码. 认证组合将附加到 registry.password
。
<$>[注]
注: 若要添加更多用户,请重新运行以前的命令而无需 -c
:
1htpasswd -B registry.password username
-c
会创建一个新的文件,因此删除它会更新现有文件。
现在创建了凭证列表,您将编辑docker-compose.yml
,命令Docker使用您创建的文件来验证用户。
1nano ~/docker-registry/docker-compose.yml
添加突出的线条:
1[label ~/docker-registry/docker-compose.yml]
2version: '3'
3
4services:
5 registry:
6 image: registry:latest
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
您已添加了指定使用 HTTP 身份验证的环境变量,并提供创建的htpasswd
文件的路径。对于REGISTRY_AUTH
,您将htpasswd
指定为其值,即您正在使用的身份验证方案,并将REGISTRY_AUTH_HTPASSWD_PATH
设置为身份验证文件的路径。
您还安装了 ./auth
目录,以使文件在注册表容器中可用。
现在您可以验证您的身份验证是否正常工作. 首先,导航到主目录:
1cd ~/docker-registry
然后,运行注册表,执行:
1docker compose up
在您的浏览器中,更新您的域名页面,您将被要求提供用户名和密码。
提供有效的凭证组合后,您将访问带有空JSON对象的页面:
1[environment third]
2{}
您已成功身份验证并获得访问注册表. 通过在您的终端中按CTRL+C
退出。
您的注册表现在是安全的,只能在身份验证后访问,然后将其配置为作为背景流程运行,同时可以通过自动启动重新启动。
步骤 4 – 启动 Docker 注册表作为服务
您可以通过指示 Docker Compose 始终保持运行,确保注册表容器每次启动或系统崩溃后启动。
打开docker-compose.yml
进行编辑:
1nano docker-compose.yml
将下列行添加到注册表
块中:
1[label docker-compose.yml]
2...
3 registry:
4 restart: always
5...
将重新启动
设置为始终
,确保容器能够存活在重新启动中。
您现在可以通过输入 -d
来启动您的注册表作为背景过程:
1docker compose up -d
随着您的注册表在背景中运行,您可以自由关闭SSH会话,终端和注册表不会受到影响。
由于 Docker 图像的大小可能非常大,您将进一步增加 Nginx 将接受上传的最大文件大小。
步骤 5 — 增加 Nginx 的文件上传大小
在您可以将图像推到注册表之前,您需要确保您的注册表能够处理大型文件上传。在 Nginx 中,文件上传的默认大小限制为1m
,这对于 Docker 图像来说并不足够。
打开它来编辑:
1sudo nano /etc/nginx/nginx.conf
将突出的行添加到http
部分:
1[label /etc/nginx/nginx.conf]
2...
3http {
4 client_max_body_size 16384m;
5 ...
6}
7...
client_max_body_size
参数现在设置为16384m
,使最大上传大小等于16GB。
保存并关闭文件。
重新启动 Nginx 以应用配置更改:
1sudo systemctl restart nginx
在此步骤中,您更新了 Nginx 允许的文件大小,您现在可以将大图像上传到您的 Docker 注册表,而不让 Nginx 阻止传输或错误。
步骤 6 – 发布到您的私人 Docker 注册表
现在你的Docker注册表服务器正在运行并接受大型文件大小,你可以尝试将图像推到它上,因为你没有任何可用的图像,你会使用公共Docker注册表的Docker Hub的ubuntu
图像来测试这一点。
在您的客户端服务器的新终端会话中,运行以下命令来下载ubuntu
图像,运行它,并访问其壳:
1[environment second]
2docker run -t -i ubuntu /bin/bash
i
和t
旗帜为您提供交互式容器进入容器。
一旦你进入,创建一个名为成功
的文件,运行:
1[environment second]
2touch /SUCCESS
通过创建此文件,您已定制您的容器. 您将稍后使用它来检查您是否使用完全相同的容器。
走出容器壳:
1[environment second]
2exit
现在,从您刚刚定制的容器创建一个新图像:
1[environment second]
2docker commit $(docker ps -lq) test-image
新图像现在在本地可用,您将将其推到您的容器注册表。
1[environment second]
2docker login https://your_domain
当被提示时,输入您在步骤 3 中定义的用户名和密码凭证。
产量将是:
1[secondary_label Output]
2[environment second]
3...
4Login Succeeded
一旦登录,重命名创建的图像:
1[environment second]
2docker tag test-image your_domain/test-image
最后,将新标记的图像推到您的注册表:
1[environment second]
2docker push your_domain/test-image
您将收到类似于以下的输出:
1[secondary_label Output]
2[environment second]
3Using default tag: latest
4The push refers to a repository [your_domain/test-image]
51cf9c9034825: Pushed
6f4a670ac65b6: Pushed
7latest: digest: sha256:95112d0af51e5470d74ead77932954baca3053e04d201ac4639bdf46d5cd515b size: 736
您已验证您的注册表通过登录处理用户身份验证,并允许已验证的用户将图像推到注册表。
步骤 7 – 从您的私人 Docker 注册表中提取
现在您已经将图像推到您的私人注册表,您将尝试从中提取。
在主服务器上,使用您之前设置的用户名和密码登录:
1docker login https://your_domain
尝试通过运行引导测试图像
:
1docker pull your_domain/test-image
Docker 将下载图像. 使用以下命令运行容器:
1docker run -it your_domain/test-image /bin/bash
它将为容器加载壳。
通过运行来列出当前的文件:
1ls
文件列表将包含您之前创建的SUCCESS
文件,确认此容器使用您创建的相同图像:
1SUCCESS bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
输出容器壳:
1exit
您已经测试了拍摄和拍摄图像,并完成了安全注册表的设置,您可以使用它来存储自定义图像。
结论
在本教程中,您设置了自己的私人 Docker 注册表并发布了 Docker 图像,正如介绍中提到的,您还可以使用 TravisCI或类似的 CI 工具来自动推送到私人注册表。
通过在工作流中使用 Docker 容器,您可以确保包含代码的图像在任何机器上都具有相同的行为,无论是在生产中还是在开发中。