使用 DevStream 部署 Harbor

    • 有一个可用的 Kubernetes 集群,版本 1.10+
    • 配置好 StorageClass

    可选满足

    • 配置好 Ingress 控制器(如果需要使用 Ingress 暴露服务)

    Harbor 本身并不关注如何实现存储高可用,所以 Harbor 通过 PVCs 的方式持久化数据。 另外 Harbor 也不关注 PostgreSQL、Redis 的高可用方式部署,而是提供了对接外部 PostgreSQL 和 Redis 的能力。

    Harbor 部署架构整体如下图所示(图片来自 Harbor 官网):

    下文将介绍如何配置 插件,完成 Harbor 应用的部署。

    minikube 方式部署的 k8s 集群自带一个默认的 StorageClass,另外部署 Ingress 控制器只需要执行 minikube addons enable ingress 命令即可。 其他方式部署的 k8s 集群中如何配置 StorageClass 和 Ingress Controller,请查阅k8s 官方文档

    你可以选择顺序阅读本文档,从而全面了解 harbor 插件的使用方法;也可以选择直接跳到下文”典型场景”小节,直接寻找你感兴趣的使用场景开始阅读。

    如果仅是用于开发、测试等目的,希望快速完成 Harbor 的部署,可以使用如下配置快速开始:

    config.yaml

    在成功执行 dtm apply 命令后,我们可以在 harbor 命名空间下看到下述主要资源:

    • Deployment (kubectl get deployment -n harbor)

    几乎所有 Harbor 相关服务都是以 Deployment 方式在运行:

    Bash

    1. NAME READY UP-TO-DATE AVAILABLE AGE
    2. harbor-core 1/1 1 1 2m56s
    3. harbor-jobservice 1/1 1 1 2m56s
    4. harbor-nginx 1/1 1 1 2m56s
    5. harbor-portal 1/1 1 1 2m56s
    6. harbor-registry 1/1 1 1 2m56s
    • StatefulSet (kubectl get statefulset -n harbor)

    StatefulSet 资源对应的是 Harbor 所依赖的 PostgreSQL 和 Redis。 换言之,当前部署方式会自动完成 PostgreSQL 和 Redis 的部署,但是同时需要注意 PostgreSQL 和 Redis 并不是高可用的:

    Bash

    1. NAME READY AGE
    2. harbor-database 1/1 3m40s
    3. harbor-redis 1/1 3m40s
    • Service (kubectl get service -n harbor)

    默认情况下 Harbor 通过 NodePort 方式暴露服务时,端口是 30002:

    Bash

    1. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    2. harbor NodePort 10.99.177.6 <none> 80:30002/TCP 4m17s
    3. harbor-core ClusterIP 10.106.220.239 <none> 80/TCP 4m17s
    4. harbor-database ClusterIP 10.102.102.95 <none> 5432/TCP 4m17s
    5. harbor-jobservice ClusterIP 10.98.5.49 <none> 80/TCP 4m17s
    6. harbor-portal ClusterIP 10.105.115.5 <none> 80/TCP 4m17s
    7. harbor-redis ClusterIP 10.104.100.167 <none> 6379/TCP 4m17s
    8. harbor-registry ClusterIP 10.106.124.148 <none> 5000/TCP,8080/TCP 4m17s
    • PersistentVolumeClaim (kubectl get pvc -n harbor)

    Harbor 所需要的存储卷有4个,其中也包括了 PostgreSQL 和 Redis 的存储:

    Bash

    1. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
    2. data-harbor-redis-0 Bound pvc-5b6b5eb4-c40d-4f46-8f19-ff3a8869e56f 1Gi RWO standard 5m12s
    3. database-data-harbor-database-0 Bound pvc-d7ccaf1f-c450-4a16-937a-f55ad0c7c18d 1Gi RWO standard 5m12s
    4. harbor-jobservice Bound pvc-9407ef73-eb65-4a56-8720-a9ddbcb76fef 1Gi RWO standard 5m13s
    5. harbor-registry Bound pvc-34a2b88d-9ff2-4af4-9faf-2b33e97b971f 5Gi RWO standard 5m13s
    • PersistentVolume (kubectl get pv)

    我们并没有配置 StorageClass,所以这里用的是集群内的 default StorageClass 完成的 pv 创建:

    Bash

    1. pvc-34a2b88d-9ff2-4af4-9faf-2b33e97b971f 5Gi RWO Delete Bound harbor/harbor-registry standard 5m22s
    2. pvc-5b6b5eb4-c40d-4f46-8f19-ff3a8869e56f 1Gi RWO Delete Bound harbor/data-harbor-redis-0 standard 5m22s
    3. pvc-9407ef73-eb65-4a56-8720-a9ddbcb76fef 1Gi RWO Delete Bound harbor/harbor-jobservice standard 5m22s
    4. pvc-d7ccaf1f-c450-4a16-937a-f55ad0c7c18d 1Gi RWO Delete Bound harbor/database-data-harbor-database-0 standard 5m22s

    在当前演示环境里,default StorageClass 如下(kubectl get storageclass):

    Bash

    1. NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
    2. standard (default) k8s.io/minikube-hostpath Delete Immediate false 20h

    到这里,我们就可以通过 http://127.0.0.1:3002 访问到 Harbor 登录页面了,如下:

    Harbor Login

    默认登录账号/密码是 admin/Harbor12345。登录后,可以看到默认首页如下:

    如果是在云主机上部署的 Harbor,可以通过 kubectl port-forward 命令来暴露服务:

    Bash

    注意:这里得使用主机真实网卡 ip,而我们在浏览器上输入的 ip 是云主机的公网 ip,两者并不一样。

    3.2、默认配置

    因此完整的配置文件应该是这样:

    3.3、持久化存储数据

    前面我们已经看到了如果不指定 StorageClass,Harbor 会使用集群内的 default StorageClass。 在当前演示环境中,default StorageClass 是通过 hostPath 方式提供 pv 的,因此我们可以看到 harbor-registry 所使用的 pv 配置大致如下:

    YAML

    1. apiVersion: v1
    2. kind: PersistentVolume
    3. metadata:
    4. name: pvc-34a2b88d-9ff2-4af4-9faf-2b33e97b971f
    5. spec:
    6. accessModes:
    7. - ReadWriteOnce
    8. capacity:
    9. storage: 5Gi
    10. claimRef:
    11. apiVersion: v1
    12. kind: PersistentVolumeClaim
    13. name: harbor-registry
    14. namespace: harbor
    15. hostPath:
    16. path: /tmp/hostpath-provisioner/harbor/harbor-registry
    17. persistentVolumeReclaimPolicy: Delete
    18. storageClassName: standard
    19. volumeMode: Filesystem
    20. status:
    21. phase: Bound

    可见数据其实挂在到了主机的 /tmp/hostpath-provisioner/harbor/harbor-registry 目录下了。

    Harbor 支持3种持久化存储数据配置方式:

    1. 配置 StorageClass(默认使用 default StorageClass);
    2. 使用已经存在的 pvc(手动创建);
    3. 对接 azure、gcs、s3、swift、oss 等实现镜像和 Charts 云端存储。

    我们暂时只介绍第一种方式,也就是指定 StorageClass 实现存储数据持久化。 registry、jobservice、chartmuseum、database、redis、trivy 等组件都可以单独指定 StorageClass。 假设我们现在有一个新的 StorageClass 叫做 nfs,这时候要实现前面部署的 Harbor 所有数据持久化到外部 pv,我们可以这样配置:

    YAML

    1. tools:
    2. - name: helm-installer
    3. instanceID: harbor-001
    4. dependsOn: [ ]
    5. options:
    6. valuesYaml: |
    7. persistence:
    8. persistentVolumeClaim:
    9. registry:
    10. storageClass: "nfs"
    11. accessMode: ReadWriteOnce
    12. size: 5Gi
    13. jobservice:
    14. storageClass: "nfs"
    15. accessMode: ReadWriteOnce
    16. size: 1Gi
    17. database:
    18. storageClass: "nfs"
    19. accessMode: ReadWriteOnce
    20. size: 1Gi
    21. redis:
    22. storageClass: "nfs"
    23. accessMode: ReadWriteOnce
    24. size: 1Gi

    Harbor 可以以 ClusterIP、LoadBalancer、NodePort 和 Ingress 等方式对外暴露服务。我们前面使用的就是 NodePort 方式:

    YAML

    1. tools:
    2. - name: helm-installer
    3. instanceID: harbor-001
    4. dependsOn: [ ]
    5. options:
    6. valuesYaml: |
    7. externalURL: http://127.0.0.1
    8. expose:
    9. type: nodePort

    接下来我们再介绍一下如何使用 Ingress 方式暴露服务:

    YAML

    1. tools:
    2. - name: helm-installer
    3. instanceID: harbor-001
    4. options:
    5. valuesYaml: |
    6. externalURL: http://core.harbor.domain
    7. expose:
    8. type: ingress
    9. tls:
    10. enabled: false
    11. ingress:
    12. hosts:
    13. core: core.harbor.domain

    注意:如果没有开启 TLS,这种方式暴露 Harbor 服务后 docker push/pull 命令必须带上端口。

    3.5、PostgreSQL 和 Redis 高可用

    Harbor 依赖 PostgreSQL 和 Redis 服务,默认情况下自动部署的 PostgreSQL 和 Redis 服务都是非高可用模式。 换言之如果我们单独部署高可用的 PostgreSQL 和 Redis 服务,Harbor 是支持去对接外部 PostgreSQL 和 Redis 服务的。

    TODO(daniel-hutao): 本节待细化

    3.6、https 配置

    TODO(daniel-hutao): 本节待细化

    • 使用自签名证书
    • tls.enabled 设置为 true,并编辑对应的域名 externalURL
    • 将 Pod harbor-core 中目录 /etc/core/ca 存储的自签名证书复制到自己的本机;
    • 在自己的主机上信任该证书。
    • 使用公共证书
    • 将公共证书添加为密钥 ();
    • tls.enabled 设置为 true,并编辑对应的域名 externalURL
    • 配置 tls.secretName 使用该公共证书。

    // TODO(daniel-hutao): 本节待补充

    4.1.1 HTTP + Registry + Internal Database + Internal Redis

    • 协议:http
    • 服务暴露:Ingress
    • PostgreSQL 和 Redis:自动部署

    域名和 StorageClass 可以灵活修改,这里以 core.harbor.domain 和 nfs 为例:

    YAML

    1. tools:
    2. - name: helm-installer
    3. instanceID: harbor-001
    4. dependsOn: [ ]
    5. options:
    6. valuesYaml: |
    7. externalURL: http://core.harbor.domain
    8. expose:
    9. type: ingress
    10. tls:
    11. enabled: false
    12. ingress:
    13. hosts:
    14. core: core.harbor.domain
    15. chartmuseum:
    16. enabled: false
    17. notary:
    18. enabled: false
    19. trivy:
    20. enabled: false
    21. persistence:
    22. persistentVolumeClaim:
    23. registry:
    24. storageClass: "nfs"
    25. accessMode: ReadWriteOnce
    26. size: 5Gi
    27. jobservice:
    28. storageClass: "nfs"
    29. accessMode: ReadWriteOnce
    30. size: 1Gi
    31. database:
    32. storageClass: "nfs"
    33. accessMode: ReadWriteOnce
    34. size: 1Gi
    35. redis:
    36. storageClass: "nfs"
    37. accessMode: ReadWriteOnce
    38. size: 1Gi

    部署完成后,可以看到 Ingress 配置如下(`kubectl get ingress -n harbor):

    Bash

    1. NAME CLASS HOSTS ADDRESS PORTS AGE
    2. harbor-ingress nginx core.harbor.domain 192.168.49.2 80 2m8s

    如果你的 DNS 服务器可以解析到这个域名,那么这时候就可以在浏览器里通过 访问到 Harbor 了。 如果是测试环境,我们也可以通过修改 hosts 配置来完成这个域名解析,比如:

    • 修改 hosts (cat /etc/hosts | grep harbor)

    Bash

    对于 minikube 部署的 k8s 集群,这里的 ip 可以通过 minikube ip 命令获取。 如果是正式的 k8s 集群,这里的 ip 是 k8s 集群对外暴露的 vip,可能是一个节点 ip,也可能是一个 F5 上的 vip,或者 haproxy 等方式提供的 vip。

    4.1.2 HTTP + Registry + Chartmuseum + Internal Database + Internal Redis

    4.1.3 HTTP + Registry + Chartmuseum + External Database + External Redis

    4.2、HTTPS

    4.1.1 HTTPS + Registry + Internal Database + Internal Redis

    4.1.2 HTTPS + Registry + Chartmuseum + Internal Database + Internal Redis

    4.1.3 HTTPS + Registry + Chartmuseum + External Database + External Redis

    // TODO(daniel-hutao): 本节内容近期将持续补充完善

    5.1、Helm Chart 包

    如果需要在离线环境部署 Harbor,你需要下载对应的 helm chart 包:

    Bash

    1. helm repo add harbor https://helm.goharbor.io
    2. helm repo update
    3. helm search repo harbor -l
    4. helm pull harbor/harbor --version=1.10.0

    这时你会得到一个 harbor-1.10.0.tgz 文件,你可以将其存放到一个合适的目录,比如 ~/devstream-test/harbor-1.10.0.tgz,然后在配置文件就可以这样引用这个 chart 包了:

    YAML

    1. tools:
    2. - name: helm-installer
    3. instanceID: harbor-001
    4. dependsOn: [ ]
    5. options:
    6. chart:
    7. chartPath: "~/devstream-test/harbor-1.10.0.tgz"

    harbor 插件支持使用自定义容器镜像,你需要先在 valuesYaml 部分加上如下配置:

    YAML

    1. valuesYaml: |
    2. nginx:
    3. image:
    4. repository: [[ imageRepo ]]/goharbor/nginx-photon
    5. tag: v2.5.3
    6. portal:
    7. image:
    8. repository: [[ imageRepo ]]/goharbor/harbor-portal
    9. tag: v2.5.3
    10. core:
    11. image:
    12. repository: [[ imageRepo ]]/goharbor/harbor-core
    13. tag: v2.5.3
    14. jobservice:
    15. image:
    16. repository: [[ imageRepo ]]/goharbor/harbor-jobservice
    17. tag: v2.5.3
    18. registry:
    19. registry:
    20. image:
    21. repository: [[ imageRepo ]]/goharbor/registry-photon
    22. tag: v2.5.3
    23. controller:
    24. image:
    25. repository: [[ imageRepo ]]/goharbor/harbor-registryctl
    26. tag: v2.5.3
    27. chartmuseum:
    28. image:
    29. repository: [[ imageRepo ]]/goharbor/chartmuseum-photon
    30. tag: v2.5.3
    31. trivy:
    32. image:
    33. repository: [[ imageRepo ]]/goharbor/trivy-adapter-photon
    34. tag: v2.5.3
    35. notary:
    36. server:
    37. image:
    38. repository: [[ imageRepo ]]/goharbor/notary-server-photon
    39. tag: v2.5.3
    40. signer:
    41. image:
    42. repository: [[ imageRepo ]]/goharbor/notary-signer-photon
    43. tag: v2.5.3
    44. database:
    45. internal:
    46. image:
    47. tag: v2.5.3
    48. redis:
    49. internal:
    50. image:
    51. repository: [[ imageRepo ]]/goharbor/redis-photon
    52. tag: v2.5.3
    53. image:
    54. repository: [[ imageRepo ]]/goharbor/harbor-exporter
    55. tag: v2.5.3

    这段配置中留了一个变量 [[ imageRepo ]],你可以在中定义这个变量,变量值设置成你的镜像仓库地址,例如:

    YAML

    1. imageRepo: harbor.example.com:9000

    当然,你需要保证需要的镜像都在你的镜像仓库中存在。

    你可以下载镜像列表文件, 然后借助工具脚本来准备镜像。

    Bash

    1. curl -o harbor-images.txt https://raw.githubusercontent.com/devstream-io/devstream/main/docs/plugins/helm-installer/harbor/harbor-images.txt
    2. curl -o image-pull-push.sh https://raw.githubusercontent.com/devstream-io/devstream/main/hack/image-pull-push.sh
    3. chmod +x image-pull-push.sh
    4. # 查看工具脚本的使用方法和注意事项等
    5. ./image-pull-push.sh -h
    6. # 设置镜像仓库地址,按需修改
    7. export IMAGE_REPO_ADDR=harbor.devstream.io
    8. # 下载 harbor-images.txt 中所有镜像并保存到本地压缩包中
    9. ./image-pull-push.sh -f harbor-images.txt -r ${IMAGE_REPO_ADDR} -s
    10. # 从压缩包中 load 镜像并 push 到私有镜像仓库(如果镜像仓库需要登录,则需要先手动执行 docker login)
    11. ./image-pull-push.sh -f harbor-images.txt -r ${IMAGE_REPO_ADDR} -l -u

    如果你还没有一个私有镜像仓库,可以参考这篇文章快速部署一个 Docker Registry。

    5.3、参考配置

    这时候我们需要指定本地 helm chart 包以及私有镜像仓库的镜像,所以整体的参考配置大致如下:

    YAML

    1. ---
    2. # variable config
    3. imageRepo: harbor.example.com:9000
    4. ---
    5. # plugin config
    6. tools:
    7. - name: helm-installer
    8. instanceID: harbor-001
    9. dependsOn: [ ]
    10. options:
    11. chart:
    12. chartPath: "~/devstream-test/harbor-1.10.0.tgz"
    13. valuesYaml: |
    14. externalURL: http://core.harbor.domain
    15. expose:
    16. type: ingress
    17. tls:
    18. enabled: false
    19. ingress:
    20. hosts:
    21. core: core.harbor.domain
    22. nginx:
    23. image:
    24. repository: [[ imageRepo ]]/goharbor/nginx-photon
    25. tag: v2.5.3
    26. portal:
    27. image:
    28. repository: [[ imageRepo ]]/goharbor/harbor-portal
    29. tag: v2.5.3
    30. core:
    31. image:
    32. repository: [[ imageRepo ]]/goharbor/harbor-core
    33. tag: v2.5.3
    34. jobservice:
    35. image:
    36. repository: [[ imageRepo ]]/goharbor/harbor-jobservice
    37. tag: v2.5.3
    38. registry:
    39. registry:
    40. image:
    41. repository: [[ imageRepo ]]/goharbor/registry-photon
    42. tag: v2.5.3
    43. controller:
    44. image:
    45. repository: [[ imageRepo ]]/goharbor/harbor-registryctl
    46. tag: v2.5.3
    47. chartmuseum:
    48. enabled: false
    49. image:
    50. repository: [[ imageRepo ]]/goharbor/chartmuseum-photon
    51. tag: v2.5.3
    52. trivy:
    53. enabled: false
    54. image:
    55. repository: [[ imageRepo ]]/goharbor/trivy-adapter-photon
    56. tag: v2.5.3
    57. notary:
    58. enabled: false
    59. server:
    60. image:
    61. repository: [[ imageRepo ]]/goharbor/notary-server-photon
    62. tag: v2.5.3
    63. signer:
    64. image:
    65. repository: [[ imageRepo ]]/goharbor/notary-signer-photon
    66. tag: v2.5.3
    67. database:
    68. internal:
    69. image:
    70. repository: [[ imageRepo ]]/goharbor/harbor-db
    71. tag: v2.5.3
    72. redis:
    73. internal:
    74. image:
    75. repository: [[ imageRepo ]]/goharbor/redis-photon
    76. tag: v2.5.3
    77. exporter:
    78. image:
    79. repository: [[ imageRepo ]]/goharbor/harbor-exporter
    80. tag: v2.5.3
    81. persistence:
    82. persistentVolumeClaim:
    83. registry:
    84. storageClass: "nfs"
    85. accessMode: ReadWriteOnce
    86. size: 5Gi
    87. jobservice:
    88. storageClass: "nfs"
    89. accessMode: ReadWriteOnce
    90. size: 1Gi
    91. database:
    92. storageClass: "nfs"
    93. accessMode: ReadWriteOnce
    94. size: 1Gi
    95. redis:
    96. storageClass: "nfs"
    97. size: 1Gi

    在这个参考配置里包含了全部可能用到的镜像,在部分组件不启用的情况下你完全可以移除相关的镜像配置项。不过保留在这里也不会有什么影响。