将您的私有Docker注册表部署为kubernetes中的Pod

潘弘扬
2023-12-01

Docker Registry is an application that helps you in storing and distributing container images. The most popular container registry is DockerHub, which is the standard public registry for Docker and Kubernetes. But you might face a situation where you will not want your image to be publicly available over the internet. In that case, setting up a Private Docker Registry provides you with multiple storage and authentication options which can be customized as per your requirement.

Docker Registry是一个可帮助您存储和分发容器映像的应用程序。 最受欢迎的容器注册表是DockerHub ,它是Docker和Kubernetes的标准公共注册表。 但是您可能会遇到这样的情况,即您不希望您的图像在Internet上公开提供。 在这种情况下,设置私有Docker Registry将为您提供多个存储和身份验证选项,可以根据您的要求进行自定义。

In this tutorial, we shall look at deploying a TLS-enabled Private Docker Registry as a Pod in a Kubernetes environment. This will help us to push our custom built images to the registry, which later can be pulled by any of the worker nodes and run as containers in Pods. My k8s cluster here consists of 4 Ubuntu 18.04.4 (Bionic Beaver) VMs with 1 master and 3 worker nodes.

在本教程中,我们将研究在Kubernetes环境中将启用TLS的私有Docker Registry部署为Pod。 这将帮助我们将自定义构建的映像推送到注册表,以后可以由任何工作节点将其拉出并在Pods中作为容器运行。 我的k8s集群由4个具有1个主节点和3个工作节点的Ubuntu 18.04.4(Bionic Beaver)VM组成。

root@master1:~# docker -v
Docker version 19.03.12, build 48a66213fe
root@master1:~# kubectl version --short
Client Version: v1.18.5
Server Version: v1.18.5
root@master1:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master1 Ready master 65d v1.18.5
worker1 Ready <none> 65d v1.18.5
worker2 Ready <none> 65d v1.18.5
worker3 Ready <none> 65d v1.18.5
root@master1:~#

步骤1:创建用于身份验证的文件 (Step 1: Creating files for authentication)

Let us start by creating self-signed certificates and user authentication to boost the security for our private Docker registry. The TLS certificates are created using openssl where we need to specify the name, with which we want to access our registry, in the Common Name “/CN=” field. Here, I wish to access my registry using the name docker-registry.

让我们从创建自签名证书和用户身份验证开始,以增强私有Docker注册表的安全性。 TLS证书是使用openssl创建的,我们需要在“通用名称” / CN =”字段中指定用于访问注册表的名称。 在这里,我希望使用名称docker-registry访问我的注册表

root@master1:~# mkdir -p /registry && cd "$_"
root@master1:/registry# mkdir certs
root@master1:/registry# openssl req -x509 -newkey rsa:4096 -days 365 -nodes -sha256 -keyout certs/tls.key -out certs/tls.crt -subj "/CN=docker-registry"
Generating a RSA private key
...........................................................................................................................................++++
.............................................................++++
writing new private key to 'certs/tls.key'
-----
root@master1:/registry#

Let’s use htpasswd to add user authentication for registry access. My credentials for the private registry would be myuser/mypasswd.

让我们使用htpasswd为注册表访问添加用户身份验证。 我用于私人注册表的凭据将是myuser / mypasswd

root@master1:/registry# mkdir auth
root@master1:/registry# docker run --rm --entrypoint htpasswd registry:2.6.2 -Bbn myuser mypasswd > auth/htpasswd
Unable to find image 'registry:2.6.2' locally
2.6.2: Pulling from library/registry
486039affc0a: Pulling fs layer
ba51a3b098e6: Pulling fs layer
470e22cd431a: Pulling fs layer
1048a0cdabb0: Pulling fs layer
ca5aa9d06321: Pulling fs layer
1048a0cdabb0: Waiting
ca5aa9d06321: Waiting
ba51a3b098e6: Verifying Checksum
ba51a3b098e6: Download complete
486039affc0a: Verifying Checksum
486039affc0a: Pull complete
470e22cd431a: Verifying Checksum
470e22cd431a: Download complete
ba51a3b098e6: Pull complete
1048a0cdabb0: Verifying Checksum
1048a0cdabb0: Download complete
ca5aa9d06321: Verifying Checksum
ca5aa9d06321: Download complete
470e22cd431a: Pull complete
1048a0cdabb0: Pull complete
ca5aa9d06321: Pull complete
Digest: sha256:c4bdca23bab136d5b9ce7c06895ba54892ae6db0ebfc3a2f1ac413a470b17e47
Status: Downloaded newer image for registry:2.6.2
root@master1:/registry#

