Ops Lua Analysis En

Categories:

APISIX CLI ops.lua Source Code Analysis

File Overview

apisix/cli/ops.lua is the CLI entry point module of Apache APISIX, responsible for handling all CLI subcommands (init, start, stop, restart, reload, test, etc.). It converts YAML configuration into nginx.conf and manages the OpenResty process lifecycle.


1. Command Routing Mechanism (action Table + _M.execute)

-- Lines 1104-1115: Command registry
local action = {
    help = help,
    version = version,
    init = init,
    init_etcd = etcd.init,
    start = start,
    stop = stop,
    quit = quit,
    restart = restart,
    reload = reload,
    test = test,
}

-- Lines 1118-1130: Command dispatch
function _M.execute(env, arg)
    local cmd_action = arg[1]
    if not cmd_action then
        return help()
    end
    if not action[cmd_action] then
        stderr:write("invalid argument: ", cmd_action, "\n")
        return help()
    end
    action[cmd_action](env, arg[2])
end

Design note: An action table maps commands to functions — simple and extensible. Invalid commands print a hint and display help.


2. init(env) — Initialize nginx.conf (Lines 234-890)

This is the core function, responsible for generating nginx configuration from YAML. The flow:

2.1 Environment Pre-check (Lines 236-248)

  • Detect running under /root (only allowed in dev environments)
  • Check ulimit; warn if below 1024

2.2 YAML Loading & Validation (Lines 251-259)

local yaml_conf, err = file.read_yaml_conf(env.apisix_home)
local ok, err = schema.validate(yaml_conf)

2.3 Key Configuration Validation

Check Location Description
admin_key Lines 284-331 Checks if Admin API token is set; supports auto-generation of empty tokens
admin_api_mtls Lines 333-344 SSL cert must be configured when https_admin is enabled
OpenResty version Lines 346-354 Requires >= 1.21.4
http_stub_status_module Lines 357-361 This module must be compiled in
proxy-cache plugin Lines 397-399 apisix.proxy_cache must be configured when enabled
batch-requests plugin Lines 401-421 Checks if real_ip_from includes loopback
standalone mode Lines 262-281 config_provider must be yaml/json when APISIX_STAND_ALONE=true

2.4 Proxy Mode Handling (Lines 362-380)

if yaml_conf.apisix.proxy_mode == "http" then
    enable_http, enable_stream = true, false
elseif yaml_conf.apisix.proxy_mode == "stream" then
    enable_http, enable_stream = false, true
elseif yaml_conf.apisix.proxy_mode == "http&stream" then
    enable_http, enable_stream = true, true
end

2.5 Port Conflict Detection (Lines 423-521)

validate_and_get_listen_addr() and listen_table_insert() use a ports_to_check table to prevent conflicts among admin/status/control/prometheus/HTTP/HTTPS ports.

Ports involved:

  • Admin: default 9180
  • Status: default 7085
  • Control: default 9090
  • Prometheus: default 9091

2.6 HTTP/HTTPS Listen Normalization (Lines 523-601)

  • Normalizes node_listen (which can be a number, table, or nested table) to a standard format [{ip, port, enable_http3}]
  • Normalizes ssl.listen similarly
  • Since v3.9, enable_http2 has moved from port-level to the global apisix level; port-level configs now raise errors

2.7 Port Validation (validate_port_or_range, Lines 61-131)

A hand-written port number/range validator supporting:

  • Integer port numbers (1-65535)
  • Range format (start-end)
  • IPv4 with port (0.0.0.0:8080)
  • IPv6 with port ([::1]:8080)

2.8 Template Rendering (Lines 679-889)

Packs all processed configuration into a sys_conf table, renders the nginx template using resty.template, and writes to conf/nginx.conf.

Key sys_conf fields:

  • worker_rlimit_nofile: Automatically set to worker_connections + 128
  • worker_processes: Forced to 1 in dev_mode, otherwise defaults to “auto”
  • dns_resolver: Auto-read from /etc/resolv.conf when not configured
  • extra_lua_path/cpath: Appends custom Lua paths
  • envs: Injects environment variables (including those needed for Kubernetes service discovery)

2.9 Cluster Role Handling (Lines 663-671)

if role == "control_plane" and not admin_server_addr then
    local listen = node_listen[1]
    admin_server_addr = str_format("%s:%s", listen.ip, listen.port)
end

The control_plane role automatically reuses the first node_listen address as the admin address.

2.10 Service Discovery Special Handling (Lines 811-876)

  • Kubernetes: Injects shared_dict, resolves environment variable placeholders (${VAR})
  • Consul: Injects shared_dict

3. start(env, ...) — Start APISIX (Lines 923-1019)

Execution flow:

  1. cleanup(env) — Delete custom yaml index files
  2. Deny root execution — Hard reject in production
  3. Create logs directory
  4. Wait for old process to exit — Up to 3 seconds (30× 0.1s), detected via signal.kill(pid, 0)
  5. Parse -c argument — Supports --config to specify a custom YAML path
  6. init(env) — Generate nginx.conf
  7. init_etcd(env, args) — Initialize etcd for non-data_plane roles
  8. Execute openresty — The actual launch

4. test(env) — Config Test (Lines 1022-1057)

-- Backup original nginx.conf → generate new → nginx -t → restore original nginx.conf

This is a non-destructive operation that guarantees restoration of the original nginx.conf. Handles macOS/Linux os.execute return value differences.


5. stop(env) / quit(env) — Stop Service (Lines 1060-1073)

local cmd = env.openresty_args .. [[ -s quit]]   -- Graceful stop
local cmd = env.openresty_args .. [[ -s stop]]   -- Fast stop

Both run cleanup(env) first, then send the corresponding nginx signal.

  • quit: Graceful stop; waits for workers to finish current requests
  • stop: Fast stop; immediately terminates all workers

6. restart(env) / reload(env) — Restart & Hot Reload (Lines 1076-1100)

restart: test → stop → start (verify first, then stop, then start)

reload: init → nginx -t → nginx -s reload (hot reload without service interruption)

On reload failure, only prints a message without die, avoiding disruption to the running instance.


7. Helper Functions

Function Location Purpose
local_dns_resolver() Line 189 Parse DNS server addresses from /etc/resolv.conf
version_greater_equal() Line 152 Semantic version comparison
get_openresty_version() Line 173 Get version via openresty -v
check_running() Line 912 Check if process is alive via pid file
get_lua_path() Line 215 Normalize Lua path (ensure trailing ;)
validate_port_or_range() Line 61 Port/port-range validity check
validate_and_get_listen_addr() Line 425 Inner function: validate port conflicts and build address string
listen_table_insert() Line 480 Inner function: normalize listen table insertion (supports IPv6 dual-stack)
cleanup() Line 898 Delete custom yaml index files
sleep() Line 907 Wait via os.execute("sleep " .. n)

8. Security & Design Highlights

  1. Port conflict checks — Multiple service ports (admin/status/control/prometheus/http/https) unified through ports_to_check to prevent conflicts
  2. Pre-emptive config validation — start/reload/restart/test all re-run init(), ensuring instant validation on config changes
  3. Safety warnings — Root execution, empty admin_key, low ulimit, admin_key_required=false all produce clear warnings
  4. Process detection — Checks for old processes before starting to avoid port conflicts; verifies the pid corresponds to a real process, not just a stale file
  5. Backward compatibilitynode_listen supports three historical formats: number, table, and nested table
  6. Non-destructive testing — The test command backs up and restores the original nginx.conf
  7. Graceful degradation — Reload failure does not terminate the running instance

9. Execution Flow Diagram

apisix start
  │
  ├── cleanup()
  ├── Deny /root execution
  ├── Create logs/
  ├── Wait for old process exit (max 3s)
  ├── Parse -c argument
  ├── init(env)
  │     ├── Read & validate config.yaml
  │     ├── Validate admin_key / mtls / plugins
  │     ├── Handle proxy_mode (http/stream/http&stream)
  │     ├── Port conflict detection
  │     ├── Normalize node_listen / ssl.listen
  │     ├── Handle service discovery (K8s/Consul)
  │     ├── DNS resolver config
  │     └── Template rendering → nginx.conf
  ├── init_etcd() [non data_plane]
  └── Launch openresty

apisix reload
  ├── init(env)          # Regenerate nginx.conf
  ├── nginx -t           # Syntax test
  └── nginx -s reload    # Hot reload signal

apisix restart
  ├── test(env)          # Backup→Generate→Test→Restore
  ├── stop(env)          # nginx -s stop
  └── start(env)         # Full start flow
Read More

Apache APISIX 源码阅读指南

【2026-07-26】Apache APISIX 源码阅读指南 - 从启动流程、核心模块、请求处理链路到插件系统的系统化阅读路线