Docker

为什么使用docker


 因为深刻理解到一句话:“这个程序可以在我的电脑运行”,但是就是在其他电脑上运行不上,

Docker为什么会出现


在产品开发过程中,环境的配置是十分麻烦的,比如一个项目(redis,MySQL),在不同的人手上,就要重新配置一遍环境,然后就想打包项目能不能带上环境安装打包

个人感觉docker过程就像一个安卓应用从开发到可以在手机上运行的过程:

 在编写安卓应用的时候是用Java或者其他种类的语言来编写,容然后打包成为一个以**.apk**结尾的包,发布到应用商店,然后需要使用的人从应用商店下载这个以**.apk**结尾的安装包,安装就可以使用(Java代码在手机上是不可以运行的,但是通过一系列的打包成为在android环境中可以运行的**apk**安装包)

流程:Java -- apk -- (应用商店)-- 个人下载使用apk -- 下载安装就可以使用

所以docker的流程就像这样:

(编写的项目代码 -- (env)需要的环境 )-- 打包项目带上环境 (镜像(image))-- push到docker商店 (docker hub) -- 下载我们发布的镜像 -- 直接运行(容器 container

就像docker的图标一样,采用隔离的机制,避免了在虚拟机中的端口冲突问题(在docker中可以可以使端口的映射来解决端口的问题)

Docker的历史


2010年,美国的几个it年轻人,成立了一家公司dotcloud,是关于一些虚拟机有关的容器技术,

他们将自己的容器化技术命名为docker,刚开始的时候docker并不出名,2013年通过开源,越来越多的人知道了docker的优点

2014年,docker 1.0 发布,因为十分的轻巧,所以非常火

在容器化技术出现之前,都是使用的虚拟化技术,

在windows中,安装一个vmware,可以虚拟出一个电脑或者多台电脑,但是笨重

vm:linux centos 原生镜像,隔离  需要多个虚拟机   一个虚拟机至少需要几个G,启动时间在分钟级
docker: 隔离,镜像(最核心的环境可能只需要几M的内存,秒级启动)

docker 网址:www.docker.com

在首页的最下面有关于docker的文档 :https://docs.docker.com

docker 仓库:https://hub.docker.com

docker能做什么


比较docker和虚拟机的不同

  • 就像在上图中,关于虚拟机的原理,是在内核的基础上虚拟出我们需要的环境。我们需要分配内存,需要配置cpu,在上图中,有多个APP,但是他们的所需要的环境是不一样的,所以在配置环境的时候,将所有的环境配置在共同的lib中,造成了资源的浪费和冗余,以及端口之间的冲突。
  • 但是在docker中,docker 相对于虚拟机来说,不是在宿主机上虚拟出一套硬件后在虚拟出一个操作系统,而是在宿主机上直接运行docker容器中的进程,形成相应的容器,在容器之中运行自己的APP,每个容器之间相互隔离,都有一个属于自己的文件系统,互不影响,相对来说,docker的体积会更轻.
  • docker中的镜像是分层的,比如上图中的Ubuntu刚开始是72.7 mb,但是自己可以在这个基础的版本上加入自己想要加入的东西,比如MySQL。

关于docker


image(镜像):

就好像是一个模板,通过这个模板来创建容器,一个镜像可以创建多个容器,(最终的项目代码运行就是在容器中的),就像是基本类和由这个类new的对象,image就是基本类,然后new了几个对象,这些对象就像是container,实际上使用的就是这些新的对象

container(容器)

通过镜像来创建,可以看做一合简易的Linux系统,基本操作:启动,停止,删除

(repository)仓库

存放镜像的地方,默认是国外的docker hub

Docker安装


docker网站里面有详细的步骤,推荐使用阿里云的镜像加速。

使用docker version 查看是否安装成功

使用 docker run hello-world 测试

卸载docker

卸载依赖

yum remove docker-ce-cli containerd.io

卸载资源

rm -rf /var/lib/docker docker的默认工作路径

了解一下镜像run hello-world流程

在敲下run命名后,

底层原理


docker是怎么工作的?

docker是一个cs结构的系统,docker的守护进程运行在主机上,通过socket从客户端访问

DockerServer 接收到Docker-client的指令,就会执行这个命令

相比较虚拟机来说,docker的容器少了一个Hypervisor ,少了一个抽象层,所以,新建一个容器的时候,不需要像虚拟机一样重新加载一个操作系统内核,就避免了引导过程(就像以前的老电脑开机时候一行一行的显示状态码一样,这是个很长的过程),docker是利用宿主机的操作系统,

容器内部机制

容器内部的原理就大概像命名空间一样,在Linux操作系统中,有这么一个和命名空间类似的功能,将全局资源共享到进程中,如果将这些资源包装在命名空间中,使得一些资源只对同一命名空间的进程可见。

就比如,我有一块磁盘,放在命名空间为PTD的命名空间中,那么在其他比如PSD的命名空间的进程就不可以查看或者访问我在命名空间为PTD中的磁盘。当然,PTD中的进程也不可以访问PSD 中的资源。为全局资源提供了一种虚拟化和隔离。

Docker,就是这样,每个容器在自己的‘命名空间’中运行,但是和其他容器一起使用操作系统的内核,隔离是因为内核知道根据命名空间分配相应的进程。

Docker的常用命令

帮助命令


docker version 版本信息

docker info 显示docker的系统信息,包括镜像和容器的数量

docker 命令-- help 命令的帮助命令

https://docs.docker.com 下面的 reference 有docker的全部命令

镜像命令


docker images 查看本地的所有的镜像,有仓库名,版本信息,镜像ID,创建时间,镜像大小五个属性

docker search 搜索镜像

docker pull 下载镜像

docker rmi 删除镜像

docker rmi -f $(docker images -ap)  删除全部的镜像

容器命令


新建一个容器并启动

docker run [option] image

docker run --rm [images ]. 常用来测试,退出容器就会删掉容器

option  说明
--name='Name'  容器名字   MySQL01 mysql02   用来区分容器
-d             后台方式运行
-it            使用交互方式运行,进入容器查看内容
-p             指定容器的端口  -p  8080:8080   -p 主机端口:容器端口 
-P             随机指定端口

docker ps 显示运行的容器

退出容器

exit 直接容器停止并退出

Ctrl + p + q 不停止退出

删除容器

docker rm 容器ID,不能删除正在运行的容器,如果强制删除,rm -f

docker rm -f $(docker ps -aq) 全部删除

启动和停止容器

docker start 容器ID 启动容器

docker restart 容器ID 重启容器

docker stop 容器ID 停止容器

docker kill 容器ID 强制停止容器

volume

使用docker volume --help查看使用幫助

step1:

docker volume create direct_name

step2

docker volume inspect direct_name #get volume location

step3

docker run -d -v direct_name:/home/instance -p 5000:5000 --name rain alert:0.1

常用其他命令


后台启动容器

docker run -d 镜像名

问题:使用docker ps 查看时候,发现容器停止了 ,因为docker使用后台运行,但是必须有一个前台进程,docker发现没有应用,就会自动停止,

查看日志命令

docker logs

shell

docker run -d ubuntu /bin/bash -c "while true;do echo xiaogang;sleep 1;done"

为了使用logs命令,然后在启动ubuntu时候输出

docker logs -ft --tail 10 容器ID

查看容器中的进程信息

docker top [容器ID]

查看镜像的元数据

docker inspect [容器ID]

进入当前正在运行的容器

docker exec -it 容器ID 进入容器后开启一个新的终端

docker attach 容器ID 进入容器正在执行的终端,不会启动新的终端

docker rm -f $(docker ps -aq) 删除所有的容器

拷贝文件到主机上

docker cp

docker cp 容器名:/容器内文件地址 /主机地址

el: 将容器内部的test.java拷贝到主机上,将容器内部的home目录下的test.java拷贝到主机上的home目录下

首先在进入容器内部创建或者找到拷贝的文件,

docker attach 容器名 进入容器

touch file(test.java)

docker cp 容器ID : / home/test.java /home

拷贝是一个手动过程,以后使用 -v volume卷的技术实现拷贝过程

小结


一些例子


安装使用nginx和tomcat

平时使用nginx和tomcat时候的一些部署问题

首先使用docker search 搜索相应的镜像,或者可以在docker的官网里面找到需要的版本,后根据需要下载

然后使用docker run -d --name nginx03 -p 3344:80 nginx 创建相应的容器之后,暴露主机的3344端口给容器的80 端口

最后使用docker exec -it nginx03 /bin/bash 进入创建的容器,比如在tomcat里面的WebApps里面是没有任何东西的,需要使用webapps.dist里面的ROOT里面的文件来实现相应的界面效果,

使用curl localhost:端口号 或者在浏览器里面输入主机 ip:端口号 测试访问

docker镜像


镜像

images是一种轻量级可执行的独立软件包,打包运行环境和基于运行环境开发的软件,包含某个软件所需要的所有的内容,包括代码,库,环境变量和配置文件。

联合文件系统

是一种分层,轻量级并且高性能的文件系统,支持对文件系统的修改作为一次提交来一层层的叠加,将不同的目录挂载到同一个虚拟文件系统下,镜像可以通过分层来继承,但是没有父镜像一说,更多说的是基础镜像,可以根据一个基础的镜像来制作具体应用的镜像

理解:

所有的镜像都起源于一个基础的镜像,当进行修改或者增加新的镜像时候,就会在当前镜像之上,增加新的镜像层

比如说,基于Linux创建一个新的镜像,这是第一层,然后添加Python包,这就是第二层,就是这样一层一层的增加,在不断的增加新的镜像层时候,最后的镜像始终是当前所有镜像的组合

多个Container可以共享基础Image存储,节省存储空间;快速部署 – 如果要部署多个Container,Base Image可以避免多次拷贝,实现快速部署。因为多个Container共享Image,提高多个Container中的进程命中缓存内容的几率。

容器数据卷


数据,在创建一个容器过程中,如果容器被删除,数据就会丢失。那么实现数据持久化就是一个问题。

方式1:直接使用命令挂载

docker run -it -v 主机目录:容器目录

使用docker inspect 容器id

例子:

使用MySQL镜像创建相应的容器之后,如果容器被删除,那么数据库里面的相应数据也会被删除,使用数据卷挂载的技术,使容器的数据在外部也可以进行相应的操作,相当于备份。

docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql

在这一行中

-d 表示后台运行容器

-v 进行容器和宿主机之间的挂载

-p 实现端口的暴露

--name 如果要根据一个镜像启动多个容器,那么可以使用--name 对不同的容器之间进行区分

具名和匿名挂载


三种挂载方式:

-v  容器内路径      #匿名挂载
-v  卷名:容器内路径 #具名挂载
-v  /主机路径:容器内部路径  #指定路径挂载

相应的,在挂载路径后面加上ro或者rw ,表示onlyread和readwrite,只读和可读可写

DockerFile

是用来构建docker镜像的文件,命令参数脚本。

构建步骤:

1、编写一个dockerfile文件

2、docker build构建一个镜像

3、docker run 运行镜像

4、docker push 发布镜像(dockershub 阿里云镜像仓库)

官方的镜像中,很多都是基础版本,所以我们需要自己制作自己的镜像

dockerfile构建过程


基础知识

每个关键字都是大写的字母

执行顺从上倒下

井号表示注释

每个指令都会创建提交一个新的镜像层,每一行命令都是一层

dockerfile是面向开发的,发布项目,做镜像,就需要dockerfile文件,

docker镜像成为交付的标准。必须要掌握。

dockerfile:构建文件,定义了一切的步骤,源代码

dockerimage:通过dockerfile构建生成的镜像,最终发布和运行的产品

docker容器:容器就是镜像运行起来提供服务

dockerfile的指令


通过使用这些指令,来写自己 的镜像

例子:创建一个自己的nginx

FROM NGINX
MAINTAINER  Rain<daigang344@163.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "----end----"
CMD /bin/bash

通过这个编写的文件构建镜像

docker build-f dockerfile文件路径 -t 镜像名:[tag]

使用docker history 查看更改历史

提交自己的镜像


使用方法和git一样

1、在hub docker https://hub.docker.com/上面注册自己的账号 在本地使用docker login -u 登录 会要求输入自己的密码

2、使用docker push 镜像名 发送到自己的仓库

提交到阿里云镜像仓库


1、登录阿里云, 创建相应的仓库和命名空间

2、相应的步骤阿里云上有

小结


在上图中,使用dockerfile制作一个镜像,命令是docker build 镜像名

使用docker run 启动一个容器使用docker push 发送到远端仓库,使用start stop restart 启动 停止 重启 容器

使用 docker pull 从远端仓库拉取相应的镜像,使用 docker save -o 压缩镜像,使用 docker load 解压缩镜像,tag是镜像的标签,在发布镜像时候会给镜像加上相应的版本,

使用docker commit 提交新的镜像(假设我们在docker中运行了一个Tomcat容器,我们在tomcat容器中做了一些我们自定义的修改,然后我们这个修改的tomcat容器进行commit,这样我们就形成了一个新的自定义镜像 )命令是docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名]

