如何在DigitalOcean Spaces之上设置私有Docker注册表并将其与DO Kubernetes一起使用

私有Docker注册表允许您在团队或组织内安全地共享您的图像。通过直接在Kubernetes集群中托管您的私有Docker注册表,您可以实现更高的速度,更低的延迟和更好的可用性,同时控制注册表。在本教程中,您将使用Helm将您的私有Docker注册表部署到DigitalOcean Kubernetes集群,由DigitalOcean Spaces提供支持。

作者选择了自由和开源基金作为Write for DOnations计划的一部分进行捐赠。

介绍

Docker注册表是用于命名Docker镜像的存储和内容交付系统,它是容器化应用程序的行业标准。 与公共数据库相比,私有Docker注册表允许您在团队或组织内安全地共享您的图像,具有更大的灵活性和控制力。 通过直接在Kubernetes集群中托管您的私有Docker注册表,您可以实现更高的速度,更低的延迟和更好的可用性,同时控制注册表。

底层注册表存储委托给外部驱动程序。 默认存储系统是本地文件系统,但您可以将其交换为基于云的存储驱动程序。 DigitalOcean Spaces是一个S3兼容的对象存储,专为需要可扩展,简单且经济实惠的方式存储和提供大量数据的开发人员团队和企业而设计,非常适合存储Docker镜像。 它有一个内置的CDN网络,可以在频繁访问图像时大大减少延迟。

在本教程中,您将使用Helm将您的私有Docker注册表部署到DigitalOcean Kubernetes集群,由DigitalOcean Spaces备份以存储数据。 您将为指定的Space创建API密钥,使用自定义配置将Docker注册表安装到您的群集,配置Kubernetes以对其进行适当的身份验证,并通过在群集上运行示例部署来对其进行测试。 在本教程结束时,您将在DigitalOcean Kubernetes集群上安装一个安全的私有Docker注册表。

先决条件

在开始本教程之前,您需要:

第1步 - 配置和安装Docker Registry

在此步骤中,您将为注册表部署创建配置文件,并使用Helm软件包管理器将Docker注册表安装到具有给定配置的集群。

在本教程中,您将使用名为chart_values.yaml的配置文件来覆盖Docker注册表Helm 图表的某些默认设置。 赫尔姆称其包裹,图表; 这些是概述相关Kubernetes资源选择的文件集。 您将编辑设置以将DigitalOcean Spaces指定为基础存储系统,并通过连接Let的加密TLS证书来启用HTTPS访问。

作为先决条件的一部分,您将创建echo1echo2服务以及echo_ingress入口以进行测试; 在本教程中您不需要这些,因此您现在可以删除它们。

首先通过运行以下命令删除入口:

kubectl delete -f echo_ingress.yaml

然后,删除两个测试服务:

kubectl delete -f echo1.yaml && kubectl delete -f echo2.yaml

kubectl delete命令在传递-f参数时接受要删除的文件。

创建一个将用作工作区的文件夹:

mkdir ~/k8s-registry

通过运行导航到它:

cd ~/k8s-registry

现在,使用文本编辑器创建chart_values.yaml文件:

nano chart_values.yaml

添加以下行,确保使用您的详细信息替换突出显示的行:

chart_values.yaml
ingress:
  enabled: true
  hosts:
    - registry.example.com
  annotations:
    kubernetes.io/ingress.class: nginx
    certmanager.k8s.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/proxy-body-size: "30720m"
  tls:
    - secretName: letsencrypt-prod
      hosts:
        - registry.example.com

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

第一个块, ingress ,配置将作为Helm图表部署的一部分创建的Kubernetes Ingress。 Ingress对象使外部HTTP / HTTPS路由指向群集中的内部服务,从而允许来自外部的通信。 被覆盖的值为:

  • enabled :设置为true以启用Ingress。
  • hosts :Ingress将接受流量的主机列表。
  • annotations :元数据列表,为Kubernetes的其他部分提供有关如何对待Ingress的进一步指导。 您将Ingress Controller设置为nginx ,将Let的加密集群发行者设置为生产变体( letsencrypt-prod ),并告诉nginx控制器接受最大大小为30 GB的文件,这对即使是最大的Docker镜像也是一个合理的限制。
  • tls :此子类别配置Let的加密HTTPS。 您填充hosts列表,该列表定义此Ingress将使用我们的示例域名接受HTTPS流量的安全主机。

