• apisix~集成服务发现注册中心


    摘要

    当业务量发生变化时,需要对上游服务进行扩缩容,或者因服务器硬件故障需要更换服务器。如果网关是通过配置来维护上游服务信息,在微服务架构模式下,其带来的维护成本可想而知。再者因不能及时更新这些信息,也会对业务带来一定的影响,还有人为误操作带来的影响也不可忽视,所以网关非常必要通过服务注册中心动态获取最新的服务实例信息。架构图如下所示:

    1. 服务启动时将自身的一些信息,比如服务名、IP、端口等信息上报到注册中心;各个服务与注册中心使用一定机制(例如心跳)通信,如果注册中心与服务长时间无法通信,就会注销该实例;当服务下线时,会删除注册中心的实例信息;
    2. 网关会准实时地从注册中心获取服务实例信息;
    3. 当用户通过网关请求服务时,网关从注册中心获取的实例列表中选择一个进行代理;

    常见的注册中心:Eureka, Etcd, Consul, Nacos, Zookeeper 等

    如何扩展注册中心?

    基本步骤

    APISIX 要扩展注册中心其实是件非常容易的事情,其基本步骤如下:

    1. 在 apisix/discovery/ 目录中添加注册中心客户端的实现;
    2. 实现用于初始化的 _M.init_worker() 函数以及用于获取服务实例节点列表的 _M.nodes(service_name) 函数;
    3. 将注册中心数据转换为 APISIX 格式的数据;

    集成nacos

    https://www.bookstack.cn/read/apisix-2.13-zh/275fbdd662ec08a9.md

    discovery:
      nacos:
        host:
          - "http://192.168.xx.xx:8848"
        prefix: "/nacos/v1"
        fetch_interval: 1
        weight: 1
        timeout:
          connect: 2000
          send: 2000
          read: 5000
    

    集成kubernetes

    https://www.bookstack.cn/read/apisix-2.13-zh/a586728927b44a68.md
    https://apisix.apache.org/zh/docs/apisix/next/discovery/kubernetes/

    discovery:
      kubernetes:
        service:
          schema: https
          host: 192.168.xx.xx
          port: "6443"  #这里有一个比较坑的地方,port 必须是字符串,否则会导致 APISIX 启动报错:
        client:
          token: QUtUOH...
        match:
         - default
         - ^my-[a-z]+$
    

    获取serviceaccount账号的token的方法

    apisix的k8s服务发现配置

    1 为了让 APISIX 能查询和监听 Kubernetes 的 Endpoints 资源变动,我们需要创建一个 ServiceAccount:

    kind: ServiceAccount
    apiVersion: v1
    metadata:
      name: apisix-test
      namespace: default
    

    2 以及一个具有集群级查询和监听 Endpoints 资源权限的 ClusterRole:

    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: apisix-test
    rules:
    - apiGroups: [ "" ]
      resources: [ endpoints ]
      verbs: [ get,list,watch ]
    

    3 再将这个 ServiceAccount 和 ClusterRole 关联起来:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: apisix-test
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: apisix-test
    subjects:
      - kind: ServiceAccount
        name: apisix-test
        namespace: default
    

    4 然后我们需要获取这个 ServiceAccount 的 token 值,如果 Kubernetes 是 v1.24 之前的版本,可以通过下面的方法获取 token 值:

    $ kubectl get secrets | grep apisix-test
    $ kubectl get secret apisix-test-token-89hcm -o jsonpath={.data.token} | base64 -d
    

    5 Kubernetes 从 v1.24 版本开始,不能再通过 kubectl get secret 获取 token 了,需要手动建立secret来生成token:

    # 集群执行这个yaml  
    apiVersion: v1
    kind: Secret
    metadata:
      name: apisix-test-kubeapi
      namespace: default
      annotations:
        kubernetes.io/service-account.name: "apisix-test"
    type: kubernetes.io/service-account-token
    data:
    
    # 然后执行
    kubectl get secret apisix-test-kubeapi -n default -o jsonpath={".data.token"} | base64 -d 
    

    6 我们在 APISIX 的配置文件 config.yaml 中添加如下内容( 将上面生成的 token 填写到 token 字段 ):

    discovery:
      kubernetes:
        service:
          schema: https
          host: 127.0.0.1
          port: "6443"
        client:
          token: ...
    

    上面服务发现,我选择kubernetes时,需要注意服务名的书写规范(坑比较多),而我直接在节点节,使用<服务名>.<命令空间>,这种方式反而是支持的,配置如下

    clusterrole相关操作

    kubectl describe  serviceaccount  
    kubectl describe clusterrole 
    kubectl describe clusterrolebinding 
    kubectl get rolebindings,clusterrolebindings --all-namespaces | grep apisix-test  
    

    添加上游服务

    多k8s集群时,service_name的组成,需要加个id,例如[id]/[namespace]/[name]:[portName]

    说明: service_name 必须满足格式:[namespace]/[name]:[portName]

    • namespace: Endpoints 所在的命名空间
    • name: Endpoints 的资源名
    • portName: Endpoints 定义包含的 ports.name 值,如果 Endpoints 没有定义 ports.name,请依次使用 targetPort, port 代替。设置了 ports.name 的情况下,不能使用后两者
    • 例如,下面的portName,可以通过kubectl describe endpoints 来获取,本例中portName就是http-test1

    apisix解析kubenetes后的返回值: 以如下 Endpoints 为例:

    apiVersion: v1
    kind: Endpoints
    metadata:
      name: plat-dev
      namespace: default
    subsets:
      - addresses:
          - ip: "10.5.10.109"
          - ip: "10.5.10.110"
        ports:
          - port: 3306
            name: port
    

    这里有一个比较坑的地方,port 必须是字符串,否则会导致 APISIX 启动报错 invalid discovery kubernetes configuration: object matches none of the required

    问题与解决

    • k8s token错误
    2024/05/17 03:01:07 [error] 54#54: *114 [lua] informer_factory.lua:295: list failed, kind: Endpoints, reason: Unauthorized, message : {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"Unauthorized","reason":"Unauthorized","code":401}
    

    检查token值

    • k8s serviceaccount账号无权限
    2024/05/17 03:01:07 [error] 54#54: *114 [lua] informer_factory.lua:295: list failed, kind: Endpoints, reason: Forbidden, message : {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"Unauthorized","reason":"Forbidden","code":403}
    

    检查serviceaccount绑定的clusterrole是否有endpoint权限

    • 上游服务未找到
    2024/05/17 03:05:22 [error] 52#52: *623 [lua] init.lua:400: nodes(): get unexpected upstream service_name: cms.default.svc.cluster.local, client: 192.168.60.136, server: _, request: "GET /k8s/products HTTP/1.1", host: "test-apisix.pkulaw.com"
    
    2024/05/17 03:05:22 [error] 52#52: *623 [lua] init.lua:545: handle_upstream(): failed to set upstream: no valid upstream node: nil, client: 192.168.60.136, server: _, request: "GET /k8s/products HTTP/1.1", host: "test-apisix.pkulaw.com"
    

    因为apisix中获取到的是k8s服务的endpoints地址【由service指向的pod的真实地址】,这个地址对k8s集群外是不公开的,如图

    大功告成!

    参考:https://www.aneasystone.com/archives/2023/03/apisix-service-discovery.html

  • 相关阅读:
    Java数组
    IPV4和IPV6是什么?
    JS系列1-布尔陷阱以及如何避免
    MMDeploy部署实战系列【第二章】:mmdeploy安装及环境搭建
    打印机共享设置步骤
    linux系统中三个重要的结构体
    Weblogic+Oracle11g设置开机自启动
    6.Tensors For Beginners-What are Convector
    jQuery的extend方法仅仅是字面意思上的扩展吗?
    大整数相加,相减,相乘,大整数与普通整数的相乘,相除
  • 原文地址:https://www.cnblogs.com/lori/p/18201706