【技术底稿 36】Docker Compose 微服务迁移 K3s:离线导入、镜像挂载、Nginx 重定向全踩坑复盘

一、核心背景

在不新增服务器、沿用现有 16G 开发机前提下,将原本运行在 Docker Compose 上的微服务架构(MySQL、Redis、2个 Java 服务、Nginx)迁移至 K3s,同时保留 Milvus、Ollama、FTP 等 AI 相关中间件继续由 Docker Compose 托管。

硬件环境:单机 16G 内存,已有 Docker Compose 全套服务稳定运行中。

迁移目标

  • K3s 托管 Java 应用 + Nginx + MySQL + Redis

  • 完全离线导入镜像(服务器无外网)

  • K3s Pod 访问宿主机 Docker 中间件(Milvus、Ollama)

  • 迁移期间业务尽量不中断


二、最终落地架构

组件 版本 运行环境 状态
MySQL 8.0 K3s(离线导入镜像)
Redis 7.x(latest) K3s(离线导入镜像)
Admin(Java) 1.0.0 K3s(离线导入镜像)
API(Java) 1.0.0 K3s(离线导入镜像) ⚠️ jar包待修复
Nginx latest K3s(离线导入镜像 + ConfigMap)
Milvus 2.3.0 Docker Compose(保留)
Ollama latest Docker Compose(保留)
FTP 3.0.5 宿主机原生服务

通信方式 :K3s Pod 通过宿主机 IP 192.168.18.100 访问 Docker 中间件。


三、踩坑实录与根因分析

坑 1:镜像导入明明成功,K3s 却说找不到

现象

bash

bash 复制代码
ctr -n k8s.io images import mysql.tar   # 显示成功
ctr -n k8s.io images ls                  # 能看到镜像
k3s kubectl apply -f mysql.yaml          # Pod 报 ErrImageNeverPull

根因 :K3s 使用独立的 containerd(socket:/run/k3s/containerd/containerd.sock),系统 ctr 操作的是默认 containerd(/run/containerd/containerd.sock),两个实例完全隔离。

解决 :所有镜像操作必须用 k3s ctr

bash

bash 复制代码
sudo k3s ctr -n k8s.io images import mysql.tar
sudo k3s ctr -n k8s.io images ls

坑 2:imagePullPolicy: IfNotPresent 仍然尝试外网拉取

现象 :离线环境下,即便镜像已导入,kubelet 仍报 ErrImagePull

根因 :镜像名包含 docker.io 域名时,kubelet 会尝试访问该域名进行元数据校验。

解决 :强制使用 imagePullPolicy: Never,且镜像名必须与 k3s ctr images ls 输出完全一致:

yaml

bash 复制代码
image: docker.io/library/mysql:8.0
imagePullPolicy: Never

坑 3:HostPath 挂载 jar 包失败

现象CreateContainerConfigError,describe 显示 failed to prepare subPath for volumeMount

根因 :K3s(containerd)不支持用 subPath 挂载单个文件。

解决:挂载整个目录,command 中指定完整路径:

yaml

bash 复制代码
volumeMounts:
- name: jar
  mountPath: /app
volumes:
- name: jar
  hostPath:
    path: /home/xyy/k3s-shangzhuhui/jar
    type: Directory
command: ["java", "-jar", "/app/shangzhuhui-admin-1.0.0.jar"]

坑 4:Nginx 配置文件挂载同样踩坑

现象 :同样的 failed to prepare subPath

解决:改用 ConfigMap 挂载单文件:

bash

bash 复制代码
kubectl create configmap nginx-config -n biz-shangzhuhui --from-file=nginx.conf

yaml

bash 复制代码
volumes:
- name: nginx-config
  configMap:
    name: nginx-config

坑 5:Nginx 启动报 host not found

现象host not found in upstream "shangzhuhui-api"

根因 :Nginx 启动时会解析 proxy_pass 中的域名,后端 Service 不存在则拒绝启动。

解决:注释掉不存在的后端配置,或先部署后端正则。

坑 6:Nginx 重定向丢失端口

现象 :访问 http://IP:30080/admin 返回 301,Location: http://IP/admin(丢端口)

根因try_files $uri $uri/ /admin/index.html 中的 $uri/ 触发内部重定向,Nginx 默认用 $host 构造 Location,不带端口。

解决 :添加 port_in_redirect on;,或前端直接使用 /admin/login 等具体路径绕过。

坑 7:数据库名三处不一致

现象 :应用报 Unknown database 'shangzhuhui'

根因

  • MySQL YAML 中 MYSQL_DATABASE: shangzhuhui_v2

  • SQL 脚本中 CREATE DATABASE shanzhuhui(注意字母差异)

  • 应用连接串中 shangzhuhui

解决 :统一改为 shangzhuhui,手动导入 SQL:

bash

bash 复制代码
kubectl exec -n biz-shangzhuhui mysql-xxx -- mysql -p111111 shangzhuhui < schema.sql

