返回正文
把 .NET 项目从 docker-compose 迁到 k3s
很多 .NET 项目在早期都会走同一条路:先用 docker-compose 跑起来,上线后靠 SSH + 脚本发布。
这条路能跑,但一旦业务复杂度上来,问题会集中爆发:
- 发布靠手工,容易漏步骤;
- 容器重建后 IP 变化,服务互调不稳定;
- 回滚慢,线上故障窗口长;
- 观测组件和业务组件混在一起,升级互相影响。
我把一套 .NET 服务从 docker-compose 迁到了 k3s。本文只讲实战:怎么迁、踩了哪些坑、迁完收益到底值不值。
为什么选 k3s,不是直接上“大而全”k8s
我的目标不是堆技术名词,而是解决真实问题:
- 发布和回滚标准化;
- 服务发现不再依赖写死地址;
- 探针和副本机制兜住线上稳定性;
- 可观测性链路统一管理。
k3s 对中小团队很友好:安装快、资源占用小、生态兼容标准 k8s。先把核心流程跑通,再谈多节点和高可用。
迁移思路:先做“资源映射”,再做“能力增强”
我把 compose 配置按下面方式翻译:
- service -> Deployment / StatefulSet
- env -> ConfigMap / Secret
- depends_on -> Readiness / Liveness Probe
- ports -> Service + Ingress
- volume -> PVC
这一步是迁移成败关键。很多人失败不是不会写 YAML,而是脑子里还停留在单机编排思维。
业务服务 Deployment 示例
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-api
namespace: prod
spec:
replicas: 2
selector:
matchLabels:
app: app-api
template:
metadata:
labels:
app: app-api
spec:
containers:
- name: app-api
image: ghcr.io/your-org/app-api:1.0.0
ports:
- containerPort: 8080
env:
- name: ASPNETCORE_URLS
value: http://+:8080
- name: ConnectionStrings__PostgreSQL
valueFrom:
secretKeyRef:
name: app-secret
key: pg_conn
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 20
periodSeconds: 10
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "1Gi"1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
有状态组件怎么放:别一次性把风险拉满
我的策略是:
- 先保持在已有稳定环境(宿主机或独立实例);
- 放进 k3s,但必须绑定 PVC;
- 所有连接串走 Secret,不写死 IP。
原因很直接:有状态服务上 k8s 当然可以做,但备份、恢复、IO 和故障演练要求更高。中小团队最稳妥的路径是先把无状态服务迁完,快速拿到收益。
可观测性迁移:OTel 用 管起来
如果你已经有 OpenTelemetry,迁 k3s 时建议直接改成 Helm 管理,版本和升级都更可控。
bash
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm upgrade --install otel-collector open-telemetry/opentelemetry-collector \
-n observability --create-namespace \
-f values-otel-collector.yaml1
2
3
4
2
3
4
values 里我保留了尾采样策略:
- 错误请求 100% 保留
- 慢请求 100% 保留
- 普通请求按比例采样
这样做的效果很明显:关键问题不丢,存储压力可控。
发布与回滚:从“上机器改命令”到声明式
迁移后发布流程变成:
- 构建并推送镜像;
- 更新 Deployment 镜像版本;
- 观察 rollout 状态;
- 异常直接 rollback。
常用命令:
bash
kubectl -n prod set image deploy/app-api app-api=ghcr.io/your-org/app-api:1.0.1
kubectl -n prod rollout status deploy/app-api
kubectl -n prod rollout history deploy/app-api
kubectl -n prod rollout undo deploy/app-api1
2
3
4
2
3
4
这套流程的价值很现实:回滚不再依赖“谁记得上一个版本怎么发的”。
我踩过的 6 个坑(强烈建议先看)
坑 1:探针路径写错, 无限重启
- 现象: CrashLoopBackOff
- 原因: 接口升级后路径变化,探针还指向旧地址
- 处理: liveness 和 readiness 分开设计,并设置合理延迟
坑 2:Service selector 与 Pod label 不一致
- 现象: Ingress 502
- 处理: 先看 endpoints 是否为空,再核对 label
坑 3:时区不统一,日志和 Trace 对不上
- 处理: 容器统一时区,并统一 /Loki 展示时区
坑 4:内存 limit 太紧,高峰期 OOMKilled
- 处理: 结合运行时指标调高 limit,requests 也别过低
坑 5:把 Secret 明文提交到仓库
- 处理: 仓库只存模板,真实值通过 CI/CD 或集群注入
坑 6:Ingress 没透传真实 IP
- 处理: 转发真实客户端 IP,并在 启用 ForwardedHeaders
结语
k3s 不是银弹,但它把“不稳定靠人扛”变成了“稳定靠系统兜底”。对中小规模 .NET 团队来说,这一步非常值。
如果你现在还在用 docker-compose 硬扛生产,可以先做一件事:挑一个无状态服务,按本文流程迁到 k3s。
不用一次到位,但要先跑通正确路径。
V 0.13.3 |
基于 MIT Licensed版权所有 © 2009- 2026 CMONO.NET
本站访客数
--次 本站总访问量
--人次 