k8环境harbor安装配置
基本信息
harbor是什么
在docker使用环境中,多数情况,我们需要使用docker-registry构建私有的存储库,存储业务镜像,缓存官方镜像以实现加速访问等操作,但该仓库均有很大的局限性,例如镜像管理需要使用RESTful API进行管理,使用难度大,需要重复工作。在企业大规模使用docker等容器化部署应用,迫切需要易用、受控的镜像管理工具,需要支撑多部门、多仓库的管理工具。Harbor应用而生。
Harbor 是一个用于存储和分发容器镜像的企业级 Registry 服务,支持镜像的访问控制、漏洞扫描、镜像签名、复制同步等功能。它可以帮助团队安全、高效地管理和共享容器镜像,常用于搭建私有镜像仓库,提升 DevOps 流程中的镜像管理能力。
解决问题
描述使用helm方式安装harbor。
记录安装过程遇到的问题和处理思路、处理步骤和建议。
使用前提
本文的使用环境应当满足以下约束。
- kubernetes集群。本文采用一主多从的小型集群模式。节点配置8GB+4CPU,配置过低会造成非预期错误,参照#遇到问题章节。
- NFS服务器。
- 【非必须,但推荐】k8s已经配置ingress-nginx,配置更为方便和灵活。
- 安装配置helm,版本3.2.0+。
环境版本参考
| 环境名称 | 版本 | 备注 |
|---|---|---|
| 操作系统 | Rocky Linux 9.5 | - |
| k8s集群 | v1.33.1 | 一主多从小型集群 |
| nfs-server | - | 非必须 |
| ingress-nginx | 4.12.3 | 能够正常使用,nginx规则文件可用 |
| helm | 3.18.1 | 版本不能低于3.2.0否则安装失败 |
官方资料
- kubernetes的storage-classes
https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes - k8s外部nfs驱动
https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner - harbor官网
https://goharbor.io/ - harbor-helm
harbor-helm
约定
本次安装和配置使用的命名空间harbor,本文描述中的过程或者命令使用该变名称。无特殊情况,不建议使用其他命令空间,因为涉及的过程、命令及相关文件太多了。
源码资源
本文所使用的资料通过以下方式提供共享。
https://gitee.com/huangdaozhu/helm-harbor-resource
安装准备
本章节描述安装前的准备工作。
创建命名空间
按照本文约定,命名空间名称使用harbor。
kubectl create ns harbor
rbac授权
创建命名空间后,需要创建serviceAccout并进行授权.
完整脚本参照#源码资源harbor-rbac.yaml。
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: harbor
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
namespace: harbor
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: harbor
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: harbor
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: harbor
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: harbor
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
持久化配置
本次安装采用helm方式。持久化存储使用动态provisioner和静态provisioner。动态既指定StorageClass,由k8s集群进行动态PV、PVC和PV-PVC绑定等工作,需要部署provisioner支持服务和定义StorageClass;静态既手动完成PV、PVC。
在实验过程中,数据库部分(database,postgres数据)再不同的运行环境出现了一些非预期错误,故本方案的database部分使用静态provisioner方式。
具体的操作步骤如下所示。
nfs共享目录
本章节操作在nfs服务器中执行。
完整脚本参照#源码资源包中nfs-config.sh,需要添加执行权限、修改目录信息后运行。
# 创建存储目录
mkdir -p /nfs_share/k8s/harbor/database/pv{1..3}
# 设置权限
# 为什么使用 999:999?因为 Harbor-database 的容器通常以 UID 999 运行。
chown -R 999:999 /nfs_share/k8s/harbor/database/pv{1..3}
# 添加共享目录信息
echo "/nfs_share/k8s/harbor *(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports
echo "/nfs_share/k8s/harbor/database/pv1 *(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports
echo "/nfs_share/k8s/harbor/database/pv2 *(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports
echo "/nfs_share/k8s/harbor/database/pv3 *(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports
# 解析/etc/exports文件
exportfs -r
# 查看共享目录信息
exportfs -v | grep /nfs_share/k8s/harbor
创建集群外部nfs驱动
由于本文使用的nfs非k8s集群内部服务,需要安装配置外部nfs驱动(nfs-subdir-external-provisioner),用于动态提供基于nfs的数据卷(PV)。
参考文档。
https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
https://github.com/kubernetes-csi/csi-driver-nfs/tree/master/deploy/example
完整脚本参照#资源包中nfs-provisioner-deployment.yaml。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
namespace: harbor
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
# image: registry.k8s.io/sig-storage-nfs-subdir-external-provisioner:v4.0.2
# image: registry.cn-hangzhou.aliyuncs.com/google_containers/nfs-subdir-external-provisioner:v4.0.2
# image: docker.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
# image: nfs-subdir-external-provisioner:v4.0.2
# 参考https://www.modb.pro/db/550373 文中的镜像
image: registry.cn-beijing.aliyuncs.com/mydlq/nfs-subdir-external-provisioner:v4.0.0
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
# 用户StorageClass配置,必须保持一致。
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER
value: 192.168.0.100
- name: NFS_PATH
value: /nfs_share/k8s/harbor
volumes:
- name: nfs-client-root
nfs:
server: 192.168.0.100
path: /nfs_share/k8s/harbor
创建StorageClass并设置默认
检查集群中是否存在默认StorageClass
kubectl get storageclass
如果输入结果的Name列,无default标注则表示当前集群中无默认StorageClass。需要注意StorageClass定义中必须指定storageclass.kubernetes.io/is-default-class并设置为“true”;如果已经存在,则需要注释掉改行注释。
*重要配置说明
- 上述默认StorageClass问题。
- 动态目录规则:
parameters.pathPattern,详见官方文档。本说明按照pvc命名空间/pvc名称。- pvc命名空间,取决于harbor使用的命名空间。
- pvc名称包括动态和静态两种方式。动态由harbor-helm规则生成;静态由我们自行指定。
完整脚本参照#资源包中harbor-sc.yaml。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-sc-harbor
namespace: harbor
annotations:
# 如果集群中,无默认StorageClass,必须有此注解,否则会出现问题,参考#遇到的问题。
storageclass.kubernetes.io/is-default-class: "true"
# 必须与 Deployment nfs-client-provisioner中的定义一致。
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
archiveOnDelete: "false"
pathPattern: "/${.PVC.namespace}/${.PVC.name}"
reclaimPolicy: Retain
为数据库模块创建静态PV和PVC
相比动态方式,能够有效降低错误发生。
完整脚本参照#源码资源包中harbor-database-svc.yaml。
apiVersion: v1
kind: PersistentVolume
metadata:
name: harbor-database-pv-1
namespace: harbor
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: harbor-database-nfs
nfs:
path: /nfs_share/k8s/harbor/database/pv1
server: 192.168.0.100
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: harbor-database-pv-2
namespace: harbor
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: harbor-database-nfs
nfs:
path: /nfs_share/k8s/harbor/database/pv2
server: 192.168.0.100
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: harbor-database-pv-3
namespace: harbor
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: harbor-database-nfs
nfs:
path: /nfs_share/k8s/harbor/database/pv3
server: 192.168.0.100
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: harbor-database-pvc
namespace: harbor
spec:
storageClassName: harbor-database-nfs
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
至此准备工作基本完成,下面开始执行安装。
安装过程
harbor-heml执行命令或者修改values.yaml文件方式指定参数,由于试验过程中可能会遇到命令无法处理的问题,故障采用编辑values.yaml方式执行。
添加harbor-helm仓库
helm repo add harbor https://helm.goharbor.io
查看仓库列表
查看命令
helm repo list
参考结果。
NAME URL
...
harbor https://helm.goharbor.io
...
更新仓库
查看命令
helm repo update
失败可能性较低,通常网络问题。
检索harbor仓库(获取版本信息)
查看命令
helm search repo harbor
参考结果:
NAME CHART VERSION APP VERSION DESCRIPTION
harbor/harbor 1.17.1 2.13.1 An open source trusted cloud native registry th..
NAME将用于后续命令。
CHART VERSION将用于后续命令。
拉取harbor的helm配置
为了统一配置和便于后续排错,使用登录用户home目录。
mkdir -p ~/harbor
cd ~/harbor
#heml pull {NAME} --version {CHART VERSION}
helm pull harbor/harbor --version 1.17.1
执行成功后,在~/harbor目录中将下载harbor-{CHART VERSION}.tgz文件。
解压配置包
tar zxvf harbor-1.17.1.tgz
# 配置一份原始文件,用户配置错乱时恢复使用
cp harbor/values.yaml harbor/values.yaml.backup
修改harbor部署配置
安装yaml工具
为了便于修改yaml格式的helm配置文件,安装yq工具通过命令方式修改yaml配置文件,形成shell脚本,便于自动化部署。
该工具在修改yaml优于sed工具,缺点需要下载安装。
wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq &&\
chmod +x /usr/local/bin/yq
执行配置修改
配置说明
- 修改ingress方式访问绑定域名。在公网设备上,需要配置dns;如果局域网,需要配置域名、名称解析或者修改hosts实现域名和IP的映射。
- 为模块持久化指定
storageClass或者existingClaim。
# 修改ingress绑定域名
yq eval '(.expose.ingress.hosts.core)="harbor.k8s.local"' -i harbor/values.yaml
# 代理访问地址
yq eval '(.externalURL)="https://harbor.k8s.local"' -i harbor/values.yaml
# 使用已经存在的pvc,既静态pvc
yq eval '(.persistence.persistentVolumeClaim.database.existingClaim)="harbor-database-pvc"' -i harbor/values.yaml
# 指定storageClass,既动态pvc
yq eval '(.persistence.persistentVolumeClaim.registry.storageClass)="nfs-sc-harbor"' -i harbor/values.yaml
yq eval '(.persistence.persistentVolumeClaim.jobservice.jobLog.storageClass)="nfs-sc-harbor"' -i harbor/values.yaml
yq eval '(.persistence.persistentVolumeClaim.redis.storageClass)="nfs-sc-harbor"' -i harbor/values.yaml
yq eval '(.persistence.persistentVolumeClaim.trivy.storageClass)="nfs-sc-harbor"' -i harbor/values.yaml
执行安装
安装命令
#helm install 版本名 目录名 -n 命名空间
helm install harbor harbor -n harbor
命令说明
- 版本名,用于使用helm安装、卸载、查看状态等使用,该名称用于新增pod示例的命名。例如
- 版本名-core : 核心模块
- 版本名-database: 数据模块
- 其他模块。
- 目录名指防止修改后的helm配置values.yaml的父目录,本文相对于
~/harbor目录。
确认pods状态
kubectl get pods -n harbor
可能需要几分钟才能完成所有pods的启动。
访问harbor
使用指定的域名通过浏览器访问。
https://harbor.k8s.local/
账号信息
username: admin
# 密码在~/harbor/harbor/values.yaml中harborAdminPassword指定。
password: Harbor12345
使用方法
都是图形界面,自行研究。
遇到问题
动态pvc状态全部pending状态
检查Deployment/nfs-client-provisioner运行状态是否正常。
检查Deployment/nfs-client-provisioner的describe和logs信息。
通过edit命令查看集群中的pvc的配置,重点关注namespace和storageClass信息,如果无storageClass信息,大概率因为集群中无默认StorageClass配置,参考本文相关章节配置。
联合pvc,pv,sc综合排查。
harbor-database模块logs中权限问题
故在nfs共享目录创价时,指定文件及文件夹的的拥有者为999。
helm安装Harbor后,核心模块和jobService无法启动。
在不同环境中,helm install 后core、jobService无法启动,或者反复重启,harbor界面可访问,但提示核心模块不可用。
core模块日志错误示例
Failed to upgrade schema
2025-06-26T07:46:30Z [INFO] [/common/dao/pgsql.go:135]: Upgrading schema for pgsql ...
2025-06-26T07:46:30Z [ERROR] [/common/dao/pgsql.go:140]: Failed to upgrade schema, error: "Dirty database version 3. Fix and force version."
2025-06-26T07:46:30Z [FATAL] [/core/main.go:204]: failed to migrate the database, error: Dirty database version 3. Fix and force version.
问题分析
经分析,harbor模块(部分)依赖如下所示。
portal -> core -> database #由于database异常(无法启动或者超时),导致core无法启动,随即jobservice异常,导致系统异常。
portal -> core -> redis #由于redis出现概率低,不详述
当database(postgres数据库)不能快速完成初始化,未通过readinessProbe检测自动重启。但此时core核心模块pod正在执行harbor数据库初始化,导致数据库命令执行异常,出现Dirty database version等错误。
温馨提示:经过验证,此问题可能与服务器性能有关,在高性能集群中未复现此问题。
处理思路
1、给core模块添加initContainer
initContainer增加延时或者对目标database的检测。
- 延时。利用busybox,增加睡眠,等待database启动后启动core的容器。
- 缺点:不能根据实际database启动状态自动调整。
- 优点:启动简单。
- 对database的检测。通过一些工具对目标数据的状态进行检测,带数据库稳定时,退出initContainer,启动普通Container。我们通过自行编写的小工具,连接目标数据库,并且设置阈值,达到阈值认为数据库状态正常,二进制文件不便共享。
- 缺点:实现复杂。
- 优点:能够最小化等待时间。
2、修改database模块(StatefulSet)的readinessProbe延迟检测时间initialDelaySeconds,默认为1,太小了。
修改步骤
延时启动core模块
yq eval '.portal.initContainers[0] = {"name":"wait","image":"busybox"}' -i harbor/values.yaml
# 根据不同环境尝试修改,可以由小到大
yq eval '.portal.initContainers[0].command=["sh","-c", "sleep 600"]' -i harbor/values.yaml
修改database模块readinessProbe
修改文件value.yaml的同级目录/templates/database/database-ss.yaml文件。
由于该文件非标准的模板文件,无法使用sed、yq等工具。
spec.template.spec.container[database].readinessProbe.initialDelaySeconds修改为300。
该值需要调整,尽量最小化。
修改参数
该问题主要出现再数据初始化情况下,再系统运行正常后,可以通过kubectl edit命令修改相关配置,以便能够快速重启、扩容和迁移等操作。
本文不在橡树。
辅助命令
如果过程出错需要,需要重复安装和卸载可以使用以下命令集合便于快速处理。
安装
k delete -f harbor-database-pvc.yaml
k create -f harbor-database-pvc.yaml
k delete -f harbor-database-svc.yaml
k create -f harbor-database-svc.yaml
helm install harbor harbor -n harbor
卸载
helm uninstall harbor -n harbor
kget pvc -n harbor | awk -F ' ' 'NR>1 {system("kubectl delete -n harbor pvc "$1)}'
rm -rf /nfs_share/k8s/harbor/harbor
rm -rf /nfs_share/k8s/harbor/database/pv1/*
rm -rf /nfs_share/k8s/harbor/database/pv2/*
rm -rf /nfs_share/k8s/harbor/database/pv3/*
删除Released状态的pv
kget pv | grep Released | awk -F ' ' '{system("kubectl delete pv "$1)}'
更多推荐

所有评论(0)