第 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 Volume | Docker 管理的存储 | 开发/测试 |
| 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-oracle | Oracle Linux 基础 |
percona:8.0 | Percona Server |
mariadb:11 | MariaDB |
# 多架构镜像
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"