跳到主要内容

代理传输层 (L4) 流量

默认情况下,APISIX 作为应用层 (L7) 代理运行。APISIX 还支持处理传输层 (L4) TCP 和 UDP 流量,既可以单独处理,也可以在处理应用层 (L7) 流量的基础上处理。

本指南将向你展示如何配置 APISIX 以代理传输层 (L4) 流量,并配置 流路由 以建立与 MySQL 服务器的连接。

前置条件

  • 安装 Docker
  • 安装 cURL 以向服务发送请求进行验证。
  • 安装 jq,以便创建包含 PEM 证书且不会破坏 JSON 转义的资源。
  • 安装 OpenSSL,以创建示例 mTLS 证书。
  • 按照 快速入门教程 在 Docker 或 Kubernetes 中启动一个新的 APISIX 实例。
  • 安装 MySQL Shell 以启动与 MySQL 服务器的连接。

启动 MySQL 服务器

启动一个 MySQL 实例作为示例上游服务,并将 root 密码配置为 my-secret-pw

docker run -d \
--name mysql \
--network=apisix-quickstart-net \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
mysql:9.4

启用传输层 (L4) 代理

默认情况下,APISIX 仅启用应用层 (L7) 代理。要同时代理传输层 (L4) 流量,请配置 proxy_modestream_proxy

更新 config.yaml 配置文件 如下:

docker exec apisix-quickstart /bin/sh -c "echo '
apisix:
enable_control: true
control:
ip: 0.0.0.0
port: 9092
proxy_mode: http&stream
stream_proxy:
tcp:
- 9100
# You can configure additional ports or port ranges.
# - 9200
# - "9300-9310"
# udp:
# - 9400
# - "9500-9510"
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key_required: false
allow_admin:
- 0.0.0.0/0
plugin_attr:
prometheus:
export_addr:
ip: 0.0.0.0
port: 9091
' > /usr/local/apisix/conf/config.yaml"

proxy_mode:同时接受传输层 (L4) 和应用层 (L7) 流量。

stream_proxy:配置传输层 (L4) 代理的接口。

配置 Stream 监听器

tcpudp 数组支持单个端口、绑定地址和端口范围。如果监听器需要终止 TLS,TCP 条目还可以是包含 addrtls 字段的对象。

config.yaml
apisix:
proxy_mode: http&stream
stream_proxy:
tcp:
- 9100
- "127.0.0.1:9101"
- "[::1]:9102"
- "9200-9210"
- addr: "127.0.0.1:9300-9310"
tls: true
udp:
- 9400
- "127.0.0.1:9401"
- "9500-9510"

端口值必须是 1 到 65535 之间的整数。对于端口范围,起始端口不得大于结束端口。请为 "9200-9210" 等范围值加引号,使 YAML 将其保留为字符串。同一配置中可以混用单个端口、地址、IPv6 地址、端口范围和对象条目。

请通过容器运行时、Kubernetes Service、主机防火墙以及所有外部负载均衡器公开每个已配置的监听器。例如,使用 9100 和 9200 到 9210 端口的 Docker 部署,在创建容器时需要同时配置 -p 9100:9100-p 9200-9210:9200-9210。仅在 config.yaml 中添加监听器不会自动将其公开到容器外部。

配置 PROXY 协议

APISIX 可从 TCP 负载均衡器接收 PROXY 协议,并向兼容的上游发送新的 PROXY 头。全局设置为所有 TCP 监听器提供默认值:

config.yaml
apisix:
proxy_mode: http&stream
proxy_protocol:
enable_tcp_pp: true
enable_tcp_pp_to_upstream: true
stream_proxy:
tcp:
- addr: 9100
proxy_protocol: false
proxy_protocol_to_upstream: false
- 9200

使用对象条目可为单个端口覆盖任一方向。显式的 truefalse 优先于相应的全局默认值:

config.yaml
apisix:
proxy_mode: http&stream
proxy_protocol:
enable_tcp_pp: true
stream_proxy:
tcp:
- addr: 9100
proxy_protocol: false
- addr: 9200
- addr: "[::]:9201"
proxy_protocol_to_upstream: true
设置方向效果
proxy_protocol客户端或负载均衡器到 APISIX为 TCP 监听器添加 proxy_protocol 参数,使 APISIX 消费入站头。
proxy_protocol_to_upstreamAPISIX 到上游在被代理的 Stream 数据之前发送 PROXY 头。

