前言
对于 Harbor 这样一个优秀的 Docker Registry 管理开源项目,以下内容基本上来自前人已有的研究,我只是将其在实践中进行了测试,并整理汇集了相关资料供大家参考,同时针对 Harbor 与 Rancher产品的整合做了一些实验性的工作,以更好更全面的理解 Harbor 这个工具,也更加了解 Rancher 在快速一键部署、弹性伸缩高可用等方面的优势。
Harbor 简介
Harbor 是一个企业级 Registry 服务。它对开源的 Docker Registry 服务进行了扩展,添加了更多企业用户需要的功能。Harbor 被设计用于部署一套组织内部使用的私有环境,这个私有 Registry 服务对于非常关心安全的组织来说是十分重要的。另外,私有 Registry 服务可以通过避免从公域网下载镜 像而提高企业生产力。这对于没有良好的 Internet 连接状态,使用 Docker Container 的用户是一个福音。
- 基于角色的访问控制:用户与 Docker 镜像仓库通过“项目”进行组织管理,一个用户可以对多个镜像仓库在同一命名空间(project)里有不同的权限。
- 图形化用户界面:用户可以通过浏览器来浏览,检索当前 Docker 镜像仓库,管理项目和命名空间。
- 审计管理:所有针对镜像仓库的操作都可以被记录追溯,用于审计管理。
- 国际化:基于英文与中文语言进行了本地化。可以增加更多的语言支持。
- RESTful API – RESTful API:提供给管理员对于 Harbor 更多的操控, 使得与其它管理软件集成变得更容易。
Harbor 架构介绍
(1)主要组件
Harbor 在架构上主要由五个组件构成:
- Proxy:Harbor 的 registry, UI, token 等服务,通过一个前置的反向代理统一接收浏览器、Docker 客户端的请求,并将请求转发给后端不同的服务。
- Registry:负责储存 Docker 镜像,并处理 docker push/pull 命令。由于我们要对用户进行访问控制,即不同用户对 Docker image 有不同的读写权限,Registry 会指向一个 token 服务,强制用户的每次 docker pull/push 请求都要携带一个合法的 token, Registry 会通过公钥对 token 进行解密验证。
- Core services:这是 Harbor 的核心功能,主要提供以下服务:
UI:提供图形化界面,帮助用户管理 registry 上的镜像(image),并对用户进行授权;
webhook:为了及时获取 registry 上 image 状态变化的情况, 在 Registry 上配置 webhook,把状态变化传递给 UI 模块;
token 服务:负责根据用户权限给每个 docker push/pull 命令签发 token。Docker 客户端向Registry 服务发起的请求,如果不包含token,会被重定向到这里,获得 token 后再重新向 Registry进行请求;
- Database:为 core services 提供数据库服务,负责储存用户权限、审计日志、Docker image 分组信息等数据。
- Log collector:为了帮助监控 Harbor 运行,负责收集其他组件的 log,供日后进行分析。各个组件之间的关系如下图所示:
(2)实现
Harbor 的每个组件都是以 Docker 容器的形式构建的,因此很自然地,我们使用 Docker Compose 来对它进行部署。在源代码中(https://github.com/vmware/har…,用于部署 Harbor 的 Docker Compose 模板位于 /Deployer/docker-compose.yml. 打开这个模板文件,会发现 Harbor 由 5 个容器组成:
- proxy:由 Nginx 服务器构成的反向代理。
- registry:由 Docker 官方的开源 registry 镜像构成的容器实例。
- ui:即架构中的 core services, 构成此容器的代码是 Harbor 项目的主体。
- mysql:由官方 MySql 镜像构成的数据库容器。
- log: 运行着 rsyslogd 的容器,通过 log-driver 的形式收集其他容器的日志。
这几个容器通过 Docker link 的形式连接在一起,这样,在容器之间可以通过容器名字互相访问。对终端用户而言,只需要暴露 proxy (即 Nginx)的服务端口。
(3)工作原理
下面以两个 Docker 命令为例,讲解主要组件之间如何协同工作。
1) docker login
假设我们将 Harbor 部署在主机名为 registry.yourdomainname.com 的虚机上。用户通过 docker login 命令向这个 Harbor 服务发起登录请求:docker login registry.yourdomainname.com当用户输入所需信息并点击回车后,Docker 客户端会向地址“registry.yourdomainname.com/v2/” 发出 HTTP GET 请求。 Harbor 的各个容器会通过以下步骤处理:
(a) 首先,这个请求会由监听 80 端口的 proxy 容器接收到。根据预先设置的匹配规则,容器中的 Nginx会将请求转发给后端的 registry 容器;
(b) 在 registry 容器一方,由于配置了基于 token 的认证,registry 会返回错误代码 401,提示 Docker客户端访问 token 服务绑定的 URL。在 Harbor 中,这个 URL 指向 Core Services;
(c) Docker 客户端在接到这个错误代码后,会向token服务的URL发出请求,并根据HTTP协议的BasicAuthentication 规范,将用户名密码组合并编码,放在请求头部(header);
(d)类似地,这个请求通过 80 端口发到 proxy 容器后,Nginx 会根据规则把请求转发给 ui 容器,ui 容器监听 token 服务网址的处理程序接收到请求后,会将请求头解码,得到用户名、密码;
(e) 在得到用户名、密码后,ui 容器中的代码会查询数据库,将用户名、密码与 mysql 容器中的数据进行比对(注:ui 容器还支持 LDAP 的认证方式,在那种情况下 ui 会试图和外部 LDAP 服务进行通信并校验用户名/密码)。比对成功,ui 容器会返回表示成功的状态码, 并用密钥生成 token,放在响应体中返回给 Docker 客户端。这个过程中组件间的交互过程如下图所示:
至此,一次 docker login 成功地完成了,Docker 客户端会把步骤(c)中编码后的用户名密码保存在本地的隐藏文件中。
2) docker push
用户登录成功后用 docker push 命令向 Harbor 推送一个 Docker 镜像:docker push registry.youdomainname.com/library/hello-world
(a) 首先,docker 客户端会重复 login 的过程,首先发送请求到 registry,之后得到 token 服务的地址;
(b) 之后,Docker 客户端在访问ui容器上的token服务时会提供额外信息,指明它要申请一个对imagelibrary/hello-world 进行 push 操作的 token;
(c) token 服务在经过 Nginx 转发得到这个请求后,会访问数据库核实当前用户是否有权限对该 image进行 push。如果有权限,它会把 image 的信息以及 push 动作进行编码,并用私钥签名,生成 token返回给 Docker 客户端;
(d) 得到 token 之后 Docker 客户端会把 token 放在请求头部,向 registry 发出请求,试图开始推送image。 Registry 收到请求后会用公钥解码 token 并进行核对,一切成功后,image 的传输就开始了。我们省去 proxy 转发的步骤,下图描述了这个过程中各组件的通信过程 :
Harbor 单机安装调试步骤
此次示范以 CentOS 7.2.1511 x86_64 为例:
[root@registry ~]# yum install https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.11.2-
1.el7.centos.noarch.rpm
[root@registry ~]# yum install https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-1.11.2-
1.el7.centos.x86_64.rpm
[root@registry ~]# systemctl enable docker
[root@registry ~]# systemctl start docker
[root@registry ~]# yum install git
[root@registry ~]# git clone https://github.com/vmware/harbor
[root@registry ~]# cd harbor/
[root@registry harbor]# cd Deploy/
[root@registry Deploy]# vi harbor.cfg
修改的重点内容如下:
hostname = registry.yourdomainname.comui_url_protocol = https
email_server = smtp.yourmailserver.comemail_server_port = 25
email_username = registry_admin@yourdomainname.comemail_password = yourpassword
email_from = registry_admin@yourdomainname.comemail_ssl = false
harbor_admin_password = myharborpasswordauth_mode = db_auth
db_password = yoursqlpasswordself_registration = off
customize_crt = off
修改完成
假设已经准备好站点的数字证书文件 registry.yourdomainname.com.crt 和registry.yourdomainname.com.key,则可以配置 https 的访问模式:
[root@registry Deploy]# cd config/nginx/
[root@registry nginx]# ls cert/
registry.yourdomainname.com.crt registry.yourdomainname.com.key
[root@registry nginx]# mv nginx.conf nginx.conf.bak
[root@registry nginx]# cp nginx.https.conf nginx.conf
[root@registry nginx]# vi nginx.conf
修改内容如下:
修改内容结束
安装 docker-compose 命令:
添加 harbor 的启停脚本:
添加 harbor 为 systemd 服务:
管理 Harbor 的生命周期:
关于数字证书,上面的描述适用于向第三方根证书颁发机构申请得到的数字证书文件,如果是自签名数字证书,可参考 Harbor 官方文档:https://github.com/vmware/har…
访问 Harbor:
- 网页访问方式 https://registry.yourdomainna…
- Linux Docker 客户端如需要访问这个仓库服务器,如果数字证书的根证书颁发机构不在系统列表里,则需要手动添加信任关系(需要将证书文件 registry.yourdomainname.com.crt 拷贝至指定目录),然后更新证书缓存:
cp registry.yourdomainname.com.crt /usr/local/share/ca-certificates/update-ca-certificates
以上两条命令适用于 Ubuntu 系列
cp registry.yourdomainname.com.crt/etc/pki/ca-trust/source/anchors/update-ca-trust
以上两条命令适用于 RHEL 或 CentOS 系列
登陆及 push image 过程:
[root@RancherHost01 ~]# docker login
registry.yourdomainname.com
Username: admin
Password:
Login Succeeded
[root@RancherHost01 ~]#
登陆 Web 端即可看见已经上传的 image 了:
新增用户界面:
Harbor 作为 Mirror Registry
Mirror 是 Docker Registry 的一种特殊类型,它起到了类似代理服务器的缓存角色,在用户和Docker Hub 之间做 Image 的缓存。
其基本工作原理是,当用户 pull 一个镜像时,若镜像在 mirror 服务器存在,则直接从 mirror 服务器拉取,否则若不存在该镜像,则由 mirror server 自动代理往 dockerhub(可配置)中拉取镜像,并缓存到 mirror 服务器中,当客户再次拉取这个镜像时,直接从 mirror server 中拉取,不需要再次从docker hub 中拉取。
Harbor 目前不支持 pull cache 功能,已提交 Github issue #120。 https://github.com/vmware/har…
不过我们只需要手动修改下配置即可完成,具体配置可查看官方 Registry as a pull through cache.
https://github.com/vmware/har…
我们在运行./prepare 之前修改 config/registry/config.yml 文件,追加以下配置
:proxy:remoteurl: https://registry-1.docker.io
如果需要访问私有仓库,需要填写 Docker Hub 的用户名和密码
:proxy:
remoteurl: https://registry-1.docker.io
username: [username]
password: [password]
然后重新启动 Harbor 服务:(注意不要执行./prepare)
docker-compose stop
docker-compose rm -f
docker-compose up -d
除了设置 Harbor(或者 registry),还需要配置本地 docker 服务,指定–registry-mirror 参数,修改docker 配置文件
Ubuntu:/etc/default/docker
DOCKER_OPTS="$DOCKER_OPTS --registry-mirror=https://registry.yourdomainname.com
或
DOCKER_OPTS="$DOCKER_OPTS --registry-mirror=https://registry.yourdomainname.com --insecure-registry
registry.yourdomainname.com"
或者
RHEL/CentOS:/usr/lib/systemd/system/docker.service):
[Service]
ExecStart=
ExecStart=/usr/bin/docker daemon -H fd:// --registry-mirror=https://registry.yourdomainname.com
注意:修改了 docker 配置文件,必须重启 docker 服务才能生效。
Harbor 由于引进了认证功能,因此 push 操作时,必须保证 project 存在,比如 push krystism/ffmpeg,必须保证 Harbor 创建了 krystism project,否则会失败。为了能够正常 push/pulldockerhub 的官方镜像,务必创建 library project,如图:
(1)假设本地不存在 python 镜像:
我们第一次 pull python 后,Harbor 发现不存在该镜像,于是自己作为代理往 Docker Hub 里拉取,拉取后保存到本地,可以通过 Web UI 查看。客户端再次拉取 python 镜像时,由于 Harbor 已经存在该镜像,因此不需要再往 Docker Hub 拉取,速度大幅度提高!
注意,对于 Mirror Registry 模式,虽然可以 pull cache 了,但是 push 功能却不被支持了:https://github.com/vmware/har…
(2)与 Rancher 的整合:
1)Add Harbor deploy stack and 5 services in Rancher:(在 Rancher 系统里添加包含 5 个 services 的stack)
2)Add Registry server in Rancher:(在 Rancher 系统里添加 Registry 服务器,供 Rancher Agent Hosts调用)
3)Build a Catalog entry in Rancher:(为 Harbor 创建一个 Rancher 专有的应用模板项,实现一键部署,使安装调试复杂的 Harbor 产品简单化、并实现弹性伸缩及高可用等特性)
(3)对接 LDAP 认证:
Harbor 支持两种认证方式,默认为本地存储,即账号信息存储在 mysql 下,上文已经具体介绍。接下来介绍另外一种认证方式 LDAP,只需要修改配置文件即可。需要提供 ldap url 以及 ldap basedn 参数,并且设置 auth_mode 为 ldap_auth。
快速部署 LDAP 服务:
为了测试方便,我们使用 Docker 启动一个 LDAP 服务器,启动脚本如下:
!/bin/bash
NAME=ldap_server
docker rm -f $NAME 2>/dev/null
docker run --env LDAP_ORGANISATION="Unitedstack Inc." \--env LDAP_DOMAIN="ustack.com" \
--env LDAP_ADMIN_PASSWORD="admin_password" \
-v pwd/containers/openldap/data:/var/lib/ldap \
-v pwd/containers/openldap/slapd.d:/etc/ldap/slapd.d \--detach --name $NAME osixia/openldap:1.1.2
创建新用户,首先需要定义 ldif 文件,
new_user.ldif:dn: uid=test,dc=ustack,dc=com
uid: test
cn: test
sn: 3
objectClass: topobjectClass: posixAccountobjectClass: inetOrgPersonloginShell: /bin/bashhomeDirectory:
/home/testuidNumber: 1001gidNumber: 1001userPassword: 1q2w3e4rmail: test@example.comgecos: test
通过以下脚本创建新用户,其中 ldap_server 为 LDAP 服务容器名称。
docker cp new_user.ldif ldap_server:/
docker exec ldap_server ldapadd -x \
-D "cn=admin,dc=ustack,dc=com" \
-w admin_password \-f /new_user.ldif -ZZ
查看用户是否创建成功:
docker exec ldap_server ldapsearch -x -h localhost \
-b dc=ustack,dc=com -D "cn=admin,dc=ustack,dc=com" \
-w admin_password
检查 test 用户是否存在,若存在,则说明创建成功,否则需要使用 docker logs 查看日志。
配置 Harbor 使用 LDAP 认证修改 harbor.cfg 文件关于 LDAP 配置项,如下
:auth_mode = ldap_auth
ldap_url = ldap://42.62.x.x
ldap_basedn = uid=%s,dc=ustack,dc=com
然后重新部署 Harbor:
./prepare
docker-compose stop
docker-compose rm -f
docker-compose up -d
测试是否能够使用 test 用户登录:
docker login -u test -p 1q2w3e4r \
-e test@example.com 42.62.x.x