APISIX介绍

发布时间 2023-08-08 23:21:35作者: nuccch

APISIX是什么

Apache APISIX是Apache软件基金会下的云原生API网关,它兼具动态、实时、高性能等特点,提供了负载均衡、动态上游、灰度发布(金丝雀发布)、服务熔断、身份认证、可观测性等丰富的流量管理功能。
可以使用Apache APISIX来处理传统的南北向流量,也可以处理服务间的东西向流量。
同时,它也支持作为K8s Ingress Controller来使用。
APISIX的部署架构图如下所示,包含3个部分:API Gateway负责流量转发,etcd负责配置存储,API Gateway Admin是管理人员的控制台,而且三个部分都完整支持高可用。

部署APISIX

如下操作基于APISIX最新稳定分支3.4.1进行。

APISIX支持多种安装方式,但使用Docker方式进行部署是最为方便的。
所以在安装APISIX之前,需要先确定已经安装了DockerDocker Compose
官方给出的安装步骤如下:

# 将Apache APISIX的Docker镜像下载到本地
# 这里可以选择下载指定版本的APISIX,只需要选择指定分支即可
# 如:可以选择分支release/apisix-3.4.1
# 默认从master分支下载
git clone https://github.com/apache/apisix-docker.git
# 也可以从指定分支下载
git clone -b release/apisix-3.4.1 https://github.com/apache/apisix-docker.git

# 将当前的目录切换到apisix-docker/example路径下
cd apisix-docker/example

# 运行docker-compose命令,启动Apache APISIX
docker-compose -p docker-apisix up -d
# 对应的配置文件分别是:./dashboard_conf/conf.yaml和./apisix_conf/config.yaml

安装完毕后请执行如下命令确保APISIX已经成功部署:

curl "http://127.0.0.1:9080" --head | grep Server

如果返回如下格式数据,表明APISIX已经成功部署并启动:

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
Server: APISIX/3.4.1  # 输出了APISIX版本信息

完整的APISIX服务会运行在多个端口,其中有三个是最常用的:

  • 9000:管理后台的运行端口
  • 9080:客户端访问路由时使用的端口
  • 9180:通过管理API执行路由添加等操作的端口

如上所说,APISIX管理后台运行在9000端口,访问地址http://HOST:9000/即可访问APISIX的Dashboard页面,默认管理账户:amdin/admin。

核心概念

Upstream

Upstream也称为上游,上游是对虚拟主机的抽象,即应用层服务或节点的抽象。
上游的作用是按照配置规则对服务节点进行负载均衡,它的地址信息可以直接配置到路由或服务上。当多个路由或服务引用同一个上游时,可以通过创建上游对象,在路由或服务中使用上游ID(即:upstream_id)的方式引用上游,减轻维护压力。

Route

Route也称为路由,是APISIX中最基础和最核心的资源对象。
APISIX可以通过路由定义规则来匹配客户端请求,根据匹配结果加载并执行相应的插件,最后把请求转发给到指定的上游服务。路由中主要包含三部分内容:匹配规则、插件配置和上游信息。

Service

Service也称为服务,是某类API的抽象(也可以理解为一组Route的抽象)。它通常与上游服务抽象是一一对应的,Route与Service之间,通常是N:1的关系。

对APISIX的管理操作,几乎都是在围绕这三者来进行。

APISIX实践

如下使用APISIX来实践发布API,保护API,监控API等操作。

发布API

1.创建Upstream

curl "http://127.0.0.1:9180/apisix/admin/upstreams/1" \
-H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
  "type": "roundrobin",
  "nodes": {
    "httpbin.org:80": 1 # 这里数数字1表示权重
  }
}'

在请求路径http://127.0.0.1:9180/apisix/admin/upstreams/1中的最后部分数字1表示设置upstream_id为1。
可以在nodes对象下指定多个目标地址,以达到负载均衡的效果。

2.创建Route

