实战 Kubernetes StatefulSet — MySQL 主从集群搭建
1. 引言
前面两篇文章,我们详细介绍了 Kubernetes 中 StatefulSet 的网络状态和存储状态:
有状态的节点控制器 — StatefulSet 及其网络状态
有状态的节点控制器 StatefulSet 的存储状态
那么,StatefulSet 究竟要怎么将这两者结合起来呢?实践中有哪些经典的案例可以通过 StatefulSet 方便快捷地来实现呢?本文我们就来详细介绍一下。
2. 有状态应用的典型案例 — mysql 主从
mysql 集群是一个非常典型的有状态应用,和 elasticsearch、kafka 等自选举的集群不同,mysql 的集群组建显得有些“原始”。
此前我们介绍过如何来构建一个 mysql 主从集群:
对于 mysql 集群来说,我们首先要选取主节点,并且启动它,如果这是一个已有数据 mysql 节点,还需要考虑如何备份 mysql 主节点上的数据。
此后,我们需要用另一套配置来启动若干从节点,并且在这些从节点上恢复上一步中主节点上的备份数据。
完成上述配置之后,我们还必须考虑如何保证只让主节点处理写请求,而读请求则可以在任意节点上执行。
除此以外,从节点的水平扩展也是必须考虑另一个问题。
由此可见,mysql 主从集群的构建具有网络状态 — 主节点必须先行启动,并且具有存储状态 — 每个节点需要有自己独立的存储,很显然,用 Deployment 作为控制器来进行 mysql 集群的搭建是无法实现的,而这恰恰是 StatefulSet 擅长处理的场景。
3. 主从节点的区分 — 配置与读写
3.1 主从节点不同的配置文件
mysql 主节点与从节点拥有完全不同的配置,主节点需要开启 log-bin 通过二进制的方式导出 bin-log 来实现主从复制,从节点需要配置主节点的信息外,还需要配置 super-read-only 来实现从节点的只读。
这在 Kubernetes 中是很容易实现的,我们只需要在 ConfigMap 中定义两套配置,然后在 pod 描述中依据不同的 pod 序号选择挂载不同的配置即可。
下面是一个 ConfigMap 的示例:
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
labels:
app: mysql
data:
master.cnf: |
3.2 用 Service 实现主从的读写分离
接下来,我们创建两个 Service,来实现对主从的读写分离:
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
- name: mysql
port: 3306
clusterIP: None
selector:
app: mysql
---
apiVersion: v1
kind: Service
metadata:
name: mysql-read
labels:
app: mysql
spec:
ports:
- name: mysql
port: 3306
selector:
app: mysql由于第一个 Service 配置了 clusterIP: None,所以它是一个 Headless Service,也就是它会代理编号为 0 的节点,也就是主节点。
而第二个 Service,由于在 selector 中指定了 app: mysql,所以它会代理所有具有这个 label 的节点,也就是集群中的所有节点。
4. 集群初始化工作
有了上述两部分准备工作,我们就要开始着手构建我们的 MySQL 集群了,那么,最为首要的当然就是如何去初始化整个集群的各个节点了。
集群启动前,所需的初始化步骤有:
各个节点正确获取对应的 ConfigMap 中的配置文件,并且放置在 mysql 配置文件所在的路径。
如果节点是从节点,那么需要先将数据拷贝到对应路径下。
在从节点上执行数据初始化命令。
这些操作我们可以通过定义一系列 InitContainers 来实现。
4.1 正确获取节点对应的配置文件
对于 StatefulSet 而言,每个 pod 各自的 hostname 中所具有的序号就是它们的唯一 id,因此我们可以通过正则表达式来获取这个 id,并且规定 id 为 0 表示主节点,于是,通过判断 server 的 id,就可以对 ConfigMap 中不同的配置进行获取了:
...
4.2 在从节点中实现数据拷贝
按照上一小节中的例子,我们已经知道如何去判断当前节点是否是 Master 节点,于是,我们很容易实现只在 Slave 节点中并且数据不存在的情况下进行数据拷贝操作:
...
在这个示例中,我们使用了 ncat 命令实现从上一个已经启动的节点拷贝数据到当前节点,并且使用了第三方的备份还原工具 xtrabackup 来实现数据的恢复。
5. MySQL 容器的启动
5.1 从节点启动前的数据初始化与恢复
在 initContainers 中,我们实现了在从节点中,将上一个节点的备份数据拷贝到当前节点的工作,那么,接下来我们就要去恢复这个数据了。
与此同时,我们还需要在 mysql 的实际运行中实时执行数据的同步、恢复与备份工作。上文提到的 xtrabackup 很方便地实现了这一系列功能。我们可以将这个集成工具作为一个 sidecar 启动,完成上述这些操作:
...
5.2 MySQL 容器的启动
接下来,我们就要启动我们的 MySQL 容器了:
...
这里,我们使用了官方的 MySQL 5.7 版本镜像,并且挂载了在 initContainers 中已经完成数据与配置初始化的路径作为数据路径与配置路径。
同时我们配置了健康监测:
livenessProbe:存活探针,定时执行命令,来检测节点是否存活,如果检测失败,则自动重启节点;
readinessProbe:就绪探针,在启动后周期执行指令,只有当指令执行成功后,才允许 Service 将请求转发给节点。
6. StatefulSet 配置一览
有了上述所有的描述,我们已经完整构建出了一个支持横向扩展的 MySQL 主从集群的搭建,他的配置如下:
7. 集群运行与 SQL 执行
执行 kubectl create 命令我们就可以让这个集群运行起来了:
$ kubectl create -f mysql-statefulset.yaml
接下来我们就可以通过调用对应的 Service 来实现 SQL 的执行,例如我们希望在主节点上执行建表与插入操作:
kubectl run mysql-client --image=mysql:5.7 -i --rm --restart=Never --\
mysql -h mysql-0.mysql <<EOF
CREATE DATABASE test;
CREATE TABLE test.messages (message VARCHAR(250));
INSERT INTO test.messages VALUES ('hello');
EOF然后,通过 mysql-read 这个 service 来实现读操作:
$ kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
mysql -h mysql-read -e "SELECT * FROM test.messages"
Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello |
+---------+
《实战 Kubernetes StatefulSet — MySQL 主从集群搭建》来自互联网,仅为收藏学习,如侵权请联系删除。本文URL:http://www.bookhoes.com/4021.html