Docker 之十六搭建私有仓库管理系统 Harbor

相关站点

Harbor 介绍

Harbor 是 VMware 公司开源的一个用于存储和分发 Docker 镜像的企业级 Registry 服务器,以 Docker 开源的 Registry 为基础,通过添加一些企业必需的功能特性,例如安全、标识和管理等,扩展了开源 Docker Distribution。作为一个企业级私有 Registry 服务器,Harbor 提供了更好的性能和安全,提升用户使用 Registry 构建和运行环境传输镜像的效率。Harbor 支持安装在多个 Registry 节点的镜像资源复制,镜像全部保存在私有 Registry 中,确保数据和知识产权在公司内部网络中管控。另外,Harbor 也提供了高级的安全特性,诸如用户管理,访问控制和活动审计等。

Harbor 特性

  • 基于角色的访问控制(Role Based Access Control)
  • 基于策略的镜像复制(Policy based image replication)
  • 镜像的漏洞扫描(Vulnerability Scanning)
  • AD/LDAP 集成(LDAP/AD support)
  • 镜像的删除和空间清理(Image deletion & garbage collection)
  • 友好的管理 UI(Graphical user portal)
  • 审计日志(Audit logging)
  • RESTful API
  • 部署简单(Easy deployment)

Harbor 组件

  • 依赖的外部组件:

    1. Nginx(Proxy): Harbor 的 Registry、UI、Token 等服务,通过一个前置的反向代理统一接收浏览器、Docker 客户端的请求,并将请求转发给后端不同的服务。
    2. Registry v2: Docker 官方镜像仓库,负责储存 Docker 镜像,并处理 Docker Push/Pull 命令。由于我们要对用户进行访问控制,即不同用户对 Docker 镜像有不同的读写权限,Registry 会指向一个 Token 服务,强制用户的每次 Docker Push/Pull 请求都要携带一个合法的 Token, Registry 会通过公钥对 Token 进行解密验证。
    3. Database(MySQL/Postgresql):为 Core Services 提供数据库服务,负责储存用户权限、审计日志、Docker 镜像分组信息等数据。
  • Harbor 自己的组件:

    1. Core Services(Admin Server): 这是 Harbor 的核心功能,主要提供以下服务:
      • API:提供 Harbor RESTful API
      • UI:提供图形化界面,帮助用户管理 Registry 上的镜像,并对用户进行授权。
      • Webhook:为了及时获取 Registry 上镜像状态变化的情况,在 Registry 上配置 Webhook,把状态变化传递给 UI 模块。
      • Auth 服务:负责根据用户权限给每个 Docker Push/Pull 命令签发 Token。Docker 客户端向 Registry 服务发起的请求,如果不包含 Token,会被重定向到这里,获得 Token 后再重新向 Registry 进行请求。
    2. Replication Job Service:提供多个 Harbor 实例之间的镜像同步功能。
    3. Log Collector:为了帮助监控 Harbor 运行,负责收集其他组件的日志,供日后进行分析。

Harbor 架构图

docker-harbor-architecture

Harbor 安裝方式

不建议使用 Kubernetes 来安裝,原因是镜像仓库非常重要,尽量保证安裝和维护的简洁性,因此这里直接使用 Docker Compose 的方式进行安裝。事实上 Harbor 的每个组件都是以 Docker 容器的形式构建,官方也是使用 Docker Compose 来对它进行安裝。Harbor 官方提供以下三种安裝方式:

  • 在线安装:从 Docker Hub 下载 Harbor 的镜像来安装,由于 Docker Hub 比较慢,建议 Docker 配置好加速器。
  • 离线安装:这种方式应对与安裝主机没联网的情况使用,需要提前下载离线安装包到本地。
  • OVA 安装:这个主要用 vCentor 环境时使用。

Harbor 安装环境说明

Harbor 以容器的形式进行安装,因此可以被安装到任何支持 Docker 的 Linux 发行版,本教程的安装环境如下:

环境名称版本
linux 发行版 CentOS Linux release 7.6.1810 (Core)
docker-ce18.09.0
docker-compose1.24.0-rc1
harbor1.7.1
harbor 安装方式在线安装
harbor 安装位置 /usr/local/harbor

安装 Docker

站内教程:Docker 之一 Docker 介绍与安装

安装 Docker-Compose

站内教程:Docker 之十七 Docker-Compose 安装与使用

安装 Harbor

Harbor 的在线或者离线安装程序下载地址可以从这里获取,如果下载失败,请自备梯子。

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
28
29
30
31
32
33
34
35
# 安装方式分为在线安装和离线安装两种方式,这里采用在线安装方式

# 下载在线安装程序
# wget -P /usr/local https://storage.googleapis.com/harbor-releases/release-1.7.0/harbor-online-installer-v1.7.1.tgz

# 解压下载文件
# tar zxf /usr/local/harbor-online-installer-v1.7.1.tgz -C /usr/local/

# 修改配置文件,根据自己的需求进行修改
# vim /usr/local/harbor/harbor.cfg
# 本机IP或者域名,不能是127.0.0.1或者localhost
hostname = 192.168.1.130
# 系统Harbor管理员的密码
harbor_admin_password = Harbor12345
# 禁止用户注册
self_registration = off
# 设置只有管理员可以创建项目
project_creation_restriction = adminonly

# 由于Harbor的Nginx组件默认会监听宿主机的80、443、4443端口,如果需要更改Nginx的端口映射,可以修改以下配置文件
# vim /usr/local/harbor/docker-compose.yml
ports:
- 8082:80
- 443:443
- 4443:4443

# 如果上面更改了Nginx的80端口映射,此时还需要编辑Harbor的配置文件,修改hostname加上指定的端口号
# vim harbor.cfg
hostname = 192.168.1.130:8082

# 执行安装脚本
# /usr/local/harbor/install.sh

# Harbar的日志目录是:/var/log/harbor
# Harbar相关数据卷的挂载目录默认是宿主机的/data目录,如果重新安装Harbar并在配置文件里更改了数据库密码,则需要删除/data目录,否则Harbor部分组件会启动失败

安装 Harbor 的日志信息

★查看详细日志信息★
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
[Step 0]: checking installation environment ...

Note: docker version: 18.09.0

Note: docker-compose version: 1.24.0

[Step 1]: preparing environment ...
Generated and saved secret to file: /data/secretkey
Generated configuration file: ./common/config/nginx/nginx.conf
Generated configuration file: ./common/config/adminserver/env
Generated configuration file: ./common/config/core/env
Generated configuration file: ./common/config/registry/config.yml
Generated configuration file: ./common/config/db/env
Generated configuration file: ./common/config/jobservice/env
Generated configuration file: ./common/config/jobservice/config.yml
Generated configuration file: ./common/config/log/logrotate.conf
Generated configuration file: ./common/config/registryctl/env
Generated configuration file: ./common/config/core/app.conf
Generated certificate, key file: ./common/config/core/private_key.pem, cert file: ./common/config/registry/root.crt
The configuration files are ready, please use docker-compose to start the service.

[Step 2]: checking existing instance of Harbor ...

