想要实现一个功能详尽的API网关,纯粹靠自己来写是不科学的,不过,了解必要的原理是必要的。
编译使用
我打算使用apisix作为原始项目,所以首先应该学会对apisix的编译安装并使之能够成功运行。
项目地址
Apache APISIX 是一个云原生 API 网关Apache APISIX 仪表板旨在让用户通过前端界面尽可能轻松地操作Apache APISIX
安装步骤
参考API服务网关实现之APISIX安装和部署上面这个博客在安装可视化界面的时候存在问题,看看官方的。官方dashboard部署文档我来理一下大致的过程,安装过程首先需要apisix本身,和一个展示程序。所以可以考虑从两步走,先安装apisix,然后再安装其配套的界面。以下是我这边的实践过程。
安装apisix
环境:已安装openResty centos 7
wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm rpm -ivh epel-release-latest-7.noarch.rpm yum install yum-utils yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo yum install -y etcd openresty curl git gcc luarocks lua-devel systemctl start etcd yum install -y https://github.com/apache/incubator-apisix/releases/download/1.1/apisix-1.1-0.el7.noarch.rpm
启动apisix:apisix start 查看进程或者监听端口9080
ps aux|grep apisix netstat -lntp|grep 9080
关闭各种防火墙
为了避免出现失败,但是生产环境不建议
systemctl stop firewalld.service systemctl disable firewalld.service setenforce 0 sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
安装apisix cli
在这之前,需要安装go语言的环境,nodejs环境和yarn的环境。
注意:
- go语言需要将包镜像转换为国内
go env -w GOPROXY=https://goproxy.cn,direct
- node js的版本要大于 v10.14.2
根据apisix-dashboard官方安装教程中提到的步骤如下:
git clone -b release/2.10.1 https://github.com/apache/apisix-dashboard.git && cd apisix-dashboard
这个步骤如果你的服务器连接到github超时,可以考虑在本机下载了,然后传到你的服务器上去。然后执行
make build
等待执行完毕,这个过程中会连接到github,如果连接超时,就多试几次。
成功之后,来到目录下的output目录,生成的结果就在里面 首先,conf里面是web的配置,结构如下:
conf: listen: # host: 127.0.0.1 # the address on which the `Manager API` should listen. # The default value is 0.0.0.0, if want to specify, please enable it. # This value accepts IPv4, IPv6, and hostname. port: 9000 # The port on which the `Manager API` should listen. # ssl: # host: 127.0.0.1 # the address on which the `Manager API` should listen for HTTPS. # The default value is 0.0.0.0, if want to specify, please enable it. # port: 9001 # The port on which the `Manager API` should listen for HTTPS. # cert: "/tmp/cert/example.crt" # Path of your SSL cert. # key: "/tmp/cert/example.key" # Path of your SSL key. allow_list: # If we don't set any IP list, then any IP access is allowed by default. - 127.0.0.1 # The rules are checked in sequence until the first match is found. - ::1 # In this example, access is allowed only for IPv4 network 127.0.0.1, and for IPv6 network ::1. # It also support CIDR like 192.168.1.0/24 and 2001:0db8::/32 etcd: endpoints: # supports defining multiple etcd host addresses for an etcd cluster - 127.0.0.1:2379 # yamllint disable rule:comments-indentation # etcd basic auth info # username: "root" # ignore etcd username if not enable etcd auth # password: "123456" # ignore etcd password if not enable etcd auth mtls: key_file: "" # Path of your self-signed client side key cert_file: "" # Path of your self-signed client side cert ca_file: "" # Path of your self-signed ca cert, the CA is used to sign callers' certificates # prefix: /apisix # apisix config's prefix in etcd, /apisix by default log: error_log: level: warn # supports levels, lower to higher: debug, info, warn, error, panic, fatal file_path: logs/error.log # supports relative path, absolute path, standard output # such as: logs/error.log, /tmp/logs/error.log, /dev/stdout, /dev/stderr # such as absolute path on Windows: winfile:///C:\error.log access_log: file_path: logs/access.log # supports relative path, absolute path, standard output # such as: logs/access.log, /tmp/logs/access.log, /dev/stdout, /dev/stderr # such as absolute path on Windows: winfile:///C:\access.log # log example: 2020-12-09T16:38:09.039+0800 INFO filter/logging.go:46 /apisix/admin/routes/r1 {"status": 401, "host": "127.0.0.1:9000", "query": "asdfsafd=adf&a=a", "requestId": "3d50ecb8-758c-46d1-af5b-cd9d1c820156", "latency": 0, "remoteIP": "127.0.0.1", "method": "PUT", "errs": []} max_cpu: 0 # supports tweaking with the number of OS threads are going to be used for parallelism. Default value: 0 [will use max number of available cpu cores considering hyperthreading (if any)]. If the value is negative, is will not touch the existing parallelism profile. authentication: secret: secret # secret for jwt token generation. # NOTE: Highly recommended to modify this value to protect `manager api`. # if it's default value, when `manager api` start, it will generate a random string to replace it. expire_time: 3600 # jwt token expire time, in second users: # yamllint enable rule:comments-indentation - username: admin # username and password for login `manager api` password: admin - username: user password: user
需要关注的几个点,首先allow_list,关乎着哪些能够访问该展示层,0.0.0.0/0 可以表示所有可访问,账号密码在users部分配置。
使用命令
./manager-api
启动项目即可。访问:http://ip:9000/user/login?redirect=/ 即可,默认密码为 admin/admin 或者 user/user
功能介绍
官方使用简介
仪表盘-导入Grafana
Grafana是数据可视化,仪表盘和图形编辑器,是 Graphite 和 InfluxDB 仪表盘和图形编辑器,同时也是开源的、功能齐全的度量仪表盘和图形编辑器,支持 Graphite,InfluxDB 和 OpenTSDB。
grafana 是一个开源的时序性统计和监控平台,支持例如 elasticsearch、graphite、influxdb 等众多的数据源,并以功能强大的界面编辑器著称。
或许可以考虑放到网络安全智能防控平台与elasticsearch一起玩。官网相当于已经实现了各种定制的仪表盘,然后只需要导入即可。官网的配置似乎都是需要配置一堆节点的,但是我这里只有一个简单的填http链接的操作。 目前还不知道怎么用,似乎是需要我自己做独立安装。
vim /etc/yum.repos.d/grafana.repo # 在里面放入 [grafana] name=grafana baseurl=https://packages.grafana.com/oss/rpm-beta repo_gpgcheck=1 enabled=1 gpgcheck=1 gpgkey=https://packages.grafana.com/gpg.key sslverify=1 sslcacert=/etc/pki/tls/certs/ca-bundle.crt # 保存后执行 yum -y install grafana systemctl enable grafana-server systemctl start grafana-server
来看看grafana-server的内容
[Unit] Description=Grafana instance Documentation=http://docs.grafana.org Wants=network-online.target After=network-online.target After=postgresql.service mariadb.service mysqld.service [Service] EnvironmentFile=/etc/sysconfig/grafana-server User=grafana Group=grafana Type=notify Restart=on-failure WorkingDirectory=/usr/share/grafana RuntimeDirectory=grafana RuntimeDirectoryMode=0750 ExecStart=/usr/sbin/grafana-server \ --config=${CONF_FILE} \ --pidfile=${PID_FILE_DIR}/grafana-server.pid \ --packaging=rpm \ cfg:default.paths.logs=${LOG_DIR} \ cfg:default.paths.data=${DATA_DIR} \ cfg:default.paths.plugins=${PLUGINS_DIR} \ cfg:default.paths.provisioning=${PROVISIONING_CFG_DIR} LimitNOFILE=10000 TimeoutStopSec=20 CapabilityBoundingSet= DeviceAllow= LockPersonality=true MemoryDenyWriteExecute=false NoNewPrivileges=true PrivateDevices=true PrivateTmp=true ProtectClock=true ProtectControlGroups=true ProtectHome=true ProtectHostname=true ProtectKernelLogs=true ProtectKernelModules=true ProtectKernelTunables=true ProtectProc=invisible ProtectSystem=full RemoveIPC=true RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX RestrictNamespaces=true RestrictRealtime=true RestrictSUIDSGID=true SystemCallArchitectures=native UMask=0027 [Install] WantedBy=multi-user.target
导入数据和控制台:分分钟搞定Grafana(图文详解)目前我这边没有数据可视化展示需求,也不是重点,就不考虑了。
路由
路由(Route)是请求的入口点,它定义了客户端请求与服务之间的匹配规则。路由可以与服务(Service)、上游(Upstream)关联,一个服务可对应一组路由,一个路由可以对应一个上游对象(一组后端服务节点),因此,每个匹配到路由的请求将被网关代理到路由绑定的上游服务中。
上游
上游列表包含了已创建的上游服务(即后端服务),可以对上游服务的多个目标节点进行负载均衡和健康检查。
服务
服务由路由中公共的插件配置、上游目标信息组合而成。服务与路由、上游关联,一个服务可对应一组上游节点、可被多条路由绑定
消费者
消费者是路由的消费方,形式包括开发者、最终用户、API 调用等。创建消费者时,需绑定至少一个认证类插件 包含身份验证、安全防护、流量控制、无服务器架构、可观测性和其他几个类型。
插件
插件类型有如下几种:官方插件文档
- 身份验证
- 一个需要使用的身份验证插件consumer。将基本身份验证添加到 aservice或route.
- 与 Keycloak 身份服务器一起使用的授权插件。Keycloak 是符合 OAuth/OIDC 和 UMA 的身份服务器。虽然它是与 Keycloak 一起开发的,但它也应该与任何 OAuth/OIDC 和 UMA 兼容的身份提供者一起使用
- Casbin
- 基于 lua-casbin 的 Apache APISIX 插件,支持基于各种访问模型的强大授权
- 是一个强大的、高效的开源访问控制框架,其权限管理机制支持多种访问控制模型
- 官网地址
- authz-casbin
- authz-keycloak
- basic-auth
然后consumer将其密钥添加到请求标头以验证其请求
- hmac-auth
- 一个需要使用的身份验证插件consumer。将 HMAC 身份验证添加到 aservice或route
- jwt-auth
- 一个需要使用的身份验证插件consumer。将 JWT 身份验证添加到service或route
- 然后consumer将其密钥添加到查询字符串参数、请求标头或cookie验证其请求
- key-auth
- 一个身份验证插件,它应该consumer一起工作
- 将密钥身份验证(有时也称为 API 密钥)添加到服务或路由。消费者然后在查询字符串参数或标头中添加他们的密钥来验证他们的请求
- ldap-auth
- 一个身份验证插件,可以与consumer. 将 Ldap 身份验证添加到 aservice或route
- openid-connect
- OAuth 2 / Open ID Connect(OIDC) 插件为 APISIX 提供身份验证和自省功能
- wolf-rbac
- 身份验证和授权 (rbac) 插件。它需要与consumer. 还需要添加wolf-rbac一个service或route。rbac 功能由wolf提供
- wolf 文档
- 安全防护
- 该插件帮助我们拦截用户请求,我们只需要指明block_rules
- 可以通过和标头ua-restriction限制对服务或路由的访问。allowlistdenylist User-Agent
- 插件在转发到上游服务之前验证请求。验证插件使用 json-schema 来验证模式。该插件可用于验证标题和正文数据。
- 通过将请求标头引用者列入白名单来限制对服务或路由的访问
- 可以通过将ip-restrictionIP 地址列入白名单或列入黑名单来限制对服务或路由的访问。可以使用 CIDR 表示法中的单个 IP、多个 IP 或范围,例如 10.10.10.0/24
- 故障注入插件,这个插件可以和其他插件一起使用,并且会在其他插件之前执行。该abort属性会直接将用户指定的http代码返回给客户端,并终止后续插件。该delay属性将延迟请求并执行后续插件
- CORS
- 根据consumer-restriction选择的不同对象做出相应的访问限制
- 插件实现了 API 熔断功能,以帮助我们保护我们的上游业务服务
- api-breaker
- consumer-restriction
- cors
- fault-injection
- ip-restriction
- referer-restriction
- request-validation
- ua-restriction
- uri-blocker
- 流量控制
- 流量拆分插件允许用户逐步引导各个上游之间的流量百分比。
- 注意:由于加权循环算法的缺点(特别是当 wrr 状态被重置时),每个上游之间的比率可能不太准确
- 使用“漏桶”方法限制请求速率
- 在给定时间窗口内通过固定数量的请求限制请求速率
- 限制请求并发插件
- limit-conn
- limit-count
- limit-req
- traffic-split
- 无服务器架构
- 指定阶段开始时运行
- 指定阶段结束时运行
- 一个内置在 Apache APISIX 中的无服务器插件,用于与Azure Serverless Function无缝集成,作为动态上游将特定 URI 的所有请求代理到 Microsoft Azure 云,这是生产环境中最常用的公共云平台之一。如果启用,此插件将终止对该特定 URI 的持续请求,并代表客户端使用用户设置的适当授权详细信息、请求标头、请求正文、参数向 azure faas(新上游)发起新请求(所有这三个组件都从原始请求传递)并将响应正文、状态代码和标头返回给调用 APISIX 代理的请求的原始客户端
- azure-functions
- serverless-post-function
- serverless-pre-function
- 可观测性
- 一个 OpenTracing 插件
- 一个将日志数据请求推送到 UDP 服务器的插件
- 一个将日志数据请求推送到 TCP 服务器的插件
- 一个将日志数据请求推送到 Syslog 的插件
- 一个使用 RF5424将Log数据请求推送到阿里云Log Server的插件
- 个通过 HTTP将访问日志数据推送到服务器的插件
- SkyWalking使用其原生 Nginx LUA 跟踪器从服务和 URI 角度提供跟踪、拓扑分析和指标
- 为通过 APISIX 代理的每个请求添加一个唯一 ID (UUID)。此插件可用于跟踪 API 请求。header_name如果请求中已经存在,插件将不会添加请求 ID
- 该插件将添加/apisix/prometheus/metrics以公开指标
- 这些指标通过单独的 Prometheus 服务器地址公开。默认情况下,地址是127.0.0.1:9091. 您可以在 中更改它conf/config.yaml
- 一个插件,用作 ngx_lua nginx 模块的 Kafka 客户端驱动程序
- 此插件提供将请求日志数据作为 JSON 对象推送到外部 Kafka 集群的能力
- 一个将日志数据请求推送到 HTTP/HTTPS 服务器的插件
- 一个将 APISIX 的日志数据推error.log送到 TCP 服务器或Apache SkyWalking的插件
- 该插件将提供将级别选择的日志数据发送到监控工具和其他 TCP 服务器以及通过 HTTP 的 SkyWalking 的能力
- 个内置于 Apache APISIX 的监控插件,用于与Datadog 无缝集成,Datadog是云应用程序最常用的监控和可观察性平台之一。如果启用,此插件支持针对每个请求和响应周期的多种强大类型的指标捕获,这基本上反映了系统的行为和健康状况
- datadog
- error-log-logger
- http-logger
- kafka-logger
- prometheus
- request-id
- skywalking
- skywalking-logger
- sls-logger
- syslog
- tcp-logger
- udp-logger
- zipkin
- 其他
- 响应重写插件,重写上游返回的内容以及 Apache APISIX 本身
- 动态更改 APISIX 看到的客户端 IP 和端口
- 一个上游代理信息重写插件
- 提供镜像客户端请求的能力
- 注意:镜像请求返回的响应被忽略
- 提供了缓存上游响应数据的能力,并且可以与其他插件一起使用。该插件支持基于磁盘的缓存,未来将支持基于内存的缓存
- 动态设置 Nginx 的 gzip 行为
- http(s)转grpc
- 执行大多数内置 Lua 插件之前,在插件运行器中运行特定的外部插件
- 执行大多数内置 Lua 插件之后,在插件运行器中运行特定的外部插件
- 动态控制 Nginx 的行为以处理客户端请求
- 可以接受多个请求并apisix通过http管道发送它们,并将聚合响应返回给客户端,当客户端需要访问多个API时可以显着提高性能
- batch-requests
- client-control
- ext-plugin-post-req
- ext-plugin-pre-req
- grpc-transcode
- gzip
- proxy-cache
- proxy-mirror
- proxy-rewrite
- real-ip
- response-rewrite
对插件的合理使用已经可以解决绝大部分的问题,比如proxy-mirror和Proxy-cache拿来镜像客户端的请求,然后我们就可以拿这些请求来额外做分析,比如做反爬虫和数据安全相关的内容。
证书
证书被网关用于处理加密请求,它将与 SNI 关联,并与路由中主机名绑定。
修改定制化
前端界面的修改
前端项目的代码结构如下 其中web目录里面就是项目的前端页面 整个项目是由vue编写,可以使用yarn构建,所以项目可以以调试模式使用yarn build命令进行运行。官方调试文档值得注意的是,我这边在最后一步,使用yarn start似乎不能成功登陆,只能使用yarn start:e2e指令。
调试的目的就是不用走编译,边改边看效果,因为yarn会检测文件变化然后进行实时编译。调试的结构为
# 进入到web目录,项目运行在8000端口 yarn start:e2e # 退出到dashboard项目目录 # 开启接口服务,运行在9000端口 make api-run
然后访问8000端口的应用,登录即可。让我们来看看实时编译: 然后我去修改defaultSettings.ts: 可以看见就触发了实时编译。这个时候就可以进行动态调试。页面也发生了变化(123是我刚刚才加的)
这只是一个简单的例子,更多的修改将在接下来的文章里面跟大家分享。
apisix能力层的修改
github上面的APISIX有很多不同的版本,所以掌握一手github切换分支的操作就尤为重要。就我观察而言,这个版本的代码应该是最新的比稳定版本多了一些插件。我之前的源码是在gitee上面下载的,结果版本太老了,代码跟github不是一样的,所以还是回归到了github。慢点就慢点吧。从github下载的apisix的代码如下: 其中核心代码在apisix目录中, 里面都是lua脚本,其整体架构和之前分析的Orange差不多,都是由插件系统构成扩展功能,只不过基础的实现不一样。我们在开源项目上做扩展主要就是扩展插件。要扩展插件就得弄明白其插件调用流程以及实现方法。这部分内容将在接下来的文章里面描绘。