k8s搭建mysql主从同步

1. mysql master镜像配置

  首先,我们需要从docker hub官网上获取dockerfile文件用于后续的修改。地址如下:
  https://github.com/docker-library/mysql.git
  然后将8.0文件夹复制一份,将另一份命名为8.0_slave。进入到8.0文件夹中,里面有个Dockerfile.debian文件,修改名字为Dockerfile

mv Dockerfile.debian Dockerfile

  config文件夹下面的my.cnf是mysql的配置文件。我们需要加上以下配置

[mysql]
default-character-set=utf8 #设置mysql客户端默认字符集

[client]
default-character-set=utf8 #设置mysql客户端连接服务端时默认使用的端口

[mysqld]
server-id = 1   
log-bin = master-bin
log_bin_index =  master-bin.index
max_connections=200 # 允许最大连接数
max_connect_errors=10 # 允许连接失败的次数。这是为了防止有人从该主机试图攻击数据库系统
character-set-server=utf8 # 服务端使用的字符集默认为UTF8
default_authentication_plugin=mysql_native_password  # 默认使用“mysql_native_password”插件认证
binlog_format=mixed
expire_logs_days        = 10         #binlog过期清理时间
max_binlog_size         = 100m       #binlog每个日志文件大小
binlog_cache_size       = 4m         #binlog缓存大小
max_binlog_cache_size   = 512m       #最大binlog缓存大小

其中server-id 是mysql master库的id,必须是唯一且与slave库不一致。log-bin开启binlog并指定了binlog文件的名字。
  由于我们位于东八区,所以需要设置一下时区。在刚才的Dockerfile文件 ENTRYPOINT 上面加入下列命令
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENTRYPOINT ["docker-entrypoint.sh"]

  最后,我们需要修改一下 docker-entrypoint.sh。在docker_setup_db()函数的最后加上下列代码,用于创建一个slave用户并配置权限。

	if [ -n "$MYSQL_REPLICATION_USER" ] && [ -n "$MYSQL_REPLICATION_PASSWORD" ]; then
		mysql_note "Creating user $MYSQL_REPLICATION_USER"
		docker_process_sql --database=mysql <<<"CREATE USER '$MYSQL_REPLICATION_USER'@'%' IDENTIFIED WITH 'mysql_native_password' BY '$MYSQL_REPLICATION_PASSWORD';"
		docker_process_sql --database=mysql <<<"GRANT REPLICATION SLAVE ON *.* TO '$MYSQL_REPLICATION_USER'@'%';"
		docker_process_sql --database=mysql <<<"FLUSH PRIVILEGES ;"
	fi

2. mysql slave镜像配置

  首先进入到8.0_slave文件夹。与master类似,里面有个Dockerfile.debian文件,修改名字为Dockerfile

mv Dockerfile.debian Dockerfile

  修改config文件夹下面的my.cnf。

[mysql]
default-character-set=utf8 # 设置mysql客户端默认字符集

[client]
default-character-set=utf8# 设置mysql客户端连接服务端时默认使用的端口

[mysqld]
server-id = 10
log-bin = log/slave10-bin
log_bin_index =  log/slave10-bin.index
relay-log = log/slave10-relay-bin 
relay-log-index = log/slave10-relay-bin.index
max_connections=200 # 允许最大连接数
max_connect_errors=10 # 允许连接失败的次数。这是为了防止有人从该主机试图攻击数据库系统
character-set-server=utf8 # 服务端使用的字符集默认为UTF8
default_authentication_plugin=mysql_native_password  # 默认使用“mysql_native_password”插件认证
binlog_format=mixed
expire_logs_days        = 10         #binlog过期清理时间
max_binlog_size         = 100m       #binlog每个日志文件大小
binlog_cache_size       = 4m         #binlog缓存大小
max_binlog_cache_size   = 512m       #最大binlog缓存大小	

其中server-id 是mysql slave库的id,必须是唯一且与刚才的master库不一致,我这里是设置为固定10,当有多个slave时,可以固定设置为10,11,12,每个slave的id必须不一致。
  修改时区。在刚才的Dockerfile文件 ENTRYPOINT 上面加入下列命令
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ENTRYPOINT ["docker-entrypoint.sh"]

  最后,我们需要修改一下 docker-entrypoint.sh。在docker_setup_db()函数的最后加上下列代码,用于设置master的地址,用于同步的账号密码,并开启slave模式。

	if [ -n "$MYSQL_MASTER_SERVICE_HOST" ] && [ -n "$MYSQL_REPLICATION_USER" ]; then
			mysql_note "Connecting master_host: $MYSQL_MASTER_SERVICE_HOST, which user: $MYSQL_REPLICATION_USER, password: $MYSQL_REPLICATION_PASSWORD"
			docker_process_sql --database=mysql <<<"STOP SLAVE;"
			docker_process_sql --database=mysql <<<"CHANGE MASTER TO master_host='$MYSQL_MASTER_SERVICE_HOST', master_user='$MYSQL_REPLICATION_USER', master_password='$MYSQL_REPLICATION_PASSWORD' ;"
			docker_process_sql --database=mysql <<<"START SLAVE;"
	fi

可以看到上面我们写的代码里面有个变量MYSQL_MASTER_SERVICE_HOST,这个环节变量是k8s自动生成的,所以master mysql的service必须命名为mysql-master。

3. 生成docker镜像

在8.0文件夹中执行指令生成master镜像

docker build -t mysql_master:v0.1 .

在8.0_slave文件夹中执行指令生成slave镜像

docker build -t mysql_slave:v0.1 .

4. k8s配置

在喜欢的文件夹中,创建mysql_master.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: database
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-master
  namespace: database
  labels:
    app: mysql-master
spec:
  ports:
    - port: 3306
      targetPort: 3306
  selector:
    app: mysql-master
---
apiVersion: apps/v1                 #apiserver的版本
kind: Deployment                                      #副本控制器deployment,管理pod和RS
metadata:
  namespace: database
  name: mysql-master                                            #deployment的名称,全局唯一
spec:
  replicas: 1                                                #Pod副本期待数量
  selector:
    matchLabels:                                         #定义RS的标签
      app: mysql-master                                             #符合目标的Pod拥有此标签
  strategy:                                                  #定义升级的策略
    type: RollingUpdate                               #滚动升级,逐步替换的策略
  template:                                                #根据此模板创建Pod的副本(实例)
    metadata:
      labels:
        app: mysql-master                                       #Pod副本的标签,对应RS的Selector
    spec:
      containers:                                          #Pod里容器的定义部分
      - name: mysql                                     #容器的名称
        image: mysql_master:v0.1                         #容器对应的docker镜像
        ports:
        - containerPort: 3306                         #容器暴露的端口号
        env:                                                   #写入到容器内的环境容量
        - name: MYSQL_ROOT_PASSWORD   #定义了一个mysql的root密码的变量
          value: "123456"
        - name: MYSQL_REPLICATION_USER   #定义了一个mysql的root密码的变量
          value: "rep1ication"
        - name: MYSQL_REPLICATION_PASSWORD   #定义了一个mysql的root密码的变量
          value: "123456"

创建mysql_slave.yaml

apiVersion: v1
kind: Service
metadata:
  name: mysql-slave
  namespace: database
  labels:
    app: mysql-slave
spec:
  ports:
    - port: 3306
      targetPort: 3306
  selector:
    app: mysql-slave
---
apiVersion: apps/v1                 #apiserver的版本
kind: Deployment                                      #副本控制器deployment,管理pod和RS
metadata:
  namespace: database
  name: mysql-slave                                            #deployment的名称,全局唯一
spec:
  replicas: 1                                                #Pod副本期待数量
  selector:
    matchLabels:                                         #定义RS的标签
      app: mysql-slave                                             #符合目标的Pod拥有此标签
  strategy:                                                  #定义升级的策略
    type: RollingUpdate                               #滚动升级,逐步替换的策略
  template:                                                #根据此模板创建Pod的副本(实例)
    metadata:
      labels:
        app: mysql-slave                                       #Pod副本的标签,对应RS的Selector
    spec:
      containers:                                          #Pod里容器的定义部分
      - name: mysql                                     #容器的名称
        image: mysql_slave:v0.1                           #容器对应的docker镜像
        ports:
        - containerPort: 3306                         #容器暴露的端口号
        env:                                                   #写入到容器内的环境容量
        - name: MYSQL_ROOT_PASSWORD   #定义了一个mysql的root密码的变量
          value: "123456"
        - name: MYSQL_REPLICATION_USER   #定义了一个mysql的root密码的变量
          value: "rep1ication"
        - name: MYSQL_REPLICATION_PASSWORD   #定义了一个mysql的root密码的变量
          value: "123456"

最后执行,查看k8s里面pod的情况是否正常,需要注意,为了方便,本人没有为pod设置持久化存储,正常的环境都是需要设置持久化存储的。设置外部存储容器的/var/lib/mysql即可实现持久化。

