尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Kubernetes离线部署MySQL 5.7全攻略:镜像导入、持久化与避坑指南

发布时间:2026/9/29 4:17:07

资讯中心
01
ARTICLE

Kubernetes离线部署MySQL 5.7全攻略:镜像导入、持久化与避坑指南

Kubernetes离线部署MySQL 5.7全攻略:镜像导入、持久化与避坑指南
简介面向需要在离线或内网环境部署 MySQL 5.7 的 Kubernetes 运维和开发人员这份资源把镜像加载与 YAML 编排整合成可直接使用的完整交付包省去手工编写清单和拉取镜像的流程。压缩包共 7 个文件包含 2 个 tar 镜像包、4 个 YAML 资源清单和 1 个说明文档整体大小 280.98MB镜像按 load 方式导入后即可直接创建 Deployment、Service、持久化卷与存储声明。内容围绕 mysql-5.7 的部署闭环展开既给出数据库实例和访问入口也明确了数据目录挂载方案适合离线集群快速落地也可用于理解 Kubernetes 下 Stateful 应用的存储与网络配置。已有 1095 人学习下载对需要验证 Kubernetes 存储、负载均衡和中间件迁移的读者较有参考价值。1. Kubernetes 部署 MySQL 5.7 全套离线包为什么离线环境比在线环境更容易翻车[init] using kubernetes version: v1.26.0 [preflight] running pre-flight checks这是很多人在内网装完 K8s 集群后看到的第一段输出。集群起来了接下来第一件事往往就是“给我搞个 MySQL 来用”。但真到了离线环境你才会发现在线环境那套docker pull mysql:5.7的流程完全走不通——镜像拉不下来、Node 节点没外网、仓库没配置连apt install mysql-server都要半天。这篇实战笔记要拆的是一份能在 Kubernetes 里离线部署 MySQL 5.7 的全量落地包包含镜像、编排 YAML、初始化脚本和常用配置。适用人群是正在做内网交付、kubesphere 安装 mysql 失败、或刚从 Docker 迁移到 K8s 还没摸透持久化的运维和开发。它解决的核心问题就三件事镜像怎么进集群、数据怎么不丢、连不上时怎么排查。2. 镜像离线入库docker save 与 ctr 导入的完整路径离线包的核心资产是镜像。不管你在哪个 Node 上装 MySQL只要镜像没进到容器运行时里后面全是白搭。先说整体思路在能联网的机器上docker pull出 MySQL 5.7 镜像docker save成 tar 包传到内网 Node 后用docker load或ctr images import导入。这里有一条很容易被忽略的边界——如果你的 K8s 用的是 containerd 运行时docker load进去的镜像是不会被 Kubelet 自动识别的。2.1 先确认容器运行时别把镜像装错了仓库K8s 1.26 及之后版本绝大多数发行版默认用 containerd 作为运行时。你在这个 Node 上直接敲docker images能看到镜像不代表kubectl run能拉到。因为 docker 和 containerd 各自维护独立镜像存储。正确做法是先检查kubelet的运行时配置cat /var/lib/kubelet/config.yaml | grep containerRuntime cat /etc/containerd/config.toml | grep disabled_plugins如果disabled_plugins里有docker说明 containerd 是主要运行时。那么离线导入就得走ctr。我一般建议两种方式都做双保险# 在联网机器上打镜像 docker pull mysql:5.7 docker save -o mysql-5.7-offline.tar mysql:5.7 # 传到内网 Node 后用 docker 方式导入 docker load -i mysql-5.7-offline.tar # 再用 containerd 方式导入适配 Kubelet 调度 ctr -n k8s.io images import mysql-5.7-offline.tar每次导入完成都要验证镜像确实出现在k8s.io命名空间里ctr -n k8s.io images list | grep mysql提示-n k8s.io这个命名空间是 Kubelet 拉镜像时唯一会去翻的空间。写错或漏了Deployment 启动时会一直报image pull failed。2.2 配置 imagePullPolicy离线环境不能还是 IfNotPresent 就将就spec: containers: - name: mysql image: mysql:5.7 imagePullPolicy: IfNotPresentIfNotPresent表示本地有镜像就用本地不触发远程拉取。离线环境里不要设成Always否则每次 Pod 重建都会去访问不存在的仓库镜像名如果带私有仓库地址还会直接卡在ErrImagePull里。这里的参数看起来只是三行 YAML实际决定你有没有后悔药吃。另一个常见情况镜像在 A 节点导入了但 Deployment 被调度到 B 节点。解决办法是给两个 Node 都执行一遍 load 和 import或者像我在生产环境里用的方式——把镜像放到内网自建 Registry 再通过imagePullSecrets配置认证。离线包通常自带单节点镜像所以除非你有多节点读写需求否则逐台导入更省事。3. 编写 Deployment 与持久化不要让容器重启就丢数据离线部署最坑的不是“起不来”而是“起来了但一重启数据没了”。MySQL 是状态化应用容器文件系统随 Pod 一起销毁。所以必须把数据目录映射到 PV/PVC 里。这里需要结合离线包里的mysql-deployment.yaml讲清楚几个关键参数。3.1 PVC 声明与访问模式为什么 MySQL 不能用 ReadWriteManyapiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-data spec: accessModes: - ReadWriteOnce resources: requests: storage: 20GiMySQL 的正常工作模式是单实例读写数据目录。ReadWriteOnce表示一块卷只能被一个节点挂载读写ReadWriteMany适合 NFS 多读场景但不适合 MySQL 这种对 IO 延迟敏感的应用。如果离线包里给了 NFS 类型的 PV你可以保留ReadWriteOnce不要让 NFS 服务器参与多节点同时写否则会有锁竞争和数据损坏风险。3.2 配置 StatefulSet 场景下的存储到底应该用 Deployment 还是 StatefulSet单机 MySQL 用 Deployment 足够但如果你准备把离线包扩展到主从复制建议提前切到 StatefulSetapiVersion: apps/v1 kind: StatefulSet metadata: name: mysql spec: serviceName: mysql-headless replicas: 1 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:5.7 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: root-password volumeMounts: - name: mysql-storage mountPath: /var/lib/mysql ports: - containerPort: 3306 volumeClaimTemplates: - metadata: name: mysql-storage spec: accessModes: - ReadWriteOnce resources: requests: storage: 20GivolumeClaimTemplates是 StatefulSet 的专属特性每个副本自动生成独立 PVC名字后缀带上序号。单副本时它的作用和 Deployment 静态 PVC 没差别但好处是以后想扩主从只需要加副本数。不过注意StatefulSet 扩副本不等于 MySQL 主从自动建立那是另外一套初始化逻辑后面第五节会讲。3.3 Node 亲和与调度避免 MySQL 被调度到没有该镜像的节点spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - node01如果你的离线包只在 node01 上导入了镜像最好直接指定调度节点否则 Kubelet 发现镜像不存在后会到远端去拉离线环境就是无限CrashLoopBackOff。这个配置看起来只是调度策略但其实能帮你提前避开大量“玄学”排障时间。以上三个阶段走完后MySQL 容器就能稳定启动了。但实际你还会碰到初始化脚本、时区、字符集、配置参数这些“看不见的坑”它们共同决定这个数据库到底能不能被业务正常使用。4. 初始化 MySQL 5.7环境变量、配置文件与字符集MySQL 官方镜像的docker-entrypoint.sh会根据环境变量完成初始化、创建用户和库。离线包一般会把一套初始化配置放在mysql-init目录里这部分价值很大因为手工一条条去执行太容易翻车。4.1 用 Secret 管理密码别把密码写死在 YAML 里kubectl create secret generic mysql-secret \ --from-literalroot-passwordMyStr0ngPassw0rd \ --from-literalreplica-passwordReplicaPassw0rd \ -n default生产环境禁止在 YAML 里明文写MYSQL_ROOT_PASSWORD。离线包如果自带 Secret 文件也建议你重新生成一次密钥不要沿用包里的默认值。这个动作虽然只有几分钟但它决定了数据库暴露后是不是裸奔。4.2 业务默认库和用户在初始化脚本里一次性配好离线包里通常带init.sql内容长得像这样CREATE DATABASE IF NOT EXISTS app_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER appuser% IDENTIFIED BY AppUserPassw0rd; GRANT ALL PRIVILEGES ON app_db.* TO appuser%; FLUSH PRIVILEGES;这个init.sql要挂载到/docker-entrypoint-initdb.d/下官方镜像首次启动时会自动执行spec: containers: - name: mysql image: mysql:5.7 volumeMounts: - name: init-sql mountPath: /docker-entrypoint-initdb.d提示docker-entrypoint-initdb.d下的脚本只在数据目录为空时执行。如果 PVC 里已经有了旧数据初始化脚本不会跑第二次。想重新初始化就把 PVC 删掉再重建但这个操作等价于“数据全清”操作前务必确认。4.3 配置文件 my.cnf时区、字符集、内存参数一次定好MySQL 默认字符集是latin1业务中存中文容易乱码所以离线包一般自带my.cnf[mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_general_ci default-time-zone08:00 max_connections500 innodb_buffer_pool_size1G lower_case_table_names1挂载方式spec: containers: - name: mysql image: mysql:5.7 volumeMounts: - name: mysql-conf mountPath: /etc/mysql/conf.d/my.cnf subPath: my.cnfsubPath: my.cnf这一行非常关键不加它Kubernetes 会把整个my.cnf文件当成目录挂载进去容器一启动就报错终止在No such file or directory。innodb_buffer_pool_size的默认值只有 128M对生产库来说太小离线包如果给的是 1G 配置说明面向的是小型业务库如果内存较大的机器比如 16G改成 4G 后性能提升非常明显。这是“参数设对了立刻见效”的典型例子。5. 避坑排查离线部署 MySQL 最常见的 5 个翻车现场这章直接写我实际排障中反复遇到的 5 个问题每一条都是现象、原因、解决三步走。建议存下来以后遇到同类问题直接对照。5.1 现象Pod 一直 CrashLoopBackOff日志提示mysqld: Cant create/write to file /var/lib/mysql/原因PV/PVC 挂载目录权限不对。MySQL 镜像内部用mysql用户uid 999而 NFS 或 hostPath 目录默认属于 root导致 mysql 进程无权限写数据文件。解决在宿主机上把挂载目录授权给 999 用户chown -R 999:999 /data/mysql如果是 NFS需要在 NFS 服务端同步修改并在挂载参数里加nfsvers4然后重启 Pod 验证。5.2 现象连接 MySQL 报ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock原因客户端用的是 socket 方式连接但 Pod 里没有 socket 文件或者容器里根本没搭mysql客户端。解决在 K8s 环境里不要通过 NodePort 去连宿主机的 socket。应该走 TCPkubectl exec -it mysql-0 -- mysql -h127.0.0.1 -uroot -p如果业务侧连接的是 Service先确认 Service 类型apiVersion: v1 kind: Service metadata: name: mysql spec: selector: app: mysql ports: - port: 3306 targetPort: 3306 clusterIP: NoneclusterIP: None表示 Headless Service适合 StatefulSet 的稳定网络标识但如果你从集群外部访问需要改成NodePort并检查安全组是否放行 3xxxx 端口。5.3 现象NodePort 在外网访问不通从集群内telnet端口正常原因K8s 集群 NodePort 端口范围是 30000-32767NodePort 本身不通多半是宿主机防火墙或安全组拦截。解决先看 Service 分配的端口然后在宿主机上执行netstat -tlnp | grep 3xxxxx iptables -L -n | grep 3306如果是防火墙挡住执行systemctl stop firewalld或按规则放行。如果是云服务器登录控制台安全组放行对应端口。5.4 现象数据写入正常但中文全部乱码原因初始化库的字符集是utf8mb4但客户端连接字符集没有设置或者my.cnf里漏配了character-set-server。解决连接时指定字符集SET NAMES utf8mb4;同时确认数据库、表、连接三级字符集都是utf8mb4。在建表时显式声明CREATE TABLE t1 (name VARCHAR(50) CHARACTER SET utf8mb4);5.5 现象Pod 起来后业务写入极慢top显示 mysqld CPU 占用高原因max_connections设太大或没有连接池业务短连接风暴把 MySQL 打挂。K8s 场景下每个 Pod 的 IP 变化快连接池参数要重点调。解决在业务连接串里加上jdbc:mysql://mysql-headless:3306/app_db?connectTimeout3000socketTimeout10000useSSLfalseserverTimezoneAsia/Shanghai另外把my.cnf的max_connections调低到 200-300同时开启skip-name-resolve减少反向 DNS 解析。离线包里如果已经带这份配置不用再动。6. 验证 MySQL 部署与进阶健康检查、数据恢复与主从扩展部署完成不代表交付完成特别是离线包这种交付物验收必须有据可查。我最常用的验证链是一行命令加上一个真实事务测试然后跑一次备份最后再验证主从复制。这套流程大概十五分钟能走完能把“能用”升级成“敢上线”。6.1 用就绪探针保证流量只给健康实例spec: containers: - name: mysql image: mysql:5.7 readinessProbe: exec: command: - sh - -c - mysqladmin ping -uroot -p$MYSQL_ROOT_PASSWORD --silent initialDelaySeconds: 30 periodSeconds: 10mysqladmin ping成功返回说明 MySQL 已经接受连接这时 Service 才会把流量转发给这个 Pod。periodSeconds: 10是探针间隔initialDelaySeconds: 30给初始化预留时间。注意探针里不能直接写死密码要用环境变量引用。6.2 备份与恢复验证离线交付最怕的就是“没测过恢复的备份等于没备份”。在 Pod 里执行全量备份kubectl exec -it mysql-0 -- mysqldump -uroot -p --all-databases --single-transaction --routines --triggers /data/backup.sql验证恢复时临时起一个空库 Pod把backup.sql导进去mysql -uroot -p /data/backup.sql然后跑一条业务 SQL 确认数据在SELECT COUNT(*) FROM ...。每次都走这一遍心里才有底。6.3 MySQL 5.7 主从复制的离线扩展如果离线包里带了主从脚本那扩展方式类似这样先让主库开 binlog创建复制账号GRANT REPLICATION SLAVE ON *.* TO repl% IDENTIFIED BY ReplicaPassw0rd; FLUSH PRIVILEGES;从库的 StatefulSet 里加上env: - name: MYSQL_REPLICA_USER value: repl - name: MYSQL_REPLICA_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: replica-password官方镜像检测到MYSQL_REPLICA_USER后会自动执行CHANGE MASTER TO和START SLAVE。启动后验证kubectl exec -it mysql-1 -- mysql -uroot -p -e SHOW SLAVE STATUS\G | grep -E Slave_IO_Running|Slave_SQL_Running两条都显示Yes主从才算真正立住。这里有个小习惯我会把Slave_IO_Running: Connecting这种状态记录在备忘录里因为十次有九次是端口没放行或复制账号密码有误。从那以后每次在内网交付完 MySQL 离线包我都会强制走一遍“先探活、再备份、最后验证主从状态”的流程。这套流程看起来多花了十几分钟但从来不会在客户现场翻车。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。