1、先说说容器吧
**
简单来说,它就是个小工具,可以把你想跑的程序,库文件啊,配置文件都一起“打包”。然后,我们在任何一个计算机的节点上,都可以使用这个打好的包。有了容器,一个命令就能把你想跑的程序跑起来,做到了一次打包,就可以到处使用。 比如我们可以把整套zabbix环境(httpd+php+mysql+zabbix-server)一起打包,然后给它搬到其它机器上直接运行。
有人把Docker和容器划等号,其实不对,Docker只是一个容器工具,而真正的容器技术是LXC(Linux Container),Docker只是更加方便地将其展现出来。
容器就像上面图里的集装箱,每一个集装箱里面都是一个完整的应用,可以将其放到任意一台机器上运行起来。
2)容器与虚拟化
容器类似于虚拟化,但和虚拟化有本质区别
虚拟化会使用虚拟机监控程序模拟硬件,从而使多个操作系统能够并行运行。但这不如容器轻便。
Linux 容器在本机操作系统上运行,与所有容器共享该操作系统,因此应用和服务能够保持轻巧,并行化快速运行。Linux 容器是我们开发、部署和管理应用方式的又一次飞跃。
Linux 容器镜像提供了可移植性和版本控制,确保能够在开发人员的笔记本电脑上运行的应用,同样也能在生产环境中正常运行。
相较于虚拟机,Linux 容器在运行时所占用的资源更少,使用的是标准接口(启动、停止、环境变量等),并会与应用隔离开。
此外,作为包含多个容器的大型应用的一部分时更加易于管理,而且这些多容器应用可以跨多个云环境进行编排。
3)容器基本概念
LXC:
是Linux Contain的缩写,就是Linux容器,是一个基于Linux内核功能特性实现轻量级虚拟化的技术。注意,Docker/Podman等容器技术,都是在LXC基础之上开发的三方工具。
LXC可以在操作系统层次上为进程提供虚拟的执行环境,一个虚拟的执行环境就是一个容器。可以为容器绑定特定的cpu和memory节点,分配特定比例的cpu时间、IO时间,限
制可以使用的内存大小(包括内存和是swap空间),提供device访问控制,提供独立的namespace(网络、pid、ipc、mnt、uts)。
容器的隔离:
容器有效地将操作系统管理的资源划分到独立的组中,并把各个独立的组进行隔离,可以让各自的组占用独立的资源,完成自己独立的任务。
因为容器最终执行的命令也是调用统一的os api来实现,是基于整个os来实现的,所以不需要单独操作系统的编译系统、执行解释器,一切工作都是基于os 的基础上来完成的。
容器通过提供一种创建和进入容器的方式,让程序像跑在独立机器那样在容器中运行,并且相互之间不影响,而且还可以共享底层的资源。
容器的共享:
容器提供环境隔离的前提下,还提供了资源共享的机制,所以容器比真正kvm 虚拟机的资源要节省许多;
4)容器核心技术
chroot:
创建一个虚拟的根目录文件系统,其实质还是调用底层的文件系统,不过是建立一个虚拟的、可以跟其它容器的虚拟文件系统相互隔离、但共享底层的文件系统
namespace :
命名空间可以提供一个进程相互隔离的独立网络空间,不同的容器间进程pid可以相同,进程并不冲突影响,但可以共享底层的计算和存储资源
cgroups:
实现了对容器的资源分配和限制,比如给容器A分配4颗CPU,8G 内存,那这个容器最多用这么多的资源。如果内存超过8G ,会启动swap,效率降低,也可能会被调
度系统给kill掉
5)企业为什么要使用容器技术
提升效率:
容器可以快速移植,这就意味着企业在开发、部署阶段快速搭建开发、测试环境,并快速部署到生产环境里。
节省资源:
一台物理机上可以运行几百个甚至上千个容器,这如果换成传统的虚拟化技术,是根本做不到的。
节省运维成本:
使用主流的容器编排和管理工具(如,K8s),可以大大降低企业的运维成本,因为K8s本身集成了高可用、负载均衡、监控、自动修复等诸多功能,一旦上线,几乎不用花额外的运维成本。
2、跟容器相关的工具(只罗列具有代表性的)
1)Docker
Docker是一个开源的应用容器引擎,基于go语言开发并遵循了apache2.0协议开源。
Docker诞生于2013年,它的出现推动了容器技术发展的步伐,使其突飞猛进、日新月异。可以说,没有Docker,就没有后续的K8s、云原生。
学K8s之前,需要先学Docker,学会其镜像、容器、卷等概念。
2)Podman
Podman是一个开源项目,在Github上已有12k+Star,可在大多数Linux平台上使用。
Podman是一个无守护进程的容器引擎,用于在Linux系统上开发、管理和运行OCI(Open Container Initiative)容器和容器镜像。
Podman提供了一个与Docker兼容的命令行工具,可以简单地为docker命令取别名为podman即可使用,所以说如果你会Docker的话可以轻松上手Podman。
目前在RHEL系统里默认自带了Podman,说明红帽公司更倾向于Podman。
3)Kubernetes(K8s)
简单来说,Kubernetes 就是一个生产级别的容器编排平台和集群管理系统,不仅能够创建、调度容器,还能够监控、管理服务器,它凝聚了 Google 等大公司和开源社区的集
体智慧,从而让中小型公司也可以具备轻松运维海量计算节点——也就是“云计算”的能力。
作为世界上最大的搜索引擎,Google 拥有数量庞大的服务器集群,为了提高资源利用率和部署运维效率,它专门开发了一个集群应用管理系统,代号 Borg,在底层支持整个公司的运转。
2014 年,Google 内部系统要“升级换代”,从原来的 Borg 切换到 Omega,于是按照惯例,Google 会发表公开论文。在发论文的同时,把 C++ 开发的 Borg 系统用 Go 语言重写并开源,于是 Kubernetes 就这样诞生了。
由于 Kubernetes 背后有 Borg 系统十多年生产环境经验的支持,技术底蕴深厚,理论水平也非常高,一经推出就引起了轰动。
然后在 2015 年,Google 又联合 Linux 基金会成立了 CNCF(Cloud Native Computing Foundation,云原生基金会),并把 Kubernetes 捐献出来作为种子项目。
有了 Google 和 Linux 这两大家族的保驾护航,再加上宽容开放的社区,作为 CNCF 的“头把交椅”,Kubernetes 旗下很快就汇集了众多行业精英,仅用了两年的时间就打败了同期的竞争对手 Apache Mesos 和 Docker Swarm,成为了这个领域的唯一霸主。
3、K8s弃用Docker
1)CRI
K8s在1.5版本里,引入了一个新的接口标准:CRI(Container Runtime Interface),它主要用来规定如何调用容器运行时来管理容器和镜像,但这个接口标准和之前的Docker调用标准有不少差异,所以两者完全不兼容。这意味着,K8s可以撇开Docker,使用其它容器运行时(如rkt)。
由于Docker用户非常庞大,K8s也意识到了直接不兼容Docker会有许多不确定风险,当时,K8s用了一个临时方案,在K8s和Docker中间开发了一个Dockershim,主要用来将Docker的接口标准转换成CRI标准。
2)Containerd
Docker意识到K8s的改变,为了迎合K8s,将Docker Engine拆分成多个模块,其中Docker Daemon部分捐献给了CNCF,也就是containerd。
Containerd 作为 CNCF 的托管项目,自然是要符合 CRI 标准的。但 Docker 出于自己诸多原因的考虑,它只是在 Docker Engine 里调用了 containerd,外部的接口仍然保持不变,也就是说还不与 CRI 兼容。
在当时的K8s版本里,实际上有两种方法调用容器:
第一种是用 CRI 接口调用 dockershim,然后 dockershim 调用 Docker,Docker 再走 containerd 去操作容器。
第二种是用 CRI 接口直接调用 containerd 去操作容器。
很明显,第一种方法多了两层调用,性能明显不如第二种方法。所以K8s决定将dockershim移除,所以也就不能直接使用Docker了,在外界看来就像是K8s弃用了Docker。
3)弃用dockershim
2020年K8s发布1.20版本时,对外声明将在后续版本里(实际上是在22年的1.24版本里)移除dockershim,也就是取消对Docker的支持。当时,众多吃瓜群众理解错了意思,认为成了K8s弃用Docker。它实际上只是“弃用了 dockershim”这个小组件,也就是说把 dockershim 移出了 kubelet,并不是“弃用了 Docker”这个软件产品。
这个举措对K8s 和 Docker 来说都不会有什么太大的影响,因为他们两个都早已经把下层都改成了开源的 containerd,原来的 Docker 镜像和容器仍然会正常运行,唯一的变化就是 K8s绕过了 Docker,直接调用 Docker 内部的 containerd 而已。
4)K8s移除dockershim后对Docker的影响
虽然现在 Kubernetes 不再默认绑定 Docker,但 Docker 还是能够以其他的形式与 Kubernetes 共存的。
首先,因为容器镜像格式已经被标准化了(OCI 规范,Open Container Initiative),Docker 镜像仍然可以在 Kubernetes 里正常使用,原来的开发测试、CI/CD 流程都不需要改动,我们仍然可以拉取 Docker Hub 上的镜像,或者编写 Dockerfile 来打包应用。
其次,Docker 是一个完整的软件产品线,不止是 containerd,它还包括了镜像构建、分发、测试等许多服务,甚至在 Docker Desktop 里还内置了 Kubernetes。单就容器开发的便利性来讲,Docker 还是暂时难以被替代的,广大云原生开发者可以在这个熟悉的环境里继续工作,利用 Docker 来开发运行在 Kubernetes 里的应用。
再次,虽然 Kubernetes 已经不再包含 dockershim,但 Docker 公司却把这部分代码接管了过来,另建了一个叫 cri-dockerd的项目,作用也是一样的,把 Docker Engine 适配成 CRI 接口,这样 kubelet 就又可以通过它来操作 Docker 了,就仿佛是一切从未发生过。
综合来看,Docker 虽然在容器编排战争里落败,被 Kubernetes 排挤到了角落,但它仍然具有强韧的生命力,多年来积累的众多忠实用户和数量庞大的应用镜像是它的最大资本和后盾,足以支持它在另一条不与 Kubernetes 正面交锋的道路上走下去。而对于我们这些初学者来说,Docker 方便易用,具有完善的工具链和友好的交互界面,市面上很难找到能够与它媲美的软件了,应该说是入门学习容器技术和云原生的“不二之选”。
4、Docker快速入门
4.1 Docker安装
在Rocky8上,可以先配置对应版本的yum仓库,然后使用yum工具安装Docker
1)先安装yum-utils工具
yum install -y yum-utils
2)配置Docker官方的yum仓库
yum-config-manager
–add-repo
https://download.docker.com/linux/centos/docker-ce.repo
3)查看Docker版本
yum list docker-ce --showduplicates | sort -r
4)安装指定版本Docker
yum install docker-ce-20.10.9-3.el8
如果不指定版本,就安装最新版
yum install docker-ce
5)启动服务
$ systemctl start docker
$ systemctl enable docker
4.2 Docker镜像和容器
关于镜像这个词,大家并不陌生,比如系统安装盘就叫镜像,另外我们常见的Ghost技术做的GHO系统镜像和这个Docker镜像非常像。我们可以将GHO镜像安装到任何计算机上,这样就拿到了跟源系统一模一样的系统。
当然Docker镜像也有它独特之处,比如它是分层的,这个后续在拉取镜像时可以看到。
Docker镜像拉取下来后,可以直接运行,运行后的东西我们叫做容器,一个镜像可以启动很多容器。
容器就是镜像启动后的状态,在Linux系统里看就是一个进程,但容器是可以进入得,就像进入了一个虚拟机里。
1)配置加速器
编写/etc/docker/daemon.json
cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": ["https://dhq9bx4f.mirror.aliyuncs.com"]
}
EOF
重启docker服务
systemctl restart docker
检查是否生效
docker info |grep -A 3 ‘Registry Mirrors’
2)拉取镜像
docker pull busybox ##这个busybox就是系统镜像名字,它是从hub.docker.com去拉取镜像
3)查看当前系统镜像
docker image ls
4)搜索镜像
docker search ubuntu
如果要查询镜像版本,可以访问https://hub.docker.com/,搜索对应的镜像名字,然后点tag进行查看
拉取指定版本镜像
docker pull nginx:1.23.2
5)给镜像打标签
docker tag busybox:latest yoyolinux:1.2.3
6)启动容器
docker run -itd busybox
##把镜像启动为容器,-i表示让容器的标准输入打开,-t表示分配一个伪终端,-d表示后台启动,要把-i -t -d 放到镜像名字前面
7)查看容器
docker ps -a ##如果不加-a,则不显示已经停止的容器
8)停止容器
docker stop de1fb9c8902e ##后面这一串字符串为容器ID,可以通过docker ps查看
9)删除容器
docker rm de1fb9c8902e ##如果容器未停止,会报错,需要加-f选项
10)删除镜像
docker rmi yoyoinux:1.2.3
##这里因为yoyolinux:1.2.3为busybox的一个tag,所以此操作只会删除此tag,并不会真正删镜像,如果该镜像没有tag,则直接删除镜像
也可以直接指定镜像ID
docker rmi beae173ccac6
11)拉取镜像+启动容器
docker run -itd redis
12)启动容器时,给容器自定义名字
docker run --name web01 -itd ubuntu
13)容器运行后直接退出并删除
docker run --rm -it ubuntu bash -c “sleep 30”
14)进入容器操作
docker exec -it web01 bash
进去后,相当于进入了一个虚拟机一样,安装个nginx
apt update
apt install -y nginx
15)将容器重新打包成新镜像
docker commit -m “install nginx” -a “yoyo” web01 nginx_ubuntu:1.0
##这里的web01可以改为容器id
再次查看镜像列表,发现多了一个nginx_ubuntu:1.0
16)将镜像保存为一个文件
docker save nginx -o nginx.img
17)将导出的镜像文件导入
docker load --input nginx.img
18)将容器导出为一个文件
docker export web01 > web01.tar
19)将导出的文件导入为新的镜像
docker import - ubuntu_test < web01.tar
20)docker save和docker export的差异
docker save保存的是镜像,docker export保存的是容器
docker save会保留镜像所有的历史记录docker export不会,即没有commit历史
docker load用来载入镜像包,docker import用来载入容器包,但两者都会恢复为镜像;
docker load不能对载入的镜像重命名,而docker import可以为镜像指定新名称。
4.3 Docker的数据持久化
1)将本地目录映射到容器里
mkdir -p /data/
docker run -tid -v /data/:/data ubuntu bash ## -v
用来指定挂载目录 :前面的/data/为宿主机本地目录 :后面的/data/为容器里的目录,会在容器中自动创建
可以在/data/里创建一个文件
echo “hello” > /data/1.txt
然后到容器里查看
docker exec -it c82a5a00ae68 bash -c “cat /data/1.txt”
2)数据卷
创建数据卷
docker volume create testvol ##testvol为数据卷名字
列出数据卷
docker volume ls
查看数据卷信息
docker volume inspect testvol
使用数据卷
docker run -itd --name yoyo01 -v testvol:/data/ ubuntu
##和前面直接映射本地目录不同,冒号左边为数据卷名字
多个容器共享一个数据卷
docker run -itd --name yoyo02 -v testvol:/data/ ubuntu
3)将宿主机上的文件快速传输进容器里
docker cp /etc/fstab yoyo01:/tmp/test.txt
docker exec -it yoyo01 cat /tmp/test.txt
4.4 Docker网络
Docker服务启动时会生成一个docker0的网卡,这个网卡是实现容器网络通信的根本。 默认容器使用的网络类型为桥接(bridge)模式,这个桥接和我们的vmware里的桥接可不是一回事。它更像是vmware的NAT模式。
每启动一个容器,都会产生一个虚拟网卡 vethxxx
iptables -nvL -t nat ##可以看到DOCKER相关规则,容器之所以可以联网,是靠这些规则实现的
1)host模式
docker run -itd --net=host --name yoyo03 ubuntu
可以进入容器内,查看hostname,查看ip地址。 这种模式,容器和宿主机共享主机名、IP。
2)container模式
该模式下,在启动容器时,需要指定目标容器ID或者name,意思是将要启动的容器使用和目标容器一样的网络,即它们的IP一样
docker run -itd --net=container:/yoyo01 --name yoyo04 ubuntu
可以进入容器查看yoyo01 yoyo04的网络情况
3)none模式
即不需要配置网络
docker run -itd --net=none --name=yoyo05 ubuntu_test bash
查看:
docker exec -it yoyo05 bash -c “ip add”
4)bridge模式
也就是默认模式,可以–net=bridge 也可以不指定–net,默认就是bridge
5)端口映射
docker run -itd -v /data/:/var/www/html/ -p 8088:80 --name yoyo06
ubuntu_test bash
说明: -p后面跟 宿主机监听端口:容器监听端口
4.5 Dockerfile编写
什么是Dockerfile?是实现自定镜像的一种手段,通过编写Dockerfile,来编译成自己想要的镜像。
Dockerfile 格式
1)FROM 指定基于哪个基础镜像,例如:
FROM ubuntu:latest
2)MAINTAINER 指定作者信息,例如:
MAINTAINER yoyo yoyo@yoyolinux.com
3)RUN 后面跟具体的命令,例如:
RUN apt update
RUN apt install -y curl RUN
[“apt”,“install”,“-y”,“curl” ] ##这种写法偏复杂
4)CMD 用来指定容器启动时用到的命令,只能有一条,格式如下:
CMD [“executable”, “param1”, “param2”] CMD command param1 param2
CMD [“param1”, “param2”]
CDM示例:
CMD [“/bin/bash”, “/usr/local/nginx/sbin/nginx”, “-c”, “/usr/local/nginx/conf/nginx.conf”]
5)EXPOSE 指定要映射的端口,格式:
EXPOSE […]
EXPOSE示例:
EXPOSE 22 80 8443 ##要暴露22,80,8443三个端口
说明:这个需要配合-P(大写)来工作,也就是说在启动容器时,需要加上-P,让它自动分配。如果想指定具体的端口,也可以使用-p(小写)来指定。
6)ENV 为后续的RUN指令提供一个环境变量,我们也可以定义一些自定义的变量,例如
ENV MYSQL_version 5.7
7)ADD 将本地的一个文件或目录拷贝到容器的某个目录里。 其中src为Dockerfile所在目录的相对路径,它也可以是一个url。例如:
ADD conf/vhosts /usr/local/nginx/conf
8)COPY 类似于ADD,将本地文件拷贝到容器里,不过它不支持URL,例如:
COPY 123.txt /data/456.txt
9)ENTRYPOINT 格式类似CMD
容器启动时要执行的命令,它和CMD很像,也是只有一条生效,如果写多个只有最后一条有效。
和CMD不同是: CMD 是可以被 docker run 指令覆盖的,而ENTRYPOINT不能覆盖。
比如,容器名字为yoyo 我们在Dockerfile中指定如下CMD:
CMD [“/bin/echo”, “test”]
假如启动容器的命令是
docker run yoyo
则会输出 test
假如启动容器的命令是
docker run -it yoyo /bin/bash
则什么都不会输出
ENTRYPOINT不会被覆盖,而且会比CMD或者docker run指定的命令要靠前执行
ENTRYPOINT [“echo”, “test”]
假如启动容器的命令是
docker run -it yoyo 123
则会输出 test 123 ,这相当于要执行命令 echo test 123
10)VOLUME 创建一个可以从本地主机或其他容器挂载的挂载点。
VOLUME [“/data”]
11)USER指定RUN、CMD或者ENTRYPOINT运行时使用的用户
USER yoyo
12)WORKDIR 为后续的RUN、CMD或者ENTRYPOINT指定工作目录
WORKDIR /tmp/
Dockerfile示例:
vi Dockerfile
FROM ubuntu
MAINTAINER aming yoyo@yoyolinux.com
RUN apt update
RUN apt install -y libpcre2-dev net-tools gcc zlib1g-dev make
ADD http://nginx.org/download/nginx-1.23.2.tar.gz .
RUN tar zxvf nginx-1.23.2.tar.gz
RUN mkdir -p /usr/local/nginx
RUN cd nginx-1.23.2 && ./configure --prefix=/usr/local/nginx && make && make install
COPY index.html /usr/local/nginx/html/index.html
COPY index2.html /usr/local/nginx/html/2.html
EXPOSE 80
ENTRYPOINT /usr/local/nginx/sbin/nginx -g "daemon off;"
编译成镜像
docker build -t ubuntu_nginx:1.0 .
说明: -t 后面跟镜像名字:tag, 这里的. 表示使用当前目录下的Dockerfile,并且工作目录也为当前目录,如果想使用其它目录下的Dockerfile,还可以使用-f选项来指定,例如
docker build -t ubuntu_nginx:1.0 -f /data/docker/nginx.dkf /tmp/
##这里/tmp/目录为工作目录,比如COPY文件的时候,到/tmp/下面去找
使用镜像
docker run -itd --name nginx -P ubuntu_nginx:1.0
##使用-P(大写P)可以随机映射一个端口到宿主机,也可以使用-p(小p)指定具体端口
4.6 docker-compose
Docker compose可以方便我们快捷高效地管理容器的启动、停止、重启等操作,它类似于linux下的shell脚本,基于yaml语法,在该文件里我们可以描述应用的架构,比如用什么镜像、数据卷、网络模式、监听端口等信息。
我们可以在一个compose文件中定义一个多容器的应用(比如wordpress),然后通过该compose来启动这个应用。
1)安装docker-compose
官方地址
https://github.com/docker/compose/releases
curl -L https://github.com/docker/compose/releases/download/v2.12.2/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
chmod a+x /usr/local/bin/docker-compose
测试并查看版本
docker-compose version
2)用docker-compose快速部署应用
编辑wordpress的compose yaml文件
vi docker-compose.yml ##写入如下内容
services:
db: # 服务1:db
image: mysql:5.7 # 使用镜像 mysql:5.7版本
volumes:
- db_data:/var/lib/mysql # 数据持久化
restart: always # 容器服务宕机后总是重启
environment: # 环境配置
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress: # 服务2:wordpress
depends_on: # wordpress服务启动时依赖db服务,所以会自动先启动db服务
- db
image: wordpress:latest # 使用镜像 wordpress:latest最新版
ports:
- "8000:80" #端口映射8000:80
restart: always
environment: # 环境
WORDPRESS_DB_HOST: db:3306 # wordpress连接db的3306端口
WORDPRESS_DB_USER: wordpress # wordpress的数据库用户为wordpress
WORDPRESS_DB_PASSWORD: wordpress # wordpress的数据库密码是wordpress
WORDPRESS_DB_NAME: wordpress # wordpress的数据库名字是wordpress
volumes:
db_data: {}
启动
docker-compose up -d
查看
docker-compose ps
还可以停止
docker-compose stop
4.7 私有镜像仓库harbor搭建
Docker容器应用的开发和运行离不开可靠的镜像管理,虽然Docker官方也提供了公共的镜像仓库,但是从安全和效率等方面考虑,部署我们私有环境内的Registry也是非常必要的。Harbor是 由VMware公司开源的企业级的Docker Registry管理项目,它包括权限管理(RBAC)、LDAP、日志审核、管理界面、自我注册、镜像复制和中文支持等功能。
harbor官方地址: https://goharbor.io
github地址:https://github.com/goharbor/harbor
0)提前准备一个ca证书
如果有自己的域名,可以到https://freessl.cn/ 申请免费的ssl证书
安装好docker-compose
1)下载harbor离线包
https://github.com/goharbor/harbor/releases
我这里下载的是2.6.2
2)将下载的包上传到linux,解压
tar zxf harbor-offline-installer-v2.6.2.tgz -C /opt/
3)准备配置文件
cd /opt/harbor
cp harbor.yml.tmpl harbor.yml ##将模板配置文件拷贝一份为正式的配置文件
4)编辑配置文件
vi harbor.yml
修改 hostname: reg.mydomain.com 为 hostname: harbor.yoyolinux.com
修改 certificate: /your/certificate/path 和 private_key: /your/private/key/path 为具体的证书地址
修改 harbor_admin_password 为合适的密码
5)安装
sh install.sh
6)服务的停止和启动
cd /opt/harbor
docker-compose ps ##查看服务
docker-compose stop ##关闭
docker-compose up -d ##启动
7)访问web界面
8)拉取公共镜像
$docker pull tomcat docker tag tomcat
harbor.yoyolinux.com/yoyo/tomcat:latest
9)把tomcat镜像推送到harbor
$ docker login https://harbor.yuankeedu.com
输入用户名和密码
# docker push harbor.yuankeedu.com/aminglinux/tomcat:latest
问题: x509: certificate signed by unknown authority
需要在客户端机器上(也就是你执行docker login的机器上)执行
$ echo -n | openssl s_client -showcerts -connect harbor.yoyolinux.com:443 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' >> /etc/ssl/certs/ca-bundle.trust.crt
2)systemctl restart docker