坑 8:K3s Pod 无法访问宿主机 Docker 中间件

现象 :Java 应用启动报 DEADLINE_EXCEEDED,无法连接 Milvus。

排障过程

  1. 先用 curl 测试网络:kubectl exec -it pod -- curl http://192.168.18.100:19530/api/v1/health 返回 404

  2. 404 是 Milvus 健康检查接口路径问题,非网络不通------说明网络层正常

  3. 确认问题不在网络,而在应用 jar 包本身

  4. 健康检查接口最终返回 milvus: "OK",确认网络连接正常

结论:网络层无问题,隔离正常。


四、迁移前后资源对比(16G 服务器)

对比数据

指标 Docker Compose K3s 变化
系统总内存占用 4.1G / 15G 4.4G / 15G +0.3G
系统可用内存 11G 10G -1G
K3s 控制平面 --- ~1G +1G
MySQL 424M 389M -35M
Redis 33M 5M -28M
Nginx 4.8M 2M -2.8M
Admin(Java) 493M 391M -102M

核心结论

  • K3s 控制平面开销约 1G 内存(kubelet + containerd + flannel + apiserver 等)

  • 业务容器内存占用显著降低:Java 服务降低 20%,Redis 降低 85%

  • 净增仅 300MB 内存,换来完整 Kubernetes 编排能力

  • 资源代价极低,适合资源敏感型单机迁移场景


五、固化铁律(K3s 离线部署)

  1. 镜像导入 :一律用 sudo k3s ctr -n k8s.io images import,不用系统 ctr

  2. imagePullPolicy :离线环境必须 Never,镜像名与 k3s ctr images ls 完全一致

  3. HostPath 挂载 :挂目录不挂单文件,避免 subPath

  4. Nginx 配置:用 ConfigMap 挂载,不用 hostPath 单文件

  5. Nginx 重定向 :添加 port_in_redirect on,前端尽量使用具体路径

  6. 后端依赖 :Nginx 中 proxy_pass 的后端 Service 必须先存在,否则注释

  7. 数据库:SQL、初始化环境变量、应用连接串,三处库名必须一致

  8. 排障优先级 :先验镜像是否导入正确(k3s ctr images ls),再看 Pod 日志


六、常用命令速查

bash

bash 复制代码
# 导入镜像到 K3s 的 containerd
sudo k3s ctr -n k8s.io images import <tar包>

# 查看已导入的镜像
sudo k3s ctr -n k8s.io images ls

# 重启 K3s 服务
sudo systemctl restart k3s

# 查看命名空间下所有资源
kubectl get all -n <namespace>

# 实时查看 Pod 日志(按标签筛选)
kubectl logs -n <namespace> -l app=<label> -f

# 创建 ConfigMap 从文件
kubectl create configmap <名称> -n <ns> --from-file=<文件>

# 删除 ConfigMap
kubectl delete configmap <名称> -n <ns>

# 端口转发:本地工具连接 K3s 内 MySQL(临时调试用)
kubectl port-forward -n <ns> svc/mysql 3306:3306

# 查看 Pod 资源占用(CPU/内存)
kubectl top pods -n <namespace>

# 进入容器调试
kubectl exec -it -n <namespace> <pod名> -- bash

七、底稿收尾

本文是《技术底稿》系列第 36 篇,记录 Docker Compose 微服务迁移 K3s 过程中的完整踩坑、排障、资源对比与规范固化。

核心价值

  • 8 条可复用的 K3s 离线部署铁律

  • 迁移前后真实资源对比数据

  • 单机混合架构(K3s + Docker Compose)通信方案

适合小团队单机迁移、无外网环境 K3s 落地的参考范本。

相关推荐
不做无法实现的梦~1 小时前
Docker 新手到团队协作指南
运维·docker·容器
Tisfy1 小时前
VSCode Docker(Code Server)首次调试C++长时间下载debuginfo问题
c++·vscode·docker
SPC的存折1 小时前
22、K8S-Helm
云原生·容器·kubernetes
nix.gnehc2 小时前
Langfuse v3 Docker 部署
运维·人工智能·docker·容器·langfuse
行者-全栈开发2 小时前
【前端安全】CVE-2026-44578:Next.js SSRF 漏洞深度解析与修复实战指南
websocket·云原生·next.js·安全防护·vercel·cve-2026-44578·中间件绕过
JiaWen技术圈2 小时前
Web 安全防护 介绍
运维·nginx·安全
Elastic 中国社区官方博客2 小时前
在 Kubernetes 上的 Elastic Cloud:简化的可用区感知、重启和 mTLS
大数据·数据库·搜索引擎·云原生·容器·kubernetes·全文检索
蜀道山老天师2 小时前
Prometheus监控Hadoop集群(实操完整版,含避坑指南)
大数据·linux·运维·hadoop·云原生·prometheus
sbjdhjd2 小时前
01| 裸机部署 K8S:从零搭建生产可用集群
运维·经验分享·云原生·kubernetes·开源·kubelet·kubeless