• Kubernetes DevOps: Harbor


    Harbor 是一个 CNCF 基金会托管的开源的可信的云原生 docker registry 项目,可以用于存储、签名、扫描镜像内容,Harbor 通过添加一些常用的功能如安全性、身份权限管理等来扩展 docker registry 项目,此外还支持在 registry 之间复制镜像,还提供更加高级的安全功能,如用户管理、访问控制和活动审计等,在新版本中还添加了 Helm 仓库托管的支持。

    Harbor 最核心的功能就是给 docker registry 添加上一层权限保护的功能,要实现这个功能,就需要我们在使用 docker login、pull、push 等命令的时候进行拦截,先进行一些权限相关的校验,再进行操作,其实这一系列的操作 docker registry v2 就已经为我们提供了支持,v2 集成了一个安全认证的功能,将安全认证暴露给外部服务,让外部服务去实现。

    本章使用到的yaml文件地址:https://files.cnblogs.com/files/sanduzxcvbnm/harbor_yaml.zip?t=1654757668

    Harbor 认证原理

    上面我们说了 docker registry v2 将安全认证暴露给了外部服务使用,那么是怎样暴露的呢?我们在命令行中输入 docker login https://registry.qikqiak.com 为例来为大家说明下认证流程:

    • docker client 接收到用户输入的 docker login 命令,将命令转化为调用 engine api 的 RegistryLogin 方法
    • 在 RegistryLogin 方法中通过 http 盗用 registry 服务中的 auth 方法
    • 因为我们这里使用的是 v2 版本的服务,所以会调用 loginV2 方法,在 loginV2 方法中会进行 /v2/ 接口调用,该接口会对请求进行认证
    • 此时的请求中并没有包含 token 信息,认证会失败,返回 401 错误,同时会在 header 中返回去哪里请求认证的服务器地址
    • registry client 端收到上面的返回结果后,便会去返回的认证服务器那里进行认证请求,向认证服务器发送的请求的 header 中包含有加密的用户名和密码
    • 认证服务器从 header 中获取到加密的用户名和密码,这个时候就可以结合实际的认证系统进行认证了,比如从数据库中查询用户认证信息或者对接 ldap 服务进行认证校验
    • 认证成功后,会返回一个 token 信息,client 端会拿着返回的 token 再次向 registry 服务发送请求,这次需要带上得到的 token,请求验证成功,返回状态码就是200了
    • docker client 端接收到返回的200状态码,说明操作成功,在控制台上打印 Login Succeeded 的信息 至此,整个登录过程完成,整个过程可以用下面的流程图来说明:

    要完成上面的登录认证过程有两个关键点需要注意:怎样让 registry 服务知道服务认证地址?我们自己提供的认证服务生成的 token 为什么 registry 就能够识别?

    对于第一个问题,比较好解决,registry 服务本身就提供了一个配置文件,可以在启动 registry 服务的配置文件中指定上认证服务地址即可,其中有如下这样的一段配置信息:

    ......
    auth:
      token:
        realm: token-realm
        service: token-service
        issuer: registry-token-issuer
        rootcertbundle: /root/certs/bundle
    ......
    

    其中 realm 就可以用来指定一个认证服务的地址,下面我们可以看到 Harbor 中该配置的内容。

    关于 registry 的配置,可以参考官方文档:https://docs.docker.com/registry/configuration/

    第二个问题,就是 registry 怎么能够识别我们返回的 token 文件?如果按照 registry 的要求生成一个 token,是不是 registry 就可以识别了?所以我们需要在我们的认证服务器中按照 registry 的要求生成 token,而不是随便乱生成。那么要怎么生成呢?我们可以在 docker registry 的源码中可以看到 token 是通过 JWT(JSON Web Token)来实现的,所以我们按照要求生成一个 JWT 的 token 就可以了。

    对 golang 熟悉的同学可以去 clone 下 Harbor 的代码查看下,Harbor 采用 beego 这个 web 开发框架,源码阅读起来不是特别困难。我们可以很容易的看到 Harbor 中关于上面我们讲解的认证服务部分的实现方法。

    安装

    由于 Harbor 涉及的组件太多了,所以我们这里用更加便捷的 Helm 来进行安装。首先添加仓库地址:

    $ helm repo add harbor https://helm.goharbor.io
    "harbor" has been added to your repositories
    

    在安装 Harbor 的时候有很多可以配置的参数,可以在 harbor-helm 项目上进行查看。

    比如这里我们将主域名配置为 harbor.k8s.local,通过前面的 Ceph RBD 的 StorageClass 来提供存储,又因为前面我们在安装 GitLab 的时候就已经单独安装了 postgresql 和 reids 两个数据库,所以我们也可以配置 Harbor 使用这两个外置的数据库,这样可以降低资源的使用。但是使用外置的数据库我们需要提前手动创建数据库,比如我们这里使用的 GitLab 提供的数据库,则进入该 Pod 创建 registry、clair、notary_server、notary_signer 这4个数据库:

    $ kubectl get pods -n kube-ops -l name=postgresql
    NAME                         READY   STATUS    RESTARTS   AGE
    postgresql-6f494df48-frsvp   1/1     Running   0          5d3h
    $ kubectl exec -it postgresql-6f494df48-frsvp /bin/bash -n kube-ops
    root@postgresql-6f494df48-frsvp:~$ sudo su - postgres
    postgres@postgresql-6f494df48-frsvp:~$ psql
    psql (10.9 (Ubuntu 10.9-1.pgdg18.04+1))
    Type "help" for help.
    
    postgres=# CREATE DATABASE registry OWNER postgres;  # 创建 registry 数据库
    CREATE DATABASE
    postgres=# GRANT ALL PRIVILEGES ON DATABASE registry to postgres;  # 授权给 postgres 用户
    GRANT
    postgres=# GRANT ALL PRIVILEGES ON DATABASE registry to gitlab;  # 授权给 gitlab 用户
    GRANT
    # Todo: 用同样的方式创建其他三个数据库:clair、notary_server、notary_signer
    ......
    postgres-# \q  # 退出
    

    数据库准备过后,就可以使用我们自己定制的 values 文件来进行安装了,完整的定制的 values 文件如下所示:(harbor-values.yaml)

    externalURL: https://harbor.k8s.local
    harborAdminPassword: Harbor12345
    logLevel: debug
    
    expose:
      type: ingress
      tls:
        enabled: true
      ingress:
        hosts:
          core: harbor.k8s.local
          notary: notary.k8s.local
    
    persistence:
      enabled: true
      resourcePolicy: "keep"
      persistentVolumeClaim:
        registry:
          # 如果需要做高可用,多个副本的组件则需要使用支持 ReadWriteMany 的后端,ceph rbd 是不支持的,可以使用 cephfs,可以换成前面我们使用的 rook-ceph-fs
          storageClass: "rook-ceph-block"  
          # 如果是高可用的,多个副本组件需要使用 ReadWriteMany,默认为 ReadWriteOnce
          accessMode: ReadWriteOnce  
          size: 50Gi
        chartmuseum:
          storageClass: "rook-ceph-block"
          accessMode: ReadWriteOnce
          size: 10Gi
        jobservice:
          storageClass: "rook-ceph-block"
          accessMode: ReadWriteOnce
          size: 2Gi
    
    database:
      type: external
      external:
        host: "postgresql.kube-ops.svc.cluster.local"
        port: "5432"
        username: "gitlab"
        password: "passw0rd"
    
    redis:
      type: external
      external:
        #host: "redis.kube-ops.svc.cluster.local"
        #port: "6379"
        addr: "redis.kube-ops.svc.cluster.local:6379" # 不再使用host和port,否则使用host和port写法,不会采用配置,而是用默认的192.168.0.2:6379
    
    # 默认为一个副本,如果要做高可用,只需要设置为 replicas >= 2 即可
    portal:
      replicas: 1
    core:
      replicas: 1
    jobservice:
      replicas: 1
    registry:
      replicas: 1
    chartmuseum: 
      replicas: 1
    clair:
      replicas: 1
    notary:
      server:
        replicas: 1
      signer:
        replicas: 1
    

    这些配置信息都是根据 Harbor 的 Chart 包默认的 values 值进行覆盖的。现在我们直接安装即可:

    $ helm install myharbor harbor/harbor -f values.yaml -n kube-ops
    NAME: myharbor
    LAST DEPLOYED: Tue May 12 15:23:15 2020
    NAMESPACE: kube-ops
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    NOTES:
    Please wait for several minutes for Harbor deployment to complete.
    Then you should be able to visit the Harbor portal at https://harbor.k8s.local.
    For more details, please visit https://github.com/goharbor/harbor.
    

    正常情况下隔一会儿就可以安装成功了:

    $ helm ls -n kube-ops
    NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART       APP VERSION
    myharbor        kube-ops        1               2020-05-12 15:23:15.194263 +0800 CST    deployed        harbor-1.3.21.10.2     
    $ kubectl get pods -n kube-ops -l app=harbor
    NAME                                            READY   STATUS    RESTARTS   AGE
    myharbor-harbor-chartmuseum-95f9d9898-mqqnn     1/1     Running   0          40m
    myharbor-harbor-clair-79fb8694b4-5dnk5          2/2     Running   10         40m
    myharbor-harbor-core-c7bdc46d9-qw9hc            1/1     Running   0          79s
    myharbor-harbor-jobservice-549d67dc87-j6hr8     1/1     Running   0          79s
    myharbor-harbor-notary-server-9d57d5d9f-9fvbw   1/1     Running   10         40m
    myharbor-harbor-notary-signer-658d7886-2ssrl    1/1     Running   10         40m
    myharbor-harbor-portal-65588b87db-d7whd         1/1     Running   0          40m
    myharbor-harbor-registry-5f7cc9d69d-89hkw       2/2     Running   0          40m
    

    安装完成后,我们就可以将域名 harbor.k8s.local 解析到 Ingress Controller 所在的节点,我们这里使用的仍然是 Traefik,由于我们开启了 KubernetesIngress 支持的,所以我们只需要将域名解析到 Traefik 的 Pod 所在节点即可,然后就可以通过该域名在浏览器中访问了:

    $ kubectl get ingress -n kube-ops
    NAME                      HOSTS                               ADDRESS   PORTS     AGE
    myharbor-harbor-ingress   harbor.k8s.local,notary.k8s.local             80, 443   41m
    

    用户名使用默认的 admin,密码则是上面配置的默认 Harbor12345:

    登录过后即可进入 Harbor 的 Dashboard 页面:

    我们可以看到有很多功能,默认情况下会有一个名叫 library 的项目,该项目默认是公开访问权限的,进入项目可以看到里面还有 Helm Chart 包的管理,可以手动在这里上传,也可以对该项目里面的镜像进行一些其他配置。

    推送镜像

    现在我们来测试下使用 docker cli 来进行 pull/push 镜像,直接使用 docker login 命令登录:

    $ docker login harbor.k8s.local
    Username: admin
    Password: 
    INFO[0006] Error logging in to v2 endpoint, trying next endpoint: Get https://harbor.k8s.local/v2/: x509: certificat
    e signed by unknown authority 
    Get https://harbor.k8s.local/v2/: x509: certificate signed by unknown authority
    

    这是因为在使用 docker login 登录的时候会使用 https 的服务,而我们这里是自签名的证书,所以就报错了,我们可以将使用到的 CA 证书文件复制到 /etc/docker/certs.d/harbor.k8s.local 目录下面来解决这个问题(如果该目录不存在,则创建它)。ca.crt 这个证书文件我们可以通过 Ingress 中使用的 Secret 资源对象来提供:

    $ kubectl get secret myharbor-harbor-ingress -n kube-ops -o yaml
    apiVersion: v1
    data:
      ca.crt: <ca.crt>
      tls.crt: <tls.crt>
      tls.key: <tls.key>
    kind: Secret
    metadata:
      creationTimestamp: "2020-05-12T07:23:20Z"
      labels:
        app: harbor
        chart: harbor
        heritage: Helm
        release: myharbor
      name: myharbor-harbor-ingress
      namespace: kube-ops
      resourceVersion: "67162075"
      selfLink: /api/v1/namespaces/kube-ops/secrets/myharbor-harbor-ingress
      uid: 10a9d22d-3889-4ac4-87c4-5cda7e701828
    type: kubernetes.io/tls
    

    其中 data 区域中 ca.crt 对应的值就是我们需要证书,不过需要注意还需要做一个 base64 的解码,这样证书配置上以后就可以正常访问了。

    不过由于上面的方法较为繁琐,所以一般情况下面我们在使用 docker cli 的时候是在 docker 启动参数后面添加一个 --insecure-registry 参数来忽略证书的校验的,在 docker 启动配置文件 /usr/lib/systemd/system/docker.service 中修改ExecStart的启动参数:

    ExecStart=/usr/bin/dockerd --insecure-registry harbor.k8s.local
    

    或者在 Docker Daemon 的配置文件中添加:

    $ cat /etc/docker/daemon.json
    {
      "insecure-registries" : [
        "harbor.k8s.local"
      ],
      "registry-mirrors" : [
        "https://ot2k4d59.mirror.aliyuncs.com/"
      ]
    }
    

    然后保存重启 docker,再使用 docker cli 就没有任何问题了:

    $ docker login harbor.k8s.local
    Username: admin
    Password:
    Login Succeeded
    

    比如我们本地现在有一个名为 busybox 的镜像,现在我们想要将该镜像推送到我们的私有仓库中去,应该怎样操作呢?首先我们需要给该镜像重新打一个具有 harbor.k8s.local 前缀的镜像,然后推送的时候就可以识别到推送到哪个镜像仓库:

    $ docker tag busybox harbor.k8s.local/library/busybox
    $ docker push harbor.k8s.local/library/busybox
    The push refers to repository [harbor.k8s.local/library/busybox]
    5b0d2d635df8: Pushed 
    latest: digest: sha256:a2490cec4484ee6c1068ba3a05f89934010c85242f736280b35343483b2264b6 size: 527
    

    推送完成后,我们同样可以在 Portal 页面上看到这个镜像的信息:

    镜像 push 成功,同样可以测试下 pull:

    $ docker rmi harbor.k8s.local/library/busybox
    Untagged: harbor.k8s.local/library/busybox:latest
    Untagged: harbor.k8s.local/library/busybox@sha256:a2490cec4484ee6c1068ba3a05f89934010c85242f736280b35343483b2264b6
    $ docker rmi busybox
    Untagged: busybox:latest
    Untagged: busybox@sha256:061ca9704a714ee3e8b80523ec720c64f6209ad3f97c0ff7cb9ec7d19f15149f
    $ docker pull harbor.k8s.local/library/busybox:latest
    latest: Pulling from library/busybox
    e2334dd9fee4: Already exists 
    Digest: sha256:a2490cec4484ee6c1068ba3a05f89934010c85242f736280b35343483b2264b6
    Status: Downloaded newer image for harbor.k8s.local/library/busybox:latest
    harbor.k8s.local/library/busybox:latest
    
    $ docker images |grep busybox
    harbor.k8s.local/library/busybox                                                                                                       latest              be5888e67be6        3 weeks ago         1.22MB
    

    到这里证明上面我们的私有 docker 仓库搭建成功了,大家可以尝试去创建一个私有的项目,然后创建一个新的用户,使用这个用户来进行 pull/push 镜像,Harbor还具有其他的一些功能,比如镜像复制,大家可以自行测试,感受下 Harbor 和官方自带的 registry 仓库的差别。

    高可用

    Harbor 的大部分组件都是无状态的应用,比如 portal、core 等只需要增加其相应的副本数量即可;在存储数据层面,需要提供高可用的 Postgresql、Redis 集群,另外针对镜像和 chart 服务也需要提供可持久的存储。

    要做高可用的 Harbor,我们需要将数据库通过外置方式进行提供,当然也需要保证数据库是高可用的。然后存储需要允许跨节点数据共享,也就是要支持 ReadWriteMany 模式,我们这里使用 Ceph 的话就需要使用 CephFS。然后将其他组件副本数设置为大于等于2:portal.replicas、core.replicas、jobservice.replicas、registry.replicas、chartmuseum.replicas、clair.replicas、notary.server.replicas 以及 notary.signer.replicas。

    然后重新使用 Helm 进行安装即可。

  • 相关阅读:
    Vue2.5
    Vue --- :is
    Vue面试中经常会被问到的面试题
    100道JS构造函数面试题
    100道前端面试题
    占位
    06-验证码-基本功能实现
    由ES规范学JavaScript(二):深入理解“连等赋值”问题
    JS中keyup, keypress, keydown以及oninput四个事件的区别
    Java中class的声明
  • 原文地址:https://www.cnblogs.com/sanduzxcvbnm/p/16326778.html
Copyright © 2020-2023  润新知