Harbor高可用理论及实践(汇聚篇)

一、理论概述

什么是harbor

Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,可以用来构建企业内部的Docker镜像仓库。

harbor是基于docker registry进行了相应的企业级扩展,从而获得了更加广泛的应用,新特性包括:管理用户界面,基于角色的访问控制 ,AD/LDAP集成以及审计日志等。

harbor要解决的问题

以Docker为代表的容器技术的出现,改变了传统的交付方式。通过把业务及其依赖的环境打包进Docker镜像,解决了开发环境和生产环境的差异问题,提升了业务交付的效率。如何高效地管理和分发Docker镜像?是众多企业需要考虑的问题。

有了docker自带的registry为什么还要用harbor

  1. harbor的安全机制

可以根据角色灵活的进行权限控制,如访客只需给pull权限即可

  1. harbor的镜像同步机制

为什么需要镜像同步

1. 对系统稳定性要求高,需要多个仓库保证高可用性

2. 更常用的场景是,在企业级软件环境中,会在软件开发的不同阶段存在不同的镜像仓库

传统镜像同步方式是采用RSYNC服务来定义两个仓库之间的镜像数据同步!!

harbor同步机制

1. 采用用Harbor自己的API来进行镜像下载和传输,作到与底层存储环境解耦。 

2. 利用任务调度和监控机制进行复制任务的管理,保障复制任务的健壮性。在同步过程中,如果源镜像已删除,Harbor会自动同步删除远端的镜像。在镜像同步复制的过程中,Harbor会监控整个复制过程,遇到网络等错误,会自动重试。 


3. 提供复制策略机制保证项目级的复制需求。在Harbor中,可以在项目中创建复制策略,来实现对镜像的同步。与Docker Registry的不同之处在于,Harbor的复制是推(PUSH)的策略,由源端发起,而Docker Registry的复制是拉(PULL)的策略,由目标端发起。 
  1. 可利用图形界面进行镜像等等的管理

参考文章

  1. 提供分层传输机制,优化网络传输

Docker镜像是是分层的,而如果每次传输都使用全量文件(所以用FTP的方式并不适合),显然不经济。必须提供识别分层传输的机制,以层的UUID为标识,确定传输的对象。

harbor的架构组件

  • Harbor在架构上主要由五个组件构成:
1. Proxy:Harbor的registry,UI, token等服务,通过一个前置的反向代理统一接收浏览器、Docker客户端的请求,并将请求转发给后端不同的服务。

2. Registry: 负责储存Docker镜像,并处理docker push/pull 命令。由于我们要对用户进行访问控制,即不同用户对Dockerimage有不同的读写权限,Registry会指向一个token服务,强制用户的每次docker pull/push请求都要携带一个合法的token,Registry会通过公钥对token 进行解密验证。

3. Core services: 这是Harbor的核心功能,主要提供以下服务:

    UI:提供图形化界面,帮助用户管理registry上的镜像(image), 并对用户进行授权。

    webhook:为了及时获取registry 上image状态变化的情况, 在Registry上配置webhook,把状态变化传递给UI模块。

    token 服务:负责根据用户权限给每个docker push/pull命令签发token. Docker 客户端向Regiøstry服务发起的请求,如果不包含token,会被重定向到这里,获得token后再重新向Registry进行请求。

4. Database:为core services提供数据库服务,负责储存用户权限、审计日志、Dockerimage分组信息等数据。

5. Log collector:为了帮助监控Harbor运行,负责收集其他组件的log,供日后进行分析。
  • Harbor每个组件实现方式

Harbor的每个组件都是以Docker容器的形式构建的,因此很自然地,我们使用Docker Compose来对它进行部署。

在源代码中(https://github.com/vmware/harbor), 用于部署Harbor的Docker Compose 模板位于/Deployer/docker-compose.yml. 打开这个模板文件,会发现Harbor由5个容器组成:

1. proxy:由Nginx 服务器构成的反向代理。

2. registry:由Docker官方的开源registry 镜像构成的容器实例。

3. ui: 即架构中的coreservices, 构成此容器的代码是Harbor项目的主体。

4. mysql: 由官方MySql镜像构成的数据库容器。

5. log: 运行着rsyslogd的容器,通过log-driver的形式收集其他容器的日志。

这几个容器通过Docker link的形式连接在一起,这样,在容器之间可以通过容器名字互相访问。对终端用户而言,只需要暴露proxy (即Nginx)的服务端口。

Harbor工作原理

由于只说干货比较抽象,我们用具体的命令作为表现形式研究工作流程

  • docker login登录命令

假设我们将Harbor部署在IP 为192.168.1.10的机器上。用户通过docker login命令向这个Harbor服务发起登录请求:

docker login 192.168.1.10

当用户输入所需信息并点击回车后,Docker 客户端会向地址 “192.168.1.10/v2/” 发出HTTP GET请求。 Harbor的各个容器会通过以下步骤处理:

1.. 1.10机器上收到该请求,会有映射到宿主机80端口的容器接收到。根据匹配规则,容器中Nginx将请求转发给registry容器;

2.. registry容器,由于基于token认证,registry返回错误代码401,提示docker客户端访问token去访问token服务绑定的URL。在harbor中,这个URL指向的是CoreServices(核心服务组件);

3.. Docker  客户端在接到这个错误代码后,会向token服务的URL发出请求,并根据HTTP协议的BasicAuthentication规范,将用户名密码组合并编码,放在请求头部(header);

4.. 这个请求通过1.10:80发送到proxy容器后,Nginx根据规则吧请求转发给UI容器,UI容器监听token服务网址的处理程序,接收到请求后,将请求头解码,得到了用户名和密码;

5.. 得到用户名、密码后,UI容器中的代码会查询数据库,将用户名、密码与mysql容器中的数据进行比对。比对成功的话,UI容器返回表示成功状态吗,用秘钥生成token,放在响应体中返回给docker客户端

docker push 192.168.1.10/library/hello-word

1.. docker客户端重复login的过程,首先发送请求到registry,之后得到token服务的地址;

2.. 之后,docker客户端在访问UI容器的token服务时会提供额外的信息,指明它要申请一个对library/hello-word进行push操作的token;

3.. token服务在经过Nginx转发得到了这个请求后,访问数据库合适当前用户是否有权限对该image进行push操作。如果有权限,会把image信息以及push动作进行编码,并且用私钥签名,生成token返回给docker客户端

4.. 得到token之后docker客户端将token放在请求头部,向registry发出请求,师徒开始推送image。registry收到请求后会用公钥解码token并且进行核对,一切成功后,image传输就开始了

参考文章

harbor几个高可用方案参考文章——理论

二、部署harbor及其主从复制

环境

主机名 IP地址 角色
harbor1 192.168.111.3 harbor仓库
harbor2 192.168.111.4 harbor备份仓库
client 192.168.111.5 docker客户端

本案例搭建harbor镜像仓库的高可用,但是由于harbor并没有相应官方方案推荐,本案例只是简单采用主主高可用,并且基于keepalived的VIP实现

  • 部署docker
  • 三台机器部署社区版docker
[root@localhost ~]# yum -y install yum-utils device-mapper-persistent-data lvm2
#安装依赖

[root@localhost ~]# wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo
#下载docker的repo

[root@localhost ~]# yum -y install docker-ce

[root@localhost ~]# mkdir /etc/docker
[root@localhost ~]# vim /etc/docker/daemon.json

{
        "registry-mirrors":["https://*******.mirror.aliyuncs.com"]
}
#阿里云镜像加速

#systemctl start docker

地址需要个人前往阿里云获得,参考这篇文档

在本案例中,一开始我初心是想要部署一个基于https的高可用harbor镜像仓库,但是后来生成证书时发现应该是必须要用域名才可以,但是如果用域名的话,我的高可用VIP如何实现,故去除https的配置,用普通连接

  • 部署harbor
  • 两个harbor部署

从 github harbor 官网 release 页面下载指定版本的安装包。

1、在线安装包
    $ wget https://github.com/vmware/harbor/releases/download/v1.1.2/harbor-online-installer-v1.1.2.tgz
    $ tar xvf harbor-online-installer-v1.1.2.tgz
2、离线安装包
    $ wget https://github.com/vmware/harbor/releases/download/v1.1.2/harbor-offline-installer-v1.1.2.tgz
    $ tar xvf harbor-offline-installer-v1.1.2.tgz

[root@harbor1 ~]# mv harbor /usr/local/
[root@harbor1 ~]# vim /usr/local/harbor/harbor.cfg
hostname = 192.168.111.3
ui_url_protocol = http


[root@harbor2 cert]# vim /usr/local/harbor/harbor.cfg
hostname = 192.168.111.4
ui_url_protocol = http
  • 两个harbor部署docker-compose
curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m`
[root@harbor2 ~]# mv docker-compose /usr/local/bin/docker-compose
[root@harbor2 ~]# chmod +x !$
chmod +x /usr/local/bin/docker-compose
  • 启动配置
[root@harbor2 ~]# sh /usr/local/harbor/install.sh 
[root@harbor1 ~]# sh /usr/local/harbor/install.sh
#此过程比较慢

浏览器访问测试https://192.168.111.3/https://192.168.111.4/

  • 测试
[root@localhost anchors]# docker pull cirros

[root@localhost anchors]# docker login 192.168.111.3
Username: admin
Password: 

[root@localhost anchors]# docker tag cirros:latest 192.168.111.3/joinbest1/cirros:test1
[root@localhost anchors]# docker push 192.168.111.3/joinbest1/cirros:test1
The push refers to repository [192.168.111.3/joinbest1/cirros]
abbd6d6ac643: Pushed 
75b99987219d: Pushed 
0cc237193a30: Pushed 
test1: digest: sha256:96137d51e0e46006243fa2403723eb47f67818802d1175b5cde7eaa7f19446bd size: 943

  • 部署主主复制(实现任何一个镜像仓库有改动,都要同步到另一侧)

两端都是如此

这时,在对端harbor已经可以看到刚才创建的测试镜像了。

由于部署是主主,对端harbor仓库也要进行以上操作

  • 小测试
[root@localhost anchors]# docker tag cirros:latest 192.168.111.4/joinbest1/cirros:test2
[root@localhost anchors]# docker push 192.168.111.4/joinbest1/cirros:test2 
The push refers to repository [192.168.111.4/joinbest1/cirros]
abbd6d6ac643: Layer already exists 
75b99987219d: Layer already exists 
0cc237193a30: Layer already exists 
test2: digest: sha256:96137d51e0e46006243fa2403723eb47f67818802d1175b5cde7eaa7f19446bd size: 943
#上面的部分输出含义是该层数据已经存在,但是不影响,因为在harbor中并不是每个镜像都要全量上传,而是分层存储,更利于节省空间,以层的UUID为标识

这时两个仓库上都有了该镜像

也就是说,无论我再任何一个仓库操作,都会同步给其它的任何仓库

  • 部署keepalived高可用实现

本案例太多因素出自笔者主观,也许可靠,也许不可靠

两台harbor安装keepalived
#yum -y install keepalived

[root@harbor1 harbor]# cat /etc/keepalived/keepalived.conf 
! Configuration File for keepalived

global_defs {
   notification_email {
     [email protected]
     [email protected]
     [email protected]
   }
   script_user root
   #需要制定脚本运行用户
   notification_email_from [email protected]
   smtp_server 192.168.200.1
   smtp_connect_timeout 30
   router_id LVS_DEVEL
   vrrp_skip_check_adv_addr
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}
vrrp_script check_harbor {
    script "/opt/harbor.sh"
    interval 2
    weight 20

}
#使用监控脚本来监控自身80端口,因为他是整个harbor的访问入口

vrrp_instance VI_1 {
    state MASTER
    interface ens32
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.111.100/32 dev ens32 label ens32:2
    }
    track_script {
    check_harbor
    } 
}

--------------注意修改关键配置项------------
[root@harbor2 harbor]# cat /etc/keepalived/keepalived.conf 
! Configuration File for keepalived

global_defs {
   notification_email {
     [email protected]
     [email protected]
     [email protected]
   }
   script_user root
   #需要制定脚本运行用户
   notification_email_from [email protected]
   smtp_server 192.168.200.1
   smtp_connect_timeout 30
   router_id LVS_DEVEL1
   vrrp_skip_check_adv_addr
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}
vrrp_script check_harbor {
    script "/opt/harbor.sh"
    interval 2
    weight 20

}

vrrp_instance VI_1 {
    state BACKUP
    interface ens32
    virtual_router_id 51
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.111.100/32 dev ens32 label ens32:2
    }
    track_script {
    check_harbor
    } 
}


----------故障切换脚本-----------
[root@harbor1 harbor]# vim /opt/harbor.sh 

#!/bin/bash

sum=`netstat -lnpt | grep -wo 80 | wc -l`

if [ $sum -eq 0 ]; then
        pkill -9 keepalived
fi

[root@harbor1 harbor]# chmod +x /opt/harbor.sh 


[root@localhost anchors]# vim /usr/lib/systemd/system/docker.service 
#主机3

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock  --insecure-registry 192.168.111.3 --insecure-registry 192.168.111.4 --insecure-registry 192.168.111.100
#将VIP添加入可不安全访问

[root@localhost anchors]# docker login 192.168.111.100
Authenticating with existing credentials...
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
#目前是可以访问

[root@localhost anchors]# docker images
REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE
192.168.111.4/joinbest1/cirros   test2               bc94bceaae77        5 months ago        10.3MB
cirros                           latest              bc94bceaae77        5 months ago        10.3MB
192.168.111.3/joinbest1/cirros   test1               bc94bceaae77        5 months ago        10.3MB
#镜像也正常

[root@harbor1 harbor]# docker-compose stop
Stopping harbor-jobservice  ... done
Stopping nginx              ... done
Stopping harbor-ui          ... done
Stopping harbor-adminserver ... done
Stopping redis              ... done
Stopping registry           ... done
Stopping harbor-db          ... done
Stopping harbor-log         ... done
#测试故障切换

[root@harbor2 harbor]# ip a | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    inet 192.168.111.4/24 brd 192.168.111.255 scope global noprefixroute ens32
    inet 192.168.111.100/32 scope global ens32:2
#ip切过来了
  • 测试keepalived的VIP的写入是否同步其余仓库
[root@localhost anchors]# docker tag cirros:latest 192.168.111.100/joinbest1/cirros:test3
[root@localhost anchors]# docker push 192.168.111.100/joinbest1/cirros:test3 
The push refers to repository [192.168.111.100/joinbest1/cirros]
abbd6d6ac643: Layer already exists 
75b99987219d: Layer already exists 
0cc237193a30: Layer already exists 
test3: digest: sha256:96137d51e0e46006243fa2403723eb47f67818802d1175b5cde7eaa7f19446bd size: 943
#目前正常写入

#通过web界面对111.3和111.4的joinbest1仓库看看镜像是否同步
#这里先把刚才测试关闭的harbor1仓库给启动
Starting log         ... done
Starting registry    ... done
Starting mysql       ... done
Starting adminserver ... done
Starting ui          ... done
Starting redis       ... done
Starting jobservice  ... done
Starting proxy       ... done
#好,我这里是全部可以正常同步,就算是刚才客户端上传时,harbor1是宕机的,但是重新启动后,还是会进行同步,不过应该是有触发机制,我后来又创建了一个test4,刚开始harbor1是没有test3的,我上传test4之后出发了同步机制,这时harbor1的镜像仓库也是正常工作了,便将3.4一起同步过去了

三、总结

  • 1.本高可用方案,生产环境有待考量
  • 2.刚开始是尝试着做基于https的连接,但是因为诸多因素:ssl工具或许不支持基于ip的加密只支持域名,但是要配合keepalived肯定是需要ip的,故舍弃https;经过思考,如果只是做私有仓库使用的话,那么不使用https或许也是非常可行的。
  • 3.keepalived很好用,很有感触
  • 4.本案例环境都是在测试环境,在生产环境的相应压力中,并不知道会有怎么样的后果

报错

raise Exception("Error: the protocol must be https when Harbor is deployed with Notary")
Exception: Error: the protocol must be https when Harbor is deployed with Notary
#我输入得命令是sh install.sh --with-notary --with-clair
#其中使用--with-notary含义是启用镜像签名;必须是https才可以,把该选项去掉即可·




[root@localhost anchors]# docker login 192.168.111.3
Username: admin
Password: 
Error response from daemon: Get https://192.168.111.3/v2/: dial tcp 192.168.111.3:443: connect: connection refused
#客户端连接报错,默认使用的是https我需要修改为可以使用http来进行连接

[root@localhost anchors]# vim /usr/lib/systemd/system/docker.service 
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock  --insecure-registry 192.168.111.3 --insecure-registry 192.168.111.4

猜你喜欢

转载自www.cnblogs.com/joinbestgo/p/11114828.html