Kubernetes Ingress是管理集群外部HTTP和HTTPS流量的核心入口,它把域名、路径这样的路由规则从应用代码中剥离出来,交给集群统一管理。对于用Golang编写的后端服务来说,配合Ingress可以让一个Go服务只关心业务逻辑,路由分发、TLS证书终止、跨域配置这些横切关注点全部交给基础设施层处理。这篇文章从Ingress的底层机制讲起,逐步给出完整的部署配置和Golang服务端的配合改造方案。

一、先弄清楚Ingress到底管了什么
很多初学者会把Ingress、Service、Ingress Controller三个概念混在一起。实际上Ingress本身只是一个API对象,它只是一份声明:告诉集群“访问api.ippipp.com/v1的请求应该转发到名叫user-service的后端”。这份声明本身不具备任何转发能力,真正干活的是Ingress Controller,比如Nginx Ingress Controller、Traefik或者Kong。
Ingress Controller会持续监听集群中的Ingress资源变化,一旦有新的规则写入,它会把这些规则翻译成自己内部的配置。以Nginx Ingress Controller为例,它会生成对应的nginx.conf片段并执行热加载,所以规则变更通常几秒内就能生效,不需要重启任何Pod。理解这一点很重要:你在Ingress里配置的路由规则,最终会变成Nginx配置,所以Nginx的很多限制在Ingress上同样存在。
还有一个容易踩的坑:Ingress只能路由到Service,不能直接路由到Pod。请求链路是 外部流量 → Ingress Controller(通常是LoadBalancer类型的Service)→ 根据Ingress规则匹配 → 目标Service → 通过Endpoint转发到具体Pod。你的Golang服务就是最末端的Pod,它收到的是Controller转发过来的请求,这意味着客户端的原始IP会丢失,后面我们会专门讲怎么找回它。
二、部署Golang服务并编写Ingress规则
假设我们有一个用标准库写的Golang服务,监听8080端口,提供一个健康检查接口和一个业务接口。镜像构建时要注意:Go编译出来的是静态二进制,推荐使用多阶段构建,基础镜像用scratch或alpine,产物通常只有十几MB。
package main
import (
"net/http"
"log"
)
func main() {
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
})
http.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("user data"))
})
log.Fatal(http.ListenAndServe(":8080", nil))
}接下来写部署清单。Service类型用ClusterIP即可,不需要LoadBalancer,因为流量入口已经交给Ingress Controller了。注意Service的selector必须和Pod标签完全一致,否则Endpoints为空,Ingress会返回503。
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: registry.ippipp.com/user-service:v1.0.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /healthz
port: 8080
---
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: user-service-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: api.ippipp.com
http:
paths:
- path: /v1/user(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: user-service
port:
number: 80
- path: /healthz
pathType: Exact
backend:
service:
name: user-service
port:
number: 80这里有几个细节值得展开。第一,ingressClassName指定了由哪个Controller接管,如果集群里装了多个Controller,漏配这一项规则不会生效。第二,pathType有Prefix、Exact、ImplementationSpecific三种,Prefix匹配前缀并要求以斜杠分段,Exact要求完全一致。第三,上面用到的正则路径配合rewrite-target注解实现了路径重写:外部访问/v1/user/list时,实际转发到后端的是/list,Go服务端代码里不需要感知/v1这个前缀。
多服务路由也很常见。可以在同一个Ingress里为不同路径配置不同后端,比如/api/order转发到订单服务,/api/user转发到用户服务,也可以为不同域名各自建一条规则,实现基于host的虚拟主机路由。这样Golang侧的每个服务保持独立部署、独立扩容,路由收敛在网关层统一管理。
三、Golang服务端需要配合处理的几件事
经过Ingress转发后,请求到达Go服务时已经不再是原始形态,服务端要做针对性处理。最典型的就是获取真实客户端IP。Controller转发请求时会附加X-Forwarded-For、X-Real-IP、X-Forwarded-Proto这几个头,其中X-Forwarded-For是一个逐级追加的列表,格式为“客户端IP, 代理1, 代理2”。如果Controller配置了启用Proxy Protocol,Go服务端读取时要把列表最左边未受信任的部分当作真实IP。
func realIP(r *http.Request) string {
// 优先取X-Real-IP,由Nginx Ingress Controller设置
if ip := r.Header.Get("X-Real-IP"); ip != "" {
return ip
}
// 兜底解析X-Forwarded-For的第一个地址
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
parts := strings.Split(xff, ",")
return strings.TrimSpace(parts[0])
}
// 直连场景
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return r.RemoteAddr
}
return host
}使用Gin框架的话更简单,设置router.SetTrustedProxies为Controller所在的网段,Gin会自动解析ClientIP方法返回真实IP。切记不要把SetTrustedProxies设为nil(信任所有代理),否则任何人伪造X-Forwarded-For头都能骗过你的限流和审计逻辑。
另一件需要处理的事是优雅关闭。Ingress Controller在滚动更新时会把流量切走,但已经建立的连接还在,Go服务必须正确处理SIGTERM信号:先停止接收新请求,等待存量请求处理完再退出。否则会出现滚动更新期间大量502的情况。配合terminationGracePeriodSeconds和readinessProbe一起设置,可以做到对用户无感的发布。
四、进阶用法与常见故障排查
Ingress配合注解还能实现不少高级功能。灰度发布可以通过nginx.ingress.kubernetes.io/canary系列注解完成:同一个host和path建两条Ingress,一条作为主干,一条加上canary-weight注解按比例分流,比如5%的流量打到新版本,观察无误后逐步放大。TLS则是在Ingress的tls字段里声明secret,Controller会自动完成证书的加载和HTTPS终止,后端Go服务全程只收HTTP,不需要在代码里管证书轮换。
排查故障时有一个固定套路。先执行kubectl describe ingress看Controller有没有回写事件,Backends字段显示kubectl get endpoints user-service确认Pod是否就绪。最后看Controller本身的日志和生成的Nginx配置,Nginx Ingress Controller提供了/nginx_status端点可以观察连接情况。
常见错误码也有规律可循:404通常是Ingress规则里的host和实际访问域名不匹配;502多半是后端Pod没起来或者优雅关闭没做好;503是Endpoints为空;413是请求体超过Nginx默认的1M限制,加注解nginx.ingress.kubernetes.io/proxy-body-size调大即可。掌握这些对应关系,排障时基本能快速定位问题所在层。
总体来说,Ingress让Golang服务从路由职责中解放出来,服务端只需专注业务和少量转发相关的适配。建议在项目初期就把健康检查、优雅关闭、真实IP获取这三件事纳入标准模板,后续接入Ingress几乎是零成本的。
GolangKubernetes IngressHTTP路由修改时间:2026-09-05 02:42:43