写在最前

根据你自身的需求选择任意一种部署方式,如果是新人可以先看之前的篇章来完成基础环境的安装与配置。

1. 前置要求

  1. 二进制部署任意版本docker

2. docker 部署

https://hub.docker.com/_/mysq

mkdir -p /data/mysql8/conf /data/mysql8/data

docker run -d \
  --privileged=true \
  --name mysql8 \
  --restart=always \
  -p 3306:3306 \
  -v /data/mysql8/conf:/etc/mysql/conf.d \
  -v /data/mysql8/data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -e MYSQL_DATABASE=test-db \
  mysql:8.0.20 \
  --character-set-server=utf8mb4 \
  --collation-server=utf8mb4_bin

2.3 备份与还原

2.3.1 只读账号

创建只读的账号来进行操作,禁止远程连接,禁止弱密码,禁止使用root账号。

推荐使用在线随机密码生成器或者uuid作为密码都是不错的选择。

https://uutool.cn/password/

-- 创建新用户,替换 'backup_user' 和 'your_password' 为你想要的用户名和密码
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY '实际密码';
-- 给用户赋予所有数据库的只读权限
GRANT SELECT ON *.* TO 'backup_user'@'localhost';
-- 刷新权限
FLUSH PRIVILEGES;

3.2.2 备份脚本

原理是执行 docker exec 获取所有的库名再循环它使用 docker exec 导出该库的所有数据得到sql文件再打包出去。

每次执行判断将该目录下的超过xx天后的目录给删除掉。只保留xx天的数据。

所有的日志都拼接在 LOG_OUTPUT 变量里,后续可以拓展将此变量的内容通过邮件发送出去,每天都能看到可控的信息。

image-gcmq.png

#!/bin/bash

# MySQL 容器名称
CONTAINER_NAME="mysql8"

# 备份用户和密码
MYSQL_USER="backup_user"
MYSQL_PASSWORD="实际密码"

# 保留的天数,例如保留 7 天
RETENTION_DAYS=7

# 初始化日志变量
LOG_OUTPUT=""

# 获取当前日期并创建对应的文件夹,日期格式为 "2024-09-20"
DATE=$(date +"%Y-%m-%d")
BACKUP_DIR="/backup/mysql/$DATE"
mkdir -p "$BACKUP_DIR"

# 记录备份开始
LOG_OUTPUT+="备份开始...\n"

# 获取所有数据库名
DATABASES=$(docker exec "$CONTAINER_NAME" sh -c "exec mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -e 'SHOW DATABASES;'" | grep -Ev "(Database|information_schema|performance_schema|mysql|sys)")

# 循环导出每个数据库并打包为 .tar.gz
for DB in $DATABASES; do
    # 导出数据库为 SQL 文件
    docker exec "$CONTAINER_NAME" sh -c "exec mysqldump --single-transaction -u$MYSQL_USER -p$MYSQL_PASSWORD $DB" > "$BACKUP_DIR/$DB.sql"
    
    # 记录导出日志
    # LOG_OUTPUT+="数据库 $DB 已导出为 $DB.sql。\n"
    
    # 将 SQL 文件压缩为 .tar.gz
    tar -czf "$BACKUP_DIR/$DB.tar.gz" -C "$BACKUP_DIR" "$DB.sql"
    
    # 记录压缩日志
    LOG_OUTPUT+="数据库 $DB 已压缩为 $DB.tar.gz。\n"
    
    # 删除原始的 SQL 文件
    rm "$BACKUP_DIR/$DB.sql"
done

# 记录备份完成
LOG_OUTPUT+="备份完成,存储在 $BACKUP_DIR 中。\n"

# 获取当前日期的时间戳
CURRENT_TIMESTAMP=$(date +%s)

# 记录删除日志
DELETE_LOG=""

