基本信息

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-nginx4.12.3能够正常使用,nginx规则文件可用
helm3.18.1版本不能低于3.2.0否则安装失败

官方资料

约定

本次安装和配置使用的命名空间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)}'
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