文章目录
docker基础
1.docker 介绍
docker是什么
- 使用最广泛的开源容器引擎
- 一种操作系统级的虚拟化技术
- 使用docker创建的容器以一种特殊进程的方式在宿主机上运行
- 一个简单的应用程序打包工具
docker理念:一次构建封装到处运行
docker:解决了运行环境和配置问题软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术。
docker能干嘛
1.之前的虚拟机技术
2.容器虚拟化技术
3.开发/运维
4.企业级
与虚拟机的区别
虚拟机可以在一种操作系统里面运行另一种操作系统,比如在Windows里面运行linux系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对与底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使应用程序,操作系统和硬件三者之间的逻辑不变。
虚拟机的缺点
1.资源占用多 2.冗余步骤多 3.启动慢
比较docker和传统虚拟机的不同:
1.传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整的操作系统,在该系统上再运行所需应用进程;
2.而容器内的应用进程直接运行与宿主机的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便;
3.每个容器之间互相隔离,每个容器有自己的文件系统,容器之间进程不会互相影响,能区分计算资源。
docker容器 | 虚拟机(VM) | |
---|---|---|
操作系统 | 与宿主机共享OS | 宿主机OS上运行虚拟机OS |
存储大小 | 镜像小,便于存储与传输 | 镜像庞大 |
运行性能 | 几乎无额外性能损失 | 操作系统额外的CPU、内存消耗 |
移植性 | 轻便、灵活、适应于linux | 笨重,与虚拟化技术耦合度高 |
硬件亲和性 | 面向软件开发者 | 面向硬件运维者 |
启动速度 | 快速,秒级 | 慢,分钟级 |
docker流程图
为什么用docker
更轻量: 基于容器的虚拟化,仅包含业务运行所需的runtime环境,CentOS/Ubuntu基础镜像仅170M;宿主机可部署100-1000个容器
更高效: 无操作系统虚拟化开销
计算:轻量,无额外开销
存储:系统盘aufs/dm/overlayfs;数据盘:volume
网络:宿主机网络,NS隔离
更敏捷、更灵活
分层的存储和包管理,devops理念
支持多种网络配置
docker的优势
轻量,秒级的快速启动速度
简单,易用,活跃的社区
标准统一的打包/部署/运行方案
镜像支持增量分发,易于部署
易于构建,良好的REST API ,也很适合自动化测试和持续集成
性能,尤其是内存和IO开销
docker官网: http://www.docker.com
docker中文网站: https://www.docker-cn.com
2.docker安装
docker基本组成:
仓库(repository):是集中存放镜像文件的场所
镜像(image):就是一个只读的模板,镜像可以用来创建docker容器,一个镜像可以创建很多容器。容器运行是需要环境的而镜像就是提供这些环境的。
容器(container):独立运行的一个或一组应用。容器是用镜像创建的运行实例。
总结:
Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就视乎image镜像文件。只有通过这个镜像文件才能生成Docker容器。image文件可以看做是容器的模板。Docker根据image文件生成容器的实例。同一个image文件,可以生成多个同时运行的容器实例。
image文件生成的容器实例,本身也是一个文件,称为镜像文件
一个容器运行一种服务,当我们需要的时候,就可以通过docker客户端创建一个对应的运行实例,也就是我们的容器
至于仓库,就是放了一推镜像的地方,我们可以把镜像发布到仓库中,需要的时候从仓库中拉下来就可以了
docker 安装:
# 卸载旧的版本
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
# 需要的安装包
yum install -y yum-utils
# 设置镜像仓库
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo # 默认使用国外的,建议使用国内的
yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 推荐使用阿里云,快
# 更新yum软件包索引
yum makecache fist
# 安装docker
yum -y install docker-ce docker-ce-cli containerd.io
# 启动docker
systemctl start docker
# 使用docker version查看是否安装成功
# 测试hello-world
docker run hello-world
# 查看镜像
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest d1165f221234 5 weeks ago 13.3kB
# 解决:需要:container-selinux >= 2:2.74的问题
yum install --setopt=obsoletes=0 \
docker-ce-17.03.2.ce-1.el7.centos.x86_64 \
docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch # on a new system with yum repo defined, forcing older version and ignoring obsoletes introduced by 17.06.0
# 使用脚本一键安装
# docker安装
curl -sSL https://get.daocloud.io/docker | sh
# 配置docker镜像站
curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io
docker 卸载
# 停止docker服务
systemctl stop docker
# 卸载依赖
yum remove docker-ce docker-ce-cli containerd.io
# 删除资源
rm -rf /var/lib/docker #docker默认工作路径
镜像加速器
# 1、登录阿里云,找到容器服务
# 2、找到镜像加速地址
# 3、配置
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://vrg74rno.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
# 也可以配置网易云镜像加速
{
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}
docker run
流程图
docker的底层原理
docker是怎样工作的:
- docker是一个CS结构的系统,docker守护进程运行在主机上,然后通过socket连接从客户端访问,守护进程从客户端接收命令并管理运行在主机上的容器,docker-server接收到docker-client的指令,就会执行这个命令!
docker为什么比vm快:
-
docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机上的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。
-
docker不需要像虚拟机一样重新加载一个内核系统,避免引导,所以在启动速度上有较大的优势
3.docker常用命令
帮助命令
docker version # 显示docker的版本信息
docker info # 显示docker的系统信息,包括镜像和容器的数量
docker --help # docker帮助命令
镜像命令
- docker images # 列出本地主机上的镜像
选项 :
-a # 列出本地所有的镜像(含中间映像层)
-q # 只显示镜像的ID
--digests # 显示镜像的摘要信息
--no-trunc # 显示完整的镜像信息
[root@97 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest fce289e99eb9 15 months ago 1.84kB
# REPOSITORY:表示镜像的仓库源
# TAG:镜像的标签
# IMAGE ID:镜像的ID
# CREATED:镜像创建的时间
# SIZE:镜像大小
同一个仓库源可以有多个tag,代表这个仓库的不同个版本,我们使用 repository:tag 来定义不同的镜像。
- docker search # 从
http://hup.docker.com
网站中查找镜像
语法:
docker search [options]
镜像的名字
选项:
# 通过收藏来过滤
--filter=STARS=1000 # 搜索出来的镜像就是STARS大于1000的
[root@97 ~]# docker search nginx --filter=STARS=1000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 14713 [OK]
jwilder/nginx-proxy Automated Nginx reverse proxy for docker con… 2009 [OK]
- docker pull # 下载镜像
# 语法:
docker pull 镜像名[:tag]
docker pull tomcat 等价于 docker pull tomcat:latest # 冒号后面不接版本号默认下载最新版
[root@localhost ~]# docker pull mysql
Using default tag: latest # 如果不写tag,默认latest最新版
latest: Pulling from library/mysql
f7ec5a41d630: Already exists # 分层下载,docker image的核心,联合文件系统
9444bb562699: Pull complete
6a4207b96940: Pull complete
181cefd361ce: Pull complete
8a2090759d8a: Pull complete
15f235e0d7ee: Pull complete
d870539cd9db: Pull complete
5726073179b6: Pull complete
eadfac8b2520: Pull complete
f5936a8c3f2b: Pull complete
cca8ee89e625: Pull complete
6c79df02586a: Pull complete
Digest: sha256:6e0014cdd88092545557dee5e9eb7e1a3c84c9a14ad2418d5f2231e930967a38 # 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址
- docker rmi #删除某个镜像
[root@localhost ~]# docker rmi -f 镜像ID # 删除指定镜像
[root@localhost ~]# docker rmi -f 镜像ID 镜像ID 镜像ID # 删除指定的多个镜像
[root@localhost ~]# docker rmi -f $(docker images -qa) # 删除全部镜像
容器命令
有镜像才能创建容器,这是根本前提(下载一个centos镜像演示)
docker pull centos
新建并启动镜像
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
OPTIONS 说明:
--name="容器新名字" :为容器指定一个名称,用于区分容器
-d:后台运行容器
-i:以交换模式运行容器,通常与 -t 同时使用
-t:为容器重新分配一个伪输入终端,通畅与 -i 同时使用
-P:随机端口映射
-p:指定端口映射,有以下四种格式
ip:hostPort:containerPort
ip:conrainerPort
hostPort:containerPort
containerPort
# 新建容器并启动,会在新的docker里面的centos的/目录下面
[root@97 ~]# docker run -it centos
[root@ab9486eb362f /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@ab9486eb362f /]# pwd
/
列出当前所有正在运行的容器
[root@97 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ab9486eb362f centos "/bin/bash" 9 minutes ago Up 9 minutes musing_lalande
NAME就是 --name指定的别名,没有指定就使用默认NAME
docker ps 常用选项
-a 列出当前所有正在运行的容器和历史上运行过的容器
-l 显示最近创建的容器
-n 显示最近n个创建的容器
-q 静默模式,只显示容器编号
--no-trunc 不截断输出
[root@97 ~]# docker ps -q
ab9486eb362f
退出容器的两种方法
exit 容器停止退出
ctrl+P+Q 容器不停止退出
删除容器
[root@localhost ~]# docker rm 容器ID # 删除指定容器,不可删除正在运行的容器,如过要删除请加 -f
[root@localhost ~]# docker rm -f $(docker ps -qa) # 删除全部容器
docker ps -a -q | xargs docker rm # 删除所有容器
启动停止容器
docker start 容器ID # 启动容器
docker restart 容器ID # 重启容器
docker stop 容器ID # 停止容器
docker kill 容器ID # 强制停止当前容器
删除已停止的容器
[root@97 ~]# docker stop 9edbc71d62bc
9edbc71d62bc
[root@97 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ab9486eb362f centos "/bin/bash" 4 days ago Up 4 days musing_lalande
[root@97 ~]# docker ps -n 2
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9edbc71d62bc centos "/bin/bash" 20 minutes ago Exited (0) 2 minutes ago myctos
ab9486eb362f centos "/bin/bash" 4 days ago Up 4 days musing_lalande
[root@97 ~]# docker rm 9edbc71d62bc
[root@97 ~]# docker rm ab9486eb362f -f #删除没有停止的容器加-f强制删除
启动守护式容器
docker run -d 容器名
[root@97 ~]# docker run -d centos
74558d5b6bfb0034d99d2d675bdbaff8914647d8c463b21e134d63d20e4441d7
[root@97 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 问题:然后docker ps 进行查看,发现容器已经退出
说明:如果不是那些一直挂起的命令(top tail)就是会自动退出的
重点:docker容器后台运行就必须要有一个前台进程
比如:nginx容器启动后,发现自己没有提供服务,就会立刻停止
[root@97 ~]# docker run -d centos /bin/bash -c "while true;do echo zz;sleep 2;done"
ec25dc5cafc03578d963cb129ea9cf0b215b716434019b7d08df93aa3c65a190
[root@97 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec25dc5cafc0 centos "/bin/bash -c 'while…" 3 seconds ago Up 3second bold_goldwasser
查看docker日志
docker logs -f -t --tail 容器ID
-t 是加入时间戳
-f 是跟随最新的日志打印
--tail number 显示最后的多少条
[root@97 ~]# docker logs ec25dc5cafc0
zz
zz
zz
...
[root@97 ~]# docker logs -t -f --tail 3 ec25dc5cafc0
2020-04-15T09:09:08.290949108Z zz
2020-04-15T09:09:10.293420735Z zz
2020-04-15T09:09:12.296657506Z zz
...
查看容器内运行的进程
docker top 容器ID
[root@localhost ~]# docker top 14795dc7e6fa
UID PID PPID C STIME TTY TIME CMD
root 41784 41761 0 16:29 pts/0 00:00:00 /bin/bash
查看容器的元数据
命令:
docker inspect 容器ID
进入正在运行的容器并以命令交互
docker attach 容器ID # 是直接进入容器启动的命令终端,不会启动新的进程
docker exec -it 容器ID # 是在容器中打开新的终端,可以在里面操作(常用)
[root@97 ~]# docker run -it centos
[root@7de493efe07b /]# Ctrl+p+q #不停止退出
[root@97 ~]# docker attach 7de493efe07b #attach方式进入
[root@7de493efe07b /]# ls /tmp
ks-script-_srt3u3c ks-script-gpqu_kuo
[root@97 ~]# docker attach 7de493efe07b ls /tmp
"docker attach" requires exactly 1 argument.
See 'docker attach --help'.
[root@97 ~]# docker exec -it 7de493efe07b ls /tmp
ks-script-_srt3u3c ks-script-gpqu_kuo
#exec方式登录可以不进入容器执行命令返回结果,attach方式必须先进去容器然后执行操作
从容器内拷贝文件到主机上
docker cp 容器ID:容器内路径 目的主机路径
[root@97 ~]# docker exec -it 7de493efe07b touch aa.text
[root@97 ~]# docker exec -it 7de493efe07b ls
aa.text dev home lib64 media opt root sbin sys usr
[root@97 ~]# docker cp 7de493efe07b:aa.text /root
[root@97 ~]# ls
aa.text
docker部署nginx
# 搜索镜像
[root@localhost ~]# docker search nginx --filter=STARS=3000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 14718 [OK]
# 下载镜像
[root@localhost ~]# docker pull nginx
# 启动容器
[root@localhost ~]# docker run --name nginx01 -d -p 8080:80 nginx
--name 起个别名
-p 宿主机端口:容器内端口(映射)
// 现在访问宿主机的8080端口就可以访问到容器内的80端口
docker 部署 Tomcat
# 搜索镜像
[root@localhost ~]# docker search tomcat
# 下载镜像
[root@localhost ~]# docker pull tomcat
# 启动运行容器
[root@localhost ~]# docker run --name tomcat01 -d -p 8081:8080 tomcat
[root@localhost ~]# docker ps
# web访问没有问题,但是发现404
# 进入容器
[root@localhost ~]# docker exec -it tomcat01 /bin/bash
root@58d86d3ca8d3:/usr/local/tomcat# ls webapps
root@58d86d3ca8d3:/usr/local/tomcat#
# 发现问题:发现webapps为空,因为阿里云镜像的原因,默认是最小镜像,所有不必要的都会剔除,保证最小可运行环境
#
root@58d86d3ca8d3:/usr/local/tomcat# cp -r webapps.dist/* webapps # 再次访问就正常了
可视化
- portainer
什么是portainer
docker
图形化界面管理工具,提供一个后台面板供我们操作!
[root@localhost ~]# docker run -d -p 8088:9000 \
--restart=always -v "/var/run/docker.sock:/var/run/docker.sock" --privileged=true portainer/portainer
4.docker 镜像
镜像是什么
一种轻量级、可执行独立软件包,用来打包软件运行环境和基于运行环境开发的软件,包含软件所需的所有内容,代码、库、环境变量和配置文件。
所有的应用,直接打包docker镜像就可以直接跑起来
镜像的获取途径:
-
从远程仓库获取
-
朋友给你
-
自己制作一个镜像DockerFile
docker镜像加载原理
UnionFS(联合文件系统)
是一种分层、轻量级并且高性能的文件系统,它支持对文件系统修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。union文件系统是docker镜像的基础。镜像可以通过分层来进行集成,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看来只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
docker镜像加载原理
docker的镜像实际上是由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system)主要包含BootLoader和kernel,BootLoader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在docker镜像的最底层是bootfs。这一层与我们典型的Linux系统是一样的。包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中的 /dev,/proc,/bin,/etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
平时我们安装虚拟机中的centos镜像都是好几个G,为什么到docker中才200M左右??
对于一个精简版的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就行了,因为底层直接用host的kernel,自己只需要提供rootfs就行了。由此可见对于不同的Linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以共用bootfs。
分层的镜像
以我们的pull
为例,在下载的过程中我们可以看到docker的镜像好想是在一层一层的在下载。
为什么tomcat的镜像会有400多M这么大??
就是因为分层的镜像tomcat需要这些---->kernel–centos–jdk8–tomcat–docker容器
docker为何采用这种分层
最大的一个好处就是—资源共享
比如有多个镜像都从相同的base镜像构建而来,那么宿主机只需要在磁盘上保存一份base镜像,同时内存中也只需加载一份base镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
docker镜像的特点
docker镜像都是只读的
当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称为“容器层”,"容器层"之下的都叫镜像层。
docker commit
docker commit
提交容器副本使之成为一个新镜像
docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的新的镜像名:[标签名]
案例演示
从hub
上下载tomcat
镜像到本地运行,然后故意删除tomcat
容器的帮助文档,此时将没有文档的tomcat
容器为模板commit
一个新的镜像,名为 atguigu/tomcat02
# 从hub上下载tomcat镜像到本地并成功运行
[root@97 ~]# docker images tomcat
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat latest e36064f7c6f0 3 weeks ago 528MB
[root@97 ~]# docker run -it -p 8888:8080 tomcat # 这样就启动了tomcat
-P 主机端口:docker容器端口
-p 随机分配端口
-i 交互
-t 终端
[root@97 ~]# docker run -it -P tomcat
[root@97 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS
893b6a90f5fc tomcat "catalina.sh run" 54 seconds ago Up 53 seconds
PORTS NAMES
0.0.0.0:32768->8080/tcp recursing_cohen #(这里可以看到使用-P随机分配的映射端口为32768)
# 此时使用localhost:32768即可访问到tomcat的首页
# 故意删除上一步镜像生产tomcat容器的文档
[root@97 ~]# docker run -it -p 8888:8080 tomcat
[root@97 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS
58ff859e46b5 tomca "catalina.sh run" 21 seconds ago Up 20 seconds
PORTS NAMES
0.0.0.0:8888->8080/tcp competent_gates
[root@97 ~]# docker exec -it 58ff859e46b5 /bin/bash
root@58ff859e46b5:/usr/local/tomcat# rm -rf webapps/docs
再次访问localhost:8888的时候文档就没有了
# 也即当前的tomcat运行实例是一个没有文档内容的容器,以它为模板commit一个没有doc的tomcat新镜像atguigu/tomcat02
[root@97 ~]# docker commit -a="zzyy" -m="tomcat del docs" 58ff859e46b5 atguigu/tomcat02
sha256:8848761752371b74c6bf4b30348e675565ed51eba918179947340170bd125219
[root@97 ~]# docker images # 查看新生成的镜像
REPOSITORY TAG IMAGE ID CREATED SIZE
atguigu/tomcat02 latest 884876175237 About a minute ago 528MB
tomcat latest e36064f7c6f0 3 weeks ago 528MB
[root@97 ~]# docker rm -f $(docker ps -q) # 删除所有正在运行的容器
[root@97 ~]# docker run -it -p 8888:8080 atguigu/tomcat02 #此时使用新镜像启动后是没有文档的
5.docker容器数据卷
是什么
在docker容器中运行的数据我们想把它保存下来就会使用卷,类似一个外置的活动硬盘。有点类似我们redis里面的rdb和aof文件。
将运用运行的环境打包形成容器运行,运行可以伴随着容器,但是我们对数据的要求希望是持久化的,容器之间希望有可能共享数据
docker容器产生的数据,如果不通过dockercommit生成新的镜像,使得数据做为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了
为了能保存数据在docker中,我们使用卷。
能干嘛
主要做数据共享和数据持久化的工作
特点:
1.数据卷可在容器之间共享或重用数据
2.卷中的更改可以之间生效
3.数据卷中的更改不会包含在镜像的更新中
4.数据卷的生命周期一直持续到没有容器使用它为止
数据卷添加
直接-V命令添加
docker run -it -v /宿主机绝对路径目录:/容器内目录 镜像名
[root@97 ~]# docker run -it -v /MyData:/MyDataContainer centos
# 会在宿主机生成/MyData在容器内生成/MyDataContainer
[root@d2cd5bada4b1 /]# ls
MyDataContainer
[root@97 ~]# ls /
MyData
# 查看数据是否挂载成功
[root@97 ~]# docker inspect d2cd5bada4b1
...
"Binds": [
"/MyData:/MyDataContainer"
],
...
"Mounts": [
{
"Type": "bind",
"Source": "/MyData", # 主机地址
"Destination": "/MyDataContainer", # 容器内地址
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
...
# 容器和宿主机之间数据共享
[root@97 ~]# cd /MyData/
[root@97 MyData]# touch aa # 宿主机创建文件
[root@d2cd5bada4b1 /]# cd MyDataContainer/
[root@d2cd5bada4b1 MyDataContainer]# ls # 容器内可以看到并修改
aa
[root@d2cd5bada4b1 MyDataContainer]# echo '123'>aa
[root@d2cd5bada4b1 MyDataContainer]# touch bb
[root@97 MyData]# ls # 实现了数据共享
aa bb
[root@97 MyData]# cat aa
123
# 容器退出后主机修改的内容能否同步?
# 退出容器
[root@d2cd5bada4b1 MyDataContainer]# exit
# 宿主机新建文件
[root@97 MyData]# touch text
[root@97 MyData]# ls
aa bb text
# 重新启动容器
[root@97 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@97 ~]# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
d2cd5bada4b1 centos "/bin/bash" 30 minutes ago Exited (0) About a minute ago
NAMES
vigorous_gates
[root@97 ~]# docker start d2cd5bada4b1
d2cd5bada4b1
[root@97 ~]# docker attach d2cd5bada4b1 # 即使容器退出后宿主机的数据还是可以实现共享
[root@d2cd5bada4b1 /]# ls MyDataContainer/
aa bb text
备注:docker挂载主机目录docker访问出现cannot open director Permission denied(不能写)
解决办法:在挂载目录后多加一个 --privileged=true
Mysql 数据同步
# 获取镜像
[root@localhost ~]# docker pull mysql:5.7
# 启动运行mysql,需要做数据卷挂载,并且需要设置密码
# 官方:docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 启动容器
-d 后台运行
-p 端口映射
-v 数据卷挂载(目录映射)
-e 环境配置
--name 容器名
[root@localhost ~]# docker run -d -p 3300:3306 -v /home/mysql01/conf:/etc/mysql/conf.d \
-v /home/mysql01/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=admin123 --name mysql01 mysql:5.7
# 查看映射的目录
[root@localhost ~]# cd /home/mysql01/
[root@localhost mysql01]# ls
conf data
[root@localhost mysql01]# ls data/
auto.cnf ca.pem client-key.pem ibdata1 ib_logfile1 mysql private_key.pem server-cert.pem sys
ca-key.pem client-cert.pem ib_buffer_pool ib_logfile0 ibtmp1 performance_schema public_key.pem server-key.pem
# 本地测试连接
# 连接服务器的3300 --- 容器的3306(映射)
[root@localhost ~]# mysql -uroot -padmin123 -h127.0.0.1 -P 3300
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.33 MySQL Community Server (GPL)
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]>create database test; # 创建一个数据库
# 查看本地映射目录
[root@localhost mysql01]# ls data/
auto.cnf ca.pem client-key.pem ibdata1 ib_logfile1 mysql private_key.pem server-cert.pem sys
ca-key.pem client-cert.pem ib_buffer_pool ib_logfile0 ibtmp1 performance_schema public_key.pem server-key.pem test
# 发现新创建的test库已经同步
# 删除容器之后,本地挂载(映射)的数据不会丢失
# 实现了数据同步
[root@localhost ~]# docker rm mysql01 -f
mysql01
[root@localhost mysql01]# ls data/
auto.cnf ca.pem client-key.pem ibdata1 ib_logfile1 mysql private_key.pem server-cert.pem sys
ca-key.pem client-cert.pem ib_buffer_pool ib_logfile0 ibtmp1 performance_schema public_key.pem server-key.pem test
具名和匿名挂载
# 匿名挂载
-v 容器内路径
# 只写容器内路径 /etc/nginx
[root@localhost ~]# docker run -d -v /etc/nginx -P --name nginx01 nginx
# 查看所有 volume 信息
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local 68eaaa44a6c52dc1207fafea0396a9963f6d08143e080521b3a98027c30ee72f
# 这就是匿名挂载,启动容器时 -v 只写了容器内路径
# 具名挂载
-v 卷名:容器内路径
[root@localhost ~]# docker run -d --name nginx02 -P -v jm:/etc/nginx nginx
# 查看所有volume信息
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local jm
# 这就是具名挂载,通过 -v 卷名:容器内路径
# 那么容器内的目录挂载到了本地什么地方?
# 查看一下卷的inspect
[root@localhost ~]# docker volume inspect jm
[
{
"CreatedAt": "2021-04-18T00:27:59+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/jm/_data", # 就是挂载到了本地的这个目录
"Name": "jm",
"Options": null,
"Scope": "local"
}
]
所有没有指定目录的情况下,都会挂载到本地的
/var/lib/docker/volumes/卷名/_data
通过具名挂载,可以方便的找到我们的卷,比较常用
如何区分,具名,匿名,指定路径,这几种挂载方式
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v 宿主机路径:容器内路径 # 直径路径挂载
命令带权限
# 命令带权限
ro: 只读
rw: 可读可写
# 这个权限是对容器而言的,一旦设置了ro,容器内部就不能进行写操作,外部是可以的,默认权限是rw
docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
[root@97 ~]# docker run -it -v /Host:/Container:ro centos
[root@30e7325ecd69 /]# ls
Container
[root@97 ~]# cd /
[root@97 /]# ls
Host
[root@30e7325ecd69 /]# cd Container/ # 容器中不能往宿主机端增删改
[root@30e7325ecd69 Container]# touch a
touch: missing file operand
Try 'touch --help' for more information.
[root@97 /]# cd Host/
[root@97 Host]# echo '123'>abc
[root@30e7325ecd69 Container]# ls # 而宿主机端可以向容器端进行增删改
abc
[root@30e7325ecd69 Container]# cat abc
123
初识DockerFile
dockerfile就是用来构建docker镜像的构建文件,命令脚本!
通过这个脚本可以生成镜像,镜像是一层一层的,脚本就是一个一个命令!
# 创建一个dockerfile文件,名字可以随机,建议就用dockerfile
mkdir /home/docker-test-volume
cd /home/docker-test-volume
vim dockerfile1
# 文件中的内容:指令(大写) 参数
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "-----end-----"
CMD /bin/bash
# 生成镜像
-f dockerfile的路径
-t ‘name:tag’
docker build -f dockerfile1 -t scl/contos:1.0 .
# 查看生成的镜像
[root@localhost docker-test-volume]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
scl/contos 1.0 79d7251626b6 12 minutes ago 209MB
# 使用镜像运行容器
docker run -it 79d7251626b6 /bin/bash
# 进入容器,可以看到我们挂载的数据卷目录volume01和volume02
[root@localhost docker-test-volume]# docker exec -it 44f5b69616e6 /bin/bash
[root@44f5b69616e6 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
# 查看容器的inspect可以看到容器外部对应的挂载目录
[root@localhost ~]# docker inspect 44f5b69616e6
"Mounts": [
{
"Type": "volume",
"Name": "3c3a232b92451405debb36659eacdd3f19d76568ee08c0b595a05e86ede0a5a2",
"Source": "/var/lib/docker/volumes/3c3a232b92451405debb36659eacdd3f19d76568ee08c0b595a05e86ede0a5a2/_data",
"Destination": "volume01",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "13d98e2870b98cfbc7e06a0b1d31f5604e147601c11433d86bc919acfeca9cd5",
"Source": "/var/lib/docker/volumes/13d98e2870b98cfbc7e06a0b1d31f5604e147601c11433d86bc919acfeca9cd5/_data",
"Destination": "volume02",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
# 测试能否同步
[root@localhost ~]# cd /var/lib/docker/volumes/3c3a232b92451405debb36659eacdd3f19d76568ee08c0b595a05e86ede0a5a2/_data
[root@localhost _data]# touch container01.txt
[root@localhost _data]# ls
container01.txt
# 查看容器内部是否同步
[root@localhost _data]# docker exec -it 44f5b69616e6 ls volume01
container01.txt
以后我们构建自己的镜像的时候这种方式会经常用到
如果构建镜像的时候没有挂载卷,要手动镜像挂载,-v 卷名:容器内路径
数据卷容器
命名的容器挂载数据卷,其他容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为数据卷容器。
容器与容器之间的数据同步就要用到容器数据卷
# 启动3个容器,3个容器之间实现数据共享(就使用前面用dockerfile构建的镜像)
# 启动第一个容器
[root@localhost ~]# docker run -it --name docker01 scl/contos:1.0
[root@264b84f04e0a /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
# 启动第二个容器
--volumes-from 父容器名 # 通过这个参数就可以让运行的子容器和父容器实现数据共享,父容器叫做数据卷容器
[root@localhost ~]# docker run -it --name docker02 --volumes-from docker01 scl/contos:1.0
[root@9d8e2db59cae /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
# 测试两个容器之间的数据能否同步
# 第一个容器,往卷里面创建一个文件
[root@264b84f04e0a /]# touch volume01/test.txt && ls volume01
test.txt
# 在第二个容器里面对应的volume01查看数据是否同步
[root@30783b56ac1c /]# ls volume01/
test.txt
# 启动第三个容器
[root@localhost ~]# docker run -it --name docker03 --volumes-from docker01 scl/contos:1.0
[root@a4973931ca03 /]# ls volume01/ # 发现数据已经同步过来了
test.txt
## 第一个容器(父容器)没了,后面的两个容器还能否实现数据同步
# 先删除第一个容器docker01
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a4973931ca03 scl/contos:1.0 "/bin/sh -c /bin/bash" 2 minutes ago Up 2 minutes docker03
30783b56ac1c scl/contos:1.0 "/bin/sh -c /bin/bash" 20 minutes ago Up 20 minutes docker02
264b84f04e0a scl/contos:1.0 "/bin/sh -c /bin/bash" 24 minutes ago Up 24 minutes docker01
[root@localhost ~]# docker rm -f 264b84f04e0a
264b84f04e0a
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a4973931ca03 scl/contos:1.0 "/bin/sh -c /bin/bash" 2 minutes ago Up 2 minutes docker03
30783b56ac1c scl/contos:1.0 "/bin/sh -c /bin/bash" 21 minutes ago Up 21 minutes docker02
# 测试发现即使父容器被删了,剩下的2个容器还是可以实现数据共享
[root@30783b56ac1c /]# touch volume01/test2.txt && ls
test.txt test2.txt
[root@a4973931ca03 /]# ls volume01/
test.txt test2.txt
# 只要有一个容器还在使用数据,数据就不会丢失
# 它不是共享机制,而是一种双向拷贝的机制,即使父容器没了,但是数据已经被其他容器拷贝出去了
总结
-
容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器使用它为止
-
只有使用这些数据的所有容器都被删除了,数据才会没有
-
但是一旦卷被挂载到了本地,那么数据将不会丢失
6. DockerFile
DockerFile介绍
docker是用来构建docker镜像的文件,命令参数脚本
1、编写一个 dockerfile 文件
2、docker build 构建成为一个镜像
3、docker run 运行镜像
4、docker push 发布镜像(dockerhub、阿里云镜像仓库)
很多官方镜像都是基础包,很多功能没有,我们通常会构建自己的镜像
DockerFile构建过程
基础知识:
1、每个保留关键字(指令)都必须是大写字母
2、执行从上到下顺序执行
3、# 表示注释
4、每一个指令都会创建提交一个新的镜像层,并提交
步骤:
DockerFile:构建文件,定义了一切的步骤,源代码
Docker镜像:通过 DockerFile 构建生成的镜像,最终发布和运行的产品(用来是jar包或者war包的形式)
Docker容器:镜像运行起来的提供服务的
DockerFile的指令
FROM # 基础镜像,一切从这里开始
MAINTAINER # 镜像是谁写的,姓名+邮箱
RUN # 镜像构建的时候需要运行的命令
ADD # 步骤:添加内容,tomcat镜像,tomcat压缩包,添加的压缩包会自动解压
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 暴露端口配置
CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD # 当构建一个被继承 DockerFile 这个时候就会运行 ONBUILD 的指令,触发指令(了解)
COPY # 类似ADD,将我们的文件拷贝到镜像中
ENV # 构建的时候设置环境变量
DockerFile构建镜像
在 Docker Hub 中 %99
的镜像都是从这个基础的镜像过来的,然后配置需要的软件和配置来构建
创建一个自己的centos
我们在官方下载的centos镜像是阉割版的,很多基础命令无法使用,vim
, ifconfig
等,现在使用DockerFile构建一个新的centos把这两个命令添加进去
# 编写dockerfile文件
FROM centos
MAINTAINER scl<123456@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 777
CMD echo "$MYPATH"
CMD echo "---end---"
CMD /bin/bash
# 构建镜像
-f dockerfile的路径
-t ‘镜像名:[tag]’
[root@localhost dockerfile]# docker build -f dockerfile-centos -t mycentos:1.0 .
...
Successfully built 347eac4d8903
Successfully tagged mycentos:1.0
# 出现这个就构建成功了
...
# 查看images
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mycentos 1.0 347eac4d8903 4 minutes ago 291MB
# 运行测试
[root@localhost ~]# docker run -it mycentos:1.0
[root@35a6ac33be4b local]# vi
vi view vigr vim vimdiff vimtutor vipw
[root@35a6ac33be4b local]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
RX packets 8 bytes 656 (656.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
# 发现新加的vim和ifconfig命令都可以使用了
# 可以通过history查看别人的镜像怎么构建的
[root@localhost ~]# docker history mycentos:1.0
IMAGE CREATED CREATED BY SIZE COMMENT
347eac4d8903 46 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
acf27fc4746d 46 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
006f9aa94a91 46 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
4dd1e6594c91 46 minutes ago /bin/sh -c #(nop) EXPOSE 777 0B
f44dbb7c7caf 46 minutes ago /bin/sh -c yum -y install net-tools 23.3MB
dc2c37c873d8 46 minutes ago /bin/sh -c yum -y install vim 58MB
29cb5ccbc0d1 47 minutes ago /bin/sh -c #(nop) WORKDIR /usr/local 0B
ac53dc0d97ab 47 minutes ago /bin/sh -c #(nop) ENV MYPATH=/usr/local 0B
35c356ba27c1 47 minutes ago /bin/sh -c #(nop) MAINTAINER scl<123456@qq.… 0B
300e315adb2f 4 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 4 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 4 months ago /bin/sh -c #(nop) ADD file:bd7a2aed6ede423b7… 209MB
CMD 和 ENTRYPOINT 的区别
CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
- cmd测试
# 编写 DockerFile 文件
[root@localhost dockerfile]# vim dockerfile-cmd-test
FROM centos
CMD ["ls","-a"]
# 构建镜像
[root@localhost dockerfile]# docker build -f dockerfile-cmd-test -t cmdtest .
# 运行镜像
[root@localhost dockerfile]# docker run -it 86dc225f6a27
. .dockerenv dev home lib64 media opt root sbin sys usr
.. bin etc lib lost+found mnt proc run srv tmp var
# 运行镜像的时候加上一个参数,发现会报错
# cmd构建的镜像,后面的命令会替代DockerFile里的命令参数,而 -l 也不是完整的命令所以报错
# 所以在cmd构建的镜像中容器运行时添加的命令参数必须是完整的命令+参数
[root@localhost dockerfile]# docker run -it 86dc225f6a27 -l
ERRO[0000] error waiting for container: context canceled
[root@localhost dockerfile]# docker run -it 86dc225f6a27 ls -al
total 0
drwxr-xr-x 1 root root 6 Apr 20 18:40 .
drwxr-xr-x 1 root root 6 Apr 20 18:40 ..
-rwxr-xr-x 1 root root 0 Apr 20 18:40 .dockerenv
lrwxrwxrwx. 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 360 Apr 20 18:40 dev
drwxr-xr-x. 1 root root 66 Apr 20 18:40 etc
drwxr-xr-x. 2 root root 6 Nov 3 15:22 home
- entrypoint测试
# 编写 DockerFile 文件
[root@localhost dockerfile]# vim dockerfile-entyrpoint-test
FROM centos
ENTRYPOINT ["ls","-a"]
# 构建镜像
[root@localhost dockerfile]# docker build -f dockerfile-entyrpoint-test -t entyrpointtest .
# 运行镜像
[root@localhost dockerfile]# docker run -it entyrpointtest
. .dockerenv dev home lib64 media opt root sbin sys usr
.. bin etc lib lost+found mnt proc run srv tmp var
# 发现DockerFile里面指定的 ls -a 在容器运行的时候会被执行
# 那现在我们在容器运行的时候加上一个参数看看会发生什么
# 直接类似于在DockerFile文件中的 ENTRYPOINT ["ls","-a","-l"] 追加了参数
[root@localhost dockerfile]# docker run -it entyrpointtest -l
total 0
drwxr-xr-x 1 root root 6 Apr 20 18:24 .
drwxr-xr-x 1 root root 6 Apr 20 18:24 ..
-rwxr-xr-x 1 root root 0 Apr 20 18:24 .dockerenv
lrwxrwxrwx. 1 root root 7 Nov 3 15:22 bin -> usr/bin
drwxr-xr-x 5 root root 360 Apr 20 18:24 dev
drwxr-xr-x. 1 root root 66 Apr 20 18:24 etc
drwxr-xr-x. 2 root root 6 Nov 3 15:22 home
tomcat镜像
1、准备镜像文件 Tomcat 压缩包,jdk的压缩包
[root@localhost ~]# mkdir /scl/build/tomcat
[root@localhost tomcat]# ll
total 165956
-rw-r--r-- 1 root root 10914435 Sep 22 2020 apache-tomcat-9.0.24.tar.gz
-rw-r--r-- 1 root root 159019376 Apr 20 20:40 jdk-8u11-linux-x64.tar.gz
2、编写DockerFile文件
官方命名Dockerfile,这样在build的时候就不需要使用-f去指定了,build会自动找到DockerFile文件
[root@localhost tomcat]# vim Dockerfile
FROM centos
MAINTAINER whell<1539163444@qq.com>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u11-linux-x64.tar.gz /usr/local
ADD apache-tomcat-9.0.24.tar.gz /usr/local
RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.24
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.24
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.24/bin/startup.sh && tail -f /usr/local/apache-tomcat-9.0.24/logs/catalina.out
3、构建镜像
[root@localhost tomcat]# docker build -t tomcat-diy .
...
Successfully built 1cf20abcb26a
Successfully tagged diy-tomcat:latest
# 查看镜像
[root@localhost tomcat]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mytomcat latest d6f25b8d2b51 2 minutes ago 607MB
centos latest 300e315adb2f 4 months ago 209MB
4、启动运行
[root@localhost tomcat]# docker run -d --name mytomcat01 -p 9090:8080 -v /root/scl/build/tomcat/test:/usr/local/apache-tomcat-9.0.24/webapps/test -v /root/scl/build/tomcat/tomcatlogs:/usr/local/apache-tomcat-9.0.24/logs d6f25b8d2b51
[root@localhost tomcat]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
866d8da21035 d6f25b8d2b51 "..." ... :9090->8080/tcp, :::9090->8080/tcp mytomcat01
5、测试
# 浏览器访问tomcat首页正常
http://192.168.100.200:9090/
# 进去挂载目录创建一个jsp文件
[root@localhost tomcat]# cd test/
[root@localhost test]# pwd
/root/scl/build/tomcat/test
[root@localhost test]# cat index.jsp
<html>
<head>
<title>test page</title>
</head>
<body>
<%
out.println("Hello World");
%>
</body>
</html>
# 访问验证
[root@localhost test]# curl http://192.168.100.200:9090/test/
<html>
<head>
<title>test page</title>
</head>
<body>
Hello World
</body>
</html>
# 或者浏览器访问,http://192.168.100.200:9090/test/
#会发现我们在本机挂载目录修改的项目会自动同步到容器内部,日志文件也会同步,不用经常的进出容器了
[root@localhost tomcatlogs]# ll
total 24
-rw-r-----. 1 root root 6566 Apr 26 17:48 catalina.2021-04-26.log
-rw-r-----. 1 root root 6566 Apr 26 17:48 catalina.out
-rw-r-----. 1 root root 0 Apr 26 17:48 host-manager.2021-04-26.log
-rw-r-----. 1 root root 408 Apr 26 17:48 localhost.2021-04-26.log
-rw-r-----. 1 root root 1370 Apr 26 18:26 localhost_access_log.2021-04-26.txt
-rw-r-----. 1 root root 0 Apr 26 17:48 manager.2021-04-26.log
发布镜像
发布镜像到dockerhub
-
地址
https://hub.docker.com/
注册自己的账号 -
登录账号
-
在服务器上提交镜像
# docker登录成功
-u # dockerhub的用户名
-p # dockerhub的密码
[root@localhost test]# docker login -u 1539163444
Password:
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 test]#
# 增加一个tag
# 注意:添加tag时必须在前面加上自己的dockerhub的username
[root@localhost ~]# docker tag d6f25b8d2b51 1539163444/tomcat:1.0
[root@localhost tomcat]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
1539163443/mytomcat-diy 1.0 d6f25b8d2b51 About a minute ago 607MB
mytomcat-diy latest d6f25b8d2b51 About a minute ago 607MB
# 提交 docker push
[root@localhost tomcat]# docker push 1539163444/mytomcat-diy:1.0
The push refers to repository [docker.io/1539163444/mytomcat-diy]
581b8a0b4636: Pushing [=====> ] 6.001MB/58.03MB
b7ade13dd0f4: Pushing [==========> ] 3.151MB/15.42MB
917daac03648: Pushing [> ] 3.266MB/324MB
1c9697fc6ef2: Pushed
2653d992f4ef: Pushing [> ] 2.167MB/209.3MB
# 可以发现提交的时候也是按照镜像的层级来提交的
# 提交成功之后,可以在dockerhub上面看到我们提交的镜像,也可以使用docker pull下载我们自己的镜像
[root@localhost tomcat]# docker pull 1539163444/centos:1.0
1.0: Pulling from 1539163443/centos
7a0437f04f83: Downloading [=======================> ] 36.08MB/75.18MB
809ba0bafaa3: Downloading [====================================> ] 20.6MB/28.43MB
bab541a28d78: Download complete
7. docker网络
理解docker0
docker创建的容器的网络能否与宿主机(Linux服务器)互通
# 启动一个tomcat镜像
[root@localhost ~]# docker run -d --name tomcat01 tomcat
# 查看一下容器内部的ip
[root@localhost ~]# docker exec -it tomcat01 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
24: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 查看外部服务器的ip
[root@localhost ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:4d:60:46 brd ff:ff:ff:ff:ff:ff
inet 192.168.100.200/24 brd 192.168.100.255 scope global noprefixroute ens33
valid_lft forever preferred_lft forever
inet6 fe80::7a7b:9a8d:634f:34bb/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:d8:8d:47:25 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:d8ff:fe8d:4725/64 scope link
valid_lft forever preferred_lft forever
25: vethbe938ed@if24: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 02:6f:ad:d1:9d:b2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::6f:adff:fed1:9db2/64 scope link
valid_lft forever preferred_lft forever
# 发现docker0的ip和容器内部的IP(docker分配!)相同网段
# 可以直接ping通
172.17.0.1 172.17.0.2
[root@localhost ~]# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.115 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.041 ms
原理
我们每启动一个容器,docker都会分配一个ip给容器,我们只要安装了docker就会有一个网卡docker0
测试:
# 再启动一个容器
[root@localhost ~]# docker run -d --name tomcat02 tomcat
# 容器内ip
[root@localhost ~]# docker exec -it tomcat02 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
26: eth0@if27: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 容器外ip
[root@localhost ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:4d:60:46 brd ff:ff:ff:ff:ff:ff
inet 192.168.100.200/24 brd 192.168.100.255 scope global noprefixroute ens33
valid_lft forever preferred_lft forever
inet6 fe80::7a7b:9a8d:634f:34bb/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:d8:8d:47:25 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:d8ff:fe8d:4725/64 scope link
valid_lft forever preferred_lft forever
25: vethbe938ed@if24: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 02:6f:ad:d1:9d:b2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::6f:adff:fed1:9db2/64 scope link
valid_lft forever preferred_lft forever
27: veth62d9317@if26: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether fe:2f:46:ad:a4:d7 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::fc2f:46ff:fead:a4d7/64 scope link
valid_lft forever preferred_lft forever
### 发现没创建一个容器,服务器就会多出一个网卡,并且服务器多出的网卡和每个容器内的网卡是可以对的上好的
# 我们发现这个容器带来的网卡都是一对一对的出现
# Veth-pair 就是一对虚拟设备接口,他们都是成对出现,一段连着协议,一段彼此连接
# 正是因为这个特性, veth-pair 充当一个桥梁,连接各种虚拟网络设备
# openstack、docker容器之间的互连,OVS的连接,都是使用 veth-pair 这个技术
刚刚启动了2个容器,这两个容器是否能ping通
[root@localhost ~]# docker exec -it tomcat01 ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.286 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.079 ms
网络模型图
结论
tomcat01 和 tomcat02 是共用的一个路由器也就是 docker0
所有的容器不指定网络的情况下,都是由 docker0 来路由的,docker 会给我们的容器分配一个默认的可用ip
容器之间的连接都是通过Veth-pair技术来转发的
docker所有的网络接口都是虚拟的,虚拟的转发效率高
–link
docker0的问题:他不支持容器名连接访问!
通过名字来访问容器
# 通常通过名字无法访问
[root@localhost ~]# docker exec -it tomcat01 ping tomcat02
ping: tomcat02: Name or service not known
# 启动一个Tomcat03
[root@localhost ~]# docker run -d --name tomcat03 --link tomcat02 tomcat
# 通过 tomcat03 来ping tomcat02,发现使用--link创建后的03可以直接ping 02的名字
[root@localhost ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.091 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.051 ms
# 反过来tomcat02 来ping tomcat03, 发现不行
[root@localhost ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known
查看 docker network inspect
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
6c782efeea0d bridge bridge local
ef72364e9231 host host local
1b4f2b80c780 none null local
[root@localhost ~]# docker network inspect 6c782efeea0d
{
"Name": "bridge",
"Id": "6c782efeea0d82c2a0abefb3da0c0ca29424f9e225a699e4d70a094f3cb42a34",
"Created": "2021-04-27T14:46:01.820447557+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default", # 这就是我们的docker0
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
## 三个容器的详细信息
"ConfigOnly": false,
"Containers": {
"3460fad21735d72aec11c37c701ebf4fb737dab1eb464d5724e495d6cbc8499b": {
"Name": "tomcat03",
"EndpointID": "3c6ba0dbcaf55e497c324e2db963fc0e5729f42787b1c4c729c8ecce2952c93b",
"MacAddress": "02:42:ac:11:00:04",
"IPv4Address": "172.17.0.4/16",
"IPv6Address": ""
},
"4a62f71fdac97b2964705e39c7f24f3220b0bf4ec46eca7fdc3f2b811f2fc88f": {
"Name": "tomcat01",
"EndpointID": "c8a48beb2f97d60546346fe9461fca2d0df5ae2336419b4eb28c4f53eaadf307",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"93a0487e38424f9b28004b8133182deb81c625d619bcbcc60e3f7d813bda07d8": {
"Name": "tomcat02",
"EndpointID": "ddb21166e697e32532b466d613a9024b5507b774b7e38fcb70f3ae1523a69525",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
其实Tomcat03 就是 在本地配置了 Tomcat02 的配置
[root@localhost ~]# docker exec -it 3460fad21735 cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 tomcat02 93a0487e3842 # 访问tomcat02这个容器直接映射到本地
172.17.0.4 3460fad21735
结论
--link就是在hosts配置中增加了一个172.17.0.3 tomcat02 93a0487e3842 映射
我们现在玩docker已经不建议使用--link了
自定义网络
查看所有的docker网络
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
6c782efeea0d bridge bridge local
ef72364e9231 host host local
1b4f2b80c780 none null local
网络模式
bridge : 桥接 docker默认
none : 不配置网络
host : 和宿主机共享网络
container: 容器内网络连通(用的少,局限性很大)
测试
# 我们通常直接启动的命令,其实有个默认的参数 ——net bridge ,而这个就是docker0
[root@localhost ~]# docker run -d -P --name tomcat01 tomcat
[root@localhost ~]# docker run -d -P --name tomcat01 --net bridge tomcat
# 自定义一个网络
-d bridge 桥接(默认)
--subnet 192.168.0.0/16 子网
--gateway 192.168.0.1 网关
[root@localhost ~]# docker network create -d bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
6c782efeea0d bridge bridge local
ef72364e9231 host host local
0c61d27ddb34 mynet bridge local
1b4f2b80c780 none null local
# 查看刚创建的网络信息
[root@localhost ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id": "0c61d27ddb3417219a712f1839b2b48d99c9a4e918cd1263ab50e3c7a3d30224",
"Created": "2021-04-28T16:40:08.378640581+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
# 启动两个容器,不走docker0,走我们刚刚创建好的网络mynet
[root@localhost ~]# docker run -d -P --name tomcat-mynet-01 --net mynet tomcat
[root@localhost ~]# docker run -d -P --name tomcat-mynet-02 --net mynet tomcat
# 再次查看我们创建的网络的详细信息,发现创建好的两个容器就在里面了
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
6c782efeea0d bridge bridge local
ef72364e9231 host host local
0c61d27ddb34 mynet bridge local
1b4f2b80c780 none null local
[root@localhost ~]# docker network inspect 0c61d27ddb34
"Containers": {
"499b2d7963e9f7c54081933b808dd22b44726d7a997f46982a6727158d9aa401": {
"Name": "tomcat-mynet-01",
"EndpointID": "0fdbabad2c18af90c7c0c88b38ede357c9236650e6b52a5b39e1c3338ab58b7f",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"71e4675d62cb64055df633c4aa53cf3b955d260982a777c80316bb975e4f3360": {
"Name": "tomcat-mynet-02",
"EndpointID": "f02f3683250ed9e9b4dfae00733afbb90829e056e10df6111fb7c0c68b9afec7",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
# 再次测ping连接
[root@localhost ~]# docker exec -it tomcat-mynet-01 ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.092 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.047 ms
[root@localhost ~]# docker exec -it tomcat-mynet-01 ping tomcat-mynet-02
PING tomcat-mynet-02 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat-mynet-02.mynet (192.168.0.3): icmp_seq=1 ttl=64 time=0.052 ms
64 bytes from tomcat-mynet-02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.051 ms
[root@localhost ~]# docker exec -it tomcat-mynet-02 ping tomcat-mynet-01
PING tomcat-mynet-01 (192.168.0.2) 56(84) bytes of data.
64 bytes from tomcat-mynet-01.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.044 ms
64 bytes from tomcat-mynet-01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.048 ms
# 测试发现
没有使用--link也可以直接ping名字了
容器1 ping 容器2(ip) 可以通
容器1 ping 容器2(名字) 可以通
反过来ping也是可以通的
好处:
- 不同的集群使用不同的网络,保证集群是安全和健康的
网络连通
如图,两个不同网络中的容器,他们之间怎么互连
不同的两个网卡之间是不能连通的,但是容器和网卡是可以连通的
# 创建两个docker0网络中的容器
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat02 tomcat
[root@localhost ~]# docker ps
CONTAINER ID IMAGE CREATED STATUS PORTS NAMES
bd4acf85d4ed tomcat 13 minutes ago Up 13 minutes 0.0.0.0:49158->8080/tcp, :::49158->8080/tcp tomcat02
b8dc0d6c10d0 tomcat 13 minutes ago Up 13 minutes 0.0.0.0:49157->8080/tcp, :::49157->8080/tcp tomcat01
71e4675d62cb tomcat 22 hours ago Up 22 hours 0.0.0.0:49156->8080/tcp, :::49156->8080/tcp tomcat-mynet-02
499b2d7963e9 tomcat 22 hours ago Up 22 hours 0.0.0.0:49155->8080/tcp, :::49155->8080/tcp tomcat-mynet-01
# 尝试用tomcat01 ping tomcat-mynet-01
# 发现不行,他们属于两个不同的网络
[root@localhost ~]# docker exec -it tomcat01 ping tomcat-mynet-01
ping: tomcat-mynet-01: Name or service not known
# 测试打通tomcat01 与 mynet
# 通过docker network connect 网络名 容器名 将一个容器加到某一个网络
# 连通之后就是tomcat01一个容器两个ip
[root@localhost ~]# docker network connect mynet tomcat01
[root@localhost ~]# docker network inspect mynet
"Containers": {
"499b2d7963e9f7c54081933b808dd22b44726d7a997f46982a6727158d9aa401": {
"Name": "tomcat-mynet-01",
"EndpointID": "0fdbabad2c18af90c7c0c88b38ede357c9236650e6b52a5b39e1c3338ab58b7f",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"71e4675d62cb64055df633c4aa53cf3b955d260982a777c80316bb975e4f3360": {
"Name": "tomcat-mynet-02",
"EndpointID": "f02f3683250ed9e9b4dfae00733afbb90829e056e10df6111fb7c0c68b9afec7",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
# 这边看到直接将tomcat01加入到了mynet
"b8dc0d6c10d06fd959319456723f35d93b4152f1f347e4766ccc59624963f3a3": {
"Name": "tomcat01",
"EndpointID": "ba341ab92524604c9f4a1d39551079ed2d8d219925c2a1a9caa1567bff8b2892",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
}
},
# 再次测试,发现可以互通
[root@localhost ~]# docker exec -it tomcat01 ping tomcat-mynet-01
PING tomcat-mynet-01 (192.168.0.2) 56(84) bytes of data.
64 bytes from tomcat-mynet-01.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.218 ms
64 bytes from tomcat-mynet-01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.057 ms
redis集群部署
# 创建redis的网络
[root@localhost ~]# docker network create --subnet 172.38.0.0/16 redis
# 通过脚本创建六个配置文件
#!/bin/bash
for port in $(seq 1 6);do
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-$port/conf/redis.conf
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1$port
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
# 六个节点和配置文件都创建好了
[root@localhost redis]# ls
node-1 node-2 node-3 node-4 node-5 node-6
# 启动redis容器,通过for循环创建六个redis容器
for i in `seq 6`;do
docker run -d --name redis-$i -p 637$i:6379 -p 1637$i:16379 -v /mydata/redis/node-$i/data:/data -v /mydata/redis/node-$i/conf/redis.conf:/etc/redis/redis.conf --net redis --ip 172.38.0.1$i redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
done
# 创建集群
# 随便进去一个redis容器
[root@localhost redis]# docker exec -it redis-1 /bin/sh
/data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
# 验证集群
/data # redis-cli -c
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:252
cluster_stats_messages_pong_sent:249
cluster_stats_messages_sent:501
cluster_stats_messages_ping_received:244
cluster_stats_messages_pong_received:252
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:501
127.0.0.1:6379> cluster nodes
b99032c696586c0de1608350eeeabc8f5dc2c750 172.38.0.14:6379@16379 slave a10c9f0e389d8add55d609ebabb4c190229d9a1a 0 1619765797409 4 connected
8d8b328e095a8e77a5e021551ae715f509934cac 172.38.0.12:6379@16379 master - 0 1619765797000 2 connected 5461-10922
a10c9f0e389d8add55d609ebabb4c190229d9a1a 172.38.0.13:6379@16379 master - 0 1619765797913 3 connected 10923-16383
e755a2159044c9a4a141c804614fea6a5f0ed9f8 172.38.0.11:6379@16379 myself,master - 0 1619765796000 1 connected 0-5460
5c6fe8c46f32b1add603dd407722c03b7784383b 172.38.0.15:6379@16379 slave e755a2159044c9a4a141c804614fea6a5f0ed9f8 0 1619765796401 5 connected
3e91c0e39f423def2a9cbf0cf1b58d96fe1903c3 172.38.0.16:6379@16379 slave 8d8b328e095a8e77a5e021551ae715f509934cac 0 1619765797000 6 connected
# 验证高可用
# set一个值,看到是172.38.0.13(master)处理的
127.0.0.1:6379> set a b
-> Redirected to slot [15495] located at 172.38.0.13:6379
OK
# 那现在把172.38.0.13这个容器停掉
[root@localhost ~]# docker stop redis-3
# 重新连接后,发现从172.38.0.14(slave) get到了这个值
/data # redis-cli -c
127.0.0.1:6379> get a
-> Redirected to slot [15495] located at 172.38.0.14:6379
"b"
# 这个时候查看节点信息
# 发现了172.38.0.13:6379@16379 master,fail,之前的172.38.0.13(master)故障转移
# 发现172.38.0.14 从slave变成了master
# redis集群搭建完毕
172.38.0.14:6379> cluster nodes
3e91c0e39f423def2a9cbf0cf1b58d96fe1903c3 172.38.0.16:6379@16379 slave 8d8b328e095a8e77a5e021551ae715f509934cac 0 1619766297101 6 connected
b99032c696586c0de1608350eeeabc8f5dc2c750 172.38.0.14:6379@16379 myself,master - 0 1619766298000 7 connected 10923-16383
e755a2159044c9a4a141c804614fea6a5f0ed9f8 172.38.0.11:6379@16379 master - 0 1619766299416 1 connected 0-5460
8d8b328e095a8e77a5e021551ae715f509934cac 172.38.0.12:6379@16379 master - 0 1619766298000 2 connected 5461-10922
5c6fe8c46f32b1add603dd407722c03b7784383b 172.38.0.15:6379@16379 slave e755a2159044c9a4a141c804614fea6a5f0ed9f8 0 1619766299000 5 connected
a10c9f0e389d8add55d609ebabb4c190229d9a1a 172.38.0.13:6379@16379 master,fail - 1619766136592 1619766136089 3 connected