[Step 3]: starting Harbor ...
Creating network "harbor_harbor" with the default driver
Pulling log (goharbor/harbor-log:v1.7.1)...
v1.7.1: Pulling from goharbor/harbor-log
321a8da5ee1f: Pull complete
e58cb02d4a79: Pull complete
b1addcae27cf: Pull complete
0add5fe71c61: Pull complete
701d7cb4751e: Pull complete
ae052802ba8f: Pull complete
474572a6c946: Pull complete
Digest: sha256:1465ec82b77534eb4687093fff91c752ac655d4ed1fb7e7b23bb6e3905a1ef18
Status: Downloaded newer image for goharbor/harbor-log:v1.7.1
Pulling registry (goharbor/registry-photon:v2.6.2-v1.7.1)...
v2.6.2-v1.7.1: Pulling from goharbor/registry-photon
321a8da5ee1f: Already exists
427e471dc5bb: Pull complete
79d644c380a9: Pull complete
d1ee69ba441f: Pull complete
13ee399ae5e6: Pull complete
52da6cf3d71f: Pull complete
e6dfe8d3336d: Pull complete
2261e5dd4591: Pull complete
Digest: sha256:dccc66572458001ed3b8f8ead0f0a89f0455747992528bafb857ed031bae07dc
Status: Downloaded newer image for goharbor/registry-photon:v2.6.2-v1.7.1
Pulling registryctl (goharbor/harbor-registryctl:v1.7.1)...
v1.7.1: Pulling from goharbor/harbor-registryctl
321a8da5ee1f: Already exists
60ab2a220157: Pull complete
685cb36a4aa6: Pull complete
6ab9cbb7c05b: Pull complete
d66f51b51c32: Pull complete
152d893b8817: Pull complete
Digest: sha256:de4b9c6684b7005379df6c48c05d2884c6b3cced0c98f8814c4506d71f781b9c
Status: Downloaded newer image for goharbor/harbor-registryctl:v1.7.1
Pulling postgresql (goharbor/harbor-db:v1.7.1)...
v1.7.1: Pulling from goharbor/harbor-db
321a8da5ee1f: Already exists
3b62caa7690c: Pull complete
0c0b8f8af809: Pull complete
68db7c777555: Pull complete
810390407c8c: Pull complete
d99f5e0b551e: Pull complete
0dedd5da1f5d: Pull complete
5e156cfb841f: Pull complete
0433d5b9e1ad: Pull complete
Digest: sha256:6031b1ed9337c3af78e627ecd45351e0e0d630b83cf45e1c924cb3e5b006cb44
Status: Downloaded newer image for goharbor/harbor-db:v1.7.1
Pulling adminserver (goharbor/harbor-adminserver:v1.7.1)...
v1.7.1: Pulling from goharbor/harbor-adminserver
321a8da5ee1f: Already exists
3235adc5dfba: Pull complete
36df358268ae: Pull complete
f07cf44733c3: Pull complete
153223fc88f2: Pull complete
Digest: sha256:5a539a2c733ca9efcd62d4561b36ea93d55436c5a86825b8e43ce8303a7a0752
Status: Downloaded newer image for goharbor/harbor-adminserver:v1.7.1
Pulling core (goharbor/harbor-core:v1.7.1)...
v1.7.1: Pulling from goharbor/harbor-core
321a8da5ee1f: Already exists
95d433145bab: Pull complete
49d3e2a9635a: Pull complete
6a4cbc768efe: Pull complete
7e7d30cebeb5: Pull complete
Digest: sha256:2791572f21aeaa7e62d3ee90b5b7ced3903633d9809d19fa32d3a524d580fc12
Status: Downloaded newer image for goharbor/harbor-core:v1.7.1
Pulling portal (goharbor/harbor-portal:v1.7.1)...
v1.7.1: Pulling from goharbor/harbor-portal
321a8da5ee1f: Already exists
0c2edbea17ee: Pull complete
35f0e6ee2803: Pull complete
815b36cabaa4: Pull complete
Digest: sha256:37a16e2ab4dc1499b25ce4a3f42c34a3c524fcfcd31f7433a459a738d4cec3b6
Status: Downloaded newer image for goharbor/harbor-portal:v1.7.1
Pulling redis (goharbor/redis-photon:v1.7.1)...
v1.7.1: Pulling from goharbor/redis-photon
321a8da5ee1f: Already exists
e37a237fdce1: Pull complete
a533db83c439: Pull complete
60f1956f70fa: Pull complete
c7eecf8b746b: Pull complete
Digest: sha256:9a10e8d0c3640c0207d94409fc61783643a2f5d866d4e1136c0718b3a5ac3015
Status: Downloaded newer image for goharbor/redis-photon:v1.7.1
Pulling jobservice (goharbor/harbor-jobservice:v1.7.1)...
v1.7.1: Pulling from goharbor/harbor-jobservice
321a8da5ee1f: Already exists
4809bd624b7e: Pull complete
889c696c8f56: Pull complete
72d181b0302b: Pull complete
Digest: sha256:c6706d51a3476235d8e801806141aa8e7279608268fca8be8ccd2e74987db093
Status: Downloaded newer image for goharbor/harbor-jobservice:v1.7.1
Pulling proxy (goharbor/nginx-photon:v1.7.1)...
v1.7.1: Pulling from goharbor/nginx-photon
321a8da5ee1f: Already exists
044755eb163c: Pull complete
Digest: sha256:c941c386eb99613b4c7481b9e433372bfac07beddb52a4e73dd7356ac8373189
Status: Downloaded newer image for goharbor/nginx-photon:v1.7.1
Creating harbor-log ... done
Creating harbor-adminserver ... done
Creating registryctl ... done
Creating harbor-db ... done
Creating registry ... done
Creating redis ... done
Creating harbor-core ... done
Creating harbor-portal ... done
Creating harbor-jobservice ... done
Creating nginx ... done

✔ ----Harbor has been installed and started successfully.----

Now you should be able to visit the admin portal at http://192.168.1.130.
For more details, please visit https://github.com/goharbor/harbor .

Harbor启动/停止/重启

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
28
29
30
31
32
33
34
35
36
37
38
# 如果某个Harbor组件启动失败,可以在日志目录/var/log/harbor下查看具体的日志信息,进一步定位启动失败的原因
# 启动时Harbor默认会监听宿主机的80、443、4443端口,启动Harbor之前必须确保宿主机的80、443、4443端口不被占用,否则Harbor相关组件会启动失败。

# 查看Harbor容器的运行状态
# docker ps

# 或者通过docker-compose查看,此时需要进入Harbor安装脚本所在的目录里执行相关命令
# cd /usr/local/harbor

# 查看Harbor容器的运行状态
# docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------------------------------------------------
harbor-adminserver /harbor/start.sh Up (healthy)
harbor-core /harbor/start.sh Up (healthy)
harbor-db /entrypoint.sh postgres Up (healthy) 5432/tcp
harbor-jobservice /harbor/start.sh Up
harbor-log /bin/sh -c /usr/local/bin/ ... Up (healthy) 127.0.0.1:1514->10514/tcp
harbor-portal nginx -g daemon off; Up (healthy) 80/tcp
nginx nginx -g daemon off; Up (healthy) 0.0.0.0:443->443/tcp, 0.0.0.0:4443->4443/tcp, 0.0.0.0:80->80/tcp
redis docker-entrypoint.sh redis ... Up 6379/tcp
registry /entrypoint.sh /etc/regist ... Up (healthy) 5000/tcp
registryctl /harbor/start.sh Up (healthy)

# 启动Harbor容器
# docker-compose start

# 停止Harbor容器
# docker-compose stop

# 重启Harbor容器
# docker-compose restart

# 停止并删除Harbor容器,加上-v参数可以同时移除挂载在容器上的目录
# docker-compose down

# 创建并启动Harbo容器,参数“-d”表示后台运行命令
# docker-compose up -d

Harbor测试访问

浏览器输入以下地址或者域名访问Harbor的Web界面,默认账号密码:admin/Harbor12345
http://192.168.1.130
harbor-web-ui

将本地镜像Push到Harbor

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
28
29
30
31
# 配置Docker客户端允许使用Http协议,如果Nginx更改了的端口映射,需要在以下IP地址后面指定具体的端口号
# vim /etc/docker/daemon.json
{
"insecure-registries":["192.168.1.130"]
}

# 重新加载Docker的配置文件
# systemctl daemon-reload

# 重启Docker
# systemctl restart docker

# 拉取Docker官方的Centos镜像
# docker pull centos:latest

# 查看镜像列表
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 1e1148e4cc2c 7 weeks ago 202MB
....

# 登录Harbor Registry,回车后输入admin用户的帐号信息(admin/Harbor12345)
# docker login 192.168.1.130

# 如果不使用默认项目名library,则需要使用admin用户提前登录Harbor的Web界面,手动创建新项目后再进行Push操作
# 给镜像打上相应的标签, 注意标签格式: ip/{project-name}/{image-name}[:tag]
# 项目library只有admin有写的权限
# docker tag centos:latest 192.168.1.130/library/centos:1.0

# 将本地镜像Push到Harbor
# docker push 192.168.1.130/library/centos:1.0

将Harbor镜像Pull到本地

1
2
3
4
5
6
7
8
9
# 删除上面创建的镜像
# docker rmi centos
# docker rm 192.168.1.130/library/centos:1.0

# 将Harbor镜像Pull到本地
# docker pull 192.168.1.130/library/centos:1.0

# 查看镜像列表
# docker ps

Harbor安装后更改Nginx的端口映射

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
28
29
30
31
32
33
34
35
# 进入Harbor的安装目录
# cd /usr/local/harbor

# 停止并删除Harbor容器,加上-v参数可以同时移除挂载在容器上的目录
# docker-compose down

# 编辑compose的配置文件,修改Nginx的80端口映射
# vim docker-compose.yml
ports:
- 8082:80
- 443:443
- 4443:4443

# 编辑Harbor的配置文件,修改hostname加上指定的端口号
# vim harbor.cfg
hostname = 192.168.1.130:8082

# 重新生成配置文件
# prepare

# 创建并启动Harbor容器
# docker-compose up -d

# 查看Harbor的容器列表,发现Nginx的端口映射已经更改成功
# docker-compose ps
harbor-adminserver /harbor/start.sh Up (health: starting)
harbor-core /harbor/start.sh Up (health: starting)
harbor-db /entrypoint.sh postgres Up (health: starting) 5432/tcp
harbor-jobservice /harbor/start.sh Up
harbor-log /bin/sh -c /usr/local/bin/ ... Up (healthy) 127.0.0.1:1514->10514/tcp
harbor-portal nginx -g daemon off; Up (health: starting) 80/tcp
nginx nginx -g daemon off; Up (health: starting) 0.0.0.0:443->443/tcp, 0.0.0.0:4443->4443/tcp, 0.0.0.0:8082->80/tcp
redis docker-entrypoint.sh redis ... Up 6379/tcp
registry /entrypoint.sh /etc/regist ... Up (health: starting) 5000/tcp
registryctl /harbor/start.sh Up (health: starting)

