DevOps实战系列【第七章】:详解Docker私服Harbor篇

时间:2023-01-08 13:59:47

个人亲自录制全套DevOps系列实战教程 :​​手把手教你玩转DevOps全栈技术​

DevOps实战系列【第七章】:详解Docker私服Harbor篇

Harbor私服搭建

讲完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仓库

Docker容器部署[http协议版]


下载安装包:从官方(​​https://github.com/goharbor/harbor/releases​​​)下载harbor稳定版,
离线安装包:harbor-offline-installer-v2.6.2.tgz,大约有769M,如果大家无法访问可以找我索取。
目录:我们依然按照老规矩,将文件拷贝到/docker/harbor目录,目录结构如下,所有资料下文都有。

DevOps实战系列【第七章】:详解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="xxx@126.com"
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

DevOps实战系列【第七章】:详解Docker私服Harbor篇

看到这样的日志表示启动完成,而容器内部其实是启动了多个容器:

DevOps实战系列【第七章】:详解Docker私服Harbor篇

8.验证:浏览器中输入10.10.1.199:9090

输入默认用户admin,密码Harbor12345

DevOps实战系列【第七章】:详解Docker私服Harbor篇

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服务再试,就没问题了。

宿主机部署Harbor[https协议版]


1.数字证书

这里我们使用openssl工具来生成证书,其实我们会经常遇到ssh-keygen、openssl、keytool,甚至有时候会用到puttygen,这里简单说明下他们的关系:

完全参考官方文档安装:​​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

​出错了:​​​DevOps实战系列【第七章】:详解Docker私服Harbor篇

提示我们目录或文件不存在: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 $@

DevOps实战系列【第七章】:详解Docker私服Harbor篇

再次执行ok!

# 进行启动 ./install.sh

DevOps实战系列【第七章】:详解Docker私服Harbor篇


​问题:​​​该问题是启动harbor相关的nginx容器时遇到宿主机80端口被占用的情况,我是因为omv主机服务的端口用了80

​​​有两种修改方法:​

  • 修改omv更换成其他端口
  • 修改harbor的nginx容器成其他端口:很简单,打开harbor目录,​​此时因为运行了install.sh​​​,已经生成了docker-compose.yml,我们打开找到位置修改端口即可DevOps实战系列【第七章】:详解Docker私服Harbor篇
  • 再启动
  • DevOps实战系列【第七章】:详解Docker私服Harbor篇
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/​​​DevOps实战系列【第七章】:详解Docker私服Harbor篇

因为我们使用的自签名证书,所以浏览器从服务端拿到证书后是无法通过已知的CA认证机构校验的,所以需要我们自己将证书加入到浏览器的信任列表,我们此处选择继续访问即可。

和docker容器方式一样可以正常访问,此处不截图了,避免重复。

7.验证Docker推送和拉取

docker的使用,这里和Nexus有些区别,harbor同nexus一样,都是需要我们自己创建仓库的,只不过nexus每个仓库我们可以单独指定端口,而harbor则不可以,

所以为了区分拉取/推送哪个仓库,我们需要打标签时加上namespace(即仓库名),而nexus则可以通过端口来区分。

我们先来创建一个仓库:从页面可知也可以创建代理仓库,此处我们选择公开,即允许匿名拉取。

DevOps实战系列【第七章】:详解Docker私服Harbor篇

进入仓库,可以看到有很多配置功能,这也是他比nexus强大的地方之一,比如webhooks可以对接harbor仓库的10几个事件通知,方便我们做监控。

DevOps实战系列【第七章】:详解Docker私服Harbor篇

进入"镜像仓库"选项卡,我们可以看到镜像列表,右侧有推送命令,大家可以自行查看

DevOps实战系列【第七章】:详解Docker私服Harbor篇

# 先来拉取一下busybox:注意我们使用https,所以要使用域名访问
# 如果大家域名不能访问需要将harbor服务器的hosts进行映射,即10.10.1.199 omv.local配上
docker pull omv.local/test/busybox:latest

DevOps实战系列【第七章】:详解Docker私服Harbor篇

显然我们并不存在这样的镜像。

# 从公网拉取镜像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版本开始增加了这个功能,通过新建"目标"指定外网仓库,然后新建工程指定为代理,以此实现

DevOps实战系列【第七章】:详解Docker私服Harbor篇

​​​注意:​​​拉取时我们除了要加项目名到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对镜像的管理更加强大