提供界面的镜像仓库可以选择 SUSE 的 Portus 和 VMware 的 Harbor。我选择的是 Harbor。其实一开始是倾向于 Portus 的,因为 Application tokens 功能看起来很有用,但是尝试部署时总是报错,Ruby 程序就是这么难搞。只好去尝试 Harbor。用 OIDC 登录时,有个 CLI 密码,类似 Application tokens 的功能,不必将个人密码写到 .docker/config.json
中去。本文记录安装及使用 Harbor(1.9.0) 遇到的一些问题。
Contents
安装
直接使用 helm 安装到 Kubernetes 集群中,首先准备好 Postgres 和 Redis,使用 kubedb,参考 使用 kubedb 管理数据库 一文。然后提前创建配置文件中指定的 postgres
数据库,可以用 pgAdmin
创建。
1 2 3 4 |
coreDatabase: "registry" clairDatabase: "clair" notaryServerDatabase: "notary_server" notarySignerDatabase: "notary_signer" |
使用 helm 安装 Harbor。
1 2 3 4 5 6 |
# 更新仓库,使用最新版 helm repo update # 测试环境 helm install cr harbor/harbor --version 1.2.0 --namespace dev -f ./values-test.yaml # 生产环境 helm install cr harbor/harbor --version 1.2.0 --namespace intra -f ./values.yaml |
values.yaml
中,配置外部 Postgres 和 外部 Redis,例如:
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 |
database: type: external external: host: "ha-postgres.demo.svc.cluster.local" port: "5432" username: "postgres" password: "password" coreDatabase: "registry" clairDatabase: "clair" notaryServerDatabase: "notary_server" notarySignerDatabase: "notary_signer" sslmode: "disable" maxIdleConns: 50 maxOpenConns: 100 podAnnotations: {} redis: type: external external: host: "redis-quickstart.demo.svc.cluster.local" port: "6379" coreDatabaseIndex: "0" jobserviceDatabaseIndex: "1" registryDatabaseIndex: "2" chartmuseumDatabaseIndex: "3" password: "" podAnnotations: {} |
测试环境证书
顺便提一下测试环境自签名证书问题,需要在 docker 客户端导入 CA 证书,把CA放到/etc/pki/ca-trust/source/anchors
,在命令行运行/bin/update-ca-trust
,这样证书就导入到系统中去了。然后重启 docker daemon。
OIDC
参考 使用dex实现OIDC Provider 一文。注意 oidc scope 要填 profile
,否则获取不到用户名等信息。
- oidc供应商: 随便填 (dex)
- oidc endpoint: https://oidc.xxx.com
- oidc 客户端标识:和 无状态服务 oidc 的设置保持一致(Harbor)
- oidc 密码:和 无状态服务 oidc 的设置保持一致(Harbor)
- oidc scope:openid,offline_access,profile
问题记录
push失败排查
- 报错
denied: requested access to the resource is denied
,可能是因为仓库不存在,检查project/image:tag
是否正确 - 报错
blob upload invalid
,可能是因为 s3 上传失败,检查 s3 nginx代理的client_max_body_size
,设置为足够大的值,比如5000m
Redis Cluster报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
2019-09-19T15:28:15Z [DEBUG] [/common/dao/project.go:149]: sql:=select distinct p.project_id, p.name, p.owner_id, p.creation_time, p.update_time from project as p where p.deleted=false order by p.name, param= [] 2019/09/19 15:28:15 [I] [asm_amd64.s:1337] http server Running on http://:8080 2019/09/19 15:28:41 [E] [server.go:2774] MOVED 5089 192.168.64.117:6379 2019/09/19 15:28:41 [D] [server.go:2774] | 10.112.33.205| 503 | 9.958842ms| nomatch| GET /api/ping 2019/09/19 15:28:41 [E] [server.go:2774] MOVED 13229 192.168.65.101:6379 2019/09/19 15:28:41 [D] [server.go:2774] | 10.112.33.205| 503 | 1.940507ms| nomatch| GET /api/ping 2019/09/19 15:28:51 [E] [server.go:2774] MOVED 3558 192.168.64.117:6379 2019/09/19 15:28:51 [D] [server.go:2774] | 10.112.33.205| 503 | 6.13463ms| nomatch| GET /api/ping 2019/09/19 15:28:51 [E] [server.go:2774] MOVED 10236 192.168.100.31:6379 2019/09/19 15:28:51 [D] [server.go:2774] | 10.112.33.205| 503 | 3.027112ms| nomatch| GET /api/ping 2019/09/19 15:29:01 [E] [server.go:2774] MOVED 938 192.168.64.117:6379 2019/09/19 15:29:01 [D] [server.go:2774] | 10.112.33.205| 503 | 1.920783ms| nomatch| GET /api/ping 2019/09/19 15:29:01 [E] [server.go:2774] MOVED 6511 192.168.100.31:6379 2019/09/19 15:29:01 [D] [server.go:2774] | 10.112.33.205| 503 | 2.258429ms| nomatch| GET /api/ping 2019-09-19T15:29:01Z [INFO] [/core/main.go:146]: capture system signal terminated, to close "closing" channel 2019-09-19T15:29:04Z [INFO] [/core/main.go:152]: Timeout waiting goroutines to exit |
1.9.0 版本还不支持 Redis Cluster,改用单节点。
OIDC模式CLI密码失效
表现为隔一段时间未从浏览器登录 Harbor,则 docker login
会失败,从浏览器登录一次之后 docker login
又能成功了。怀疑是 OIDC provider 未正确实现 refresh_token
功能。验证及解决方案:调短 Dex idtoken 过期时间(比如1分钟) 来验证是否是 refresh_token
的问题,然后尝试修复 refresh_token
问题。
通过实现了 refresh_token
的测试 connector, 设置 idTokens
过期时间为 20s
,发现 docker login
20s 后依然可以登录。而使用线上未实现 refresh_token
的 connector, 则 20 s 后登录失效。
解决方案: 直接抄 github connector 的 refresh
代码即可。
S3 问题
Harbor 1.9.0 S3 不可用,镜像都是 0B,而且只显示第一个 repository。另外 S3 垃圾回收似乎也有问题。因此使用 ceph swift。用 Nginx 给 swift api 做代理时,需要 ceph.conf
设置 rgw swift url
,否则不能正确获取 X-Storage-Url
响应头:
1 2 3 |
[global] ... rgw swift url = https://s3.com |
使用 curl 测试:
1 2 3 4 5 6 7 8 9 10 11 |
# curl -i -H "X-Auth-User: test:swift" -H "X-Auth-Key: secret" https://s3.com/auth HTTP/1.1 204 No Content Date: Fri, 20 Sep 2019 08:27:15 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive X-Storage-Url: http://s3.com:443/swift/v1 X-Storage-Token: AUTH_rgwtk X-Auth-Token: AUTH_rgwtk X-Trans-Id: tx00000000000000000010c-005d848d63-2a62-default X-Openstack-Request-Id: tx00000000000000000010c-005d848d63-2a62-default Strict-Transport-Security: max-age=15724800; includeSubDomains |
设置不正确时可能的报错,抓包可以看到 X-Storage-Url
被自动加上了 7480
端口,协议也变成了 HTTP:
1 2 |
swift --os-cacert /home/deca.pem -R=1 -A https://s3.play.cn/auth -U harbor -K secretkey list HTTPConnectionPool(host='s3.play.cn', port=7480): Max retries exceeded with url: /swift/v1?format=json (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f4c0bfe1b00>: Failed to establish a new connection: [Errno 111] Connection refused',)) |
Kubernetes使用Harbor
由于 OIDC 模式可能存在 CLI 密码过期问题,会导致 Kubernetes 拉取镜像失败。Harbor 不像 Gitea 那样既支持数据库用户又支持 OIDC 用户,因此只能选择 admin 这个唯一的数据库用户来作为 Kubernetes imagePullSecret
的账号。需要考虑以 imagepullsecret 是否会暴露给用户。结论是 未通过 dashboard
暴露给用户,可以使用 admin 账号。
为每个 namespace 添加 imagePullSecret。
1 |
for id in `kubectl get ns |awk 'NR>1{print $1}'`;do kubectl -n $id apply -f cr-key.yaml;done |
cr-key.yaml
格式如下:
1 2 3 4 5 6 7 |
apiVersion: v1 kind: Secret metadata: name: cr-image-pull-secret data: .dockerconfigjson: 包含admin账号的~/.docker/config.json的base64编码 type: kubernetes.io/dockerconfigjson |
参考资料
1 2 3 4 5 6 |
1. https://github.com/goharbor/harbor/issues/8620 2. https://github.com/goharbor/harbor/issues/8121 3. https://goharbor.io/ 4. http://lists.ceph.com/pipermail/ceph-users-ceph.com/2016-April/009213.html 5. https://wiki.annhe.net/02-工程实践/kubernetes/infrastructure/registry 6. https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ |
发表回复