VMware Cloud 应用现代化 计算虚拟化

vSphere with Kubernetes实战之:访问映像注册表

VMware 在2020年4月2日发布了vSphere 7.0的正式版。这个版本的vSphere最显著的特性之一是它在hypervisor层内部创建了Kubernetes控制平面。该功能使vSphere大大扩展了它所能运行的工作负载范围,涵盖了更多当前流行的应用程序。我们将此功能之前称为“ Project Pacific”或WCP(工作负载控制平面),现在的正式名称是“vSphere with Kubernetes”。要了解有关vSphere 7的新功能的更多信息,请访问我们的网站:https://www.vmware.com/products/vsphere.html.

 

1. 背景介绍

vSphere with Kubernetes 自带了一个内嵌的映像注册表(image registry)–Harbor。  大多数映像注册表(image registry)都启用了HTTPS,Harbor也是如此。通常来说在Kubernetes中部署的应用要访问映像注册表(GCR,Docker Hub或Harbor),您需要准备两种凭证:

  1. 映像注册表的服务器CA证书。运行映像获(image fetch)取的组件,需要在它的KeyStore有足够的信息来验证注册表的服务器证书。对于Docker Hub和GCR,应该都没有问题,因为客户端系统始终安装了公共认可的知名根证书来验证它们所签名的这些证书。但如果要部署带有自签名证书的私有注册表(例如Harbor),则还有更多设置步骤。
  2. 访问映像注册表的凭证(通常是用户名和密码)。公共存储库在访问(pull操作)映像的时候不需要凭据,在上传或更新(push操作)映像的时候需要。私有注册表(例如Harbor)在将映像拉取或推送(pull和push)时始终需要凭据。

在实际部署中,对大多数映像注册表的使用可能有多种不同的使用方案。有的在Supervior集群中直接使用内置的harbor,有的在Tanzu集群中使用内置的Harbor,还有的则需要使用他们企业内部已经存在的映像注册表服务。本文将通过实战的方式,来一步步展示如何在不同的部署情况下,正确的配置Supervisor和Tanzu集群,才能流畅的访问映像注册表的服务。下面的实战练习需要几个前提条件:

 

2. Docker Client访问内嵌的Harbor

在实际安装过程中,如果用户不提供自己的证书,vSphere with Kubernetes会自己生成一个自签名的证书,内嵌的Harbor也使用这个根证书签发的证书。因此,Docker客户端需要将这个证书导入到系统中,才能使用Harbor服务。

  1. 通过浏览器登录到Harbor界面。选择你要访问的Project或者(Namespace)。
  2. 转到“Repositories”页,下载注册表证书
  3. 将这个证书添加到本机的系统里,例如,在MacOS里需要运行下面的命令(最后的参数是刚才保存证书的文件路径):
    $ security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain pathtofile/harbor-cert.crt
  4. 重启Docker服务
  5. 通过下面的命令来验证Docker客户端和Harbor的集成(下面的例子中10.147.18.2是我的Harbor服务的IP地址):

    $ docker login 10.147.18.2
    $ docker pull busybox:latest
    $ docker tag busybox:latest 10.147.18.2/wangyu/busybox:latest
    $ docker push 10.147.18.2/wangyu/busybox:latest

 

3. Supervisor集群中访问内置的Harbor

要从Supervisor集群中访问内置的Harbor服务,例如,创建带有指向Harbor存储库的URL的Pod,我们无需执行任何操作即可实现此目的。这是因为,一方面,Harbour Registry和Esxi Spherelet(映像获取服务所在的地方)共享相同的根证书,因此它们自然彼此信任。另一方面,在Supervisor群集中创建命名空间时会自动创建默认的映像像拉取秘钥。

