作为Write for DOnations计划的一部分,作者选择了免费和开源基金来接受捐赠。
介绍
一个码头工人的注册表是名为泊坞图像,这是集装箱应用的行业标准的存储和内容传送系统。与公共镜像相比,私有 Docker 注册表允许您在团队或组织内安全地共享您的镜像,具有更大的灵活性和控制力。通过将私有 Docker 注册表直接托管在 Kubernetes 集群中,您可以获得更高的速度、更低的延迟和更好的可用性,同时控制注册表。
底层注册表存储被委托给外部驱动程序。默认存储系统是本地文件系统,但您可以将其替换为基于云的存储驱动程序。DigitalOcean Spaces是一种兼容 S3 的对象存储,专为需要可扩展、简单且经济实惠的方式来存储和服务海量数据的开发人员团队和企业而设计,非常适合存储 Docker 镜像。内置CDN网络,可以大大减少频繁访问图片时的延迟。
在本教程中,您将使用Helm将私有 Docker 注册表部署到DigitalOcean Kubernetes集群,由 DigitalOcean Spaces 备份以存储数据。您将为指定的空间创建 API 密钥,使用自定义配置将 Docker 注册表安装到您的集群,配置 Kubernetes 以对其进行正确的身份验证,并通过在集群上运行示例部署来测试它。在本教程结束时,您将在 DigitalOcean Kubernetes 集群上安装一个安全的私有 Docker 注册表。
先决条件
在开始本教程之前,您需要:
-
Docker 安装在您将访问集群的机器上。对于 Ubuntu 18.04,请访问如何在 Ubuntu 18.04 上安装和使用 Docker。您只需要完成第 1步和第 2 步。否则,请访问 Docker 的网站以获取其他发行版。
-
Docker Hub 上的一个帐户,用于存储您将在本教程中创建的 Docker 映像。
-
Git 安装在您将用于访问集群的机器上。为Ubuntu 18.04,遵循步骤1中的如何在Ubuntu上安装的Git 18.04教程。其他平台请访问官网。
-
一个 DigitalOcean Kubernetes 集群,您的连接配置被配置为
kubectl
默认值。创建集群时kubectl
显示的“连接到集群”步骤下显示了有关如何配置的说明。要了解如何在 DigitalOcean 上创建 Kubernetes 集群,请参阅Kubernetes 快速入门。 -
带有 API 密钥(访问和秘密)的 DigitalOcean 空间。要了解如何创建 DigitalOcean 空间和 API 密钥,请参阅如何创建 DigitalOcean 空间和 API 密钥。
-
安装在本地计算机上的 Helm 包管理器。要做到这一点,完成第1步的如何与头盔3包管理器上Kubernetes集群上安装软件的教程。
-
安装在集群上的 Nginx Ingress Controller 和 Cert-Manager。有关如何执行此操作的指南,请参阅如何使用 Helm 在 DigitalOcean Kubernetes 上设置 Nginx Ingress。
-
带有两条 DNS A 记录的域名指向 Ingress 使用的 DigitalOcean Load Balancer。如果您使用 DigitalOcean 来管理域的 DNS 记录,请参阅如何管理 DNS 记录以创建 A 记录。在本教程中,我们将 A 记录称为
registry.your_domain
和k8s-test.your_domain
。
注意:您在本教程中使用的域名必须与如何在 DigitalOcean Kubernetes先决条件教程中设置 Nginx Ingress 中使用的域名不同。
第 1 步 – 配置和安装 Docker Registry
在此步骤中,您将为注册表部署创建一个配置文件,并使用 Helm 包管理器使用给定的配置将注册表安装到您的集群。
在本教程的过程中,您将使用一个名为的配置文件chart_values.yaml
来覆盖 Docker 注册表 Helm chart 的一些默认设置。Helm 称其为包、图表;这些文件集概述了 Kubernetes 资源的相关选择。您将编辑设置以将 DigitalOcean Spaces 指定为底层存储系统,并通过连接 Let’s Encrypt TLS 证书来启用 HTTPS 访问。
作为 Nginx Ingress Controller 先决条件的一部分,您创建了示例服务和一个 Ingress。在本教程中您不需要它们,因此您可以通过运行以下命令来删除它们:
- kubectl delete -f hello-kubernetes-first.yaml
- kubectl delete -f hello-kubernetes-second.yaml
- kubectl delete -f hello-kubernetes-ingress.yaml
kubectldelete
命令在传递-f
参数时接受要删除的文件。
我们将使用 GitLab 的Container Registry fork,而不是使用官方 Docker 注册表(它与 S3 存储提供程序存在问题),您需要下载和构建它。
创建一个文件夹作为您的工作区:
- mkdir ~/k8s-registry
通过运行导航到它:
- cd ~/k8s-registry
git
通过运行以下命令下载 Container Registry 存储库:
- git clone https://gitlab.com/gitlab-org/container-registry.git
输出将类似于:
OutputCloning into 'container-registry'...
remote: Enumerating objects: 1706, done.
...
Resolving deltas: 100% (13955/13955), done.
存储库现在位于container-registry
目录中。导航到它:
- cd container-registry
您现在拥有容器注册表的源代码。要在集群中使用它,您需要从中构建一个 Docker 映像并将其推送到公共注册表,例如 Docker Hub。
运行以下命令切换到最新稳定版本的分支:
- git checkout v2.13.1-gitlab
运行以下命令以构建注册表的 Docker 映像,替换your_dockerhub_username
为您的 Docker Hub 用户名:
- docker build -t your_dockerhub_username/registry:dev .
此命令可能需要一些时间才能完成。输出会很长,应该类似于:
Output...
Successfully built 27322ec15cf7
Successfully tagged your_dockerhub_username/registry:dev
现在镜像已构建,要将其推送到您的帐户,您首先需要登录:
- docker login
出现提示时输入您的 Docker Hub 用户名和密码。输出的结尾应如下所示:
Output...
Login Succeeded
您现在可以推送图像:
- docker push your_dockerhub_username/registry:dev
最终输出将如下所示:
OutputThe push refers to repository [docker.io/your_dockerhub_username/registry]
c3baf7582a54: Pushed
bc49969a328b: Pushed
0694fbf8288a: Pushed
3e207b409db3: Mounted from library/alpine
dev: digest: sha256:02399157107a1d72312fb4f383f4c8c53a08f3e206d787a9c9380f446b008184 size: 1156
既然您已经构建并推送了注册表,请导航回您的工作区:
- cd ~/k8s-registry
chart_values.yaml
使用您喜欢的文本编辑器创建您的文件:
- nano chart_values.yaml
添加以下行,确保用您的详细信息替换突出显示的行:
ingress:
enabled: true
hosts:
- registry.your_domain
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "30720m"
tls:
- secretName: docker-registry-prod
hosts:
- registry.your_domain
storage: s3
secrets:
htpasswd: ""
s3:
accessKey: "your_space_access_key"
secretKey: "your_space_secret_key"
s3:
region: your_space_region
regionEndpoint: your_space_region.digitaloceanspaces.com
secure: true
bucket: your_space_name
image:
repository: your_dockerhub_username/registry
tag: dev
第一个块,ingress
配置将作为 Helm 图表部署的一部分创建的 Kubernetes Ingress。Ingress 对象使外部 HTTP/HTTPS 路由指向集群中的内部服务,从而允许来自外部的通信。覆盖的值是:
enabled
: 设置为true
启用 Ingress。hosts
:Ingress 将接受流量的主机列表。annotations
:元数据列表,为 Kubernetes 的其他部分提供关于如何处理 Ingress 的进一步指导。您将 Ingress Controller 设置为nginx
,将 Let’s Encrypt 集群颁发者设置为生产变体 (letsencrypt-prod
),并告诉nginx
控制器接受最大大小为 30 GB 的文件,这对于即使是最大的 Docker 映像也是一个合理的限制。tls
:这个子类别配置让我们加密 HTTPS。您hosts
使用我们的示例域名填充定义此 Ingress 将从哪些安全主机接受 HTTPS 流量的列表。该secretName
(此处设为docker-registry-prod
)指定该证书(S)将所存储的秘密的名字,一般必须为您创建的每个入口或部署不同。
然后,您将文件系统存储设置为s3
– 另一个可用选项是filesystem
. 这里s3
表示使用与 DigitalOcean Spaces 实现的行业标准 Amazon S3 API 兼容的远程存储系统。
在下一个块中,secrets
配置用于访问s3
子类别下的 DO 空间的密钥。最后,在s3
块中,您配置指定空间的参数。
在文件的末尾,您将刚刚推送的注册表镜像指定为将要部署的镜像,而不是官方的 Docker 注册表。
保存并关闭文件。
现在,如果您还没有这样做,请设置您的 A 记录以指向您在先决条件教程中作为 Nginx Ingress Controller 安装的一部分创建的负载均衡器。要了解如何在 DigitalOcean 上设置您的 DNS,请参阅如何管理 DNS 记录。
部署 Docker 注册表的图表位于twuni存储库中。通过运行将其添加到 Helm:
- helm repo add twuni https://helm.twun.io
在从中安装任何东西之前,您需要刷新其缓存。这将更新有关图表存储库的最新信息。为此,请运行以下命令:
- helm repo update
现在,您将通过 Helm 运行以下自定义配置来部署 Docker 注册表图表:
- helm install docker-registry twuni/docker-registry -f chart_values.yaml
您将看到以下输出:
OutputNAME: docker-registry
LAST DEPLOYED: ...
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
https://registry.your_domain/
现在可以从您之前指定的域名访问注册表。
您已在 Kubernetes 集群上配置并部署了 Docker 注册表。接下来,您将测试新部署的 Docker 注册表的可用性。
第 2 步 – 测试推和拉
在这一步中,您将通过向新部署的 Docker 注册表推送和拉取镜像来测试新部署的 Docker 注册表。目前,注册表是空的。要推送某些内容,您需要在您工作的机器上有一个可用的图像。mysql
为此,让我们使用Docker 映像。
首先mysql
从 Docker Hub拉取:
- docker pull mysql
您的输出将如下所示:
OutputUsing default tag: latest
latest: Pulling from library/mysql
27833a3ba0a5: Pull complete
...
e906385f419d: Pull complete
Digest: sha256:9643e9fbd6330d10686f8922292dcb20995e7b792c17d4e94ddf95255f1d5449
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest
您现在可以在本地使用该图像。要通知 Docker 将其推送到何处,您需要使用主机名对其进行标记,如下所示:
- docker tag mysql registry.your_domain/mysql
然后,将映像推送到新注册表:
- docker push registry.your_domain/mysql
此命令将成功运行并指示您的新注册表已正确配置并接受流量 – 包括推送新图像。如果您看到错误,请根据步骤 1 和 2 仔细检查您的步骤。
要干净地测试从注册表中提取,首先mysql
使用以下命令删除本地图像:
- docker rmi registry.your_domain/mysql && docker rmi mysql
然后,从注册表中提取它:
- docker pull registry.your_domain/mysql
此命令将需要几秒钟才能完成。如果它运行成功,则意味着您的注册表工作正常。如果显示错误,请根据之前的命令仔细检查您输入的内容。
您可以通过运行以下命令列出本地可用的 Docker 映像:
- docker images
您将看到列出本地计算机上可用图像的输出,以及它们的 ID 和创建日期。
您的 Docker 注册表已配置。您已向其推送图像并验证您可以将其拉下。现在让我们添加身份验证,以便只有某些人可以访问图像。
第 3 步 – 添加帐户身份验证和配置 Kubernetes 访问
在此步骤中,您将使用该htpasswd
实用程序为注册表设置用户名和密码身份验证。
该htpasswd
实用程序来自 Apache 网络服务器,您可以使用它来创建文件,这些文件存储用于 HTTP 用户基本身份验证的用户名和密码。htpasswd
文件的格式是username:hashed_password
(每行一个),它具有足够的可移植性以允许其他程序也使用它。
htpasswd
为简单起见,您将使用 Dockerized 变体。运行以下命令登录组合追加到htpasswd_file
,更换username
和password
使用所需的凭据:
- docker run --rm -ti xmartlabs/htpasswd username password >> htpasswd_file
Docker 要求使用bcrypt算法对密码进行哈希处理,此处隐式使用了该算法。bcrypt 算法是一种基于 Blowfish 分组密码的密码散列函数,带有一个工作因子参数,该参数指定散列函数的成本。
您可以为想要添加的任意数量的用户重复此命令。
完成后,htpasswd_file
通过运行以下命令显示内容:
- cat htpasswd_file
选择并复制显示的内容。
要添加验证您多克尔注册表,你需要编辑chart_values.yaml
和添加内容htpasswd_file
的htpasswd
变量。
打开chart_values.yaml
编辑:
- nano chart_values.yaml
找到如下所示的行:
htpasswd: ""
编辑它以匹配以下内容,替换htpasswd\_file\_contents
为您从 复制的内容htpasswd_file
:
htpasswd: |-
htpasswd_file_contents
注意缩进,文件内容的每一行前必须有四个空格。您可以删除空行(如果有)。
添加内容后,保存并关闭文件。
要将更改传播到您的集群,请运行以下命令:
- helm upgrade docker-registry twuni/docker-registry -f chart_values.yaml
输出将类似于您第一次部署 Docker 注册表时显示的输出:
OutputRelease "docker-registry" has been upgraded. Happy Helming!
NAME: docker-registry
LAST DEPLOYED: ...
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
https://registry.your_domain/
此命令调用 Helm 并指示它在应用文件后升级现有版本,在您的情况下docker-registry
,其图表stable/docker-registry
在图表存储库中定义chart_values.yaml
。
现在,您将再次尝试从注册表中提取图像:
- docker pull registry.your_domain/mysql
输出将如下所示:
OutputUsing default tag: latest
Error response from daemon: Get https://registry.your_domain/v2/mysql/manifests/latest: no basic auth credentials
它正确失败,因为您没有提供凭据。这意味着您的 Docker 注册表正确地授权了请求。
要登录到注册表,请运行以下命令:
- docker login registry.your_domain
请记住替换registry.your_domain
为您的域地址。它会提示您输入用户名和密码。如果它显示错误,请仔细检查您htpasswd_file
包含的内容。您必须从htpasswd_file
在此步骤前面创建的 中指定用户名和密码组合。
要测试登录,您可以通过运行以下命令再次尝试拉取:
- docker pull registry.your_domain/mysql
输出将类似于以下内容:
OutputUsing default tag: latest
latest: Pulling from mysql
Digest: sha256:f2dc118ca6fa4c88cde5889808c486dfe94bccecd01ca626b002a010bb66bcbe
Status: Image is up to date for registry.your_domain/mysql:latest
您现在已经配置了 Docker 并且可以安全地登录。要将 Kubernetes 配置为登录到您的注册表,请运行以下命令:
- sudo kubectl create secret docker-registry regcred --docker-server=registry.your_domain --docker-username=your_username --docker-password=your_password
此命令在您的集群中创建一个密钥,其名称regcred
将包含您的注册表的登录信息并将其解析为dockerconfigjson
,它在 Kubernetes 中定义了一个注册表凭据。
请记住替换registry.your_domain
为您的注册域,并将您之前创建的登录凭据之一替换为your_username
和your_password
。
您将看到以下输出:
Outputsecret/regcred created
您已经htpasswd
创建了一个登录配置文件,配置了注册表来验证请求,并创建了一个包含登录凭证的 Kubernetes 秘密。接下来,您将测试 Kubernetes 集群和注册表之间的集成。
第 4 步 – 通过运行示例部署来测试 Kubernetes 集成
在此步骤中,您将使用存储在集群内注册表中的映像运行示例部署,以测试 Kubernetes 集群和注册表之间的连接。
在最后一步中,您创建了一个名为 的机密regcred
,其中包含您的私有注册表的登录凭据。它可能包含多个注册表的登录凭据,在这种情况下,您必须相应地更新 Secret。
您可以通过指定imagePullSecrets
. 当 Docker 注册表需要身份验证时,此步骤是必需的。
现在,您将从私有 Docker 注册表中部署一个示例Hello World 映像到您的集群。首先,为了推送它,您将通过运行以下命令将其拉到您的机器上:
- docker pull paulbouwer/hello-kubernetes:1.8
然后,通过运行标记它:
- docker tag paulbouwer/hello-kubernetes:1.8 registry.your_domain/paulbouwer/hello-kubernetes:1.8
最后,将其推送到您的注册表:
- docker push registry.your_domain/paulbouwer/hello-kubernetes:1.8
从您的机器中删除它,因为您在本地不再需要它:
- docker rmi registry.your_domain/paulbouwer/hello-kubernetes:1.8
现在,您将部署示例 Hello World 应用程序。首先,hello-world.yaml
使用文本编辑器创建一个新文件:
- nano hello-world.yaml
接下来,您将定义一个 Service 和一个 Ingress,以使集群外部可以访问该应用程序。添加以下行,用您的域替换突出显示的行:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-kubernetes-ingress
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: k8s-test.your_domain
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: hello-kubernetes
port:
number: 80
---
apiVersion: v1
kind: Service
metadata:
name: hello-kubernetes
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
selector:
app: hello-kubernetes
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-kubernetes
spec:
replicas: 3
selector:
matchLabels:
app: hello-kubernetes
template:
metadata:
labels:
app: hello-kubernetes
spec:
containers:
- name: hello-kubernetes
image: registry.your_domain/paulbouwer/hello-kubernetes:1.8
ports:
- containerPort: 8080
imagePullSecrets:
- name: regcred
首先,您为 Hello World 部署定义入口,您将通过 Nginx 入口控制器拥有的负载均衡器路由该入口。然后,您定义一个可以访问部署中创建的 pod 的服务。在实际部署规范中,您将 指定image
为位于您的注册表中的那个,并设置imagePullSecrets
为regcred
您在上一步中创建的 。
保存并关闭文件。要将其部署到您的集群,请运行以下命令:
- kubectl apply -f hello-world.yaml
您将看到以下输出:
Outputingress.extensions/hello-kubernetes-ingress created
service/hello-kubernetes created
deployment.apps/hello-kubernetes created
您现在可以导航到您的测试域 —k8s-test.your_domain
本教程中的第二个 A 记录。您将看到 Kubernetes Hello world!页。
Hello World 页面列出了一些环境信息,例如 Linux 内核版本和发出请求的 pod 的内部 ID。您还可以通过网络界面访问您的空间,以查看您在本教程中使用的图像。
如果要在测试后删除此 Hello World 部署,请运行以下命令:
- kubectl delete -f hello-world.yaml
在此步骤中,您创建了一个示例 Hello World 部署来测试 Kubernetes 是否正确地从您的私有注册表中提取图像。
结论
您现在已经在 DigitalOcean Kubernetes 集群上成功部署了自己的私有 Docker 注册表,并使用 DigitalOcean Spaces 作为底层存储层。您可以存储的图像数量没有限制,Spaces 可以无限扩展,同时提供相同的安全性和稳健性。但是,在生产中,您应该始终尽可能地优化 Docker 映像,请查看如何为生产优化 Docker 映像教程。