Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 22 additions & 63 deletions apisix/cli/ngx_tpl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
-- limitations under the License.
--

-- Nothing rendered below may depend on which plugins `plugins` or
-- `stream_plugins` in config.yaml happen to list. That list is only the
-- boot-time default: `/apisix/plugins` in etcd replaces it while APISIX runs,
-- long after this template has been rendered. A shared memory zone, or any
-- other nginx directive, cannot be added without a reload, so a plugin enabled
-- that way would otherwise find the memory it needs missing.
return [=[
# Configuration File - Nginx Server Configs
# This is a read-only file, do not try to modify it.
Expand Down Expand Up @@ -65,12 +71,12 @@ env "{*name*}";
thread_pool grpc-client-nginx-module threads=1;

lua {
{% if enabled_stream_plugins["prometheus"] then %}
{% if enable_stream then %}
# declared here rather than in http{} so that the stream subsystem, which
# cannot see dicts declared inside http{}, shares the same metrics
lua_shared_dict prometheus-metrics {* meta.lua_shared_dict["prometheus-metrics"] *};
{% end %}
{% if enabled_plugins["prometheus"] or enabled_stream_plugins["prometheus"] then %}
lua_shared_dict prometheus-cache {* meta.lua_shared_dict["prometheus-cache"] *};
{% end %}
{% if standalone_with_admin_api then %}
lua_shared_dict standalone-config {* meta.lua_shared_dict["standalone-config"] *};
lua_shared_dict standalone-status {* meta.lua_shared_dict["standalone-status"] *};
Expand All @@ -82,16 +88,15 @@ lua {
lua_shared_dict upstream-healthcheck {* meta.lua_shared_dict["upstream-healthcheck"] *};
}

{% if enabled_stream_plugins["prometheus"] and not enable_http then %}
{% if not enable_http and prometheus_server_addr then %}
# the stream subsystem has no server of its own to export metrics from
http {
lua_package_path "{*extra_lua_path*}$prefix/deps/share/lua/5.1/?.lua;$prefix/deps/share/lua/5.1/?/init.lua;]=]
.. [=[{*apisix_lua_home*}/?.lua;{*apisix_lua_home*}/?/init.lua;;{*lua_path*};";
lua_package_cpath "{*extra_lua_cpath*}$prefix/deps/lib64/lua/5.1/?.so;]=]
.. [=[$prefix/deps/lib/lua/5.1/?.so;;]=]
.. [=[{*lua_cpath*};";

{% if enabled_stream_plugins["prometheus"] then %}

init_by_lua_block {
require "resty.core"
local process = require("ngx.process")
Expand Down Expand Up @@ -125,7 +130,6 @@ http {
stub_status;
}
}
{% end %}
}
{% end %}

Expand All @@ -150,7 +154,7 @@ stream {
# backs apisix_stream_active_connections and apisix_stream_bandwidth; the
# counters live in nginx so that they keep moving during a long-lived
# session instead of only being known once it ends
{% if use_apisix_base and enabled_stream_plugins["prometheus"] and stream.metrics_zone_size then %}
{% if use_apisix_base and stream.metrics_zone_size then %}
apisix_stream_metrics_zone {* stream.metrics_zone_size *};
{% end %}

Expand All @@ -163,9 +167,7 @@ stream {
lua_shared_dict tars-stream {* stream.lua_shared_dict["tars-stream"] *};
{% end %}

{% if enabled_stream_plugins["limit-conn"] then %}
lua_shared_dict plugin-limit-conn-stream {* stream.lua_shared_dict["plugin-limit-conn-stream"] *};
{% end %}

# for discovery shared dict
{% if discovery_shared_dicts then %}
Expand Down Expand Up @@ -438,79 +440,50 @@ http {
lua_shared_dict plugin-ai-rate-limiting-reset-header 10m;
{% end %}

{% if enabled_plugins["limit-conn"] then %}
lua_shared_dict plugin-limit-conn {* http.lua_shared_dict["plugin-limit-conn"] *};
lua_shared_dict plugin-limit-conn-redis-cluster-slot-lock {* http.lua_shared_dict["plugin-limit-conn-redis-cluster-slot-lock"] *};
{% end %}

{% if enabled_plugins["limit-req"] then %}
lua_shared_dict plugin-limit-req-redis-cluster-slot-lock {* http.lua_shared_dict["plugin-limit-req-redis-cluster-slot-lock"] *};
lua_shared_dict plugin-limit-req {* http.lua_shared_dict["plugin-limit-req"] *};
{% end %}

{% if enabled_plugins["limit-count"] then %}
lua_shared_dict plugin-limit-count {* http.lua_shared_dict["plugin-limit-count"] *};
lua_shared_dict plugin-limit-count-lock {* http.lua_shared_dict["plugin-limit-count-lock"] *};
lua_shared_dict plugin-limit-count-redis-cluster-slot-lock {* http.lua_shared_dict["plugin-limit-count-redis-cluster-slot-lock"] *};
lua_shared_dict plugin-limit-count-reset-header {* http.lua_shared_dict["plugin-limit-count"] *};
{% end %}

{% if enabled_plugins["limit-conn"] or enabled_plugins["limit-req"] or enabled_plugins["limit-count"] then %}
# tracks unhealthy redis cluster nodes for fast-fail
lua_shared_dict redis_cluster_health 10m;
{% end %}

{% if enabled_plugins["graphql-limit-count"] then %}
lua_shared_dict plugin-graphql-limit-count {* http.lua_shared_dict["plugin-graphql-limit-count"] *};
lua_shared_dict plugin-graphql-limit-count-reset-header {* http.lua_shared_dict["plugin-graphql-limit-count-reset-header"] *};
{% if not enabled_plugins["limit-count"] then %}
lua_shared_dict plugin-limit-count-redis-cluster-slot-lock {* http.lua_shared_dict["plugin-limit-count-redis-cluster-slot-lock"] *};
{% end %}
{% end %}

{% if enabled_plugins["prometheus"] and not enabled_stream_plugins["prometheus"] then %}
{% if not (use_apisix_base and enable_stream) then %}
# on APISIX-Base with the stream subsystem on, this one lives in lua{} so
# that both subsystems share it
lua_shared_dict prometheus-metrics {* http.lua_shared_dict["prometheus-metrics"] *};
{% end %}

{% if enabled_plugins["skywalking"] then %}
lua_shared_dict tracing_buffer {* http.lua_shared_dict.tracing_buffer *}; # plugin: skywalking
{% end %}

{% if enabled_plugins["api-breaker"] then %}
lua_shared_dict plugin-api-breaker {* http.lua_shared_dict["plugin-api-breaker"] *};
{% end %}

{% if enabled_plugins["openid-connect"] or enabled_plugins["authz-keycloak"] then %}
# for openid-connect and authz-keycloak plugin
lua_shared_dict discovery {* http.lua_shared_dict["discovery"] *}; # cache for discovery metadata documents
{% end %}

{% if enabled_plugins["openid-connect"] then %}
# for openid-connect plugin
lua_shared_dict jwks {* http.lua_shared_dict["jwks"] *}; # cache for JWKs
lua_shared_dict introspection {* http.lua_shared_dict["introspection"] *}; # cache for JWT verification results
{% end %}

{% if enabled_plugins["cas-auth"] then %}
lua_shared_dict cas_sessions {* http.lua_shared_dict["cas-auth"] *};
{% end %}

{% if enabled_plugins["authz-keycloak"] then %}
# for authz-keycloak
lua_shared_dict access-tokens {* http.lua_shared_dict["access-tokens"] *}; # cache for service account access tokens
{% end %}

{% if enabled_plugins["ocsp-stapling"] then %}
lua_shared_dict ocsp-stapling {* http.lua_shared_dict["ocsp-stapling"] *}; # cache for ocsp-stapling
{% end %}

{% if enabled_plugins["ext-plugin-pre-req"] or enabled_plugins["ext-plugin-post-req"] then %}
lua_shared_dict ext-plugin {* http.lua_shared_dict["ext-plugin"] *}; # cache for ext-plugin
{% end %}

{% if enabled_plugins["mcp-bridge"] or enabled_plugins["openapi-to-mcp"] then %}
lua_shared_dict mcp-session {* http.lua_shared_dict["mcp-session"] *}; # cache for mcp-session
{% end %}

{% if config_center == "xds" then %}
lua_shared_dict xds-config 10m;
Expand All @@ -524,9 +497,7 @@ http {
{% end %}
{% end %}

{% if enabled_plugins["error-log-logger"] then %}
lua_capture_error_log 10m;
{% end %}
lua_capture_error_log 10m;

lua_ssl_verify_depth 5;
ssl_session_timeout 86400;
Expand Down Expand Up @@ -616,7 +587,7 @@ http {
{% end %}
}

{% if enabled_plugins["dubbo-proxy"] then %}
{% if use_apisix_base then %}
upstream apisix_dubbo_backend {
server 0.0.0.1;
balancer_by_lua_block {
Expand Down Expand Up @@ -710,7 +681,7 @@ http {
}
{% end %}

{% if enabled_plugins["prometheus"] and prometheus_server_addr then %}
{% if prometheus_server_addr then %}
server {
listen {* prometheus_server_addr *} reuseport;

Expand Down Expand Up @@ -812,7 +783,7 @@ http {

{% if deployment_role ~= "control_plane" then %}

{% if enabled_plugins["proxy-cache"] or enabled_plugins["graphql-proxy-cache"] then %}
{% if proxy_cache and proxy_cache.zones then %}
# for proxy cache
{% for _, cache in ipairs(proxy_cache.zones) do %}
{% if cache.disk_path and cache.cache_levels and cache.disk_size then %}
Expand Down Expand Up @@ -955,7 +926,7 @@ http {
{% end %}
# http server location configuration snippet ends

{% if enabled_plugins["dubbo-proxy"] then %}
{% if use_apisix_base then %}
set $dubbo_service_name '';
set $dubbo_service_version '';
set $dubbo_method '';
Expand Down Expand Up @@ -1036,7 +1007,7 @@ http {
# to be appended, which only $proxy_add_x_forwarded_for does.
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

{% if enabled_plugins["proxy-cache"] or enabled_plugins["graphql-proxy-cache"] then %}
{% if proxy_cache and proxy_cache.zones then %}
### the following configuration is to cache response content from upstream server
set $upstream_cache_zone off;
set $upstream_cache_key '';
Expand All @@ -1057,9 +1028,7 @@ http {

proxy_pass $upstream_scheme://apisix_backend$upstream_uri;

{% if enabled_plugins["proxy-mirror"] then %}
mirror /proxy_mirror;
{% end %}

header_filter_by_lua_block {
apisix.http_header_filter_phase()
Expand Down Expand Up @@ -1096,9 +1065,7 @@ http {
grpc_ssl_name $upstream_host;
grpc_pass $upstream_scheme://apisix_backend;

{% if enabled_plugins["proxy-mirror"] then %}
mirror /proxy_mirror_grpc;
{% end %}

header_filter_by_lua_block {
apisix.http_header_filter_phase()
Expand All @@ -1113,7 +1080,7 @@ http {
}
}

{% if enabled_plugins["dubbo-proxy"] then %}
{% if use_apisix_base then %}
location @dubbo_pass {
access_by_lua_block {
apisix.dubbo_access_phase()
Expand All @@ -1137,7 +1104,6 @@ http {
}
{% end %}

{% if enabled_plugins["proxy-buffering"] then %}
location @disable_proxy_buffering {
access_by_lua_block {
apisix.disable_proxy_buffering_access_phase()
Expand All @@ -1160,9 +1126,7 @@ http {

proxy_pass $upstream_scheme://apisix_backend$upstream_uri;

{% if enabled_plugins["proxy-mirror"] then %}
mirror /proxy_mirror;
{% end %}

header_filter_by_lua_block {
apisix.http_header_filter_phase()
Expand All @@ -1178,9 +1142,7 @@ http {

proxy_buffering off;
}
{% end %}

{% if enabled_plugins["proxy-mirror"] then %}
location = /proxy_mirror {
internal;

Expand All @@ -1206,9 +1168,7 @@ http {
proxy_set_header Host $upstream_host;
proxy_pass $upstream_mirror_uri;
}
{% end %}

{% if enabled_plugins["proxy-mirror"] then %}
location = /proxy_mirror_grpc {
internal;

Expand All @@ -1233,7 +1193,6 @@ http {
rewrite ^ $upstream_mirror_grpc_path break;
grpc_pass $upstream_mirror_host;
}
{% end %}
}
{% end %}

Expand Down
4 changes: 1 addition & 3 deletions apisix/cli/ops.lua
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ Please modify "admin_key" in conf/config.yaml .
end

local zipkin_set_ngx_var
if enabled_plugins["zipkin"] and yaml_conf.plugin_attr["zipkin"] then
if yaml_conf.plugin_attr["zipkin"] then
zipkin_set_ngx_var = yaml_conf.plugin_attr["zipkin"].set_ngx_var
end

Expand All @@ -835,8 +835,6 @@ Please modify "admin_key" in conf/config.yaml .
enable_http = enable_http,
enable_stream = enable_stream,
enabled_discoveries = enabled_discoveries,
enabled_plugins = enabled_plugins,
enabled_stream_plugins = enabled_stream_plugins,
dubbo_upstream_multiplex_count = dubbo_upstream_multiplex_count,
status_server_addr = status_server_addr,
admin_server_addr = admin_server_addr,
Expand Down
55 changes: 48 additions & 7 deletions apisix/plugin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -417,12 +417,26 @@ local function get_plugin_names(config)
end


-- Whether the stream subsystem runs, which decides if the exporter has to
-- carry the L4 metrics. Deliberately not `stream_plugins` from config.yaml:
-- that list is only the boot-time default, and /apisix/plugins in etcd can
-- turn the stream prometheus plugin on later, by which time the metrics have
-- to already exist.
local function stream_subsystem_enabled()
local conf = core.config.local_conf()
local proxy_mode = conf.apisix.proxy_mode
return proxy_mode == "stream" or proxy_mode == "http&stream"
end


function _M.load(config)
local ignored, http_plugin_names, stream_plugin_names = get_plugin_names(config)
if ignored then
return local_plugins
end

local exporter = require("apisix.plugins.prometheus.exporter")

if ngx.config.subsystem == "http" then
if not http_plugin_names then
core.log.error("failed to read plugin list from local file")
Expand All @@ -436,6 +450,23 @@ function _M.load(config)
if not ok then
core.log.error("failed to load plugins: ", err)
end

-- The exporter is not built by the plugin's own init hook: that
-- hook cannot build it on a hot load, because http_init() returns
-- early outside the init phases. init_worker built it whatever
-- config.yaml listed, so here it is only restored or dropped --
-- which is what lets /apisix/plugins turn prometheus on later.
--
-- `active` is false on every reload, not only when prometheus was
-- just switched on: load() above unloads each plugin first, and
-- prometheus exports exporter.destroy() as its `destroy` hook.
local enabled = local_plugins_hash["prometheus"] ~= nil
local active = exporter.get_prometheus() ~= nil
if not enabled then
exporter.destroy()
elseif not active then
exporter.http_init(stream_subsystem_enabled())
end
end
end

Expand Down Expand Up @@ -922,20 +953,30 @@ end


function _M.init_prometheus()
local _, http_plugin_names, stream_plugin_names = get_plugin_names()
local enabled_in_http = core.table.array_find(http_plugin_names, "prometheus")
local enabled_in_stream = core.table.array_find(stream_plugin_names, "prometheus")

-- Not gated on the plugin list either: /apisix/plugins can enable
-- prometheus after this has run, and nothing would start the timer then.
-- It returns at once while the exporter is destroyed, which is the state
-- load() leaves it in when prometheus is not enabled.
-- For stream-only mode, there are separate calls in ngx_tpl.lua.
-- And for other modes, whether in stream or http plugins,
-- the prometheus exporter needs to be initialized.
if is_http and (enabled_in_http or enabled_in_stream) then
if is_http then
require("apisix.plugins.prometheus.exporter").init_exporter_timer()
end
end


function _M.init_worker()
-- Built here rather than from the plugin's init hook, and whatever
-- `plugins` in config.yaml lists: http_init() returns early outside the
-- init phases, so it can restore what this built but cannot build it on a
-- hot load. load() below destroys it again when prometheus is not in the
-- list, so a deployment that never enables it keeps paying nothing.
local exporter = require("apisix.plugins.prometheus.exporter")
if is_http then
exporter.http_init(stream_subsystem_enabled())
else
exporter.stream_init()
end

-- someone's plugin needs to be initialized after prometheus
-- see https://github.com/apache/apisix/issues/3286
_M.load()
Expand Down
Loading
Loading