因此这一小节的目的不是需要您执行什么步骤,而是一起来看看系统在创建命名空间的时候,都创建了哪些与映像拉取操作相关的资源和秘钥。

  1. 通过kubectl命令,以数据中心管理员或者Namespace管理员的身份登录Supervisor 集群。然后执行下面的命令来获得相关的secret(格式为NAMESPACE-default-image-pull-secret)

    $ kubectl vsphere login --server=10.117.233.1 --vsphere-username [email protected]
    $ kubectl get secret wangyu-default-image-pull-secret -o json -n wangyu
    {
       "apiVersion": "v1",
       "data": {
           ".dockerconfigjson": "ewoJCQkJImF.....QkJfQ=="
       },
       "kind": "Secret",
       "metadata": {
           "creationTimestamp": "2020-04-11T10:41:40Z",
           "name": "wangyu-default-image-pull-secret",
           "namespace": "wangyu",
           "ownerReferences": [
               {
                   "apiVersion": "registryagent.vmware.com/v1alpha1",
                   "blockOwnerDeletion": true,
                   "controller": true,
                   "kind": "Project",
                   "name": "wangyu",
                   "uid": "1b2a528c-45b5-4262-a5cc-d0d6e1744ccf"
               }
           ],
           "resourceVersion": "76067",
           "selfLink": "/api/v1/namespaces/wangyu/secrets/wangyu-default-image-pull-secret",
           "uid": "2c7a7fc1-97fa-49ba-b54a-6c5225ffe487"
       },
       "type": "kubernetes.io/dockerconfigjson
    }
  2. 在当前的命名空间里,缺省(default)的 Service Account已经和这个secret绑定在一起了,因此,当你从Harbor拉取映像的时候,就算你什么都没有指定,缺省的也会使用了这个secret

    $ kubectl describe sa default -n wangyu
    Name:                default
    Namespace:           wangyu
    Labels:              <none>
    Annotations:         <none>
    Image pull secrets:  wangyu-default-image-pull-secret
    Mountable secrets:   default-token-lqfwr
    Tokens:              default-token-lqfwr
    Events:              <none>
  3. 下面是个在Supervisor集群中使用内置Harbor服务来创建pod的例子:

    $ more busybox-harbor.yaml
    apiVersion: v1
    kind: Pod
    metadata:
    name: busybox
    spec:
    imagePullSecrets:
       - name: externalimgpull
    containers:
    - image: 10.147.18.2/wangyu/wybusybox:latest
       command:
         - sleep
         - "3600"
       imagePullPolicy: IfNotPresent
       name: busybox
      restartPolicy: Always

 

4. Supervisor集群中访问外部自签名的Harbor

