news 2026/10/12 14:59:59

Kubernetes 离线部署 MySQL 5.7 全量包实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kubernetes 离线部署 MySQL 5.7 全量包实战指南

简介:这份资源面向需要在 Kubernetes 集群中部署 MySQL 的运维与开发人员,提供一套可直接落地的离线部署方案,重点解决内网或无外网环境下镜像拉取困难、YAML 配置零散的问题。压缩包共 7 个文件,约 280.98MB,以 4 个 yaml 清单、2 个 tar 镜像包和 1 个 txt 说明为主:yaml 覆盖 Deployment、Service、持久化存储与存储卷声明等核心对象,tar 包内含 MySQL 5.7 镜像,可通过 load 方式导入本地镜像仓库,txt 则记录部署要点。资源已积累 1095 人学习下载,说明其在实际生产与测试场景中具备一定参考价值。读者拿到后可直接复用整套编排文件,快速完成镜像导入、存储挂载与服务暴露,省去从零编写清单和排查镜像来源的时间,适合作为 Kubernetes 上部署有状态数据库的入门与实战模板。

1. 内网 Kubernetes 里装 MySQL 5.7:为什么离线包才是真正的拦路虎

很多团队第一次在 Kubernetes 上部署 MySQL 5.7,都是在能连外网的环境里跑通的:kubectl apply一个 StatefulSet,镜像从 Docker Hub 拉,几分钟就起来了。可一旦搬到生产内网、金融机房或者边缘节点,情况立刻反转——镜像拉不动,ImagePullBackOff挂在那里,Pod 永远起不来。这时候你才会意识到,kubernetes 部署 mysql 全量离线包(mysql5.7)这件事,难点从来不是 YAML 写得好不好,而是所有依赖能不能在断网环境里自给自足。

这篇笔记面向的是需要在隔离网络里交付 MySQL 5.7 的一线工程师:镜像怎么打包、依赖怎么带进去、StatefulSet 怎么配、初始化脚本怎么塞、出问题怎么查。我会按「先讲清离线包该包含什么,再一步步落地,最后把踩过的坑摊开」的顺序写。如果你手上正好有一台能联网的跳板机和一套内网 K8s 集群,跟着走就能复现。

2. 离线包到底要装什么:镜像、依赖与目录规划

2.1 为什么不能只docker save一个 mysql:5.7

新手最容易犯的错,是以为离线包就是docker save mysql:5.7 -o mysql.tar完事。真到内网docker load之后,Pod 还是起不来,因为 MySQL 5.7 官方镜像在初始化阶段会依赖一批基础层和工具,尤其是当你用了自定义my.cnf、初始化 SQL、或者基于官方镜像做了二次构建时,这些层必须一起带走。

常见做法是:在联网机器上先把最终要用的镜像完整构建好,再整包导出。判断「完整」的标准很简单——在联网机器上用docker run能正常初始化并启动,导出的 tar 在内网docker load后docker images能看到同样的 IMAGE ID,且docker history的层数一致。

我一般会把离线包拆成三块:镜像 tar 包、K8s 资源清单、初始化数据(SQL 脚本和配置文件)。三块分开管理,出问题时能快速定位是哪一块没带全。

2.2 离线包的目录结构设计

一个能复用的离线包目录,我通常这样组织:

mysql57-offline/ ├── images/ │ └── mysql-5.7.44.tar # docker save 导出的镜像 ├── manifests/ │ ├── namespace.yaml │ ├── secret.yaml # root 密码、普通用户密码 │ ├── configmap.yaml # my.cnf 片段 │ ├── service.yaml # Headless Service │ └── statefulset.yaml # 核心工作负载 ├── init/ │ └── init.sql # 建库建表脚本 └── load.sh # 一键导入脚本

这个结构的好处是:images/只负责镜像搬运,manifests/只负责 K8s 编排,init/只负责数据初始化。任何一块要更新,不影响其他两块。load.sh把导入动作固化下来,避免每次手敲命令漏步骤。

提示:镜像 tar 包体积通常在 400MB 到 600MB 之间,取决于基础层是否被复用。传输前先确认内网节点的磁盘余量,/var/lib/docker或 containerd 的存储目录至少留出两倍空间。

2.3 镜像版本与 tag 的确定

