侧边栏壁纸
博主头像
VanLiuZhi博主等级

今天也是充满希望的一天!

  • 累计撰写 8 篇文章
  • 累计创建 5 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

APISIX Ingress Controller K8s 落地经验

Administrator
2024-04-05 / 0 评论 / 0 点赞 / 64 阅读 / 6795 字

apisix 企业落地实践经验

apisix可以独立于k8s运行,apisix在k8s中运行,需要apisix-ingress-controller组件同步上游数据(主要是Pod IP)

apisix的配置数据比较灵活,可以通过API修改,如果是在k8s中,则可以通过CRD配置,并且优先考虑CRD,因为API配置的数据最终会被CRD覆盖,默认1小时进行一次CRD全量同步

这里就引申出大部分云原生网关的设计思想,数据面(apisix)和控制面(apisix-ingress-controller),数据面可以单独部署,独立于k8s之外,此时使用API配置,如果在k8s中,应该避免API操作,或者保证API操作的数据不和CRD的冲突,另外CRD的数据是持久化在k8s etcd中的,而API数据是持久化在apisix的etcd中,考虑数据维护性和恢复可用性,优先CRD(建议前期可以API,配和dashboard快速上手,后期把API的往CRD迁移)

CRD的优势:如果apisix各个组件,甚至apisix的etcd都挂了,也没关系,找可用的节点快速部署恢复,etcd重新搭建,也可以马上恢复。减少维护k8s etcd,apisix etcd 2套etcd的压力

在大部分云原生网关组件中,svc不再发挥作用了,pod的请求负载均衡由网关直连pod ip,这样可以实现多样化的负载均衡策略,灰度策略等,在7层扩展丰富的能力。svc的iptables实现 LB 策略很有限,一般是iptables规则做概率转发,如果切换到ipvs可以丰富一点,不过本质都是4层转发,要实现复杂场景还是需要apisix这种云原生网关组件(或者走ebpf,服务网格等方向,扩展4层转发的能力,从一些业界案例来看,建议还是apisix这种基于nginx容易落地一点)

Proxy Protocol

如何使用proxy协议,在配置文件中,有这部分

# proxy_protocol:                    # PROXY Protocol configuration
  #   listen_http_port: 9181           # APISIX listening port for HTTP traffic with PROXY protocol.
  #   listen_https_port: 9182          # APISIX listening port for HTTPS traffic with PROXY protocol.
  #   enable_tcp_pp: true              # Enable the PROXY protocol when stream_proxy.tcp is set.
  #   enable_tcp_pp_to_upstream: true  # Enable the PROXY protocol.

nginx 中配置,这个配置还是复杂的,具体要测试验证一下,一般listen 80 proxy_protocol配置

stream {
    server {
        listen 80 proxy_protocol;
        ...
        proxy_pass backend:port;
    }
}

注意:需要双方都启用proxy协议,否则容易出问题

提醒:apisix的proxy协议,根据我看官方文档和Github的issue来看,proxy协议的端口不能和原本的80,443共用一个,看到issue上还有很多人在讨论,一般大家使用这个协议,主要还是为了传递源ip,但是具体落地上分开使用2个端口,我没遇到这样的场景,都是希望使用同一个端口,所以这块感觉还不能满足需求,或者有更好的落地方案,需要注意一下

获取真实ip

如果网络过了NAT,通常就不能取到真实ip了,以下是apisix的一些部署场景

case:

  • nginx -> apisix -> pod
  • apisix -> pod
  • vip -> apisix

重点在于,各层转发,是不是传递真实ip,如果是7层,保证 X-Forwarded-For 设置正确 如果是4层,保证能用proxy协议传递ip

nginx 配置补充说明:

  • 使用proxy_add_x_forwarded_for,把传递代理后的ip加到后面,这样 X-Forwarded-For 可以读到整个代理链路的ip列表 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

  • 设置这一层代理的真实ip proxy_set_header X-Real-IP $proxy_add_x_forwarded_for;

k8s 部署架构说明