如果要使用已有的映像注册表(Harbor或其他),那么要从Supervisor群集访问这个注册表需要更多的配置步骤,尤其是您的注册表服务正在使用自签名证书。

  1. 通过kubectl命令,以数据中心管理员的身份登录Supervisor 集群。
  2. 在命名空间里创建一个映像拉取的secret,例如在下面的例子中,我们创建了一个名为“externalimagepull”的secret(你可以任意选择其他的名称)。”–docker-server”指向你的Harbor地址;最后还需要提供登录Harbor的用户名和密码。

    $ kubectl -n wangyu create secret docker-registry externalimgpull --docker-server=10.147.18.2 \ --docker-username=YourName --docker-password=YourPasswd
    secret/externalimgpull created
  3. 通过浏览器登录到Harbor界面。选择你要访问的Project或者(Namespace)。
  4. 转到“Repositories”页,下载注册表证书.
  5. 在系统命名空间(kube-system)里去修改 image-fetcher-ca-bundle这个资源,将刚才下载的证书的内容追加到这个资源的证书列表的后面,并且保存。

    $ kubectl edit configmap image-fetcher-ca-bundle -n kube-system
    .... apiVersion: v1 data:  ca-bundle: |    -----BEGIN CERTIFICATE-----    MIIENTCCAx2gAwIBAgIJAPSI+BwiQ0E/MA0GCSqGSIb3DQEBCwUAMIGlMQswCQYD    VQQDDAJDQTEXMBUGCgmSJomT8ixkARkWB3ZzcGhlcmUxFTATBgoJkiaJk/IsZAEZ    FgVsb2NhbDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExJzAlBgNV    BAoMHnN0bmV0LWNwZC12Yy0xNy5lbmcudm13YXJlLmNvbTEbMBkGA1UECwwSVk13    YXJlIEVuZ2luZWVyaW5nMB4XDTIwMDMwMTEyNTE0MVoXDTMwMDIyNzEyNTE0MVow    gaUxCzAJBgNVBAMMAkNBMRcwFQYKCZImiZPyLGQBGRYHdnNwaGVyZTEVMBMGCgmS    JomT8ixkARkWBWxvY2FsMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5p    YTEnMCUGA1UECgwec3RuZXQtY3BkLXZjLTE3LmVuZy52bXdhcmUuY29tMRswGQYD    VQQLDBJWTXdhcmUgRW5naW5lZXJpbmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw    ggEKAoIBAQCxkC3pJGQWYQP55bN9Brb3qwDl7aoEX1r/g21/jFoX35nwh7WC/5Jf    waQ1S0DiVytIdeZNoy/3W4/bKz0tDLTb+cmm2irJtn+5potx4ntZvrFJkTbSrJpO    7yrZXefbCYFnMCW9mzwAx/G97Es+s5+fdnv6epTy18fNC+AogAlt210SGvlM0wbw    k+oEZZBdVjz7/Meszw+V7EzG4KS5o5giMYBHAABkHDHlnAAh/5/93MdK9+j/Mg2n    izlQNXO+2VVG8LoVm1MLp+RiV5r410TmjER1W4TYRgtI390otZLLHSfI6xYQYey0    W7jjatXwneg/M1x10Do6fW1xxlYYg+l5AgMBAAGjZjBkMB0GA1UdDgQWBBRbVNOq    DOikfAxH0lWdp0hgBIG1+jAfBgNVHREEGDAWgQ5lbWFpbEBhY21lLmNvbYcEfwAA    ATAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0B    AQsFAAOCAQEAKptfB8HHyXpe2t2ncLUgC13WYk9LIJZeMlKtZiCTYIP6fsavNO43    JD5TUw7tc6dbKB9EQNDghuvekmzUazkaxksD3fMVERdeNqqGPGGmApIZJ71BAHBV    XS+ayql9Hxr/vvhwVIwPWkBsBfd3bStsNeNcBLeQyYRDpLs8pI6JAqpzDj2A2dIO    2YvUDsK4/bY992S3rIKs8mi8oC93O1MewpDTLnJNEbaG95+68sTn+21Xq88blDTP    V2YdsN2f67njV7wqbRPN+W/fGSv6UD6wLdb+EB2Rxjx7WHDCmBXyCFIkKFEj9EQw    7u2eCZG38ZbubwKJLITFHV2N9nZf/Kx3Lw==    -----END CERTIFICATE-----    -----BEGIN CERTIFICATE-----    MIIENTCCAx2gAwIBAgIJAPSI+BwiQ0E/MA0GCSqGSIb3DQEBCwUAMIGlMQswCQYD    VQQDDAJDQTEXMBUGCgmSJomT8ixkARkWB3ZzcGhlcmUxFTATBgoJkiaJk/IsZAEZ    FgVsb2NhbDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExJzAlBgNV    BAoMHnN0bmV0LWNwZC12Yy0xNy5lbmcudm13YXJlLmNvbTEbMBkGA1UECwwSVk13    YXJlIEVuZ2luZWVyaW5nMB4XDTIwMDMwMTEyNTE0MVoXDTMwMDIyNzEyNTE0MVow    gaUxCzAJBgNVBAMMAkNBMRcwFQYKCZImiZPyLGQBGRYHdnNwaGVyZTEVMBMGCgmS    JomT8ixkARkWBWxvY2FsMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5p    YTEnMCUGA1UECgwec3RuZXQtY3BkLXZjLTE3LmVuZy52bXdhcmUuY29tMRswGQYD    VQQLDBJWTXdhcmUgRW5naW5lZXJpbmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw    ggEKAoIBAQCxkC3pJGQWYQP55bN9Brb3qwDl7aoEX1r/g21/jFoX35nwh7WC/5Jf    waQ1S0DiVytIdeZNoy/3W4/bKz0tDLTb+cmm2irJtn+5potx4ntZvrFJkTbSrJpO    7yrZXefbCYFnMCW9mzwAx/G97Es+s5+fdnv6epTy18fNC+AogAlt210SGvlM0wbw ....
    configmap/image-fetcher-ca-bundle edited
  6. 现在可以在Supervisor集群上部署Pod或Deployment,将刚才创建的secret应用到部署文件中去:

    $ more nginx-harbor.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: nginx-deployment
    labels:
       app: myapp
    spec:
    replicas: 1
    selector:
       matchLabels:
         app: myapp
    template:
       metadata:
         labels:
           app: myapp
       spec:
         containers:
         - name: nginx
           image: 10.147.18.2/wangyu/nginx:latest
           ports:
           - containerPort: 80
         imagePullSecrets:
           - name: externalimgpull
  7. 当然,你可以将命名空间里缺省(default)的Service Account和这个映像拉取的secret进行绑定,这样,在当前命名空间部署Pod或Deployment的时候,就不需要每次在部署文件中指定映像拉取的secret:

    $ kubectl get sa default -o json -n wangyu
    {
       "apiVersion": "v1",
       "imagePullSecrets": [
           {
               "name": "wangyu-default-image-pull-secret"
           }
       ],
       "kind": "ServiceAccount",
       "metadata": {
           "creationTimestamp": "2020-03-06T07:19:28Z",
           "name": "default",
           "namespace": "wangyu",
           "resourceVersion": "850464",
           "selfLink": "/api/v1/namespaces/wangyu/serviceaccounts/default",
           "uid": "96b9abe5-14aa-4d55-bf66-02ce4bffc5ec"
       },
       "secrets": [
           {
               "name": "default-token-kfwj8"
           }
       ]
    }
    $ kubectl edit sa default

 

5. Tanzu集群中访问自签名的Harbor

当您想从Tanzu Kubernetes群集访问私有映像存储库时,内置的Harbor 注册表和外部其他注册表之间没有什么区别。因为Tanzu Kubernetes群集既没有映像拉取秘钥的默认设置,它们也根本不信任您的私有注册表(例如Harbor)的自签名证书。所以要想从Tanzu Kubernetes集群访问自签名的注册表(内置或外部),需要更多的步骤来设置证书和凭证。证书的设置相对来说要更加麻烦一些,它需要在Tanzu Kubernetes群集的每一个节点上(包括所有的Master节点和Workder节点),都要加入Harbor自签名的CA的证书信息,使其信任。下面我们来看看具体的步骤:

  1. 通过浏览器登录到Harbor界面。选择你要访问的Project或者(Namespace)。
  2. 转到“Repositories”页,下载注册表证书并保存到一个文件里,例如crt.
  3. 通过kubectl命令,以数据中心管理员或者Namespace管理员的身份登录Supervisor 集群
    $ kubectl vsphere login --server=10.117.233.1 --vsphere-username [email protected]
  4. 在Tanzu集群所在的Supervisor命名空间里查看秘钥的信息,可以通过以下命令获得有关Tanzu集群跟SSH登录相关的秘钥信息,记住这个secret的名字,在下面的例子输出中是“my-tanzu-cluster-ssh”。

    kubectl get secret -n wangyu | grep ssh-auth
    my-tanzu-cluster-ssh     kubernetes.io/ssh-auth        1      18d
  5. 查看Tanzu集群的节点信息和IP地址

    $ kubectl get virtualmachines -n wangyu
    NAME                                              AGE
    my-tanzu-cluster-control-plane-kffsj              18d
    wy-tanzu-cluster-workers-c5jwz-86b575c4bb-5lbst   18d
    wy-tanzu-cluster-workers-c5jwz-86b575c4bb-9hwqg   18d
    wy-tanzu-cluster-workers-c5jwz-86b575c4bb-lvth9   18d

    $ kubectl get virtualmachines -n wangyu -o yaml | grep vmIp
       vmIp: 10.244.1.2
       vmIp: 10.244.1.3
       vmIp: 10.244.1.5
        vmIp: 10.244.1.4
  6. 创建configmap,以便于将Harbor的证书导入。
    $ kubectl create configmap harborca --from-file=harbor.crt -n wangyu
  7. 通过kubectl创建一个跳板机,并且将访问Tanzu集群节点的秘钥和Harbor的证书也一起放在跳板机里,就可以方便的将这个证书信息导入到Tanzu集群中的各个节点里。

    $ more jumpbox.yaml
    apiVersion: v1
    kind: Pod
    metadata:
    name: jumpbox
    spec:
    containers:
    - image: "photon:3.0"
       name: jumpbox
       command: [ "/bin/bash", "-c", "--" ]
       args: [ "yum install -y openssh-server; mkdir /root/.ssh; cp /root/ssh/ssh-privatekey /root/.ssh/id_rsa; \
    chmod 600 /root/.ssh/id_rsa; while true; do sleep 30; done;" ]

       volumeMounts:
         - mountPath: "/root/ssh"
           name: ssh-key
           readOnly: true  
         - name: harborca
           mountPath: /tmp/harborca
    volumes:    
    - name: ssh-key
         secret:
           secretName: my-tanzu-cluster-ssh
       - name: harborca
         configMap:
            name: harborca
  8. 在当前的命名空间里部署这个跳板机
    $ kubectl apply -f jumpbox.yaml -n wangyu
    
    pod/jumpbox created
  9. 并且在每个节点上执行下面的命令(注意替换节点的IP地址)。

    $ kubectl exec -it jumpbox /usr/bin/scp /tmp/harborca/harbor.crt [email protected]:/tmp/harbor.crt
    $ kubectl exec -it jumpbox /usr/bin/ssh [email protected] 'sudo bash -c \ "cat /tmp/harbor.crt >> /etc/pki/tls/certs/ca-bundle.crt"'
    $ kubectl exec -it jumpbox /usr/bin/ssh [email protected] 'sudo systemctl restart docker.service'
  10. 以Tanzu集群管理员的身份登录。(详细步骤请参考vSphere with Kubernetes实战之:用户访问控制
  11. 在Tanzu的命名空间里(你想要部署应用的命名空间)创建一个映像拉取的secret,例如在下面的例子中,我们在default命名空间里创建了一个名为“externalimagepull”的secret(你可以任意选择其他的名称)。”–docker-server”指向你的Harbor地址;最后还需要提供登录Harbor的用户名和密码。

    $ kubectl -n default create secret docker-registry externalimgpull --docker-server=10.147.18.2 \ --docker-username=YourName --docker-password=YourPasswd
    secret/externalimgpull created
  12. 现在可以在Tanzu集群上的命名空间(本例中为default)部署Pod或Deployment,将刚才创建的secret应用到部署文件中去:

    $ more nginx-harbor.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: nginx-deployment
    labels:
       app: myapp
    spec:
    replicas: 1
    selector:
       matchLabels:
         app: myapp
    template:
       metadata:
         labels:
           app: myapp
       spec:
         containers:
         - name: nginx
           image: 10.147.18.2/wangyu/nginx:latest
           ports:
           - containerPort: 80
         imagePullSecrets:
           - name: externalimgpull
  13. 设置完成后可以删除跳板机。

 

6. 总结

在各种开发部署的环境下,一般的映像注册表都会使用自签名的证书。如果是在Supervisor集群中使用vSphere with Kubernetes内置的harbor,那比较简单,基本上不需要做任何额外的配置。但是如果想在Tanzu集群中也使用内置或者外置的映像注册表,就需要按照本文描述的一步步操作来完成这些任务。

 

JiaMing的评论:

文中第五大步第7小步需要ingress和egress连接公网
在一些客户环境,在做vsphere with kubernetes测试时,ingress个egress不能连接公网,这样用于ssh到tanzu集群节点的jumpbox无法从公网拉取photon镜像。此时,我们需要从公网拉取photon镜像并装上ssh工具,把镜像打包上传至harbor,让jumpbox拉取harbor中的photon镜像作为ssh到tanzu集群节点的跳板机。
1.从公网拉取photon镜像
docker pull photon:3.0

2.利用该镜像创建一个容器,并给容器安装ssh
docker run -t -i photon:3.0 /bin/bash
yum install openssh-server
exit

3.查找刚修改的容器名
docker ps -a
例子中container id 为26ef51868114

4.将修改过的容器作为镜像上传
docker commit 26ef51868114 photon-ssh

5.把镜像上传到harbor
docker login
docker tag photon-ssh // photon-ssh
docker push // photon-ssh

6.在superviser cluster从harbor拉取photon-ssh镜像创建跳板机,在yaml里把image指向harbor的镜像。

apiVersion: v1
kind: Pod
metadata:
  name: jumpbox
  spec:
    containers:
  - image: // photon-ssh
    name: jumpbox
    command: [ "/bin/bash", "-c", "--" ]
    args: [ "rm -rf /root/.ssh; mkdir /root/.ssh; cp /root/ssh/ssh-privatekey /root/.ssh/id_rsa;chmod 600 /root/.ssh/id_rsa; while true; do sleep 30; done;" ]
    volumeMounts:
      - mountPath: "/root/ssh"
        name: ssh-key
        readOnly: true   
      - name: harborca
        mountPath: /tmp/harborca
  volumes:
    - name: ssh-key
      secret:
        secretName: my-tanzu-cluster-ssh
    - name: harborca
      configMap:
        name: harborca

后续步骤一致