Revolyssup opened a new pull request, #12667: URL: https://github.com/apache/apisix/pull/12667
In a previous change, the prometheus server was moved from priviliged agent to worker which sometimes during restarts causes port conflict issues. This PR fixes it by enabling port reuse for prometheus server address. ## Reproduction steps 1. Run the following script ```bash #!/bin/bash # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # DEFAULT_ETCD_IMAGE_NAME="bitnamilegacy/etcd" DEFAULT_ETCD_IMAGE_TAG="3.5.7" DEFAULT_APISIX_IMAGE_NAME="apache/apisix" DEFAULT_APISIX_IMAGE_TAG="3.14.0-ubuntu" DEFAULT_ETCD_LISTEN_PORT=2379 DEFAULT_APISIX_PORT=9180 DEFAULT_ETCD_NAME="etcd-quickstart" DEFAULT_APP_NAME="apisix-quickstart" DEFAULT_NET_NAME="apisix-quickstart-net" usage() { echo "Runs a Docker based Apache APISIX." echo echo "See the document for more information:" echo " https://docs.api7.ai/apisix/getting-started" exit 0 } echo_fail() { printf "\e[31m✘ \e[0m$@\n" } echo_pass() { printf "\e[32m✔ \e[0m$@\n" } echo_warning() { printf "\e[33m⚠ $@\e[0m\n" } ensure_docker() { { docker ps -q >/dev/null 2>&1 } || { return 1 } } ensure_curl() { { curl -h >/dev/null 2>&1 } || { return 1 } } install_apisix() { echo "Installing APISIX with the quickstart options." echo "" echo "Creating bridge network ${DEFAULT_NET_NAME}." docker network create -d bridge $DEFAULT_NET_NAME && echo_pass "network ${DEFAULT_NET_NAME} created" || { echo_fail "Create network failed!" return 1 } echo "" echo "Starting the container ${DEFAULT_ETCD_NAME}." docker run -d \ --name ${DEFAULT_ETCD_NAME} \ --network=$DEFAULT_NET_NAME \ -e ALLOW_NONE_AUTHENTICATION=yes \ -e ETCD_ADVERTISE_CLIENT_URLS=http://${DEFAULT_ETCD_NAME}:${DEFAULT_ETCD_LISTEN_PORT} \ ${DEFAULT_ETCD_IMAGE_NAME}:${DEFAULT_ETCD_IMAGE_TAG} && echo_pass "etcd is listening on ${DEFAULT_ETCD_NAME}:${DEFAULT_ETCD_LISTEN_PORT}" || { echo_fail "Start etcd failed!" return 1 } echo "" APISIX_DEPLOYMENT_ETCD_HOST="[\"http://${DEFAULT_ETCD_NAME}:${DEFAULT_ETCD_LISTEN_PORT}\"]" echo "Starting the container ${DEFAULT_APP_NAME}." docker run -d \ --name ${DEFAULT_APP_NAME} \ --network=$DEFAULT_NET_NAME \ -p9080:9080 -p9180:9180 -p9443:9443/tcp -p9443:9443/udp -p9090:9092 -p9100:9100 -p9091:9091 \ -e APISIX_DEPLOYMENT_ETCD_HOST=${APISIX_DEPLOYMENT_ETCD_HOST} \ ${DEFAULT_APISIX_IMAGE_NAME}:${DEFAULT_APISIX_IMAGE_TAG} && validate_apisix && sleep 2 || { echo_fail "Start APISIX failed!" return 1 } docker exec ${DEFAULT_APP_NAME} /bin/bash -c "echo ' apisix: enable_control: true control: ip: "0.0.0.0" port: 9092 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" docker exec ${DEFAULT_APP_NAME} apisix reload >>/dev/null 2>&1 echo_warning "WARNING: The Admin API key is currently disabled. You should turn on admin_key_required and set a strong Admin API key in production for security." echo "" } destroy_apisix() { echo "Destroying existing ${DEFAULT_APP_NAME} container, if any." echo "" docker rm -f $DEFAULT_APP_NAME >>/dev/null 2>&1 docker rm -f $DEFAULT_ETCD_NAME >>/dev/null 2>&1 docker network rm $DEFAULT_NET_NAME >>/dev/null 2>&1 sleep 2 } validate_apisix() { local rv=0 retry 30 curl "http://localhost:${DEFAULT_APISIX_PORT}/apisix/admin/services" >>/dev/null 2>&1 && echo_pass "APISIX is up" || rv=$? } main() { ensure_docker || { echo_fail "Docker is not available, please install it first" exit 1 } ensure_curl || { echo_fail "curl is not available, please install it first" exit 1 } destroy_apisix install_apisix || { exit 1 } echo_pass "APISIX is ready!" } main "$@" ``` 1. Send request ```bash curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d ' { "id": "getting-started-ip", "uri": "/ip", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' HTTP/1.1 403 Forbidden Server: openresty Date: Mon, 13 Oct 2025 10:15:20 GMT Content-Type: text/html; charset=utf-8 Content-Length: 225 Connection: keep-alive <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> <hr><center>openresty</center> <p><em>Powered by <a href="https://apisix.apache.org/">APISIX</a>.</em></p></body> </html> ``` Result: Request will be forbidden. To confirm check error log and you will see the log `bind() to 0.0.0.0:9091 failed (98: Address already in use)` ## Fix The above script is modified to update the code in apisix container with the fix ```bash #!/bin/bash # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # DEFAULT_ETCD_IMAGE_NAME="bitnamilegacy/etcd" DEFAULT_ETCD_IMAGE_TAG="3.5.7" DEFAULT_APISIX_IMAGE_NAME="apache/apisix" DEFAULT_APISIX_IMAGE_TAG="3.14.0-ubuntu" DEFAULT_ETCD_LISTEN_PORT=2379 DEFAULT_APISIX_PORT=9180 DEFAULT_ETCD_NAME="etcd-quickstart" DEFAULT_APP_NAME="apisix-quickstart" DEFAULT_NET_NAME="apisix-quickstart-net" usage() { echo "Runs a Docker based Apache APISIX." echo echo "See the document for more information:" echo " https://docs.api7.ai/apisix/getting-started" exit 0 } echo_fail() { printf "\e[31m✘ \e[0m$@\n" } echo_pass() { printf "\e[32m✔ \e[0m$@\n" } echo_warning() { printf "\e[33m⚠ $@\e[0m\n" } ensure_docker() { { docker ps -q >/dev/null 2>&1 } || { return 1 } } ensure_curl() { { curl -h >/dev/null 2>&1 } || { return 1 } } install_apisix() { echo "Installing APISIX with the quickstart options." echo "" echo "Creating bridge network ${DEFAULT_NET_NAME}." docker network create -d bridge $DEFAULT_NET_NAME && echo_pass "network ${DEFAULT_NET_NAME} created" || { echo_fail "Create network failed!" return 1 } echo "" echo "Starting the container ${DEFAULT_ETCD_NAME}." docker run -d \ --name ${DEFAULT_ETCD_NAME} \ --network=$DEFAULT_NET_NAME \ -e ALLOW_NONE_AUTHENTICATION=yes \ -e ETCD_ADVERTISE_CLIENT_URLS=http://${DEFAULT_ETCD_NAME}:${DEFAULT_ETCD_LISTEN_PORT} \ ${DEFAULT_ETCD_IMAGE_NAME}:${DEFAULT_ETCD_IMAGE_TAG} && echo_pass "etcd is listening on ${DEFAULT_ETCD_NAME}:${DEFAULT_ETCD_LISTEN_PORT}" || { echo_fail "Start etcd failed!" return 1 } echo "" APISIX_DEPLOYMENT_ETCD_HOST="[\"http://${DEFAULT_ETCD_NAME}:${DEFAULT_ETCD_LISTEN_PORT}\"]" echo "Starting the container ${DEFAULT_APP_NAME}." docker run -d \ --name ${DEFAULT_APP_NAME} \ --network=$DEFAULT_NET_NAME \ -p9080:9080 -p9180:9180 -p9443:9443/tcp -p9443:9443/udp -p9090:9092 -p9100:9100 -p9091:9091 \ -e APISIX_DEPLOYMENT_ETCD_HOST=${APISIX_DEPLOYMENT_ETCD_HOST} \ ${DEFAULT_APISIX_IMAGE_NAME}:${DEFAULT_APISIX_IMAGE_TAG} && validate_apisix && sleep 2 || { echo_fail "Start APISIX failed!" return 1 } #fix docker exec ${DEFAULT_APP_NAME} sed -i 's/listen {\* prometheus_server_addr \*};/listen {\* prometheus_server_addr \*} reuseport so_keepalive=off;/g' /usr/local/apisix/apisix/cli/ngx_tpl.lua docker exec ${DEFAULT_APP_NAME} apisix reload >>/dev/null 2>&1 docker exec ${DEFAULT_APP_NAME} /bin/bash -c "echo ' apisix: enable_control: true control: ip: "0.0.0.0" port: 9092 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" docker exec ${DEFAULT_APP_NAME} apisix reload >>/dev/null 2>&1 echo_warning "WARNING: The Admin API key is currently disabled. You should turn on admin_key_required and set a strong Admin API key in production for security." echo "" } destroy_apisix() { echo "Destroying existing ${DEFAULT_APP_NAME} container, if any." echo "" docker rm -f $DEFAULT_APP_NAME >>/dev/null 2>&1 docker rm -f $DEFAULT_ETCD_NAME >>/dev/null 2>&1 docker network rm $DEFAULT_NET_NAME >>/dev/null 2>&1 sleep 2 } validate_apisix() { local rv=0 retry 30 curl "http://localhost:${DEFAULT_APISIX_PORT}/apisix/admin/services" >>/dev/null 2>&1 && echo_pass "APISIX is up" || rv=$? } main() { ensure_docker || { echo_fail "Docker is not available, please install it first" exit 1 } ensure_curl || { echo_fail "curl is not available, please install it first" exit 1 } destroy_apisix install_apisix || { exit 1 } echo_pass "APISIX is ready!" } main "$@" ``` 1. Send curl request ```bash curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d ' { "id": "getting-started-ip", "uri": "/ip", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }' HTTP/1.1 201 Created Date: Mon, 13 Oct 2025 10:33:51 GMT Content-Type: application/json Transfer-Encoding: chunked Connection: keep-alive Server: APISIX/3.14.0 Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: * Access-Control-Max-Age: 3600 X-API-VERSION: v3 {"key":"/apisix/routes/getting-started-ip","value":{"create_time":1760351631,"upstream":{"type":"roundrobin","nodes":{"httpbin.org:80":1}},"uri":"/ip","update_time":1760351631,"id":"getting-started-ip"}} ``` Result: The request passes ### Checklist - [ ] I have explained the need for this PR and the problem it solves - [ ] I have explained the changes or the new features added to this PR - [ ] I have added tests corresponding to this change - [ ] I have updated the documentation to reflect this change - [ ] I have verified that this change is backward compatible (If not, please discuss on the [APISIX mailing list](https://github.com/apache/apisix/tree/master#community) first) <!-- Note 1. Mark the PR as draft until it's ready to be reviewed. 2. Always add/update tests for any changes unless you have a good reason. 3. Always update the documentation to reflect the changes made in the PR. 4. Make a new commit to resolve conversations instead of `push -f`. 5. To resolve merge conflicts, merge master instead of rebasing. 6. Use "request review" to notify the reviewer after making changes. 7. Only a reviewer can mark a conversation as resolved. --> -- 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]