kubectl apply -f mysql_master.yaml 
kubectl apply -f mysql_salve.yaml 

5. 检查测试

(1)检查结果

进入slave对应的pod,进入mysql

mysql -uroot -p
show slave status\G;

如果 Slave_IO_Running,Slave_SQL_Running状态都是yes,说明主从mysql配置完成。
如果不成功,则需要使用kubectl describe命令来查看slave pod的日志,一般最有可能就是master mysql设置slave账号出现问题。在mysql里面使用命令

select user,host from mysql.user; //结果应该有账号rep1ication
show grants for 'rep1ication'@'%'; 
//结果为GRANT USAGE SLAVE ON *.* TO 'rep1ication'@'%' 则为错误
//结果为GRANT REPLICATION SLAVE ON *.* TO 'rep1ication'@'%'则为正确

(2)测试

进入master对应的pod,进入mysql

kubectl exec -it mysql-master-pe18a /bin/bash
mysql -uroot -p
create database test; use test; create table test_tb(id int(3),name char(10)); 
insert into test_tb values(001,'ok');
insert into test_tb values(002,'ok');

然后在slave的pod里面,查看是否有数据

mysql -uroot -p
use test;
select * from test_tb;

6. 镜像,配置文件下载

镜像地址:一共两个,一个是master,一个是slave
https://registry.hub.docker.com/r/evilskyman/mysql_master
https://registry.hub.docker.com/r/evilskyman/mysql_slave
Dockerfile和yaml文件
https://gitee.com/evilskyman/mysql-test

  • 0
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
//以下为串口通讯方式的控制程序 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <termios.h> #include <errno.h> #define FINGERPRINT_STARTCODE 0xEF01 //起始码 #define FINGERPRINT_COMMANDPACKET 0x01 //指令包 #define FINGERPRINT_DATA_PACKET 0x02 //数据包 #define FINGERPRINT_ACKPACKET 0x07 //应答包 #define FINGERPRINT_ENDDATAPACKET 0x08 //结束数据包 #define FINGERPRINT_TIMEOUT 1000 //超时时间 int fd = -1; //串口文件描述符 void serialInit(char *port) //串口初始化 { struct termios options; fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY); if (fd < 0) { perror("open"); return; } tcgetattr(fd, &options); options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; options.c_iflag = IGNPAR | ICRNL; options.c_oflag = 0; options.c_lflag = 0; tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &options); } int serialSend(unsigned char *data, int len) //串口发送数据 { int ret = -1; ret = write(fd, data, len); if (ret < 0) { perror("write"); return -1; } return ret; } int serialRecv(unsigned char *data, int len) //串口接收数据 { int ret = -1; fd_set readfds; struct timeval timeout; FD_ZERO(&readfds); FD_SET(fd, &readfds); timeout.tv_sec = 0; timeout.tv_usec = FINGERPRINT_TIMEOUT; ret = select(fd + 1, &readfds, NULL, NULL, &timeout); if (ret == -1) { perror("select"); return -1; } else if (ret == 0) { printf("select timeout\n"); return -1; } else { ret = read(fd, data, len); if (ret < 0) { perror("read"); return -1; } return ret; } } int fingerprintVerify() //指纹验证 { unsigned char cmd[12] = {0}; //指令 unsigned char recvBuf[12] = {0}; //接收缓存 int ret = -1; //返回值 int i = 0; //计数器 int sum = 0; //校验和 cmd[0] = (FINGERPRINT_STARTCODE >> 8) & 0xff; //起始码高位 cmd[1] = FINGERPRINT_STARTCODE & 0xff; //起始码低位 cmd[2] = 0x00; //设备地址 cmd[3] = 0x03; //数据长度 cmd[4] = FINGERPRINT_COMMANDPACKET; //指令类型 cmd[5] = 0x01; //指令代码 cmd[6] = 0x00; //参数1 cmd[7] = 0x00; //参数2 sum = cmd[4] + cmd[5] + cmd[6] + cmd[7]; //计算校验和 cmd[8] = (sum >> 8) & 0xff; //校验和高位 cmd[9] = sum & 0xff; //校验和低位 cmd[10] = (FINGERPRINT_ENDDATAPACKET >> 8) & 0xff; //结束码高位 cmd[11] = FINGERPRINT_ENDDATAPACKET & 0xff; //结束码低位 ret = serialSend(cmd, sizeof(cmd)); //发送指令 if (ret < 0) { printf("serial send failed\n"); return -1; } ret = serialRecv(recvBuf, sizeof(recvBuf)); //接收应答 if (ret < 0) { printf("serial recv failed\n"); return -1; } if (recvBuf[0] != ((FINGERPRINT_STARTCODE >> 8) & 0xff) || recvBuf[1] != (FINGERPRINT_STARTCODE & 0xff)) //判断起始码 { printf("start code error\n"); return -1; } if (recvBuf[4] != FINGERPRINT_ACKPACKET) //判断是否为应答包 { printf("not ack packet\n"); return -1; } if (recvBuf[5] != 0x01) //判断指令代码 { printf("verification failed\n"); return -1; } return 0; } int main() { serialInit("/dev/ttyUSB0"); //初始化串口 if (fingerprintVerify() == 0) //指纹验证 { printf("verification succeed\n"); } else { printf("verification failed\n"); } close(fd); //关闭串口 return 0; } //以下为USB通讯方式的控制程序 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <libusb-1.0/libusb.h> #define FINGERPRINT_STARTCODE 0xEF01 //起始码 #define FINGERPRINT_COMMANDPACKET 0x01 //指令包 #define FINGERPRINT_DATA_PACKET 0x02 //数据包 #define FINGERPRINT_ACKPACKET 0x07 //应答包 #define FINGERPRINT_ENDDATAPACKET 0x08 //结束数据包 #define FINGERPRINT_TIMEOUT 1000 //超时时间 libusb_device_handle *dev_handle = NULL; //USB设备句柄 int usbInit() //USB初始化 { int ret = -1; libusb_init(NULL); dev_handle = libusb_open_device_with_vid_pid(NULL, 0x0403, 0x6001); //根据设备的VID和PID打开设备 if (dev_handle == NULL) { printf("open device failed\n"); libusb_exit(NULL); return -1; } ret = libusb_claim_interface(dev_handle, 0); //申请接口0 if (ret < 0) { printf("claim interface failed\n"); libusb_close(dev_handle); libusb_exit(NULL); return -1; } return 0; } int usbSend(unsigned char *data, int len) //USB发送数据 { int ret = -1; ret = libusb_bulk_transfer(dev_handle, 0x02, data, len, &len, FINGERPRINT_TIMEOUT); //使用bulk传输方式发送数据 if (ret < 0) { printf("send data failed\n"); return -1; } return len; } int usbRecv(unsigned char *data, int len) //USB接收数据 { int ret = -1; ret = libusb_bulk_transfer(dev_handle, 0x81, data, len, &len, FINGERPRINT_TIMEOUT); //使用bulk传输方式接收数据 if (ret < 0) { printf("recv data failed\n"); return -1; } return len; } int fingerprintVerify() //指纹验证 { unsigned char cmd[12] = {0}; //指令 unsigned char recvBuf[12] = {0}; //接收缓存 int ret = -1; //返回值 int i = 0; //计数器 int sum = 0; //校验和 cmd[0] = (FINGERPRINT_STARTCODE >> 8) & 0xff; //起始码高位 cmd[1] = FINGERPRINT_STARTCODE & 0xff; //起始码低位 cmd[2] = 0x00; //设备地址 cmd[3] = 0x03; //数据长度 cmd[4] = FINGERPRINT_COMMANDPACKET; //指令类型 cmd[5] = 0x01; //指令代码 cmd[6] = 0x00; //参数1 cmd[7] = 0x00; //参数2 sum = cmd[4] + cmd[5] + cmd[6] + cmd[7]; //计算校验和 cmd[8] = (sum >> 8) & 0xff; //校验和高位 cmd[9] = sum & 0xff; //校验和低位 cmd[10] = (FINGERPRINT_ENDDATAPACKET >> 8) & 0xff; //结束码高位 cmd[11] = FINGERPRINT_ENDDATAPACKET & 0xff; //结束码低位 ret = usbSend(cmd, sizeof(cmd)); //发送指令 if (ret < 0) { printf("usb send failed\n"); return -1; } ret = usbRecv(recvBuf, sizeof(recvBuf)); //接收应答 if (ret < 0) { printf("usb recv failed\n"); return -1; } if (recvBuf[0] != ((FINGERPRINT_STARTCODE >> 8) & 0xff) || recvBuf[1] != (FINGERPRINT_STARTCODE & 0xff)) //判断起始码 { printf("start code error\n"); return -1; } if (recvBuf[4] != FINGERPRINT_ACKPACKET) //判断是否为应答包 { printf("not ack packet\n"); return -1; } if (recvBuf[5] != 0x01) //判断指令代码 { printf("verification failed\n"); return -1; } return 0; } int main() { usbInit(); //初始化USB if (fingerprintVerify() == 0) //指纹验证 { printf("verification succeed\n"); } else { printf("verification failed\n"); } libusb_release_interface(dev_handle, 0); //释放接口0 libusb_close(dev_handle); //关闭设备 libusb_exit(NULL); //退出libusb return 0; } //以下为TTL通讯方式的控制程序 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <wiringPi.h> #define FINGERPRINT_STARTCODE 0xEF01 //起始码 #define FINGERPRINT_COMMANDPACKET 0x01 //指令包 #define FINGERPRINT_DATA_PACKET 0x02 //数据包 #define FINGERPRINT_ACKPACKET 0x07 //应答包 #define FINGERPRINT_ENDDATAPACKET 0x08 //结束数据包 #define FINGERPRINT_TIMEOUT 1000 //超时时间 const int PIN_TXD = 15; //发送引脚 const int PIN_RXD = 16; //接收引脚 void ttlInit() //TTL初始化 { wiringPiSetup(); pinMode(PIN_TXD, OUTPUT); pinMode(PIN_RXD, INPUT); } int ttlSend(unsigned char *data, int len) //TTL发送数据 { int i = 0; int j = 0; int sum = 0; digitalWrite(PIN_TXD, LOW); delay(10); for (i = 0; i < len; i++) //循环发送每个字节 { for (j = 0; j < 8; j++) //循环发送每个位 { digitalWrite(PIN_TXD, (data[i] >> j) & 0x01); //发送数据位 delayMicroseconds(100); } digitalWrite(PIN_TXD, HIGH); //发送停止位 delayMicroseconds(100); } return len; } int ttlRecv(unsigned char *data, int len) //TTL接收数据 { int i = 0; int j = 0; int ret = -1; int sum = 0; for (i = 0; i < len; i++) //循环接收每个字节 { for (j = 0; j < 8; j++) //循环接收每个位 { while (digitalRead(PIN_RXD) == LOW); //等待接收到起始位 delayMicroseconds(50); data[i] |= digitalRead(PIN_RXD) << j; //接收数据位 } while (digitalRead(PIN_RXD) == HIGH); //等待接收到停止位 delayMicroseconds(50); } return len; } int fingerprintVerify() //指纹验证 { unsigned char cmd[12] = {0}; //指令 unsigned char recvBuf[12] = {0}; //接收缓存 int ret = -1; //返回值 int i = 0; //计数器 int sum = 0; //校验和 cmd[0] = (FINGERPRINT_STARTCODE >> 8) & 0xff; //起始码高位 cmd[1] = FINGERPRINT_STARTCODE & 0xff; //起始码低位 cmd[2] = 0x00; //设备地址 cmd[3] = 0x03; //数据长度 cmd[4] = FINGERPRINT_COMMANDPACKET; //指令类型 cmd[5] = 0x01; //指令代码 cmd[6] = 0x00; //参数1 cmd[7] = 0x00; //参数2 sum = cmd[4] + cmd[5] + cmd[6] + cmd[7]; //计算校验和 cmd[8] = (sum >> 8) & 0xff; //校验和高位 cmd[9] = sum & 0xff; //校验和低位 cmd[10] = (FINGERPRINT_ENDDATAPACKET >> 8) & 0xff; //结束码高位 cmd[11] = FINGERPRINT_ENDDATAPACKET & 0xff; //结束码低位 ret = ttlSend(cmd, sizeof(cmd)); //发送指令 if (ret < 0) { printf("ttl send failed\n"); return -1; } ret = ttlRecv(recvBuf, sizeof(recvBuf)); //接收应答 if (ret < 0) { printf("ttl recv failed\n"); return -1; } if (recvBuf[0] != ((FINGERPRINT_STARTCODE >> 8) & 0xff) || recvBuf[1] != (FINGERPRINT_STARTCODE & 0xff)) //判断起始码 { printf("start code error\n"); return -1; } if (recvBuf[4] != FINGERPRINT_ACKPACKET) //判断是否为应答包 { printf("not ack packet\n"); return -1; } if (recvBuf[5] != 0x01) //判断指令代码 { printf("verification failed\n"); return -1; } return 0; } int main() { ttlInit(); //初始化TTL if (fingerprintVerify() == 0) //指纹验证 { printf("verification succeed\n"); } else { printf("verification failed\n"); } return 0; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值