Add FastCGI support for PHP-FPM and backend server protocol configuration
- Introduced `proto` and `socket` options for backend communication, supporting protocols like FastCGI (fcgi) and HTTP/2 (h2). - Updated HAProxy configuration templates to handle Unix socket paths and protocol-specific settings. - Enhanced documentation with examples for FastCGI using Unix sockets and TCP connections. - Added new tests to validate FastCGI support, including expected configuration generation.
This commit is contained in:
parent
51f8cd4659
commit
a993025718
4 changed files with 126 additions and 15 deletions
|
|
@ -6,20 +6,22 @@ sidebar_position: 11
|
||||||
|
|
||||||
## Container (Docker or Swarm) labels
|
## Container (Docker or Swarm) labels
|
||||||
|
|
||||||
| Label | Description | Default | Example |
|
| Label | Description | Default | Example |
|
||||||
|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|--------------|------------------------------------------------------------------------------------------------------------------|
|
|---------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|------------------------------------------------------------------------------------------------------------------|
|
||||||
| easyhaproxy.[definition].host | Host(s) HAProxy is listening. More than one host use comma as delimiter | **required** | somehost.com OR host1.com,host2.com |
|
| easyhaproxy.[definition].host | Host(s) HAProxy is listening. More than one host use comma as delimiter | **required** | somehost.com OR host1.com,host2.com |
|
||||||
| easyhaproxy.[definition].mode | (Optional) Is this `http` or `tcp` mode in HAProxy. | http | http or tcp |
|
| easyhaproxy.[definition].mode | (Optional) Is this `http` or `tcp` mode in HAProxy. | http | http or tcp |
|
||||||
| easyhaproxy.[definition].port | (Optional) Port HAProxy will listen for the host. | 80 | 3000 |
|
| easyhaproxy.[definition].port | (Optional) Port HAProxy will listen for the host. | 80 | 3000 |
|
||||||
| easyhaproxy.[definition].localport | (Optional) Port container is listening. | 80 | 8080 |
|
| easyhaproxy.[definition].localport | (Optional) Port container is listening. | 80 | 8080 |
|
||||||
| easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to URL redirect. | *empty* | \{"foo.com":"https://bla.com", "bar.com":"https://bar.org"} |
|
| easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to URL redirect. | *empty* | \{"foo.com":"https://bla.com", "bar.com":"https://bar.org"} |
|
||||||
| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `certbot` is enabled. | *empty* | base64 cert + key |
|
| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `certbot` is enabled. | *empty* | base64 cert + key |
|
||||||
| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | false | true or false |
|
| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | false | true or false |
|
||||||
| easyhaproxy.[definition].ssl-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl |
|
| easyhaproxy.[definition].ssl-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl |
|
||||||
| easyhaproxy.[definition].certbot | (Optional) Generate certificate with certbot. Do not use with `sslcert` parameter. More info [here](acme.md). | false | true OR false |
|
| easyhaproxy.[definition].certbot | (Optional) Generate certificate with certbot. Do not use with `sslcert` parameter. More info [here](acme.md). | false | true OR false |
|
||||||
| easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | false | true OR false |
|
| easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | false | true OR false |
|
||||||
| easyhaproxy.[definition].clone_to_ssl | (Optional) It copies the configuration to HTTPS(443) and disable SSL from the current config. **Do not use** this with `ssl` or `certbot` parameters | false | true OR false |
|
| easyhaproxy.[definition].clone_to_ssl | (Optional) It copies the configuration to HTTPS(443) and disable SSL from the current config. **Do not use** this with `ssl` or `certbot` parameters | false | true OR false |
|
||||||
| easyhaproxy.[definition].balance | (Optional) HAProxy balance algorithm. See [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4.2-balance) | roundrobin | roundrobin, source, uri, url_param, hdr, rdp-cookie, leastconn, first, static-rr, rdp-cookie, hdr_dom, map-based |
|
| easyhaproxy.[definition].balance | (Optional) HAProxy balance algorithm. See [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4.2-balance) | roundrobin | roundrobin, source, uri, url_param, hdr, rdp-cookie, leastconn, first, static-rr, rdp-cookie, hdr_dom, map-based |
|
||||||
|
| easyhaproxy.[definition].proto | (Optional) Backend server protocol (e.g., fcgi for PHP-FPM, h2 for HTTP/2) | *empty* | fcgi, h2 |
|
||||||
|
| easyhaproxy.[definition].socket | (Optional) Unix socket path for backend connection (alternative to host:port) | *empty* | /run/php/php-fpm.sock |
|
||||||
|
|
||||||
:::info Understanding Definitions
|
:::info Understanding Definitions
|
||||||
The `[definition]` is a string identifier that groups related configuration labels together. Different definitions create separate HAProxy configurations.
|
The `[definition]` is a string identifier that groups related configuration labels together. Different definitions create separate HAProxy configurations.
|
||||||
|
|
@ -93,6 +95,54 @@ docker run \
|
||||||
some/tcp-service
|
some/tcp-service
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### FastCGI (PHP-FPM) Support
|
||||||
|
|
||||||
|
EasyHAProxy supports FastCGI protocol for PHP-FPM and other FastCGI applications.
|
||||||
|
|
||||||
|
#### Using Unix Socket
|
||||||
|
|
||||||
|
```yaml title="PHP-FPM with Unix socket"
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
php-fpm:
|
||||||
|
image: php:8.2-fpm
|
||||||
|
labels:
|
||||||
|
easyhaproxy.fcgi.host: phpapp.local
|
||||||
|
easyhaproxy.fcgi.port: 80
|
||||||
|
easyhaproxy.fcgi.socket: /run/php/php-fpm.sock
|
||||||
|
easyhaproxy.fcgi.proto: fcgi
|
||||||
|
volumes:
|
||||||
|
- /run/php:/run/php
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using TCP Connection
|
||||||
|
|
||||||
|
```yaml title="PHP-FPM with TCP connection"
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
php-fpm:
|
||||||
|
image: php:8.2-fpm
|
||||||
|
labels:
|
||||||
|
easyhaproxy.fcgi.host: phpapp.local
|
||||||
|
easyhaproxy.fcgi.port: 80
|
||||||
|
easyhaproxy.fcgi.localport: 9000
|
||||||
|
easyhaproxy.fcgi.proto: fcgi
|
||||||
|
```
|
||||||
|
|
||||||
|
**Generated HAProxy Configuration:**
|
||||||
|
|
||||||
|
```
|
||||||
|
backend srv_phpapp_local_80
|
||||||
|
balance roundrobin
|
||||||
|
mode http
|
||||||
|
option forwardfor
|
||||||
|
http-request set-header X-Forwarded-Port %[dst_port]
|
||||||
|
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||||
|
server srv-0 /run/php/php-fpm.sock check weight 1 proto fcgi
|
||||||
|
```
|
||||||
|
|
||||||
### Redirect Domains
|
### Redirect Domains
|
||||||
|
|
||||||
```bash title="Domain redirect configuration"
|
```bash title="Domain redirect configuration"
|
||||||
|
|
|
||||||
|
|
@ -175,13 +175,33 @@ class HaproxyConfigGenerator:
|
||||||
""
|
""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Protocol for backend server communication (e.g., fcgi, h2)
|
||||||
|
proto = self.label.get(
|
||||||
|
self.label.create([definition, "proto"]),
|
||||||
|
""
|
||||||
|
)
|
||||||
|
|
||||||
|
# Unix socket path (alternative to host:port)
|
||||||
|
socket_path = self.label.get(
|
||||||
|
self.label.create([definition, "socket"]),
|
||||||
|
""
|
||||||
|
)
|
||||||
|
|
||||||
for hostname in sorted(d[host_label].split(",")):
|
for hostname in sorted(d[host_label].split(",")):
|
||||||
hostname = hostname.strip()
|
hostname = hostname.strip()
|
||||||
self.serving_hosts.append("%s:%s" % (hostname, port))
|
self.serving_hosts.append("%s:%s" % (hostname, port))
|
||||||
easymapping[port]["hosts"].setdefault(hostname, {})
|
easymapping[port]["hosts"].setdefault(hostname, {})
|
||||||
easymapping[port]["hosts"][hostname].setdefault("containers", [])
|
easymapping[port]["hosts"][hostname].setdefault("containers", [])
|
||||||
easymapping[port]["hosts"][hostname].setdefault("certbot", False)
|
easymapping[port]["hosts"][hostname].setdefault("certbot", False)
|
||||||
easymapping[port]["hosts"][hostname]["containers"] += ["{}:{}".format(container, ct_port)]
|
easymapping[port]["hosts"][hostname].setdefault("proto", proto)
|
||||||
|
|
||||||
|
# Determine server address: Unix socket or TCP host:port
|
||||||
|
if socket_path:
|
||||||
|
server_address = socket_path
|
||||||
|
else:
|
||||||
|
server_address = "{}:{}".format(container, ct_port)
|
||||||
|
|
||||||
|
easymapping[port]["hosts"][hostname]["containers"] += [server_address]
|
||||||
easymapping[port]["hosts"][hostname]["certbot"] = certbot
|
easymapping[port]["hosts"][hostname]["certbot"] = certbot
|
||||||
easymapping[port]["hosts"][hostname]["redirect_ssl"] = self.label.get_bool(
|
easymapping[port]["hosts"][hostname]["redirect_ssl"] = self.label.get_bool(
|
||||||
self.label.create([definition, "redirect_ssl"])
|
self.label.create([definition, "redirect_ssl"])
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ backend srv_{{ host }}
|
||||||
tcp-check connect{{ " ssl" if o["ssl-check"] == "ssl" }}
|
tcp-check connect{{ " ssl" if o["ssl-check"] == "ssl" }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for c in o["hosts"][k]["containers"] %}
|
{% for c in o["hosts"][k]["containers"] %}
|
||||||
server srv-{{ loop.index0 }} {{ c }} check weight 1{{ " verify none" if o["ssl-check"] == "ssl" }}
|
server srv-{{ loop.index0 }} {{ c }} check weight 1{{ " verify none" if o["ssl-check"] == "ssl" }}{{ " proto " + o["hosts"][k]["proto"] if o["hosts"][k].get("proto") }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@ def test_parser_finds_services_raw():
|
||||||
"my-stack_agent:9001"
|
"my-stack_agent:9001"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
|
"proto": "",
|
||||||
"redirect_ssl": False,
|
"redirect_ssl": False,
|
||||||
"plugin_configs": []
|
"plugin_configs": []
|
||||||
}
|
}
|
||||||
|
|
@ -143,6 +144,7 @@ def test_parser_finds_services_raw():
|
||||||
"my-stack_cadvisor:8080"
|
"my-stack_cadvisor:8080"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
|
"proto": "",
|
||||||
"redirect_ssl": False,
|
"redirect_ssl": False,
|
||||||
"plugin_configs": []
|
"plugin_configs": []
|
||||||
},
|
},
|
||||||
|
|
@ -152,6 +154,7 @@ def test_parser_finds_services_raw():
|
||||||
"my-stack_node-exporter:9100"
|
"my-stack_node-exporter:9100"
|
||||||
],
|
],
|
||||||
"certbot": True,
|
"certbot": True,
|
||||||
|
"proto": "",
|
||||||
"redirect_ssl": False,
|
"redirect_ssl": False,
|
||||||
"plugin_configs": []
|
"plugin_configs": []
|
||||||
}
|
}
|
||||||
|
|
@ -171,6 +174,7 @@ def test_parser_finds_services_raw():
|
||||||
"my-stack_node-exporter:9100"
|
"my-stack_node-exporter:9100"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
|
"proto": "",
|
||||||
"redirect_ssl": False,
|
"redirect_ssl": False,
|
||||||
"plugin_configs": []
|
"plugin_configs": []
|
||||||
},
|
},
|
||||||
|
|
@ -180,6 +184,7 @@ def test_parser_finds_services_raw():
|
||||||
"some-service:80"
|
"some-service:80"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
|
"proto": "",
|
||||||
"redirect_ssl": False,
|
"redirect_ssl": False,
|
||||||
"plugin_configs": []
|
"plugin_configs": []
|
||||||
}
|
}
|
||||||
|
|
@ -204,6 +209,7 @@ def test_parser_finds_services_raw():
|
||||||
"some-service:80"
|
"some-service:80"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
|
"proto": "",
|
||||||
"redirect_ssl": False,
|
"redirect_ssl": False,
|
||||||
"plugin_configs": []
|
"plugin_configs": []
|
||||||
}
|
}
|
||||||
|
|
@ -471,6 +477,7 @@ def test_parser_finds_services_clone_to_ssl_raw():
|
||||||
"10.152.183.215:8080"
|
"10.152.183.215:8080"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
|
"proto": "",
|
||||||
"redirect_ssl": False,
|
"redirect_ssl": False,
|
||||||
"plugin_configs": []
|
"plugin_configs": []
|
||||||
},
|
},
|
||||||
|
|
@ -480,6 +487,7 @@ def test_parser_finds_services_clone_to_ssl_raw():
|
||||||
"10.152.183.62:8080"
|
"10.152.183.62:8080"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
|
"proto": "",
|
||||||
"redirect_ssl": False,
|
"redirect_ssl": False,
|
||||||
"plugin_configs": []
|
"plugin_configs": []
|
||||||
},
|
},
|
||||||
|
|
@ -489,6 +497,7 @@ def test_parser_finds_services_clone_to_ssl_raw():
|
||||||
"10.152.183.62:8080"
|
"10.152.183.62:8080"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
|
"proto": "",
|
||||||
"redirect_ssl": False,
|
"redirect_ssl": False,
|
||||||
"plugin_configs": []
|
"plugin_configs": []
|
||||||
}
|
}
|
||||||
|
|
@ -508,6 +517,7 @@ def test_parser_finds_services_clone_to_ssl_raw():
|
||||||
"10.152.183.215:8080"
|
"10.152.183.215:8080"
|
||||||
],
|
],
|
||||||
"certbot": False,
|
"certbot": False,
|
||||||
|
"proto": "",
|
||||||
"redirect_ssl": False,
|
"redirect_ssl": False,
|
||||||
"plugin_configs": []
|
"plugin_configs": []
|
||||||
}
|
}
|
||||||
|
|
@ -525,6 +535,37 @@ def test_parser_finds_services_clone_to_ssl_raw():
|
||||||
assert parsed_object == processed
|
assert parsed_object == processed
|
||||||
assert [] == cfg.certbot_hosts
|
assert [] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
def test_parser_fcgi():
|
||||||
|
"""Test FastCGI support with proto and socket parameters"""
|
||||||
|
line_list = load_fixture("services-fcgi")
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"customerrors": False,
|
||||||
|
"stats": {
|
||||||
|
"port": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg = easymapping.HaproxyConfigGenerator(result)
|
||||||
|
haproxy_config = cfg.generate(line_list)
|
||||||
|
|
||||||
|
assert len(haproxy_config) > 0
|
||||||
|
|
||||||
|
# Verify proto fcgi is in the output
|
||||||
|
assert "proto fcgi" in haproxy_config
|
||||||
|
|
||||||
|
# Verify Unix socket path is used
|
||||||
|
assert "/run/php/php-fpm.sock" in haproxy_config
|
||||||
|
|
||||||
|
# Verify TCP connection is also present
|
||||||
|
assert "172.17.0.3:9000" in haproxy_config
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
with open(path + "/expected/services-fcgi.txt", 'r') as expected_file:
|
||||||
|
assert expected_file.read() == haproxy_config
|
||||||
|
assert [] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
|
||||||
# test_parser_finds_services_raw()
|
# test_parser_finds_services_raw()
|
||||||
# test_parser_tcp()
|
# test_parser_tcp()
|
||||||
# test_parser_multiple_hosts()
|
# test_parser_multiple_hosts()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue