Docker

  • 1. Docker 入门
    • 1.1 Docker 是什么
    • 1.2 Docker 和 虚拟机
    • 1.3 镜像 容器 仓库
    • 1.4 Docker 架构
    • 1.5 Docker 安装
    • 1.6 docker run hello-world
  • 2. Docker 常用命令
    • 2.1 镜像命令
    • 2.2 容器命令
  • 3. 数据持久化
    • 3.1 数据卷
    • 3.2 挂载
  • 4. Docker 安装软件
    • 4.1 Redis
    • 4.2 MySQL
    • 4.3 MySQL 主从复制
      • Linux
      • Docker
    • 4.4 Redis 分布式存储方案
      • 哈希取余分区
      • 一致性哈希算法分区
      • 哈希槽分区
    • 4.5 Redis 3主3从集群配置
    • 4.6 Redis 主从容错切换迁移
    • 4.7 Redis 主从扩容
    • 4.8 Redis 主从缩容
    • 4.9 上述操作流程图
  • 5. 镜像结构
    • 5.1 镜像分层结构
    • 5.2 docker commit
  • 6. Dockerfile
  • 7. 镜像仓库

1. Docker 入门

1.1 Docker 是什么

大型项目组件较多,运行环境也比较复杂,部署时存在一些问题,例如:依赖关系复杂,容易出现兼容性问题开发、测试、生产环境有差异

不同环境的操作系统不同,Docker 如何解决?

操作系统结构

  1. Linux 内核与计算机硬件交互,提供操作硬件的指令。
  2. 系统应用封装内核指令为函数,提供给程序员调用。
  3. 程序基于系统函数库实现功能。

Ubuntu 和 CentOS 都是基于 Linux 内核的,只是系统应用不同,提供的函数库有所差异。

综上所述,Docker 解决不同环境的操作系统不同的方式:

  • Docker 将程序与所需要调用的系统函数库一起打包。
  • Docker 运行到不同的操作系统时,直接基于打包的函数库,借助操作系统的 Linux 内核运行。

Docker 如何解决大型项目依赖关系复杂,不同组件依赖的兼容性问题?

  • Docker 将应用、函数库、依赖、配置一起打包,形成 可移植镜像
  • 应用运行在容器中,使用沙箱机制,相互隔离。

Docker 如何解决开发、测试、生产环境有差异的问题?

Docker 镜像中包含完整运行环境,包括系统函数库,无需考虑环境差异,只需要依赖系统的 Linux 内核,可以在任何 Linux 操作系统上运行。

Docker 是一个 快速交付应用、运行应用的技术。通俗的说,Docker 完成的事情就是 从搬家到搬楼

  1. 可以将程序及其依赖、运行环境一起打包成一个镜像,可以在任意 Linux 操作系统中运行,镜像即应用。

    • 镜像技术:打破代码即应用的概念,从系统环境开始,自下而上的打包应用。
  2. 运行时利用沙箱机制形成隔离容器,各个应用互不干扰。
  3. 启动、移除都可以通过一行命令完成,方便快捷。

1.2 Docker 和 虚拟机

虚拟机

利用 Hypervisor 技术模拟出计算机的硬件设备,在这个模拟出的计算机上安装操作系统,可以在该操作系统上安装任意适合的 应用、依赖、函数库。

虚拟机是在一个操作系统中装了另外一个操作系统,应用执行时以为自己在一台真实的电脑上,因此先调用内部的操作系统,操作系统再与 Hypervisor 交互,将信息传递给外部操作系统,外部操作系统再去调用计算机硬件。经过层层传递,性能较差。

Docker

Docker 有着比虚拟机更少的抽象层。

  • Docker 不需要 Hypervisor 实现硬件资源虚拟化,运行在 Docker 容器上的程序使用的是实际物理机的硬件资源。
  • 因此在 CPU 、内存利用率上,Docker 将会在效率上有明显的优势。

Docker 利用的是宿主机的内核。

  • Docker 将应用及其需的依赖、函数库、甚至操作系统函数库一起打包,应用运行时直接调用本地函数库,与 Linux 内核进行交互。

  • Docker 是一个系统进程;虚拟机是在操作系统中的操作系统。
  • Docker 体积小、启动速度快、性能好;虚拟机体积大、启动速度慢、性能一般。

1.3 镜像 容器 仓库

镜像(Image):Docker 将应用程序及其所需的依赖、函数库、环境、配置等文件打包成一个可交付的运行环境,这个运行环境就是为镜像。

  • 例如:一个 MySQL 镜像中包含各种文件(data 目录文件、logs 目录文件、bin 目录文件等),以及 MySQL 需要的函数库等等,镜像就是硬盘中的文件。

容器(Container):镜像中的应用程序运行后形成的进程就是容器,Docker 会给容器做隔离,对外不可见。

  • 将 MySQL 镜像运行起来,所启动的进程就是容器。
  • 容器需要进行隔离,让容器内有独立的 CPU 资源、内存资源、甚至文件系统,在该容器内运行的进程会以为自己是计算机中的唯一进程,从而起到隔离作用。不论镜像运行出多少个容器,容器间都是相互隔离的。
  • 基于镜像创建容器,容器从镜像中读取数据,但是不能向镜像中写数据,镜像是只读的。可以从镜像中读取的数据拷贝到容器中,然后进行修改。

仓库(Repository):集中存放镜像的地方。

  • 镜像构建完成后,可以在当前主机上运行,若需要在其他服务器上使用该镜像,需要一个集中的存储和分发镜像的服务,Docker Registry 就是这样的服务。Docker 官方提供的 Registry 称为 DockerHub。
  • 一个 Docker Registry 可以包含多个仓库(Repository),每个仓库可以包含多个标签(Tag),每个标签对应一个镜像。
  • 一个仓库会包含同一个软件不同版本的镜像,标签就对应该软件的各个版本。可以通过 <仓库名>:<标签> 的格式指定具体是该软件哪个版本的镜像,标签默认为 latest

1.4 Docker 架构

Docker 是一个 Client-Server 模式的架构,由两部分组成:

服务端(Server):Docker Daemon(守护进程),负责处理 Docker 指令,管理镜像、容器等。

客户端(Client):通过命令向 Docker 服务端发送指令。

  • Docker Client 是与 Docker Daemon 建立通信的客户端。
  • 可执行文件为 docker,使用 docker + 参数 的形式。例如:docker iamgesdocker 为不可变参数,images 为可变参数。

1.5 Docker 安装

Uninstall Old Version

sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine

Install Docker

# Install GCC
yum -y install gcc
yum -y install gcc-c++# Install the yum-utils package (which provides the yum-config-manager utility) and set up the repository.
sudo yum install -y yum-utils
sudo yum-config-manager \--add-repo \http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo# 更新 yum 软件包索引
# Cent OS 7
yum makecache fast
# Cent OS 8
yum makecache# Install Docker Engine
yum -y install docker-ce docker-ce-cli containerd.io

Tips

# Start / Stop / Restart / Status
systemctl start docker
systemctl stop docker
systemctl restart docker
systemctl status docker# Set Docker Daemon
systemctl enable docker
systemctl list-unit-files | grep docker
systemctl is-enabled docker# 查看 Docker 概要信息
docker version
docker info

Images Accelerator (aliyun)

# Images Accelerator
mkdir -p /etc/dockertee /etc/docker/daemon.json <<-'EOF'
{"registry-mirrors": ["https://xzstr9ov.mirror.aliyuncs.com"]
}
EOFsystemctl daemon-reloadsystemctl restart docker

Verify Docker Engine is installed corretly by running the hello-world image.

docker run hello-world

Uninstall Docker

systemctl stop dockeryum remove docker-ce docker-ce-cli containerd.iorm -rf /var/lib/docker
rm -rf /var/lib/containerd

1.6 docker run hello-world

[root@VM-8-5-centos docker]# docker run hello-world
Unable to find image 'hello-world:latest' locally         # 无法从本地找到 hello-world:latest 镜像
latest: Pulling from library/hello-world                            # 从 library 仓库拉取
2db29710123e: Pull complete
Digest: sha256:2498fce14358aa50ead0cc6c19990fc6ff866ce72aeb5546e1d59caac3d0d60f
Status: Downloaded newer image for hello-world:latestHello from Docker!
This message shows that your installation appears to be working correctly.  # 下列消息显示安装成功To generate this message, Docker took the following steps:1. The Docker client contacted the Docker daemon.2. The Docker daemon pulled the "hello-world" image from the Docker Hub.(amd64)3. The Docker daemon created a new container from that image which runs theexecutable that produces the output you are currently reading.4. The Docker daemon streamed that output to the Docker client, which sent itto your terminal.
  1. Docker Client 成功连接到 Docker Daemon;
  2. Docker Daemon 从 DockerHub 中拉取到 hello-world 镜像;
  3. Docker Daemon 为该景象创建了一个新容器,该容器运行生成您正在 Reading 的内容;
  4. Docker Daemon 将其传输到 Docker Client,后者将其发送终端。

2. Docker 常用命令

查看 Docker 总体帮助文档docker --help

查看 Docker 命令帮助文档docker 具体命令 --help

2.1 镜像命令

镜像由两部分组成:[repository]:[tag],未指定 tag 时,默认为 latest 最新版本的镜像。

搜索镜像、拉取镜像、查看本地镜像、查看空间占用情况。

# docker search:搜索镜像。
[root@VM-centos ~]# docker search nginx
NAME              DESCRIPTION                         STARS     OFFICIAL   AUTOMATED
nginx             Official build of Nginx.            18097     [OK]
bitnami/nginx     Bitnami nginx Docker Image          150                  [OK]
...# docker pull:拉取镜像。
[root@VM-centos ~]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
a2abf6c4d29d: Pull complete
...
Digest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest# docker images:列出本地主机上的镜像。(-q:只显示镜像 ID)
[root@VM-centos ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
nginx        latest    605c77e624dd   13 months ago   141MB
[root@VM-centos ~]# docker images -q
605c77e624dd# docker system df:查看 镜像、容器、数据卷 所占空间。
[root@VM-centos ~]# docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          1         0         141.5MB   141.5MB (100%)
Containers      0         0         0B        0B
Local Volumes   0         0         0B        0B
Build Cache     0         0         0B        0B

保存镜像为压缩包、删除镜像、加载压缩包为镜像。

# docker save [OPTIONS] IMAGE:保存镜像为压缩包。(-o:输出到一个指定的文件中)
[root@VM-centos ~]# docker save -o nginx.tar nginx
[root@VM-centos ~]# ls
nginx.tar# docker rmi:根据 镜像ID 删除镜像。(-f:forced)
[root@VM-centos ~]# docker rmi -f 605c77e624dd# docker load [OPTIONS]:加载压缩包为镜像。(-i:需要加载的文件)
[root@VM-centos ~]# docker load -i nginx.tar
...
Loaded image: nginx:latest
[root@VM-centos ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
nginx        latest    605c77e624dd   13 months ago   141MB

虚悬镜像 dangling image:仓库名、标签都是 <none> 的镜像,

[root@VM-8-5-centos docker]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
<none>       <none>    d42a6212ff02   2 weeks ago     6.55MB

查看虚悬镜像docker image ls -f dangling=true

[root@VM-8-5-centos docker]# docker image ls -f dangling=true
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
<none>       <none>    d42a6212ff02   2 weeks ago     6.55MB

虚悬镜像无意义,可以删除docker image prune

[root@VM-centos ~]# docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:40bfdb190cea0026b62f0d65731ed19d0ca756714824e43e69b3faf613689e40

2.2 容器命令

  • docker ps [OPTIONS]查看当前所有正在运行的容器

    • -a :查看当前所有正在运行的容器 + 历史上运行过的容器。
  • docker logs CONTAINER查看容器日志
  • docker start / restart / stop / kill / rm / rm - f CONTAINER启动、重启、停止、强制停止、删除、强制删除容器

运行容器docker run --name=nginx -p 80:80 -d nginx

  • --name="xxx" :为容器指定一个名称。
  • -d后台运行 容器并返回容器 ID。(守护式容器,后台运行)
  • -p hostPort:containerPort指定端口映射,将宿主机端口与容器端口映射
    • 假设在服务器上部署一个 Nginx 容器,端口为 80,无法访问到 Nginx,因为容器是对外隔离的。
    • 通过端口映射,将宿主机端口与容器端口映射,此时任何访问宿主机 80 端口的请求都会被转发到容器。
# 运行一个支持数据持久化的 Redis 容器
docker run --name redis -p 6379:6379 -d redis redis-server --appendonly yes

以交互模式进入容器并执行一条命令docker exec -it nginx /bin/bash

  • docker exec:进入容器内部,执行一条命令。
  • -it :为要进入的容器创建一个终端,并且允许与容器进行交互。
  • /bin/bash:进入容器后执行的命令, /bin/bash 是终端交互命令。
# 以交互模式进入 Redis 容器并执行 `redis-cli` 命令
docker exec -it redis redis-cli

3. 数据持久化

Docker 容器与数据耦合,存在很多问题:

  • 不便于修改,需要进入容器内部才能修改。
  • 数据不可复用,容器内的修改对外不可见,所有修改对新建容器是不可复用的。
  • 升级维护困难,数据在容器内,升级容器时需要删除容器,数据也会被删除。

3.1 数据卷

数据卷(volume):数据卷是一个虚拟目录,指向宿主机文件系统中的某个目录。将容器与数据分离,解耦合。

  • 便于修改,宿主机和容器建立通信,在宿主机内修改,容器中实时生效。
  • 数据可复用,容器的 conf 目录挂载到 conf 数据卷,新建容器若想复用 conf,将其 conf 目录同样挂载到 conf 数据卷即可。(一个数据卷可以被多个容器同时挂载)
  • 升级维护删除容器,无需担心数据被删除。

操作数据卷:docker volume [COMMAND]

  • docker volume create xxx:创建数据卷。
  • docker volume ls:查看所有数据卷。
  • docker volume inspect xxx:查看数据卷的详细信息。
  • docker volume rm xxx:删除数据卷。

3.2 挂载

通过 -v 指定:宿主机的目录容器内目录对应关系

  • 数据卷挂载-v volumeName:/targetContainerPath,创建容器时 volume 不存在,则自动创建 volume。

    # 创建容器,容器内的 /usr/share/nginx/html 目录 挂载到 html 数据卷。
    docker run --name=nginx -p 80:80 -v html:/usr/share/nginx/html -d nginx# 在此处修改 index.html 会同步到容器内。
    [root@VM-centos ~]# cd /var/lib/docker/volumes/html/_data/
    [root@VM-8-5-centos _data]# ls
    50x.html  index.html
    
  • 目录挂载-v [宿主机目录/文件]:[容器内目录/文件],先在宿主机中创建好目录,再进行挂载。

    # 创建并运行容器,MySQL 容器内数据存储目录挂载到 /docker/mysql/data、配置文件挂载到 /docker/mysql/conf 中。
    docker run -p 3306:3306 --name=mysql \
    -v /docker/mysql/data:/var/lib/mysql \
    -v /docker/mysql/conf:/etc/mysql/conf.d \
    -e MYSQL_ROOT_PASSWORD=root \
    -d mysql
    

数据卷挂载由 Docker 管理目录;目录挂载由创建者自己管理目录,目录容易查找。

挂载目录出现 cannot open directory. Permission denied 问题:在挂载目录后加一个 --privileged=true 参数扩大容器的权限。

4. Docker 安装软件

4.1 Redis

创建目录 /docker/redis,将 redis.conf 文件模版 copy 到该目录下进行修改。

# 密码验证(可选)
requirepass root
# 允许外地连接:将 bind 127.0.0.1 注释掉
# 防止与 docker run 中 -d 参数冲突
daemonize no
# Redis 数据持久化(可选)
appendonly yes
docker pull redis# 注意:redis-server 的启动需要读取 /etc/redis/redis.conf 配置文件,而该配置文件由 /docker/redis/redis.conf 映射。
docker run -p 6379:6379 --name redis --privileged=true \
-v /docker/redis/redis.conf:/etc/redis/redis.conf \
-v /docker/redis/data:/data \
-d redis

4.2 MySQL

MySQL 5.7

# MySQL 5.7
docker pull mysql:5.7docker run --name=mysql -p 3306:3306 --privileged=true \
-v /docker/mysql/data:/var/lib/mysql \
-v /docker/mysql/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7# 进入 MySQL 容器并查看字符集
docker exec -it mysql /bin/bash
mysql -u root -p
SHOW VARIABLES LIKE 'character%';
# my.cnf
[client]
default-character-set=utf8[mysql]
default-character-set=utf8[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve

MySQL 8

# MySQL 8
docker pull mysqldocker run -p 3306:3306 --name=mysql \
-v /docker/mysql/log:/var/log/mysql \
-v /docker/mysql/data:/var/lib/mysql \
-v /docker/mysql/conf:/etc/mysql/conf.d \
-v /etc/localtime:/etc/localtime:ro \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql# 进入 MySQL 容器并查看字符集
docker exec -it mysql /bin/bash
SHOW VARIABLES LIKE 'character%';
# my.cnf
[client]
default-character-set = utf8mb4[mysql]
default-character-set = utf8mb4[mysqld]
init_connect='SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci'
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
skip-character-set-client-handshake
skip-name-resolve

4.3 MySQL 主从复制

MySQL 主从复制的过程是异步的,主库(Master)负责写,从库(Slave)负责读,读写分离。

MySQL 主从复制(解决 MySQL 单点故障):一台或多台 Slave 将 Master 的 binlog 复制到 Slave 中 ,并将其解析后执行,实现 Slave 和 Master 的数据一致。

主从复制原理:

  1. Master 将数据的变化记录到 binlog(Binary Log)中,并且通知从库,主库中存在数据更新。
  2. Slave 中的 IO 线程会发起请求连接到 Master。
  3. 将 Master 中的 binlog 拷贝到中继日志(Relay Log)中。
  4. 从库中的 SQL 线程检测到中继日志中有更新,更新到从库的数据库中,保证主从的数据同步。

Linux

主库配置

  1. 修改配置文件 /etc/my.cnf,修改完毕后通过 systemctl restart mysqld 重启 MySQL。

    [mysqld]
    # MySQL 服务 ID,保证集群环境中唯一
    server_id=1
    # 是否只读,1 只读,0 读写
    read-only=0
    # 开启二进制日志功能
    log-bin=mysql-bin
    
  2. 登录 MySQL,设置主库配置。

    mysql -u root -p# 登录 MySQL 后:1)创建一个用户名为 sun、密码为 Sun@0705 的用户。2)为该用户授权同步复制权限。
    mysql> CREATE USER 'sun'@'%' IDENTIFIED WITH mysql_native_password BY 'Sun@0705';
    mysql> GRANT REPLICATION SLAVE ON *.* TO 'sun'@'%';# 查看 Master 状态(记录下 File 和 Position 的值)
    mysql> show master status;
    +-----------------------+----------+--------------+------------------+-------------------+
    | File                  | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +-----------------------+----------+--------------+------------------+-------------------+
    | mysql-bin.000001      |      617 |              | mysql            |                   |
    +-----------------------+----------+--------------+------------------+-------------------+
    

从库配置

  1. 修改配置文件 /etc/my.cnf,修改完毕后通过 systemctl restart mysqld 重启 MySQL。

    [mysqld]
    # MySQL 服务 ID,保证集群环境中唯一
    server_id=2
    # 是否只读,1 只读,0 读写
    read-only=1
    
  2. 登录 MySQL,配置主从复制。

    mysql -u root -p# 登录 MySQL 后执行下列 SQL。
    mysql> change master to master_host='10.0.8.5', master_user='sun', master_password='Sun@0705', master_port=3307, master_log_file='mysql-bin.000001', master_log_pos=617;
    # master_log_file:指定从数据库要复制数据的日志文件(File)。
    # master_log_pos:指定从数据库的哪个位置开始复制数据(Position)。# 开启同步操作
    mysql> start slave;
    

Docker

主库容器实例 3307 配置

  1. 创建主库容器实例 3307。

    docker run -p 3307:3306 --name=mysql-master --privileged=true \
    -v /docker/mysql-master/log:/var/log/mysql \
    -v /docker/mysql-master/data:/var/lib/mysql \
    -v /docker/mysql-master/conf:/etc/mysql/conf.d \
    -v /etc/localtime:/etc/localtime:ro \
    -e MYSQL_ROOT_PASSWORD=root \
    -d mysql
    
  2. 修改配置并通过 docker restart mysql-master 重启 mysql-master 实例。

    # /docker/mysql-master/conf/my.cnf
    [mysqld]
    # MySQL 服务 ID,保证集群环境中唯一
    server_id=1
    # 是否只读,1 只读,0 读写
    read-only=0
    # 开启二进制日志功能
    log-bin=mysql-bin
    
  3. 登录 MySQL,设置主库配置。

    # 登录 MySQL:1)创建一个用户名为 sun、密码为 Sun@0705 的用户。2)为该用户授权同步复制权限。
    mysql -u root -p
    mysql> CREATE USER 'sun'@'%' IDENTIFIED WITH mysql_native_password BY 'Sun@0705';
    mysql> GRANT REPLICATION SLAVE ON *.* TO 'sun'@'%';# 查看 Master 状态(记录下 File 和 Position 的值)
    mysql> show master status;
    +------------------+----------+--------------+------------------+-------------------+
    | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +------------------+----------+--------------+------------------+-------------------+
    | mysql-bin.000001 |      658 |              |                  |                   |
    +------------------+----------+--------------+------------------+-------------------+
    

从库容器实例 3308 配置

  1. 创建从库容器实例 3308。

    docker run -p 3308:3306 --name=mysql-slave --privileged=true \
    -v /docker/mysql-slave/log:/var/log/mysql \
    -v /docker/mysql-slave/data:/var/lib/mysql \
    -v /docker/mysql-slave/conf:/etc/mysql/conf.d \
    -v /etc/localtime:/etc/localtime:ro \
    -e MYSQL_ROOT_PASSWORD=root \
    -d mysql
    
  2. 修改配置并通过 docker restart mysql-slave 重启 mysql-slave 实例。

    # /docker/mysql-slave/conf/my.cnf
    [mysqld]
    # MySQL 服务 ID,保证集群环境中唯一
    server_id=2
    # 是否只读,1 只读,0 读写
    read-only=1
    
  3. 登录 MySQL,配置主从复制。

    mysql -u root -p# 登录 MySQL 后执行下列 SQL。
    mysql> change master to master_host='192.168.1.6', master_user='sun', master_password='Sun@0705', master_port=3307, master_log_file='mysql-bin.000001', master_log_pos=658;
    # master_log_file:指定从数据库要复制数据的日志文件(File)。
    # master_log_pos:指定从数据库的哪个位置开始复制数据(Position)。# 开启同步操作
    mysql> start slave;# 查看主从同步状态(Slave_IO_Running、Slave_SQL_Running 均为 YES)
    mysql> show slave status \G;
    *************************** 1. row ***************************Slave_IO_State: Waiting for master to send eventMaster_Host: 10.0.8.5Master_User: slaveMaster_Port: 3307Connect_Retry: 30Master_Log_File: mall-mysql-bin.000001Read_Master_Log_Pos: 617Relay_Log_File: mall-mysql-relay-bin.000002Relay_Log_Pos: 325Relay_Master_Log_File: mall-mysql-bin.000001Slave_IO_Running: YesSlave_SQL_Running: Yes
    

测试

# 查看主从同步状态(Slave_IO_Running、Slave_SQL_Running 均为 YES)
mysql> show slave status \G;
*************************** 1. row ***************************Slave_IO_State: Waiting for master to send eventMaster_Host: 10.0.8.5Master_User: slaveMaster_Port: 3307Connect_Retry: 30Master_Log_File: mall-mysql-bin.000001Read_Master_Log_Pos: 617Relay_Log_File: mall-mysql-relay-bin.000002Relay_Log_Pos: 325Relay_Master_Log_File: mall-mysql-bin.000001Slave_IO_Running: YesSlave_SQL_Running: Yes# Master
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+mysql> create database db;
mysql> use db;
mysql> create table tb(id int, name varchar(20));
mysql> insert into tb values(1, 'Jack'),(2, 'Rose'),(3, 'Mike');mysql> select * from tb;
+------+------+
| id   | name |
+------+------+
|    1 | Jack |
|    2 | Rose |
|    3 | Mike |
+------+------+# Slave
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| db                 |
| mysql              |
| performance_schema |
| sys                |
+--------------------+mysql> use db;
mysql> select * from tb;
+------+------+
| id   | name |
+------+------+
|    1 | Jack |
|    2 | Rose |
|    3 | Mike |
+------+------+

4.4 Redis 分布式存储方案

单 Master 架构中 Master 和 Slave 的数据一样的、能容纳的数据量也一样。数据量超过 Master 的内存时,Redis 会使用 LRU 算法清除部分数据。无法容纳更多数据,只能通过 Redis Cluster(Redis 分布式解决方案)解决单 Master 架构的内存、并发、流量等瓶颈

通过 Redis 使用分布式存储,一般有三种解决方案:哈希取余分区、一致性哈希算法分区、哈希槽分区

哈希取余分区

n 个 Redis 实例构成集群,每次读写操作都需要通过 hash(key) % n 计算数据映射到哪一个节点上。

  • 优点:简单粗暴、直接有效,起到负载均衡、分而治之的作用。
  • 缺点:对节点进行扩容或缩容比较麻烦,原来的取模公示会发生变化,映射关系需要全部重新进行计算。

一致性哈希算法分区

一致性哈希算法主要是为了解决 分布式缓存的数据变动和映射的问题。当服务器个数发生变动时,尽量不影响客户端与服务器的映射关系。

  1. Hash 环:圆环的正上方的点代表 0,0 右侧的第一个点为 1,以此类推 2、3、4 … 直到 232 - 1;0 的左侧第一个点代表 232 - 1。这个由 2^32 个点组成的圆环称为 Hash 环
  2. 节点映射:通过 hash(服务器 IP) % 2^32 得到 0 到 232 -1 之间的整数,Hash 环上必定有一个点与之对应。可以使用这个整数代替服务器。
    • 假设有 3 个节点 Node1、Node1、Node3,经过计算后,确定其在环上的位置。
  3. Key 落到服务器上的落键规则:存储一个键值对时,通过 hash(key) 计算 Key 的哈希值 ,确定该 Key 在环上的位置。从该位置顺时针行走,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储到该节点上。(A - Node2、B - Node3、C D - Node1。)

扩展性:在 Node 3 和 Node1 之间新增 Node4,只有 Node3 到 Node1 之间的映射关系需要重新计算。(A - Node2、B - Node3、C - Node4、D - Node1)

容错性:假设 Node1 宕机,Request A、B、C 不会受到影响,只有 D 会被重新定位到 Node2。(D、A - Node2、B - Node3、C - Node4)

数据倾斜问题:节点太少会因为分布不均匀而造成数据倾斜(缓存的对象大部分集中在某一台服务器上)。

哈希槽分区

RedisCluster 使用 16384 个槽(Slot) 管理一段整数集合。若有 5 个节点,每个 Master 节点负责管理一部分 Slot,每个节点管理大约 3276(16384 / 5)个槽。

向 RedisCluster 添加一个 Key 时

  1. 对每个 Key 通过使用 CRC16 算法得到哈希值,使用哈希值对 16384 进行取模(slot = CRC16(key) / 16384)得到对应的 Slot 编号。(这个 Key 应该分布到哪个 Hash Slot)
  2. Client 连接集群时,会获取集群 Slot 配置信息,通过 Key 对应的 Slot 编号从而确定该存储该 Key 的节点。

优点

  1. 解耦数据和节点之间的关系:增加一个 Master 只需将其他 Master 的 Slot 分一部分给新的 Master 管理;移除一个 Master 只需要将该 Master 管理的 Slot 分配给其他 Master。
  2. 某个 Master 挂掉不影响整个集群:因为请求是到 Slot 而不是到 Master。但 Slot 迁移完成之前,请求挂掉的节点也不行。
  3. Slot 迁移过程很快、节点自身维护 Slot 的映射关系、支持 Slot、Node、Key 之间的映射关系的查询。

4.5 Redis 3主3从集群配置

新建六个 Docker 容器实例

# --net host                             使用宿主机的IP和端口
# --privileged=true                获取宿主机root用户权限
# --cluster-enabled yes         开启Redis集群
# --appendonly yes                  开启Redis持久化
docker run -d --name redis-node-1 --net host --privileged=true -v /docker/redis/share/redis-node-1:/data redis:6.2.7 --cluster-enabled yes --appendonly yes --port 6381
docker run -d --name redis-node-2 --net host --privileged=true -v /docker/redis/share/redis-node-2:/data redis:6.2.7 --cluster-enabled yes --appendonly yes --port 6382
docker run -d --name redis-node-3 --net host --privileged=true -v /docker/redis/share/redis-node-3:/data redis:6.2.7 --cluster-enabled yes --appendonly yes --port 6383
docker run -d --name redis-node-4 --net host --privileged=true -v /docker/redis/share/redis-node-4:/data redis:6.2.7 --cluster-enabled yes --appendonly yes --port 6384
docker run -d --name redis-node-5 --net host --privileged=true -v /docker/redis/share/redis-node-5:/data redis:6.2.7 --cluster-enabled yes --appendonly yes --port 6385
docker run -d --name redis-node-6 --net host --privileged=true -v /docker/redis/share/redis-node-6:/data redis:6.2.7 --cluster-enabled yes --appendonly yes --port 6386[root@VM-8-5-centos /]# docker ps
CONTAINER ID   IMAGE         COMMAND                  STATUS          NAMES
101572a0bca2   redis:6.2.7   "docker-entrypoint.s…"   Up 3 seconds    redis-node-6
f064876acbf8   redis:6.2.7   "docker-entrypoint.s…"   Up 7 seconds    redis-node-5
510f6204c893   redis:6.2.7   "docker-entrypoint.s…"   Up 12 seconds   redis-node-4
15573dd8d2e9   redis:6.2.7   "docker-entrypoint.s…"   Up 16 seconds   redis-node-3
66705fdcc00d   redis:6.2.7   "docker-entrypoint.s…"   Up 20 seconds   redis-node-2
9e3a4eb91b53   redis:6.2.7   "docker-entrypoint.s…"   Up 24 seconds   redis-node-1

进入 redis-node-1 容器为6台机器构建集群关系

docker exec -it redis-node-1 /bin/bash# --cluster-replicas 1 为每个 master 节点创建一个 slave 节点(1:主节点数/从节点数的比例。按照先后顺序区分主从节点)
redis-cli --cluster create 10.0.8.5:6381 10.0.8.5:6382 10.0.8.5:6383 10.0.8.5:6384 10.0.8.5:6385 10.0.8.5:6386 --cluster-replicas 1

主节点:6381(0-5460)、6382(5461-10922)、6383(10923-16383);从节点:6384、6385、6386。

查看集群状态

[root@VM-centos ~]# docker exec -it redis-node-1 /bin/bash
root@VM-8-5-centos:/data# redis-cli -p 6381
127.0.0.1:6381> 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:3639
cluster_stats_messages_pong_sent:3626
cluster_stats_messages_sent:7265
cluster_stats_messages_ping_received:3621
cluster_stats_messages_pong_received:3639
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:7265127.0.0.1:6381> cluster nodes
c7b13199fab912864f017152e21087410aaa0d57 10.0.8.5:6382@16382 master - 0 1669902912010 2 connected 5461-10922
79f11a738ffad25ea1254ddac0fbc5d722d701a3 10.0.8.5:6383@16383 master - 0 1669902913013 3 connected 10923-16383
fcc55a534821ebc59b822e99cfbcb08a3342bf85 10.0.8.5:6384@16384 slave c7b13199fab912864f017152e21087410aaa0d57 0 1669902914016 2 connected
d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381@16381 myself,master - 0 1669902909000 1 connected 0-5460
041dedb520107124a587919ea5e1e567aec91802 10.0.8.5:6385@16385 slave 79f11a738ffad25ea1254ddac0fbc5d722d701a3 0 1669902915018 3 connected
4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386@16386 slave d3e6283a3c79525793b5e3b98e047afef091aa5e 0 1669902914000 1 connected# 查看主从节点的对应关系
M 6382 - c7b13199fab912864f017152e21087410aaa0d57(6384 - slave)
M 6383 - 79f11a738ffad25ea1254ddac0fbc5d722d701a3(6385 - slave)
M 6381 - d3e6283a3c79525793b5e3b98e047afef091aa5e(6386 - slave)

4.6 Redis 主从容错切换迁移

数据读写存储

分配给 6381 的槽为 0-5460,存储 k1k4 的数据槽分为别 12706 和 8455,应该存储在 6383(5461-10922) 和 6384(10923-16383)中。

[root@VM-centos ~]# docker exec -it redis-node-1 /bin/bash
root@VM-8-5-centos:/data# redis-cli -p 6381
127.0.0.1:6381> keys *
(empty array)
127.0.0.1:6381> set k1 v1
(error) MOVED 12706 10.0.8.5:6383
127.0.0.1:6381> set k2 v2
OK
127.0.0.1:6381> set k3 v3
OK
127.0.0.1:6381> set k4 v4
(error) MOVED 8455 10.0.8.5:6382

防止路由失效,连接 Redis-CLI 时增加参数 -credis-cli -p Port -c

[root@VM-centos ~]# docker exec -it redis-node-1 /bin/bash
root@VM-8-5-centos:/data# redis-cli -p 6381 -c
127.0.0.1:6381> set k1 v1
-> Redirected to slot [12706] located at 10.0.8.5:6383
OK
10.0.8.5:6383> set k2 v2
-> Redirected to slot [449] located at 10.0.8.5:6381
OK
10.0.8.5:6381> set k3 v3
OK
10.0.8.5:6381> set k4 v4
-> Redirected to slot [8455] located at 10.0.8.5:6382
OK

查看集群信息redis-cli --cluster check IP:Port

[root@VM-centos ~]# docker exec -it redis-node-1 /bin/bash
root@VM-8-5-centos:/data# redis-cli --cluster check 10.0.8.5:6381
10.0.8.5:6381 (d3e6283a...) -> 2 keys | 5461 slots | 1 slaves.
10.0.8.5:6382 (c7b13199...) -> 1 keys | 5462 slots | 1 slaves.
10.0.8.5:6383  (79f11a73...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 4 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.8.5:6381)
M: d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381slots:[0-5460] (5461 slots) master1 additional replica(s)
M: c7b13199fab912864f017152e21087410aaa0d57 10.0.8.5:6382slots:[5461-10922] (5462 slots) master1 additional replica(s)
M: 79f11a738ffad25ea1254ddac0fbc5d722d701a3 10.0.8.5:6383slots:[10923-16383] (5461 slots) master1 additional replica(s)
S: fcc55a534821ebc59b822e99cfbcb08a3342bf85 10.0.8.5:6384slots: (0 slots) slavereplicates c7b13199fab912864f017152e21087410aaa0d57
S: 041dedb520107124a587919ea5e1e567aec91802 10.0.8.5:6385slots: (0 slots) slavereplicates 79f11a738ffad25ea1254ddac0fbc5d722d701a3
S: 4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386slots: (0 slots) slavereplicates d3e6283a3c79525793b5e3b98e047afef091aa5e
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

容错切换迁移

6381 宕机,6386 上位成为新的 master。

root@VM-8-5-centos:/data# redis-cli -p 6381 -c# M - 6381, S - 6386
127.0.0.1:6381> cluster nodes
d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381@16381 myself,master - 0 1669918251000 1 connected 0-5460
4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386@16386 slave d3e6283a3c79525793b5e3b98e047afef091aa5e 0 1669918254000 1 connected
...[root@VM-centos ~]# docker stop redis-node-1
redis-node-1[root@VM-centos ~]# docker exec -it redis-node-2 /bin/bash
root@VM-8-5-centos:/data# redis-cli -p 6382 -c
# 6381 fail, 6386 -> master
127.0.0.1:6382> cluster nodes
c7b13199fab912864f017152e21087410aaa0d57 10.0.8.5:6382@16382 myself,master - 0 1669918426000 2 connected 5461-10922
4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386@16386 master - 0 1669918427000 7 connected 0-5460
d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381@16381 master,fail - 1669918305328 1669918301318 1 disconnected# 6381 中存储的 k2 和 k3 由 6386 接管
127.0.0.1:6382> get k1
-> Redirected to slot [12706] located at 10.0.8.5:6383
"v1"
10.0.8.5:6383> get k2
-> Redirected to slot [449] located at 10.0.8.5:6386
"v2"
10.0.8.5:6386> get k3
"v3"
10.0.8.5:6386> get k4
-> Redirected to slot [8455] located at 10.0.8.5:6382
"v4"

此时再启动 6381,6386 为主节点,6381 变为从节点。

docker start redis-node-1# M - 6386, S - 6381
10.0.8.5:6382> cluster nodes
4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386@16386 master - 0 1669918913000 7 connected 0-5460
d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381@16381 slave 4d357a9680744b62c005a600df262ee4ee5760df 0 1669918915677 7 connected
...

恢复最初状态,即 6381 为主节点,6386 为其从节点。

# 先停止 6386(6386 fail, 6381 -> master)
docker stop redis-node-610.0.8.5:6382> cluster nodes
4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386@16386 master,fail - 1669919090161 1669919087000 7 disconnected
d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381@16381 master - 0 1669919105218 8 connected 0-5460
...# 再启动 6386(M - 6381, S - 6386)
docker start redis-node-6
10.0.8.5:6382> cluster nodes
4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386@16386 slave d3e6283a3c79525793b5e3b98e047afef091aa5e 0 1669919223621 8 connected
d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381@16381 master - 0 1669919223000 8 connected 0-5460

4.7 Redis 主从扩容

新建 6387、6388 两个节点。

docker run -d --name redis-node-7 --net host --privileged=true -v /docker/redis/share/redis-node-7:/data redis:6.2.7 --cluster-enabled yes --appendonly yes --port 6387
docker run -d --name redis-node-8 --net host --privileged=true -v /docker/redis/share/redis-node-8:/data redis:6.2.7 --cluster-enabled yes --appendonly yes --port 6388[root@VM-8-5-centos /]# docker ps
CONTAINER ID   IMAGE         COMMAND                  CREATED         STATUS          PORTS     NAMES
fbb74f9e2093   redis:6.2.7   "docker-entrypoint.s…"   3 seconds ago   Up 2 seconds              redis-node-8
90d966b02b53   redis:6.2.7   "docker-entrypoint.s…"   5 seconds ago   Up 4 seconds              redis-node-7
101572a0bca2   redis:6.2.7   "docker-entrypoint.s…"   7 hours ago     Up 5 minutes              redis-node-6
f064876acbf8   redis:6.2.7   "docker-entrypoint.s…"   7 hours ago     Up 7 hours                redis-node-5
510f6204c893   redis:6.2.7   "docker-entrypoint.s…"   7 hours ago     Up 7 hours                redis-node-4
15573dd8d2e9   redis:6.2.7   "docker-entrypoint.s…"   7 hours ago     Up 7 hours                redis-node-3
66705fdcc00d   redis:6.2.7   "docker-entrypoint.s…"   7 hours ago     Up 7 hours                redis-node-2
9e3a4eb91b53   redis:6.2.7   "docker-entrypoint.s…"   7 hours ago     Up 10 minutes             redis-node-1

将新增的 6387 节点(空槽)作为 master 节点加入原集群redis-cli --cluster add-node IP:Port(新增节点) IP:Port(原集群主节点)

[root@VM-8-5-centos /]# docker exec -it redis-node-7 /bin/bash# 6387 作为新增的 master 节点;6381 作为原集群节点中的领路人
root@VM-8-5-centos:/data# redis-cli --cluster add-node 10.0.8.5:6387 10.0.8.5:6381
>>> Adding node 10.0.8.5:6387 to cluster 10.0.8.5:6381
>>> Performing Cluster Check (using node 10.0.8.5:6381)
M: d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381slots:[0-5460] (5461 slots) master1 additional replica(s)
M: c7b13199fab912864f017152e21087410aaa0d57 10.0.8.5:6382slots:[5461-10922] (5462 slots) master1 additional replica(s)
S: 041dedb520107124a587919ea5e1e567aec91802 10.0.8.5:6385slots: (0 slots) slavereplicates 79f11a738ffad25ea1254ddac0fbc5d722d701a3
M: 79f11a738ffad25ea1254ddac0fbc5d722d701a3 10.0.8.5:6383slots:[10923-16383] (5461 slots) master1 additional replica(s)
S: 4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386slots: (0 slots) slavereplicates d3e6283a3c79525793b5e3b98e047afef091aa5e
S: fcc55a534821ebc59b822e99cfbcb08a3342bf85 10.0.8.5:6384slots: (0 slots) slavereplicates c7b13199fab912864f017152e21087410aaa0d57
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 10.0.8.5:6387 to make it join the cluster.
[OK] New node added correctly.# 第一次检查集群情况(可以发现 6387 暂时没有槽号)
root@VM-8-5-centos:/data# redis-cli --cluster check 10.0.8.5:6387
10.0.8.5:6387 (9ad00ce1...) -> 0 keys | 0 slots | 0 slaves.
10.0.8.5:6381 (d3e6283a...) -> 2 keys | 5461 slots | 1 slaves.
10.0.8.5:6382 (c7b13199...) -> 1 keys | 5462 slots | 1 slaves.
10.0.8.5:6383 (79f11a73...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 4 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.8.5:6387)
M: 9ad00ce1058971627be3ea98cb12a21391f5ecd7 10.0.8.5:6387slots: (0 slots) master
M: d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381slots:[0-5460] (5461 slots) master1 additional replica(s)
S: fcc55a534821ebc59b822e99cfbcb08a3342bf85 10.0.8.5:6384slots: (0 slots) slavereplicates c7b13199fab912864f017152e21087410aaa0d57
S: 4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386slots: (0 slots) slavereplicates d3e6283a3c79525793b5e3b98e047afef091aa5e
M: c7b13199fab912864f017152e21087410aaa0d57 10.0.8.5:6382slots:[5461-10922] (5462 slots) master1 additional replica(s)
M: 79f11a738ffad25ea1254ddac0fbc5d722d701a3 10.0.8.5:6383slots:[10923-16383] (5461 slots) master1 additional replica(s)
S: 041dedb520107124a587919ea5e1e567aec91802 10.0.8.5:6385slots: (0 slots) slavereplicates 79f11a738ffad25ea1254ddac0fbc5d722d701a3
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

重新分配槽号redis-cli --cluster reshard IP:Port

# 重新分配成本太高,其他3个节点各自匀出来一部分,从 6381/6382/6383 三个节点中分别匀出 1364 个槽给新节点。
# 6387:slots:[0-1364],[5461-6826],[10923-12287]
# 6381:slots:[1365-5460] (4096 slots)
# 6382:slots:[6827-10922] (4096 slots)
# 6383:slots:[12288-16383] (4096 slots)
redis-cli --cluster reshard 10.0.8.5:6381

# 第二次检查集群情况(可以发现 6381、6382、6383、6387 均为 4096 个槽位)
root@VM-8-5-centos:/data# redis-cli --cluster check 10.0.8.5:6387
10.0.8.5:6387 (9ad00ce1...) -> 1 keys | 4096 slots | 0 slaves.
10.0.8.5:6381 (d3e6283a...) -> 1 keys | 4096 slots | 1 slaves.
10.0.8.5:6382 (c7b13199...) -> 1 keys | 4096 slots | 1 slaves.
10.0.8.5:6383 (79f11a73...) -> 1 keys | 4096 slots | 1 slaves.
[OK] 4 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.8.5:6387)
M: 9ad00ce1058971627be3ea98cb12a21391f5ecd7 10.0.8.5:6387slots:[0-1364],[5461-6826],[10923-12287] (4096 slots) master
M: d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381slots:[1365-5460] (4096 slots) master1 additional replica(s)
S: fcc55a534821ebc59b822e99cfbcb08a3342bf85 10.0.8.5:6384slots: (0 slots) slavereplicates c7b13199fab912864f017152e21087410aaa0d57
S: 4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386slots: (0 slots) slavereplicates d3e6283a3c79525793b5e3b98e047afef091aa5e
M: c7b13199fab912864f017152e21087410aaa0d57 10.0.8.5:6382slots:[6827-10922] (4096 slots) master1 additional replica(s)
M: 79f11a738ffad25ea1254ddac0fbc5d722d701a3 10.0.8.5:6383slots:[12288-16383] (4096 slots) master1 additional replica(s)
S: 041dedb520107124a587919ea5e1e567aec91802 10.0.8.5:6385slots: (0 slots) slavereplicates 79f11a738ffad25ea1254ddac0fbc5d722d701a3
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

为主节点 6387 分配从节点 6388redis-cli --cluster add-node IP:SlavePort IP:MasterPort --cluster-slave --cluster-master-id 主机节点ID

root@VM-8-5-centos:/data# redis-cli --cluster add-node 10.0.8.5:6388 10.0.8.5:6387 --cluster-slave --cluster-master-id 9ad00ce1058971627be3ea98cb12a21391f5ecd7
>>> Adding node 10.0.8.5:6388 to cluster 10.0.8.5:6387
>>> Performing Cluster Check (using node 10.0.8.5:6387)
M: 9ad00ce1058971627be3ea98cb12a21391f5ecd7 10.0.8.5:6387slots:[0-1364],[5461-6826],[10923-12287] (4096 slots) master
M: d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381slots:[1365-5460] (4096 slots) master1 additional replica(s)
S: fcc55a534821ebc59b822e99cfbcb08a3342bf85 10.0.8.5:6384slots: (0 slots) slavereplicates c7b13199fab912864f017152e21087410aaa0d57
S: 4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386slots: (0 slots) slavereplicates d3e6283a3c79525793b5e3b98e047afef091aa5e
M: c7b13199fab912864f017152e21087410aaa0d57 10.0.8.5:6382slots:[6827-10922] (4096 slots) master1 additional replica(s)
M: 79f11a738ffad25ea1254ddac0fbc5d722d701a3 10.0.8.5:6383slots:[12288-16383] (4096 slots) master1 additional replica(s)
S: 041dedb520107124a587919ea5e1e567aec91802 10.0.8.5:6385slots: (0 slots) slavereplicates 79f11a738ffad25ea1254ddac0fbc5d722d701a3
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 10.0.8.5:6388 to make it join the cluster.
Waiting for the cluster to join>>> Configure node as replica of 10.0.8.5:6387.
[OK] New node added correctly.# 第三次检查集群状态
root@VM-8-5-centos:/data# redis-cli --cluster check 10.0.8.5:6381
10.0.8.5:6381 (d3e6283a...) -> 1 keys | 4096 slots | 1 slaves.
10.0.8.5:6382 (c7b13199...) -> 1 keys | 4096 slots | 1 slaves.
10.0.8.5:6383 (79f11a73...) -> 1 keys | 4096 slots | 1 slaves.
10.0.8.5:6387 (9ad00ce1...) -> 1 keys | 4096 slots | 1 slaves.
[OK] 4 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.8.5:6381)
M: d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381slots:[1365-5460] (4096 slots) master1 additional replica(s)
M: c7b13199fab912864f017152e21087410aaa0d57 10.0.8.5:6382slots:[6827-10922] (4096 slots) master1 additional replica(s)
S: 041dedb520107124a587919ea5e1e567aec91802 10.0.8.5:6385slots: (0 slots) slavereplicates 79f11a738ffad25ea1254ddac0fbc5d722d701a3
M: 79f11a738ffad25ea1254ddac0fbc5d722d701a3 10.0.8.5:6383slots:[12288-16383] (4096 slots) master1 additional replica(s)
S: e61bf9ffad6c7742d890106fa8840b921621aba0 10.0.8.5:6388slots: (0 slots) slavereplicates 9ad00ce1058971627be3ea98cb12a21391f5ecd7
S: 4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386slots: (0 slots) slavereplicates d3e6283a3c79525793b5e3b98e047afef091aa5e
M: 9ad00ce1058971627be3ea98cb12a21391f5ecd7 10.0.8.5:6387slots:[0-1364],[5461-6826],[10923-12287] (4096 slots) master1 additional replica(s)
S: fcc55a534821ebc59b822e99cfbcb08a3342bf85 10.0.8.5:6384slots: (0 slots) slavereplicates c7b13199fab912864f017152e21087410aaa0d57
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

4.8 Redis 主从缩容

将从节点 6388 从集群中删除redis-cli --cluster del-node IP:SlavePort 从机节点ID

# 第一次检查集群状态,获取 6388 节点的 ID
root@VM-8-5-centos:/data# redis-cli --cluster check 10.0.8.5:6387
M: 9ad00ce1058971627be3ea98cb12a21391f5ecd7 10.0.8.5:6387slots:[0-1364],[5461-6826],[10923-12287] (4096 slots) master1 additional replica(s)
S: e61bf9ffad6c7742d890106fa8840b921621aba0 10.0.8.5:6388slots: (0 slots) slavereplicates 9ad00ce1058971627be3ea98cb12a21391f5ecd7
...# 删除从节点 6388
root@VM-8-5-centos:/data# redis-cli --cluster del-node 10.0.8.5:6388 e61bf9ffad6c7742d890106fa8840b921621aba0
>>> Removing node e61bf9ffad6c7742d890106fa8840b921621aba0 from cluster 10.0.8.5:6388
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.# 检查集群状态,发现从节点 6388 已被删除
root@VM-8-5-centos:/data# redis-cli --cluster check 10.0.8.5:6387
...
M: 9ad00ce1058971627be3ea98cb12a21391f5ecd7 10.0.8.5:6387
M: d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381
M: c7b13199fab912864f017152e21087410aaa0d57 10.0.8.5:6382
M: 79f11a738ffad25ea1254ddac0fbc5d722d701a3 10.0.8.5:6383
S: fcc55a534821ebc59b822e99cfbcb08a3342bf85 10.0.8.5:6384
S: 041dedb520107124a587919ea5e1e567aec91802 10.0.8.5:6385
S: 4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386
...

清空主节点 6387 的槽号,重新分配。此处将清出来的槽号分配给 6381。

root@VM-8-5-centos:/data# redis-cli --cluster reshard 10.0.8.5:6381
...

# 第二次检查集群状态,发现 6387 的 slots 为 0
root@VM-8-5-centos:/data# redis-cli --cluster check 10.0.8.5:6381
10.0.8.5:6381 (d3e6283a...) -> 2 keys | 8192 slots | 1 slaves.
10.0.8.5:6382 (c7b13199...) -> 1 keys | 4096 slots | 1 slaves.
10.0.8.5:6383 (79f11a73...) -> 1 keys | 4096 slots | 1 slaves.
10.0.8.5:6387 (9ad00ce1...) -> 0 keys | 0 slots | 0 slaves.
...

将从节点 6387 删除redis-cli --cluster del-node IP:MasterPort 主机节点ID

root@VM-8-5-centos:/data# redis-cli --cluster del-node 10.0.8.5:6387 9ad00ce1058971627be3ea98cb12a21391f5ecd7
>>> Removing node 9ad00ce1058971627be3ea98cb12a21391f5ecd7 from cluster 10.0.8.5:6387
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.# 第三次检查集群状态,集群恢复为 3主3从。
root@VM-8-5-centos:/data# redis-cli --cluster check 10.0.8.5:6381
10.0.8.5:6381 (d3e6283a...) -> 2 keys | 8192 slots | 1 slaves.
10.0.8.5:6382 (c7b13199...) -> 1 keys | 4096 slots | 1 slaves.
10.0.8.5:6383 (79f11a73...) -> 1 keys | 4096 slots | 1 slaves.
[OK] 4 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.8.5:6381)
M: d3e6283a3c79525793b5e3b98e047afef091aa5e 10.0.8.5:6381slots:[0-6826],[10923-12287] (8192 slots) master1 additional replica(s)
M: c7b13199fab912864f017152e21087410aaa0d57 10.0.8.5:6382slots:[6827-10922] (4096 slots) master1 additional replica(s)
S: 041dedb520107124a587919ea5e1e567aec91802 10.0.8.5:6385slots: (0 slots) slavereplicates 79f11a738ffad25ea1254ddac0fbc5d722d701a3
M: 79f11a738ffad25ea1254ddac0fbc5d722d701a3 10.0.8.5:6383slots:[12288-16383] (4096 slots) master1 additional replica(s)
S: 4d357a9680744b62c005a600df262ee4ee5760df 10.0.8.5:6386slots: (0 slots) slavereplicates d3e6283a3c79525793b5e3b98e047afef091aa5e
S: fcc55a534821ebc59b822e99cfbcb08a3342bf85 10.0.8.5:6384slots: (0 slots) slavereplicates c7b13199fab912864f017152e21087410aaa0d57
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

4.9 上述操作流程图

5. 镜像结构

5.1 镜像分层结构

镜像:将应用程序及其所需的系统函数库、环境、配置、依赖等打包而成。

镜像是分层结构,每一层称为一个 Layer。

  • Base Image 层:包括基本的系统函数库、环境变量、文件系统。
  • Entrypoint 层:入口,镜像中应用启动的命令。
  • 其他:在 Base Image 层的基础上添加依赖、安装程序、完成整个应用的安装和配置。

  • 镜像构建时,会一层一层的构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何变化只发生在自己这一层。

    • 删除前一层文件的操作,不是真的删除前一层的文件,而是在当前曾标记为该文件已删除。容器运行时,不会看到这个文件,但是该文件会一直跟随镜像。
  • 镜像分层存储方便镜像的复用、定制。
    • 可以用之前构建好的镜像作为基础层,然后进一步添加新的层,定制自己所需的内容,构建新的镜像。
  • 镜像层是只读的,容器层是可写的。(为了保证镜像能够被多个容器共享)
    • 容器启动时,一个新的可写层被加载到镜像的顶部,即容器层,容器之下的都叫做 镜像层。所有对容器的改动都只会发生在容器层中,只有容器层是可写的。

5.2 docker commit

提交容器副本成为一个新的镜像:docker commit -m="描述信息" -a="作者" 容器ID 镜像名称:[标签名]

# 原镜像中没有 vim 命令
[root@VM-centos ~]# docker exec -it redis /bin/bash
root@8feb8f482ce1:/data# vim hello.txt
bash: vim: command not found# 安装 vim 命令
root@8feb8f482ce1:/data# apt-get update
root@8feb8f482ce1:/data# apt-get -y install vim# 安装完成后,commit 镜像
[root@VM-centos ~]# docker commit -m="add vim cmd" -a="sun" 8feb8f482ce1 sun/redis:1.1
[root@VM-centos ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
sun/redis    1.1       bc672caa305b   3 seconds ago   167MB
redis        latest    7614ae9453d1   14 months ago   113MB# 启动新镜像,与原始镜像进行比较
[root@VM-centos ~]# docker run --name=redis -p 6379:6379 -d sun/redis:1.1 redis-server --appendonly yes
[root@VM-centos ~]# docker exec -it redis /bin/bash
root@e859241025ca:/data# vim hello.txt
root@e859241025ca:/data# cat hello.txt
hello docker!

6. Dockerfile

Dockerfile 是用于构建 Docker 镜像的文本文件,其中包含一条条构建镜像所需的指令和参数。每条指令构建一层镜像,因此每条指令的内容就是描述该层镜像如何构建。

指令

  1. 每条保留字指令都必须为大写字母,并且后面至少跟随一个参数。
  2. 指令按照从上到下的顺序执行。
  3. # 表示注释。
  4. 每条指令都会创建一个新的镜像层并对镜像进行提交。
Instruction Description 格式
FROM 指定基础镜像 FROM centos:7
MAINTAINER 镜像维护者的姓名和邮箱地址(非必需) MAINTAINER name<email>
ENV 设置环境变量,可在后面指令使用 ENV key value
COPY 拷贝本地文件到镜像的指定目录 COPY /mysql-5.7.rpm /tmp
RUN 执行 Linux 的 shell 命令,一般为安装的命令 RUN yum install gcc
EXPOSE 指定容器对外暴露出的端口 EXPOSE 8080
ENTRYPOINT 指定容器启动时要运行的命令 ENTRYPOINT java -jar xx.jar

案例

  1. 新建空文件夹 docker-demo

    mkdir /dokcer-demo
    
  2. 拷贝资料中的 docker-demo.jar 文件、jdk8.tar.gz文件 和 Dockerfiledocker-demo 目录下。

    # 指定基础镜像
    FROM ubuntu:16.04
    # 配置环境变量,JDK 的安装目录
    ENV JAVA_DIR=/usr/local# 拷贝 JDK 和 Java 项目 到容器中
    COPY ./jdk8.tar.gz $JAVA_DIR/
    COPY ./docker-demo.jar /tmp/app.jar# 安装JDK
    RUN cd $JAVA_DIR \&& tar -xf ./jdk8.tar.gz \&& mv ./jdk1.8.0_171 ./java8# 配置环境变量
    ENV JAVA_HOME=$JAVA_DIR/java8
    ENV PATH=$PATH:$JAVA_HOME/bin# 暴露端口
    EXPOSE 8090
    # 入口,Java 项目的启动命令
    ENTRYPOINT java -jar /tmp/app.jar
    
  3. 运行 docker build 命令。

    docker build -t app:1.0 .
    
[root@VM-8-5-centos docker-demo]# ls
docker-demo.jar  Dockerfile  jdk8.tar.gz[root@VM-8-5-centos docker-demo]# docker build -t app:1.0 .
[+] Building 30.5s (9/9) FINISHED                                                                                      => [internal] load build definition from Dockerfile                                                              0.1s=> => transferring dockerfile: 548B                                                                              0.0s=> [internal] load .dockerignore                                                                                 0.1s=> => transferring context: 2B                                                                                   0.0s=> [internal] load metadata for docker.io/library/ubuntu:16.04                                                  16.3s=> [internal] load build context                                                                                 2.1s=> => transferring context: 216.56MB                                                                             2.1s=> [1/4] FROM docker.io/library/ubuntu:16.04@sha256:0f71fa8d4d2d4292c3c617fda2b36f6dabe5c8b6e34c3dc5b0d17d4e704  6.0s=> [2/4] COPY ./jdk8.tar.gz /usr/local/                                                                          2.2s=> [3/4] COPY ./docker-demo.jar /tmp/app.jar                                                                     0.2s=> [4/4] RUN cd /usr/local  && tar -xf ./jdk8.tar.gz  && mv ./jdk1.8.0_171 ./java8                               3.4s=> exporting to image                                                                                            2.4s=> => exporting layers                                                                                           2.4s=> => writing image sha256:ffdd31decbc9f811a11d9b74c590da71ccb67d691fe96193f75f4704d3e53ea6                      0.0s=> => naming to docker.io/library/app:1.0                                                                        0.0s[root@VM-8-5-centos docker-demo]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
app          1.0       ffdd31decbc9   7 seconds ago   739MB# 创建并运行容器(访问 http://IP:8090/hello/count)
[root@VM-8-5-centos docker-demo]# docker run --name=app -p 8090:8090 -d app:1.0

7. 镜像仓库

镜像仓库( Docker Registry )有公共的和私有的两种形式:

  • 公共仓库:例如 Docker Hub,国内也有类似 Docker Hub 的公开服务,例如 网易云镜像服务、阿里云镜像服务等。
  • 用户还可以在本地搭建私有 Docker Registry。

搭建私有仓库

docker pull registry
docker run --name registry -p 5000:5000 --restart=always -v registry-data:/var/lib/registry -d registry# 查询私服库上的镜像
[root@VM-8-5-centos bin]# curl -XGET IP:5000/v2/_catalog
{"repositories":[]}# docker tag 镜像 Host:Port/Repository:Tag
[root@VM-8-5-centos bin]# docker tag sun/redis:1.1 IP:5000/redis:1.1
[root@VM-8-5-centos bin]# docker images
REPOSITORY                  TAG       IMAGE ID       CREATED         SIZE
IP:5000/redis   1.1       1583a3927f2e   28 hours ago    167MB# Docker 默认不允许 HTTP 方式推送镜像,通过配置选项取消该限制
[root@VM-centos ~]# vim /etc/docker/daemon.json
{"registry-mirrors": ["https://xzstr9ov.mirror.aliyuncs.com"],"insecure-registries": ["10.0.8.5:5000"]
}# 将镜像 push 到私服库
[root@VM-centos ~]# docker push IP:5000/redis:1.1
[root@VM-centos ~]# curl -XGET IP:5000/v2/_catalog
{"repositories":["redis"]}# 从私服库拉取镜像
[root@VM-centos ~]# docker pull IP:5000/redis:1.1

Docker 学习笔记(Docker 架构 / 镜像 / 容器 / 常用命令 / Dockerfile / 镜像仓库)相关推荐

  1. 狂神说--docker学习笔记-docker安装,常用命令,以及使用

    狂神说bilibili视频地址:https://www.bilibili.com/video/BV1og4y1q7M4?p=1 1. Docker概述 1.1 Docker为什么出现? 大家经常做一款 ...

  2. Docker学习 (一) 下载安装及基本常用命令

    Docker快速入门笔记 1.docker概述 1.1.docker基本介绍 Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源,让开发者可以打包他们的应用 ...

  3. Docker学习笔记 — Docker私有仓库搭建

    2019独角兽企业重金招聘Python工程师标准>>> 和Mavan的管理一样,Dockers不仅提供了一个中央仓库,同时也允许我们使用registry搭建本地私有仓库. 使用私有仓 ...

  4. Docker学习笔记 - Docker Compose

    一.概念 Docker Compose 用于定义运行使用多个容器的应用,可以一条命令启动应用(多个容器). 使用Docker Compose 的步骤: 定义容器 Dockerfile 定义应用的各个服 ...

  5. 《Linux编程》学习笔记 ·001【基本操作、常用命令】

    注:前言.目录见 https://blog.csdn.net/qq_44220418/article/details/108428971 文章目录 一.基本操作 1.查看目录/文件 2.路径跳转 (1 ...

  6. (课程学习笔记)玩转Linux:常用命令实例指南

    链接:https://edu.csdn.net/course/play/26264/326631 课程一:linux入门经典书籍推荐 linux入门到精通 鸟哥的linux私房菜 linux就该这么学 ...

  7. Linux学习笔记(一):常用命令(2)

    3.帮助命令 A,帮助命令:man    B,其他帮助命令 3.1,格式:man [命令名] 查看命令拥有哪个级别的帮助:                                       ...

  8. 学习笔记 - Linux学习笔记 第四讲 Linux常用命令

    第四讲 Linux常用命令 4.1.1 Linux常用命令-文件处理命令-命令格式与目录处理命令ls clear 清屏命令ls -l #l代表long ls -a #a代表all ls -h #h代表 ...

  9. Docker学习笔记 - Docker容器的日志

    docker logs  [-f]  [-t]  [--tail]  容器名 -f -t --tail="all" 无参数:返回所有日志 -f 一直跟踪变化并返回 -t 带时间戳返 ...

最新文章

  1. 网络空间安全Windows系统命令行学习笔记
  2. jQuery中FormData的使用
  3. 我们一般的前端开发流程
  4. 谷歌Chrome 紧急修复已遭利用的两个0day
  5. TClientDataSet[2]: Data、XMLData
  6. java飞机大战boos代码_飞机大战 java 源代码
  7. MATLAB 指定线型和颜色
  8. 如何减小电压跟随器输出电阻_三分钟带你搞懂运算放大器与比较器的区别
  9. linux安全模块学习之LSM的介绍实现
  10. 【问题记录】Win10笔记本电脑禁用自带键盘的方法
  11. 程序设计基础课程设计——学生成绩管理程序
  12. 极客创新大赛|微创机器人号探索飞船即将启航
  13. 如何快速入门成为一名数据分析师
  14. GPON OMCI简介
  15. win10 下 caffe 的第一个测试程序(附带详细讲解)
  16. STM32的PDR_ON引脚,比较好的解释(转载+补充)
  17. 数学与泛型编程(6)编程的基本概念
  18. 笔试 面试题 网友汇总(放在自己的文章列表里)
  19. 文件IOday02--------时间编程与文件IO
  20. 基于GAN的小目标检测算法总结(1)——Perpetual GAN

热门文章

  1. Lab3-实现计划项app辅助类实现
  2. 【读书笔记】《比基尼口才》文皙铉
  3. 怎么体现声屏障的美观性?
  4. 【转】操作系统Unix、Windows、Mac OS、Linux的故事
  5. 在有n个学生的成绩表里,每条信息由姓名与分数组成,要求:1按分数高低次序,输出每个学生的名字,分数相同的为同一名次,2按名次输出每个学生的姓名与分数。
  6. 这份公众号运营攻略,可以帮你系统地运营好公众号
  7. 鸿蒙基于linux系统,鸿蒙操作系统(HarmonyOS)是基于Linux的吗?尽管已知道它是基于微内核的...
  8. 每日一支TED——Ethan Nadelmann:为什么我们应该终止禁毒战争
  9. 每个人都会经历一段迷茫
  10. 逆向工程实验——pre6(汇编、Android逆向、RSA算法破解)