关于docker的操作上满这幅图中做了详细的解释

docker 网络


在Linux中,每启动一个docker 容器,都会分配一个IP地址,所有的容器在不指定网络的情况下,都是docker 0路由的,

容器内部的网卡信息

docker 使用的是Linux的桥接技术,宿主机中是一个docker的容器网桥

容器中的网卡都是成对出现的,evth-pair 就是一对虚拟的设备接口,一端连着协议,一端连着彼此,利用这个特性,连接各种虚拟设备,

port      # 查看映射端口对应的容器内部源端口
pause     # 暂停容器
ps        # 猎户容器列表
pull      # 从docker镜像源服务器拉取指定镜像或者库镜像
push      # 推送指定镜像或者库镜像至docker源服务器
restart   # 重启运行的容器
rm        # 移除一个或多个容器
rmi       # 移除一个或多个镜像 (无容器使用该镜像才可删除,否则需要删除相关容器才可继续或 -f 强制删除)
run       # 创建一个新的容器并运行一个命令
save      # 保存一个镜像为一个 tar 包【对应 load】
search    # 在 docker hub 中搜索镜像
start     # 启动容器
stop      # 停止容器
tag       # 给源中镜像打标签
top       # 查看容器中运行的进程信息
unpause   # 取消暂停容器
version   # 查看 docker版本号
wait      # 截取容器停止时的退出状态值

