强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第 18 章:容器化部署

第 18 章:容器化部署

Docker 和 Kubernetes 已成为现代 MySQL 部署的主流方式。本章详解容器化最佳实践。


18.1 Docker 部署 MySQL

18.1.1 快速启动

docker run -d \
  --name mysql8 \
  -e MYSQL_ROOT_PASSWORD=MyStr0ng!Pass \
  -p 3306:3306 \
  mysql:8.4

18.1.2 生产级 Docker 部署

docker run -d \
  --name mysql8 \
  --restart=unless-stopped \
  -e MYSQL_ROOT_PASSWORD=MyStr0ng!Pass \
  -e MYSQL_DATABASE=myapp \
  -e MYSQL_USER=appuser \
  -e MYSQL_PASSWORD=App!Pass123 \
  -p 3306:3306 \
  -v /data/mysql/data:/var/lib/mysql \
  -v /data/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf:ro \
  -v /data/mysql/log:/var/log/mysql \
  -v /data/mysql/init:/docker-entrypoint-initdb.d:ro \
  --ulimit nofile=65535:65535 \
  --memory=8g \
  --cpus=4 \
  mysql:8.4 \
  --default-authentication-plugin=caching_sha2_password \
  --character-set-server=utf8mb4 \
  --collation-server=utf8mb4_0900_ai_ci

18.1.3 自定义配置文件

# conf/my.cnf
[mysqld]
default-time-zone = '+08:00'
character-set-server = utf8mb4
collation-server = utf8mb4_0900_ai_ci
innodb_buffer_pool_size = 4G
max_connections = 300
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1

[client]
default-character-set = utf8mb4

18.1.4 数据初始化脚本

-- init/01-schema.sql
USE myapp;

CREATE TABLE IF NOT EXISTS users (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

18.2 Docker Compose 部署

18.2.1 单机部署

# docker-compose.yml
version: '3.8'

services:
  mysql:
    image: mysql:8.4
    container_name: mysql8
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: myapp
      MYSQL_USER: appuser
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./conf/my.cnf:/etc/mysql/conf.d/my.cnf:ro
      - ./init:/docker-entrypoint-initdb.d:ro
      - mysql_log:/var/log/mysql
    command: >
      --default-authentication-plugin=caching_sha2_password
      --innodb-buffer-pool-size=4G
      --max-connections=300
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - backend

  adminer:
    image: adminer
    container_name: adminer
    restart: unless-stopped
    ports:
      - "8080:8080"
    depends_on:
      mysql:
        condition: service_healthy
    networks:
      - backend

volumes:
  mysql_data:
  mysql_log:

networks:
  backend:

18.2.2 主从复制部署

# docker-compose-replication.yml
version: '3.8'

services:
  mysql-master:
    image: mysql:8.4
    container_name: mysql-master
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
    ports:
      - "3306:3306"
    volumes:
      - master_data:/var/lib/mysql
      - ./conf/master.cnf:/etc/mysql/conf.d/my.cnf:ro
    networks:
      - mysql-net

  mysql-slave:
    image: mysql:8.4
    container_name: mysql-slave
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
    ports:
      - "3307:3306"
    volumes:
      - slave_data:/var/lib/mysql
      - ./conf/slave.cnf:/etc/mysql/conf.d/my.cnf:ro
    depends_on:
      - mysql-master
    networks:
      - mysql-net

volumes:
  master_data:
  slave_data:

networks:
  mysql-net:
# conf/master.cnf
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1

# conf/slave.cnf
[mysqld]
server-id = 2
relay-log = relay-bin
read-only = ON

18.3 Kubernetes 部署

18.3.1 基础部署

# mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.4
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        resources:
          requests:
            memory: "4Gi"
            cpu: "2"
          limits:
            memory: "8Gi"
            cpu: "4"
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
        - name: mysql-config
          mountPath: /etc/mysql/conf.d
      volumes:
      - name: mysql-data
        persistentVolumeClaim:
          claimName: mysql-pvc
      - name: mysql-config
        configMap:
          name: mysql-config
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
spec:
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: 3306
  type: ClusterIP
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Gi
  storageClassName: standard

18.3.2 StatefulSet 部署(推荐生产使用)

# mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.4
        ports:
        - containerPort: 3306
          name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: mysql-data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 100Gi

18.3.3 MySQL Operator(推荐)

# 使用 MySQL Operator(Oracle 官方或 Percona)
apiVersion: mysql.oracle.com/v2
kind: InnoDBCluster
metadata:
  name: mycluster
spec:
  secretName: mysql-secret
  instances: 3
  router:
    instances: 2
  datadirVolumeClaimTemplate:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 100Gi

18.4 容器化注意事项

18.4.1 数据持久化

方式说明推荐
Docker VolumeDocker 管理的存储开发/测试
Bind Mount映射宿主机目录简单生产
PVC (K8s)持久化存储卷K8s 生产

⚠️ 绝对不要将 MySQL 数据存储在容器的可写层(容器删除数据丢失)。

18.4.2 资源限制

# Docker
docker run --memory=8g --cpus=4 mysql:8.4

# K8s
resources:
  requests:
    memory: "4Gi"  # 保证的最小资源
    cpu: "2"
  limits:
    memory: "8Gi"  # 最大可用资源
    cpu: "4"

18.4.3 健康检查

# K8s 健康检查
livenessProbe:
  exec:
    command:
    - mysqladmin
    - ping
    - -h
    - localhost
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  exec:
    command:
    - mysql
    - -h
    - localhost
    - -u
    - root
    - -p${MYSQL_ROOT_PASSWORD}
    - -e
    - "SELECT 1"
  initialDelaySeconds: 5
  periodSeconds: 5

18.5 Docker 镜像选择

镜像说明
mysql:8.4官方镜像
mysql:8.4-oracleOracle Linux 基础
percona:8.0Percona Server
mariadb:11MariaDB
# 多架构镜像
docker pull --platform linux/amd64 mysql:8.4
docker pull --platform linux/arm64 mysql:8.4

业务场景

场景 1:开发环境一键搭建

# docker-compose.dev.yml
version: '3.8'
services:
  mysql:
    image: mysql:8.4
    environment:
      MYSQL_ROOT_PASSWORD: devpass
      MYSQL_DATABASE: myapp_dev
    ports:
      - "3306:3306"
    volumes:
      - ./init:/docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 3s
      retries: 10

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

扩展阅读