MySQL 5.7 的镜像 tag 选择有个细节:mysql:5.7是滚动 tag,不同时间拉到的可能是 5.7.44 也可能是更早的补丁版本。离线交付要求可复现,所以必须锁定到具体小版本,比如mysql:5.7.44。在联网机器上docker pull mysql:5.7.44,然后docker tag成内网私有仓库的地址,再导出。

如果内网有 Harbor 或 registry,导入后重新打 tag 推上去;如果没有,就直接用docker load后的本地镜像,StatefulSet 里imagePullPolicy设成IfNotPresent,避免 K8s 反复尝试去外网拉。

3. 在联网机器上构建并导出 MySQL 5.7 镜像

3.1 拉取并验证镜像完整性

第一步在能联网的机器上操作,确保 Docker 可用:

# 锁定具体小版本,避免滚动 tag 带来的不确定性 docker pull mysql:5.7.44 # 查看镜像的层信息和大小,确认没有异常 docker inspect mysql:5.7.44 --format '{{.Id}} {{.Size}}' # 记录 IMAGE ID,后面内网 load 后要对比 docker images mysql:5.7.44

docker pull完成后,docker inspect输出的 Id 是镜像的 sha256 摘要,这是判断内网导入是否一致的唯一可靠依据。Size 字段是压缩后的大小,实际导出 tar 会更大一些。

参数说明:--format用 Go 模板语法提取字段,.Id是完整摘要,.Size是字节数。如果你要写自动化脚本,可以把这两个值存到文件里,内网导入后做 diff。

3.2 导出镜像 tar 包

# 导出为 tar,-o 指定输出文件 docker save mysql:5.7.44 -o mysql-5.7.44.tar # 校验 tar 包完整性,记录 sha256 sha256sum mysql-5.7.44.tar > mysql-5.7.44.tar.sha256 # 查看 tar 包大小 ls -lh mysql-5.7.44.tar

docker save会把镜像的所有层打包成一个 tar,包含 manifest、config 和 layer 数据。导出后一定要算 sha256,因为大文件在跨网段传输时偶发损坏,内网导入前先sha256sum -c校验,能省掉很多「镜像损坏」的玄学排查。

逻辑说明:docker save和docker export是两回事。save保留镜像的完整层结构和元数据,load后能还原成镜像;export只导出容器文件系统,丢失层信息,不能用于镜像搬运。离线包场景必须用save。

3.3 准备初始化 SQL 和配置文件

MySQL 5.7 官方镜像支持在/docker-entrypoint-initdb.d/放.sql、.sh、.sql.gz文件,容器首次初始化时按字母序执行。我一般把建库脚本放这里:

-- init.sql:首次启动时自动执行 -- 创建业务库,字符集用 utf8mb4 兼容 emoji CREATE DATABASE IF NOT EXISTS app_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; -- 创建业务用户,限制来源网段 CREATE USER IF NOT EXISTS 'app_user'@'%' IDENTIFIED BY 'ChangeMe_2024'; GRANT ALL PRIVILEGES ON app_db.* TO 'app_user'@'%'; FLUSH PRIVILEGES;

注意CREATE USER的密码不要写死在脚本里提交到代码库,实际交付时用 Secret 挂载或者启动时通过环境变量注入。这里写明文只是为了演示语法。

配置文件my.cnf片段通过 ConfigMap 挂载,覆盖默认参数:

[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci max_connections=500 innodb_buffer_pool_size=1G slow_query_log=1 long_query_time=2

innodb_buffer_pool_size是最关键的参数,一般设为容器内存限制的 50% 到 70%。设太大容器会被 OOM Kill,设太小查询性能上不去。max_connections要结合业务连接池大小,500 是个保守起点。

4. 内网 K8s 落地:从 load 镜像到 StatefulSet 跑起来

4.1 导入镜像并确认

内网节点上执行:

# 先校验文件完整性 sha256sum -c mysql-5.7.44.tar.sha256 # 导入镜像 docker load -i mysql-5.7.44.tar # 确认 IMAGE ID 与联网机器一致 docker images mysql:5.7.44

如果集群用的是 containerd 而不是 Docker,导入命令不同:

# containerd 环境导入镜像 ctr -n k8s.io images import mysql-5.7.44.tar # 确认导入结果 ctr -n k8s.io images ls | grep mysql

-n k8s.io指定命名空间,K8s 默认从k8s.io这个 namespace 找镜像。如果导到了default,Pod 照样拉不到。这是 containerd 环境最常见的翻车点之一。

4.2 创建 Secret 和 ConfigMap

# secret.yaml apiVersion: v1 kind: Secret metadata: name: mysql-secret namespace: mysql-offline type: Opaque stringData: mysql-root-password: "RootPass_2024" mysql-password: "AppPass_2024"
# configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: mysql-config namespace: mysql-offline data: my.cnf: | [mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci max_connections=500 innodb_buffer_pool_size=1G

stringData会自动做 base64 编码,比手写data字段省事。ConfigMap 里的my.cnf通过 volume 挂载到/etc/mysql/conf.d/,MySQL 启动时会自动读取该目录下的.cnf文件。

4.3 StatefulSet 核心配置

apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql namespace: mysql-offline spec: serviceName: mysql-headless replicas: 1 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:5.7.44 imagePullPolicy: IfNotPresent ports: - containerPort: 3306 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: mysql-root-password - name: MYSQL_DATABASE value: app_db volumeMounts: - name: data mountPath: /var/lib/mysql - name: config mountPath: /etc/mysql/conf.d - name: init mountPath: /docker-entrypoint-initdb.d resources: requests: memory: "2Gi" cpu: "1" limits: memory: "2Gi" cpu: "2" volumes: - name: config configMap: name: mysql-config - name: init configMap: name: mysql-init volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 20Gi

关键点:imagePullPolicy: IfNotPresent保证用本地已导入的镜像,不去外网拉。volumeClaimTemplates为每个 Pod 自动创建 PVC,StatefulSet 删除重建后数据仍在。resources.limits.memory要和innodb_buffer_pool_size匹配,2Gi 内存限制配 1G buffer pool 是合理比例。

4.4 验证部署结果

# 查看 Pod 状态 kubectl -n mysql-offline get pods -w # 查看初始化日志,确认 SQL 执行成功 kubectl -n mysql-offline logs mysql-0 | tail -50 # 进入容器验证连接 kubectl -n mysql-offline exec -it mysql-0 -- mysql -uroot -p

Pod 从Pending到Running通常需要 30 到 60 秒,首次初始化因为要执行 SQL 会更久。如果卡在Pending,先看 PVC 是否绑定成功;如果卡在ContainerCreating,看镜像是否真的导入到了正确的 namespace。

5. 离线部署 MySQL 5.7 的避坑清单

5.1 镜像导入后 Pod 仍报 ImagePullBackOff

现象:kubectl describe pod显示Failed to pull image,但docker images明明能看到镜像。

原因:集群用 containerd 时,docker load导入的镜像在 Docker 的存储里,containerd 看不到。两套运行时各有各的镜像存储。

解决:用ctr -n k8s.io images import重新导入,或者配置crictl确认运行时类型。kubectl get nodes -o wide看 CONTAINER-RUNTIME 列,是docker://还是containerd://。

5.2 初始化 SQL 没执行

现象:Pod 起来了,但app_db不存在,/docker-entrypoint-initdb.d/里的脚本像没跑过。

原因:MySQL 官方镜像只在数据目录为空时执行初始化脚本。如果 PVC 里已经有数据(比如之前跑过一次),再次启动会跳过初始化。

解决:确认 PVC 是全新的,或者手动删掉 PVC 重建。kubectl -n mysql-offline delete pvc>#!/bin/bash # load.sh:内网一键导入 set -euo pipefail IMAGE_TAR="images/mysql-5.7.44.tar" RUNTIME=$(kubectl get nodes -o jsonpath='{.items[0].status.nodeInfo.containerRuntimeVersion}') echo "检测到运行时: $RUNTIME" if [[ "$RUNTIME" == docker* ]]; then docker load -i "$IMAGE_TAR" elif [[ "$RUNTIME" == containerd* ]]; then ctr -n k8s.io images import "$IMAGE_TAR" else echo "未知运行时,请手动导入" exit 1 fi kubectl apply -f manifests/ echo "部署完成,等待 Pod 就绪..." kubectl -n mysql-offline rollout status statefulset/mysql --timeout=180s

set -euo pipefail让脚本遇到错误立即退出,避免半途失败还继续往下跑。rollout status会阻塞直到 StatefulSet 就绪或超时,比手动get pods轮询靠谱。

6.2 验证离线包完整性的检查表

交付前我会过一遍这个表:

检查项命令预期结果
镜像 tar 校验sha256sum -cOK
镜像已导入docker images或ctr images ls有对应 tag
Secret 存在kubectl get secretmysql-secret
ConfigMap 存在kubectl get cmmysql-config, mysql-init
PVC 绑定kubectl get pvcBound
Pod 就绪kubectl get podsRunning, Ready 1/1
初始化 SQL 生效kubectl exec ... mysql -e "show databases"含 app_db

这张表可以直接贴到交付文档里,验收时逐项打勾。

6.3 版本升级时的注意事项

MySQL 5.7 小版本升级(比如 5.7.44 升到 5.7.45)在离线环境里要格外小心。官方镜像的 entrypoint 会检查数据目录版本,如果镜像版本低于数据目录版本,会报Invalid MySQL server upgrade并拒绝启动。

我的习惯是:升级前先mysqldump全量备份,然后删掉 StatefulSet 但保留 PVC,导入新镜像后重建。如果新镜像启动时报版本不兼容,说明数据目录的版本号比镜像高,这时候只能回滚镜像或者用mysql_upgrade手动处理。离线环境没有后悔药,备份是唯一的保险。

6.4 一个我踩过的坑

有次交付到客户内网,所有检查项都过了,Pod 也 Running,但业务连不上。排查了半天发现是 Service 的clusterIP: None配了 Headless,而业务方用的是普通 Service 的 ClusterIP 访问。Headless Service 只返回 Pod IP,不提供负载均衡,单副本场景下其实也能用,但客户端如果依赖 DNS 轮询就会出问题。

后来我养成了一个习惯:交付前一定用业务方的连接方式实测一次,而不是只验证kubectl exec能连上。kubectl exec走的是容器内 localhost,和走 Service 完全是两条路径。这个教训让我在后续每个离线包里都加了一个test-connection.sh,模拟业务侧的真实连接。

希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 14:58:40

LoRA自训实战:从Llama到ChatGLM的消费级显卡微调指南

简介:面向AI研发工程师与算法学习者,这份以LoRA微调实战为核心的学习资料包,聚焦Llama(Alpaca LoRA)与ChatGLM(ChatGLM Tuning)两大主流模型的低秩适配训练。包内围绕用户故事生成、测试代码生成…

作者头像 李华
网站建设 2026/10/12 14:57:10

2026年安全运维拐点:AI原生安全与行为基线分析实战

2025年做安全运维的时候,我最大的感受是:告警越来越不像人制造的,更像是一台不知疲倦的机器在试探我的边界。几个大项目一跑,很多团队的负责人都在问同一个问题——2026年我们到底该往哪儿投钱、往哪儿堆人?这篇东西不…

作者头像 李华
网站建设 2026/10/12 14:55:22

2026年3月学工系统厂家推荐,三维实证核心竞争力测评

✅作者简介:合肥自友科技 📌核心产品:智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…

作者头像 李华
网站建设 2026/10/12 14:53:35

数据通信基础:搞清带宽与速率,性能指标才不难

简介:《网络基础与应用》系列课件中「数据通信基础」部分的完整PPT合集,共22张,面向计算机网络初学者、高校相关专业学生及备考人员,用于搭建对数据通信体系的整体认知框架。资源包压缩后仅393KB,包含1个pptx演示文稿&…

作者头像 李华
网站建设 2026/10/12 14:52:39

LSTM与Kalman融合实战:用神经网络补偿状态估计偏差

简介:LTSM_Kalman 是一份面向时间序列预测学习者的实战代码包,聚焦将长短期记忆网络与卡尔曼滤波器结合用于气温预测。项目先由 LSTM 对历史温度序列建模并输出初步预测,再借助卡尔曼滤波融合观测值进行状态估计,从而在噪声环境下…

作者头像 李华
网站建设 2026/10/12 14:51:35

Java开发者必看:LangChain4j大模型应用开发入门实战

1. 为什么Java开发者需要一个专属的LLM框架先说个现象:这半年陆续有几个做Java后端的朋友问我,说他们想在自己的项目里接入大模型能力,结果一搜教程全是Python。什么LangChain、LlamaIndex,Demo跑起来倒是热闹,可真要落…

作者头像 李华