Kubernetes部署MySQL
在部署MySQL前,先解释一下Deployment和StatefulSet差异
Deployment vs StatefulSet
Deployment
Deployment 主要用于部署无状态应用(Stateless Application)。它管理 Pod 的副本集,并支持滚动更新、回滚等功能。
特点:
-
无状态:Deployment 管理的 Pod 是完全相同的、可替代的。任何一个 Pod 都可以被另一个 Pod 替换,不会影响应用的整体功能。
-
随机命名和身份:Pod 的名称是随机的(例如:my-app-5d89b4f6d-xxxxx),没有固定的顺序。
-
共享存储:多个 Pod 可以共享同一个持久化存储(例如:同一个 PVC),但通常每个 Pod 不需要独立的存储。
-
服务发现和负载均衡:通过 Service 访问时,流量会被随机分配到任意一个 Pod。
-
扩缩容:可以轻松地增加或减少副本数,新 Pod 会替代旧 Pod,没有顺序要求。
-
使用场景:
-
Web 服务器(如 Nginx、Apache)
-
无状态 API 服务
-
任何不需要保存状态或状态存储在外部(如数据库、Redis)的应用
-
StatefulSet
StatefulSet 用于部署有状态应用(Stateful Application),每个 Pod 有唯一的、稳定的身份标识。
特点:
-
有状态:每个 Pod 有独立的、稳定的网络标识和存储。
-
有序部署和扩展:
-
当部署多个 Pod 时,它们会按顺序创建(从 0 到 N-1),并且会等待前一个 Pod 准备就绪后才会创建下一个。
-
缩容时,顺序相反(从 N-1 到 0)。
-
-
稳定的网络标识:
-
每个 Pod 都有一个稳定的主机名,格式为: - 。
-
例如:一个名为 web 的 StatefulSet 有三个副本,Pod 名称分别为 web-0、web-1、web-2。
-
每个 Pod 拥有一个稳定的 DNS 名称: . . .svc.cluster.local。
-
-
独立的存储:
-
每个 Pod 可以拥有独立的持久化存储(通过 VolumeClaimTemplate 为每个 Pod 创建独立的 PVC)。
-
当 Pod 被重新调度时,会挂载相同的存储,从而保持状态。
-
-
使用场景:
-
数据库(如 MySQL、PostgreSQL 集群)
-
分布式系统(如 Zookeeper、Etcd、Kafka)
-
任何需要持久化数据且每个实例有独立状态的应用
-
核心差异总结
| 特性 | Deployment | StatefulSet |
|---|---|---|
| 适用场景 | 无状态应用 | 有状态应用 |
| Pod身份 | 可互换、匿名 | 唯一、有序、稳定 |
| 网络标识 | 随机名称,不稳定 | 固定名称,有序(web-0, |
| 存储 | 共享存储,Pod间无区别 | 独立存储,每个Pod专用 |
| 部署策略 | 滚动更新,可并行 | 顺序部署/删除(0→1→2) |
| 服务发现 | 通过Service负载均衡 | 通过Headless |
部署MySQL
1. 创建命名空间
mysql-namespace.yaml
yaml
apiVersion: v1
kind: Namespace
metadata:
name: mysql
2. 创建本地存储PV和StorageClass
mysql-storage.yaml
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-local-pv
labels:
type: local
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /data/mysql # 节点上的本地目录
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- colima # 替换为实际节点名称
3. 创建MySQL配置ConfigMap
mysql-configmap.yaml
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
namespace: mysql
data:
custom.cnf: |
[mysqld]
default_authentication_plugin=mysql_native_password
skip-name-resolve
explicit_defaults_for_timestamp
max_connections=1000
innodb_buffer_pool_size=256M
innodb_log_file_size=128M
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
4. 创建MySQL密码Secret
mysql-secret.yaml
yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: mysql
type: Opaque
data:
root-password: cm9vdDEyMyE= # echo -n 'root123!' | base64
user-password: YXBwdXNlcjEyMyE= # echo -n 'appuser123!' | base64
5. 创建MySQL StatefulSet
mysql-statefulset.yaml
yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: mysql
spec:
serviceName: mysql
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
- name: MYSQL_DATABASE
value: "myapp"
- name: MYSQL_USER
value: "appuser"
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: user-password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
- name: mysql-config
mountPath: /etc/mysql/conf.d
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
exec:
command:
- sh
- -c
- "mysqladmin ping -u root -p${MYSQL_ROOT_PASSWORD}"
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
command:
- sh
- -c
- "mysqladmin ping -u root -p${MYSQL_ROOT_PASSWORD}"
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 1
volumes:
- name: mysql-config
configMap:
name: mysql-config
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "local-storage"
resources:
requests:
storage: 10Gi
6. 创建MySQL Service
mysql-service
yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: mysql
labels:
app: mysql
spec:
ports:
- port: 3306
targetPort: 3306
name: mysql
selector:
app: mysql
type: ClusterIP
---
# 可选:创建NodePort Service用于外部访问
apiVersion: v1
kind: Service
metadata:
name: mysql-external
namespace: mysql
spec:
type: NodePort
ports:
- port: 3306
targetPort: 3306
nodePort: 30306
selector:
app: mysql
7. 部署脚本
sh
kubsuclr apply -f mysql-namespace.yaml
kubectl apply -f mysql-storage.yaml
kubectl apply -f mysql-configmap.yaml
kubectl apply -f mysql-secret.yaml
kubectl apply -f mysql-statefulset.yaml
kubectl apply -f mysql-service.yaml
8. 测试验证
1. 查看部署情况
sh
# 查看Pod状态
kubectl get pods -n mysql -w
# 检查PV/PVC状态...
kubectl get pv,pvc -n mysql
# 检查Service...
kubectl get svc -n mysql
# 进入mysql
kubectl exec -it -n mysql mysql-0 -- mysql -u root -p

2. 验证插入数据重启后数据不丢失
- 创建表并保存数据
sql
CREATE TABLE IF NOT EXISTS `users` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` VARCHAR(50) NOT NULL COMMENT '用户名',
`password` CHAR(60) NOT NULL COMMENT 'Bcrypt加密密码', -- 固定60字符长度
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`))
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
COMMENT='用户信息表';
insert into users(username,`password`, created_at, updated_at) values('wilson', '123456', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP())
select * from users;
- 重启k8s服务
sh
# 删除mysql pod
kubectl delete -f mysql-statefulset.yaml
# 重新部署mysql statefulset
kubectl apply -f mysql-statefulset.yaml

- 验证数据
sh
# 重新进入
kubectl exec -it -n mysql mysql-0 -- mysql -u root -p
use myapp;
select * from users;

重要说明
-
节点选择:需要根据实际环境修改PV中的节点名称
-
目录权限:确保节点上的本地目录有正确的读写权限
-
数据持久性:本地存储的数据不会在Pod重新调度时自动迁移
-
备份策略:重要数据务必建立定期备份机制
-
资源限制:根据实际需求调整CPU和内存限制
这个配置提供了一个生产可用的单机MySQL部署方案,包含了健康检查、资源配置、数据持久化等关键功能。
引用
https://github.com/WilsonPan/java-developer