如何在Kubernetes中使用Traefik IngressRoute将请求代理到外部IP地址?
嘿,我完全懂你的困扰!Traefik的文档确实把大部分注意力放在了集群内部服务的代理上,但其实它完全支持转发请求到外部IP——只是需要先把外部服务「包装」成Kubernetes的Service资源,因为Traefik的IngressRoute只能关联K8s的Service。下面我一步步给你讲怎么实现和你Nginx配置完全一致的效果:
核心思路
Traefik的IngressRoute必须绑定K8s的Service,所以我们需要创建一个无选择器的Service,再手动关联外部IP的Endpoint,把外部服务伪装成K8s内部服务。之后再配置路径重写和自定义请求头,和你Nginx里的配置一一对应。
步骤1:创建指向外部IP的Service和Endpoint
首先,我们要创建一个没有Pod选择器的Service(因为外部IP不是集群内的Pod),然后手动创建Endpoint来绑定外部IP和端口:
1.1 定义Service
apiVersion: v1 kind: Service metadata: name: external-api-service namespace: xxx # 替换成你的命名空间 spec: ports: - name: http port: 5000 # Traefik会访问这个端口 targetPort: 5000 # 外部服务的实际端口 # 注意:这里不要加selector,我们后面手动绑定Endpoint
1.2 定义Endpoint
这个Endpoint会把上面的Service和外部IP关联起来:
apiVersion: v1 kind: Endpoints metadata: name: external-api-service # 必须和上面的Service同名同命名空间 namespace: xxx subsets: - addresses: - ip: 10.139.158.30 # 你的外部服务IP ports: - name: http # 必须和Service里的port name一致 port: 5000
现在,这个external-api-service就会把所有请求转发到10.139.158.30:5000了。
步骤2:配置路径重写中间件
你的Nginx配置里把/my_api/xxx转发到了/api/v1/xxx,这需要Traefik的路径重写中间件来实现:
apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: rewrite-my-api namespace: xxx spec: replacePathRegex: regex: ^/my_api/(.*) # 匹配所有以/my_api/开头的路径,捕获后面的部分 replacement: /api/v1/$1 # 把捕获的部分替换到/api/v1/后面
如果有人请求https://kubernetes_host.com/my_api/foo,这个中间件会把路径改成/api/v1/foo再转发给外部服务,和你Nginx的效果完全一致。
步骤3:配置自定义请求头中间件
为了和你Nginx里的请求头配置对齐,我们需要创建一个自定义头的中间件,覆盖或添加需要的请求头:
apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: custom-proxy-headers namespace: xxx spec: headers: customRequestHeaders: Host: "{{ index .Request.Header \"Host\" 0 }}" # 保留原请求的Host头 X-Real-IP: "{{ remoteAddr }}" # 传递客户端真实IP X-Scheme: "http" X-Forwarded-Proto: "http" # Traefik默认会自动添加X-Forwarded-For和X-Forwarded-Proto,不过这里我们显式配置和Nginx一致
步骤4:编写最终的IngressRoute
现在把所有组件整合到IngressRoute里:
apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: route-proxy namespace: xxx spec: entryPoints: - websecure # 替换成你实际使用的entryPoint(比如web、websecure) routes: - kind: Rule match: PathPrefix(`/my_api`) # 匹配所有以/my_api开头的请求 middlewares: - name: rewrite-my-api # 先做路径重写 - name: custom-proxy-headers # 再添加自定义请求头 services: - kind: Service name: external-api-service # 指向我们创建的外部服务 port: 5000 # 如果你的entryPoint是HTTPS,记得配置TLS证书 # tls: # certResolver: default # 替换成你的证书解析器
额外小技巧
如果外部服务是域名而不是IP,你可以用ExternalName类型的Service,这样就不用手动创建Endpoint了:
apiVersion: v1 kind: Service metadata: name: external-api-service namespace: xxx spec: type: ExternalName externalName: external-api.example.com # 外部服务的域名 ports: - name: http port: 5000
这样Kubernetes会自动解析这个域名到对应的IP,Traefik就能直接转发请求了。
备注:内容来源于stack exchange,提问作者Maksim

