K8s 不支持单个 Service 同时声明 TCP 和 UDP 端口;必须拆分为两个独立 Service,分别指定 protocol,且需确保 CNI 支持 UDP、Go 应用监听对应端口、NodePort 场景下 UDP 必须显式指定 nodePort。

直接说结论:K8s 本身不支持单个 Service 同时声明 TCP 和 UDP 端口;TCP 多端口用标准 Service 即可,UDP(或混合协议)必须拆成独立 Service,且需确保底层网络插件和 CNI 支持 UDP 流量转发。
Service 不允许混写 TCP/UDP 端口
你在 ports 列表里写两个条目、一个设 protocol: TCP、另一个设 protocol: UDP,kubectl apply 会直接报错:Service "xxx" is invalid: spec.ports[1].protocol: Unsupported value: "UDP"(具体错误位置可能不同)。K8s API 强制要求同一个 Service 的所有 ports 必须协议一致。
所以别试图在一个 YAML 里“省事”混写,这是被 API 层硬性拒绝的。
- 想暴露 TCP 80 + TCP 443 → 写在一个
Service里,合法 - 想暴露 TCP 80 + UDP 53 → 必须拆成两个
Service,各自指定protocol -
ClusterIP/NodePort/LoadBalancer类型都受此限制
Go 应用容器内必须监听对应端口
Service 只是流量入口,真正收包靠容器里的 Go 程序。如果你在 Service 里写了 targetPort: 53,但 Go 代码没启动 UDP listener,连接会直接被拒绝(connection refused 或超时)。
典型 Go 监听片段示例:
// TCP 服务(如 HTTP)
go http.ListenAndServe(":8080", nil)
<p>// UDP 服务(如 DNS 查询代理)
udpAddr, <em> := net.ResolveUDPAddr("udp", ":53")
conn, </em> := net.ListenUDP("udp", udpAddr)
defer conn.Close()
// 后续处理 conn.ReadFromUDP / WriteToUDP
</p>
- 务必在容器启动时同时启动 TCP 和 UDP listener,不能只启一个
- Dockerfile 中
EXPOSE行只是文档作用,不影响实际监听,但建议写全(EXPOSE 8080 53/udp)便于团队理解 - Pod 的
containerPort字段也建议按实际监听端口一一列出,否则kubectl describe pod里看不到端口信息
NodePort 场景下 UDP 端口要手动指定
当你用 type: NodePort 暴露 UDP 服务时,nodePort 字段不能省略 —— K8s 不会像 TCP 那样自动分配可用 NodePort 给 UDP(v1.22+ 仍如此)。漏写会导致创建失败或端口为 0。
正确写法示例(UDP Service):
apiVersion: v1
kind: Service
metadata:
name: go-udp-service
spec:
type: NodePort
protocol: UDP
ports:
- port: 53
targetPort: 53
nodePort: 30053 # 必须显式指定,范围 30000–32767
selector:
app: go-multiport
- TCP 的
nodePort可以不写,K8s 自动分配;UDP 的不行 - 同一节点上,TCP 30053 和 UDP 30053 是两个独立端口,可共存
- 若集群节点防火墙开启(如 ufw/firewalld),需额外放行 UDP 30053
Ingress-Nginx 暴露非 HTTP/HTTPS 端口需 ConfigMap 配置
如果你用 ingress-nginx 作为统一入口,并希望把外部 TCP/UDP 流量(比如 9000/TCP 或 53/UDP)转给后端 Go 服务,不能靠 Ingress 资源定义 —— 它只管七层 HTTP/HTTPS。
必须通过 ingress-nginx 命名空间下的 ConfigMap 控制:
apiVersion: v1 kind: ConfigMap metadata: name: tcp-services namespace: ingress-nginx data: "9000": "default/go-tcp-service:9000" --- apiVersion: v1 kind: ConfigMap metadata: name: udp-services namespace: ingress-nginx data: "53": "default/go-udp-service:53"
- ConfigMap key 是外部暴露的端口号(字符串格式),value 是
/ : - 对应的
ingress-nginxService 必须已暴露这些端口(例如在Service的ports里加- port: 9000和- port: 53) - 更新 ConfigMap 后,ingress-nginx Pod 会热重载,无需重启
最易忽略的一点:UDP 连接无状态,Service 的 sessionAffinity 对 UDP 无效;如果你依赖客户端 IP 亲和性,得在 Go 应用层自己做连接跟踪或用外部 L4 LB(如 MetalLB + BGP 模式)。











