基于K3s搭建GitOps环境9-部署Harbor

一、核心定位

本文作为GitOps环境搭建系列的第九篇,聚焦容器镜像仓库Harbor的部署与配置。Harbor是由CNCF孵化而成的企业级开源Registry项目,提供完整的镜像存储、签名、扫描和分发能力。

在GitOps环境中,Harbor扮演”镜像存储中枢”角色,作为GitOps流程的镜像仓库环节,接收来自Tekton构建的镜像,并为ArgoCD提供可信的镜像源。Harbor与Gitea、Tekton、ArgoCD协同工作,形成完整的”代码→构建→镜像→部署”自动化流水线。

二、部署前置检查

部署前需验证K3s集群状态及前序组件运行情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 验证K3s集群状态
kubectl get nodes

# 2. 验证PostgreSQL运行状态
kubectl get pods -n postgres

# 3. 验证Redis运行状态
kubectl get pods -n redis

# 4. 验证Traefik运行状态
kubectl get pods -n kube-system -l app=traefik

# 5. 验证cert-manager运行状态
kubectl get pods -n cert-manager

# 6. 验证域名解析
nslookup harbor.example.io
nslookup notary.example.io

前置条件检查清单:

  • K3s集群运行正常
  • PostgreSQL数据库可用(用于存储Harbor元数据)
  • Redis缓存服务可用(用于Harbor会话缓存)
  • Traefik反向代理可用
  • cert-manager证书管理可用
  • 域名harbor.example.ionotary.example.io已解析至K3s节点IP
  • 已创建Harbor数据库和用户(如未创建,见下文步骤)

三、准备Harbor数据库

3.1 创建Harbor数据库

1
2
3
4
# 在PostgreSQL中创建Harbor数据库
kubectl exec -it postgres-0 -n postgres -- psql -U postgres -c "CREATE DATABASE harbor;"
kubectl exec -it postgres-0 -n postgres -- psql -U postgres -c "CREATE USER harbor WITH PASSWORD 'harbor123';"
kubectl exec -it postgres-0 -n postgres -- psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE harbor TO harbor;"

3.2 创建Harbor命名空间

1
2
# 创建Harbor专属命名空间
kubectl create namespace harbor

四、配置Harbor证书

4.1 创建Harbor证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# harbor-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: harbor-cert
namespace: harbor
spec:
secretName: harbor-tls-secret
issuerRef:
name: selfsigned-cluster-issuer
kind: ClusterIssuer
commonName: harbor.example.io
dnsNames:
- harbor.example.io
- notary.example.io
duration: 2160h
renewBefore: 360h
privateKey:
algorithm: RSA
size: 2048
usages:
- server auth

4.2 应用证书配置

1
2
3
4
5
6
# 应用证书配置
kubectl apply -f harbor-certificate.yaml

# 验证证书状态
kubectl get certificate -n harbor
kubectl describe certificate harbor-cert -n harbor

五、标准化部署Harbor

5.1 添加Helm仓库

1
2
3
# 添加Harbor官方Helm仓库
helm repo add harbor https://helm.goharbor.io --force-update
helm repo update

5.2 创建Harbor配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# harbor-values.yaml
# 参考:https://gitee.com/Chemmy/kube-template/blob/master/devops/Harbor/harbor-values.yaml

# 全局配置
expose:
type: ingress
tls:
enabled: true
certSource: secret
secret:
secretName: "harbor-tls-secret"
notarySecretName: "harbor-tls-secret"
ingress:
hosts:
core: harbor.example.io
notary: notary.example.io
annotations:
kubernetes.io/ingress.class: "traefik"
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/router.entrypoints: websecure

externalURL: https://harbor.example.io

# 管理员密码
harborAdminPassword: "HarborAdmin123!"

# 持久化存储
persistence:
enabled: true
resourcePolicy: "keep"
persistentVolumeClaim:
registry:
storageClass: "local-path"
accessMode: ReadWriteOnce
size: 100Gi
chartmuseum:
storageClass: "local-path"
accessMode: ReadWriteOnce
size: 5Gi
jobservice:
storageClass: "local-path"
accessMode: ReadWriteOnce
size: 5Gi
trivy:
storageClass: "local-path"
accessMode: ReadWriteOnce
size: 5Gi

# 外部数据库配置(使用前序部署的PostgreSQL)
database:
type: external
external:
host: "postgres.postgres.svc.cluster.local"
port: "5432"
username: "harbor"
password: "harbor123"
coreDatabase: "harbor"
clairDatabase: "harbor_clair"
notaryServerDatabase: "harbor_notary_server"
notarySignerDatabase: "harbor_notary_signer"

# 外部Redis配置
redis:
type: external
external:
addr: "redis.redis.svc.cluster.local:6379"
password: "redis123456"

# 启用ChartMuseum
chartmuseum:
enabled: true
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

# 启用Trivy漏洞扫描
trivy:
enabled: true
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"

# 资源限制
nginx:
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"

portal:
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"

core:
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

jobservice:
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

registry:
registry:
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

notary:
enabled: true
server:
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
signer:
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"

