零、前情纪要

上一遍文章

一、知识储备

Pod: Kubernetes的基本调度单元称为“pod”。通过该种抽象类别可以把更级别的抽象内容增加到容器化组件。一个pod一般包含一个或多个容器,这样可以保证它们一直位于主机上,并且可以共享资源。Kubernetes中的每个pod都被分配一个唯一的(在集群内的)IP地址这样久可以运行应用程序使用同一端口,而避免了发生冲突的问题。Pod可以定义一个卷,例如本地磁盘目录或网格磁盘,并将其暴露在pod中的一个容器之中。pod可以通过Kubernetes API手动管理,也可以委托给控制器来实现自动管理。

标签和选择器:Kubernetes是客户端(用户或内部组件)将称为“标签”的键值对附加到系统中的任何API对象,如pod和节点。相应地,“标签选择器”是针对匹配对象的标签的查询方法。标签和选择器是Kubernetes中的主要分组机制,用户确定操作适用的组件。

控制器:控制器是通过管理一组pod来实现来将实际集群状态转移到所需集群状态的对账循环机制。目前kubernetes官网提到的控制器有“ReplicaSet”、“ReplicationController”、“Deployments”、“StatefulSets”、“DaemonSet”、“Garbage Collection”、“TTL Controller for Finished Resources”、“Jobs - Run to Completion”和“CronJjo”。

ReplicationController:ReplicationController确保在任何时候都有特定数量的pod副本处于运行状态。换句话说,ReplicationController确保一个pod或一组同类的pod总是可用的。

ReplicaSet: ReplicaSet是下一代的Replication Controller。ReplicaSet和Replication Controller的唯一区别是选择器的支持。ReplicaSet支持新的基于集合的选择器需求,这在标签用户指南中有描述。而Replication Controller 仅支持基于相等选择器的需求。虽然ReplicaSets可以独立使用,但今天它主要被Deployments用作协调Pod创建、删除和更新的机制。当您使用Deployment时,您不必担心还要管理它们创建的ReplicaSet。Deployment会拥有并管理它们的ReplicaSet。

Deployments:一个Deployment控制器为Pods和ReplicaSets提供描述性的更新方式。描述Deployment中的desired state,并且Deployment控制器以受控速率更改实际状态,以达到期望状态。可以定义Deployments以创建新的ReplicaSets,或删除现有Deployments,并通过新的Deployments使用其所有资源。

StatefulSets:本文需要重点理解的概念,StatefulSet是用来管理有状态应用的工作负载API对象。StatefulSet用来管理Deployment和扩展一组Pod,并且能为这些Pod提供序号和唯一性保证。和Deployment相同的是,StatefulSet管理了基于相同容器定义的一组Pod。但和deployment不同的是,StatefulSet为它们的每个Pod维护了一个固定的ID。这些Pod是基于相同的声明来创建的,但是不能相互替换:无论怎么调度,每个Pod都有一个永久不变的ID。StatefulSet和其他控制器使用相同的工作模式。你在StatefulSet对象中定义你期待的状态,然后StatefulSet的控制器久会通过这种更新来达到那种你想要的状态。

有状态应用:实例之间存在不对等关系,比如:主从关系、主备关系的,以及实例对外部数据有依赖关系的应用,就被称为“有状态应用”。

Volumes:容器中的文件在磁盘上是临时存放的,这给容器中运行的特殊应用程序带来一些问题。首先,当容器崩溃时,kubelet将重新启动容器,容器中的文件将会丢失--因为容器会以干净的状态重建。其次,当在一个Pod中同时运行多个容器时,常常需要在这些容器之间共享文件。Kubernetes抽象出Volume对象来解决这两个问题。

PersistentVolume (PV):PV时集群中的一块存储,由管理员提供或者使用存储类动态提供。它是集群中的资源,就像节点是集群资源一样。PV是类型Volumes的卷的插件,但是其生命周期与使用PV的任何单个Pod无关。

A PersistentVolumeClaim (PVC):PVC是用户存储请求。它类似于豆荚。容器消耗节点资源,PVC消耗PV资源。Pod可以请求特定大小的CPU和内存的资源。PVC可以请求特定的大小和访问模式(例如,一次读写或多次读写)的磁盘资源。

ConfigMap:ConfigMap是一种API对象,用来将非机密性的数据保存到健值对中。使用时可以用作环境变量、命令行参数或者存储卷中的配置文件。ConfigMap将您的环境配置和容器镜像解耦,便于应用配置的修改。当您需要存储机密信息时可以使用Secret对象。

Services:Kubernetes服务本质是一组协同工作的pod,类同多层架构应用中的一层。构成服务的pod组通过标签选择器来定义。Kubernetes通过给服务分配静态IP地址和域名来提供服务发现机制,并且以轮询调度的方式将流量负载均衡到能于选择器匹配的pod的IP地址的网络连接上。

Headless Services:有时不需要或不想要负载均衡,以及单独的 Service IP。遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为“None”来创建 Headless Service。可以用 headless Service 与其他服务发现机制进行接口,而不需要与Kubernetes的实现捆绑在一起。对于 headless Service 并不会分配 Cluster IP,kube-proxy不会处理它们,而且平台也不会为它们进行负载均衡和路由。DNS如何实现自动配置,依赖于 Service 是否定义了selector

ps:概念比较多,建议大概浏览一次,下面浏览搭建过程的时候,遇到再回顾加深记忆即可。

二、MySQL集群搭建

2.1、集群要求搭建一个一主 N 从的 MySQL 集群;

从节点可以水平扩展;

所有的写操作,都只能在主节点(Master)上执行;

所有的读操作可以在所有节点上执行;

2.2、物理机环境搭建集群

下面,我们看一张MySQL主从复制的原理图:

上面的原理图告诉我们,部署这样一个一主多从的 MySQL 集群,关键在于配置 Master 节点和 Slave 节点的复制和同步。

下面我们用自然语言描述在物理机上部署这样的集群的关键步骤;1、配置并安装好 Master 节点的 MySQL;

2、通过XtraBackup将Master节点的数据备份到指定目录;

3、将第2步备份出来的目录,连同备份信息文件,拷贝到Slave的/var/lib/mysql下,然后执行CHANGE MASTER TO指令;

4、启动 Slave 节点,执行START SLAVE指令;

5、在这个集群中添加更多的Slave节点;

2.3 Kubernetes 上搭建一主多从的 MySQL 集群

2.3.1 难点

不同于物理机,在 Kubernetes 上搭建一主多从的 MySQL 集群,结合容器的技术特色,我们思考以下需要解决的困难点:Master 节点和 Slave 节点需要有不同的配置文件;

Master 节点和 Slave 节点需要能够传输备份信息文件;

第一次启动 Slave 节点,需要执行一些初始化SQL操作;

2.3.2 使用NFS做持久化存储

2.3.2.1 搭建NFS服务器

我们这里为了演示方便,决定使用相对简单的 NFS 这种存储资源。

nfs 卷能将NFS(网络文件系统)挂载到您的Pod中。不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,Volume 只是会被卸载掉。这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间“传递”

下面我们在 sz-fengyin-3 服务器上搭建NFS服务:

### 关闭防火墙

[root@sz-fengyin-3 ~]# systemctl stop firewalld.service

[root@sz-fengyin-3 ~]# systemctl disable firewalld.service

### 安装

[root@sz-fengyin-3 ~]# yum -y install nfs-utils rpcbind

### 启动 rpcbind

[root@sz-fengyin-3 ~]# systemctl start rpcbind

[root@sz-fengyin-3 ~]# systemctl enable rpcbind

systemctl start nfs-server

[root@sz-fengyin-3 ~]# systemctl enable nfs-server

[root@sz-fengyin-3 ~]# mkdir -p /data0/dbData/nfs/k8s/mysql

### 配置说明:

### /data0/dbData/nfs/k8s/mysql:是共享的数据目录

### 172.18.144.0/24:表示任何人都有权限连接,当然也可以是一个网段,一个 IP,也可以是域名

### rw:读写的权限

### sync:表示文件同时写入硬盘和内存

### no_root_squash:当登录 NFS 主机使用共享目录的使用者是 root 时,其权限将被转换成为匿名使用者,通常它的 UID 与 GID,都会变成 nobody 身份

[root@sz-fengyin-3 ~]# more /etc/exports

/data0/dbData/nfs/k8s/mysql 172.18.144.0/24(rw,sync,no_root_squash)

### 启动 nfs 服务

[root@sz-fengyin-3 ~]# systemctl restart nfs.service

[root@sz-fengyin-3 ~]# systemctl enable nfs

[root@sz-fengyin-3 ~]# systemctl status nfs

● nfs-server.service - NFS server and services

Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; enabled; vendor preset: disabled)

Drop-In: /run/systemd/generator/nfs-server.service.d

└─order-with-mounts.conf

Active: active (exited) since 二 2020-06-09 09:35:55 CST; 4h 9min ago

Process: 20003 ExecStopPost=/usr/sbin/exportfs -f (code=exited, status=0/SUCCESS)

Process: 20002 ExecStopPost=/usr/sbin/exportfs -au (code=exited, status=0/SUCCESS)

Process: 19999 ExecStop=/usr/sbin/rpc.nfsd 0 (code=exited, status=0/SUCCESS)

Process: 20033 ExecStartPost=/bin/sh -c if systemctl -q is-active gssproxy; then systemctl reload gssproxy ; fi (code=exited, status=0/SUCCESS)

Process: 20016 ExecStart=/usr/sbin/rpc.nfsd $RPCNFSDARGS (code=exited, status=0/SUCCESS)

Process: 20014 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS)

Main PID: 20016 (code=exited, status=0/SUCCESS)

Tasks: 0

Memory: 0B

CGroup: /system.slice/nfs-server.service

6月 09 09:35:55 sz-fengyin-3 systemd[1]: Starting NFS server and services...

6月 09 09:35:55 sz-fengyin-3 systemd[1]: Started NFS server and services.

### 确认 NFS Server 启动成功

[root@sz-fengyin-3 ~]# rpcinfo -p|grep nfs

100003 3 tcp 2049 nfs

100003 4 tcp 2049 nfs

100227 3 tcp 2049 nfs_acl

100003 3 udp 2049 nfs

100003 4 udp 2049 nfs

100227 3 udp 2049 nfs_acl

### 查看具体目录挂载权限

[root@sz-fengyin-3 ~]# showmount -e 172.18.144.45

Export list for 172.18.144.45:

/data0/dbData/nfs/k8s/mysql 172.18.144.0/24

接下来我们在 sz-fengyin-[1:3] 服务器上安装nfs的客户端,由此验证nfs服务器,主要三个服务器都需要配置免密登陆。下面是在 sz-fengyin-1 上的安装步骤

### 关闭防火墙

[root@sz-fengyin-1 ~]# systemctl stop firewalld.service

[root@sz-fengyin-1 ~]# systemctl disable firewalld.service

### 安装

[root@sz-fengyin-1 ~]# yum -y install nfs-utils rpcbind

### 启动rpc

[root@sz-fengyin-1 ~]# systemctl start rpcbind.service

[root@sz-fengyin-1 ~]# systemctl enable rpcbind.service

### 启动 nfs 服务

[root@sz-fengyin-1 ~]# systemctl start nfs.service

[root@sz-fengyin-1 ~]# systemctl enable nfs.service

### 磁盘挂载

[root@sz-fengyin-1 ~]# mkdir -p /data0/dbData/k8s/mysql

[root@sz-fengyin-1 ~]# mount -t nfs 172.18.144.45:/data0/dbData/nfs/k8s/mysql /data0/dbData/k8s/mysql

### 再挂载磁盘上创建文件来验证 nfs 挂载是否成功

[root@sz-fengyin-1 ~]# cd /data0/dbData/k8s/mysql

[root@sz-fengyin-1 mysql]# touch test.txt

[root@sz-fengyin-1 mysql]# ls

test.txt

[root@sz-fengyin-3 ~]# cd /data0/dbData/nfs/k8s/mysql

[root@sz-fengyin-3 mysql]# ls

test.txt

2.3.2.2 NFS动态提供Kubernetes存储卷

因为NFS没有内部分配器,但是可以使用第三方存储供应商提供的外部分配器。下面我们使用

nfs-client Provisioner 是一个自动预配置程序,它使用您现有的已经配置的NFS服务器来支持通过PVC动态配置 Kubernetes PV。PV以 $ {namespace}-$ {pvcName}-$ {pvName} 的命令格式提供在NFS服务器上。

具体的实验配置步骤如下:配置nfs-client Provisioner 的Deployment

下面配置主要修改了环境变量的 NFS_SERVER 和 NFS_PATH 和 nfs配置。

[root@sz-fengyin-1 nfs-client-provisioner]# more nfs-deployment.yamlkind:DeploymentapiVersion:apps/v1metadata:name:nfs-client-provisionerspec:replicas:1selector:matchLabels:app:nfs-client-provisionerstrategy:type:Recreatetemplate:metadata:labels:app:nfs-client-provisionerspec:serviceAccountName:nfs-client-provisionercontainers:- name:nfs-client-provisionerimage:quay.io/external_storage/nfs-client-provisioner:latestvolumeMounts:- name:nfs-client-rootmountPath:/persistentvolumesenv:- name:PROVISIONER_NAMEvalue:fuseim.pri/ifs- name:NFS_SERVERvalue:172.18.144.45- name:NFS_PATHvalue:/data0/dbData/nfs/k8s/mysqlvolumes:- name:nfs-client-rootnfs:server:172.18.144.45path:/data0/dbData/nfs/k8s/mysql配置 serviceAccount

看到上面配置我们这里需要一个名为 nfs-client-provisioner 的 serviceAccount,下面是 serviceAccount 的配置文件

[root@sz-fengyin-1 nfs-client-provisioner]# more nfs-deployment-service-accout.yamlapiVersion:v1kind:ServiceAccountmetadata:name:nfs-client-provisioner---kind:ClusterRoleapiVersion:rbac.authorization.k8s.io/v1metadata:name:nfs-client-provisioner-runnerrules:- apiGroups:[""]resources:["persistentvolumes"]verbs:["get","list","watch","create","delete"]- apiGroups:[""]resources:["persistentvolumeclaims"]verbs:["get","list","watch","update"]- apiGroups:["storage.k8s.io"]resources:["storageclasses"]verbs:["get","list","watch"]- apiGroups:[""]resources:["events"]verbs:["list","watch","create","update","patch"]- apiGroups:[""]resources:["endpoints"]verbs:["create","delete","get","list","watch","patch","update"]---kind:ClusterRoleBindingapiVersion:rbac.authorization.k8s.io/v1metadata:name:run-nfs-client-provisionersubjects:- kind:ServiceAccountname:nfs-client-provisionernamespace:defaultroleRef:kind:ClusterRolename:nfs-client-provisioner-runnerapiGroup:rbac.authorization.k8s.io创建 StorageClass 对象

nfs-client 的 Deployment 声明完成后,我们就可以来创建一个StorageClass对象了。

[root@sz-fengyin-1 nfs-client-provisioner]# more nfs-deployment-storage-class.yamlapiVersion:storage.k8s.io/v1kind:StorageClassmetadata:name:course-nfs-storageprovisioner:fuseim.pri/ifs创建并查看资源对象

[root@sz-fengyin-1 nfs-client-provisioner]# kubectl create -f nfs-deployment.yaml

deployment.apps/nfs-client-provisioner created

[root@sz-fengyin-1 nfs-client-provisioner]# kubectl create -f nfs-deployment-service-accout.yaml

serviceaccount/nfs-client-provisioner created

clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created

clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created

[root@sz-fengyin-1 nfs-client-provisioner]# kubectl create -f nfs-deployment-storage-class.yaml

storageclass.storage.k8s.io/course-nfs-storage created

[root@sz-fengyin-1 nfs-client-provisioner]# kubectl get pods

NAME READY STATUS RESTARTS AGE

nfs-client-provisioner-dc5c55797-hnck6 1/1 Running 0 12s

nginx-deployment-5cc7f59bbc-w9mrj 0/1 Pending 0 18d

[root@sz-fengyin-1 nfs-client-provisioner]# kubectl get storageclass

NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE

course-nfs-storage fuseim.pri/ifs Delete Immediate false 48s创建PVC对象

上面把StorageClass资源对象创建成功了,接下来我们来通过一个示例测试下动态 PV,这里需要创建一个 PVC 对象。

### 配置

### accessModes:支持三种类型

### ReadWriteMany 多路读写,卷能被集群多个节点挂载并读写

### ReadWriteOnce 单路读写,卷只能被单一集群节点挂载读写

### ReadOnlyMany 多路只读,卷能被多个集群节点挂载且只能读

###

####persistentVolumeReclaimPolicy:也有三种策略,这个策略是当与之关联的PVC被删除以后,这个PV中的数据如何被处理

### Retain 当删除与之绑定的PVC时候,这个PV被标记为released(PVC与PV解绑但还没有执行回收策略)且之前的数据依然保存在该PV上,但是该PV不可用,需要手动来处理这些数据并删除该PV。

### Delete 当删除与之绑定的PVC时候

### Recycle 这个在1.14版本中以及被废弃,取而代之的是推荐使用动态存储供给策略,它的功能是当删除与该PV关联的PVC时,自动删除该PV中的所有数据

[root@sz-fengyin-1 nfs-client-provisioner]# more test-pvc.yaml

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

name: test-pvc

annotations:

volume.beta.kubernetes.io/storage-class: "course-nfs-storage"

spec:

accessModes:

- ReadWriteMany

resources:

requests:

storage: 1Mi

### 创建pvc对象

[root@sz-fengyin-1 nfs-client-provisioner]# kubectl create -f test-pvc.yaml

persistentvolumeclaim/test-pvc created

### 查看绑定情况

[root@sz-fengyin-1 nfs-client-provisioner]# kubectl get pvc

NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE

test-pvc Bound pvc-4f173f15-5601-4f03-92f2-cc4a7dc71025 1Mi RWX course-nfs-storage 5s

### nfs服务器上对应的变化

[root@sz-fengyin-3 ~]# cd /data0/dbData/nfs/k8s/mysql

[root@sz-fengyin-3 mysql]# ll

总用量 4

drwxrwxrwx 2 root root 4096 6月 10 07:51 default-test-pvc-pvc-4f173f15-5601-4f03-92f2-cc4a7dc71025

查看Kubenetes管理页面:

2.3.3 ConfigMap 为 Master/Slave 节点分配不同的配置文件

根据开头介绍的ConfigMap概念可知,使用 ConfigMap 可以很好解决 MySQL集群中 Master 和 Slave 节点配置文件不一致的问题。ConfigMap 的配置信息如下:

[root@sz-fengyin-1 mysql]# more mysql-configmap.yamlapiVersion:v1kind:ConfigMapmetadata:name:mysqllabels:app:mysqldata:master.cnf:|# 主节点MySQL的配置文件[mysqld]log-binslave.cnf: |# 从节点MySQL的配置文件[mysqld]super-read-only

这里我们定义了 master.cnf 和 slave.cnf 两个MySQL的配置文件。master.cnf 开启了 log-bin,即: 使用二进制日志文件的方式进行主从复制。

slave.cnf 开启了 super-read-only,表示从节点拒绝除了主节点的数据同步操作之外的所有写操作,即它对用户是只读的。

创建ConfigMap

[root@sz-fengyin-1 mysql]# kubectl create -f mysql-configmap.yaml

configmap/mysql created

2.3.4 Secret 为 集群配置密码

[root@sz-fengyin-1 mysql]# more mysql-secret.yaml

apiVersion: v1

kind: Secret

metadata:

name: mysql-secret

labels:

app: mysql

type: Opaque

data:

password: ZmVuZ3lpbg== # echo -n "fengyin" | base64

创建secret:

[root@sz-fengyin-1 mysql]# kubectl create -f mysql-secret.yaml

secret/mysql-secret created

2.3.5 Service 为 StatefulSet和用户提供服务发现

这里我们需要创建两个service,两个Service的配置如下:

[root@sz-fengyin-1 mysql]# more mysql-service.yamlapiVersion:v1kind:Servicemetadata:name:mysqllabels:app:mysqlspec:ports:- name:mysqlport:3306clusterIP:Noneselector:app:mysql---apiVersion:v1kind:Servicemetadata:name:mysql-readlabels:app:mysqlspec:ports:- name:mysqlport:3306selector:app:mysql

配置介绍:两个Service 都代理了所有携带 app=mysql 标签的 Pod,也就是所有的 MySQL Pod。端口映射都是用 Service 的3306端口对应 Pod 的 3306 端口

一个名叫 “mysql” Service 是 Headless Service (即:clusterIP=None)。它的作用是通过为 Pod 分配 DNS 记录来固定 Pod 集群,比如 “mysql-0.mysql” 和 “mysql-1.mysql” 这样的 DNS 名字。其中, “mysql-0.mysql” 的节点就是我们的主节点;名叫 “mysql-read” 的 Service 是一个常规的 Service

规定,所有用户的读请求必须访问名叫 “mysql-read” 的Service被分配的DNS记录上,这样,读请求就可以转发到任意一个 MySQL 的主节点或者从节点上;所有用户的写请求,则必须访问到 MySQL 的主节点,即 “mysql-0.mysql” 这条 DNS 记录

创建 Services:

[root@sz-fengyin-1 mysql]# kubectl apply -f mysql-service.yaml

service/mysql created

service/mysql-read created

2.3.6 使用 StatefulSet 搭建 MySQL 集群

[root@sz-fengyin-1 mysql]# more mysql-statefulset.yamlapiVersion:apps/v1kind:StatefulSetmetadata:name:mysqllabels:app:mysqlspec:selector:matchLabels:app:mysqlserviceName:mysqlreplicas:2template:metadata:labels:app:mysqlspec:initContainers:- image:busyboxname:mysql-volume-cleanerargs:[/bin/sh, -c, 'rm -rf /var/lib/mysql/* || true']volumeMounts:- mountPath:/var/lib/mysqlname:datasubPath:mysql- name:init-mysqlimage:mysql:5.7env:- name:MYSQL_ROOT_PASSWORDvalueFrom:secretKeyRef:name:mysql-secretkey:passwordcommand:- bash- "-c"- |set -ex# 从 Pod 的序号,生成 server-id[[ $(hostname) =~ -([0-9]+)$ ]] || exit 1ordinal=${BASH_REMATCH[1]}echo [mysqld] > /mnt/conf.d/server-id.cnf# 由于 server-id 不能为 0,因此给 ID 加 100 来避开它echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf# 如果 Pod 的序号为 0,说明它是 Master 节点,从 ConfigMap 里把 Master 的配置文件拷贝到 /mnt/conf.d 目录下# 否则,拷贝 ConfigMap 里的 Slave 的配置文件if [[ ${ordinal} -eq 0 ]]; thencp /mnt/config-map/master.cnf /mnt/conf.delsecp /mnt/config-map/slave.cnf /mnt/conf.dfivolumeMounts:- name: confmountPath: /mnt/conf.d- name: config-mapmountPath: /mnt/config-map- name: clone-mysqlimage: gcr.io/google-samples/xtrabackup:1.0imagePullPolicy: IfNotPresentenv:- name: MYSQL_ROOT_PASSWORDvalueFrom:secretKeyRef:name: mysql-secretkey: passwordcommand:- bash- "-c"- |set -ex# 拷贝操作只需要在第一次启动时进行,所以数据已经存在则跳过[[ -d /var/lib/mysql/mysql ]] && exit 0# Master 节点(序号为 0)不需要这个操作[[ $(hostname) =~ -([0-9]+)$ ]] || exit 1ordinal=${BASH_REMATCH[1]}[[ $ordinal == 0 ]] && exit 0# 使用 ncat 指令,远程地从前一个节点拷贝数据到本地ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql# 执行 --prepare,这样拷贝来的数据就可以用作恢复了xtrabackup --prepare --target-dir=/var/lib/mysqlvolumeMounts:- name: datamountPath: /var/lib/mysqlsubPath: mysql- name: confmountPath: /etc/mysql/conf.dcontainers:- name: mysqlimage: mysql:5.7env:- name: MYSQL_ALLOW_EMPTY_PASSWORDvalue: "1"- name: MYSQL_ROOT_PASSWORDvalueFrom:secretKeyRef:name: mysql-secretkey: passwordports:- name: mysqlcontainerPort: 3306volumeMounts:- name: datamountPath: /var/lib/mysqlsubPath: mysql- name: confmountPath: /etc/mysql/conf.dresources:requests:cpu: 500mmemory: 1GilivenessProbe:# exec:# command: ["mysqladmin", "ping", "-h127.0.0.1", "-uroot", "-p${MYSQL_ROOT_PASSWORD}"]tcpSocket:port: 3306initialDelaySeconds: 30periodSeconds: 10timeoutSeconds: 5readinessProbe:# exec:# 通过TCP连接的方式进行健康检查# command: ["mysql", "-h127.0.0.1", "-uroot", "-p${MYSQL_ROOT_PASSWORD}", "-e" "SELECT 1"]tcpSocket:port: 3306initialDelaySeconds: 5periodSeconds: 2timeoutSeconds: 1- name: xtrabackupimage: gcr.io/google-samples/xtrabackup:1.0imagePullPolicy: IfNotPresentports:- name: xtrabackupcontainerPort: 3307env:- name: MYSQL_ROOT_PASSWORDvalueFrom:secretKeyRef:name: mysql-secretkey: passwordcommand:- bash- "-c"- |set -excd /var/lib/mysql# 从备份信息文件里读取 MASTER_LOG_FILE 和 MASTER_LOG_POS 这 2 个字段的值,用来拼装集群初始化 SQLif [[ -f xtrabackup_slave_info ]]; then# 如果 xtrabackup_slave_info 文件存在,说明这个备份数据来自于另一个 Slave 节点# 这种情况下,XtraBackup 工具在备份的时候,就已经在这个文件里自动生成了 "CHANGE MASTER TO" SQL 语句# 所以,只需要把这个文件重命名为 change_master_to.sql.in,后面直接使用即可mv xtrabackup_slave_info change_master_to.sql.in# 所以,也就用不着 xtrabackup_binlog_info 了rm -f xtrabackup_binlog_infoelif [[ -f xtrabackup_binlog_info ]]; then# 如果只是存在 xtrabackup_binlog_info 文件,说明备份来自于 Master 节点,就需要解析这个备份信息文件,读取所需的两个字段的值[[ $(cat xtrabackup_binlog_info) =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1rm xtrabackup_binlog_info# 把两个字段的值拼装成 SQL,写入 change_master_to.sql.in 文件echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.infi# 如果存在 change_master_to.sql.in,就意味着需要做集群初始化工作if [[ -f change_master_to.sql.in ]]; then# 但一定要先等 MySQL 容器启动之后才能进行下一步连接 MySQL 的操作echo "Waiting for mysqld to be ready(accepting connections)"until mysql -h 127.0.0.1 -uroot -p${MYSQL_ROOT_PASSWORD} -e "SELECT 1"; do sleep 1; doneecho "Initializing replication from clone position"# 将文件 change_master_to.sql.in 改个名字# 防止这个 Container 重启的时候,因为又找到了 change_master_to.sql.in,从而重复执行一遍初始化流程mv change_master_to.sql.in change_master_to.sql.orig# 使用 change_master_to.sql.orig 的内容,也就是前面拼装的 SQL,组成一个完整的初始化和启动 Slave 的 SQL 语句mysql -h 127.0.0.1 -uroot -p${MYSQL_ROOT_PASSWORD} << EOF$(< change_master_to.sql.orig),MASTER_HOST='mysql-0.mysql',MASTER_USER='root',MASTER_PASSWORD='${MYSQL_ROOT_PASSWORD}',MASTER_CONNECT_RETRY=10;START SLAVE;EOFfi# 使用 ncat 监听 3307 端口。# 它的作用是,在收到传输请求的时候,直接执行 xtrabackup --backup 命令,备份 MySQL 的数据并发送给请求者exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \"xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root --password=${MYSQL_ROOT_PASSWORD}"volumeMounts:- name: datamountPath: /var/lib/mysqlsubPath: mysql- name: confmountPath: /etc/mysql/conf.dvolumes:- name: confemptyDir: {}- name: config-mapconfigMap:name: mysqlvolumeClaimTemplates:- metadata:name: dataspec:accessModes:- "ReadWriteOnce"storageClassName: course-nfs-storageresources:requests:storage: 3Gi

创建StatefulSet

[root@sz-fengyin-1 mysql]# kubectl apply -f mysql-statefulset.yaml

如出现以下报错:

2019-08-25T15:36:470279Z 0 [ERROR] --initialize specified but the data directory has files in it. Aborting

2019-08-09T15:36:470367Z 0 [ERROR] Aborting

需要添加一个 initContainers 做初始化

- image:busyboxname:mysql-volume-cleanerargs:[/bin/sh, -c, 'rm -rf /var/lib/mysql/* || true']volumeMounts:- mountPath:/var/lib/mysqlname:datasubPath:mysql

2.3.7 验证查看容器状态

[root@sz-fengyin-1 mysql]# kubectl get pod -A -o wide

NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES

default mysql-0 2/2 Running 0 116m 10.244.1.161 sz-fengyin-2

default mysql-1 2/2 Running 1 115m 10.244.2.110 sz-fengyin-3 查看主从配置

[root@sz-fengyin-1 mysql]# kubectl -n default exec mysql-1 -c mysql -- bash -c "mysql -uroot -pfengyin -e 'show slave status \G'"

mysql: [Warning] Using a password on the command line interface can be insecure.

*************************** 1. row ***************************

Slave_IO_State: Waiting for master to send event

Master_Host: mysql-0.mysql

Master_User: root

Master_Port: 3306

Connect_Retry: 10

Master_Log_File: mysql-0-bin.000003

Read_Master_Log_Pos: 154

Relay_Log_File: mysql-1-relay-bin.000002

Relay_Log_Pos: 322

Relay_Master_Log_File: mysql-0-bin.000003

Slave_IO_Running: Yes

Slave_SQL_Running: Yes

Replicate_Do_DB:

Replicate_Ignore_DB:

Replicate_Do_Table:

Replicate_Ignore_Table:

Replicate_Wild_Do_Table:

Replicate_Wild_Ignore_Table:

Last_Errno: 0

Last_Error:

Skip_Counter: 0

Exec_Master_Log_Pos: 154

Relay_Log_Space: 531

Until_Condition: None

Until_Log_File:

Until_Log_Pos: 0

Master_SSL_Allowed: No

Master_SSL_CA_File:

Master_SSL_CA_Path:

Master_SSL_Cert:

Master_SSL_Cipher:

Master_SSL_Key:

Seconds_Behind_Master: 0

Master_SSL_Verify_Server_Cert: No

Last_IO_Errno: 0

Last_IO_Error:

Last_SQL_Errno: 0

Last_SQL_Error:

Replicate_Ignore_Server_Ids:

Master_Server_Id: 100

Master_UUID: 44ff2fa6-bf89-11ea-b7b2-96040b41e265

Master_Info_File: /var/lib/mysql/master.info

SQL_Delay: 0

SQL_Remaining_Delay: NULL

Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates

Master_Retry_Count: 86400

Master_Bind:

Last_IO_Error_Timestamp:

Last_SQL_Error_Timestamp:

Master_SSL_Crl:

Master_SSL_Crlpath:

Retrieved_Gtid_Set:

Executed_Gtid_Set:

Auto_Position: 0

Replicate_Rewrite_DB:

Channel_Name:

Master_TLS_Version:验证主从数据是否同步

### 现在主库创建测试的数据库、表和数据

[root@sz-fengyin-1 mysql]# kubectl exec -it mysql-0 -c mysql -n default -- /bin/bash

root@mysql-0:/# mysql -uroot -p

Enter password:

Welcome to the MySQL monitor. Commands end with ; or \g.

Your MySQL connection id is 4208

Server version: 5.7.30-log MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;

+------------------------+

| Database |

+------------------------+

| information_schema |

| mysql |

| performance_schema |

| sys |

| xtrabackup_backupfiles |

+------------------------+

5 rows in set (0.01 sec)

mysql> create database test

-> ;

Query OK, 1 row affected (0.00 sec)

mysql> use test

Database changed

mysql> create table counter(id int);

Query OK, 0 rows affected (0.03 sec)

mysql> insert into counter values(123);

Query OK, 1 row affected (0.02 sec)

mysql> exit

Bye

root@mysql-0:/# exit

exit

### 去从库查看数据是否存在

[root@sz-fengyin-1 mysql]# kubectl exec -it mysql-1 -c mysql -n default -- /bin/bash

root@mysql-1:/# mysql -uroot -p

Enter password:

Welcome to the MySQL monitor. Commands end with ; or \g.

Your MySQL connection id is 4228

Server version: 5.7.30 MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;

+------------------------+

| Database |

+------------------------+

| information_schema |

| mysql |

| performance_schema |

| sys |

| test |

| xtrabackup_backupfiles |

+------------------------+

6 rows in set (0.01 sec)

mysql> use test

Reading table information for completion of table and column names

You can turn off this feature to get a quicker startup with -A

Database changed

mysql> select * from counter;

+------+

| id |

+------+

| 123 |

+------+

1 row in set (0.00 sec)

2.3.8 扩展 MySQL 集群

在有了 StatefulSet 以后,你就可以像 Deployment 那样,非常方便地扩展这个 MySQL 集群

### 扩展节点命令

[root@sz-fengyin-1 mysql]# kubectl scale statefulset mysql --replicas=3

statefulset.apps/mysql scaled

### 查看容器状态

[root@sz-fengyin-1 mysql]# kubectl get pod -A -o wide

NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES

default mysql-0 2/2 Running 0 121m 10.244.1.161 sz-fengyin-2

default mysql-1 2/2 Running 1 121m 10.244.2.110 sz-fengyin-3

default mysql-2 2/2 Running 1 50s 10.244.1.162 sz-fengyin-2

### 查看新扩展的节点是否主动同步数据

[root@sz-fengyin-1 mysql]# kubectl exec -it mysql-2 -c mysql -n default -- /bin/bash

root@mysql-2:/# mysql -uroot -p

Enter password:

Welcome to the MySQL monitor. Commands end with ; or \g.

Your MySQL connection id is 48

Server version: 5.7.30 MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its

affiliates. Other names may be trademarks of their respective

owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;

+------------------------+

| Database |

+------------------------+

| information_schema |

| mysql |

| performance_schema |

| sys |

| test |

| xtrabackup_backupfiles |

+------------------------+

6 rows in set (0.00 sec)

mysql> use test

Reading table information for completion of table and column names

You can turn off this feature to get a quicker startup with -A

Database changed

mysql> select * from counter;

+------+

| id |

+------+

| 123 |

+------+

1 row in set (0.00 sec)

mysql>

2.3.9查看版本信息

[root@sz-fengyin-1 ~]# kubectl get controllerrevision

NAME CONTROLLER REVISION AGE

mysql-646f855877 statefulset.apps/mysql 1 10h

web-6bc849cb6b statefulset.apps/web 1 45d

[root@sz-fengyin-1 ~]# kubectl describe controllerrevision mysql-646f855877

2.3.10 删除

[root@sz-fengyin-1 mysql]# kubectl delete -f mysql-statefulset.yaml

statefulset.apps "mysql" deleted

三、 待解决问题

使用如下配置会出现 Access denied for user 'root'@'localhost' (using password: YES) 问题,还没有找到解决方案,先用TCP探测替代:

livenessProbe:exec:command:["mysqladmin","ping","-h127.0.0.1","-uroot","-p${MYSQL_ROOT_PASSWORD}"]readinessProbe:exec:# 通过TCP连接的方式进行健康检查command:["mysql","-h127.0.0.1","-uroot","-p${MYSQL_ROOT_PASSWORD}","-e""SELECT 1"]

[root@sz-fengyin-1 mysql]# kubectl logs -f mysql-0 -c mysql

2020-06-30T05:08:53.460538Z 12318 [Note] Access denied for user 'root'@'localhost' (using password: YES)

2020-06-30T05:08:55.466126Z 12319 [Note] Access denied for user 'root'@'localhost' (using password: YES)

2020-06-30T05:08:57.467856Z 12320 [Note] Access denied for user 'root'@'localhost' (using password: YES)

2020-06-30T05:08:58.203240Z 12321 [Note] Access denied for user 'root'@'localhost' (using password: YES)

2020-06-30T05:08:59.464400Z 12322 [Note] Access denied for user 'root'@'localhost' (using password: YES)

2020-06-30T05:09:01.462945Z 12323 [Note] Access denied for user 'root'@'localhost' (using password: YES)

2020-06-30T05:09:03.465287Z 12324 [Note] Access denied for user 'root'@'localhost' (using password: YES)

2020-06-30T05:09:05.463037Z 12325 [Note] Access denied for user 'root'@'localhost' (using password: YES)

2020-06-30T05:09:07.494706Z 12326 [Note] Access denied for user 'root'@'localhost' (using password: YES)

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