1. 系统架构
本文档参照Harbor官方高可用方案说明,并且在Kubernetes集群通过helm来部署Harbor。
Harbor的大部分组件都是无状态的应用,针对该种应用比如portal、core、nginx等只需要增加其相应的副本数量即可;在存储数据层面,需要提供高可用的Postgresql、redis集群,另外针对镜像和chart服务也需要提供可持久的存储(PVCs)。鉴于以上理论,也就有了下面的Harbor高可用方案设计架构图(摘自Harbor官网):

既然有了上图的系统架构设计,下面开始着手具体部署实现。
另外本文只针对Harbor高可用的设置,其它部署可参照Harbor NFS部署和Harbor CEPH部署。
2. 部署准备
2.1 Chart下载
首先下载Harbor Chart,参照如下操作即可:
# 添加helm harbor repo
helm repo add harbor https://helm.goharbor.io
# 下载chart
helm fetch harbor/harbor
# 解压缩
tar xvf harbor-xxx.tgz
cd harbor-xxx/
2.2 Harbor参数配置
跟常规部署一样,需要修改values.yaml文件:
- 修改
Harbor对外暴露方式:上面提到的两篇博文都是采用NodePort的暴露方式,此处建议采用Ingress,于是需要修改expose.ingress.hosts.core和expose.ingress.hosts.notary这两个字段; - 修改
externalURL:修改成expose.ingress.hosts.core字段的值,但是前面加上协议名称; - 修改
PostgreSQL配置:首先修改database.type-->external,然后填入database.external区域的信息比如访问数据库的host、端口、用户名、密码等信息,另外注意:这一块需要提前手动创建四个空数据库:harbor Core、Clair、Notary Server、Notary Signer,然后把数据库的名称填入database.external区域,至于PostgreSQL高可用部署下文会进一步说明; - 修改
Redis配置:首先修改redis.type-->external,然后填入redis.external区域信息,高可用方案下如果采用Redis Sentinal方案,因为Harbor上游项目代码的Redis客户端不支持Sentinal,所以可以考虑使用HAProxy; - 持久化存储配置:这一块配置跟上面所提两篇博文一致,预先安装对应存储的驱动插件,然后依据
StorageClass创建PVC进而提供PV方式; - 修改其他组件的副本数量:修改
portal.replicas,core.replicas,jobservice.replicas,registry.replicas,chartmuseum.replicas,clair.replicas,notary.server.replicasandnotary.signer.replicaston(n>=2,这里是3);
配置完之后,我们需要在环境中搭建高可用的数据库:PostgreSQL和Redis。
3. PostgreSQL高可用部署
这里采用stolon来部署PostgreSQL的高可用,具体部署步骤可参考Stolon Inside Kubernetes。
一点说明:尽管helm hub上已经有stolon的chart,但是不建议使用,本人曾经尝试安装过几次,不过最后都没安装成功,原因未知。
3.1 下载源码和镜像
按如下操作即可:
# 源码下载
git clone https://github.com/sorintlab/stolon.git
# stolon镜像下载
docker pull sorintlab/stolon:v0.13.0-pg10 # 网上随便找的一个镜像,实际使用时可根据需求修改stolon/examples/kubernetes/image/docker/Dockerfile实现订制
# 进入kubernetes部署目录
cd stolon/examples/kubernetes/
3.2 初始化Stolon集群
# stolon集群初始化
kubectl run -i -t stolonctl --image=sorintlab/stolon:v0.13.0-pg10 --restart=Never --rm -- /usr/local/bin/stolonctl --cluster-name=kube-stolon --store-backend=kubernetes --kube-resource-kind=configmap init
3.3 参数配置
进入stolon kubernetes安装目录后,可以看到:

需要修改出不多:
镜像信息:此处均修改成
sorintlab/stolon:v0.13.0-pg10postgres数据库用户名称:默认stolon,可通过修改stolon-keeper.yaml文件配置项完成设置;postgres数据库访问密码:默认password1,在secret.yaml中存储,如需更改先base64转码后存入;-
后端存储配置:HA方案采用共享存储,这里提前预先安装ceph驱动插件只需在
stolon-keeper.yaml文件做如下图示修改即可,至于容量大小按需填入;
副本数量:按需填入,这里默认都是
2;
3.4 组件安装
下面开始stolon各个组件的部署,依次执行如下命令即可:
kubectl create -f stolon-sentinel.yaml
kubectl create -f secret.yaml
kubectl create -f stolon-keeper.yaml
kubectl create -f stolon-proxy.yaml
kubectl create -f stolon-proxy-service.yaml
# RBAC相关
kubectl create -f role.yaml
kubectl create -f role-binding.yaml
如果出现如下结果说明安装基本上是ok了:

并且我们还可以通过kubectl logs命令发现两个keeper一个是master,而另一个则是standby。
3.5 Harbor高可用准备及相关参数调整
3.5.1 Harbor参数调整
更新values.yaml文件:
- database.external.host -->
stolon-proxy-service即stolon-proxy的service名称; - database.external.username -->
stolon; - database.external.password -->
password1;
3.5.2 Postgres初始化
如上面所说,需要预先在Postgres集群中创建几个空数据库,可借助kubernetes Job来完成。因为Postgres客户端命令行工具支持以文件传入SQL命令的方式,所以只需把创建数据库的命令放入几个文件里,然后通过一个脚本调用它们即可:
-
几个sql命令文件:
初始化脚本程序
#!/bin/bash
# postgresql.sh
host="stolon-proxy-service"
user="stolon"
db="postgres"
export PGPASSWORD="password1"
args=(
# force postgres to not use the local unix socket (test "external" connectibility)
--host "$host"
--username "$user"
--dbname "$db"
--quiet --no-align --tuples-only
)
if select="$(echo 'SELECT 1' | psql "${args[@]}")" && [ "$select" = '1' ]; then
psql -h stolon-proxy-service -p 5432 postgres -U stolon -f "/docker-entrypoint-initdb.d/notary_server.sql"
psql -h stolon-proxy-service -p 5432 postgres -U stolon -f "/docker-entrypoint-initdb.d/notary_signer.sql"
psql -h stolon-proxy-service -p 5432 postgres -U stolon -f "/docker-entrypoint-initdb.d/registry.sql"
psql -h stolon-proxy-service -p 5432 postgres -U stolon -f "/docker-entrypoint-initdb.d/clair.sql"
exit 0
fi
exit 1
- Job yaml文件
apiVersion: batch/v1
kind: Job
metadata:
name: stolon-init-database-job
spec:
template:
spec:
containers:
- name: stolon-proxy
image: sorintlab/stolon:master-pg10
command:
- "/bin/bash"
- "/docker-entrypoint-initdb.d/postgresql.sh"
volumeMounts:
- mountPath: /docker-entrypoint-initdb.d
name: database
restartPolicy: OnFailure #失败重启
volumes:
- name: database
hostPath:
path: /postgres_init # 把之前准备的sql和脚本文件放置这个目录下,总共5个文件
activeDeadlineSeconds: 600 #10分钟没有complete,不再重启并移除Pod
把之前准备好的sql和脚本文件拷贝到各个worker节点/postgres_init目录,然后再master执行批处理任务即可完成数据库的初始化工作,另外如果新建的数据库名称跟Harbor配置不一致注意更新,自此Postgresql高可用部署完成。
4. Redis高可用部署
4.1 Redis哨兵集群部署
直接采用helm部署即可,操作步骤可参考如下:
helm repo add stable `https://kubernetes-charts.storage.googleapis.com`
helm fetch stable/redis-ha
tar xvf redis-ha-xxx.tgz
cd redis-ha
修改values.yaml配置,修改的地方不多绝大部分默认配置即可,其中有两处额外注意一下:
-
redis访问密码设置:需提前创建一个包含redis密码信息的secret对象,比如下面这样:
然后通过kubectl create -f redis-secret.yaml创建,之后根据secret修改values.yaml对应认证信息即可:

(secret中密码都是base64转码之后的结果,此处redis密码: password1 )
注意:如果如本文redis集群最终要对接Harbor且采用haproxy tcp-check来侦测redis master,此处auth--> false。
- 持久化存储设置:因为数据库后端采用共享存储,设置好
PVC对应的storageclass和容量即可:
然后开始部署:
# 可以先检查下配置
helm install --name vienfu-redis-cluster . --debug --dry-run
# 正式安装
helm install --name vienfu-redis-cluster .
等待一会而便会发现部署完成了:

下面做个简单测试:

4.2 Harbor高可用准备及参数调整
一般情况下只需访问redis-sentinal集群服务便可对redis master进行读写,但是由于Harbor内置的redis客户端不支持sentinal,所以直接访问redis sentinal服务是不行的。那么直接访问redis-server的服务可以吗?答案也是不可以的,因为这有可能后端最终访问到redis slave节点,而slave节点却是只读的,所以需要其他一种方式能够从redis server节点中找出redis master,根据官方文档提示采用HAProxy,仔细查阅下HAProxy配置官方文档,可以通过haproxy tcp-check功能来锁定redis master,如下是可参照的haproxy配置:
# cat /etc/haproxy/conf.d/redis-ha.conf
frontend ft_redis
bind 10.0.2.15:6379 name redis
default_backend bk_redis
backend bk_redis
option tcp-check
tcp-check connect
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send info\ replication\r\n
tcp-check expect string role:master
tcp-check send QUIT\r\n
tcp-check expect string +OK
server R1 vienfu-redis-cluster-redis-ha-server-0.vienfu-redis-cluster-redis-ha.default.svc.cluster.local:6379 check inter 1s
server R2 vienfu-redis-cluster-redis-ha-server-1.vienfu-redis-cluster-redis-ha.default.svc.cluster.local:6379 check inter 1s
server R3 vienfu-redis-cluster-redis-ha-server-2.vienfu-redis-cluster-redis-ha.default.svc.cluster.local:6379 check inter 1s
然后设置开机启动及重启haproxy服务即可。
注意此处也要对应调整Harbor参数配置:redis.external.host --> 上面haproxy配置的VIP:10.0.2.15。
4.2.1 其它说明
此外如你所见,此处haproxy后端server的配置采用的是对应server的域名,直接使用POD IP也可以,不过考虑到该IP可能会变,所以建议使用域名来配置haproxy后端server,那么节点如何解析kubernetes的service域名呢?对,通过CoreDNS。
首先,需要获取CoreDNS对应的nameserver,很简单执行如下命令即可:
kubectl get svc -n kube-system

把结果dns服务对应的CLUSTER-IP作为新增的nameserver配置到节点的域名配置文件中(一般默认/etc/resolve.conf),不过高版本的linux一般都是通过systemd来控制域名解析服务,如果直接修改该文件重启域名服务是不生效的,在此提供一个简单方法直接关掉systemd-resolved并且禁用开机启动,这样直接修改/etc/resolve.conf就立刻生效了,其他方法可参照Ubuntu 18.04修改DNS。
之后,重启haproxy服务的过程中可能会碰到如下问题:提示后端server的域名无法解析,但是执行nslookup命令却能解析域名,发现也ping不通,最终参照网上nslookup works but can not ping问题最终得以解决。
5. Harbor HA安装
按照如下命令执行完成最终部署:
# 可先检查下Harbor配置
helm install --name harbor-ha . --debug --dry-run
# 正式部署
helm install --name harbor-ha .