然后,将文件系统存储设置为s3 - 另一个可用选项是filesystem 这里s3表示使用与DigitalOcean Spaces满足的行业标准Amazon S3 API兼容的远程存储系统。

在下一个块中,您可以配置密钥以访问s3子类别下的DO空间。 最后,在s3块中,配置指定Space的参数。

保存并关闭您的文件。

现在,如果您还没有这样做,请将A记录设置为指向您在先决条件教程中作为Nginx Ingress Controller安装的一部分创建的Load Balancer。 要了解如何在DigitalOcean上设置DNS,请参阅如何管理DNS记录

接下来,确保您的Space不为空。 如果您的Space中没有任何文件,Docker注册表将根本不运行。 要解决此问题,请上传文件。 导航到Spaces选项卡,找到您的Space,单击Upload File按钮,然后上传您想要的任何文件。 您可以上传刚刚创建的配置文件。

空文件上传到空白空间

在通过Helm安装任何内容之前,您需要刷新其缓存。 这将更新有关您的图表存储库的最新信息。 为此,请运行以下命令:

helm repo update

现在,您将通过运行Helm,使用此自定义配置部署Docker注册表图表:

helm install stable/docker-registry -f chart_values.yaml --name docker-registry

您将看到以下输出:

NAME:   docker-registry
...
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME                    DATA  AGE
docker-registry-config  1     1s

==> v1/Pod(related)
NAME                              READY  STATUS             RESTARTS  AGE
docker-registry-54df68fd64-l26fb  0/1    ContainerCreating  0         1s

==> v1/Secret
NAME                    TYPE    DATA  AGE
docker-registry-secret  Opaque  3     1s

==> v1/Service
NAME             TYPE       CLUSTER-IP      EXTERNAL-IP  PORT(S)   AGE
docker-registry  ClusterIP  10.245.131.143  <none>       5000/TCP  1s

==> v1beta1/Deployment
NAME             READY  UP-TO-DATE  AVAILABLE  AGE
docker-registry  0/1    1           0          1s

==> v1beta1/Ingress
NAME             HOSTS                 ADDRESS  PORTS  AGE
docker-registry  registry.example.com  80, 443  1s


NOTES:
1. Get the application URL by running these commands:
  https://registry.example.com/

Helm列出了由于Docker注册表图表部署而创建的所有资源。 现在可以从您之前指定的域名访问注册表。

您已在Kubernetes群集上配置并部署了Docker注册表。 接下来,您将测试新部署的Docker注册表的可用性。

第2步 - 测试推动和拉动

在此步骤中,您将通过推送和从中拉出图像来测试新部署的Docker注册表。 目前,注册表是空的。 要想要推动某些东西,您需要在您正在使用的机器上提供图像。 我们来使用mysql Docker镜像。

首先从Docker Hub中提取mysql

sudo docker pull mysql

您的输出将如下所示:

Using default tag: latest
latest: Pulling from library/mysql
27833a3ba0a5: Pull complete
...
e906385f419d: Pull complete
Digest: sha256:a7cf659a764732a27963429a87eccc8457e6d4af0ee9d5140a3b56e74986eed7
Status: Downloaded newer image for mysql:latest

您现在可以在本地使用该图像。 要通知Docker在哪里推送它,您需要使用主机名标记它,如下所示:

sudo docker tag mysql registry.example.com/mysql

然后,将图像推送到新的注册表:

sudo docker push registry.example.com/mysql

此命令将成功运行并指示您的新注册表已正确配置并接受流量 - 包括推送新映像。 如果您看到错误,请仔细检查第1步和2的步骤。

要干净地测试从注册表中提取,请首先使用以下命令删除本地mysql映像:

sudo docker rmi registry.example.com/mysql && sudo docker rmi mysql

然后,从注册表中提取它:

sudo docker pull registry.example.com/mysql

此命令将需要几秒钟才能完成。 如果它成功运行,则表示您的注册表正常运行。 如果显示错误,请仔细检查您对先前命令输入的内容。

您可以通过运行以下命令列出本地可用的Docker映像:

sudo docker images

您将看到输出列出本地计算机上可用的图像,以及它们的ID和创建日期。

您的Docker注册表已配置。 您已将图像推送到该图像并验证您可以将其拉下来。 现在让我们添加身份验证,这样只有特定的人才能访问代码。

