文章目录
- 一、前言
- 二、实际操作
- 步骤1:编写namespace脚本
- 步骤2:编写configmap脚本
- 步骤3:编写secret脚本(用来存放mysql密码)
- 步骤4:编写initContainer脚本
- 步骤5:编写StorageClass相关脚本
- 1)权限设置:编写ServiceAccount、ClusterRole、ClusterRoleBinding、Role、RoleBinding脚本管理NFS
- 2)每个机器上安装NFS服务,存放目录的机器上新建好 /nfs/data 目录
- 3)编写StorageClass脚本
- 4)编写nfs-provisioner的Deployment脚本
- 步骤6、编写Service脚本
- 步骤7、编写StatefulSet脚本
- 步骤8:测试主从同步
- 三、尾声
一、前言
二、实际操作
问题(六个)
- 启动顺序有要求,master节点必须比slave节点先启动 (statefulset)
- 节点挂掉了,新的pod启动必须使用原先pod的资源(持久化保证数据不丢失)
- master与slave的配置不一样
- master启动之后需要设置主从授权账户,slave需要执行change master命令,以及加入主从的命令
- 希望客户账户名密码自己配置
- slave需要知道master节点的地址
解决方案(六个,按顺序对应问题)
- statefulset:使用statefulSet可以使得pod副本按照编号顺序进行启动,只需要把pod-0作为master就可以了
- 持久化保证数据不丢失:使用pv和pvc解决,通过pvc与pod的标签进行绑定,一个pod对应一个pvc就可以保证重启后的pod依旧使用原先的资源
- 初始化所需的配置信息:使用configmap可以在容器初始化的时候指定需要的配置信息,
- 初始化执行的脚步:使用initContainer可以在容器初始化的时候执行需要的脚本
- 密码存放:使用secret可以将密码保密
- 集群内访问直接podName.serviceName:使用headless service+dns可以让slave节点通过hostname访问master,hostname固定为podName.ServiceName,如:serviceName为mysql,则master的hostname为mysql-0.mysql
八个文件,按顺序来,每个都有分步测试
部署思路
- 编写namespace脚本,创建专门的namespace
- 编写configmap,将mysql的配置文件配置到里面
- 编写secret脚本,将需要的密码配置在里面
- 编写initContainer脚本(备用),根据hostname判断是master还是slave,进而执行对应的命令
- 编写pv和pvc脚本,申请磁盘资源(通过storageClass自动进行pv/pvc的创建)
- 编写headless service脚本,配置mysql之间的网络关系
- 编写StatefulSet脚本,初始化容器
本文所有的yaml文件:https://www.syjshare.com/res/4C7C2KKF
步骤1:编写namespace脚本
01-mysql-namespace.yaml
apiVersion: v1
#创建Namespace类型资源
kind: Namespace
metadata:
#资源名称
name: mysql
#标签为app:mysql
labels:
app: mysql
测试:
#执行命令
kubectl apply -f 01-mysql-namespace.yaml
#查看命名空间
kubectl get ns
步骤2:编写configmap脚本
02-mysql-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
namespace: mysql
labels:
app: mysql
data:
#这里定义了多个数据信息
master.cnf: |
# Master配置
[mysqld]
datadir=/var/lib/mysql
pid-file=/var/run/mysqld/mysqld.pid
socket=/var/run/mysql/mysql.sock
log-error=/var/log/mysql/error.log
log-bin=mysqllog
skip-name-resolve
lower-case-table-names=1
log_bin_trust_function_creators=1
slave.cnf: |
# Slave配置
[mysqld]
datadir=/var/lib/mysql
pid-file=/var/run/mysqld/mysqld.pid
socket=/var/run/mysql/mysql.sock
log-error=/var/log/mysql/error.log
super-read-only
skip-name-resolve
log-bin=mysql-bin
lower-case-table-names=1
log_bin_trust_function_creators=1
相关命令测试
#执行命令
kubectl apply -f 02-mysql-configmap.yaml
#查看mysql命名空间下的configmap
kubectl get cm -n mysql
#查看mysql命名空间下名为mysql的configmap详情
kubectl describe configmap mysql -n mysql
步骤3:编写secret脚本(用来存放mysql密码)
03-mysql-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: mysql
labels:
app: mysql
#Opaque 类型的数据是一个 map 类型,要求value是base64编码。
type: Opaque
data:
password: YTEyMzQ1NiE= # a123456! 转成base64 echo -n "a123456!" | base64
#主从用的账号
replicationUser: Y29weQ== #copy
replicationPassword: YTEyMzQ1NiE= #a123456!
现在有两个账号密码,等一下用来登录主库和从库
root/a123456!
copy/a123456!
相关命令测试
#执行命令
kubectl apply -f 03-mysql-secret.yaml
#查看mysql命名空间下的configmap
kubectl get secret -n mysql
#查看mysql命名空间下名为mysql-secret的secret详情
kubectl describe secret mysql-secret -n mysql
步骤4:编写initContainer脚本
ps:编写的脚本,最后会在创建StatefulSet中使用,这里只是为了展示的更清晰
1)将配置文件拷贝到对应的容器中
set -ex
#从pod的hostname中通过正则获取序号,如果没有截取到就退出程序
ordinal=`hostname | awk -F"-" '{print $2}'` || exit 1
#将serverId输入到对应的配置文件中,路径可以随意(与之后的对应上就行),但是文件名不能换
echo [mysqld] > /etc/mysql/conf.d/server-id.cnf
# 由于server-id不能为0,因此给ID加100来避开它
echo server-id=$((100 + $ordinal)) >> /etc/mysql/conf.d/server-id.cnf
if [[ ${ordinal} -eq 0 ]]; then
# 如果Pod的序号为0,说明它是Master节点,从ConfigMap里把Master的配置文件拷贝到/mnt/conf.d目录下
cp /mnt/config-map/master.cnf /etc/mysql/conf.d
else
# 否则,拷贝ConfigMap里的Slave的配置文件
cp /mnt/config-map/slave.cnf /etc/mysql/conf.d
fi
2)初始化mysql集群
set -ex
cd /var/lib/mysql
#查看是否存在名为mysqlInitOk的文件,我们自己生产的标识文件,防止重复初始化集群
if [ ! -f mysqlInitOk ]; then
echo "Waiting for mysqld to be ready(accepting connections)"
#执行一条mysql的命令,查看mysql是否初始化完毕,如果没有就反复执行直到可以运行
until mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "use mysql;SELECT 1;"; do sleep 1; done
echo "Initialize ready"
#判断是master还是slave
pod_seq=`hostname | awk -F"-" '{print $2}'`
if [ $pod_seq -eq 0 ];then
#创建主从账户
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "create user '${MYSQL_REPLICATION_USER}'@'%' identified by '${MYSQL_REPLICATION_PASSWORD}';"
#设置权限
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "grant replication slave on *.* to '${MYSQL_REPLICATION_USER}'@'%' with grant option;"
#mysql8使用原生密码
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "ALTER USER '${MYSQL_REPLICATION_USER}'@'%' IDENTIFIED WITH mysql_native_password BY '${MYSQL_REPLICATION_PASSWORD}';"
#刷新配置
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "flush privileges;"
#初始化master
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "reset master;"
else
#设置slave连接的master
#mysql-0.mysql.mysql的由来{pod-name}.{service-name}.{namespace}
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e \
"change master to master_host='mysql-0.mysql.mysql',master_port=3306, \
master_user='${MYSQL_REPLICATION_USER}',master_password='${MYSQL_REPLICATION_PASSWORD}', \
master_log_file='mysql-bin.000001',master_log_pos=156;"
#重置slave
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "reset slave;"
#开始同步
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "start slave;"
#改成只读模式
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "set global read_only=1;"
fi
#运行完毕创建标识文件,防止重复初始化集群
touch mysqlInitOk
fi
步骤5:编写StorageClass相关脚本
采用StorageClass+NFS方式作为网络存储,使用这种方式会自动生成pvc和pv
所有的k8s节点上都要安装nfs,NFS搭建移步这里查看:nfs,信息如下
IP: 192.168.100.152
Export PATH: /nfs/data
1)权限设置:编写ServiceAccount、ClusterRole、ClusterRoleBinding、Role、RoleBinding脚本管理NFS
04-mysql-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: mysql
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- 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: ["watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get"]
- apiGroups: ["extensions"]
resources: ["podsecuritypolicies"]
resourceNames: ["nfs-provisioner"]
verbs: ["use"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: mysql
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: mysql
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: mysql
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
相关命令测试
#执行命令
kubectl apply -f 04-mysql-rbac.yaml
2)每个机器上安装NFS服务,存放目录的机器上新建好 /nfs/data 目录
01 选择master节点作为nfs的server,所以在master节点上,执行以下命令
# 第一步,启动nfs和rpcbind
# 启动nfs
systemctl status nfs (如果存在就开启 systmctl start nfs ,不过不存在就安装 yum -y install nfs-utils 并 systemctl start nfs)
systemctl start nfs (启动后再次查看状态,状态成功就是表示启动成功了)
systemctl enable nfs (设置为为开机自启动)
# 启动rpcbind
systemctl restart rpcbind (重启)
systemctl enable rpcbind (设置为开机自启动)
systemctl status rpcbind (查看状态,验证重启成功)
# 第二步,创建nfs目录并授予权限 /nfs/data/ 这个目录就是nfs ip那个目录
# 创建nfs目录
mkdir -p /nfs/data/
# 授予权限
chmod -R 777 /nfs/data
# 第三步,编辑export文件并保存
# 编辑export文件 对于/nfs/data目录,授予可读可写权限、根目录权限、同步数据权限
vi /etc/exports
/nfs/data *(rw,no_root_squash,sync)
/nfs/data *(rw,no_root_squash,sync,no_subtree_check) # 新版nfs
# 使得配置生效
exportfs -r
# 查看生效
exportfs
# 第四步,验证rpcbind、nfs
# 查看rpc服务的注册情况
rpcinfo -p localhost
# showmount测试
# showmount命令用于查询NFS服务器的相关信息 -e或--exports 显示NFS服务器的输出清单。
showmount -e master-ip
showmount -e 192.168.100.152
02 所有node上安装客户端 ps -ef|grep nfs
# 启动nfs
systemctl status nfs (如果存在就开启 systmctl start nfs ,不过不存在就安装 yum -y install nfs-utils 并 systemctl start nfs)
systemctl start nfs (启动后再次查看状态,状态成功就是表示启动成功了)
systemctl enable nfs (设置为为开机自启动)
# 启动rpcbind
systemctl restart rpcbind (重启)
systemctl enable rpcbind (设置为开机自启动)
systemctl status rpcbind (查看状态,验证重启成功)
linux运维关于服务
service could not found 是没有安装服务,需要 yum -y install nfs-utils
inactive 是安装了服务,但是没有启动,需要 systemctl start nfs
3)编写StorageClass脚本
05-mysql-nfs-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: mysql-nfs-storage #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
volumeBindingMode: WaitForFirstConsumer
parameters:
archiveOnDelete: "true" #pvc被删除了也需要进行存档
相关命令测试
#执行命令
kubectl apply -f 05-mysql-nfs-storageclass.yaml
#查看StorageClass信息
kubectl get sc
4)编写nfs-provisioner的Deployment脚本
所有的k8s节点上都要安装nfs,不然这段就会出问题无法运行
06-mysql-nfs-provisioner-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
namespace: mysql #与RBAC文件中的namespace保持一致
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: mysql-nfs-storage #provisioner名称,请确保该名称与 nfs-StorageClass.yaml文件中的provisioner名称保持一致
- name: NFS_SERVER
value: 192.168.100.152 #NFS Server IP地址
- name: NFS_PATH
value: /nfs/data #NFS挂载卷
volumes:
- name: nfs-client-root
nfs:
server: 192.168.100.152 #NFS Server IP地址
path: /nfs/data #NFS 挂载卷
相关命令测试
#执行命令
kubectl apply -f 06-mysql-nfs-provisioner-deployment.yaml
步骤6、编写Service脚本
07-mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: mysql
labels:
app: mysql
spec:
selector:
#匹配带有app: mysql标签的pod
app: mysql
clusterIP: None
ports:
- name: mysql
port: 3306
相关命令测试
#执行命令
kubectl apply -f 07-mysql-service.yaml
#查看mysql命名空间下service信息
kubectl get svc -n mysql
步骤7、编写StatefulSet脚本
08-mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: mysql
labels:
app: mysql
spec:
selector:
matchLabels:
app: mysql
#与mysql-service.yaml中的保持一致
serviceName: mysql
replicas: 3
template:
metadata:
labels:
app: mysql
spec:
initContainers:
- name: init-mysql
image: mysql:8.0.21
command:
- bash
- "-c"
- |
set -ex
#从pod的hostname中通过正则获取序号,如果没有截取到就退出程序
ordinal=`hostname | awk -F"-" '{print $2}'` || exit 1
#将serverId输入到对应的配置文件中,路径可以随意(与之后的对应上就行),但是文件名不能换
echo [mysqld] > /etc/mysql/conf.d/server-id.cnf
# 由于server-id不能为0,因此给ID加100来避开它
echo server-id=$((100 + $ordinal)) >> /etc/mysql/conf.d/server-id.cnf
if [[ ${ordinal} -eq 0 ]]; then
# 如果Pod的序号为0,说明它是Master节点,从ConfigMap里把Master的配置文件拷贝到/mnt/conf.d目录下
cp /mnt/config-map/master.cnf /etc/mysql/conf.d
else
# 否则,拷贝ConfigMap里的Slave的配置文件
cp /mnt/config-map/slave.cnf /etc/mysql/conf.d
fi
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
- name: MYSQL_REPLICATION_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: replicationUser
- name: MYSQL_REPLICATION_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: replicationPassword
volumeMounts:
- name: conf
mountPath: /etc/mysql/conf.d
- name: config-map
mountPath: /mnt/config-map
containers:
- name: mysql
image: mysql:8.0.21
lifecycle:
postStart:
exec:
command:
- bash
- "-c"
- |
set -ex
cd /var/lib/mysql
#查看是否存在名为mysqlInitOk的文件,我们自己生产的标识文件,防止重复初始化集群
if [ ! -f mysqlInitOk ]; then
echo "Waiting for mysqld to be ready(accepting connections)"
#执行一条mysql的命令,查看mysql是否初始化完毕,如果没有就反复执行直到可以运行
until mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "use mysql;SELECT 1;"; do sleep 1; done
echo "Initialize ready"
#判断是master还是slave
pod_seq=`hostname | awk -F"-" '{print $2}'`
if [ $pod_seq -eq 0 ];then
#创建主从账户
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "create user '${MYSQL_REPLICATION_USER}'@'%' identified by '${MYSQL_REPLICATION_PASSWORD}';"
#设置权限
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "grant replication slave on *.* to '${MYSQL_REPLICATION_USER}'@'%' with grant option;"
#mysql8使用原生密码
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "ALTER USER '${MYSQL_REPLICATION_USER}'@'%' IDENTIFIED WITH mysql_native_password BY '${MYSQL_REPLICATION_PASSWORD}';"
#刷新配置
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "flush privileges;"
#初始化master
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "reset master;"
else
#设置slave连接的master
#mysql-0.mysql.mysql的由来{pod-name}.{service-name}.{namespace}
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e \
"change master to master_host='mysql-0.mysql.mysql',master_port=3306, \
master_user='${MYSQL_REPLICATION_USER}',master_password='${MYSQL_REPLICATION_PASSWORD}', \
master_log_file='mysql-bin.000001',master_log_pos=156;"
#重置slave
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "reset slave;"
#开始同步
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "start slave;"
#改成只读模式
mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "set global read_only=1;"
fi
#运行完毕创建标识文件,防止重复初始化集群
touch mysqlInitOk
fi
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
- name: MYSQL_REPLICATION_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: replicationUser
- name: MYSQL_REPLICATION_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: replicationPassword
ports:
- name: mysql
containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: conf
mountPath: /etc/mysql/conf.d
- name: run-mysql
mountPath: /var/run/mysql
resources:
requests:
cpu: 500m
memory: 2Gi
#设置存活探针
livenessProbe:
exec:
command: ["mysqladmin", "ping", "-uroot", "-p${MYSQL_ROOT_PASSWORD}"]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
#设置就绪探针
readinessProbe:
exec:
command: ["mysqladmin", "ping", "-uroot", "-p${MYSQL_ROOT_PASSWORD}"]
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 1
volumes:
- name: config-map
#这个卷挂载到configMap上
configMap:
name: mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
#与nfs-StorageClass.yaml metadata.name保持一致
storageClassName: managed-nfs-storage
resources:
requests:
storage: 5Gi
- metadata:
name: conf
spec:
accessModes:
- ReadWriteOnce
#与nfs-StorageClass.yaml metadata.name保持一致
storageClassName: managed-nfs-storage
resources:
requests:
storage: 100Mi
- metadata:
name: run-mysql
spec:
accessModes:
- ReadWriteOnce
#与nfs-StorageClass.yaml metadata.name保持一致
storageClassName: managed-nfs-storage
resources:
requests:
storage: 100Mi
相关命令测试
#执行命令
kubectl apply -f 08-mysql-statefulset.yaml
#查看mysql命名空间下pvc信息
kubectl get pvc -n mysql
kubectl describe pvc data-mysql-0 -n mysql
#查看mysql命名空间下pv信息
kubectl get pv -n mysql
#查看mysql命名空间下pod节点信息
kubectl get pod -n mysql
#查看mysql命名空间下名为mysql-1节点的mysql从节点状态
kubectl -n mysql exec mysql-1 -c mysql -- bash -c "mysql -uroot -pa123456! -e 'show slave status \G'"
如果使用kubectl get pvc -n mysql查看状态一直是Pending,大概率是使用了k8s1.20以上版本,需要修改 apiserver 的配置文件,重新启用 SelfLink 功能。
vim /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
- command:
- kube-apiserver
...
- --feature-gates=RemoveSelfLink=false # 增加这行
如果依旧不行,用命令查看nfs-client-provisioner有没有报错
#查看pod名称
kubectl get pods -n mysql
#找到对应的pod查看日志
kubectl logs -f nfs-client-provisioner-xxxxx -n mysql
如果看到类似 unable to create directory to provision new pv: mkdir /persistentvolumes/mysql-data-mysql-0-pvc-1bb47,就在nfs服务器端给挂载的那个文件夹开通权限
chmod -R 777 /nfs/data
步骤8:测试主从同步
主库操作如下:
从库操作如下:
另外注意,show slave status\G; 只在主库上执行,不能在从库上执行
三、尾声
Kubernetes部署_使用kubernetes部署Mysql主从结构(Kubernetes工作实践类)
完!