curl "http://127.0.0.1:9180/apisix/admin/routes/1" \
-H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
  "methods": ["GET"],
  "host": "example.com",
  "uri": "/anything/*",
  "upstream_id": "1" # 在路由中指定upstream_id
}'

注意: 创建上游非必须步骤,可以通过在路由中,添加upstream对象,达到先创建Upstream再创建Route的效果。

curl "http://127.0.0.1:9180/apisix/admin/routes/1" \
-H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
  "methods": ["GET"],
  "host": "example.com",
  "uri": "/anything/*",
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "httpbin.org:80": 1 
    }
  }
}'

在请求路径http://127.0.0.1:9180/apisix/admin/routes/1中的最后部分数字1表示设置路由id为1,用于唯一标识一条路由信息,在管理后台可以看到这个ID。

当然,路由ID还可以通过在请求消息体中指定:

curl -i "http://127.0.0.1:9180/apisix/admin/routes?api_key=edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
  "id": "getting-started-ip", # 在请求消息体中指定路由ID
  "uri": "/ip",
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "httpbin.org:80": 1
    }
  }
}'

验证:

curl "http://127.0.0.1:9080/ip"

返回:

{
  "origin": "172.18.0.1, 124.126.139.14"
}

另外,如果在创建路由时对上游服务指定了多个目标节点,客户端在访问API时将使用负载均衡机制访问目标服务。
如下示例演示添加路由:当访问路径“/headers”时,将使用轮询机制转发到“httpbin.org”或“mock.api7.ai”

curl -i "http://127.0.0.1:9180/apisix/admin/routes?api_key=edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
  "id": "getting-started-headers", # 在请求消息体中指定路由ID
  "uri": "/headers",
  "upstream" : {
    "type": "roundrobin",
    "nodes": {
      "httpbin.org:443": 1,
      "mock.api7.ai:443": 1
    },
    "pass_host": "node",
    "scheme": "https"
  }
}'

验证:

curl "http://127.0.0.1:9080/headers"

来自“httpbin.org”的响应结果:

{
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.61.1", 
    "X-Amzn-Trace-Id": "Root=1-64cf349d-5d3ca35e246ed48c0e7e2b49", 
    "X-Forwarded-Host": "127.0.0.1"
  }
}

来自“mock.api7.ai”的响应结果:

{
  "headers": {
    "accept": "*/*",
    "accept-encoding": "gzip",
    "cf-connecting-ip": "124.126.139.14",
    "cf-ipcountry": "CN",
    "cf-ray": "7f25017fdb898873",
    "cf-visitor": "{\"scheme\":\"https\"}",
    "connection": "Keep-Alive",
    "content-type": "application/json",
    "host": "mock.api7.ai",
    "user-agent": "curl/7.61.1",
    "x-application-owner": "API7.ai",
    "x-forwarded-for": "172.18.0.1",
    "x-forwarded-host": "127.0.0.1",
    "x-forwarded-port": "9080",
    "x-forwarded-proto": "https",
    "x-real-ip": "124.126.139.14",
    "X-Application-Owner": "API7.ai",
    "Content-Type": "application/json"
  }
}

产生10个请求来验证负载均衡效果:

hc=$(seq 10 | xargs -i curl "http://127.0.0.1:9080/headers" -sL | grep "httpbin" | wc -l); echo httpbin.org: $hc, mock.api7.ai: $((10 - $hc))

输出:

httpbin.org: 6, mock.api7.ai: 4

3.测试Route

在创建完成路由后,你可以通过以下命令测试路由是否正常:

curl -i -X GET "http://127.0.0.1:9080/anything/get?foo1=bar1&foo2=bar2" -H "Host: example.com"

返回:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 462
Connection: keep-alive
Date: Mon, 07 Aug 2023 02:22:28 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/3.4.1

