Skip to main content

Apache APISIX网关

Apache APISIX 是一个动态、实时、高性能的 API 网关,提供负载均衡、动态上行、金丝雀发布、熔断、认证、可观察性等丰富的流量管理功能。

快速开始

本文使用apisix-3.6.0-debian

部署apisix

git clone -b v3.6.0 https://github.com/apache/apisix-docker.git
cd apisix-docker/example
docker-compose -p docker-apisix up -d

docker-compose.yml文件,包含dashboard

version: "3"

services:
apisix:
image: apache/apisix:${APISIX_IMAGE_TAG:-3.6.0-debian}
restart: always
volumes:
- ./apisix_conf/config.yaml:/usr/local/apisix/conf/config.yaml:ro
depends_on:
- etcd
ports:
- "9180:9180/tcp" # data api 端口
- "80:9080/tcp" # http service 端口
- "9091:9091/tcp" # prometheus服务端口
- "443:9443/tcp" # https service端口
- "9092:9092/tcp" # control api 端口
networks:
apisix:

etcd:
image: bitnami/etcd:3.5.11
restart: always
volumes:
- etcd_data:/bitnami/etcd
environment:
ETCD_ENABLE_V2: "true"
ALLOW_NONE_AUTHENTICATION: "yes"
ETCD_ADVERTISE_CLIENT_URLS: "http://etcd:2379"
ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
networks:
apisix:

prometheus:
image: prom/prometheus:v2.25.0
restart: always
volumes:
- ./prometheus_conf/prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
networks:
apisix:

grafana:
image: grafana/grafana:7.3.7
restart: always
ports:
- "3000:3000"
volumes:
- "./grafana_conf/provisioning:/etc/grafana/provisioning"
- "./grafana_conf/dashboards:/var/lib/grafana/dashboards"
- "./grafana_conf/config/grafana.ini:/etc/grafana/grafana.ini"
networks:
apisix:

apisix-dashboard:
image: apache/apisix-dashboard
ports:
- 19000:9000
volumes:
- ./dashboard_config.yaml:/usr/local/apisix-dashboard/conf/conf.yaml
networks:
apisix:


networks:
apisix:
driver: bridge

volumes:
etcd_data:
driver: local

apisix.yaml文件

默认配置 https://github.com/apache/apisix/blob/master/conf/config-default.yaml

apisix:
node_listen: 9080 # APISIX listening port
enable_ipv6: false

enable_control: true
control:
ip: "0.0.0.0"
port: 9092

deployment:
admin:
allow_admin: # https://nginx.org/en/docs/http/ngx_http_access_module.html#allow
- 0.0.0.0/0 # We need to restrict ip access rules for security. 0.0.0.0/0 is for test.

admin_key:
- name: "admin"
key: edd1c9f034335f136f87ad84b625c8f1
role: admin # admin: manage all configuration data

- name: "viewer"
key: 4054f7cf07e344346cd3f287985e76a2
role: viewer

etcd:
host: # it's possible to define multiple etcd hosts addresses of the same etcd cluster.
- "http://etcd:2379" # multiple etcd address
prefix: "/apisix" # apisix configurations prefix
timeout: 30 # 30 seconds

plugin_attr:
prometheus:
export_addr:
ip: "0.0.0.0"
port: 9091

dashboard_config.yaml

默认配置https://github.com/apache/apisix-dashboard/blob/master/api/conf/conf.yaml

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.
# - 0.0.0.0 # 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
- etcd: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
security:
content_security_policy: "frame-src *;"

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

plugins:
- api-breaker
- authz-casbin
- authz-casdoor
- authz-keycloak
- aws-lambda
- azure-functions
- basic-auth
# - batch-requests
- clickhouse-logger
- client-control
- consumer-restriction
- cors
- csrf
- datadog
# - dubbo-proxy
- echo
- error-log-logger
# - example-plugin
- ext-plugin-post-req
- ext-plugin-post-resp
- ext-plugin-pre-req
- fault-injection
- file-logger
- forward-auth
- google-cloud-logging
- grpc-transcode
- grpc-web
- gzip
- hmac-auth
- http-logger
- ip-restriction
- jwt-auth
- kafka-logger
- kafka-proxy
- key-auth
- ldap-auth
- limit-conn
- limit-count
- limit-req
- loggly
# - log-rotate
- mocking
# - node-status
- opa
- openid-connect
- opentelemetry
- openwhisk
- prometheus
- proxy-cache
- proxy-control
- proxy-mirror
- proxy-rewrite
- public-api
- real-ip
- redirect
- referer-restriction
- request-id
- request-validation
- response-rewrite
- rocketmq-logger
- server-info
- serverless-post-function
- serverless-pre-function
- skywalking
- skywalking-logger
- sls-logger
- splunk-hec-logging
- syslog
- tcp-logger
- traffic-split
- ua-restriction
- udp-logger
- uri-blocker
- wolf-rbac
- zipkin
- elasticsearch-logge
- openfunction
- tencent-cloud-cls
- ai
- cas-auth

Service与Upstream的区别

Service = Upstream + Plugin

插件优先级

Consumer > Consumer Group > Route > Plugin Config > Service

自定义nginx配置

有些场景需要自定义nginx 配置,nginx.conf是通过apisix/cli/ngx_tpl.lua脚本来维护,你可以在 conf/nginx.conf 看到生成的 Nginx 配置文件

apisix采用hook的机制来注入配置

