Troubleshooting HAProxy Warning Empty Environment Variable
In this article, we'll dive into a common issue encountered when upgrading HAProxy: the perplexing "Empty Environment Variable" warning. Specifically, we'll explore a scenario where HAProxy, after being updated from version 3.0 to 3.2, starts throwing warnings about an empty environment variable, even though the variable is defined in a Lua script. We will dissect the problem, understand the cause, and propose a solution to ensure your HAProxy configuration runs smoothly. So, let's troubleshoot HAProxy together and get rid of those annoying warnings, making your setup more robust and efficient.
Problem Description
Upgrading HAProxy can bring a lot of improvements, but sometimes it can also introduce unexpected issues. One such issue is the warning message that appears after upgrading to version 3.2, which indicates that an environment variable used in the configuration file is empty. This can be particularly confusing when the environment variable is, in fact, defined and populated, especially when a Lua script is involved.
The Specific Warning
After upgrading from HAProxy 3.0 to 3.2, a warning message like this may appear:
[WARNING] (84810) : config : parsing [haproxy.cfg:13]: argument number 1 at position 16 is empty and marks the end of the argument list; all subsequent arguments will be ignored:
log-format "${HAPROXY_JSON_LOG_FORMAT}"
This warning suggests that the HAPROXY_JSON_LOG_FORMAT
environment variable is empty during the configuration parsing stage. This can be quite puzzling, especially if you've confirmed that the variable is defined and contains a value.
The Root Cause
The primary reason for this warning is that HAProxy's behavior regarding Lua scripts and environment variable loading has changed. In earlier versions, Lua scripts could influence the environment during the configuration parsing stage. However, in HAProxy 3.2, Lua scripts are loaded after the initial configuration parsing. This means that any environment variables set by the script are not available when HAProxy first reads the configuration file. This shift in timing causes HAProxy to see an empty variable when it encounters ${HAPROXY_JSON_LOG_FORMAT}
in the haproxy.cfg
file, triggering the warning. Understanding this timing difference is key to resolving the issue.
Why This Matters
While this warning might seem benign, it's essential to address it for several reasons:
- Clarity and Cleanliness: Warnings clutter logs and make it harder to spot genuine issues. A clean configuration without warnings is always preferable.
- Potential Future Issues: Relying on variables that aren't available during configuration parsing can lead to unexpected behavior or configuration failures in more complex setups.
- Best Practices: It's good practice to ensure that all configuration dependencies are explicitly managed and that the configuration is self-contained as much as possible.
Understanding the Configuration
To effectively troubleshoot this issue, it's crucial to understand the configuration involved. Let's break down the key components:
The haproxy.cfg
File
This is the main configuration file for HAProxy. It defines the global settings, default options, and the listeners that handle incoming traffic. In this case, the relevant parts of the haproxy.cfg
file are:
global
master-worker
tune.lua.bool-sample-conversion normal
lua-load-per-thread init.lua
defaults
mode http
option httplog
log stdout format raw daemon info
timeout connect 1s
timeout client 10s
timeout server 10s
log-format "${HAPROXY_JSON_LOG_FORMAT}"
listen http-in
bind 127.0.0.1:8080
http-request return status 200
global
section: This section sets global parameters for HAProxy, such as enabling the master-worker mode and loading the Lua script.master-worker
: This directive configures HAProxy to run in master-worker mode, where a master process manages worker processes that handle traffic.tune.lua.bool-sample-conversion normal
: This setting adjusts how boolean values are handled in Lua scripts.lua-load-per-thread init.lua
: This directive tells HAProxy to load theinit.lua
script in each thread, which is where the environment variable is set.
defaults
section: This section defines default options for the proxies.mode http
: Specifies that the proxy should operate in HTTP mode.option httplog
: Enables HTTP logging.log stdout format raw daemon info
: Configures logging to standard output with a raw format and daemon information.timeout
directives: Set various timeout values for connections.log-format "${HAPROXY_JSON_LOG_FORMAT}"
: This is where the environment variable is used. HAProxy tries to expand this variable during configuration parsing. This is the crucial line that triggers the warning because the variable is not yet available.
listen http-in
section: This section defines a listener for HTTP traffic.bind 127.0.0.1:8080
: Specifies the address and port to listen on.http-request return status 200
: Configures HAProxy to return a 200 OK status for all HTTP requests.
The init.lua
Script
The init.lua
script is responsible for setting the HAPROXY_JSON_LOG_FORMAT
environment variable. Here’s the content:
-- setenv is a function defined by a 3rd party module
setenv("HAPROXY_JSON_LOG_FORMAT", "{}")
setenv
function: This function (presumably provided by a third-party module) sets the environment variable. It's important to note that this script is loaded after HAProxy parses thehaproxy.cfg
file, which is why the variable isn't available during the initial configuration.
Diagnosing the Issue
To confirm the issue, you can run HAProxy with the -W
flag, which validates the configuration file:
haproxy -W -f haproxy.cfg
This command will parse the configuration file and display any warnings or errors. The expected output will include the warning message about the empty environment variable.
Examining the Output of haproxy -vv
The haproxy -vv
command provides detailed information about the HAProxy build and runtime environment. This can be helpful in diagnosing issues. The output includes:
- HAProxy version and build date
- Operating system and kernel version
- Build options and feature list
- SSL and Lua library versions
- Available polling systems
- Multiplexer protocols
- Available services and filters
The key part of the output for this issue is the version information and the feature list, which confirms that Lua support is enabled. This ensures that the Lua script is indeed being loaded, but the timing of the load is the problem.
Analyzing Log Output
When HAProxy starts, it logs various messages, including the warning about the empty environment variable:
[NOTICE] (87034) : haproxy version is 3.2.3-1844da7
[WARNING] (87034) : config : parsing [haproxy.cfg:13]: argument number 1 at position 16 is empty and marks the end of the argument list; all subsequent arguments will be ignored:
log-format "${HAPROXY_JSON_LOG_FORMAT}"
^
[NOTICE] (87034) : Initializing new worker (87035)
This output clearly indicates that the warning occurs during the configuration parsing phase, before the worker processes are initialized. This further confirms that the Lua script hasn't yet set the environment variable at this point.
Solutions and Workarounds
Now that we understand the problem, let's explore some solutions and workarounds to address the "Empty Environment Variable" warning.
1. Setting the Environment Variable Externally
The most straightforward solution is to set the HAPROXY_JSON_LOG_FORMAT
environment variable outside of the Lua script, before HAProxy starts. This ensures that the variable is available during the configuration parsing phase. There are several ways to do this:
-
Shell Environment: Set the variable in the shell before running HAProxy:
export HAPROXY_JSON_LOG_FORMAT="{}" haproxy -f haproxy.cfg
-
Systemd Service File: If you're using systemd, you can set the variable in the service file:
[Service] Environment="HAPROXY_JSON_LOG_FORMAT={}" ExecStart=/usr/local/sbin/haproxy -f /usr/local/etc/haproxy/haproxy.cfg
After modifying the service file, you'll need to reload systemd and restart HAProxy:
sudo systemctl daemon-reload sudo systemctl restart haproxy
-
Docker Environment Variables: If you're running HAProxy in a Docker container, you can set the environment variable in the
docker run
command or in adocker-compose.yml
file:docker run -d -p 8080:80 --name my-haproxy -e HAPROXY_JSON_LOG_FORMAT="{}" haproxy:latest
Or in
docker-compose.yml
:version: "3.8" services: haproxy: image: haproxy:latest ports: - "8080:80" environment: HAPROXY_JSON_LOG_FORMAT: "{}"
2. Using lua-settings
to Define the Log Format
Another approach is to avoid using environment variables directly in the log-format
directive. Instead, you can use the lua-settings
directive to define the log format within the Lua script and then reference it in the configuration. This method leverages Lua's ability to set variables that HAProxy can access during runtime.
First, modify the init.lua
script to set a Lua variable:
-- set the log format as a Lua variable
haproxy.global:set_var("global", "json_log_format", "{}")
Then, update the haproxy.cfg
file to reference this variable:
global
master-worker
tune.lua.bool-sample-conversion normal
lua-load-per-thread init.lua
defaults
mode http
option httplog
log stdout format raw daemon info
timeout connect 1s
timeout client 10s
timeout server 10s
log-format %[lua.global.json_log_format]
listen http-in
bind 127.0.0.1:8080
http-request return status 200
This approach ensures that the log format is defined within the Lua context and is available when HAProxy needs it. It's a cleaner and more manageable solution when dealing with dynamic configurations.
3. Conditional Configuration with if
Directives
If you need to conditionally set the log format based on certain conditions, you can use if
directives in your haproxy.cfg
file. This allows you to define different log formats based on environment variables or other factors.
For example, you can check if the HAPROXY_JSON_LOG_FORMAT
variable is set and use it if it is, otherwise, use a default format:
defaults
mode http
option httplog
log stdout format raw daemon info
timeout connect 1s
timeout client 10s
timeout server 10s
log-format {% ifenv HAPROXY_JSON_LOG_FORMAT}${HAPROXY_JSON_LOG_FORMAT}{% else %}{