本次带来的分享是在TKE集群上搭建harbor私有仓库,另外推荐腾讯云的容器镜像服务TCR
TCR具备以下特性:
安全管理:支持 Docker 镜像、Helm Chart 存储分发及镜像安全扫描,并为企业级客户提供了细颗粒度的访问权限管理和网络访问控制。
业务拓展:支持镜像全球同步及触发器,可满足企业级客户拓展全球业务使用容器 CI/CD 工作流的需求。
极速部署:支持具有上千节点的大规模容器集群并发拉取 GB 级大镜像,可保障容器业务的极速部署。
Harbor是由VMware公司开源的企业级的Docker Registry管理项目,可以理解为harbor是Docker Registry的更高级的版本,除了提供友好的可视化界面,还能基于角色、用户进行访问控制、镜像漏洞扫描、行为审计、与企业LDAP集成等等。相比起Docker Hub、Registry提供的简单存储功能,Harbor的出现可以说是解决企业级别对于镜像仓库的功能需求。
这里还有个有趣的说法,harbor是港湾的意思,把容器比喻成集装箱,集装箱放在港湾,生动又形象。
那么废话不多说直接进入主题
Harbor其核心的组件有:
Job Service: 一种通用执行队列服务,允许其他组件/服务通过简单的静态API同时提交运行异步任务的请求
Logs: Log collector, 日志收集器,负责将其他模块的日志收集到一个地方。
GC Controller: 管理在线GC计划设置,并启动和跟踪GC进度。
Chart Museum: 提供chart管理和访问API的chart存储服务器,即helm存储。
Docker Registry: 第三方注册表服务器,负责存储Docker镜像并处理Docker推/拉命令。由于Harbor需要强制执行对图像的访问控制,因此注册表将引导客户端使用令牌服务,以便为每个请求请求提供有效的令牌。
Notary: 第三方内容信任服务器,负责安全地发布和验证内容
Web Portal: 图形化用户界面,可帮助用户管理注册表上的图像
Architecture-Overview-of-Harbor
Kubernetes集群 1.10+(本次教程用的集群为1.16.3)
helm 2.8.0+ (本次教程用的helm为2.10.0)
Ingress(nginx-ingress chart:1.20.0)
storageclass (配置存储)
通过TKE创建一个1.16版本的托管集群,这里不赘述了,在平台上点几下即可创建集群
托管集群意思是master、etcd由腾讯云提供,只需购买node即可,无需运维master层面,省事、便捷。
通过tke的控制面板安装helm,点击申请开通后会自动安装helm2,下发tiller、swift至集群中
这里还需额外配置 helm client 客户端,后续拉取Harbor Chart 会用到
登录节点配置即可
#下载helm 客户端
curl -O https://storage.googleapis.com/kubernetes-helm/helm-v2.10.0-linux-amd64.tar.gz
tar xzvf helm-v2.10.0-linux-amd64.tar.gz
sudo cp linux-amd64/helm /usr/local/bin/helm
#执行以下命令,将 Helm 配置为 Client-only。
helm init --client-only
下载过程中因网速等不可控问题,可能会导致下载helm-v2.10.0-linux-amd64.tar.gz缓慢
这里使用nginx-ingress,使用helm 一键安装,过程就略过了~
为什么使用nginx-ingress,而不使用tke-ingress,因为在使用tke-ingress配置ingress tls时,还需在腾讯云先导入证书,并且tke-ingress不支持service的类型为clusterip,在后续的配置中需要改动很多,这里为了方便读者理解,暂时先将部署过程简单化。
添加harbor的helm库
$ helm repo add harbor https://helm.goharbor.io
"harbor" has been added to your repositories
$ helm fetch harbor/harbor
$ tar -xvf harbor-1.3.1.tgz
harbor/Chart.yaml
harbor/values.yaml
harbor/templates/NOTES.txt
...
$ ls harbor/
cert Chart.yaml conf LICENSE README.md templates values.yaml
helm harbor会提供一个默认的value,里面申明了各项配置,如secret、externalURL、pvc、imagetag等等。
无需过多的修改,这里我们关心核心的几个参数,其余参数可在配置列表configuration一一对应
配置对应域名,并且需要在ingress的annotations中声明kubernetes.io/ingress.class: nginx
ingress:
hosts:
core: harbor.tke.com
notary: harbor.tke.com
controller: default
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "0"
kubernetes.io/ingress.class: nginx
externalURL: https://harbor.tke.com
若没有声明kubernetes.io/ingress.class: nginx,创建ingress时,会走tke-ingress-controller的控制器逻辑,导致创建CLB,并且会找不到证书
这里默认配置的是空的参数,如果有先创建好的pvc,需要在existingClaim参数里配置好对应的pvc 名称,否则会创建新的pvc出来。
若需指定pvc的,请先创建好对应的pvc,并在existingClaim填写好对应的pvc name,如下所示:
persistence:
enabled: true
resourcePolicy: "keep"
persistentVolumeClaim:
registry:
storageClass: ""
subPath: ""
accessMode: ReadWriteOnce
size: 5Gi
chartmuseum:
existingClaim: "harbor-chart"
storageClass: ""
subPath: ""
accessMode: ReadWriteOnce
size: 5Gi
jobservice:
existingClaim: "harbor-jobservice"
storageClass: ""
subPath: ""
accessMode: ReadWriteOnce
size: 1Gi
database:
existingClaim: "harbor-database"
storageClass: ""
subPath: ""
accessMode: ReadWriteOnce
size: 1Gi
redis:
existingClaim: "harbor-redis"
storageClass: ""
subPath: ""
accessMode: ReadWriteOnce
size: 1Gi
若是想使用其他storageclass可这么配置(如果有自己的storageclass,则把类名换成对应类名)
persistence:
enabled: true
resourcePolicy: "keep"
persistentVolumeClaim:
registry:
storageClass: "cbs"
chartmuseum:
storageClass: "cbs"
jobservice:
storageClass: "cbs"
database:
storageClass: "cbs"
redis:
storageClass: "cbs"
在value中有注释说明,这里省略了,一些关键点注释如,在配置前务必先将value的注释过一遍
如不想指定existingClaim,就不用配置,省略,后续创建harbor时会自动创建对应的pvc,并且配置为保留属性,harbor后续删除了也不会将pvc删除,保留数据盘。
由于整个value文件很长,上述只贴了个人认为比较核心的配置,在搭建过程中建议浏览一遍value文件。
精简过后的value 文件如下:
$ cat harbor-value.yaml
expose:
type: ingress
tls:
enabled: true
ingress:
hosts:
core: harbor.tke.com
notary: harbor.tke.com
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "0"
kubernetes.io/ingress.class: nginx
externalURL: https://harbor.tke.com
persistence:
enabled: true
resourcePolicy: "keep"
persistentVolumeClaim:
registry:
storageClass: "cbs"
chartmuseum:
storageClass: "cbs"
jobservice:
storageClass: "cbs"
database:
storageClass: "cbs"
redis:
storageClass: "cbs"
注意,在tke中若不指定storageclass,默认会使用cbs storageclass,此处起示范作用,在实际过程中可用其他 storageclass替换
#若是创建了精简的文件请用类似以下的命令
$ helm install --name harbor -f harbor-value.yaml ./harbor
#若是直接在value.yaml中修改的就无需加-f 参数指定
$ helm install --name harbor --namespace default ./harbor
NAME: harbor
LAST DEPLOYED: Tue Feb 25 17:04:37 2020
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
harbor-harbor-chartmuseum ClusterIP 172.16.253.254 <none> 80/TCP 1s
harbor-harbor-clair ClusterIP 172.16.252.131 <none> 8080/TCP 1s
harbor-harbor-core ClusterIP 172.16.253.19 <none> 80/TCP 1s
harbor-harbor-database ClusterIP 172.16.254.228 <none> 5432/TCP 1s
harbor-harbor-jobservice ClusterIP 172.16.253.191 <none> 80/TCP 1s
harbor-harbor-notary-server ClusterIP 172.16.255.135 <none> 4443/TCP 1s
harbor-harbor-notary-signer ClusterIP 172.16.252.60 <none> 7899/TCP 1s
harbor-harbor-portal ClusterIP 172.16.254.108 <none> 80/TCP 1s
harbor-harbor-redis ClusterIP 172.16.254.13 <none> 6379/TCP 1s
harbor-harbor-registry ClusterIP 172.16.253.232 <none> 5000/TCP,8080/TCP 1s
==> v1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
harbor-harbor-chartmuseum 1 1 1 0 1s
harbor-harbor-clair 1 1 1 0 1s
harbor-harbor-core 1 1 1 0 1s
harbor-harbor-jobservice 1 0 0 0 1s
harbor-harbor-notary-server 1 1 1 0 1s
harbor-harbor-notary-signer 1 0 0 0 1s
harbor-harbor-portal 1 0 0 0 1s
harbor-harbor-registry 1 0 0 0 1s
==> v1/StatefulSet
NAME DESIRED CURRENT AGE
harbor-harbor-database 1 1 0s
harbor-harbor-redis 1 1 0s
==> v1beta1/Ingress
NAME HOSTS ADDRESS PORTS AGE
harbor-harbor-ingress harbor.tke.com,harbor.tke.com 80, 443 0s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
harbor-harbor-chartmuseum-85b75674f6-zpnn2 0/1 Pending 0 1s
harbor-harbor-clair-84b5864556-6rr54 0/2 ContainerCreating 0 1s
harbor-harbor-core-884766589-2t84b 0/1 ContainerCreating 0 1s
harbor-harbor-jobservice-577d9f4df7-9z858 0/1 Pending 0 1s
harbor-harbor-notary-server-789d854975-hpdzw 0/1 ContainerCreating 0 1s
harbor-harbor-notary-signer-6ccfd745bb-ccqls 0/1 ContainerCreating 0 1s
harbor-harbor-portal-5cbc6d5897-lr9rp 0/1 ContainerCreating 0 1s
harbor-harbor-registry-5fb56db945-xh7hp 0/2 Pending 0 0s
harbor-harbor-database-0 0/1 Pending 0 0s
harbor-harbor-redis-0 0/1 Pending 0 0s
==> v1/Secret
NAME TYPE DATA AGE
harbor-harbor-chartmuseum Opaque 1 1s
harbor-harbor-clair Opaque 3 1s
harbor-harbor-core Opaque 7 1s
harbor-harbor-database Opaque 1 1s
harbor-harbor-jobservice Opaque 1 1s
harbor-harbor-notary-server Opaque 5 1s
harbor-harbor-registry Opaque 2 1s
==> v1/ConfigMap
NAME DATA AGE
harbor-harbor-chartmuseum 23 1s
harbor-harbor-core 41 1s
harbor-harbor-jobservice 1 1s
harbor-harbor-registry 2 1s
==> v1/PersistentVolumeClaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
harbor-harbor-chartmuseum Pending cbs 1s
harbor-harbor-jobservice Pending cbs 1s
harbor-harbor-registry Pending cbs 1s
NOTES:
Please wait for several minutes for Harbor deployment to complete.
Then you should be able to visit the Harbor portal at https://core.harbor.domain.
For more details, please visit https://github.com/goharbor/harbor.
由于这里创建的nginx-ingress用了loadbancer模式,所以创建了EXTERNAL-IP ,这时我们只要把域名的解析指向129.226.98.183 ,再通过域名即可访问harbor。
# kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
harbor-harbor-ingress harbor.tke.com,harbor.tke.com 80, 443 21s
# kubectl get svc -l app=nginx-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-ingress-controller LoadBalancer 172.16.253.162 129.226.98.183 80:30377/TCP,443:30399/TCP 13m
ingress-nginx-ingress-default-backend ClusterIP 172.16.252.178 <none> 80/TCP 13m
如果你有真实的域名,可以直接把域名解析至ingress-controller的EXTERNAL-IP,由于为了方便演示,没有用真实的域名,直接把地址映射进了 /etc/hosts 文件
$ echo 129.226.98.183 harbor.tke.com >> /etc/hosts
windows也是直接映射即可。win10的地址存放在C:\Windows\System32\drivers\etc\
映射完成后,在浏览器输入harbor.tke.com,就能访问到熟悉的harbor Portal界面了
这里的用户名密码是默认的admin / Harbor12345,在value.yaml中有声明也可修改
harbor Portal 已经可以访问了,接来下要在节点上docker login harbor.tke.com
跟第四条一样,如果没有映射域名是无法login的
# docker login harbor.tke.com
Username: admin
Password:
Error response from daemon: Get https://harbor.tke.com/v2/: x509: certificate signed by unknown authority
遇到上面的问题时,需要把证书保存在本地。
https://github.com/goharbor/harbor/blob/master/docs/1.10/install-config/configure-https.md
https://docs.docker.com/engine/security/certificates/
1. 创建存放证书目录
mkdir -pv /etc/docker/certs.d/harbor.tke.com
2. 从k8s中导出证书
kubectl get secret harbor-harbor-ingress -o jsonpath="{.data.ca\.crt}"|base64 --decode > /etc/docker/certs.d/harbor.tke.com/ca.crt
3. 测试登录
# docker login harbor.tke.com
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
测试推送镜像
$ docker tag busybox harbor.tke.com/library/busybox
$ docker push harbor.tke.com/library/busybox
The push refers to repository [harbor.tke.com/library/busybox]
195be5f8be1d: Pushed
latest: digest: sha256:edafc0a0fb057813850d1ba44014914ca02d671ae247107ca70c94db686e7de6 size: 52
推送完成后,可以在harbor的界面看到这个镜像
还可以测试拉取镜像
$ docker rmi harbor.tke.com/library/busybox
Untagged: harbor.tke.com/library/busybox:latest
Untagged: harbor.tke.com/library/busybox@sha256:edafc0a0fb057813850d1ba44014914ca02d671ae247107ca70c94db686e7de6
$ docker pull harbor.tke.com/library/busybox
Using default tag: latest
latest: Pulling from library/busybox
Digest: sha256:edafc0a0fb057813850d1ba44014914ca02d671ae247107ca70c94db686e7de6
Status: Downloaded newer image for harbor.tke.com/library/busybox:latest
$ docker images | grep harbor.tke.com/library/busybox
harbor.tke.com/library/busybox latest 6d5fcfe5ff17 2 months ago 1.22MB
若要使k8s pod可以拉取harbor私有的镜像,还需创建secret,并在workload中指定ImagePullSecrets
将config.json转换成 base64,然后写入到secret中
# 前提需先docker login harbor.tke.com
$ cat /root/.docker/config.json | base64 -w 0
ewoJImF1dGhzIjogewoJCSJoYXJib3IudGtlLmNvbSI6IHsKCQkJImF1dGgiOiAiWVdSdGFXNDZTR0Z5WW05eU1USXpORFU9IgoJCX0KCX0sCgkiSHR0cEhlYWRlcnMiOiB7CgkJIlVzZXItQWdlbnQiOiAiRG9ja2VyLUNsaWVudC8xOC4wNi4zLWNlIChsaW51eCkiCgl9Cn0=
$ cat harborkey.yaml
apiVersion: v1
kind: Secret
metadata:
name: harborkey
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: ewoJImF1dGhzIjogewoJCSJoYXJib3IudGtlLmNvbSI6IHsKCQkJImF1dGgiOiAiWVdSdGFXNDZTR0Z5WW05eU1USXpORFU9IgoJCX0KCX0sCgkiSHR0cEhlYWRlcnMiOiB7CgkJIlVzZXItQWdlbnQiOiAiRG9ja2VyLUNsaWVudC8xOC4wNi4zLWNlIChsaW51eCkiCgl9Cn0=
然后在yaml中声明imagePullSecrets
apiVersion: v1
kind: Pod
metadata:
name: test-busybox
spec:
containers:
- name: test-busybox
image: harbor.tke.com/library/busybox
imagePullSecrets:
- name: harborkey
通过tke 以及helm工具,我们快速的创建出一个k8s集群并通过helm工具快速的部署了nginx-ingress、harbor,数据持久化存放在cbs中。由于大多数的组件以无状态(Deployment)的形式运行,若集群资源充足,可多启动几个副本并打散在不同作为高可用。其中database和redist以有状态(StatefulSet)运行。
可能会踩到的坑:
- 下载harbor镜像失败
例如节点在拉取镜像时,可能会因为不可抗拒的网络问题导致下载镜像失败的。在测试过程中用的是中国香港地域的节点,所以在拉取镜像时没有遇到问题,若在部署过程中遇到下载镜像失败的,通过其他方式拉取到镜像,再推送到国内的镜像仓库中,手动替换下workload中image的配置
- 使用自己的https证书
harbor的https证书是可以用自己申请的,默认helm chart中也有一个证书,若没有指定证书,则使用chart中提供的证书。若使用自带的证书,还需先将其转换成secret,并在value中指定secret namevalue.yaml:
tls:
# Enable the tls or not. Note: if the type is "ingress" and the tls
# is disabled, the port must be included in the command when pull/push
# images. Refer to https://github.com/goharbor/harbor/issues/5291
# for the detail.
enabled: true
???? #如果要使用自己的TLS证书,请填写secret名称。
???? #机密必须包含名为:
???? #“ tls.crt”-证书
???? #“ tls.key”-私钥
???? #“ ca.crt”-CA的证书
???? #如果未设置“ secretName”,这些文件将自动生成
secretName: "harbor-tls-secret"
在创建harbor前先把secret创建好
$ kubectl create secret generic harbor-tls-secret --from-file=tls.crt=tls.crt --from-file=tls.key=tls.key --from-file=ca.cre=ca.crt
- storageclass
如果storageclass没有申请到存储资源,会导致pod无法正常启动
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。