5.3 部署Harbor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 部署Harbor
helm install harbor harbor/harbor \
--namespace harbor \
--version 1.13.2 \
-f harbor-values.yaml

# 或使用在线配置
helm install harbor harbor/harbor \
--namespace harbor \
--version 1.13.2 \
--set expose.type=ingress \
--set expose.tls.enabled=true \
--set expose.tls.secret.secretName=harbor-tls-secret \
--set expose.ingress.hosts.core=harbor.example.io \
--set expose.ingress.hosts.notary=notary.example.io \
--set externalURL=https://harbor.example.io \
--set harborAdminPassword=HarborAdmin123! \
--set persistence.enabled=true \
--set persistence.persistentVolumeClaim.registry.storageClass=local-path \
--set persistence.persistentVolumeClaim.registry.size=100Gi \
--set database.type=external \
--set database.external.host=postgres.postgres.svc.cluster.local \
--set database.external.port=5432 \
--set database.external.username=harbor \
--set database.external.password=harbor123 \
--set redis.type=external \
--set redis.external.addr=redis.redis.svc.cluster.local:6379 \
--set redis.external.password=redis123456 \
--set chartmuseum.enabled=true \
--set trivy.enabled=true \
--set notary.enabled=true

六、验证部署结果

6.1 验证组件状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看Harbor Pod状态(首次启动需要2-3分钟)
kubectl get pods -n harbor -w

# 查看Service状态
kubectl get svc -n harbor

# 查看Ingress状态
kubectl get ingress -n harbor

# 查看证书状态
kubectl get certificate -n harbor

# 查看PVC状态
kubectl get pvc -n harbor

6.2 测试访问功能

1
2
3
4
5
6
7
8
9
10
11
# 等待Harbor完全启动
sleep 120

# 测试HTTPS访问
curl -k https://harbor.example.io

# 查看Harbor日志
kubectl logs -n harbor -l app=harbor --tail=50

# 测试Notary访问
curl -k https://notary.example.io