这些示例将端口 9200 保留给 PROXY 协议流量,并显式保持本指南的 9100 监听器与后续直接使用的 MySQL 和 HTTP 客户端兼容。使用端口 9200 前,请从网关容器公开它、将 Stream 路由绑定到它,并使用支持已启用 PROXY 协议方向的负载均衡器和上游。客户端和上游兼容之前,请勿在现有监听器上启用任一方向。

这些设置仅适用于 TCP;UDP 监听器绝不会接收或发送 PROXY 协议。

警告

仅在上游预期使用 PROXY 协议时启用 proxy_protocol_to_upstream。否则,上游会将明文 PROXY 行解释为应用数据,通常会关闭连接。TLS 上游无法将该行解析为 TLS 记录,除非它明确在开始 TLS 前接受 PROXY 协议。

默认情况下,APISIX 不信任入站 PROXY 头声明的客户端地址。请仅使用允许提供客户端地址的负载均衡器地址或网络配置 nginx_config.stream.real_ip_from

config.yaml
apisix:
proxy_mode: http&stream
stream_proxy:
tcp:
- addr: 9200
proxy_protocol: true
proxy_protocol_to_upstream: true

nginx_config:
stream:
real_ip_from:
- 192.168.1.0/24
- 2001:db8:1234::/48

对于来自受信任对端的连接,$remote_addr、Stream access log 和基于地址的插件使用入站头中的地址;$realip_remote_addr 保留直接连接的对端地址。

如果对端不受信任,APISIX 会消费入站头,但保留对端地址。因此,向上游重建的 PROXY 头会标识不受信任的对端,而不是它声明的客户端。

信任一个网络会允许其中的任何对端声明任意客户端地址。请将列表限制为你控制的负载均衡器。

重新加载 APISIX 以使配置更改生效:

docker exec apisix-quickstart apisix reload

如果你使用 快速入门 在 Docker 中启动 APISIX,端口 9100 已经映射 (-p 9100:9100)。

创建流路由

创建一个 流路由 并配置 MySQL 服务器作为上游服务。

curl "http://127.0.0.1:9180/apisix/admin/stream_routes" -X PUT -d '
{
"id": "stream-route-mysql",
"server_port": 9100,
"upstream": {
"nodes": {
"mysql:3306": 1
},
"type": "roundrobin"
}
}'

验证

以 root 身份连接 MySQL 服务器,并在提示时输入密码 my-secret-pw

mysql --host=127.0.0.1 --port=9100 -u root -p

如果成功,你应该看到类似以下的欢迎文本:

Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 9.4.0 MySQL Community Server - GPL
Copyright (c) 2000, 2025, Oracle and/or its affiliates.

使用 mTLS 向 TLS 上游进行身份认证

对于要求客户端证书的 Stream 上游,将上游 scheme 设为 tls,并以内联方式或通过客户端 SSL 资源提供证书和私钥。APISIX-Runtime 必须包含 Stream 上游 mTLS 模块。

生成示例 CA、服务端证书和客户端证书:

mkdir -p stream-mtls-certs

openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-subj "/CN=stream-mtls-ca" \
-keyout stream-mtls-certs/ca.key \
-out stream-mtls-certs/ca.crt

openssl req -newkey rsa:2048 -nodes \
-subj "/CN=mtls-upstream" \
-keyout stream-mtls-certs/server.key \
-out stream-mtls-certs/server.csr
openssl x509 -req -days 365 \
-in stream-mtls-certs/server.csr \
-CA stream-mtls-certs/ca.crt \
-CAkey stream-mtls-certs/ca.key \
-CAcreateserial \
-out stream-mtls-certs/server.crt

openssl req -newkey rsa:2048 -nodes \
-subj "/CN=apisix-stream-client" \
-keyout stream-mtls-certs/client.key \
-out stream-mtls-certs/client.csr
openssl x509 -req -days 365 \
-in stream-mtls-certs/client.csr \
-CA stream-mtls-certs/ca.crt \
-CAkey stream-mtls-certs/ca.key \
-CAcreateserial \
-out stream-mtls-certs/client.crt

在 APISIX Docker 网络上启动一个上游,使其要求由示例 CA 签名的客户端证书:

docker run -d --name mtls-upstream \
--network=apisix-quickstart-net \
-v "${PWD}/stream-mtls-certs:/certs:ro" \
alpine/openssl \
s_server -accept 9443 \
-cert /certs/server.crt \
-key /certs/server.key \
-CAfile /certs/ca.crt \
-Verify 1 \
-www

