如何在DigitalOceanSpaces之上设置私有Docker注册表并将其与DigitalOceanKubernetes一起使用
作为 Write for DOnations 计划的一部分,作者选择了 Free and Open Source Fund 来接受捐赠。
介绍
Docker 注册表 是用于命名 Docker 映像的存储和内容交付系统,它是容器化应用程序的行业标准。 与公共 Docker 注册表相比,私有 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。 您只需完成Step 1和Step 2。 否则,请访问 Docker 的 网站 了解其他发行版。
Docker Hub 上的一个帐户,用于存储您将在本教程中创建的 Docker 映像。
Git 安装在您将用于访问集群的计算机上。 对于 Ubuntu 18.04,请遵循 如何在 Ubuntu 18.04 上安装 Git 教程的 Step 1。 其他平台请访问【X31X】官网【X51X】。
一个 DigitalOcean Kubernetes 集群,您的连接配置配置为
kubectl
默认值。 如何配置kubectl
的说明显示在创建集群时显示的 连接到集群 步骤下。 要了解如何在 DigitalOcean 上创建 Kubernetes 集群,请参阅 Kubernetes 快速入门 。带有 API 密钥(访问和秘密)的 DigitalOcean 空间。 要了解如何创建 DigitalOcean Space 和 API 密钥,请参阅 如何创建 DigitalOcean Space 和 API 密钥。
安装在本地计算机上的 Helm 包管理器。 为此,请完成 如何使用 Helm 3 包管理器 教程在 Kubernetes 集群上安装软件的 步骤 1。
安装在集群上的 Nginx Ingress Controller 和 Cert-Manager。 有关如何执行此操作的指南,请参阅 如何使用 Helm 在 DigitalOcean Kubernetes 上设置 Nginx 入口。
具有两个 DNS A 记录的域名指向 Ingress 使用的 DigitalOcean 负载均衡器。 如果您使用 DigitalOcean 管理您域的 DNS 记录,请参考 如何管理 DNS 记录 创建 A 记录。 在本教程中,我们将 A 记录称为
registry.your_domain
和k8s-test.your_domain
。注意:本教程中使用的域名必须与如何在DigitalOcean Kubernetes先决条件教程中设置Nginx Ingress中使用的域名不同。
第 1 步 — 配置和安装 Docker 注册表
在此步骤中,您将为注册表部署创建一个配置文件,并使用 Helm 包管理器使用给定的配置将注册表安装到您的集群中。
在本教程的过程中,您将使用一个名为 chart_values.yaml
的配置文件来覆盖 Docker 注册表 Helm chart 的一些默认设置。 Helm 将其包称为图表; 这些文件集概述了 Kubernetes 资源的相关选择。 您将编辑设置以将 DigitalOcean Spaces 指定为底层存储系统,并通过连接 Let's Encrypt TLS 证书来启用 HTTPS 访问。
作为 Nginx 入口控制器先决条件的一部分,您创建了示例服务和入口。 在本教程中您将不需要它们,因此您可以通过运行以下命令来删除它们:
kubectl delete -f hello-kubernetes-first.yaml kubectl delete -f hello-kubernetes-second.yaml kubectl delete -f hello-kubernetes-ingress.yaml
kubectl delete
命令在传递 -f
参数时接受要删除的文件。
我们将使用 GitLab 的 Container Registry 分支,而不是使用与 S3 存储提供程序存在问题的官方 Docker 注册表,您需要下载和构建它。
创建一个文件夹作为您的工作区:
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
添加以下行,确保将突出显示的行替换为您的详细信息:
图表值.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 对象使外部 HTTP/HTTPS 路由指向集群中的内部服务,从而允许来自外部的通信。 被覆盖的值为:
enabled
:设置为true
以启用 Ingress。hosts
:Ingress 将接受来自其的流量的主机列表。annotations
:元数据列表,为 Kubernetes 的其他部分提供有关如何处理 Ingress 的进一步指导。 您将 Ingress Controller 设置为nginx
,将 Let's Encrypt 集群颁发者设置为生产变体 (letsencrypt-prod
),并告诉nginx
控制器接受最大大小为 30 的文件GB,即使是最大的 Docker 镜像,这也是一个合理的限制。tls
:此子类别配置 Let's Encrypt HTTPS。 您使用我们的示例域名填充hosts
列表,该列表定义此 Ingress 将接受来自哪些安全主机的 HTTPS 流量。secretName
(此处设置为docker-registry-prod
)指定将存储证书的密钥的名称,通常对于您创建或部署的每个 Ingress 都必须不同。
然后,将文件系统存储设置为 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 注册表。 目前,注册表是空的。 要推送一些内容,您需要在您正在使用的机器上提供可用的图像。 让我们为此目的使用 mysql
Docker 映像。
从 Docker Hub 中拉出 mysql
开始:
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
选择并复制显示的内容。
要将身份验证添加到 Docker 注册表,您需要编辑 chart_values.yaml
并将 htpasswd_file
的内容添加到 htpasswd
变量中。
打开chart_values.yaml
进行编辑:
nano chart_values.yaml
找到如下所示的行:
图表值.yaml
htpasswd: ""
编辑它以匹配以下内容,将 htpasswd\_file\_contents
替换为您从 htpasswd_file
复制的内容:
图表值.yaml
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 并指示它在应用 chart_values.yaml
文件后升级现有版本,在您的情况下为 docker-registry
,其图表在图表存储库的 stable/docker-registry
中定义。
现在,您将尝试再次从注册表中提取图像:
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
来指定在 pod 定义中拉取容器时 Kubernetes 应该使用哪个 secret。 当 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,以使应用程序可以在集群外部访问。 添加以下行,将突出显示的行替换为您的域:
你好世界.yaml
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 部署定义 Ingress,您将通过 Nginx Ingress Controller 拥有的负载均衡器对其进行路由。 然后,您定义一个可以访问部署中创建的 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
您现在可以导航到您的测试域 — 本教程中的第二条 A 记录 k8s-test.your_domain
。 您将看到 Kubernetes Hello world! 页面。
Hello World 页面列出了一些环境信息,例如 Linux 内核版本和提供请求的 pod 的内部 ID。 您还可以通过 Web 界面访问您的空间,以查看您在本教程中使用过的图像。
如果要在测试后删除此 Hello World 部署,请运行以下命令:
kubectl delete -f hello-world.yaml
在此步骤中,您创建了一个示例 Hello World 部署,以测试 Kubernetes 是否正确地从您的私有注册表中提取图像。
结论
您现在已经成功地在您的 DigitalOcean Kubernetes 集群上部署了您自己的私有 Docker 注册表,使用 DigitalOcean Spaces 作为下面的存储层。 您可以存储的图像数量没有限制,空间可以无限扩展,同时提供相同的安全性和稳健性。 但是,在生产环境中,您应该尽可能地优化 Docker 映像,请查看 如何优化生产环境的 Docker 映像 教程。