使用 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
NAME READY UP-TO-DATE AVAILABLE AGE
harbor-core 1/1 1 1 2m56s
harbor-jobservice 1/1 1 1 2m56s
harbor-nginx 1/1 1 1 2m56s
harbor-portal 1/1 1 1 2m56s
harbor-registry 1/1 1 1 2m56s
- StatefulSet (
kubectl get statefulset -n harbor
)
StatefulSet 资源对应的是 Harbor 所依赖的 PostgreSQL 和 Redis。 换言之,当前部署方式会自动完成 PostgreSQL 和 Redis 的部署,但是同时需要注意 PostgreSQL 和 Redis 并不是高可用的:
Bash
NAME READY AGE
harbor-database 1/1 3m40s
harbor-redis 1/1 3m40s
- Service (
kubectl get service -n harbor
)
默认情况下 Harbor 通过 NodePort 方式暴露服务时,端口是 30002:
Bash
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
harbor NodePort 10.99.177.6 <none> 80:30002/TCP 4m17s
harbor-core ClusterIP 10.106.220.239 <none> 80/TCP 4m17s
harbor-database ClusterIP 10.102.102.95 <none> 5432/TCP 4m17s
harbor-jobservice ClusterIP 10.98.5.49 <none> 80/TCP 4m17s
harbor-portal ClusterIP 10.105.115.5 <none> 80/TCP 4m17s
harbor-redis ClusterIP 10.104.100.167 <none> 6379/TCP 4m17s
harbor-registry ClusterIP 10.106.124.148 <none> 5000/TCP,8080/TCP 4m17s
- PersistentVolumeClaim (
kubectl get pvc -n harbor
)
Harbor 所需要的存储卷有4个,其中也包括了 PostgreSQL 和 Redis 的存储:
Bash
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-harbor-redis-0 Bound pvc-5b6b5eb4-c40d-4f46-8f19-ff3a8869e56f 1Gi RWO standard 5m12s
database-data-harbor-database-0 Bound pvc-d7ccaf1f-c450-4a16-937a-f55ad0c7c18d 1Gi RWO standard 5m12s
harbor-jobservice Bound pvc-9407ef73-eb65-4a56-8720-a9ddbcb76fef 1Gi RWO standard 5m13s
harbor-registry Bound pvc-34a2b88d-9ff2-4af4-9faf-2b33e97b971f 5Gi RWO standard 5m13s
- PersistentVolume (
kubectl get pv
)
我们并没有配置 StorageClass,所以这里用的是集群内的 default StorageClass 完成的 pv 创建:
Bash
pvc-34a2b88d-9ff2-4af4-9faf-2b33e97b971f 5Gi RWO Delete Bound harbor/harbor-registry standard 5m22s
pvc-5b6b5eb4-c40d-4f46-8f19-ff3a8869e56f 1Gi RWO Delete Bound harbor/data-harbor-redis-0 standard 5m22s
pvc-9407ef73-eb65-4a56-8720-a9ddbcb76fef 1Gi RWO Delete Bound harbor/harbor-jobservice standard 5m22s
pvc-d7ccaf1f-c450-4a16-937a-f55ad0c7c18d 1Gi RWO Delete Bound harbor/database-data-harbor-database-0 standard 5m22s
在当前演示环境里,default StorageClass 如下(kubectl get storageclass
):
Bash
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
standard (default) k8s.io/minikube-hostpath Delete Immediate false 20h
到这里,我们就可以通过 http://127.0.0.1:3002 访问到 Harbor 登录页面了,如下:
默认登录账号/密码是 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
apiVersion: v1
kind: PersistentVolume
metadata:
name: pvc-34a2b88d-9ff2-4af4-9faf-2b33e97b971f
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 5Gi
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: harbor-registry
namespace: harbor
hostPath:
path: /tmp/hostpath-provisioner/harbor/harbor-registry
persistentVolumeReclaimPolicy: Delete
storageClassName: standard
volumeMode: Filesystem
status:
phase: Bound
可见数据其实挂在到了主机的 /tmp/hostpath-provisioner/harbor/harbor-registry
目录下了。
Harbor 支持3种持久化存储数据配置方式:
- 配置 StorageClass(默认使用 default StorageClass);
- 使用已经存在的 pvc(手动创建);
- 对接 azure、gcs、s3、swift、oss 等实现镜像和 Charts 云端存储。
我们暂时只介绍第一种方式,也就是指定 StorageClass 实现存储数据持久化。 registry、jobservice、chartmuseum、database、redis、trivy 等组件都可以单独指定 StorageClass。 假设我们现在有一个新的 StorageClass 叫做 nfs,这时候要实现前面部署的 Harbor 所有数据持久化到外部 pv,我们可以这样配置:
YAML
tools:
- name: helm-installer
instanceID: harbor-001
dependsOn: [ ]
options:
valuesYaml: |
persistence:
persistentVolumeClaim:
registry:
storageClass: "nfs"
accessMode: ReadWriteOnce
size: 5Gi
jobservice:
storageClass: "nfs"
accessMode: ReadWriteOnce
size: 1Gi
database:
storageClass: "nfs"
accessMode: ReadWriteOnce
size: 1Gi
redis:
storageClass: "nfs"
accessMode: ReadWriteOnce
size: 1Gi
Harbor 可以以 ClusterIP、LoadBalancer、NodePort 和 Ingress 等方式对外暴露服务。我们前面使用的就是 NodePort 方式:
YAML
tools:
- name: helm-installer
instanceID: harbor-001
dependsOn: [ ]
options:
valuesYaml: |
externalURL: http://127.0.0.1
expose:
type: nodePort
接下来我们再介绍一下如何使用 Ingress 方式暴露服务:
YAML
tools:
- name: helm-installer
instanceID: harbor-001
options:
valuesYaml: |
externalURL: http://core.harbor.domain
expose:
type: ingress
tls:
enabled: false
ingress:
hosts:
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
tools:
- name: helm-installer
instanceID: harbor-001
dependsOn: [ ]
options:
valuesYaml: |
externalURL: http://core.harbor.domain
expose:
type: ingress
tls:
enabled: false
ingress:
hosts:
core: core.harbor.domain
chartmuseum:
enabled: false
notary:
enabled: false
trivy:
enabled: false
persistence:
persistentVolumeClaim:
registry:
storageClass: "nfs"
accessMode: ReadWriteOnce
size: 5Gi
jobservice:
storageClass: "nfs"
accessMode: ReadWriteOnce
size: 1Gi
database:
storageClass: "nfs"
accessMode: ReadWriteOnce
size: 1Gi
redis:
storageClass: "nfs"
accessMode: ReadWriteOnce
size: 1Gi
部署完成后,可以看到 Ingress 配置如下(`kubectl get ingress -n harbor):
Bash
NAME CLASS HOSTS ADDRESS PORTS AGE
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
helm repo add harbor https://helm.goharbor.io
helm repo update
helm search repo harbor -l
helm pull harbor/harbor --version=1.10.0
这时你会得到一个 harbor-1.10.0.tgz
文件,你可以将其存放到一个合适的目录,比如 ~/devstream-test/harbor-1.10.0.tgz
,然后在配置文件就可以这样引用这个 chart 包了:
YAML
tools:
- name: helm-installer
instanceID: harbor-001
dependsOn: [ ]
options:
chart:
chartPath: "~/devstream-test/harbor-1.10.0.tgz"
harbor
插件支持使用自定义容器镜像,你需要先在 valuesYaml 部分加上如下配置:
YAML
valuesYaml: |
nginx:
image:
repository: [[ imageRepo ]]/goharbor/nginx-photon
tag: v2.5.3
portal:
image:
repository: [[ imageRepo ]]/goharbor/harbor-portal
tag: v2.5.3
core:
image:
repository: [[ imageRepo ]]/goharbor/harbor-core
tag: v2.5.3
jobservice:
image:
repository: [[ imageRepo ]]/goharbor/harbor-jobservice
tag: v2.5.3
registry:
registry:
image:
repository: [[ imageRepo ]]/goharbor/registry-photon
tag: v2.5.3
controller:
image:
repository: [[ imageRepo ]]/goharbor/harbor-registryctl
tag: v2.5.3
chartmuseum:
image:
repository: [[ imageRepo ]]/goharbor/chartmuseum-photon
tag: v2.5.3
trivy:
image:
repository: [[ imageRepo ]]/goharbor/trivy-adapter-photon
tag: v2.5.3
notary:
server:
image:
repository: [[ imageRepo ]]/goharbor/notary-server-photon
tag: v2.5.3
signer:
image:
repository: [[ imageRepo ]]/goharbor/notary-signer-photon
tag: v2.5.3
database:
internal:
image:
tag: v2.5.3
redis:
internal:
image:
repository: [[ imageRepo ]]/goharbor/redis-photon
tag: v2.5.3
image:
repository: [[ imageRepo ]]/goharbor/harbor-exporter
tag: v2.5.3
这段配置中留了一个变量 [[ imageRepo ]]
,你可以在中定义这个变量,变量值设置成你的镜像仓库地址,例如:
YAML
imageRepo: harbor.example.com:9000
当然,你需要保证需要的镜像都在你的镜像仓库中存在。
你可以下载镜像列表文件, 然后借助工具脚本来准备镜像。
Bash
curl -o harbor-images.txt https://raw.githubusercontent.com/devstream-io/devstream/main/docs/plugins/helm-installer/harbor/harbor-images.txt
curl -o image-pull-push.sh https://raw.githubusercontent.com/devstream-io/devstream/main/hack/image-pull-push.sh
chmod +x image-pull-push.sh
# 查看工具脚本的使用方法和注意事项等
./image-pull-push.sh -h
# 设置镜像仓库地址,按需修改
export IMAGE_REPO_ADDR=harbor.devstream.io
# 下载 harbor-images.txt 中所有镜像并保存到本地压缩包中
./image-pull-push.sh -f harbor-images.txt -r ${IMAGE_REPO_ADDR} -s
# 从压缩包中 load 镜像并 push 到私有镜像仓库(如果镜像仓库需要登录,则需要先手动执行 docker login)
./image-pull-push.sh -f harbor-images.txt -r ${IMAGE_REPO_ADDR} -l -u
如果你还没有一个私有镜像仓库,可以参考这篇文章快速部署一个 Docker Registry。
5.3、参考配置
这时候我们需要指定本地 helm chart 包以及私有镜像仓库的镜像,所以整体的参考配置大致如下:
YAML
---
# variable config
imageRepo: harbor.example.com:9000
---
# plugin config
tools:
- name: helm-installer
instanceID: harbor-001
dependsOn: [ ]
options:
chart:
chartPath: "~/devstream-test/harbor-1.10.0.tgz"
valuesYaml: |
externalURL: http://core.harbor.domain
expose:
type: ingress
tls:
enabled: false
ingress:
hosts:
core: core.harbor.domain
nginx:
image:
repository: [[ imageRepo ]]/goharbor/nginx-photon
tag: v2.5.3
portal:
image:
repository: [[ imageRepo ]]/goharbor/harbor-portal
tag: v2.5.3
core:
image:
repository: [[ imageRepo ]]/goharbor/harbor-core
tag: v2.5.3
jobservice:
image:
repository: [[ imageRepo ]]/goharbor/harbor-jobservice
tag: v2.5.3
registry:
registry:
image:
repository: [[ imageRepo ]]/goharbor/registry-photon
tag: v2.5.3
controller:
image:
repository: [[ imageRepo ]]/goharbor/harbor-registryctl
tag: v2.5.3
chartmuseum:
enabled: false
image:
repository: [[ imageRepo ]]/goharbor/chartmuseum-photon
tag: v2.5.3
trivy:
enabled: false
image:
repository: [[ imageRepo ]]/goharbor/trivy-adapter-photon
tag: v2.5.3
notary:
enabled: false
server:
image:
repository: [[ imageRepo ]]/goharbor/notary-server-photon
tag: v2.5.3
signer:
image:
repository: [[ imageRepo ]]/goharbor/notary-signer-photon
tag: v2.5.3
database:
internal:
image:
repository: [[ imageRepo ]]/goharbor/harbor-db
tag: v2.5.3
redis:
internal:
image:
repository: [[ imageRepo ]]/goharbor/redis-photon
tag: v2.5.3
exporter:
image:
repository: [[ imageRepo ]]/goharbor/harbor-exporter
tag: v2.5.3
persistence:
persistentVolumeClaim:
registry:
storageClass: "nfs"
accessMode: ReadWriteOnce
size: 5Gi
jobservice:
storageClass: "nfs"
accessMode: ReadWriteOnce
size: 1Gi
database:
storageClass: "nfs"
accessMode: ReadWriteOnce
size: 1Gi
redis:
storageClass: "nfs"
size: 1Gi
在这个参考配置里包含了全部可能用到的镜像,在部分组件不启用的情况下你完全可以移除相关的镜像配置项。不过保留在这里也不会有什么影响。