背景:

最近用golang gin框架和vue框架做了一个应用,这个应用的用途是通过获取到客户端ip然后返回具体位置。整个应用部署在自建的kubernetes集群上。这个应用使用vue做前端,使用gin做后端,版本为:  gin 1.7.7 和 vue 2.6.11

gin通过将编译好的二进制文件构建到alpine镜像中,构建好后端应用镜像然后进行deployment部署,并设置了service ClusterIP

vue也是通过编译好的dist等前端文件构建到nginx镜像中,构建好的前端应用镜像然后进行deployment部署,以service NodePort方式进行对外暴露端口。(这里需要你给nginx写好配置反向代理文件,指定后端的ClusterIP及端口)

我的k8s集群中我没有用到ingress,只是用到了service Nodeport或ClusterIP。

最后可以从前端访问到后端,唯一的问题是gin获取到的客户端ip不对。

第一: 可能是gin版本问题

发现在go中国的csdn中有一篇文章说,“石墨文档某 HTTP 服务升级 Gin 框架到 1.7.2 后突然发现一个 『Bug』,升级后服务端无法获正确的客户端 IP” ,而我的gin版本确实是1.7之后,于是通读全文,并在github上面阅读两个版本的源码,对比其差异。如下参考资料 1 

我的gin里面还是继续使用 c.ClientIP()来进行获取客户端ip地址。

第二: 可能是k8s的问题

现象是从后台的gin中总是获取到容器的ip地址,而不是客户端的ip地址,具体是由于k8s的各个节点对请求进行了转发导致,于是对前端暴露的Service Nodeport进行补丁:

kubectl patch svc mySvcName -p '{"spec":{"externalTrafficPolicy":"Local"}}'

假设你的deployment的replicas为1,并且有6台节点(192.168.1.2-192.168.1.7),你的前端pod假设在节点192.168.1.6上运行(kubectl describe pod podName),暴露在31872端口上(kubectl get svc),那么你只能在浏览器中使用此节点的ip和暴露出的端口31872进行访问,而使用其他ip(192.168.1.2,192.168.1.3,192.168.1.4,192.168.1.5,192.168.1.7)是无法访问的。

例如:有6台虚拟机节点,分为 3台主节点(master)和3台工作节点(worker),而192.168.1.1为网关路由器地址,所以从2开始,2-4为master,5-7为worker

192.168.1.2/192.168.1.3/192.168.1.4/192.168.1.5/192.168.1.6/192.168.1.7

假设你的pod在192.168.1.6上运行,此时你只能通过192.168.1.6:31872,无法使用其他ip进行访问。
这里有一个坑点: 就是当你删除或者重启这个deployment或pod,会发生此pod漂移到其他节点上运行,假设漂移到192.168.1.2,那么你对外部的访问链接也就得改变。

以前访问192.168.1.6:31872即可以访问前端,但是漂移后这个192.168.1.6:31872将无法使用,只能找这个pod的所在地址进行访问(192.168.1.2:31872)。

假设你的业务场景一直想使用一个固定的ip和port进行访问前端应用,那么就需要修改deployment yaml和serviceNodeport yaml

deployment yaml :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeName: node5
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

192.168.1.6 的nodeName是 node5 ,我们将这个pod固定到某个节点上,这样前端应用的ip地址就不会变化了。

serviceNodeport yaml:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  externalTrafficPolicy: Local  
  type: NodePort
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
      nodePort: 31872

请注意上图中这里写了NodePort 31872,这里写了定值,下次重新删除service再apply也不会改变serviceNodePort端口号了,并且也对流量进行本地处理。

综上,以后再重启甚至删除重建deployment或pod,或serviceNodeport,都不会导致你的前端应用的ip和port有任何变化,业务人员可以一直使用固定ip和端口进行访问。

参考资料:

1.  ​​​​​​https://blog.csdn.net/RA681t58CJxsgCkJ31/article/details/120426004

2.  https://kubernetes.io/zh-cn/docs/tutorials/services/source-ip/

3.  https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-pods-nodes/

Logo

开源、云原生的融合云平台

更多推荐