跳到主要内容

与 HashiCorp Consul 集成

HashiCorp Consul 维护一个记录服务实例及其健康状态的服务目录。通过服务发现,APISIX 可以监视在 Consul 服务名称下注册的健康实例,并将其用作动态上游节点。

本指南将启动一个 Consul 服务器和两个示例服务,为这两个服务注册健康检查,并配置 APISIX 发现健康实例并在它们之间进行负载均衡。

信息

本教程在同一 Docker 网络中运行 APISIX、Consul 和示例服务。生产部署应使用 APISIX 实例可以访问的地址。

如果所有服务都在 Kubernetes 中运行,通常不需要 Consul,因为 Kubernetes 可以通过 Service 和 DNS 提供服务发现。


与 Consul 集成

前置条件

启动 Consul

在 APISIX 快速入门网络中启动一个 Consul 服务器。端口 8500 绑定到环回接口,以供本地访问 API:

docker run \
--name consul \
-d \
-p 127.0.0.1:8500:8500 \
--network apisix-quickstart-net \
hashicorp/consul:2.0.3 \
consul agent \
-server \
-bootstrap-expect=1 \
-node=consul-server \
-client 0.0.0.0 \
-log-level info \
-data-dir=/consul/data

Consul Agent 会监听容器内的所有接口,使 APISIX 能够访问它;发布到主机的端口则仅限环回接口访问。

此单服务器开发配置仅用于本地测试。准备生产 Consul 集群时,请遵循 HashiCorp 的部署指南

启动示例 Web 服务

在 APISIX 快速入门网络中启动两个 NGINX 服务。每个服务返回不同的响应,以便观察负载均衡效果。

创建 web1.conf

cat > web1.conf <<'EOF'
events {
worker_connections 1024;
}

http {
access_log off;
server {
listen 80;
location / {
return 200 "Application 1 is running";
}
}
}
EOF

创建 web2.conf

cat > web2.conf <<'EOF'
events {
worker_connections 1024;
}

http {
access_log off;
server {
listen 80;
location / {
return 200 "Application 2 is running";
}
}
}
EOF

启动 web1

docker run -d \
--name web1 \
--network apisix-quickstart-net \
-v "$(pwd)/web1.conf:/etc/nginx/nginx.conf:ro" \
nginx:1.30.4-alpine

启动 web2

docker run -d \
--name web2 \
--network apisix-quickstart-net \
-v "$(pwd)/web2.conf:/etc/nginx/nginx.conf:ro" \
nginx:1.30.4-alpine

在 Consul 中注册服务

保存 APISIX 快速入门网络中服务容器的地址:

export WEB1_IP="$(
docker inspect \
--format '{{(index .NetworkSettings.Networks "apisix-quickstart-net").IPAddress}}' \
web1
)"

export WEB2_IP="$(
docker inspect \
--format '{{(index .NetworkSettings.Networks "apisix-quickstart-net").IPAddress}}' \
web2
)"

web1 注册为 svc-a 的第一个实例。HTTP 检查让 Consul 仅在 NGINX 正常响应时将该实例报告为健康:

curl "http://127.0.0.1:8500/v1/agent/service/register" -X PUT \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"ID": "svc-a1",
"Name": "svc-a",
"Address": "$WEB1_IP",
"Port": 80,
"Check": {
"HTTP": "http://$WEB1_IP/",
"Interval": "5s",
"Timeout": "2s"
}
}
EOF

web2 注册为第二个实例:

curl "http://127.0.0.1:8500/v1/agent/service/register" -X PUT \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"ID": "svc-a2",
"Name": "svc-a",
"Address": "$WEB2_IP",
"Port": 80,
"Check": {
"HTTP": "http://$WEB2_IP/",
"Interval": "5s",
"Timeout": "2s"
}
}
EOF

AddressPort:APISIX 用于所发现上游节点的网络位置。

Check:Consul 每五秒运行一次的 HTTP 健康检查。APISIX 仅发现检查状态为通过的实例。

验证 Consul 报告的两个服务实例状态均为 passing

curl "http://127.0.0.1:8500/v1/health/service/svc-a?passing=true" | \
jq '[.[] | {
ID: .Service.ID,
Address: .Service.Address,
Port: .Service.Port,
Status: .Checks[-1].Status
}]'

响应应包含 svc-a1svc-a2 两个条目,且状态均为 passing

将 Consul 连接到 APISIX

将 Consul 服务器添加到 APISIX 的 config.yaml 配置文件。该命令会移除 YAML 文档终止符、追加服务发现配置,然后恢复终止符:

docker exec -i apisix-quickstart sh -c \
'sed -i "/^\.\.\.$/d" /usr/local/apisix/conf/config.yaml &&
cat >> /usr/local/apisix/conf/config.yaml' <<'EOF'
discovery:
consul:
servers:
- http://consul:8500
...
EOF

servers 数组包含 Consul HTTP API 地址,APISIX 会监视这些地址上的服务和健康状态更新。

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

docker exec apisix-quickstart apisix reload

在 APISIX 中创建路由

创建一个路由,并将上游配置为使用 Consul 发现 svc-a 服务:

通过 Admin API 创建路由:

curl -i "http://127.0.0.1:9180/apisix/admin/routes/consul-web-route" -X PUT \
--data-binary @- <<'EOF'
{
"uri": "/consul/web/*",
"upstream": {
"service_name": "svc-a",
"discovery_type": "consul",
"type": "roundrobin"
}
}
EOF

响应 HTTP/1.1 201 Created 表明路由已创建。

验证服务发现

验证 APISIX 已发现两个服务实例:

curl -fsS "http://127.0.0.1:9090/v1/discovery/consul/dump" | \
jq --arg web1 "$WEB1_IP" --arg web2 "$WEB2_IP" -e '
([.services["svc-a"][] | select(.port == 80) | .host] | sort) ==
([$web1, $web2] | sort)
'

当发现的节点集合包含两个服务地址时,该命令返回 true。向路由发送多个请求:

for _ in $(seq 1 10); do
curl "http://127.0.0.1:9080/consul/web/"
echo
done

每个请求应返回以下响应之一。你可能会看到两种响应,且顺序可能不同:

Application 1 is running
Application 2 is running

停止 web1,验证 Consul 会从 APISIX 服务发现中移除不健康的实例:

docker stop web1

轮询 APISIX Control API,直到发现的 svc-a 节点集合中只包含 web2。该命令最多按一秒间隔尝试 20 次;如果服务发现未收敛,则以非零状态退出:

for _ in $(seq 1 20); do
nodes="$(curl --max-time 2 -fsS \
"http://127.0.0.1:9090/v1/discovery/consul/dump" | \
jq -r '.services["svc-a"] | map("\(.host):\(.port)") | join(",")')" || nodes=""
[ "$nodes" = "$WEB2_IP:80" ] && break
sleep 1
done

[ "$nodes" = "$WEB2_IP:80" ]

命令成功即确认 APISIX 已从发现的节点中移除 web1。再发送一个请求,验证流量到达 web2

curl "http://127.0.0.1:9080/consul/web/"

响应应为:

Application 2 is running

验证后重新启动 web1

docker start web1

后续步骤

使用服务发现 Control API 端点检查已发现的服务并排查注册中心更新问题。有关更多信息,请参阅 Control API 参考

除 Consul 外,APISIX 还支持与 Eureka、Nacos 和其他服务发现平台集成。