个人亲自录制全套DevOps系列实战教程 :
手把手教你玩转DevOps全栈技术
讲完Nexus3再来看下harbor,其实大同小异,只不过harbor的管理要比Nexus3更专业、功能更完善,大家按需选择即可,Nexus的优势是他能和Maven仓库复用同一个服务器。
官网:https://goharbor.io/docs/2.6.0/install-config/installation-prereqs/
其实Harbor更适合拿一台虚拟机或物理机来安装,当然也可以集成到k8s,但如果是单台docker服务下安装,就有些不太合适了,
不合适的原因是Harbor需要docker-engine、docker-compose、openssl
的支持,即要在装有这些工具包机器上安装Harbor。
有3种安装方案:
- 直接安装在宿主机[复用docker环境][演示https方式]
- 将harbor安装到容器:手动构建镜像,镜像中安装harbor需要的环境(docker环境等)[演示http方式]
- 将harbor安装到容器:类似jenkins那样,将需要的环境从宿主机映射到容器(该方式我们不再演示)
下边我们先来演示容器中单独部署Harbor仓库
下载安装包:从官方(https://github.com/goharbor/harbor/releases)下载harbor稳定版,
离线安装包:harbor-offline-installer-v2.6.2.tgz,大约有769M,如果大家无法访问可以找我索取。
目录:我们依然按照老规矩,将文件拷贝到/docker/harbor目录,目录结构如下,所有资料下文都有。
1.将离线安装包拷贝到目标目录:
/docker/harbor/harbor-offline-installer-v2.6.2.tgz
2.事先准备一个harbor的配置文件:harbor.yml
值得关注的属性如下,其他忽略【此处只需要修改hostname和注释掉https,其他默认即可】
# 因为我要宿主机映射访问,所以此处使用宿主机ip,即访问harbor时使用10.10.1.199 hostname: 10.10.1.199 http: port: 9090 # 我们优先演示http协议,所以注释掉https(注释掉) #https: #port: 9443 #certificate: /cert/certificate/path # 生成的证书目录 #private_key: /cert/private/key/path # harbor管理员admin的密码(保持默认) harbor_admin_password: Harbor12345 # 默认存储数据目录 data_volume: /data # 日志配置:日志级别、输出目录(保持默认) log: level: info local: location: /var/log/harbor
3.准备一个docker的配置文件:/etc/docker/daemon.json
主要是把镜像加速和信任列表创建好,否则最好映射到宿主,因为毕竟是容器,删除后容易丢失。
"registry-mirrors": [ "https://mtu7rhzd.mirror.aliyuncs.com", "http://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn", "https://registry.docker-cn.com" ], "insecure-registries":[ "10.10.1.199:9082", "10.10.1.199:9083", "10.10.1.199:9090" ]
4.创建Dockerfile镜像文件:/docker/harbor/Dockerfile
此处使用阿里云的yum源安装docker,也可以用官方:https://download.docker.com/linux/centos/docker-ce.repo
FROM centos:centos7 LABEL maintainer="[email protected]" USER root COPY * /docker/ WORKDIR /usr/local RUN set -e \ && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' > /etc/timezone \ && mv -f /docker/harbor-offline-installer-v2.6.2.tgz . \ && tar -zxf harbor-offline-installer-v2.6.2.tgz \ && mv -f /docker/harbor.yml harbor \ && rm -f harbor-offline-installer-v2.6.2.tgz \ && yum install -y yum-utils \ && yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker->ce.repo \ && yum install -y docker-ce docker-ce-cli docker-compose-plugin \ && mkdir -p /etc/docker/ \ && mv -f /docker/daemon.json /etc/docker \ && mv -f /docker/start.sh harbor \ && chmod 777 harbor/start.sh \ && chmod 777 /etc/rc.d/rc.local \ && echo "echo '准备脚本执行...!'" >> /etc/rc.local \ && echo "sh /usr/local/harbor/start.sh" >> /etc/rc.local \ && echo "echo '脚本执行完毕!'" >> /etc/rc.local EXPOSE 9090 EXPOSE 9443 # 这里设置的会被docker-compose.yml中设置的替换掉 CMD ["/usr/sbin/init"]
大坑:
注意我们是基于centos7的镜像构建的,而我们容器内安装有docker-engine,那么肯定要通过systemctl启动服务,而systemctl是需要root权限的,虽然我们Dockerfile指定了USER root,但这只是容器的root,他对于宿主机来说仍然是普通用户,而我们需要启动容器时指定privileged: true
,这样容器内的root才真正具备root权限,但是正式因为当前基于centos7,而centos7的privileged: true设置是不起作用的,这在官方也提供了解决方法,但是太麻烦,我们采用另一种方式,就是容器启动前先执行/usr/sbin/init
脚本,即可解决。具体大家可以到官网查询,我这里就不多说了。
官方安装:https://hub.docker.com/_/centos -> 正文部分在解决Systemd的位置,可以自行选择。
第二个坑:
容器中我们安装了docker服务,那么肯定是要通过systemctl start docker来启动,但是要想让systemctl能执行就需要开启init进程,init进程必须在系统启动的时候开启,作为第一个进程,init无法在脚本中启动,因此只能是将容器的启动命令设置成/usr/sbin/init,然后将启动服务的命令写成脚本,再把执行脚本的命令写入/etc/rc.local中,这样就可以在centos7容器中使用systemctl启动服务了。
第三个坑:
按照上述操作其实还有个坑,就是发现放到rc.local的代码并没有执行,这是因为centos7开始/etc/rc.d/rc.local的权限变成了644,并没有执行权限,而我们修改的是/etc/rc.local,他是软连接到/etc/rc.d/rc.local,所以我们还需要给/etc/rc.d/rc.local授权 chmod +x /etc/rc.d/rc.local,并且我们要先检查一下rc.local服务是否启动,如果没启动还需要让他也向docker一样随机启动(systemctl status rc-local.service/systemctl enable rc-local.service)
5.容器启动脚本vi /docker/harbor/start.sh
#!/bin/bash logfile=/var/log/harbor/harbor-run.log set -e && systemctl start docker >> $logfile && /usr/local/harbor/prepare >> $logfile && >/usr/local/harbor/install.sh >> $logfile
6.创建docker-compose.yml编排脚本:vi /docker/harbor/docker-compose.yml
version: '3' services: harbor: build: context: . dockerfile: Dockerfile image: 'lij/harbor:2.6.2-centos7' restart: always container_name: 'harbor' hostname: 'harbor' user: root ports: - '9090:9090' - '9443:9443' networks: - 'exist-net-bloom' volumes: - '/docker/harbor/log:/var/log/harbor' - '/docker/harbor/data:/data' privileged: true networks: exist-net-bloom: external: name: devops
注意:
harbor的配置文件端口我们从80改成了9090,保持了和宿主机映射的端口一致,为什么呢?
经过我的实践,如果容器中使用80,宿主机使用9090,这样映射访问harbor的web页面是没问题的,但是通过docker login 10.10.1.199:9090时就会访问不到。
7.构建部署镜像
宿主机执行部署:./docker/harbor/docker-compose up -d --build
看到这样的日志表示启动完成,而容器内部其实是启动了多个容器:
8.验证:浏览器中输入10.10.1.199:9090
9.客户端登录试试:
宿主机中执行docker login 10.10.1.199:9090,结果却报错了
Error response from daemon: Get "https://10.10.1.199:9090/v2/": http: server gave HTTP response to HTTPS client
我们不是已经加入到信任列表了吗?==>注意我们是把harbor服务地址加入到了自身容器中,而没有加入到宿主机,而此时是使用宿主机访问,所以要加入到宿主机
加入后重启宿主机docker服务再试,就没问题了。
1.数字证书
这里我们使用openssl工具来生成证书,其实我们会经常遇到ssh-keygen、openssl、keytool,甚至有时候会用到puttygen,这里简单说明下他们的关系:
- ssh-keygen:是openssh提供的管理密钥证书的工具,即通过他生成的一般是符合ssh使用的证书格式;http://www.openssh.com/
- openssl:我们知道ssl/tls协议,那openssl顾名思义,是一个开源的用于加密和安全通讯的工具包,包括生成证书等功能;https://github.com/openssl/openssl
- keytool:他是JDK提供的一个密钥管理工具,也可以生成证书等;https://docs.oracle.com/javase/8/docs/technotes/tools/windows/keytool.html
- puttygen:他是putty这个软件提供的密钥管理工具https://puttygen.tech/index.php
完全参考官方文档安装:https://goharbor.io/docs/2.6.0/install-config/configure-https/
2.生成CA根证书
生产中我们需要到CA机构申请证书,而此时我们自己生成CA证书,自己给自己签发证书
# 生成RSA私钥:当前目录生成密钥长度为4096的RSA私钥,输出文件名为ca.key openssl genrsa -out ca.key 4096 # 生成证书:根据RSA私钥生成证书文件,通过key指定RSA私钥文件,out指定生成的证书文件名,其中subj中要指定申请证书的组织信息, # 主要将CN指定成harbor所在机器的域名即可,我们这里就是宿主机了,我们将宿主机域名定义成【omv.local】 openssl req -x509 -new -nodes -sha512 -days 3650 \ -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=omv.local" \ -key ca.key \ -out ca.crt
3.生成自签名证书:即服务端(harbor)要使用的证书
# 生成私钥:使用域名定义私钥文件名 openssl genrsa -out omv.local.key 4096 # 生成服务端证书签名请求:证书请求是向CA发起申请证书的数据格式 openssl req -sha512 -new \ -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=omv.local" \ -key omv.local.key \ -out omv.local.csr # 生成x509 v3扩展文件:根据官网要求执行 cat > v3.ext <<-EOF authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth vsubjectAltName = @alt_names [alt_names] DNS.1=omv.local DNS.2=omv EOF # 使用该v3.ext文件为你的Harbor主机生成证书,即将自己的证书申请,提交给CA,然后由CA生成证书,只不过此处CA是自己 openssl x509 -req -sha512 -days 3650 \ -extfile v3.ext \ -CA ca.crt -CAkey ca.key -CAcreateserial \ -in omv.local.csr \ -out omv.local.crt
4.将证书配置给Harbor和Docker(harbor所在机器的Docker服务)
# 将自签名证书配置给harbor,即拷贝对应的文件到harbor.yml中配置指定的路径 certificate: /docker/harbor/harbor/cert/omv.local.crt private_key: /docker/harbor/harbor/cert/omv.local.key # 将omv.local.crt转换成omv.local.cert,因为docker-engine将crt认为是证书,而cert认为是客户端证书,我们要使用客户端连接harbor openssl x509 -inform PEM -in omv.local.crt -out omv.local.cert # 将对应的证书拷贝到docker服务的证书目录(需要手动创建 mkdir -p /etc/docker/certs.d/omv.local/) # 注意创建的omv.local目录,只能通过https://ommv.local访问,如果端口不是443,则目录需要带上端口,如/etc/docker/certs.d/omv.local:9443/ cp omv.local.cert /etc/docker/certs.d/omv.local/ cp omv.local.key /etc/docker/certs.d/omv.local/ cp ca.crt /etc/docker/certs.d/omv.local/ # 重启docker服务 systemctl restart docker
5.启动harbor:将harbor.yml的https节点打开
./prepare
出错了:
提示我们目录或文件不存在:No such file or directory: ‘/hostfs/docker/harbor/harbor/data/cert/omv.local.key’
但是这个目录/hostfs哪来的?
我们知道了harbor是在docker容器中运行,那么prepare脚本应该也是去创建容器了,打开这个脚本,我们发现以下代码:# 显然是在发布容器,而hostfs正式容器内部的目录,要映射到外部目录/下 docker run --rm -v $input_dir:/input \ -v $data_path:/data \ -v $harbor_prepare_path:/compose_location \ -v $config_dir:/config \ -v /:/hostfs \ --privileged \ goharbor/prepare:v2.6.2 prepare $@ # 我们做一下改造:将我的证书目录映射过去 docker run --rm -v $input_dir:/input \ -v $data_path:/data \ -v $harbor_prepare_path:/compose_location \ -v $config_dir:/config \ -v /docker/harbor/harbor/data/cert:/hostfs/docker/harbor/harbor/data/cert \ --privileged \ goharbor/prepare:v2.6.2 prepare $@
# 进行启动 ./install.sh
问题:
该问题是启动harbor相关的nginx容器时遇到宿主机80端口被占用的情况,我是因为omv主机服务的端口用了80
有两种修改方法:
- 修改omv更换成其他端口
- 修改harbor的nginx容器成其他端口:很简单,打开harbor目录,此时因为运行了install.sh,已经生成了docker-compose.yml,我们打开找到位置修改端口即可
再启动
6.验证浏览器使用:
输入https://10.10.1.199,注意此时使用的是https,所以默认不输入端口默认使用443,而上边我们看到80和443都已放开,只不过没使用80端口
那没使用上边为什么报错呢?因为即便没使用但是我们做了映射啊,所以上边还有第3种方法,就是不映射80端口,只映射443端口即可,但是默认harbor各容器内部通信是使用http协议的,并且各容器并未配置link连接(由docker-compose.yml可知)
,所以关闭80映射是影响内部通信大家可以测试一下。如果想把内部通信方式改为https其实和harbor对外https修改方式大同小异,可参看官方文档,这里就不演示了。
内部通信https配置官方文档:https://goharbor.io/docs/2.6.0/install-config/configure-internal-tls/
因为我们使用的自签名证书,所以浏览器从服务端拿到证书后是无法通过已知的CA认证机构校验的,所以需要我们自己将证书加入到浏览器的信任列表,我们此处选择继续访问即可。
和docker容器方式一样可以正常访问,此处不截图了,避免重复。
7.验证Docker推送和拉取
docker的使用,这里和Nexus有些区别,harbor同nexus一样,都是需要我们自己创建仓库的,只不过nexus每个仓库我们可以单独指定端口,而harbor则不可以,
所以为了区分拉取/推送哪个仓库,我们需要打标签时加上namespace(即仓库名),而nexus则可以通过端口来区分。
我们先来创建一个仓库:从页面可知也可以创建代理仓库,此处我们选择公开,即允许匿名拉取。
进入仓库,可以看到有很多配置功能,这也是他比nexus强大的地方之一,比如webhooks可以对接harbor仓库的10几个事件通知,方便我们做监控。
进入"镜像仓库"选项卡,我们可以看到镜像列表,右侧有推送命令,大家可以自行查看
# 先来拉取一下busybox:注意我们使用https,所以要使用域名访问 # 如果大家域名不能访问需要将harbor服务器的hosts进行映射,即10.10.1.199 omv.local配上 docker pull omv.local/test/busybox:latest
# 从公网拉取镜像busybox docker pull busybox # 打私服标签 docker tag busybox:latest omv.local/test/busybox:v1.0 # 登录私服 docker login -u admin -p Harbor12345 omv.local # 推送镜像 docker push omv.local/test/busybox:v1.0 # 删除本地busybox镜像 docker rmi -f omv.local/test/busybox:v1.0 # 拉取镜像 docker pull omv.local/test/busybox:v1.0
疑惑1:
其实通过界面对比Nexus我们会有些疑惑,Nexus有group仓库,可以汇总所有仓库内容,方便拉取,那么harbor有吗?
为此harbor提供了机器人账号,可以通过创建机器人账号关联多个仓库,这样我们使用机器人账号就可以使用多个仓库的镜像。
疑惑2:
Nexus有proxy仓库,可以作为镜像代理仓库,harbor有吗?
harbor是从v2.1.1版本开始增加了这个功能,通过新建"目标"指定外网仓库,然后新建工程指定为代理,以此实现
注意:
拉取时我们除了要加项目名到url中还需要增加一个library名称空间,来表名使用代理仓库
如:docker pull omv.local/hub/library/busybox ,这样才可以
具体可见官方文档:https://goharbor.io/docs/2.6.0/administration/configure-proxy-cache/
8.Nexus和Harbor对比:
各有长处,大家自行选择即可,下边演示我使用的nexus。
- Nexus使用更加方便
- Harbor对镜像的管理更加强大