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
apiVersion: v1 kind: Namespace metadata: name: mysql
2. 创建本地存储PV和StorageClass
mysql-storage.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
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
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
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
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. 部署脚本
kubsucl 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. 查看部署情况
# 查看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. 验证插入数据重启后数据不丢失
- 创建表并保存数据
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服务
# 删除mysql pod kubectl delete -f mysql-statefulset.yaml # 重新部署mysql statefulset kubectl apply -f mysql-statefulset.yaml

- 验证数据
# 重新进入 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