# 查找超过保留天数的备份目录并进行删除
for DIR in /backup/mysql/*; do
    # 从目录名称中提取日期
    BACKUP_DATE=$(basename "$DIR")

    # 将目录名称的日期转换为时间戳
    BACKUP_TIMESTAMP=$(date -d "$BACKUP_DATE" +%s 2>/dev/null)

    # 检查日期格式是否正确(无效日期会被忽略)
    if [ -n "$BACKUP_TIMESTAMP" ]; then
        # 计算日期差
        DIFF_DAYS=$(( (CURRENT_TIMESTAMP - BACKUP_TIMESTAMP) / (60*60*24) ))

        # 如果超过保留天数,进行删除并记录删除日志
        if [ "$DIFF_DAYS" -gt "$RETENTION_DAYS" ]; then
            DELETE_LOG+="删除目录: $DIR (超过 $DIFF_DAYS 天)\n"
            rm -rf "$DIR"
        fi
    fi
done

# 如果有删除操作,记录删除日志
if [ -n "$DELETE_LOG" ]; then
    LOG_OUTPUT+="以下目录已被删除:\n$DELETE_LOG"
else
    LOG_OUTPUT+="没有超过 $RETENTION_DAYS 天的备份被删除。\n"
fi

# 最终输出所有日志
echo -e "$LOG_OUTPUT"

3.2.3 还原脚本

3. Kubernetes 部署

namespace 我统一设置为 basic ,自行修改存储类相关配置

3.1 secret

MYSQL_ROOT_PASSWORD 值是123456

apiVersion: v1
kind: Secret
metadata:
  namespace: basic
  labels: {}
  name: mysql-secret
type: Opaque
spec:
  template:
    metadata:
      labels: {}
data:
  MYSQL_ROOT_PASSWORD: MTIzNDU2

3.2 configmap

修改全局字符集

kind: ConfigMap
apiVersion: v1
metadata:
  name: mysql8-conf
  namespace: basic
  annotations:
    kubesphere.io/creator: admin
data:
  my.cnf: |
    [client]
    default-character-set=utf8mb4

    [mysql]
    default-character-set=utf8mb4

    [mysqld]
    init_connect='SET collation_connection = utf8mb4_bin'
    init_connect='SET NAMES utf8mb4'
    character_set_server = utf8mb4
    collation_server = utf8mb4_bin
    skip-character-set-client-handshake
    skip-name-resolve

3.3 deployment

kind: Deployment
apiVersion: apps/v1
metadata:
  name: mysql8
  namespace: basic
  labels:
    app: mysql8
  annotations:
    deployment.kubernetes.io/revision: '6'
    kubesphere.io/creator: admin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql8
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: mysql8
      annotations:
        kubesphere.io/creator: admin
        kubesphere.io/imagepullsecrets: '{}'
        kubesphere.io/restartedAt: '2024-09-18T07:33:09.888Z'
        logging.kubesphere.io/logsidecar-config: '{}'
    spec:
      volumes:
        - name: host-time
          hostPath:
            path: /etc/localtime
            type: ''
        - name: mysql8-data
          persistentVolumeClaim:
            claimName: mysql8-data
        - name: mysql8-conf
          configMap:
            name: mysql8-conf
            defaultMode: 420
      containers:
        - name: container-6m2r7f
          image: 'mysql:8.0.20'
          ports:
            - name: http-3306
              containerPort: 3306
              protocol: TCP
            - name: http-33060
              containerPort: 33060
              protocol: TCP
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: MYSQL_ROOT_PASSWORD
          resources: {}
          volumeMounts:
            - name: host-time
              readOnly: true
              mountPath: /etc/localtime
            - name: mysql8-data
              mountPath: /var/lib/mysql
            - name: mysql8-conf
              readOnly: true
              mountPath: /etc/mysql/conf.d
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      dnsPolicy: ClusterFirst
      serviceAccountName: default
      serviceAccount: default
      securityContext: {}
      schedulerName: default-scheduler
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600

3.4 service

使用nodeport方式连接

kind: Service
apiVersion: v1
metadata:
  name: mysql8
  namespace: basic
  labels:
    app: mysql8
  annotations:
    kubesphere.io/creator: admin
spec:
  ports:
    - name: http-3306
      protocol: TCP
      port: 3306
      targetPort: 3306
      nodePort: 31205
  selector:
    app: mysql8
  type: NodePort
  sessionAffinity: None
  externalTrafficPolicy: Cluster
  ipFamilies:
    - IPv4
  ipFamilyPolicy: SingleStack
  internalTrafficPolicy: Cluster

3.5 备份与还原

3.5.1 只读账号

3.5.2 备份脚本

3.5.3 还原脚本

4. 异常处理

4.1 错误1

如果出现 --initialize specified but the data directory has files in it. Aborting. 异常,请尝试加上 --privileged=true 以超级权限运行。

2.2 错误2

最新的MySQL模块并未完全支持MySQL 8.0的caching_sha2_password加密方式,而MySQL 8.0中默认仍然是caching_sha2_password加密方式,因此用户认证不通过了。

这算问题也可以不算问题,在最新的navicat17版本中是能够正常连接的。

图片-kzki.png

注意,我是为了个人开发便捷将root设置为允许远程连接,生产禁止此操作。

# 1. 进入容器
docker exec -it mysql8 /bin/bash
# 2. 登录mysql
mysql -u root -p
# 3. 更新root密码规则为 mysql_native_password ,允许root远程连接,并且设密码为123456
alter user 'root'@'%' identified with mysql_native_password by '123456';
# 4. 刷新权限
flush privileges;

5. 操作总结

部署5.x很简单,但是为啥部署8.x会那么复杂多坑,可能是他的镜像做的有问题吧启动起来还不能直接使用晕死了。