例如main_configuration_snippet的hook的lua代码

# main configuration snippet starts
{% if main_configuration_snippet then %}
{* main_configuration_snippet *}
{% end %}
# main configuration snippet ends

对应apisix_config.yaml的配置

nginx_config:
main_configuration_snippet: |
daemon on;

接下来拿一个具体的例子来说明

openid-connect插件使用lua-resty-session来管理session,session默认存储在本机内存里,想要将session数据持久化到redis里,问了chatgpt给出的答案都是这个种,这种无法使用

plugin_attr:                     # 插件属性配置
session:
storage: "redis" # 指定使用 redis 存储
redis_host: "redis" # Redis 服务器地址
redis_port: 6379 # Redis 服务器端口
redis_password: "123456" # Redis 密码,如果有的话
redis_timeout: 1 # Redis 超时时间(秒)
redis_database: 1 # Redis 数据库索引

看了github上的一个issuse回复https://github.com/apache/apisix/issues/6792

·可以通过更改nginx配置的方式来实现session存储的切换

nginx_config:
http_server_configuration_snippet: |
set $session_storage redis;
set $session_secret 0123456789abcdef;
set $session_redis_host redis;
set $session_redis_password 123456;
set $session_redis_port 6379;
set $session_redis_ssl off;
set $session_redis_ssl_verify off;

看到这里,这里有一个限制,如果不ngx_tpl.lua脚本不支持改动的参数,就只能通过改apisix/cli/ngx_tpl.lua脚本来实现了。

插件

proxy-rewirte

如何实现类似nginx代理的效果

proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

使用proxy-rewirte插件

{
"uri": "/your_path",
"plugins": {
"proxy-rewrite": {
"headers": {
"X-Forwarded-Host": "$host",
"X-Forwarded-Server": "$host",
"X-Forwarded-For": "$remote_addr",
"X-Forwarded-Proto": "$scheme"
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"your_upstream_server:port": 1
}
}
}

authz-casdoor

authz-casdoor可以很方便实现未包含登录页面的系统,方便控制权限

使用插件authz-casdoor时,endpoint_addr配置如果使用https的url,会报以下错误

authz-casdoor.lua:133: phase_func(): 20: unable to get local issuer certificate

root用户进入容器

docker exec -ti --user root example-apisix-1 /bin/bash

检查ca-certificates版本

root@e7a846cf1084:/usr/local/apisix# apt search ca-certificates
Sorting... Done
Full Text Search... Done
ca-certificates/oldstable,now 20210119 all [installed]
Common CA certificates

ca-certificates-java/oldstable-updates 20190909+deb11u1 all
Common CA certificates (JKS keystore)

ca-certificates-mono/oldstable 6.8.0.105+dfsg-3.3~deb11u1 all
Common CA certificates (Mono keystore)

如果使用curl直接访问https没问题,authz-casdoor.lua里就报了这个错

魔改了一番代码,通过client:ssl_handshake(nil, host, false)跳过ssl验证,测试通过没问题。

local function fetch_access_token(code, conf)
local client = http.new()
local url = conf.endpoint_addr .. path
local parsed_url = urltools.parse(url)
local host = parsed_url.host
local port = port or (url:sub(1,5) == "https" and 443 or 80)
local ok, err = client:connect(host, port)
if not ok then
ngx.say("failed to connect: ", err)
return
end

client:ssl_handshake(nil, host, false)

local res, err = client:request({
path = path,
method = "POST",
body = ngx.encode_args({
code = code,
grant_type = "authorization_code",
client_id = conf.client_id,
client_secret = conf.client_secret
}),
headers = {
["Content-Type"] = "application/x-www-form-urlencoded"
}
})

local b = res:read_body()
local data, err = core.json.decode(b)

改代码不是很好的方式,看看能不能通过改配置的方式来实现

直接操作容器里conf/nginx.conf

http {
...
lua_ssl_verify_depth 5; # 找到这行配置
lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; # 新增这行配置
...
}

重启容器,更改没生效,原来nginx.conf是通过apisix/cli/ngx_tpl.lua 这个脚本生成的配置文件. 部分配置是通过config.yaml就可以改的,但lua_ssl_trusted_certificate这个配置不支持

直接改apisix/cli/ngx_tpl.lua脚本

lua_ssl_verify_depth 5;  # 找到这行配置
lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; # 新增这行配置

重启重启,测试通过。

以上通过改容器里的代码,不是很方便,在github上搜到这个问题https://github.com/apache/apisix/pull/4553/files/b34db9e15b245ab831a5ac1a8aafdd1b58a3cb0e

在config.yaml文件加上如下配置即可

apisix:
ssl:
ssl_trusted_certificate: /etc/ssl/certs/ca-certificates.crt

再进一步分析,为什么lua-resty-http 模块找不到系统默认的根证书(下次再找原因)

ua-restriction

ua-restriction 插件可以通过将指定 User-Agent 列入白名单或黑名单的方式来限制对服务或路由的访问。

例如以下配置,仅仅允许chrome浏览器访问

"ua-restriction": {
"_meta": {
"disable": false
},
"allowlist": [
"Chrome"
],
"bypass_missing": false
}

http-logger

The http-logger Plugin is used to push log data requests to HTTP/HTTPS servers.

"http-logger": {
"_meta": {
"disable": false
},
"include_req_body": true,
"include_resp_body": true,
"uri": "http://x.x.x.x:8889/log"
}