{
  "args": {
    "foo1": "bar1", 
    "foo2": "bar2"
  }, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "example.com", 
    "User-Agent": "curl/7.61.1", 
    "X-Amzn-Trace-Id": "Root=1-64d05564-2f4a04cc40f5806822c53bb6", 
    "X-Forwarded-Host": "example.com"
  }, 
  "json": null, 
  "method": "GET", 
  "origin": "172.18.0.1, 124.126.139.14", 
  "url": "http://example.com/anything/get?foo1=bar1&foo2=bar2"
}

保护API

在APISIX中通过插件来实现API保护,在具体实现上是通过限流限速和安全插件保护API服务,限制非正常的访问请求,保障API服务的稳定运行。
APISIX提供了多个限流限速的插件,包括limit-connlimit-reqlimit-count

  • limit-conn插件主要用于限制客户端对服务的并发请求数。
  • limit-req插件使用漏桶算法限制对用户服务的请求速率。
  • limit-count插件主要用于在指定的时间范围内,限制每个客户端总请求个数。

APISIX除了提供限流限速的插件外,还提供了很多其他的关于流量的插件来满足实际场景的需求:

  • proxy-cache:该插件提供缓存后端响应数据的能力,它可以和其他插件一起使用。该插件支持基于磁盘和内存的缓存。
  • request-validation:该插件用于提前验证向上游服务转发的请求。
  • proxy-mirror:该插件提供了镜像客户端请求的能力。流量镜像是将线上真实流量拷贝到镜像服务中,以便在不影响线上服务的情况下,对线上流量或请求内容进行具体的分析。
  • api-breaker:该插件实现了 API 熔断功能,从而帮助我们保护上游业务服务。
  • traffic-split:该插件使用户可以逐步引导各个上游之间的流量百分比。,你可以使用该插件实现蓝绿发布,灰度发布。
  • request-id:该插件通过 APISIX 为每一个请求代理添加 unique ID 用于追踪 API 请求。
  • proxy-control:该插件能够动态地控制 NGINX 代理的相关行为。
  • client-control:该插件能够通过设置客户端请求体大小的上限来动态地控制 NGINX 处理客户端的请求。

同时,也提供了许多用户认证和授权的插件:

  • Key Authentication:用于向RouteService添加身份验证密钥(API key),需要与Consumer一起配合才能工作,通过Consumer将其密钥添加到查询字符串参数或标头中以验证其请求。
  • Basic Authentication:使用basic-auth插件可以将Basic_access_authentication添加到RouteService中。
  • JSON Web Token (JWT) Authentication:用于将JWT身份验证添加到ServiceRoute中,通过Consumer将其密匙添加到查询字符串参数、请求头或cookie中用来验证其请求。
  • Keycloak:用于通过Keycloak Identity Server添加身份验证。
  • Casdoor:使用authz-casdoor插件可添加Casdoor集中认证方式。
  • Wolf RBACwolf-rbac插件为role-based access control系统提供了添加wolfRouteService的功能。此插件需要与Consumer一起使用。
  • OpenID ConnectOpenID Connect(OIDC)是基于OAuth 2.0的身份认证协议,APISIX可以与支持该协议的身份认证服务对接,如Okta、Keycloak、Ory Hydra、Authing等,实现对客户端请求的身份认证。
  • Central Authentication Service (CAS):使用cas-auth查询从SP(服务提供者)的角度访问CAS(中央身份验证服务2.0)IdP(身份提供者)来进行身份验证。
  • HMAC:将HMAC authentication添加到Route或者Service,该插件需要和Consumer一起使用,API的使用者必须将密匙添加到请求头中以验证其请求。
  • Casbinauthz-casbin插件是一个基于Lua Casbin的访问控制插件,该插件支持各种access control models的强大授权场景。
  • LDAPldap-auth插件可用于给路由或服务添加LDAP身份认证,该插件使用lua-resty-ldap连接LDAP服务器。
  • Open Policy Agent (OPA)opa插件可用于与Open Policy Agent进行集成,实现后端服务的认证授权与访问服务等功能解耦,减少系统复杂性。
  • Forward Authenticationforward-auth插件使用的是经典外部认证。当身份认证失败时,可以实现自定义错误或者重定向到认证页面的场景。forward-auth插件巧妙地将身份认证和授权逻辑移到了一个专门的外部服务中,APISIX将用户的请求转发给认证服务并阻塞原始请求,然后在认证服务下以非2xx状态响应时进行结果替换。