At this point, our /registry directory looks like this:

此时,我们的/ registry目录如下所示:

root@master1:/# ls -R /registry/
/registry/:
auth certs/registry/auth:
htpasswd/registry/certs:
tls.crt tls.key
root@master1:/#

步骤2:使用机密安装证书 (Step 2: Using Secrets to mount the certificates)

In Kubernetes, a Secret is a resource that will enable you to inject sensitive data into a container when it starts up. This data can be anything like password, OAuth tokens or ssh keys. They can be exposed inside a container as mounted files or volumes or environment variables.

在Kubernetes中, 秘密是一种资源,使您可以在启动时将敏感数据注入到容器中。 该数据可以是密码,OAuth令牌或ssh密钥之类的任何数据。 它们可以作为装入的文件或卷或环境变量在容器内部公开。

The below command creates a Secret of type tls named certs-secret in the default namespace from the pair of public/private keys we just created.

下面的命令在我们刚创建的一对公钥/私钥的默认命名空间中创建一个名为certs-secret的 tls类型的Secret。

root@master1:/# kubectl create secret tls certs-secret --cert=/registry/certs/tls.crt --key=/registry/certs/tls.key
secret/certs-secret created
root@master1:/#

The Secret auth-secret that we create from the htpasswd file is of type generic which means the Secret was created from a local file.

我们从htpasswd文件创建的Secret auth-secret类型为generic ,这意味着Secret是从本地文件创建的。

root@master1:/# kubectl create secret generic auth-secret --from-file=/registry/auth/htpasswd
secret/auth-secret created
root@master1:/#

步骤3:为存储库创建持久卷和声明 (Step 3: Creating Persistent Volume and Claim for repository storage)

The images that are pushed to our registry should be placed in a consistent storage location. Hence, we will be using a Persistent Volume of 1 GB hosted at a temporary location in the node where our registry Pod will be running. The Pod uses a Persistent Volume Claim which will be bound to the newly created volume as a one-to-one mapping.

推送到我们的注册表的图像应放置在一致的存储位置。 因此,我们将在注册表Pod将运行的节点的临时位置使用1 GB的永久卷 。 Pod使用“ 持久卷声明” ,该声明将以一对一映射的方式绑定到新创建的卷。

apiVersion: v1
kind: PersistentVolume
metadata:
name: docker-repo-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /tmp/repository
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: docker-repo-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

Copy the above content into a yaml file, say repository-volume.yaml and execute the below command:

将以上内容复制到yaml文件中,例如repository-volume.yaml并执行以下命令:

root@master1:/# kubectl create -f repository-volume.yaml
persistentvolume/docker-repo-pv created
persistentvolumeclaim/docker-repo-pvc created
root@master1:/#

步骤4:创建注册表容器 (Step 4: Creating the Registry Pod)

Next, let us create the actual Pod and a corresponding Service to access it. In the yaml file docker-registry-pod.yaml below, the image that we use for our registry is called registry which is downloaded from DockerHub. The images pushed to this registry will be saved in /var/lib/registry directory internally, hence we mount our Persistent Volume using the Claim docker-repo-pvc to persist the images permanently. The environment variables which are required by the registry container are taken care by the Secrets that we mount as volumes.

接下来,让我们创建实际的Pod和相应的服务来访问它。 在YAML文件下的码头工人,注册表pod.yaml,我们使用我们的注册表中的图像被称为是从DockerHub下载注册表 。 推送到此注册表的映像将在内部保存在/ var / lib / registry目录中,因此,我们使用Claim docker-repo-pvc挂载永久卷来永久保留映像。 注册表容器所需的环境变量由我们作为卷装入的Secrets负责。

The Service is named docker-registry, with which we want to access our docker private registry. Note that this was the exact name that was given in the Common Name “/CN=” field while generating the TLS certificates. The registry container by default is exposed at port 5000 and we bind our Service to this port accordingly.

该服务名为docker-registry ,我们要通过它访问我们的docker私有注册表。 请注意,这是在生成TLS证书时在“通用名称” / CN =”字段中给出的确切名称。 默认情况下,注册表容器在端口5000处公开,因此我们将服务绑定到该端口。