第3步 - 添加帐户身份验证和配置Kubernetes访问

在此步骤中,您将使用htpasswd实用程序为注册表设置用户名和密码身份验证。

htpasswd实用程序来自Apache Web服务器,您可以使用它来创建存储用户名和密码的文件,以便对HTTP用户进行基本身份验证。 htpasswd文件的格式是username:hashed_password (每行一个),它足够便携,允许其他程序也使用它。

要使htpasswd在系统上可用,您需要通过运行以下命令来安装它:

sudo apt install apache2-utils -y

注意: 如果您是从Mac运行本教程,则需要使用以下命令在您的计算机上使用htpasswd

docker run --rm -v ${PWD}:/app -it httpd htpasswd -b -c /app/htpasswd_file sammy password

通过执行以下命令创建它:

touch htpasswd_file

将用户名和密码组合添加到htpasswd_file

htpasswd -B htpasswd_file username

Docker需要使用bcrypt算法对密码进行哈希处理,这就是我们传递-B参数的原因。 bcrypt算法是基于Blowfish分组密码的密码散列函数,具有工作因子参数,该参数指定散列函数的成本。

请记住用您想要的用户名替换username名。 运行时, htpasswd会询问您是否附带密码并将组合添加到htpasswd_file 您可以为要添加的用户重复此命令。

现在,通过运行以下命令显示htpasswd_file的内容:

cat htpasswd_file

选择并复制显示的内容。

要向Docker注册表添加身份验证,您需要编辑chart_values.yaml并在htpasswd变量中添加htpasswd_file的内容。

打开chart_values.yaml进行编辑:

nano chart_values.yaml

找到如下所示的行:

chart_values.yaml
  htpasswd: ""

编辑它以匹配以下内容,将htpasswd\_file\_contents替换为您从htpasswd_file复制的内容:

chart_values.yaml
  htpasswd: |-
    htpasswd_file_contents

小心缩进,文件内容的每一行必须在它之前有四个空格。

添加完内容后,保存并关闭文件。

要将更改传播到群集,请运行以下命令:

helm upgrade docker-registry stable/docker-registry -f chart_values.yaml

输出将类似于第一次部署Docker注册表时显示的输出:

Release "docker-registry" has been upgraded. Happy Helming!
LAST DEPLOYED: ...
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME                    DATA  AGE
docker-registry-config  1     3m8s

==> v1/Pod(related)
NAME                              READY  STATUS   RESTARTS  AGE
docker-registry-6c5bb7ffbf-ltnjv  1/1    Running  0         3m7s

==> v1/Secret
NAME                    TYPE    DATA  AGE
docker-registry-secret  Opaque  4     3m8s

==> v1/Service
NAME             TYPE       CLUSTER-IP      EXTERNAL-IP  PORT(S)   AGE
docker-registry  ClusterIP  10.245.128.245  <none>       5000/TCP  3m8s

==> v1beta1/Deployment
NAME             READY  UP-TO-DATE  AVAILABLE  AGE
docker-registry  1/1    1           1          3m8s

==> v1beta1/Ingress
NAME             HOSTS                ADDRESS        PORTS    AGE
docker-registry  registry.example.com  159.89.215.50  80, 443  3m8s


NOTES:
1. Get the application URL by running these commands:
  https://registry.example.com/