各种部署架构传递ip场景:

  • 主机网络模式 这种模式直连到主机网络,没有问题

  • SLB 这种模式主要看厂商,要支持源ip传递

  • externalIPs 推荐没有SLB的首选模式,首先hostnetwork不利于Pod变更,externalIPs很好解决这种问题

NodePort 要设置,如果是ClusterIP,从external ip进去的流量可以负载均衡,转发到其它pod,如果要保留源ip,就得配置externalTrafficPolicy: Local,这个配置必须要NodePort模式,此时相当于把发往external ip 80 的请求直接转发给pod(内核iptables实现),不需要NAT,可以保留源ip,但是失去svc负载均衡能力,多实例情况下,一般前面要挂一个VIP,实现高可用架构

配置参考,nodePort不发挥作用了,可以在网络防火墙策略上关闭,只开放80,443:

spec:
  ports:
    - name: apisix-gateway
      protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30238
    - name: tls
      protocol: TCP
      port: 443
      targetPort: 443
      nodePort: 32435
  selector:
    app.kubernetes.io/instance: apisix
    app.kubernetes.io/name: apisix
  type: NodePort
  externalIPs:
    - 10.90.xx.xx
  sessionAffinity: None
  externalTrafficPolicy: Local

扩展:4层转发 PROXY 协议不是唯一解决方案,如果是一些硬件负载均衡,透明代理也可以做到保留源IP,具体技术细节就比较广了

比如这个模块TOA,内核安装后,可以从扩展的tcp头部中取到源ip ( 具体原理就是lb把源ip写到扩展头部中,上游服务通过TOA模块,hack的方式修改内核系统调用,实现从扩展的tcp头部中取源ip 这种方式不改变源ip,保证lb的下游能正确回包。它修改了内核的getpeername系统调用,而nginx这种软件取ip也是调用getpeername getpeername 是一个系统调用,它被用于套接字编程中,以获取与某个套接字(socket)相关联的对端(peer)的地址信息 )

https://github.com/Huawei/TCP_option_address

https://baijiahao.baidu.com/s?id=1752059279143587469&wfr=spider&for=pc

路由配置

  1. 路由匹配,通过proxy-rewrite插件,走正则,需求比较多的场景,适合一个host给多个服务使用,服务使用路由区别,转发给上游的时候,路由会去掉(正则有一定的性能损耗),要注意斜杆必须按照下面的方式配置
  2. X-Forwarded-Prefix 设置,apisix没有带这个头,有些服务需要,比如swagger
  3. 开启websocket,默认不开启
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: mytest
  namespace: apisix
spec:
  http:
    - backends:
        - serviceName: mytest
          servicePort: 80
      match:
        hosts:
          - test-server.abc.com
        paths:
          - /abc/test/v1.0/*
      name: mytest
      plugins:
        - config:
            headers:
              set:
                X-Forwarded-Prefix: /abc/test/v1.0/
            regex_uri:
              - /abc/test/v1.0/(.*)
              - /$1
          enable: true
          name: proxy-rewrite
      websocket: true

上游服务配置

  1. 上游服务超时时间配置
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
  name: mytest
  namespace: apisix
spec:
  timeout:
    connect: 100s
    read: 300s
    send: 300s

安装

helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-subdir-external-provisioner-local nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
    --set nfs.server=10.90.32.382 \
    --set nfs.path=/home/k8s
kubectl patch storageclass <your-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage-retain
  annotations:
    storageclass.kubernetes.io/is-default-class: 'true'
provisioner: cluster.local/nfs-subdir-external-provisioner-local
parameters:
  onDelete: retain
  pathPattern: ${.PVC.namespace}/${.PVC.name}/${.PVC.annotations.nfs.io/storage-path}
reclaimPolicy: Retain
volumeBindingMode: Immediate
kubectl create ns ingress-apisix
helm install apisix apisix/apisix \
  --set gateway.type=NodePort \
  --set ingress-controller.enabled=true \
  --set etcd.persistence.storageClass="managed-nfs-storage-retain" \
  --set etcd.persistence.size="4Gi" \
  --namespace ingress-apisix \
  --set ingress-controller.config.apisix.serviceNamespace=ingress-apisix
0

评论区