6.3 初始化Harbor

  1. 浏览器访问:https://harbor.example.io
  2. 用户名:admin
  3. 密码:HarborAdmin123!
  4. 首次登录后立即修改密码
  5. 创建项目(如gitops-demo

七、配置容器运行时访问

7.1 Docker客户端配置

1
2
3
4
5
6
7
8
9
10
# 获取Harbor CA证书
kubectl get secret harbor-tls-secret -n harbor -o jsonpath='{.data.ca\.crt}' | base64 --decode > harbor-ca.crt

# 配置Docker信任Harbor证书
sudo mkdir -p /etc/docker/certs.d/harbor.example.io
sudo cp harbor-ca.crt /etc/docker/certs.d/harbor.example.io/ca.crt
sudo systemctl restart docker

# 登录Harbor
docker login harbor.example.io -u admin -p HarborAdmin123!

7.2 Containerd配置(K3s节点)

1
2
3
4
# 配置Containerd信任Harbor证书
sudo mkdir -p /etc/containerd/certs.d/harbor.example.io
sudo cp harbor-ca.crt /etc/containerd/certs.d/harbor.example.io/ca.crt
sudo systemctl restart containerd

7.3 测试镜像推送

1
2
3
4
5
6
7
8
# 拉取测试镜像
docker pull alpine:latest

# 打标签
docker tag alpine:latest harbor.example.io/gitops-demo/alpine:latest

# 推送镜像
docker push harbor.example.io/gitops-demo/alpine:latest

八、Harbor与GitOps全链路集成

完成Harbor部署后,GitOps环境搭建系列已具备完整的端到端交付能力。以下阐述Harbor与系列中其他核心组件的联动关系。

8.1 全链路组件矩阵

组件 职责 在GitOps链路中的角色
Gitea 代码托管与版本控制 开发者推送代码的单一可信数据源
Tekton 云原生CI/CD流水线引擎 监听代码变更,构建镜像并推送至Harbor
Harbor 容器镜像仓库与安全扫描 存储签名镜像,提供可信镜像分发
ArgoCD GitOps持续部署控制平面 监听Harbor镜像Tag变化,自动同步至K3s

8.2 完整流水线联动流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌─────────────────────────────────────────────────────────────────┐
│ GitOps 完整链路 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ① 开发者 ──push──▶ Gitea (Git仓库) │
│ │
│ ② Tekton Pipeline ──webhook触发──▶ 克隆代码 │
│ │
│ ③ Tekton Pipeline ──构建──▶ Docker镜像 │
│ │ │
│ │ docker push │
│ ▼ │
│ ④ Harbor (镜像仓库) ◀── 推送镜像 │
│ │ • 存储镜像bits │
│ │ • Trivy CVE扫描 │
│ │ • Notary镜像签名(可选) │
│ │
│ ⑤ ArgoCD Application ──监听镜像Tag变化──▶ 检测到新镜像 │
│ │ │
│ │ 更新Deployment镜像Tag │
│ ▼ │
│ ⑥ K3s集群 ──kubectl apply──▶ 新Pod以新镜像启动 │
│ │
└─────────────────────────────────────────────────────────────────┘

8.3 Harbor与Gitea、Tekton、ArgoCD联动

Harbor + Gitea(认证集成)

Harbor与Gitea可共用外部认证源(如LDAP/OIDC),实现统一身份管理:

1
2
3
4
5
6
7
8
# Harbor配置外部认证
harbor-core:
config:
auth_mode: "oidc_auth"
oidc_name: "Gitea OIDC"
oidc_endpoint: "https://gitea.example.io/oauth"
oidc_client_id: "harbor-client"
oidc_secret: "harbor-oidc-secret"

Harbor + Tekton(镜像构建推送)

Tekton Pipeline执行构建并将镜像推送至Harbor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Tekton Task: 构建并推送镜像至Harbor
spec:
steps:
- name: git-clone
image: alpine/git
script: |
git clone $(params.REPO_URL) $(params.REPO_DIR)

- name: build-image
image: docker:latest
script: |
docker build -t $(params.IMAGE):$(params.TAG) $(params.REPO_DIR)
docker tag $(params.IMAGE):$(params.TAG) \
harbor.example.io/gitops-demo/$(params.IMAGE):$(params.TAG)

- name: push-image
image: docker:latest
script: |
docker login harbor.example.io -u $(params.HARBOR_USER) -p $(params.HARBOR_PASS)
docker push harbor.example.io/gitops-demo/$(params.IMAGE):$(params.TAG)

Harbor + ArgoCD(镜像驱动部署)

ArgoCD Image Updater自动轮询Harbor镜像仓库,发现新Tag后更新配置并触发同步:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ArgoCD Application(Image Updater注解)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gitops-demo-app
annotations:
argocd-image-updater.argoproj.io/image-list: |
demo=harbor.example.io/gitops-demo/demo-image
argocd-image-updater.argoproj.io/demo.update-strategy: latest
argocd-image-updater.argoproj.io/write-back-method: git
spec:
source:
repoURL: https://github.com/your-org/gitops-configs.git
path: apps/demo-app
targetRevision: main
destination:
server: https://kubernetes.default.svc
namespace: gitops-demo

8.4 安全集成要点

  1. 镜像签名链路:Tekton构建 → Notary签名 → 推送Harbor → 部署时验签,全链路可信;
  2. 漏洞扫描卡点:Harbor Trivy扫描未通过的CVE镜像(Critical/High),可配置阻止推送;
  3. 认证打通:Harbor与Gitea共用OIDC认证,实现统一身份管理;
  4. 网络隔离:Harbor仅通过Traefik 443端口暴露,K3s节点通过内部网络拉取镜像。

九、服务访问方式

9.1 集群内访问

  • Web界面harbor-core.harbor.svc.cluster.local
  • Registry APIharbor-registry.harbor.svc.cluster.local
  • ChartMuseumharbor-chartmuseum.harbor.svc.cluster.local

9.2 集群外访问

  • Web界面https://harbor.example.io
  • Registryharbor.example.io(Docker push/pull)
  • Notaryhttps://notary.example.io

十、日常运维命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看Harbor状态
kubectl get all -n harbor

# 查看日志
kubectl logs -f deployment/harbor-core -n harbor
kubectl logs -f deployment/harbor-registry -n harbor
kubectl logs -f deployment/harbor-portal -n harbor

# 重启组件
kubectl rollout restart deployment -n harbor

# 备份Harbor数据
kubectl exec -n harbor $(kubectl get pod -n harbor -l component=registry -o jsonpath='{.items[0].metadata.name}') -- tar czf /tmp/registry-backup.tar.gz /storage
kubectl cp harbor/$(kubectl get pod -n harbor -l component=registry -o jsonpath='{.items[0].metadata.name}'):/tmp/registry-backup.tar.gz ./registry-backup-$(date +%Y%m%d).tar.gz

# 查看资源使用情况
kubectl top pods -n harbor

十一、常见问题修复

问题现象 排查方向 修复方案
Harbor无法启动 数据库连接失败 检查PostgreSQL连接,验证用户名密码
证书错误 证书配置/Secret 检查cert-manager状态,验证证书Secret
镜像推送失败 认证/网络 检查Docker登录状态,验证网络连通性
PVC无法绑定 存储类/磁盘空间 检查local-path存储类,确认节点有足够空间
Trivy扫描失败 网络/资源 检查Trivy网络连通性,增加资源限制
Notary服务异常 证书/配置 检查Notary证书配置,验证域名解析

十二、配置参考

所有Harbor配置文件和部署脚本请参考:
https://gitee.com/Chemmy/kube-template/tree/master/devops/Harbor

该目录包含:

  • Harbor Helm values配置
  • 证书和路由配置
  • 数据库初始化脚本
  • 生产环境优化配置
  • 集成配置示例

总结

本文完成了Harbor在K3s集群中的标准化部署,实现了镜像仓库的完整功能。Harbor作为GitOps环境的镜像存储中枢,为自动化构建和部署提供了可靠的镜像管理能力。

部署完成后,建议配置Tekton Pipeline实现自动镜像构建和推送,配置ArgoCD实现镜像更新自动同步。至此,GitOps环境的所有核心组件已部署完成,形成了完整的自动化流水线。