一些基本的命令。

docker images 查看本地镜像

docker run -d --name 容器名(可以是多个) -p 宿主机端口:容器端口 容器名

上一句命令主要是用在部署服务器上面,在一个容器中运行多个服务

自定义网络

查看所有的docker 网络

网络模式

bridge 桥接模式。默认

none 不配置网络

host. share network with host

Container 容器网络联通

测试

docker run -d -P --nama tomcat01 tomcat 
docker run -d -P --name tomcat01 --net bridge tomacat

# 自定义网络
# bridge 
docker network create --driver bridge  --subnet 192.168.0.0/16 --geteway 192.168.0.1 mynet
root@xiaogang:~# docker network create --driver bridge  --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
c99b09dcc450d3ab2d2f6a8bb81c4a5afa0f17d42cfa5718aa5e1a8a5e9e3642
root@xiaogang:~# 

root@xiaogang:~# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
4c696778bfab        bridge              bridge              local
db42b2ce883e        host                host                local
c99b09dcc450        mynet               bridge              local
0f13dfc2aac3        none                null                local
root@xiaogang:~#

网络连通

测试 docker01 dao 自定义网络之间的容器连通

root@xiaogang:~# docker network connect mynet tomcat01


将tomcat01 加入到mynet网段中
一个容器两个IP

部署redis集群

docker network create redis --subnet 172.38.0.0/16
for port in $(seq 1 6); do
  echo "Creating directory for node-${port}"
  mkdir -p /mydata/redis/node-${port}/conf
  if [ $? -eq 0 ]; then
    echo "Directory created successfully"
  else
    echo "Failed to create directory"
    exit 1
  fi

  echo "Creating redis.conf for node-${port}"
  touch /mydata/redis/node-${port}/conf/redis.conf
  if [ $? -eq 0 ]; then
    echo "redis.conf created successfully"
  else
    echo "Failed to create redis.conf"
    exit 1
  fi

  echo "Writing configuration to redis.conf"
  cat << EOF > /mydata/redis/node-${port}/conf/redis.conf
port 6379
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
  if [ $? -eq 0 ]; then
    echo "Configuration written successfully"
  else
    echo "Failed to write configuration"
    exit 1
  fi
done
# 创建容器的脚本

docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /mydata/redis/node-1/data:/data \
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.11 redis redis-server /etc/redis/redis.conf

docker run -p 6372:6379 -p 16372:16379 --name redis-2 \
-v /mydata/redis/node-2/data:/data \
-v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.12 redis redis-server /etc/redis/redis.conf

docker run -p 6373:6379 -p 16373:16379 --name redis-3 \
-v /mydata/redis/node-3/data:/data \
-v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.13 redis redis-server /etc/redis/redis.conf

docker run -p 6374:6379 -p 16374:16379 --name redis-4 \
-v /mydata/redis/node-4/data:/data \
-v /mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.14 redis redis-server /etc/redis/redis.conf

docker run -p 6375:6379 -p 16375:16379 --name redis-5 \
-v /mydata/redis/node-5/data:/data \
-v /mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.15 redis redis-server /etc/redis/redis.conf

docker run -p 6376:6379 -p 16376:16379 --name redis-6 \
-v /mydata/redis/node-6/data:/data \
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.16 redis redis-server /etc/redis/redis.conf
创建集群
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
# 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
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: e7700cf1bea4a79a1613dcf5f6fd046607738a74 172.38.0.11:6379
   slots:[0-5460] (5461 slots) master
M: 1133afbd48c55cb7dde887a5438dc59d1e7038a2 172.38.0.12:6379
   slots:[5461-10922] (5462 slots) master
M: 741b4a8ac77cd79b7a09f42310ef2771dd2e9adb 172.38.0.13:6379
   slots:[10923-16383] (5461 slots) master
S: 4a53d12a49b2b4d5a3f8b7cacd6c267d4076eae4 172.38.0.14:6379
   replicates 741b4a8ac77cd79b7a09f42310ef2771dd2e9adb
S: 9c336e0b3bd3c6804a3f09bc75561fa7722a87ad 172.38.0.15:6379
   replicates e7700cf1bea4a79a1613dcf5f6fd046607738a74
S: fe6c4a3d6fc34330a9ac70ac251c6f83bf1fa32e 172.38.0.16:6379
   replicates 1133afbd48c55cb7dde887a5438dc59d1e7038a2
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: e7700cf1bea4a79a1613dcf5f6fd046607738a74 172.38.0.11:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: 741b4a8ac77cd79b7a09f42310ef2771dd2e9adb 172.38.0.13:6379
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: fe6c4a3d6fc34330a9ac70ac251c6f83bf1fa32e 172.38.0.16:6379
   slots: (0 slots) slave
   replicates 1133afbd48c55cb7dde887a5438dc59d1e7038a2
S: 4a53d12a49b2b4d5a3f8b7cacd6c267d4076eae4 172.38.0.14:6379
   slots: (0 slots) slave
   replicates 741b4a8ac77cd79b7a09f42310ef2771dd2e9adb
M: 1133afbd48c55cb7dde887a5438dc59d1e7038a2 172.38.0.12:6379
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 9c336e0b3bd3c6804a3f09bc75561fa7722a87ad 172.38.0.15:6379
   slots: (0 slots) slave
   replicates e7700cf1bea4a79a1613dcf5f6fd046607738a74
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 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:73
cluster_stats_messages_pong_sent:79
cluster_stats_messages_sent:152
cluster_stats_messages_ping_received:74
cluster_stats_messages_pong_received:73
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:152
127.0.0.1:6379> cluster nodes
741b4a8ac77cd79b7a09f42310ef2771dd2e9adb 172.38.0.13:6379@16379 master - 0 1740718606000 3 connected 10923-16383
fe6c4a3d6fc34330a9ac70ac251c6f83bf1fa32e 172.38.0.16:6379@16379 slave 1133afbd48c55cb7dde887a5438dc59d1e7038a2 0 1740718606000 2 connected
4a53d12a49b2b4d5a3f8b7cacd6c267d4076eae4 172.38.0.14:6379@16379 slave 741b4a8ac77cd79b7a09f42310ef2771dd2e9adb 0 1740718607863 3 connected
e7700cf1bea4a79a1613dcf5f6fd046607738a74 172.38.0.11:6379@16379 myself,master - 0 1740718606000 1 connected 0-5460
1133afbd48c55cb7dde887a5438dc59d1e7038a2 172.38.0.12:6379@16379 master - 0 1740718607000 2 connected 5461-10922
9c336e0b3bd3c6804a3f09bc75561fa7722a87ad 172.38.0.15:6379@16379 slave e7700cf1bea4a79a1613dcf5f6fd046607738a74 0 1740718607000 1 connected
127.0.0.1:6379> 
127.0.0.1:6379> set a b
-> Redirected to slot [15495] located at 172.38.0.13:6379对应的容器redis-3停掉
OK
172.38.0.13:6379> get a
Error: Server closed the connection
172.38.0.13:6379> exit
# redis-cli -c
# 在这一步之前将172.38.0.13:6379对应的容器redis-3停掉
127.0.0.1:6379> get a
-> Redirected to slot [15495] located at 172.38.0.14:6379
"b" 
# 会发现在172.38.0.14:6379中拿到数据 
172.38.0.14: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:7
cluster_my_epoch:7
cluster_stats_messages_ping_sent:458
cluster_stats_messages_pong_sent:452
cluster_stats_messages_meet_sent:1
cluster_stats_messages_auth-req_sent:5
cluster_stats_messages_sent:916
cluster_stats_messages_ping_received:447
cluster_stats_messages_pong_received:458
cluster_stats_messages_fail_received:1
cluster_stats_messages_auth-ack_received:2
cluster_stats_messages_received:908
172.38.0.14:6379> cluster nodes
4a53d12a49b2b4d5a3f8b7cacd6c267d4076eae4 172.38.0.14:6379@16379 myself,master - 0 1740718819000 7 connected 10923-16383
fe6c4a3d6fc34330a9ac70ac251c6f83bf1fa32e 172.38.0.16:6379@16379 slave 1133afbd48c55cb7dde887a5438dc59d1e7038a2 0 1740718818855 2 connected
9c336e0b3bd3c6804a3f09bc75561fa7722a87ad 172.38.0.15:6379@16379 slave e7700cf1bea4a79a1613dcf5f6fd046607738a74 0 1740718818554 1 connected
e7700cf1bea4a79a1613dcf5f6fd046607738a74 172.38.0.11:6379@16379 master - 0 1740718819858 1 connected 0-5460
741b4a8ac77cd79b7a09f42310ef2771dd2e9adb 172.38.0.13:6379@16379 master,fail - 1740718720566 1740718718000 3 connected
1133afbd48c55cb7dde887a5438dc59d1e7038a2 172.38.0.12:6379@16379 master - 0 1740718820561 2 connected 5461-10922
172.38.0.14:6379> get a
"b"
172.38.0.14:6379>