限流限速

如下以limit-count插件为例,介绍如何通过限流限速插件保护API服务。

1.创建上游

curl "http://127.0.0.1:9180/apisix/admin/upstreams/1" \
-H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
  "type": "roundrobin",
  "nodes": {
    "httpbin.org:80": 1
  }
}'

2.创建路由

curl -i http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "uri": "/index.html",
    "plugins": {
        "limit-count": { # 使用limit-count插件限定在60秒内最多只能访问上游2次,超过2次则返回503状态码
            "count": 2,
            "time_window": 60,
            "rejected_code": 503,
            "key_type": "var",
            "key": "remote_addr"
        }
    },
  "upstream_id": "1" # 指定了upstream_id
}'

3.测试插件:

curl http://127.0.0.1:9080/index.html

使用上述命令在60秒内连续访问3次后,则会出现如下错误。

<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>openresty</center>
<p><em>Powered by <a href="https://apisix.apache.org/">APISIX</a>.</em></p></body>
</html>

同时产生100个请求查看限速插件的效果:

count=$(seq 100 | xargs -i curl "http://127.0.0.1:9080/index.html" -I -sL | grep "503" | wc -l); echo \"200\": $((100 - $count)), \"503\": $count

输出:

# 100个请求只有2个可以正常执行,98个都失败了
"200": 2, "503": 98

授权认证

如下以key-auth插件为例,限定访问指定路由时需要携带认证信息。

1.添加消费者(Consumer):

curl -i "http://127.0.0.1:9180/apisix/admin/consumers?api_key=edd1c9f034335f136f87ad84b625c8f1"  -X PUT -d '
{
  "username": "tom",
  "plugins": {
    "key-auth": {
      "key": "abcdefghijklmnopqrstuvwxyz"
    }
  }
}'

2.对指定路由启用Key认证:getting-started-ip

curl -i "http://127.0.0.1:9180/apisix/admin/routes/getting-started-ip?api_key=edd1c9f034335f136f87ad84b625c8f1" -X PATCH -d '
{
  "plugins": {
    "key-auth": {}
  }
}'

验证:

# 不带API-KEY访问:
curl -i "http://127.0.0.1:9080/ip"
# 返回:
HTTP/1.1 401 Unauthorized
Date: Sun, 06 Aug 2023 06:07:47 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/3.4.1

{"message":"Missing API key found in request"}
# 带API-KEY访问(使用正确的API-KEY):
curl -i "http://127.0.0.1:9080/ip" -H 'apikey: abcdefghijklmnopqrstuvwxyz'
# 返回:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 45
Connection: keep-alive
Date: Sun, 06 Aug 2023 06:08:52 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/3.4.1

{
  "origin": "172.18.0.1, 124.126.139.14"
}
# 使用不正确的API-KEY访问:
curl -i "http://127.0.0.1:9080/ip" -H 'apikey: abcdefghijklmnopqrstuvwxyz123'
# 返回:
HTTP/1.1 401 Unauthorized
Date: Sun, 06 Aug 2023 06:09:39 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/3.4.1

{"message":"Invalid API key in request"}

监控API

APISIX中提供了很多具有丰富功能的可观测性插件,可以通过使用和设置这些插件,来了解API行为,进而使整个业务流程更加清晰。
API可观测性可分为三个关键部分:日志、指标、链路追踪。

日志

可以通过一些APISIX的日志插件,将APISIX的日志发送到指定的日志服务中。
以下示例展示了在指定路由上启动http-logger的示例。