apiVersion: v1
kind: Pod
metadata:
name: docker-registry-pod
labels:
app: registry
spec:
containers:
- name: registry
image: registry:2.6.2
volumeMounts:
- name: repo-vol
mountPath: "/var/lib/registry"
- name: certs-vol
mountPath: "/certs"
readOnly: true
- name: auth-vol
mountPath: "/auth"
readOnly: true
env:
- name: REGISTRY_AUTH
value: "htpasswd"
- name: REGISTRY_AUTH_HTPASSWD_REALM
value: "Registry Realm"
- name: REGISTRY_AUTH_HTPASSWD_PATH
value: "/auth/htpasswd"
- name: REGISTRY_HTTP_TLS_CERTIFICATE
value: "/certs/tls.crt"
- name: REGISTRY_HTTP_TLS_KEY
value: "/certs/tls.key"
volumes:
- name: repo-vol
persistentVolumeClaim:
claimName: docker-repo-pvc
- name: certs-vol
secret:
secretName: certs-secret
- name: auth-vol
secret:
secretName: auth-secret
---
apiVersion: v1
kind: Service
metadata:
name: docker-registry
spec:
selector:
app: registry
ports:
- port: 5000
targetPort: 5000

Create the Registry Pod and the Service using the following command:

使用以下命令创建注册表Pod和服务:

root@master1:/# kubectl create -f docker-registry-pod.yaml
pod/docker-registry-pod created
service/docker-registry created
root@master1:/# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/docker-registry-pod 1/1 Running 0 36sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/docker-registry ClusterIP 10.107.59.73 <none> 5000/TCP 36s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 65d
root@master1:/#

步骤5:允许从群集中的所有节点访问注册表 (Step 5: Allowing access to the registry from all the nodes in the cluster)

We observe from the above step that our registry can be accessed at 10.107.59.73:5000, since the ip-address of our Service turned out to be 10.107.59.73. Note that this value will be different in your case.

从上面的步骤中,我们观察到可以通过10.107.59.73:5000访问注册表,因为我们服务的ip地址原来是10.107.59.73。 请注意,根据您的情况,此值将有所不同。

Let’s make a note of the registry name and its ip-address as environment variables.

让我们记下注册表名称及其IP地址作为环境变量。

root@master1:/# export REGISTRY_NAME="docker-registry"
root@master1:/# export REGISTRY_IP="10.107.59.73"
root@master1:/#

Now, we shall append the entry “10.107.59.73 docker-registry” to the /etc/hosts file of all the nodes in our cluster so that this ip-address is resolved to the name docker-registry.

现在,我们应将条目“ 10.107.59.73 docker-registry”附加到集群中所有节点的/ etc / hosts文件中,以便将此IP地址解析为名称docker-registry

The above step can also be done using a single command from the master node (Enter password, if prompted):

也可以使用来自主节点的单个命令来完成上述步骤(如果出现提示,请输入密码):

root@master1:/# for x in $(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="InternalIP")].address }'); do ssh root@$x "echo '$REGISTRY_IP $REGISTRY_NAME' >> /etc/hosts"; done
root@master1:/#

Next, we must copy the tls.crt that we created earlier as “ca.crt” into a custom /etc/docker/certs.d/docker-registry:5000 directory in all the nodes in our cluster to make sure that our self-signed certificate is trusted by Docker. Note that the directory that is created inside /etc/docker/certs.d should be having the name of the format<registry_name>:<registry_port>.

接下来,我们必须将之前创建为“ ca.crt”tls.crt复制到集群中所有节点中的自定义/etc/docker/certs.d/docker-registry:5000目录中,以确保签名证书由Docker信任。 请注意,在/etc/docker/certs.d内部创建的目录的名称应为<registry_name>:<registry_port>格式

This step can be done manually or with the help of a single command from the master node as follows:

可以手动执行此步骤,也可以在主节点上通过单个命令进行以下操作:

root@master1:/# for x in $(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="InternalIP")].address }'); do ssh root@$x "rm -rf /etc/docker/certs.d/$REGISTRY_NAME:5000;mkdir -p /etc/docker/certs.d/$REGISTRY_NAME:5000"; done
root@master1:/#
root@master1:/# for x in $(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="InternalIP")].address }'); do scp /registry/certs/tls.crt root@$x:/etc/docker/certs.d/$REGISTRY_NAME:5000/ca.crt; done
tls.crt 100% 1822 2.7MB/s 00:00
tls.crt 100% 1822 2.4MB/s 00:00
tls.crt 100% 1822 1.6MB/s 00:00
tls.crt 100% 1822 2.1MB/s 00:00
root@master1:/#

The above step forces Docker to verify our self-signed certificate even though it is not signed by a known authority.

上述步骤迫使Docker验证我们的自签名证书,即使该证书未由已知机构签名也是如此。

第6步:测试我们的私有Docker注册表 (Step 6: Testing our Private Docker Registry)

Now, let us try to login to the registry from the master node, using the same credentials we created earlier:

现在,让我们尝试使用我们之前创建的相同凭据从主节点登录到注册表:

root@master1:/# docker login docker-registry:5000 -u myuser -p mypasswd
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. Seehttps://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeeded
root@master1:/#

Hurray! This worked!!

欢呼! 这有效!!

Before we start using our registry, let us create a Secret of type docker-registry which uses the credentials myuser/mypasswd for enabling all the nodes in our cluster to authenticate with our private Docker registry.

在开始使用注册表之前,让我们创建一个类型为docker -registry的Secret,它使用凭据myuser / mypasswd启用集群中的所有节点以使用我们的私有Docker注册表进行身份验证。

root@master1:/# kubectl create secret docker-registry reg-cred-secret --docker-server=$REGISTRY_NAME:5000 --docker-username=myuser --docker-password=mypasswd
secret/reg-cred-secret created
root@master1:/#

Let us try to push a custom image to our private Docker registry.

让我们尝试将自定义映像推送到我们的私有Docker注册表中。

root@master1:/# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
bf5952930446: Pull complete
cb9a6de05e5a: Pull complete
9513ea0afb93: Pull complete
b49ea07d2e93: Pull complete
a5e4a503d449: Pull complete
Digest: sha256:b0ad43f7ee5edbc0effbc14645ae7055e21bc1973aee5150745632a24a752661
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
root@master1:/#
root@master1:/# docker tag nginx:latest docker-registry:5000/mynginx:v1
root@master1:/#
root@master1:/# docker push docker-registry:5000/mynginx:v1
The push refers to repository [docker-registry:5000/mynginx]
550333325e31: Layer already exists
22ea89b1a816: Layer already exists
a4d893caa5c9: Layer already exists
0338db614b95: Layer already exists
d0f104dc0a1f: Layer already exists
v1: digest: sha256:179412c42fe3336e7cdc253ad4a2e03d32f50e3037a860cf5edbeb1aaddb915c size: 1362
root@master1:/#

We can actually verify that our custom image mynginx:v1 was indeed saved into our registry.

我们实际上可以验证我们的自定义映像mynginx:v1是否确实已保存到注册表中。

root@master1:/# kubectl exec docker-registry-pod -it -- sh
/ # ls /var/lib/registry/docker/registry/v2/repositories/
mynginx
/ #

Finally, let us create a new Pod that uses the image mynginx:v1 that we just pushed to our registry.

最后,让我们创建一个新Pod,该Pod使用我们刚推送到注册表中的映像mynginx:v1

root@master1:/# kubectl run nginx-pod --image=docker-registry:5000/mynginx:v1
pod/nginx-pod created
root@master1:/#
root@master1:/# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
docker-registry-pod 1/1 Running 0 46m 10.38.0.1 worker2 <none> <none>
nginx-pod 1/1 Running 0 38s 10.38.0.2 worker2 <none> <none>
root@master1:/#
root@master1:/# curl 10.38.0.2:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>
root@master1:/#

Congratulations on making it this far!! We just created a Private Docker Registry running as a Pod where our images will be stored in a Persistent Volume and can be pulled by any of the worker nodes to run your application.

恭喜您成功!! 我们刚刚创建了一个以Pod身份运行的Private Docker Registry,其中,我们的映像将存储在Persistent Volume中,并且可以由任何辅助节点拉出以运行您的应用程序。

Happy learning!!

学习愉快!

翻译自: https://medium.com/swlh/deploy-your-private-docker-registry-as-a-pod-in-kubernetes-f6a489bf0180

 类似资料: