Máquina: (ubuntu20.04) kubernetes:1.23
materia | 192.168.111.141 |
nodo01 | 192.168.111.142 |
nodo02 | 192.168.111.143 |
nfs | 192.168.111.144 |
1. Configurar nfs
Configure nfs para crear almacenamiento sostenible.
Instalar el servidor nfs en nfs
apt -y install nfs-server
Cree una carpeta para el almacenamiento backend.
mkdir /data-share
#修改权限
chown nobody:nogroup /data-share/
chmod 777 /data-share/
Edite el archivo /etc/exports para administrar carpetas
root@nfs:~# cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/data-share 192.168.111.0/24(rw,sync,no_root_squash,no_subtree_check)
Ejecute el siguiente comando para recargar nfs
root@nfs:~# exportfs -arv
root@nfs:~# showmount -e 127.0.0.1
Export list for 127.0.0.1:
/data-share 192.168.111.0/24
Descargue nfs-common en el cliente, es decir, node01 y node02
apt -y install nfs-common
Utilice node01 y node02 para montar nfs respectivamente y comprobar si el montaje se puede realizar correctamente.
#挂载
root@node01:~# mount -t nfs 192.168.111.144:/data-share /data
#df -h 查看
root@node01:/data# df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 389M 2.4M 387M 1% /run
/dev/mapper/ubuntu--vg-ubuntu--lv 29G 13G 15G 46% /
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/sda2 1.5G 246M 1.2G 18% /boot
tmpfs 389M 4.0K 389M 1% /run/user/0
nfs:/data-share 29G 7.8G 20G 29% /data
#测试一下是否可以共享文件夹
root@node01:/data# echo "Hello,I'm node01" >> hello
root@node01:/data# cat hello
Hello,I'm node01
# 切换为nfs服务器,查看
root@nfs:~# cd /data-share/
root@nfs:/data-share# ll
total 12
drwxrwxrwx 2 nobody nogroup 4096 Oct 20 12:16 ./
drwxr-xr-x 20 root root 4096 Oct 20 03:47 ../
-rw-r--r-- 1 nobody nogroup 17 Oct 20 12:17 hello
root@nfs:/data-share# cat hello
Hello,I'm node01
是可以挂载成功的。node02也是同样的方式测试一下。
2. Escriba pv para almacenamiento continuo
1. Instale nfs:
Descargue el archivo yaml de nfs y modifique la configuración del archivo yaml.
Dirección de descarga del archivo Yaml: https://raw.githubusercontent.com/kubernetes-sigs/nfs-subdir-external-provisioner/master/deploy/deployment.yaml
root@master01:/opt/k8s/controller/mysql# wget https://raw.githubusercontent.com/kubernetes-sigs/nfs-subdir-external-provisioner/master/deploy/deployment.yaml
#改一下名字更好的辨认
root@master01:/opt/k8s/controller/mysql# mv deployment.yaml nfs-deploy.yaml
## nfs-deploy.yaml配置如下
root@master01:/opt/k8s/controller/mysql# cat nfs-deploy.yaml
apiVersion: v1
kind: Namespace
metadata:
name: nfs-pro
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-pro #更改为我们上面创建的namespace
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: registry.cn-hangzhou.aliyuncs.com/smxy-cc/nfs-subdir-external-provisioner:v4.0.2 #更改一下镜像,用原来的镜像可能会出现下载失败的情况
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER
value: 192.168.111.144 #更改为我们nfs server的IP地址
- name: NFS_PATH
value: /data-share #更改为我们想要挂载的目录
volumes:
- name: nfs-client-root
nfs:
server: 192.168.111.144 #更改为我们nfs server的IP地址
path: /data-share #更改为我们想要挂载的目录
##创建
root@master01:/opt/k8s/controller/mysql# kubectl apply -f nfs-deploy.yaml
Cree el archivo rbac.yaml (recuerde cambiar el espacio de nombres a nfs-pro creado anteriormente)
[root@master01/opt/k8s/controller/mysql] # cat nfs-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner #与deployment文件中的一致
namespace: nfs-pro
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- 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: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-pro
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
# replace with namespace where provisioner is deployed
namespace: nfs-pro
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
# replace with namespace where provisioner is deployed
namespace: nfs-pro
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: nfs-pro
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
Crear clase de almacenamiento (después de crear sc, ya no necesitamos crear pv uno por uno)
root@master01/opt/k8s/controller/mysql] # cat nfs-sc.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage #StorageClass的名字,创建PVC时要用到
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner #NFS 的provisioner 名称,与deployment中设置的需一致
parameters:
archiveOnDelete: "false"
[root@master01/opt/k8s/controller/mysql] # kubectl apply -f nfs-sc.yaml
crear pvc
[root@master01/opt/k8s/controller/mysql] # cat nfs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
namespace: nfs-pro
spec:
accessModes:
- ReadWriteMany
volumeMode: Filesystem
storageClassName: nfs-storage #与上节创建的storageClass名称一致
resources:
requests:
storage: 8Gi
[root@master01/opt/k8s/controller/mysql] # kubectl apply -f nfs-pvc.yaml
Tercero, realizar la replicación maestro-esclavo de la base de datos.
Al implementar la replicación maestro-esclavo de la base de datos, debemos resolver tres puntos:
- El nodo maestro y el nodo esclavo deben tener archivos de configuración diferentes (es decir, my.cnf).
- El nodo matser y el nodo esclavo deben poder transmitir archivos de información de respaldo.
- Antes de iniciar el nodo esclavo por primera vez, es necesario realizar algunas operaciones SQL iniciales.
Resolvámoslo a continuación:
Debido a que esto es bastante complicado de implementar, primero definimos un marco y luego agregamos configuraciones al marco.
Presentemos este marco juntos.
El controlador que elegimos es StatefulSet, porque nuestro clúster de replicación maestro-esclavo es un pod con estado y StatefulSet está preparado para clústeres con estado, por lo que es razonable elegir StatefulSet como nuestro controlador. Hablaré de esto más adelante durante la implementación real.
seletor: Indica que este controlador solo controlará pods con la etiqueta app:mysql
serviceName: esta es también la mayor diferencia entre statefulSet e implementación: le indica a StatefulSet que utilice el servicio sin cabeza mysql para garantizar la "identidad resoluble" del Pod al ejecutar el ciclo de control.
réplicas: controla principalmente el número de vainas.
plantilla: esta es la información de la plantilla del pod. Entre ellos, initContainers son los pods de inicialización, porque los tres problemas que tenemos que resolver antes necesitan hacer un juicio, es decir, el juicio del nodo maestro y el nodo esclavo. Y este initContainers puede ayudarnos a juzgar master y salve.
volumeClaimTemplates: debido a que nuestro clúster de base de datos es un clúster con estado de almacenamiento, necesitamos almacenar nuestros datos de manera sostenible a través de pv.
A continuación, cree dos servicios, uno para el nodo maestro y otro para el nodo esclavo. El nodo maestro utiliza un servicio sin cabeza, mientras que el nodo esclavo utiliza un servicio ordinario.
[root@master01/opt/k8s/controller/mysql] # cat mysql-svc.yaml
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
Eso es todo por el marco. A continuación abordaremos los tres puntos que planteamos anteriormente respectivamente.
1. El nodo maestro y el nodo esclavo requieren archivos de configuración diferentes (my.cnf)
Debido a que configmap puede ayudarnos mejor a montar los archivos requeridos, escribimos un mapa de configuración para almacenar archivos de configuración.
Mapa de configuración.yaml
[root@master01/opt/k8s/controller/mysql] # cat ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
labels:
app: mysql
data:
master.cnf: |
# 主节点MySQL的配置文件
[mysqld]
log-bin
slave.cnf: |
# 从节点MySQL的配置文件
[mysqld]
super-read-only
configmap también es un valor en forma de clave-valor. El que está delante de | es la clave y el que está después de | es el valor. El nodo maestro es log-bin y el nodo esclavo es de solo lectura, lo que significa que solo se permite la lectura.
Cree un mapa de configuración y vea los detalles.
[root@master01/opt/k8s/controller/mysql] # kubectl describe cm mysql
Name: mysql
Namespace: default
Labels: app=mysql
Annotations: <none>
Data
====
master.cnf:
----
# 主节点MySQL的配置文件
[mysqld]
log-bin
slave.cnf:
----
# 从节点MySQL的配置文件
[mysqld]
super-read-only
BinaryData
====
Events: <none>
Monte nuestro mapa de configuración en nuestro pod mediante el comando de montaje y el nombre del montaje es config-map.
- name: config-map
configMap:
name: mysql
A continuación debemos colocar master.cnf y Slave.cnf en los nodos correspondientes.
El siguiente código tiene una explicación detallada.
initContainers:
- name: init-mysql
image: mysql:5.7
command:
- bash
- "-c"
- |
# 这个命令的作用就是下面如果命令执行错误时,会停止不会再往下执行。
set -ex
# 第一步运用正则匹配,若 - 后面的数字是0-9的进行下面的操作,若不是则退出。
[[ $HOSTNAME =~ -([0-9]+)$ ]] || exit 1
# ordinal 等于hostname中 - 后面的数字
ordinal=${BASH_REMATCH[1]}
# 输入【mysqld】到文件里,作用是创建文件server-id.cnf
echo [mysqld] > /mnt/conf.d/server-id.cnf
# 因为server-id=0的话有特殊的含义,所以我们+100来避开他
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
# 下面是判断mysql的主从,这里是将序号为0的设为主数据库,其他的都是从数据库。
# 我们通过下面的挂载操作(volumeMounts中的config-map),是将configmap中的两个
#配置文件放在了 /mnt/config-map 下。
# 若pod的序列号为0(master节点),我们将/mnt/config-map下的master.cnf文件放
#在/mnt/conf.d下面。若不为0(slave节点),我们将/mnt/config-map下的slave.cnf文
#件放在/mnt/conf.d下面。
# 并将/mnt/conf.d/进行挂载,名字为conf
if [[ $ordinal -eq 0 ]]; then
cp /mnt/config-map/master.cnf /mnt/conf.d/
else
cp /mnt/config-map/slave.cnf /mnt/conf.d/
fi
# 进行挂载上面已经讲过了。
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
- name: config-map
mountPath: /mnt/config-map
2. El nodo matser y el nodo esclavo deben poder transmitir archivos de información de respaldo.
Aquí usaremos el complemento Xtrabackup. XtraBackup es una herramienta de copia de seguridad y recuperación de MySQL de código abierto que se utiliza principalmente en la industria. XtraBackup se utiliza para realizar una copia de seguridad de los datos del nodo maestro en el directorio especificado. Este paso generará automáticamente un archivo xtrabackup_binlog_info con dos contenidos, que necesitamos al inicializar el nodo esclavo.
El código es el siguiente (detallado):
- name: clone-mysql
image: yizhiyong/xtrabackup
command:
- bash
- "-c"
- |
set -ex
# 拷贝任务只第一次启动时才执行,所以如果有文件的话,说明已经不是第一次执行了,所以退出
[[ -d /var/lib/mysql/mysql ]] && exit 0
# 判断如果 - 后面的数字不是 0-9,就退出.
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
# ordinal= ‘-’ 后面的数字
ordinal=${BASH_REMATCH[1]}
# 判断ordinal的值是不是0,也就是说mysql为mysql-0(master节点),master节点并不需要备份,所以退出。剩下的都是slave节点,也是需要从master节点备份数据的。
[[ $ordinal -eq 0 ]] && exit 0
# 通过ncat监视mysql的3307端口,并用xbstream将/var/lib/mysql(mysql的)文件夹下的文件拷贝过来
ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
# 进行将得到的数据保存在/var/lib/mysql(xtrabackup的)下,用于下面的挂载
xtrabackup --prepare --target-dir=/var/lib/mysql
# 将我们的拷贝过来的文件夹挂载一下,用于我们下面的mysql容器使用。
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: conf
mountPath: /etc/mysql/conf.d
3. Inicialice el nodo esclavo
Actualmente tenemos los archivos de configuración (master.cnf, Slave.cnf) y los archivos que queremos respaldar, pero necesitamos configurar algunos comandos SQL al inicializar el nodo esclavo. Entonces usamos un nuevo contenedor para configurar los comandos SQL del nodo esclavo.
El código es el siguiente (detallado):
- name: xtrabackup
image: yizhiyong/xtrabackup
ports:
- name: xtrabackup
containerPort: 3307
command:
- bash
- "-c"
- |
set -ex
# 通过下面的挂载操作我们已经在/var/lib/mysql中挂载到了clone-mysql中的数据,进入/var/lib/mysql中
cd /var/lib/mysql
# 判断前面挂载的数据是不是salve节点的文件,如果是slave节点的文件,我们可以直接拷贝过来使用。
if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
# 我们直接去出有用信息放在change_master_to.sql.in文件中
cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
# 我们用完后就删除,因为初始化操作执行一次就行了。
rm -f xtrabackup_slave_info xtrabackup_binlog_info
# 判断是master节点的话,我们需要在xtrabackup_binlog_info中取出我们有用的两个信息。
elif [[ -f xtrabackup_binlog_info ]]; then
# 去出xtrabackup_binlog_info文件中的两个信息,我上面有讲,使用Xtrabackup时会创建一个xtrabackup_binlog_info的文件,文件中有两个信息是我们初始化salve节点时使用的。
[[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
# 取出后就删掉,防止我们再次初始化
rm -f xtrabackup_binlog_info xtrabackup_slave_info
# 将我们取出的数据编写一下放进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.in
fi
# 如果有这个文件就说明我们需要做初始化操作了。
if [[ -f change_master_to.sql.in ]]; then
# 我们必须要等到mysql容器启动起来再进行操作初始化操作。所以until一下我们的初始化操作。
echo "Waiting for mysqld to be ready (accepting connections)"
until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done
# 进行初始操作,用我们前面得到的change_master_to.sql.in拼接成一个完整的SQL命令。
echo "Initializing replication from clone position"
mysql -h 127.0.0.1 \
-e "$(<change_master_to.sql.in), \
MASTER_HOST='mysql-0.mysql', \
MASTER_USER='root', \
MASTER_PASSWORD='', \
MASTER_CONNECT_RETRY=10; \
START SLAVE;" || exit 1
# 删掉,防止重启时,再次初始化。
mv change_master_to.sql.in change_master_to.sql.orig
fi
# 使用ncat打开一个3307的端口。这也与我们前面监听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"
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: conf
mountPath: /etc/mysql/conf.d
#对该容器做一个限制,因为这个容器只不过是一个我们的配置容器。
resources:
requests:
cpu: 100m
memory: 100Mi
4. La definición del propio contenedor mysql.
Hasta ahora hemos resuelto con éxito los tres puntos anteriores. Podemos echar un vistazo a la definición del propio contenedor mysql, que es muy simple.
El código es el siguiente (detallado):
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "1"
ports:
- name: mysql
containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: conf
mountPath: /etc/mysql/conf.d
resources:
requests:
cpu: 500m
memory: 1Gi
# 前面的没啥好说的一眼都知道什么意思了。
# 下面做了一个监控容器健康的检查。可以看一下我前面的文章有介绍
livenessProbe:
exec:
# 执行命令ping一下本身,如果ping不同,就是监看有问题
command: ["mysqladmin", "ping"]
# 容器创建成功30S后执行
initialDelaySeconds: 30
# 每10s执行一次
periodSeconds: 10
# 超时5S就说明健康有问题。
timeoutSeconds: 5
readinessProbe:
exec:
# Check we can execute queries over TCP (skip-networking is off).
command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 1
En este punto, todos nuestros preparativos están completos.
5. Complete el archivo yaml
[root@master01/opt/k8s/controller/mysql] # cat mysql-stateSetful2.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
app.kubernetes.io/name: mysql
serviceName: mysql
replicas: 3
template:
metadata:
labels:
app: mysql
app.kubernetes.io/name: mysql
spec:
initContainers:
- name: init-mysql
image: mysql:5.7
command:
- bash
- "-c"
- |
set -ex
# Generate mysql server-id from pod ordinal index.
[[ $HOSTNAME =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo [mysqld] > /mnt/conf.d/server-id.cnf
# Add an offset to avoid reserved server-id=0 value.
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
# Copy appropriate conf.d files from config-map to emptyDir.
if [[ $ordinal -eq 0 ]]; then
cp /mnt/config-map/primary.cnf /mnt/conf.d/
else
cp /mnt/config-map/replica.cnf /mnt/conf.d/
fi
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
- name: config-map
mountPath: /mnt/config-map
- name: clone-mysql
image: gcr.io/google-samples/xtrabackup:1.0
command:
- bash
- "-c"
- |
set -ex
# Skip the clone if data already exists.
[[ -d /var/lib/mysql/mysql ]] && exit 0
# Skip the clone on primary (ordinal index 0).
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
[[ $ordinal -eq 0 ]] && exit 0
# Clone data from previous peer.
ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
# Prepare the backup.
xtrabackup --prepare --target-dir=/var/lib/mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "1"
ports:
- name: mysql
containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
resources:
requests:
cpu: 500m
memory: 1Gi
livenessProbe:
exec:
command: ["mysqladmin", "ping"]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
# Check we can execute queries over TCP (skip-networking is off).
command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 1
- name: xtrabackup
image: gcr.io/google-samples/xtrabackup:1.0
ports:
- name: xtrabackup
containerPort: 3307
command:
- bash
- "-c"
- |
set -ex
cd /var/lib/mysql
# Determine binlog position of cloned data, if any.
if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
# XtraBackup already generated a partial "CHANGE MASTER TO" query
# because we're cloning from an existing replica. (Need to remove the tailing semicolon!)
cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
# Ignore xtrabackup_binlog_info in this case (it's useless).
rm -f xtrabackup_slave_info xtrabackup_binlog_info
elif [[ -f xtrabackup_binlog_info ]]; then
# We're cloning directly from primary. Parse binlog position.
[[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
rm -f xtrabackup_binlog_info xtrabackup_slave_info
echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
fi
# Check if we need to complete a clone by starting replication.
if [[ -f change_master_to.sql.in ]]; then
echo "Waiting for mysqld to be ready (accepting connections)"
until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done
echo "Initializing replication from clone position"
mysql -h 127.0.0.1 \
-e "$(<change_master_to.sql.in), \
MASTER_HOST='mysql-0.mysql', \
MASTER_USER='root', \
MASTER_PASSWORD='', \
MASTER_CONNECT_RETRY=10; \
START SLAVE;" || exit 1
# In case of container restart, attempt this at-most-once.
mv change_master_to.sql.in change_master_to.sql.orig
fi
# Start a server to send backups when requested by peers.
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"
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
resources:
requests:
cpu: 100m
memory: 100Mi
volumes:
- name: conf
emptyDir: {}
- name: config-map
configMap:
name: mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
storageClassName: "nfs-storage"
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
Crear conjunto con estado
kubectl apply -f mysql-statefulset.yaml
Creado con éxito
[root@master01/opt/k8s/controller/mysql] # kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql-0 2/2 Running 0 10m 10.220.140.73 node02 <none> <none>
mysql-1 2/2 Running 0 10m 10.220.196.133 node01 <none> <none>
6. Pruebas
Escriba datos a través del nodo maestro.
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
Ver datos a través del servicio mysql-read.
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 |
+---------+
pod "mysql-client" deleted
Conectémonos a mysql-1 para verificar los datos. Descubrimos que podemos ver los datos que escribimos con mysql-0 arriba, lo que indica que la replicación maestro-esclavo fue exitosa.
## 这是我后面测试时,通过上面的方法新加入的数据
[root@master01/opt/k8s/controller/mysql] # kubectl run -it mysql-client --image=mysql:5.7 --rm --restart=Never -- mysql -h mysql-1.mysql -e "select * from test.messages;"
+-------------------+
| message |
+-------------------+
| hello,kubernetes! |
| hello,mysql-read! |
+-------------------+
pod "mysql-client" deleted
Usamos mysql-1 para ingresar los datos. Se encontró un error. El contenido del error es que los permisos del servidor son de solo lectura y no se pueden escribir.
[root@master01/opt/k8s/controller/mysql] # kubectl run -it mysql-client --image=mysql:5.7 --rm --restart=Never -- mysql -h mysql-1.mysql -e "insert into test.messages values ('hello,I am mysql-1');"
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --super-read-only option so it cannot execute this statement
pod "mysql-client" deleted
pod default/mysql-client terminated (Error)
7. Errores de grabación
Finalmente, registre los errores encontrados.
root@master01:/opt/k8s/controller/mysql# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-0 1/2 CrashLoopBackOff 3 (5s ago) 60s
root@master01:/opt/k8s/controller/mysql# kubectl logs mysql-0
error: a container name must be specified for pod mysql-0, choose one of: [mysql xtrabackup] or one of the init containers: [init-mysql clone-mysql]
root@master01:/opt/k8s/controller/mysql# kubectl logs mysql-0 -c clone-mysql
+ [[ -d /var/lib/mysql/mysql ]]
++ hostname
+ [[ mysql-0 =~ -([0-9]+)$ ]]
+ ordinal=0
+ [[ 0 -eq 0 ]]
+ exit 0
root@master01:/opt/k8s/controller/mysql# kubectl logs mysql-0 -c mysql
2022-10-21 17:09:31+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 5.7.36-1debian10 started.
chown: changing ownership of '/var/lib/mysql/': Operation not permitted
root@master01:/opt/k8s/controller/mysql# kubectl logs mysql-0 -c xtrabackup
+ cd /var/lib/mysql
+ [[ -f xtrabackup_slave_info ]]
+ [[ -f xtrabackup_binlog_info ]]
+ [[ -f change_master_to.sql.in ]]
+ 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'
root@master01:/opt/k8s/controller/mysql# kubectl logs mysql-0 -c clone-mysql
+ [[ -d /var/lib/mysql/mysql ]]
++ hostname
+ [[ mysql-0 =~ -([0-9]+)$ ]]
+ ordinal=0
+ [[ 0 -eq 0 ]]
+ exit 0
¡Encontré un error al crear mysql! ! ! ! Entonces es hora de encontrar fallas. . . . Encontré un error al crear el pod mysql
chown: cambiar la propiedad de '/var/lib/mysql/': operación no permitida
Significa que no hay permiso. Luego miré mi configuración de nfs y estaba mal. .
Cambie /etc/exports a lo siguiente:
root@nfs:/data-share# cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/data-share 192.168.111.0/24(rw,sync,no_root_squash,no_subtree_check)