前言
- 学习视频:Java项目《谷粒商城》架构师级Java项目实战,对标阿里P6-P7,全网最强
- 学习文档:
3. 接口文档:https://easydoc.net/s/78237135/ZUqEdvA4/hKJTcbfd
4. 本内容仅用于个人学习笔记,如有侵扰,联系删
五、MySQL 集群
0、集群简介
-
集群的目标
- 高可用(High Availability),是当一台服务器停止服务后,对于业务及用户毫无影响。 停止服务的原因可能由于网卡、路由器、机房、CPU负载过高、内存溢出、自然灾害等不可预期的原因导致,在很多时候也称单点问题。
- 突破数据量限制,一台服务器不能储存大量数据,需要多台分担,每个存储一部分,共同存储完整个集群数据。最好能做到互相备份,即使单节点故障,也能在其他节点找到数据。
- 数据备份容灾,单点故障后,存储的数据仍然可以在别的地方拉起。
- 压力分担,由于多个服务器都能完成各自一部分工作,所以尽量的避免了单点压力的存在
-
集群的基础形式
1、集群原理
以上可以作为企业中常用的数据库解决方案;
- MySQL-MMM 是 Master-Master Replication Manager for MySQL(mysql 主主复制管理器)的简称,是 Google 的开源项目(Perl 脚本)。MMM 基于 MySQL Replication 做的扩展架构,主要用来监控 mysql 主主复制并做失败转移。其原理是将真实数据库节点的IP(RIP)映射为虚拟 IP(VIP)集。
mysql-mmm 的监管端会提供多个虚拟 IP(VIP),包括一个可写 VIP,多个可读 VIP,通过监管的管理,这些 IP 会绑定在可用 mysql 之上,当某一台 mysql 宕机时,监管会将 VIP迁移至其他 mysql。在整个监管过程中,需要在 mysql 中添加相关授权用户,以便让 mysql 可以支持监理机的维护。授权的用户包括一个mmm_monitor 用户和一个 mmm_agent 用户,如果想使用 mmm 的备份工具则还要添加一个 mmm_tools 用户。
- MHA(Master High Availability)目前在 MySQL 高可用方面是一个相对成熟的解决方案, 由日本 DeNA 公司 youshimaton(现就职于 Facebook 公司)开发,是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中,MHA 能做到在 0~30 秒之内自动完成数据库的故障切换操作(以 2019 年的眼光来说太慢了),并且在进行故障切换的过程中,MHA 能在最大程度上保证数据的一致性,以达到真正意义上的高可用。
- InnoDB Cluster 支持自动 Failover、强一致性、读写分离、读库高可用、读请求负载均衡,横向扩展的特性,是比较完备的一套方案。但是部署起来复杂,想要解决 router单点问题好需要新增组件,如没有其他更好的方案可考虑该方案。 InnoDB Cluster 主要由 MySQL Shell、MySQL Router 和 MySQL 服务器集群组成,三者协同工作,共同为MySQL 提供完整的高可用性解决方案。MySQL Shell 对管理人员提供管理接口,可以很方便的对集群进行配置和管理,MySQL Router 可以根据部署的集群状况自动的初始化,是客户端连接实例。如果有节点 down 机,集群会自动更新配置。集群包含单点写入和多点写入两种模式。在单主模式下,如果主节点 down 掉,从节点自动替换上来,MySQL Router 会自动探测,并将客户端连接到新节点。
2、Docker 安装模拟 MySQL 主从复制集群
2.1、下载 mysql 镜像
docker pull mysql:5.7
2.2、创建 Master 实例并启动
docker run -p 3307:3306 --name mysql-master \
-v /mydata/mysql/master/log:/var/log/mysql \
-v /mydata/mysql/master/data:/var/lib/mysql \
-v /mydata/mysql/master/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
参数说明:
- -p 3307:3306:将容器的 3306 端口映射到主机的 3307 端口
- -v /mydata/mysql/master/conf:/etc/mysql:将配置文件夹挂在到主机
- -v /mydata/mysql/master/log:/var/log/mysql:将日志文件夹挂载到主机
- -v /mydata/mysql/master/data:/var/lib/mysql/:将配置文件夹挂载到主机
- -e MYSQL_ROOT_PASSWORD=root:初始化 root 用户的密码
修改 master 基本配置
vim /mydata/mysql/master/conf/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
skip-character-set-client-handshake
skip-name-resolve
# 注意:skip-name-resolve 一定要加,不然连接 mysql 会超级慢
添加 master 主从复制部分配置
vim /mydata/mysql/master/conf/my.cnf
server_id=1
log-bin=mysql-bin
read-only=0
binlog-do-db=gulimall_ums
binlog-do-db=gulimall_pms
binlog-do-db=gulimall_oms
binlog-do-db=gulimall_sms
binlog-do-db=gulimall_wms
binlog-do-db=gulimall_admin
replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema
重启 master
docker restart mysql-master
2.3、创建 Slave 实例并启动
docker run -p 3317:3306 --name mysql-slaver-01 \
-v /mydata/mysql/slaver/log:/var/log/mysql \
-v /mydata/mysql/slaver/data:/var/lib/mysql \
-v /mydata/mysql/slaver/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
修改 slave 基本配置
vim /mydata/mysql/slaver/conf/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
skip-character-set-client-handshake
skip-name-resolve
添加 master 主从复制部分配置
vim /mydata/mysql/slaver/conf/my.cnf
server_id=2
log-bin=mysql-bin
read-only=1
binlog-do-db=gulimall_ums
binlog-do-db=gulimall_pms
binlog-do-db=gulimall_oms
binlog-do-db=gulimall_sms
binlog-do-db=gulimall_wms
binlog-do-db=gulimall_admin
replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema
重启 slaver
docker restart mysql-slaver-01
2.4、为 master 授权用户来他的同步数据
直接用navicat连接mysql-master数据库执行步骤2.2、3就可以
#1、进入 master 容器
docker exec -it mysql /bin/bash
#2、进入 mysql 内部 (mysql –uroot -p)
#2.1、授权 root 可以远程访问( 主从无关,为了方便我们远程连接 mysql)
grant all privileges on *.* to 'root'@'%' identified by 'root' with grant option;
flush privileges;
#2.2、添加用来同步的用户
GRANT REPLICATION SLAVE ON *.* to 'backup'@'%' identified by '123456';
#3、查看 master 状态
show master status;
2.5、配置 slaver 同步 master 数据
#1、进入 slaver 容器
docker exec -it mysql-slaver-01 /bin/bash
#2、进入 mysql 内部(mysql –uroot -p)
#2.1、授权 root 可以远程访问( 主从无关,为了方便我们远程连接 mysql)
grant all privileges on *.* to 'root'@'%' identified by 'root' with grant option;
flush privileges;
#2.2、设置主库连接
change master to master_host='192.168.119.127',master_user='backup',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=0,master_port=3307;
#3、启动从库同步
start slave;
#4、查看从库状态
show slave status;
至此主从配置完成;
总结:
- 主从数据库在自己配置文件中声明需要同步哪个数据库,忽略哪个数据库等信息。并且 server-id 不能一样
- 主库授权某个账号密码来同步自己的数据
- 从库使用这个账号密码连接主库来同步数据
3、MyCat 或者 ShardingSphere
shardingSphere: https://shardingsphere.apache.org/index_zh.html
auto_increment_offset: 1 从几开始增长
auto_increment_increment: 2 每次的步长
简介
Apache ShardingSphere 是一款分布式的数据库生态系统, 可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。
Apache ShardingSphere 设计哲学为 Database Plus,旨在构建异构数据库上层的标准和生态。 它关注如何充分合理地利用数据库的计算和存储能力,而并非实现一个全新的数据库。 它站在数据库的上层视角,关注它们之间的协作多于数据库自身。
-
ShardingSphere-JDBC
ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 -
ShardingSphere-Proxy
ShardingSphere-Proxy 定位为透明化的数据库代理端,通过实现数据库二进制协议,对异构语言提供支持。
3.1、下载安装 Sharding-Proxy
-
镜像方式
docker pull apache/sharding-proxy
docker run -d -v /mydata/sharding-proxy/conf:/opt/sharding-proxy/conf -v /mydata/sharding-proxy/lib:/opt/sharding-proxy/lib --env PORT=3308 -p13308:3308 apache/sharding-proxy:latest
-
压缩包下载
https://shardingsphere.apache.org/document/current/cn/downloads/
3.2、server.yaml 文件
authentication:
users:
root:
password: root
sharding:
password: sharding
authorizedSchemas: sharding_db
#
props:
# max.connections.size.per.query: 1
# acceptor.size: 16 # The default value is available processors count * 2.
executor.size: 16 # Infinite by default.
# proxy.frontend.flush.threshold: 128 # The default value is 128.
# # LOCAL: Proxy will run with LOCAL transaction.
# # XA: Proxy will run with XA transaction.
# # BASE: Proxy will run with B.A.S.E transaction.
# proxy.transaction.type: LOCAL
# proxy.opentracing.enabled: false
# query.with.cipher.column: true
sql.show: false
3.3、配置数据分片+读写分离
-
数据分片 - config-sharding.yaml
###################################################################################################### # # If you want to connect to MySQL, you should manually copy MySQL driver to lib directory. # ###################################################################################################### schemaName: sharding_db # dataSources: ds_0: url: jdbc:mysql://192.168.119.127:3307/demo_ds_0?serverTimezone=UTC&useSSL=false username: root password: root connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 ds_1: url: jdbc:mysql://192.168.119.127:3307/demo_ds_1?serverTimezone=UTC&useSSL=false username: root password: root connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 # shardingRule: tables: t_order: actualDataNodes: ds_${0..1}.t_order_${0..1} tableStrategy: inline: shardingColumn: order_id algorithmExpression: t_order_${order_id % 2} keyGenerator: type: SNOWFLAKE column: order_id t_order_item: actualDataNodes: ds_${0..1}.t_order_item_${0..1} tableStrategy: inline: shardingColumn: order_id algorithmExpression: t_order_item_${order_id % 2} keyGenerator: type: SNOWFLAKE column: order_item_id bindingTables: - t_order,t_order_item defaultDatabaseStrategy: inline: shardingColumn: user_id algorithmExpression: ds_${user_id % 2} defaultTableStrategy: none:
-
读写分离
-
config-master_slave.yaml
###################################################################################################### # # If you want to connect to MySQL, you should manually copy MySQL driver to lib directory. # ###################################################################################################### schemaName: sharding_db_1 # dataSources: master_0_ds: url: jdbc:mysql://192.168.119.127:3307/demo_ds_0?serverTimezone=UTC&useSSL=false username: root password: root connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 slave_ds_0: url: jdbc:mysql://192.168.119.127:3317/demo_ds_0?serverTimezone=UTC&useSSL=false username: root password: root connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 masterSlaveRule: name: ms_ds masterDataSourceName: master_0_ds slaveDataSourceNames: - slave_ds_0 # - slave_ds_1 # - slave_ds_1
-
config-master_slave_2.yaml
###################################################################################################### # # If you want to connect to MySQL, you should manually copy MySQL driver to lib directory. # ###################################################################################################### schemaName: sharding_db_2 # dataSources: master_1_ds: url: jdbc:mysql://192.168.119.127:3307/demo_ds_1?serverTimezone=UTC&useSSL=false username: root password: root connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 slave_ds_1: url: jdbc:mysql://192.168.119.127:3317/demo_ds_1?serverTimezone=UTC&useSSL=false username: root password: root connectionTimeoutMilliseconds: 30000 idleTimeoutMilliseconds: 60000 maxLifetimeMilliseconds: 1800000 maxPoolSize: 50 masterSlaveRule: name: ms_ds_1 masterDataSourceName: master_1_ds slaveDataSourceNames: - slave_ds_1 # - slave_ds_1 # - slave_ds_1
-
3.4、修改
修改mysql-master
和mysql-slaver-01
服务的my.cnf
,添加我们ShardingSphere配置的数据库
binlog-do-db=demo_ds_0
binlog-do-db=demo_ds_1
重启数据库
docker restart mysql-master mysql-slaver-01
mysql-master
和mysql-slaver-01
连接创建两个库demo_ds_0、demo_ds_1
进入sharding-proxy的bin目录下cmd 输入start.bat 3388
指定端口号
D:\ShardingSphere\apache-shardingsphere-incubating-4.0.0-sharding-proxy-bin\bin>start.bat 3388
在sharding_db创建测试表
CREATE TABLE `t_order` (
`order_id` bigint(20) NOT NULL,
`user_id` int(11) NOT NULL,
`status` varchar(50) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `t_order_item` (
`order_item_id` bigint(20) NOT NULL,
`order_id` bigint(20) NOT NULL,
`user_id` int(11) NOT NULL,
`content` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`status` varchar(50) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`order_item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
4、k8s 有状态服务部署
可以使用 kubesphere,快速搭建 MySQL 环境。
- 有状态服务抽取配置为 ConfigMap
- 有状态服务必须使用 pvc 持久化数据
- 服务集群内访问使用 DNS 提供的稳定域名
具体搭建流程:
4.1、 mysql-master(mysql主节点)
4.1.1、创建配置 - mysql-mster-cnf
我们把mysql配置的配置文件也单独挂在出来,先关闭服务创建,点击 配置中心 -》配置,创建mysql-mster-cnf
文件
-
填写基本信息
- 名称:mysql-mster-cnf
- 别名:mysql配置
-
配置设置
- 键:
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 skip-character-set-client-handshake skip-name-resolve server_id=1 log-bin=mysql-bin read-only=0 binlog-do-db=gulimall_ums binlog-do-db=gulimall_pms binlog-do-db=gulimall_oms binlog-do-db=gulimall_sms binlog-do-db=gulimall_wms binlog-do-db=gulimall_admin replicate-ignore-db=mysql replicate-ignore-db=sys replicate-ignore-db=information_schema replicate-ignore-db=performance_schema
- 键:
-
创建
4.1.2、创建存储卷 - gulimall-mysql-master-pvc
创建名称为gulimall-mysql-master-pvc
的存储卷
4.1.3、创建服务 - gulimall-mysql-master
-
选择有状态服务
-
填写基本信息
- 名称:gulimall-mysql-master
- 别名:mysql主节点
-
容器镜像
- 容器组选择分散部署
- 添加容器镜像
这里我们使用的私有镜像仓库-Harbor创建容器,使用默认端口,内存要大于1000M,环境变量选择原先配置好的MYSQL_ROOT_PASSWORD
- 容器组选择分散部署
-
挂载存储
-
挂载配置文件
首先,选择我们先前配置好的配置文件:mysql-master-cnf
,然后选择读写模式 ,我们查看原先docker创建mysql-master,发现我们以前挂载mysql的配置都是/etc/mysql
下的,所以我们路径填写/etc/mysql
,然后点击√
-
添加存储卷
首先,选择我们先前创建好的存储卷:gulimall-mysql-master-pvc
,然后选择读写模式,我们查看原先docker创建mysql-master,发现我们以前挂载mysql的数据都是/var/lib/mysql
下的,所以我们路径填写/var/lib/mysql
,然后点击√
-
-
点击下一步
-
点击创建
-
至此,gulimall-mysql-master服务创建完成
4.1.4、测试
-
验证容器是否创建成功
点击gulimall-mysql-master,进去之后我们可以看到服务是运行中,代表服务创建成功。
-
验证创建的mysql-master服务是否正确
进入容器组的
gulimall-mysql-master-ii799w-0
点击终端,进入容器内部,查看配置是否是我们配置的内容,可以看到my.conf正是我们的配置
-
验证其他服务是否能ping通
我们看到gulimall-mysql-master
服务的DNS为gulimall-mysql-master.gulimall
,我们在kubespere找一个其他服务进行测试,看看是否能ping通。
点击 worpress服务 -》容器组 -》wordpress-v1-5d8b597899-xz5cn,进入页面,然后点击左上角的终端
ping gulimall-mysql-master.gulimall
结果看到可以ping通。
4.2、 mysql-slaver(mysql从节点)
4.2.1、创建配置 - mysql-slaver-cnf
我们把mysql配置的配置文件也单独挂在出来,先关闭服务创建,点击 配置中心 -》配置,创建mysql-mster-cnf
文件
- 填写基本信息
- 名称:mysql-slaver-cnf
- 别名:mysql-slaver配置
2. 配置设置
-
键:
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 skip-character-set-client-handshake skip-name-resolve server_id=2 log-bin=mysql-bin read-only=1 binlog-do-db=gulimall_ums binlog-do-db=gulimall_pms binlog-do-db=gulimall_oms binlog-do-db=gulimall_sms binlog-do-db=gulimall_wms binlog-do-db=gulimall_admin replicate-ignore-db=mysql replicate-ignore-db=sys replicate-ignore-db=information_schema replicate-ignore-db=performance_schema
4.2.2、创建存储卷 - gulimall-mysql-slaver-pvc
创建名称为gulimall-mysql-slaver-pvc
的存储卷
4.2.3、创建服务 - gulimall-mysql-slaver
-
选择有状态服务
-
填写基本信息
- 名称:gulimall-mysql-slaver
- 别名:mysql从节点
-
容器镜像
-
容器组选择分散部署
-
添加容器镜像
这里我们使用的私有镜像仓库-Harbor创建容器,使用默认端口,内存要大于1000M,环境变量选择原先配置好的MYSQL_ROOT_PASSWORD
-
-
挂载存储
-
挂载配置文件
首先,选择我们先前配置好的配置文件:mysql-slaver-cnf
,然后选择读写模式 ,我们查看原先docker创建mysql-slaver-01,发现我们以前挂载mysql的配置都是/etc/mysql
下的,所以我们路径填写/etc/mysql
,然后点击√
-
添加存储卷
首先,选择我们先前创建好的存储卷:gulimall-mysql-slaver-pvc
,然后选择读写模式,我们查看原先docker创建mysql-slaver-01,发现我们以前挂载mysql的数据都是/var/lib/mysql
下的,所以我们路径填写/var/lib/mysql
,然后点击√
-
-
点击下一步
-
点击创建
-
至此,gulimall-mysql-slaver服务创建完成
4.3、为 master 授权用户来他的同步数据
-
点击 gulimall-mysql-master 服务 -》容器组 -》gulimall-mysql-master-ii799w-0,进入页面,然后点击左上角的终端进入容器,输入
mysql -uroot -p123456
登录mysql
-
添加用来同步的用户
GRANT REPLICATION SLAVE ON *.* to 'backup'@'%' identified by '123456';
-
查看 master 状态
show master status;
-
退出容器
4.4、配置 slaver 同步 master 数据
-
点击 gulimall-mysql-slaver 服务 -》容器组 -》gulimall-mysql-slaver-3urxgv-0,进入页面,然后点击左上角的终端进入容器,输入
mysql -uroot -p123456
登录mysql
-
设置主库连接
change master to master_host='gulimall-mysql-master.gulimall',master_user='backup',master_password='123456',master_log_file='mysql-bin.000002',master_log_pos=0,master_port=3306;
-
启动从库同步
start slave;
-
查看从库状态
show slave status\G;
4.5、测试
在guilimall-mysql-master服务创建一个数据库,看看是否可以同步到gulimall-mysql-slaver
- 进入gulimall-mysql-master容器,查看都有哪些数据库
show databases;
- 创建数据库
CREATE DATABASE gulimall_oms DEFAULT CHARACTER SET utf8mb4;
3. 去gulimall-mysql-slaver服务验证是否同步过来
可以看到创建的数据库已经同步过来
4.6、k8s搭建服务思路
- 每一个MySQL/Redis/elasticseash/rabbitmq必须都是-个有状态服务
- 每一个MySQL/Redis/elasticseash/rabbitmq挂载自己配置文件(cm)和PVC
- 以后的IP都用域名即可搭建出集群
因为电脑配置原因,后面的服务集群我们不全部使用集群,方法都是想通的,后面都是搭建单机
六、Redis 集群
1、redis 集群形式
1.1、数据分区方案
1、客户端分区
客户端分区方案 的代表为 Redis Sharding,Redis Sharding 是 Redis Cluster 出来之前,业界普遍使用的 Redis 多实例集群 方法。Java 的 Redis 客户端驱动库 Jedis,支持 Redis Sharding 功能,即 ShardedJedis 以及 结合缓存池 的 ShardedJedisPool。
-
优点
不使用 第三方中间件,分区逻辑 可控,配置 简单,节点之间无关联,容易 线性扩展,灵
活性强。 -
缺点
客户端 无法 动态增删 服务节点,客户端需要自行维护 分发逻辑,客户端之间 无连接共享,
会造成 连接浪费。
2、代理分区
代理分区常用方案有 Twemproxy 和 Codis。
3、redis-cluster
1.2、高可用方式
1.2.1、Sentinel( 哨兵机制)支持高可用
前面介绍了主从机制,但是从运维角度来看,主节点出现了问题我们还需要通过人工干预的方式把从节点设为主节点,还要通知应用程序更新主节点地址,这种方式非常繁琐笨重, 而且主节点的读写能力都十分有限,有没有较好的办法解决这两个问题,哨兵机制就是针对第一个问题的有效解决方案,第二个问题则有赖于集群!哨兵的作用就是监控 Redis 系统的运行状况,其功能主要是包括以下三个:
- 监控(Monitoring): 哨兵(sentinel) 会不断地检查你的 Master 和 Slave 是否运作正常。
- 提醒(Notification): 当被监控的某个 Redis 出现问题时, 哨兵(sentinel) 可以通过 API向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover): 当主数据库出现故障时自动将从数据库转换为主数据库。
哨兵的原理
Redis 哨兵的三个定时任务,Redis 哨兵判定一个 Redis 节点故障不可达主要就是通过三个定时监控任务来完成的:
-
每隔 10 秒每个哨兵节点会向主节点和从节点发送"info replication" 命令来获取最新的
拓扑结构
-
每隔 2 秒每个哨兵节点会向 Redis 节点的_sentinel_:hello 频道发送自己对主节点是否故障的判断以及自身的节点信息,并且其他的哨兵节点也会订阅这个频道来了解其他哨兵节点的信息以及对主节点的判断
-
每隔 1 秒每个哨兵会向主节点、从节点、其他的哨兵节点发送一个 “ping” 命令来做心跳检测
如果在定时 Job3 检测不到节点的心跳,会判断为“主观下线”。如果该节点还是主节点那么还会通知到其他的哨兵对该主节点进行心跳检测,这时主观下线的票数超过了数时,那么这个主节点确实就可能是故障不可达了,这时就由原来的主观下线变为了“客观下线”。故障转移和 Leader 选举
如果主节点被判定为客观下线之后,就要选取一个哨兵节点来完成后面的故障转移工作,选举出一个 leader,这里面采用的选举算法为 Raft。选举出来的哨兵 leader 就要来完成故障转移工作,也就是在从节点中选出一个节点来当新的主节点,这部分的具体流程可参考引用. 《深入理解 Redis 哨兵搭建及原理》
1.2.2、redis-cluster
详见下章
2、Redis-Cluster
https://redis.io/topics/cluster-tutorial/
Redis 的官方多机部署方案,Redis Cluster。一组 Redis Cluster 是由多个 Redis 实例组成,官方推荐我们使用 6 实例,其中 3 个为主节点,3 个为从结点。一旦有主节点发生故障的时候,Redis Cluster 可以选举出对应的从结点成为新的主节点,继续对外服务,从而保证服务的高可用性。那么对于客户端来说,知道知道对应的 key 是要路由到哪一个节点呢?Redis Cluster把所有的数据划分为 16384 个不同的槽位,可以根据机器的性能把不同的槽位分配给不同的 Redis 实例,对于 Redis 实例来说,他们只会存储部分的 Redis 数据,当然,槽的数据是可以迁移的,不同的实例之间,可以通过一定的协议,进行数据迁移。
2.1、槽
Redis 集群的功能限制;Redis 集群相对 单机 在功能上存在一些限制,需要 开发人员 提前
了解,在使用时做好规避。JAVA CRC16 校验算法
- key 批量操作 支持有限。
- 类似 mset、mget 操作,目前只支持对具有相同 slot 值的 key 执行 批量操作。
对于 映射为不同 slot 值的 key 由于执行 mget、mget 等操作可能存在于多个节
点上,因此不被支持。
- 类似 mset、mget 操作,目前只支持对具有相同 slot 值的 key 执行 批量操作。
- key 事务操作 支持有限。
- 只支持 多 key 在 同一节点上 的 事务操作,当多个 key 分布在 不同 的节点上时 无法 使用事务功能。
- key 作为 数据分区 的最小粒度
- 不能将一个 大的键值 对象如 hash、list 等映射到 不同的节点。
- 不支持 多数据库空间
- 单机 下的 Redis 可以支持 16 个数据库(db0 ~ db15),集群模式 下只能使用 一个 数据库空间,即 db0。
- 复制结构 只支持一层
- 从节点 只能复制 主节点,不支持 嵌套树状复制 结构。
- 命令大多会重定向,耗时多
2.2、一致性 hash
一致性哈希 可以很好的解决 稳定性问题,可以将所有的 存储节点 排列在 收尾相接 的Hash 环上,每个 key 在计算 Hash 后会 顺时针 找到 临接 的 存储节点 存放。而当有节点 加入 或 退出 时,仅影响该节点在 Hash 环上 顺时针相邻 的 后续节点。
Hash 倾斜
如果节点很少,容易出现倾斜,负载不均衡问题。一致性哈希算法,引入了虚拟节点,在整个环上,均衡增加若干个节点。比如 a1,a2,b1,b2,c1,c2,a1 和 a2 都是属于 A 节点的。解决 hash 倾斜问题
3、部署 Cluster
3.1、创建 6 个 redis 节点
3 主 3 从方式,从为了同步备份,主进行 slot 数据分片
for port in $(seq 7001 7006); 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
port ${port}
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 192.168.119.127
cluster-announce-port ${port}
cluster-announce-bus-port 1${port}
appendonly yes
EOF
docker run -p ${port}:${port} -p 1${port}:1${port} --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d redis:5.0.7 redis-server /etc/redis/redis.conf
done
如果停止或者删除可以操作以下命令
docker stop $(docker ps -a |grep redis-700 | awk '{ print $1}')
docker rm $(docker ps -a |grep redis-700 | awk '{ print $1}')
3.2、使用 redis 建立集群
# 随便进入一个redis节点容器
docker exec -it redis-7001 bash
# 通过redis-cli创建redis集群
redis-cli --cluster create 192.168.119.127:7001 192.168.119.127:7002 192.168.119.127:7003 192.168.119.127:7004 192.168.119.127:7005 192.168.119.127:7006 --cluster-replicas 1
3.3、测试集群效果
随便进入某个 redis 容器
docker exec -it redis-7002 /bin/bash
使用 redis-cli 的 cluster 方式进行连接
redis-cli -c -h 192.168.119.127 -p 7006
Get/Set 命令测试,将会重定向节点宕机,slave 会自动提升为 master,master 开启后变为 slave
root@174151a9f811:/data# redis-cli -c -h 192.168.119.127 -p 7006
192.168.119.127:7006> set hello 1
-> Redirected to slot [866] located at 192.168.119.127:7001
OK
192.168.119.127:7001> set a aaaa
-> Redirected to slot [15495] located at 192.168.119.127:7003
OK
192.168.119.127:7003> get hello
-> Redirected to slot [866] located at 192.168.119.127:7001
"1"
192.168.119.127:7001> get a
-> Redirected to slot [15495] located at 192.168.119.127:7003
"aaaa"
192.168.119.127:7003>
获取集群信息
cluster info;
获取集群节点
cluster nodes;
4、k8s部署redis
参照有状态部署即可
4.1、创建配置 - redis-conf
- 基本信息
- 名称:redis-conf
- 名称:redis-conf
- 配置设置
- 键(Key):redis-conf
- 值(Value):appendonly yes
- 创建
4.2、创建存储卷 - gulimall-redis-pvc
- 基本信息
创建名称为gulimall-redis-pvc
的存储卷
- 存储卷设置
- 创建
4.3、创建服务- gulimall -redis
-
选择有状态服务
-
填写基本信息
- 名称:gulimall-redis
- 别名:redis服务
-
容器镜像
-
因为就一个容器,我们容器组选择容器组默认部署就好
-
添加容器镜像
这里我们使用的私有镜像仓库-Harbor创建容器,使用默认端口
-
设置一个启动命令,这里配置参考docker创建 6 个 redis 节点的命令 redis-server /etc/redis/redis.conf
4. 下一步
5. 挂载存储
- 挂载存储卷
这里的挂载路径也参考docker创建 6 个 redis 节点的命令 /data
- 挂载配置文件
- 文件挂载路径:/etc/redis
- 特定的键:redis-conf
- 路径:redis.conf
这里的配置我们参考创建 6 个 redis 节点的命令![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/2c4e8c6ac63e42a784c1d6664e1d5658.png)
- 下一步
- 创建
4.4、测试
我们点击 gulimall-redis
服务 -》 容器组 -》gulimall-redis-4vf9pc-0
-》端口
通过redis-cli客户端命令进行get和set方法测试
# redis-cli
127.0.0.1:6379> set hello aaa
OK
127.0.0.1:6379> get hello
"aaa"
127.0.0.1:6379>
七、Elasticsearch 集群
1、集群原理
https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/distributed-cluster.html
elasticsearch 是天生支持集群的,他不需要依赖其他的服务发现和注册的组件,如 zookeeper这些,因为他内置了一个名字叫 ZenDiscovery 的模块,是 elasticsearch 自己实现的一套用于节点发现和选主等功能的组件,所以 elasticsearch 做起集群来非常简单,不需要太多额外的配置和安装额外的第三方组件。
1.1、单节点
- 一个运行中的 Elasticsearch 实例称为一个节点,而集群是由一个或者多个拥有相同cluster.name 配置的节点组成, 它们共同承担数据和负载的压力。当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据。
- 当一个节点被选举成为 主节点时, 它将负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。 而主节点并不需要涉及到文档级别的变更和搜索等操作,所以当集群只拥有一个主节点的情况下,即使流量的增加它也不会成为瓶颈。任何节点都可以成为主节点。我们的示例集群就只有一个节点,所以它同时也成为了主节点。
- 作为用户,我们可以将请求发送到 集群中的任何节点 ,包括主节点。 每个节点都知道任意文档所处的位置,并且能够将我们的请求直接转发到存储我们所需文档的节点。无论我们将请求发送到哪个节点,它都能负责从各个包含我们所需文档的节点收集回数据,并将最终结果返回給客户端。 Elasticsearch 对这一切的管理都是透明的。
1.2、集群健康
Elasticsearch 的集群监控信息中包含了许多的统计数据,其中最为重要的一项就是 集群健康 , 它在 status 字段中展示为 green 、 yellow 或者 red 。
status 字段指示着当前集群在总体上是否工作正常。它的三种颜色含义如下:
green:所有的主分片和副本分片都正常运行。
yellow:所有的主分片都正常运行,但不是所有的副本分片都正常运行。
red:有主分片没能正常运行。
1.3、分片
- 一个 分片 是一个底层的 工作单元 ,它仅保存了全部数据中的一部分。我们的文档被存储和索引到分片内,但是应用程序是直接与索引而不是与分片进行交互。分片就认为是一个数据区
- 一个分片可以是 主 分片或者 副本 分片。索引内任意一个文档都归属于一个主分片,所以主分片的数目决定着索引能够保存的最大数据量。
- 在索引建立的时候就已经确定了主分片数,但是副本分片数可以随时修改。
- 让我们在包含一个空节点的集群内创建名为 blogs 的索引。 索引在默认情况下会被分配 5 个主分片, 但是为了演示目的,我们将分配 3 个主分片和一份副本(每个主分片拥有一个副本分片):
此时集群的健康状况为 yellow 则表示全部 主分片都正常运行(集群可以正常服务所有请求),但是 副本 分片没有全部处在正常状态。 实际上,所有 3 个副本分片都是 unassigned —— 它们都没有被分配到任何节点。在同一个节点上既保存原始数据又保存副本是没有意义的,因为一旦失去了那个节点,我们也将丢失该节点上的所有副本数据。
当前我们的集群是正常运行的,但是在硬件故障时有丢失数据的风险。
1.4、新增节点
当你在同一台机器上启动了第二个节点时,只要它和第一个节点有同样的 cluster.name 配置,它就会自动发现集群并加入到其中。 但是在不同机器上启动节点的时候,为了加入到同一集群,你需要配置一个可连接到的单播主机列表。 详细信息请查看最好使用单播代替组播
此时,cluster-health 现在展示的状态为 green ,这表示所有 6 个分片(包括 3 个主分片和3 个副本分片)都在正常运行。我们的集群现在不仅仅是正常运行的,并且还处于 始终可用 的状态。
1.5、水平扩容-启动第三个节点
Node 1 和 Node 2 上各有一个分片被迁移到了新的 Node 3 节点,现在每个节点上都拥有 2 个分片,而不是之前的 3 个。 这表示每个节点的硬件资源(CPU, RAM, I/O)将被更少的分片所共享,每个分片的性能将会得到提升。
在运行中的集群上是可以动态调整副本分片数目的,我们可以按需伸缩集群。让我们把副本数从默认的 1 增加到 2
blogs 索引现在拥有 9 个分片:3 个主分片和 6 个副本分片。 这意味着我们可以将集群扩容到 9 个节点,每个节点上一个分片。相比原来 3 个节点时,集群搜索性能可以提升 3 倍。
6、应对故障
- 我们关闭的节点是一个主节点。而集群必须拥有一个主节点来保证正常工作,所以发生的第一件事情就是选举一个新的主节点: Node 2 。
- 在我们关闭 Node 1 的同时也失去了主分片 1 和 2 ,并且在缺失主分片的时候索引也不能正常工作。 如果此时来检查集群的状况,我们看到的状态将会为 red :不是所有主分片都在正常工作。
- 幸运的是,在其它节点上存在着这两个主分片的完整副本, 所以新的主节点立即将这些分片在 Node 2 和 Node 3 上对应的副本分片提升为主分片, 此时集群的状态将会为 yellow 。 这个提升主分片的过程是瞬间发生的,如同按下一个开关一般。
- 为什么我们集群状态是 yellow 而不是 green 呢? 虽然我们拥有所有的三个主分片,但是同时设置了每个主分片需要对应 2 份副本分片,而此时只存在一份副本分片。 所以集群不能为 green 的状态,不过我们不必过于担心:如果我们同样关闭了 Node 2 ,我们的程序 依然 可以保持在不丢任何数据的情况下运行,因为 Node 3 为每一个分片都保留着一份副本。
- 如果我们重新启动 Node 1 ,集群可以将缺失的副本分片再次进行分配。如果 Node 1依然拥有着之前的分片,它将尝试去重用它们,同时仅从主分片复制发生了修改的数据文件。
1.7、问题与解决
1、主节点
主节点负责创建索引、删除索引、分配分片、追踪集群中的节点状态等工作。Elasticsearch中的主节点的工作量相对较轻,用户的请求可以发往集群中任何一个节点,由该节点负责分发和返回结果,而不需要经过主节点转发。而主节点是由候选主节点通过 ZenDiscovery 机制选举出来的,所以要想成为主节点,首先要先成为候选主节点。
2、候选主节点
在 elasticsearch 集群初始化或者主节点宕机的情况下,由候选主节点中选举其中一个作为主节点。指定候选主节点的配置为:node.master: true。
当主节点负载压力过大,或者集中环境中的网络问题,导致其他节点与主节点通讯的时候,主节点没来的及响应,这样的话,某些节点就认为主节点宕机,重新选择新的主节点,这样的话整个集群的工作就有问题了,比如我们集群中有 10 个节点,其中 7 个候选主节点,1个候选主节点成为了主节点,这种情况是正常的情况。但是如果现在出现了我们上面所说的主节点响应不及时,导致其他某些节点认为主节点宕机而重选主节点,那就有问题了,这剩下的 6 个候选主节点可能有 3 个候选主节点去重选主节点,最后集群中就出现了两个主节点的情况,这种情况官方成为“脑裂现象”;
集群中不同的节点对于 master 的选择出现了分歧,出现了多个 master 竞争,导致主分片和副本的识别也发生了分歧,对一些分歧中的分片标识为了坏片。
3、数据节点
数据节点负责数据的存储和相关具体操作,比如 CRUD、搜索、聚合。所以,数据节点对机器配置要求比较高,首先需要有足够的磁盘空间来存储数据,其次数据操作对系统 CPU、Memory 和 IO 的性能消耗都很大。通常随着集群的扩大,需要增加更多的数据节点来提高可用性。指定数据节点的配置:node.data: true。
elasticsearch 是允许一个节点既做候选主节点也做数据节点的,但是数据节点的负载较重,所以需要考虑将二者分离开,设置专用的候选主节点和数据节点,避免因数据节点负载重导致主节点不响应。
5、脑裂”问题可能的成因
1.网络问题:集群间的网络延迟导致一些节点访问不到 master,认为 master 挂掉了从而选举出新的 master,并对 master 上的分片和副本标红,分配新的主分片
2.节点负载:主节点的角色既为 master 又为 data,访问量较大时可能会导致 ES 停止响应造成大面积延迟,此时其他节点得不到主节点的响应认为主节点挂掉了,会重新选取主节点。
3.内存回收:data 节点上的 ES 进程占用的内存较大,引发 JVM 的大规模内存回收,造成 ES进程失去响应。
- 脑裂问题解决方案:
- 角色分离:即 master 节点与 data 节点分离,限制角色;数据节点是需要承担存储和搜索的工作的,压力会很大。所以如果该节点同时作为候选主节点和数据节点,那么一旦选上它作为主节点了,这时主节点的工作压力将会非常大,出现脑裂现象的概率就增加了。
- 减少误判:配置主节点的响应时间,在默认情况下,主节点 3 秒没有响应,其他节点就认为主节点宕机了,那我们可以把该时间设置的长一点,该配置是:
discovery.zen.ping_timeout: 5 - 选举触发:discovery.zen.minimum_master_nodes:1(默认是 1),该属性定义的是为了形成一个集群,有主节点资格并互相连接的节点的最小数目。
- 一 个 有 10 节 点 的 集 群 , 且 每 个 节 点 都 有 成 为 主 节 点 的 资 格 , discovery.zen.minimum_master_nodes 参数设置为 6。
- 正常情况下,10 个节点,互相连接,大于 6,就可以形成一个集群。
- 若某个时刻,其中有 3 个节点断开连接。剩下 7 个节点,大于 6,继续运行之前的集群。而断开的 3 个节点,小于 6,不能形成一个集群。
- 该参数就是为了防止”脑裂”的产生。
- 建议设置为(候选主节点数 / 2) + 1,
8、集群结构
以三台物理机为例。在这三台物理机上,搭建了 6 个 ES 的节点,三个 data 节点,三个 master节点(每台物理机分别起了一个 data 和一个 master),3 个 master 节点,目的是达到(n/2)+1 等于 2 的要求,这样挂掉一台 master 后(不考虑 data),n 等于 2,满足参数,其他两个 master 节点都认为 master 挂掉之后开始重新选举,
master 节点上
node.master = true
node.data = false
discovery.zen.minimum_master_nodes = 2
data 节点上
node.master = false
node.data = true
2、集群搭建
所有之前先运行:
sysctl -w vm.max_map_count=262144
我们只是测试,所以临时修改。永久修改使用下面
#防止 JVM 报错
echo vm.max_map_count=262144 >> /etc/sysctl.conf
sysctl -p
2.0、准备 docker 网络
Docker 创建容器时默认采用 bridge 网络,自行分配 ip,不允许自己指定。
在实际部署中,我们需要指定容器 ip,不允许其自行分配 ip,尤其是搭建集群时,固定 ip是必须的。
我们可以创建自己的 bridge 网络 : mynet,创建容器的时候指定网络为 mynet 并指定 ip即可。
查看网络模式
docker network ls
创建一个新的 bridge 网络
docker network create --driver bridge --subnet=192.168.0.0/16 --gateway=192.168.0.1 mynet
查看网络信息
docker network inspect mynet
以后使用–network=mynet --ip 192.168.0.x 指定 ip
2.1、3-Master 节点创建
for port in $(seq 1 3); \
do \
mkdir -p /mydata/elasticsearch/master-${port}/config
mkdir -p /mydata/elasticsearch/master-${port}/data
chmod -R 777 /mydata/elasticsearch/master-${port}
cat << EOF >/mydata/elasticsearch/master-${port}/config/elasticsearch.yml
cluster.name: my-es #集群的名称,同一个集群该值必须设置成相同的
node.name: es-master-${port} #该节点的名字
node.master: true #该节点有机会成为 master 节点
node.data: false #该节点可以存储数据
network.host: 0.0.0.0
http.host: 0.0.0.0 #所有 http 均可访问
http.port: 920${port}
transport.tcp.port: 930${port}
#discovery.zen.minimum_master_nodes: 2 #设置这个参数来保证集群中的节点可以知道其它 N 个有 master 资格的节点。官方推荐(N/2)+1
discovery.zen.ping_timeout: 10s #设置集群中自动发现其他节点时 ping 连接的超时时间
discovery.seed_hosts: ["192.168.0.21:9301", "192.168.0.22:9302", "192.168.0.23:9303"] #设置集群中的 Master 节点的初始列表,可以通过这些节点来自动发现其他新加入集群的节点,es7的新增配置
cluster.initial_master_nodes: ["192.168.0.21"] #新集群初始时的候选主节点,es7 的新增配置
EOF
docker run --name elasticsearch-node-${port} \
-p 920${port}:920${port} -p 930${port}:930${port} \
--network=mynet --ip 192.168.0.2${port} \
-e ES_JAVA_OPTS="-Xms300m -Xmx300m" \
-v /mydata/elasticsearch/master-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/master-${port}/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/master-${port}/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2
done
docker stop $(docker ps -a |grep elasticsearch-node-* | awk '{ print $1}')
docker rm $(docker ps -a |grep elasticsearch-node-* | awk '{ print $1}')
2.2、3-Data-Node 创建
for port in $(seq 4 6); \
do \
mkdir -p /mydata/elasticsearch/node-${port}/config
mkdir -p /mydata/elasticsearch/node-${port}/data
chmod -R 777 /mydata/elasticsearch/node-${port}
cat << EOF >/mydata/elasticsearch/node-${port}/config/elasticsearch.yml
cluster.name: my-es #集群的名称,同一个集群该值必须设置成相同的
node.name: es-node-${port} #该节点的名字
node.master: false #该节点有机会成为 master 节点
node.data: true #该节点可以存储数据
network.host: 0.0.0.0
#network.publish_host: 192.168.119.127 #互相通信 ip,要设置为本机可被外界访问的 ip,否则无法通信
http.host: 0.0.0.0 #所有 http 均可访问
http.port: 920${port}
transport.tcp.port: 930${port}
#discovery.zen.minimum_master_nodes: 2 #设置这个参数来保证集群中的节点可以知道其它 N 个有 master 资格的节点。官方推荐(N/2)+1
discovery.zen.ping_timeout: 10s #设置集群中自动发现其他节点时 ping 连接的超时时间
discovery.seed_hosts: ["192.168.0.21:9301", "192.168.0.22:9302", "192.168.0.23:9303"] #设置集群中的 Master 节点的初始列表,可以通过这些节点来自动发现其他新加入集群的节点,es7的新增配置
cluster.initial_master_nodes: ["192.168.0.21"] #新集群初始时的候选主节点,es7 的新增配置
EOF
docker run --name elasticsearch-node-${port} \
-p 920${port}:920${port} -p 930${port}:930${port} \
--network=mynet --ip 192.168.0.2${port} \
-e ES_JAVA_OPTS="-Xms300m -Xmx300m" \
-v /mydata/elasticsearch/node-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/node-${port}/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/node-${port}/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2
done
3、测试集群
http://192.168.0.21:9201/_nodes/process?pretty 查看节点状况
http://192.168.0.21:9201/_cluster/stats?pretty 查看集群状态
http://192.168.0.21.10:9201/_cluster/health?pretty 查看集群健康状况
http://192.168.0.21:9202/_cat/nodes 查看各个节点信息
$ curl localhost:9200/_cat
/_cat/allocation
/_cat/shards
/_cat/shards/{index}
/_cat/master
/_cat/nodes
/_cat/indices
/_cat/indices/{index}
/_cat/segments
/_cat/segments/{index}
/_cat/count
/_cat/count/{index}
/_cat/recovery
/_cat/recovery/{index}
/_cat/health
/_cat/pending_tasks
/_cat/aliases
/_cat/aliases/{alias}
/_cat/thread_pool
/_cat/plugins
/_cat/fielddata
/_cat/fielddata/{fields}
/_cat/nodeattrs
/_cat/repositories
/_cat/snapshots/{repository}
3、k8s 上部署
有状态服务
jvm.options -Xms100m
-Xmx512m
3.1、创建配置 - elastic-search-conf
-
创建配置
- 名称:
elastic-search-conf
- 名称:
-
配置设置
-
http.host:0.0.0.0
-
discovery.type:single-node
-
ES_JAVA_OPTS:-Xms64m -Xmx512m
-
-
创建
3.2、创建存储卷 - gulimall-elasticsearch-pvc
- 创建一个名称为
gulimall-elasticsearch-pvc
的存储卷
- 下一步
3、创建
3.3、创建服务 - gulimall-elasticsearch
-
选择有状态服务
-
基本信息
- 名称:gulimall-elasticsearch
- 名称:gulimall-elasticsearch
-
容器镜像
- 因为就一个容器,我们容器组选择容器组默认部署就好
- 添加容器镜像
这里我们使用的私有镜像仓库-Harbor创建容器,使用默认端口
- 因为就一个容器,我们容器组选择容器组默认部署就好
-
挂载存储
这里挂载路径参考docker创建elasticsearch的路径/usr/share/elasticsearch/data
-
下一步
-
创建
3.4、测试
以admin登录kubespere,进入控制台
查看elasticsearch有json数据,则安装成功。
/ # curl http://gulimall-elasticsearch.gulimall:9200
{
"name" : "gulimall-elasticsearch-z8eh94-0",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "fc3Pm8DLTzCXSHGYXR8oTA",
"version" : {
"number" : "7.4.2",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "2f90bbf7b93631e52bafb59b3b049cb44ec25e96",
"build_date" : "2019-10-28T20:40:44.881551Z",
"build_snapshot" : false,
"lucene_version" : "8.2.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
3.5、创建服务 - gulimall-kibana
-
创建一个无状态服务
-
填写基础信息
- 名称:gulimall-kibana
- 名称:gulimall-kibana
-
容器镜像
- 因为就一个容器,我们容器组选择容器组默认部署就好
- 添加容器镜像
这里我们使用的私有镜像仓库-Harbor创建容器,使用默认端口
我们需要访问elasticsearch路径,这里环境变量配置
ELASTICSEARCH_HOSTS
:http://gulimall-elasticsearch.gulimall:9200
- 因为就一个容器,我们容器组选择容器组默认部署就好
-
挂载存储
不需要挂载,直接下一步
-
创建
配置外网访问,这样我们可以在外网访问kabana
我们可以看到外网访问的端口为31275
浏览器访问:http://192.168.119.130:31275
八、RabbitMQ 集群
1、集群形式
RabbiMQ 是用 Erlang 开发的,集群非常方便,因为 Erlang 天生就是一门分布式语言,但其本身并不支持负载均衡。
RabbitMQ 集群中节点包括内存节点(RAM)、磁盘节点(Disk,消息持久化),集群中至少有一个 Disk 节点。
-
普通模式(默认)
对于普通模式,集群中各节点有相同的队列结构,但消息只会存在于集群中的一个节点。对于消费者来说,若消息进入 A 节点的 Queue 中,当从 B 节点拉取时,RabbitMQ 会将消息从 A 中取出,并经过 B 发送给消费者。
应用场景:该模式各适合于消息无需持久化的场合,如日志队列。当队列非持久化,且创建该队列的节点宕机,客户端才可以重连集群其他节点,并重新创建队列。若为持久化,只能等故障节点恢复。 -
镜像模式
与普通模式不同之处是消息实体会主动在镜像节点间同步,而不是在取数据时临时拉取,高可用;该模式下,mirror queue 有一套选举算法,即 1 个 master、n 个 slaver,生产者、消费者的请求都会转至 master。应用场景:可靠性要求较高场合,如下单、库存队列。
缺点:若镜像队列过多,且消息体量大,集群内部网络带宽将会被此种同步通讯所消耗。
(1)镜像集群也是基于普通集群,即只有先搭建普通集群,然后才能设置镜像队列。
(2)若消费过程中,master 挂掉,则选举新 master,若未来得及确认,则可能会重复消费。
1.1、搭建集群
mkdir /mydata/rabbitmq
cd rabbitmq/
mkdir rabbitmq01 rabbitmq02 rabbitmq03
docker run -d --hostname rabbitmq01 --name rabbitmq01 \
-v /mydata/rabbitmq/rabbitmq01:/var/lib/rabbitmq \
-p 15673:15672 \
-p 5673:5672 \
-e RABBITMQ_ERLANG_COOKIE='atguigu' rabbitmq:management
docker run -d --hostname rabbitmq02 --name rabbitmq02 \
-v /mydata/rabbitmq/rabbitmq02:/var/lib/rabbitmq \
-p 15674:15672 \
-p 5674:5672 \
-e RABBITMQ_ERLANG_COOKIE='atguigu' \
--link rabbitmq01:rabbitmq01 rabbitmq:management
docker run -d --hostname rabbitmq03 --name rabbitmq03 \
-v /mydata/rabbitmq/rabbitmq03:/var/lib/rabbitmq \
-p 15675:15672 \
-p 5675:5672 \
-e RABBITMQ_ERLANG_COOKIE='atguigu' \
--link rabbitmq01:rabbitmq01 \
--link rabbitmq02:rabbitmq02 rabbitmq:management
--hostname 设置容器的主机名
RABBITMQ_ERLANG_COOKIE 节点认证作用,部署集成时 需要同步该值
1.2、节点加入集群
docker exec -it rabbitmq01 /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
exit
进入第二个节点
docker exec -it rabbitmq02 /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq01
rabbitmqctl start_app
exit
进入第三个节点
docker exec -it rabbitmq03 bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq01
rabbitmqctl start_app
exit
访问一个节点后台:http://192.168.119.127:15675/ 可以看到集群搭建成功。
但是,目前只是一个普通集群,普通集群会出现单点故障的问题,下面我们继续实现镜像集群。
1.3、实现镜像集群
docker exec -it rabbitmq01 bash
rabbitmqctl set_policy -p / ha "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
rabbitmqctl set_policy -p / ha-all “^” ’{“ha-mode”:“all”}’
策略模式 all 即复制到所有节点,包含新增节点,策略正则表达式为 “^” 表示所有匹配所有队列名称。“^hello”表示只匹配名为 hello 开始的队列
可以使用 rabbitmqctl list_policies -p /
;查看 vhost/下面的所有 policy在 cluster 中任意节点启用策略,策略会自动同步到集群节点
[root@localhost rabbitmq]# docker exec -it rabbitmq01 bash
root@rabbitmq01:/# rabbitmqctl list_policies -p /
2、集群测试
随便在 mq 上创建一个队列,发送一个消息,保证整个集群其他节点都有这个消息。如果master 宕机,其他节点也能成为新的 master
- 在rabbitmq01创建一个队列,看看其他的rabbitmq有没有队列
点击rabbitmq03,查看是否有队列
- 在rabbitmq01发送一个持久化消息,看看其他rabbitmq有没有消息并手动确认消费,再查看是否都消费掉
点击rabbitmq02查看队列是否有消息,并手动确认消费掉
查看其他rabbitmq01、rabbitmq02、rabbitmq03是否都消费掉
3、k8s 上部署
3.1、创建存储卷 - gulimall-rabbitmq-pvc
创建一个名称为gulimall-rabbitmq-pvc的存储卷
3.2、创建服务 - gulimall-rabbitmq-management
- 创建一个有状态服务
- 填写基本信息
- 名称 :
gulimall-rabbitmq-management
- 名称 :
- 容器镜像
- 挂载存储
挂载路径参考原先docker创建rabbitmq的路径:/var/lib/rabbitmq
- 创建
九、其他集群搭建
1、k8s 部署 nacos
以前docker部署命令
docker run --env MODE=standalone --name nacos \
-v /mydata/nacos/conf:/home/nacos/conf -d -p 8848:8848 nacos/nacos-server:v2.1.1
1.1、创建存储卷 - gulimall-nacos-pvc
1.2、创建服务 - gulimall-nacos
-
选择一个有状态服务
-
填写基础信息
- 名称:gulimall-nacos
- 名称:gulimall-nacos
-
容器镜像
-
因为单机部署所以选择容器组默认部署
-
添加容器镜像
镜像:nacos/nacos-server:v2.1.1注意:内存至少1000M,否则容易内存溢出,服务创建失败
添加环境变量
MODE
:standalone
-
-
挂载存储
挂载路径:/home/nacos/data
-
创建
1.3、如何把已部署的有状态服务做成外网访问?
1、通过nginx代理,外网可以访问
2、删除服务,重新创建无状态服务
我们使用第二种方式,但是我们不是完全删除,之删除服务,像容器组不删除。
- 删除服务,有状态负载集不删除
服务里没有了nacos
容器组还有nacos
- 创建指定工作负载的服务
- 填写基本信息
- 指定工作负载
- 服务设置
- 创建
设置外网访问
- 可以看到对外暴露端口号:30271
8、随便选则一个节点ip访问,访问地址:http://192.168.119.130:30271/nacos
1.4、创建服务 - gulimall-nacos-service
我们已经部署了一台允许外网访问的gulinall-nacos服务,我们通过gulimall-sentinel服务的终端测试发现,内部集群无法访问gulinall-nacos服务的DNS:gulimall-nacos.gulimall
。
因为我们后面搭建集群项目各个微服务需要用到nacos,所以接下来我们需要创建一个集群内部通过服务的后端 Endpoint IP 直接访问服务。
- 选择指定工作负载创建服务
- 填写基本信息
- 名称:
gulimall-nacos-service
- 名称:
- 服务设置
- 创建
- 测试集群内部是否可以访问gulimall-nacos-service服务的DNS:
gulimall-nacos-service.gulimall
我们通过gulimall-sentinel服务的终端测试发现现在是正常访问的。
2、k8s 部署 zipkin
docker run -d -p 9411:9411 openzipkin/zipkin
或者
docker run --env STORAGE_TYPE=elasticsearch --env ES_HOSTS=192.168.56.10:9200 openzipkin/zipkin
2.1、创建服务 - gulimall-zipkin
-
选择无状态服务
-
填写基础信息
- 名称:
gulimall-zipkin
- 别名:链路追踪
- 名称:
-
容器镜像
- 选择容器组默认部署
- 添加容器镜像
镜像:openzipkin/zipkin
环境变量- STORAGE_TYPE:elasticsearch
- ES_HOSTS:http://gulimall-elasticsearch.gulimall:9200
- 选择容器组默认部署
-
挂载存储
-
创建
-
外网访问端口号:31610
-
随便找一个节点ip进行测试:http://192.168.119.130:31610/zipkin/
3.2、创建服务 - gulimall-zipkin-service
我们已经部署了一台允许外网访问的gulimall-zipkin服务,由此可知集群内部无法访问。因为我们后面搭建集群项目各个微服务需要用到zipkin,所以接下来我们需要创建一个集群内部通过服务的后端 Endpoint IP 直接访问服务。
-
选择指定工作负载创建服务
-
填写基本信息
- 名称:
gulimall-sentimel-service
- 名称:
-
服务设置
-
创建
-
测试集群内部是否可以访问gulimall-zipkin-service服务的DNS:
gulimall-zipkin-service.gulimall
我们通过gulimall-sentinel服务的终端测试发现现在是正常访问的。
3、k8s 部署 sentinel
可以制作一个镜像并启动它,暴露访问
docker run --name sentinel -d -p 8858:8858 -d bladex/sentinel-dashboard:1.6.3
3.1、创建服务 - gulimall-sentinel
- 选择无状态服务
- 填写基础信息
- 名称:
gulimall-sentinel
- 别名:熔断/降级服务
- 名称:
- 容器镜像
- 选择容器组默认部署
- 添加容器镜像
镜像:bladex/sentinel-dashboard:1.6.3
- 选择容器组默认部署
- 挂载存储
- 创建
设置外网访问
6、外网可以访问的端口号:30702 - 随便选择一个节点ip,进行测试:http://192.168.119.130:30702/
3.2、创建服务 - gulimall-sentimel-service
我们已经部署了一台允许外网访问的gulimall-sentimel服务,由此可知集群内部无法访问。因为我们后面搭建集群项目各个微服务需要用到sentimel,所以接下来我们需要创建一个集群内部通过服务的后端 Endpoint IP 直接访问服务。
-
选择指定工作负载创建服务
-
填写基本信息
- 名称:
gulimall-sentimel-service
- 名称:
-
服务设置
-
创建
-
测试集群内部是否可以访问gulimall-sentimel-service服务的DNS:
gulimall-sentimel-service.gulimall
我们通过gulimall-sentinel服务的终端测试发现现在是正常访问的。