Harbor镜像仓库笔记

提供界面的镜像仓库可以选择 SUSE 的 Portus 和 VMware 的 Harbor。我选择的是 Harbor。其实一开始是倾向于 Portus 的,因为 Application tokens 功能看起来很有用,但是尝试部署时总是报错,Ruby 程序就是这么难搞。只好去尝试 Harbor。用 OIDC 登录时,有个 CLI 密码,类似 Application tokens 的功能,不必将个人密码写到 .docker/config.json 中去。本文记录安装及使用 Harbor(1.9.0) 遇到的一些问题。

安装

直接使用 helm 安装到 Kubernetes 集群中,首先准备好 Postgres 和 Redis,使用 kubedb,参考 使用 kubedb 管理数据库 一文。然后提前创建配置文件中指定的 postgres 数据库,可以用 pgAdmin 创建。

    coreDatabase: "registry"
    clairDatabase: "clair"
    notaryServerDatabase: "notary_server"
    notarySignerDatabase: "notary_signer"

使用 helm 安装 Harbor。

# 更新仓库,使用最新版
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,例如:

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报错

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 响应头:

[global]
  ...
  rgw swift url = https://s3.com

使用 curl 测试:

# 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:

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。

for id in `kubectl get ns |awk 'NR>1{print $1}'`;do kubectl -n $id apply -f cr-key.yaml;done

cr-key.yaml 格式如下:

apiVersion: v1
kind: Secret
metadata:
  name: cr-image-pull-secret
data:
  .dockerconfigjson: 包含admin账号的~/.docker/config.json的base64编码
type: kubernetes.io/dockerconfigjson

参考资料

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/

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注