如何使用密码、SSL 和 S3 后端运行自己的 Docker 镜像仓库
启动注册表
密码时间
私有SSL证书
S3存储后端
客户端连接
用于检查注册表内容的 API
概括
我还记得开始使用 Docker 的那天。它的简洁性让我惊叹不已。我仔细阅读了官方文档(写得非常好),还找了一些入门教程。我成功创建了我的第一个镜像。现在有了镜像,
我想把它部署到我的服务器上。第一个选择是DockerHub,但免费账户要求你共享镜像。如果你想保持镜像的私密性,这项服务就需要付费。
或者,您也可以运行自己的Docker 镜像仓库,并拥有以下灵活性:
- 用密码保护它
- 将图像存储在本地或云端(S3/Azure/GoogleCloud)
- 使用您自签名的 SSL 证书
“该注册表是一个无状态、高度可扩展的服务器端应用程序,用于存储和分发 Docker 镜像。”
Docker 官方文档对此主题进行了深入讲解。您应该阅读该文档以了解它是什么以及如何运行它。在本文中,我将讨论我在部署注册表服务器时遇到的问题。我将解释如何实现上面提到的三项内容。本文的其余部分假设您已阅读官方网站上的“注册表概述”和“了解 Docker 注册表” (约 3 分钟)。
启动注册表
注册表应用本身运行在容器中。它有一个前端组件(可选),允许您通过浏览器访问注册表数据。每个组件都在各自的容器中运行,这些容器构成了应用栈。应用栈是一组相互链接以执行特定任务的应用(栈)。我们可以使用docker-compose来运行应用栈,而无需单独运行每个容器。
这是我们最初使用的 docker-compose.yml 文件:
version: '2'
registry:
image: registry:v2
restart: always
ports:
- "5000"
environment:
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
REGISTRY_HTTP_ADDR: 0.0.0.0:5000
registry-frontend:
image: konradkleine/docker-registry-frontend:v2
restart: always
environment:
ENV_DOCKER_REGISTRY_HOST: 'registry'
ENV_DOCKER_REGISTRY_PORT: 5000
links:
- registry
ports:
- "8080:80"
expose:
- 80
让我们简单分析一下,因为它只是简单的配置(没有密码、S3 或 SSL)。
registry和registry-frontend是我技术栈中的容器名称。您可以随意命名。环境变量REGISTRY_STORAGE_DELETE_ENABLED允许您从注册表中删除镜像,而REGISTRY_HTTP_ADDR则绑定监听地址。
注册表前端容器使用ENV_DOCKER_REGISTRY_HOST和ENV_DOCKER_REGISTRY_PORT作为其连接的地址。由于我们使用了 docker-compose,因此可以通过名称访问它。links配置将容器链接到另一个服务,在本例中是注册表。
你难道不好奇端口和暴露设置之间的区别吗?区别在于,端口设置会让主机以及compose文件中其他服务都能访问到定义的端口,而暴露设置实际上只是将端口暴露给其他服务,而不会将其发布到主机。在这种情况下,只能指定内部端口。
端口还连接外部端口(在主机上定义)和容器分配的端口。对于容器而言registry,端口 5000 可以从任何地方访问。另一方面,注册表前端仅将端口 80 暴露给文件中定义的其他服务,而从外部访问则被限制在端口 8080。
要运行这些服务,我们需要在docker-compose.ymldocker-compose up文件所在的目录下运行命令。 您可以使用以下命令验证服务是否正在运行。如果容器已启动,您现在可以访问http://localhost:8080查看您的镜像仓库数据。docker ps
密码时间
为了搭建我的私有镜像仓库,我想增加一些安全措施。我想设置密码保护。
设置完成后,客户端首次登录镜像仓库时需要输入密码。
这非常简单,网上有一篇很棒的文章详细介绍了如何操作。您可以查看Medium 上的文章“Private Docker Registry Part 2: let's add basic authentication”,并根据自身需求进行配置。
私有SSL证书
安全始终是一个重要问题。在概念验证阶段,我们往往会忽略这一点。我们总是想先让产品快速运行起来。但这需要额外的步骤,而我们通常会把它留到最后。
我的建议是——只要能用 SSL,就用它。这值得你多花些时间,而且没有任何缺点。Docker
客户端默认使用 HTTPS,所以在我看来,我们有三种选择:
- 让客户端使用 HTTP 而不是 HTTPS
- 配置客户端以信任我们的注册表,并启用insecure-registries参数
- 将我们的证书添加到受信任的证书列表中,可以是 Docker 引擎的证书,也可以是操作系统的证书。
我认为第三种方案最适合我。我会使用我自己的服务器证书。
我假设您已经为服务器生成了证书(cert.crt、cert.pem 和 ca-certificate)。
请将公钥和私钥放在/var/lib/docker/certs目录下。为了让镜像仓库使用这些证书,我们需要在镜像仓库的环境变量下,将以下内容添加到我们的配置中:
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/registry_gnosis.crt
REGISTRY_HTTP_TLS_KEY: /certs/registry_gnosis_key.pem
volumes:
- /var/lib/docker/certs:/certs
好了,我们的服务器端现在已经使用证书了。太棒了!但是还没完。
为了让客户端能够连接到镜像仓库,我们需要它们的工作站信任我们的证书颁发机构 (CA)。否则,连接将无法建立。如果您的客户端机器已经信任您的 CA 证书,那就完成了。否则,您需要将 CA 证书复制到客户端机器上。您可以将其复制到`/etc/docker /certs.d` 目录,Docker 会自动信任它;或者,您也可以手动让操作系统信任它(在 Ubuntu 系统上,其他操作系统可能有所不同):
- 将其复制到 /usr/local/share/ca-certificates/
- sudo update-ca-certificates
完成上述操作后,请务必在客户端重启 Docker 服务。
现在我们的注册表已经具备基本身份验证和 SSL 加密支持,接下来我们继续进行最后的配置步骤。在解释完如何使用S3 作为后端之后,我将演示如何连接到注册表。
S3存储后端
镜像仓库为我们提供镜像服务。我们可以向其中拉取镜像,也可以从中推送镜像。因此,这些镜像必须存储在某个磁盘上。默认情况下,它们位于运行镜像仓库的 Docker 主机上。您可以显式地将其挂载到不同的卷上,但您还可以使用更酷炫的方法。我使用AWS S3 存储服务作为后端。这意味着,我上传到镜像仓库的每个镜像都会保存在 S3 上的一个专用存储桶中。
这样做的好处是:
- 我不需要操心备份的事。
- 我的存储空间无限。
- 灾难恢复方面,万一我的注册表运行所在的机器发生故障或其他意外,我可以部署一个新服务并将其连接到 S3 存储桶。所有数据都会被保留。
请再读一遍第 3 点。这太重要了。只要我在docker-compose.yml文件中配置好 S3 存储,我就可以在世界任何地方启动另一个镜像仓库容器,从而访问我的所有镜像。
要配置存储,我们需要在注册表组件的环境部分添加以下代码片段:
REGISTRY_STORAGE: s3
REGISTRY_STORAGE_S3_ACCESSKEY: <api access key>
REGISTRY_STORAGE_S3_SECRETKEY: <api secret>
REGISTRY_STORAGE_S3_BUCKET: <bucket name>
REGISTRY_STORAGE_S3_REGION: <region>
REGISTRY_HEALTH_STORAGEDRIVER_ENABLED: false
这很简单,但最后一个很重要。REGISTRY_HEALTH_STORAGEDRIVER_ENABLED参数很重要。
在我添加它之前,我遇到了一些问题。我不记得具体的错误信息了,但经过一番搜索,我发现如果使用空存储桶运行注册表(我的存储桶就是空的),则此健康检查会失败,服务也无法启动。所以我不得不禁用它,问题就解决了。
上传数据后,您可以选择启用此检查。但我个人觉得这无关紧要,所以一直保持禁用状态。
客户端连接
为了使用我的新镜像仓库,我需要连接客户端,以便它可以从仓库拉取或推送镜像。请记住,我们的镜像仓库和 Docker 客户端默认都使用 SSL,因此我需要信任我的 CA,如上所述。
要连接,请执行docker login -u <user> <url:port>(不带 https 前缀)。
现在,在我的 Ubuntu 机器上执行此操作时,我遇到了另一个错误:
“保存凭据时出错:存储凭据时出错 - 错误:退出状态 1,输出:无法在没有 X11 $DISPLAY 的情况下自动启动 D-Bus”。
为了解决这个问题,我需要从操作系统中删除一个软件包:apt remove golang-docker-credential-helpers
这样就解决了。登录镜像仓库后,默认情况下,凭据保存在隐藏目录~/.docker/config.json中的一个文件中。显然这不是最佳实践,你可以在这里找到更好的替代方案。
推拉
要拉取或推送镜像,我们只需引用我们镜像仓库的地址即可docker pull our-registry.com:<port>/image。
如果您没有域名,也可以使用 IP 地址。
用于检查注册表内容的 API
我将使用一个虚拟域名registry.gnosis.org:5000作为我的镜像仓库服务器。
容器运行后,我可以通过访问http://gnosis.example.com查看镜像仓库的内容,包括仓库、镜像和标签。如果您决定不使用前端,或者需要在应用程序中使用它,则可以通过其 API 访问镜像仓库。
我将举一些有用的例子,
# list of the repositories
chen@gns:~$ curl -ksS -u admin https://gnosis.example.com/v2/_catalog
Enter host password for user 'admin':
{"repositories":["sample_image","nginx"]}
# list of tags for an image
chen@gns:~$ curl -ksS -u admin https://gnosis.example.com/v2/sample_image/tags/list
Enter host password for user 'admin':
{"name":"sample_image","tags":["0.1", "0.2", "latest"]}
概括
我详细介绍了部署注册表的过程。我知道我没有详尽地讲解,但这并非本文的重点。如果您需要一个私有注册表,搭建起来非常简单,网上有很多教程可以参考。我分享了
在部署注册表时遇到的问题以及我采用的配置方法。
使用密码和 SSL 作为安全措施非常重要,切记这一点。如果可以使用云服务提供商存储图片,可以省去很多麻烦。但如果无法使用云服务提供商,也并非世界末日,只需注意我列出的那些事项即可。
完成后的效果图如下docker-compose.yml:
version: '2'
registry:
image: registry:v2
restart: always
ports:
- "5000"
environment:
REGISTRY_STORAGE_DELETE_ENABLED: 'true'
REGISTRY_HTTP_ADDR: 0.0.0.0:5000
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/registry_gnosis.crt
REGISTRY_HTTP_TLS_KEY: /certs/registry_gnosis_key.pem
REGISTRY_STORAGE: s3
REGISTRY_STORAGE_S3_ACCESSKEY: <api access key>
REGISTRY_STORAGE_S3_SECRETKEY: <api secret>
REGISTRY_STORAGE_S3_BUCKET: <bucket name>
REGISTRY_STORAGE_S3_REGION: <region>
REGISTRY_HEALTH_STORAGEDRIVER_ENABLED: false
volumes:
- /var/lib/docker/certs:/certs
registry-frontend:
image: konradkleine/docker-registry-frontend:v2
restart: always
environment:
ENV_DOCKER_REGISTRY_HOST: 'registry'
ENV_DOCKER_REGISTRY_PORT: 5000
links:
- registry
ports:
- "8080:80"
expose:
- 80
