K8S中ingress-nginx通过header路由到不同后端
背景
公司使用ingress-nginx作为网关的项目,需要在相同域名、uri,根据header将请求转发到不同的后端中 在稳定发布的情况下,ingress-nginx是没有语法直接支持根据header做转发的。但是这个可以利用灰度发布的特性实现header路由功能
准备
package main
import "github.com/gin-gonic/gin"
func main ( ) {
r := gin. Default ( )
r. GET ( "/app" , func ( context * gin. Context) {
context. JSON ( 200 , gin. H{ "message" : "app1" } )
} )
r. Run ( ":8080" )
}
使用Dockerfile构建镜像
这里构建 goapp1:v1,goapp2:v1两个镜像(goapp2请将main.go修改 “message”: “app2”)
FROM golang:1.17.13
RUN mkdir -p /go/app/; \
cd /go/app/; \
go mod init app1;\
GOPROXY="https://goproxy.cn,direct" go get github.com/gin-gonic/gin@v1.6.3
WORKDIR /go/app/
COPY main.go /go/app
EXPOSE 8080
CMD go run main.go
使用灰度发布的特性进行header的路由
此解决方案参考:https://v2-1.docs.kubesphere.io/docs/zh-CN/quick-start/ingress-canary/ 注:本人使用低版本ingress-nginx,高版本的请大家自行修改不同之处 首先部署goapp1:v1 和 goapp2:v1 的deployment和service
apiVersion : apps/v1
kind : Deployment
metadata :
name : goapp1
namespace : default
spec :
replicas : 1
selector :
matchLabels :
app : goapp1
template :
metadata :
labels :
app : goapp1
spec :
containers :
- image : goapp1: v1
imagePullPolicy : IfNotPresent
name : goapp1
ports :
- containerPort : 80
protocol : TCP
---
apiVersion : v1
kind : Service
metadata :
name : goapp1
namespace : default
spec :
ports :
- port : 8080
protocol : TCP
targetPort : 8080
selector :
app : goapp1
部署稳定发布版本的ingress,路由至goapp1
apiVersion : networking.k8s.io/v1
kind : Ingress
metadata :
name : goapp1
namespace : default
annotations :
kubernetes.io/ingress.class : nginx
spec :
rules :
- host : test.com
http :
paths :
- path : /app
pathType : Prefix
backend :
service :
name : goapp1
port :
number : 8080
部署canary版本的ingress,路由至goapp2
这里可见 域名都是 test.com,uri都是 /app 注解:
nginx.ingress.kubernetes.io/canary: “true” # 启用canary灰度发布特性 nginx.ingress.kubernetes.io/canary-by-header: canary # 通过header可选择是否转发至canary版本的后端
apiVersion : networking.k8s.io/v1
kind : Ingress
metadata :
name : goapp2
namespace : default
annotations :
kubernetes.io/ingress.class : nginx
nginx.ingress.kubernetes.io/canary : "true"
nginx.ingress.kubernetes.io/canary-by-header : canary
spec :
rules :
- host : test.com
http :
paths :
- path : /app
pathType : Prefix
backend :
service :
name : goapp2
port :
number : 8080
for i in { 1 .. 20 } ;
do curl test.com:31132/app -H "canary: never" ;
echo -e "" ;
done
for i in { 1 .. 20 } ;
do curl test.com:31132/app -H "canary: always" ;
echo -e "" ;
done
效果如下,可以看到可以通过header控制发送请求到不同后端,能够满足需求
通过nginx进行转发
第二种方法可通过在k8s集群部署一个nginx, 通过nginx进行分流
流量路径如下: ingress-nginx --> nginx --> goapp1或goapp2 这里nginx写法有比较多,我选择最简单的通过if判断$http_my_header 在使用$http_my_header之前,需要对ingress-nginx和nginx添加参数,允许header中存在下划线
kubectl edit cm ingress-nginx-controller
------------------
apiVersion: v1
data:
allow-snippet-annotations: "true"
enable-underscores-in-headers: "true"
ignore-invalid-headers: "false"
kind: ConfigMap
部署nginx,nginx中开启允许header下划线的参数:underscores_in_headers on;
apiVersion : apps/v1
kind : Deployment
metadata :
name : nginx
namespace : default
spec :
replicas : 1
selector :
matchLabels :
app : nginx
template :
metadata :
labels :
app : nginx
spec :
containers :
- name : nginx
image : nginx: 1.24.0
ports :
- containerPort : 80
volumeMounts :
- name : nginx
mountPath : /etc/nginx/nginx.conf
subPath : nginx.conf
volumes :
- name : nginx
configMap :
name : nginx
items :
- key : nginx.conf
path : nginx.conf
---
apiVersion : v1
kind : Service
metadata :
name : nginx
namespace : default
spec :
selector :
app : nginx
ports :
- protocol : TCP
port : 80
targetPort : 80
---
apiVersion : v1
data :
nginx.conf : |
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
upstream upstream_server1 {
server goapp1: 8080;
}
upstream upstream_server2 {
server goapp2: 8080;
}
include /etc/nginx/mime.types;
default_type application/octet- stream;
log_format main '$remote_addr - $remote_user [ $time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$http_my_header"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
underscores_in_headers on;
listen 80;
server_name test.com;
location /app {
if ($http_my_header = "value1") {
proxy_pass http: //upstream_server1;
}
if ($http_my_header = "value2") {
proxy_pass http: //upstream_server2;
}
}
}
}
kind : ConfigMap
metadata :
name : nginx
namespace : default
上面的配置判断 $http_my_header是 value1 还是 value2,再转发到不同的upstream 测试
curl test.com/app -H "my_header:value1"
curl test.com/app -H "my_header:value2"