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"
}