生成TLS证书,用于Harbor配置Https

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
28
29
30
31
32
33
34
35
36
37
# 下面以IP:192.168.1.130为例子,实际操作中将命令中的IP地址修改为自己的IP地址即可

# 创建存放证书的临时目录
# mkdir ~/cert
# cd ~/cert

# 创建自签名根证书
# openssl req \
-newkey rsa:4096 -nodes -sha256 -keyout ca.key \
-x509 -days 10000 -out ca.crt \
-subj "/C=CN/ST=Guangdong/L=Shenzhen/O=test_company/OU=IT/CN=test/emailAddress=test@qq.com"

# ls
ca.crt ca.key

# 产生证书签名请求
# openssl req \
-newkey rsa:4096 -nodes -sha256 -keyout harbor-registry.key \
-out harbor-registry.csr \
-subj "/C=CN/ST=Guangdong/L=Shenzhen/O=test_company/OU=IT/CN=192.168.1.130/emailAddress=test@qq.com"

# ls
ca.crt ca.key harbor-registry.csr harbor-registry.key

# 为Registry主机产生证书
# echo subjectAltName = IP:192.168.1.130 > extfile.cnf
# openssl x509 -req -days 10000 -in harbor-registry.csr -CA ca.crt -CAkey ca.key -CAcreateserial -extfile extfile.cnf -out harbor-registry.crt

# ls
ca.crt ca.key ca.srl extfile.cnf harbor-registry.crt harbor-registry.csr harbor-registry.key

# 创建Harbor的证书目录
# mkdir -p /opt/cert

# 拷贝harbor-registry证书到Harbor的证书目录
# cp harbor-registry.crt /opt/cert/
# cp harbor-registry.key /opt/cert/

Harbor安装后配置Https

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
28
29
# 进入Harbor的安装目录
# cd /usr/local/harbor

# 停止并删除Harbor容器,加上-v参数可以同时移除挂载在容器上的目录
# docker-compose down

# 修改harbor.cfg配置文件
# vim /usr/local/harbor/harbor.cfg
ui_url_protocol = https
hostname = 192.168.1.130
ssl_cert = /opt/cert/harbor-registry.crt
ssl_cert_key = /opt/cert/harbor-registry.key

# 重新生成配置文件
# ./prepare

# 让Docker客户端默认使用Https协议访问Registry,需要去掉“insecure-registries”相关配置项
# 查看daemon.json文件中是否有"insecure-registries":["192.168.1.130"],如果有则将其删除掉
# vim /etc/docker/daemon.json
{"insecure-registries":[""]}

# 重新加载Docker的配置文件
# systemctl daemon-reload

# 重启Docker
# systemctl restart docker

# 创建并启动Harbor容器
# docker-compose up -d

测试通过Https协议访问Harbor

  • 通过浏览器访问
    这里首先需要将上面产生的~/cert/ca.crt导入到浏览器的受信任的根证书中,然后就可以通过Https协议访问Harbor的Web界面了,但不能保证所有浏览器都支持。访问地址是:https://192.168.1.130

  • 通过Docker命令来访问

    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
    # 创建Docker的证书目录,目录名称是IP地址,需要根据自己的情况进行修改
    # 特别注意,如果Nginx的443端口映射到了其他端口,以下命令中的目录名称都需要带上具体的Https端口号,例如 "mkdir -p /etc/docker/certs.d/192.168.1.130:8443"
    # mkdir -p /etc/docker/certs.d/192.168.1.130

    # 将上面产生的ca.crt拷贝到Docker的证书目录下
    # cp ~/cert/ca.crt /etc/docker/certs.d/192.168.1.130

    # 重启Docker
    # systemctl restart docker

    # 启动Harbor容器
    # docker-compose up -d

    # 登录Harbor Registry,回车后输入admin用户的帐号信息(admin/Harbor12345)
    # docker login 192.168.1.130

    # 查看镜像列表
    # docker images
    REPOSITORY TAG IMAGE ID CREATED SIZE
    centos latest 1e1148e4cc2c 7 weeks ago 202MB
    ....

    # 给本地镜像打上标签
    # docker tag centos:latest 192.168.1.130/library/centos:1.0

    # 将本地镜像Push到Harbor
    # docker push 192.168.1.130/library/centos:1.0

Harbor的坑

安装Harbor的时候,不要更改数据库默认密码,包括Postgresql、Redis,否则Harbor相关组件很有可能启动失败,导致Web界面显示”502 Gateway”错误

参考博客