为避免将私钥放入 Upstream 资源,请根据 PEM 文件创建客户端 SSL 资源:

jq -n \
--rawfile cert stream-mtls-certs/client.crt \
--rawfile key stream-mtls-certs/client.key \
'{type: "client", cert: $cert, key: $key}' | \
curl "http://127.0.0.1:9180/apisix/admin/ssls/stream-client" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
--data-binary @-

创建 TLS 上游并引用客户端 SSL 资源:

curl "http://127.0.0.1:9180/apisix/admin/upstreams/stream-mtls" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"scheme": "tls",
"type": "roundrobin",
"nodes": {
"mtls-upstream:9443": 1
},
"tls": {
"client_cert_id": "stream-client"
}
}'

临时更新现有 Stream 路由以使用 mTLS 上游。后续流量拆分示例会再次用 MySQL 上游替换该路由配置。

curl "http://127.0.0.1:9180/apisix/admin/stream_routes/stream-route-mysql" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"server_port": 9100,
"upstream_id": "stream-mtls"
}'

当上游信任签署 client.crt 的 CA 时,TLS 握手成功,Stream 流量会到达上游。APISIX 会在资源校验时拒绝格式错误的内联证书和密钥对;若引用的客户端 SSL 资源缺失或类型错误,则关闭 Stream 会话。如果未配置客户端证书,或上游不信任呈现的证书,上游会拒绝握手,客户端不会收到代理响应。请同时检查 APISIX 和上游 TLS 错误日志,以区分配置和解析错误与上游验证失败。

通过 TCP 监听器发送普通 HTTP 请求。APISIX 会与示例上游建立 TLS 连接并出示其客户端证书:

curl -i "http://127.0.0.1:9100/"

你应收到以下开头的响应:

HTTP/1.0 200 ok
Content-type: text/html

你也可以在 Upstream 或内联 Stream Route 上游中内联配置 upstream.tls.client_certupstream.tls.client_key。启用数据加密时,APISIX 会在存储前加密内联 client_key;Stream Route 的 GET 响应不会以明文返回该密钥。

要使用 $secret://...$env://... 证书引用,请将引用置于客户端 SSL 资源的 certkey 字段,并通过 client_cert_id 选择它。Stream Worker 会在上游握手前解析这些字段。

拆分 Stream 流量

traffic-split 插件可以为 Stream 路由选择备用上游。Stream 路由会向规则表达式公开 route_id 等变量,而加权上游可以通过 ID 引用独立的上游对象。

使用不同的 root 密码启动第二个 MySQL 实例:

docker run -d \
--name mysql-canary \
--network=apisix-quickstart-net \
-e MYSQL_ROOT_PASSWORD=canary-secret-pw \
mysql:9.4

为灰度实例创建备用上游:

curl "http://127.0.0.1:9180/apisix/admin/upstreams/mysql-canary" -X PUT \
-d '{
"type": "roundrobin",
"nodes": {
"mysql-canary:3306": 1
}
}'

更新 Stream 路由,使其路由 ID 匹配的请求使用灰度上游。仅包含权重的条目代表 Stream 路由的默认上游。下面的 1:0 权重会将所有匹配连接发送到灰度实例,以便进行确定性验证。

curl "http://127.0.0.1:9180/apisix/admin/stream_routes/stream-route-mysql" -X PUT \
-d '{
"server_port": 9100,
"plugins": {
"traffic-split": {
"rules": [
{
"match": [
{
"vars": [["route_id", "==", "stream-route-mysql"]]
}
],
"weighted_upstreams": [
{
"upstream_id": "mysql-canary",
"weight": 1
},
{
"weight": 0
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"mysql:3306": 1
}
}
}'

要将约 10% 的匹配连接发送到灰度实例,请将它的权重保持为 1,并将仅包含权重的默认上游条目改为 9。流量拆分必须同时包含两个条目;只修改单个条目的权重不会让其余流量继续使用路由的默认上游。

再次连接到 9100 端口,并在提示时输入 canary-secret-pw

mysql --host=127.0.0.1 --port=9100 -u root -p

连接成功即表示规则选择了 mysql-canary。如果没有规则匹配,APISIX 会使用 Stream 路由的默认上游。

下一步

APISIX 在接受下游客户端请求或代理到上游服务时,还支持在 TCP 连接上使用 TLS 进行传输层(L4)代理。有关相关 TLS 和 Stream 路由概念,请参阅 SSL 证书Stream 路由