hanzhenfang commented on issue #13385:
URL: https://github.com/apache/apisix/issues/13385#issuecomment-4628006370
Hi @shreemaan-abhishek ,I can reproduce the issue with a minimal Docker
Compose setup on `apache/apisix:3.16.0-debian`.
In this minimal reproduction, the deleted node is still returned by the
health check Control API immediately after it is removed from the upstream
config. It is later purged after the purge window, but during that window the
Control API still exposes the deleted node.
### Environment
- APISIX: `apache/apisix:3.16.0-debian`
- etcd: `bitnamilegacy/etcd:3.6.4`
- Upstream services: `nginx:alpine`
- Health check type: `tcp`
- Deployment mode: traditional / etcd
### docker-compose.yaml
```yaml
services:
etcd:
image: bitnamilegacy/etcd:3.6.4
container_name: apisix-etcd
environment:
- ALLOW_NONE_AUTHENTICATION=yes
- ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379
ports:
- "127.0.0.1:2379:2379"
apisix:
image: apache/apisix:3.16.0-debian
container_name: apisix
depends_on:
- etcd
- upstream-1
- upstream-2
volumes:
- ./config.yaml:/usr/local/apisix/conf/config.yaml:ro
ports:
- "127.0.0.1:9080:9080"
- "127.0.0.1:9090:9090"
- "127.0.0.1:9180:9180"
upstream-1:
image: nginx:alpine
container_name: upstream-1
ports:
- "127.0.0.1:18083:80"
upstream-2:
image: nginx:alpine
container_name: upstream-2
ports:
- "127.0.0.1:18084:80"
```
### config.yaml
```yaml
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
allow_admin:
- 0.0.0.0/0
admin_key:
- name: admin
key: "my-secret-admin-key"
role: admin
etcd:
host:
- "http://etcd:2379"
apisix:
node_listen: 9080
# For local reproduction only.
# Do not expose Control API publicly in production.
enable_control: true
control:
ip: "0.0.0.0"
port: 9090
```
### Steps to reproduce
Start from a clean environment:
```bash
docker compose down --remove-orphans
docker compose up -d
```
Create an upstream with two nodes and active TCP health check:
```bash
curl -i http://127.0.0.1:9180/apisix/admin/upstreams/issue-13385-demo \
-H "X-API-KEY: my-secret-admin-key" \
-H "Content-Type: application/json" \
-X PUT \
-d '{
"type": "roundrobin",
"nodes": {
"upstream-1:80": 1,
"upstream-2:80": 1
},
"checks": {
"active": {
"type": "tcp",
"timeout": 1,
"healthy": {
"interval": 1,
"successes": 2
},
"unhealthy": {
"interval": 1,
"tcp_failures": 2,
"timeouts": 3
}
}
}
}'
```
Create a route using this upstream:
```bash
curl -i http://127.0.0.1:9180/apisix/admin/routes/issue-13385-demo \
-H "X-API-KEY: my-secret-admin-key" \
-H "Content-Type: application/json" \
-X PUT \
-d '{
"uri": "/issue-13385-demo",
"upstream_id": "issue-13385-demo",
"plugins": {
"proxy-rewrite": {
"uri": "/"
}
}
}'
```
Trigger the upstream once so the health checker is initialized:
```bash
curl -i http://127.0.0.1:9080/issue-13385-demo
sleep 3
```
Check the health check state:
```bash
curl -sS http://127.0.0.1:9090/v1/healthcheck/upstreams/issue-13385-demo
```
At this point, both nodes are returned.
Now remove `upstream-1:80` from the upstream config:
```bash
curl -i http://127.0.0.1:9180/apisix/admin/upstreams/issue-13385-demo \
-H "X-API-KEY: my-secret-admin-key" \
-H "Content-Type: application/json" \
-X PUT \
-d '{
"type": "roundrobin",
"nodes": {
"upstream-2:80": 1
},
"checks": {
"active": {
"type": "tcp",
"timeout": 1,
"healthy": {
"interval": 1,
"successes": 2
},
"unhealthy": {
"interval": 1,
"tcp_failures": 2,
"timeouts": 3
}
}
}
}'
```
Confirm the Admin API only contains one node:
```bash
curl -sS http://127.0.0.1:9180/apisix/admin/upstreams/issue-13385-demo \
-H "X-API-KEY: my-secret-admin-key"
```
The upstream config now contains only:
```json
"nodes": {
"upstream-2:80": 1
}
```
Immediately query the health check state again:
```bash
curl -sS http://127.0.0.1:9090/v1/healthcheck/upstreams/issue-13385-demo
```
### Actual behavior
The deleted node is still returned by the Control API health check endpoint.
In my local run:
```text
upstream-1 -> 172.22.0.2
upstream-2 -> 172.22.0.4
```
After deleting `upstream-1:80`, the Admin API only showed `upstream-2:80`,
but the health check endpoint still returned both nodes:
```json
{
"nodes": [
{
"port": 80,
"ip": "172.22.0.2",
"hostname": "172.22.0.2",
"status": "healthy",
"purge_time": 1780631859.964
},
{
"port": 80,
"ip": "172.22.0.4",
"hostname": "172.22.0.4",
"status": "healthy",
"purge_time": 1780631859.964
}
],
"type": "tcp",
"name": "/apisix/upstreams/issue-13385-demo"
}
```
After hitting the route again, the current node no longer had `purge_time`,
but the deleted node was still visible:
```json
{
"nodes": [
{
"port": 80,
"ip": "172.22.0.4",
"hostname": "172.22.0.4",
"status": "healthy"
},
{
"port": 80,
"ip": "172.22.0.2",
"hostname": "172.22.0.2",
"status": "healthy",
"purge_time": 1780631928.128
}
],
"type": "tcp",
"name": "/apisix/upstreams/issue-13385-demo"
}
```
After waiting longer, the deleted node was eventually purged in this minimal
setup:
```bash
sleep 15
curl -sS http://127.0.0.1:9090/v1/healthcheck/upstreams/issue-13385-demo
```
Result:
```json
{
"nodes": [
{
"port": 80,
"ip": "172.22.0.4",
"hostname": "172.22.0.4",
"status": "healthy"
}
],
"type": "tcp",
"name": "/apisix/upstreams/issue-13385-demo"
}
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]