此命令调用Helm并指示它在应用chart_values.yaml文件后,使用其在图表存储库中的stable/docker-registry中定义的图表升级现有版本(在您的情况下为chart_values.yaml stable/docker-registry

现在,您将尝试再次从注册表中提取图像:

sudo docker pull registry.example.com/mysql

输出将如下所示:

Using default tag: latest
Error response from daemon: Get https://registry.example.com/v2/mysql/manifests/latest: no basic auth credentials

它正确失败,因为您没有提供凭据。 这意味着您的Docker注册表正确授权请求。

要登录注册表,请运行以下命令:

sudo docker login registry.example.com

请记住将registry.example.com替换为您的域名地址。 它会提示您输入用户名和密码。 如果显示错误,请仔细检查htpasswd_file包含的内容。 您必须在此步骤中先前创建的htpasswd_file定义用户名和密码组合。

要测试登录,可以尝试通过运行以下命令再次执行:

sudo docker pull registry.example.com/mysql

输出类似于以下内容:

Using default tag: latest
latest: Pulling from mysql
Digest: sha256:f2dc118ca6fa4c88cde5889808c486dfe94bccecd01ca626b002a010bb66bcbe
Status: Image is up to date for registry.example.com/mysql:latest

您现在已经配置了Docker并且可以安全登录。 要配置Kubernetes以登录到注册表,请运行以下命令:

sudo kubectl create secret generic regcred --from-file=.dockerconfigjson=/home/sammy/.docker/config.json --type=kubernetes.io/dockerconfigjson

您将看到以下输出:

secret/regcred created

此命令在集群中创建一个名为regcred的秘密,获取Docker存储凭据的JSON文件的内容,并将其解析为dockerconfigjson ,它定义了Kubernetes中的注册表凭据。

您已使用htpasswd创建登录配置文件,配置注册表以验证请求,并创建包含登录凭据的Kubernetes机密。 接下来,您将测试Kubernetes集群和注册表之间的集成。

第4步 - 通过运行示例部署来测试Kubernetes集成

在此步骤中,您将运行一个示例部署,其中包含存储在群集内注册表中的映像,以测试Kubernetes群集和注册表之间的连接。

在最后一步中,您创建了一个名为regcred的机密,其中包含私有注册表的登录凭据。 它可能包含多个注册表的登录凭据,在这种情况下,您必须相应地更新Secret。

您可以通过指定imagePullSecrets指定在pod定义中拉取容器时Kubernetes应使用的秘密。 当Docker注册表需要身份验证时,此步骤是必需的。

您现在将从私有Docker注册表部署示例Hello World映像到您的群集。 首先,为了推送它,您可以通过运行以下命令将其拉到您的机器:

sudo docker pull paulbouwer/hello-kubernetes:1.5

然后,通过运行标记它:

sudo docker tag paulbouwer/hello-kubernetes:1.5 registry.example.com/paulbouwer/hello-kubernetes:1.5

最后,将其推送到您的注册表:

sudo docker push registry.example.com/paulbouwer/hello-kubernetes:1.5

在您的机器上删除它,因为您不再需要它在本地:

sudo docker rmi registry.example.com/paulbouwer/hello-kubernetes:1.5

现在,您将部署示例Hello World应用程序。 首先,使用文本编辑器创建一个新文件hello-world.yaml

nano hello-world.yaml

接下来,您将定义服务和Ingress,以使应用程序可以在群集外部访问。 添加以下行,用您的域替换突出显示的行:

你好,world.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-kubernetes-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: k8s-test.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: hello-kubernetes
          servicePort: 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.example.com/paulbouwer/hello-kubernetes:1.5
        ports:
        - containerPort: 8080
      imagePullSecrets:
      - name: regcred

首先,为Hello World部署定义Ingress,您将通过Nginx Ingress Controller拥有的Load Balancer进行路由。 然后,您定义可以访问在部署中创建的pod的服务。 在实际部署规范中,您将image指定为位于注册表中的image ,并将imagePullSecrets设置为regcred ,这是您在上一步中创建的。

保存并关闭文件。 要将其部署到群集,请运行以下命令:

kubectl apply -f hello-world.yaml

您将看到以下输出:

ingress.extensions/hello-kubernetes-ingress created
service/hello-kubernetes created
deployment.apps/hello-kubernetes created

您现在可以导航到您的测试域 - 本教程中的第二个A记录k8s-test.example.com 你会看到Kubernetes Hello世界! 页。

Hello World页面

Hello World页面列出了一些环境信息,例如Linux内核版本和请求服务的pod的内部ID。 您还可以通过Web界面访问您的Space,以查看您在本教程中使用过的图像。

如果要在测试后删除此Hello World部署,请运行以下命令:

kubectl delete -f hello-world.yaml

您已经创建了一个示例Hello World部署,以测试Kubernetes是否正确地从您的私有注册表中提取图像。

结论

您现在已经在DigitalOcean Kubernetes集群上成功部署了自己的私有Docker注册表,使用DigitalOcean Spaces作为下面的存储层。 您可以存储的图像数量没有限制,Spaces可以无限延伸,同时提供相同的安全性和稳健性。 但是,在生产中,您应该始终尽可能地优化Docker镜像,请参阅如何优化Docker镜像以进行生产教程。