k8s服务暴露方案 - Ingress + NodePort双模式设计

Posted by Forgus on 2026-03-26

一、背景

当前 deploy.sh 使用随机 NodePort 暴露服务(node_port=$((30000 + RANDOM % 2767))),导致每次部署后端口变化,无法固定访问地址。


二、目标

  1. 固定端口: 每次部署同一应用,NodePort 保持不变
  2. Ingress 支持: 支持通过域名访问,统一 80 端口
  3. 双模式并存: Ingress 和 NodePort 同时存在,互不干扰
  4. 未来兼容: 支持 Tailscale、公网 DDNS 等场景

三、架构设计

3.1 流量路由

1
2
3
4
5
6
7
外部请求

├── 域名访问 ──→ Ingress Controller (80/443) ──→ Service (ClusterIP) ──→ Pod
│ 通过 host 路由 (如 sample-app.local)

└── 固定端口访问 ──→ Service (固定NodePort) ──→ Pod
192.168.2.40:30090

3.2 组件关系

  • Ingress Controller: ingress-nginx(手动安装到集群)
  • Service: 同时保留 NodePort 类型(固定端口)+ Ingress 资源
  • 端口分配: 通过配置文件 /root/projects/.nodeport-registry 手动管理

四、详细设计

4.1 固定 NodePort 配置文件

文件路径: /root/projects/.nodeport-registry

格式:

1
2
3
4
5
6
# 应用名 → 固定NodePort 映射表
# 端口范围: 30000-32767
# 新增应用时手动添加
sample-app: 30090
text-reader-backend: 30100
text-reader-admin: 30101

规则:

  • 应用名格式: {project}{project}-{app}(与脚本中 deployment_name 一致)
  • 端口范围: 30000-32767
  • 未配置的应用: 部署时报错,提示用户添加配置
  • 端口冲突: 启动时检查是否被其他 Service 占用

4.2 Ingress Controller 安装(手动)

用户需在 Master 节点执行:

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
# 1. 安装 ingress-nginx
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml

# 2. 等待 Controller 就绪
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=120s

# 3. Patch 为 HostNetwork 模式(绑定 Master 节点 80/443)
kubectl patch deployment ingress-nginx-controller \
--namespace ingress-nginx \
--type='json' \
--patch='[
{"op": "add", "path": "/spec/template/spec/containers/0/hostNetwork", "value": true},
{"op": "add", "path": "/spec/template/spec/dnsPolicy", "value": "ClusterFirstWithHostNet"}
]'

# 4. 添加节点选择器,确保调度到 Master 节点
kubectl patch deployment ingress-nginx-controller \
--namespace ingress-nginx \
--type='json' \
--patch='[
{"op": "add", "path": "/spec/template/spec/nodeSelector", "value": {"node-role.kubernetes.io/control-plane": ""}},
{"op": "add", "path": "/spec/template/spec/tolerations", "value": [{"key": "node-role.kubernetes.io/control-plane", "operator": "Exists", "effect": "NoSchedule"}]}
]'

4.3 改动

4.3.1 新增参数

参数 说明 默认值
--domain DOMAIN 自定义域名 {app}.local
--enable-ingress 是否生成 Ingress 资源 true(默认启用)
--no-ingress 不生成 Ingress 资源 -

4.3.2 端口配置文件读取

新增函数 get_fixed_nodeport():

  • 读取 /root/projects/.nodeport-registry
  • 解析 YAML 格式(使用 grep/awk,不依赖额外工具)
  • 返回应用对应的固定端口
  • 未找到则报错

4.3.3 修改 generate_service()

改动前:

1
local node_port=$((30000 + RANDOM % 2767))  # 随机

改动后:

1
2
local node_port
node_port=$(get_fixed_nodeport "$deployment_name") # 固定

4.3.4 新增 generate_ingress()

生成 Ingress YAML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {deployment_name}
namespace: {NAMESPACE}
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: {domain}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {deployment_name}
port:
number: {PORT}

域名逻辑:

  • 如果指定 --domain: 使用指定域名
  • 否则: 使用 {app}.local

4.3.5 修改 deploy_to_k8s()

应用顺序调整为:

  1. ConfigMap(如果有)
  2. Deployment
  3. Service
  4. Ingress(如果启用)

4.3.6 修改 check_deployment_status()

输出同时显示:

1
2
3
4
[OK] 部署完成!
[INFO] 访问地址:
- 内网域名: http://sample-app.local (需配置DNS解析到 192.168.2.40)
- 固定端口: http://192.168.2.40:30090

4.4 域名解析配置

内网访问(二选一):

  1. 路由器 DNS(推荐): 在路由器添加 DNS 记录 *.local → 192.168.2.40
  2. hosts 文件: 在访问设备添加 192.168.2.40 sample-app.local

未来公网访问:

  • 购买域名 + Cloudflare DDNS
  • 路由器端口转发 80/443 → 192.168.2.40
  • Ingress 配置不变,只需修改 host 为真实域名

五、Tailscale 兼容性

Tailscale 与 Ingress 不冲突:

  • Tailscale 节点可通过 192.168.2.40 访问 Ingress
  • Tailscale MagicDNS 可解析内网域名
  • Tailscale Funnel 可暴露 Ingress 到公网

六、端口分配建议

应用 端口 说明
Longhorn UI 30000 已有
Gitea HTTP 30080 已有
Gitea SSH 30022 已有
Woodpecker CI 30800 已有
Woodpecker gRPC 31431 已有
Zot Registry 30500 已有
Sample App 30090 已有
新应用 30100+ 按顺序分配

七、错误处理

场景 处理
端口未配置 报错并提示在 .nodeport-registry 中添加
端口被占用 报错并显示占用该端口的 Service
Ingress Controller 未安装 警告但不阻止部署(NodePort 仍可用)
配置文件格式错误 报错并提示正确格式