curl http://127.0.0.1:9180/apisix/admin/routes/1 \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
  "plugins": {
    "http-logger": {
      "uri": "http://mockbin.org/bin/5451b7cd-af27-41b8-8df1-282ffea13a61"
    }
  },
  "upstream_id": "1",
  "uri": "/get"
}'

注意: 可以通过修改uri属性,将上述http-logger的服务器地址更换为其他服务器地址:

{
   "uri": "http://mockbin.org/bin/5451b7cd-af27-41b8-8df1-282ffea13a61"
}

创建成功后,可以通过以下命令向get端点发送请求以生成日志。

curl -i http://127.0.0.1:9080/get

请求成功后,可以单击模拟服务器链接查看访问日志。
展示效果如下:

指标

指标是在⼀段时间内测量的数值。与日志不同,指标在默认情况下是结构化的,这使得查询和优化存储变得更加容易。
APISIX也提供了Prometheus的插件来获取API指标,并在Prometheus中暴露它们。
通过使用APISIX提供的Grafana仪表板元数据,并从Prometheus中获取指标,更加方便地监控API

通过以下命令启用prometheus插件:

curl http://127.0.0.1:9180/apisix/admin/routes/1  \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
  "uri": "/get",
  "plugins": {
    "prometheus": {}
  },
  "upstream_id": "1" # 指定stream_id
}'

启用成功后,可以通过/apisix/prometheus/metrics接口获取APISIX的指标。

curl -i http://127.0.0.1:9091/apisix/prometheus/metrics

还可以通过http://localhost:9090/targetsPrometheus仪表板上查看端点的状态。

链路追踪

链路追踪就是将一次请求还原成调用链路,并将该请求的调用情况使用拓扑的方式展现,比如展示各个微服务节点上的耗时,请求具体经过了哪些服务器以及每个服务节点的请求状态等内容。
通过如下示例,在指定路由中启用zipkin插件:

curl http://127.0.0.1:9180/apisix/admin/routes/1  \
-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
  "methods": [
    "GET"
  ],
  "uri": "/get",
  "plugins": {
    "zipkin": {
      "endpoint": "http://127.0.0.1:9411/api/v2/spans",
      "sample_ratio": 1
    }
  },
  "upstream_id": "1" # 指定upstream_id
}'

通过以下命令请求APISIX:

curl -i http://127.0.0.1:9080/get

如下所示,返回结果中的header部分附加了一些额外的跟踪标识符(TraceId、SpanId和ParentId):

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 470
Connection: keep-alive
Date: Sun, 06 Aug 2023 10:03:16 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Server: APISIX/3.4.1

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "127.0.0.1", 
    "User-Agent": "curl/7.61.1", 
    "X-Amzn-Trace-Id": "Root=1-64cf6fe0-09e409de516b26f632946e46", 
    "X-B3-Parentspanid": "1b9b7f3fe43796c2",            // ParentId
    "X-B3-Sampled": "1", 
    "X-B3-Spanid": "0e881fc8b8fe5ec6",                  // SpanId
    "X-B3-Traceid": "267172faeb84010f3c50229afdb851d6", // TraceId
    "X-Forwarded-Host": "127.0.0.1"
  }, 
  "origin": "172.18.0.1, 124.126.139.14", 
  "url": "http://127.0.0.1/get"
}

【参考】
Get APISIX
为什么Apache APISIX选择NGINX+Lua技术栈?
apisix高性能网关-中文开发文档
王院生:Apache APISIX 微服务网关极致性能架构解析
再谈 APISIX 高性能实践
APISIX架构分析:如何动态管理Nginx集群?
保姆级教程,从概念到实践帮你快速上手 Apache APISIX Ingress
实践一年之久,vivo 如何基于 APISIX 进行业务基础架构的演进
APISIX网关在雪球生产实践
APISIX+Dubbo+Nacos 最佳实践