From d3687d76323d391265e89df1d0c2dde5d4195d56 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Thu, 11 Jul 2019 01:21:03 -0500 Subject: [PATCH 01/17] Starting the Discovery Script --- .gitignore | 3 +- Dockerfile | 2 ++ README.md | 83 +++++++++++++++++++++++++++++------------------- discover.py | 39 +++++++++++++++++++++++ discover.sh | 30 +++++++++++++++++ requirements.txt | 3 +- 6 files changed, 126 insertions(+), 34 deletions(-) create mode 100644 discover.py create mode 100644 discover.sh diff --git a/.gitignore b/.gitignore index 7a63f2c..4c78e58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea *~ -venv \ No newline at end of file +venv +.docker_data \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 5a6e50a..7ed7c32 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,8 @@ RUN apk add --no-cache bash python3 py-yaml COPY entrypoint.* / COPY conf.d /etc/haproxy/conf.d COPY assets/errors-custom/* /etc/haproxy/errors-custom/ +RUN pip install --upgrade pip \ + && pip install -r requirements.txt ENTRYPOINT [ "/entrypoint.sh" ] CMD ["haproxy", "-f", "/etc/haproxy/haproxy.cfg"] \ No newline at end of file diff --git a/README.md b/README.md index 92e8c48..2efd1ec 100644 --- a/README.md +++ b/README.md @@ -5,53 +5,57 @@ This Docker image will create dynamically the `haproxy.cfg` based on very simple # Features - Enable or disable Stats on port 1936 with custom password -- Simple mapping host => host:port -- Simple redirect host => host:port +- Discover and setup haproxy from Docker Tag +- Discover and setup haproxy redirect from Docker Tag # Basic Usage -Create a yaml file in your machine called `easyconfig.cfg` and put the contents: - -```yaml -stats: - username: admin - password: password - port: 1936 # Optional (default 1936) - -customerrors: true # Optional (default false) - -easymapping: - - port: 80 - hosts: - host1.com.br: container:5000 - host2.com.br: other:3000 - redirect: - www.host1.com.br: http://host1.com.br - - - port: 443 - ssl_cert: /etc/easyconfig/mycert.pem - hosts: - host1.com.br: container:80 - - - port: 8080 - hosts: - host3.com.br: domain:8181 -``` - -Then run (remember to enable the proper ports): +Docker run: ```bash docker run \ - -v /path/to/local:/etc/easyconfig \ -p 80:80 \ -p 8080:8080 \ -p 1936:1936 \ --name easy-haproxy-instance \ + -e DISCOVER=swarm \ + -e HAPROXY_USERNAME=admin \ + -e HAPROXY_PASSWORD=password \ + -e HAPROXY_STATS_PORT=1936 \ + -e REFRESH_STATE=1 \ -d byjg/easy-haproxy ``` +The `DISCOVER` environment variable will define where is located your containers (see below more details): +- swarm +- awsecs +# Tags to defined: + +| Tag | Description | +|--------------------------------------------|---------------------------------------------------------------------------------------------------------| +| com.byjg.easyhaproxy.definitions | A Comma delimited list with the definitions. Each name requires the definition of the parameters below. | +| com.byjg.easyhaproxy.port. | What is the port that the HAProxy will listen to. | +| com.byjg.easyhaproxy.host. | What is the host that the HAProxy will listen to. | +| com.byjg.easyhaproxy.redirect. | Host redirects from connections in the port defined above. | +| com.byjg.easyhaproxy.sslcert. | Cert PEM Base64 encoded. | + +E.g. + +``` +docker run \ + -l com.byjg.easyhaproxy.definitions=http \ + -l com.byjg.easyhaproxy.port.http=80\ + -l com.byjg.easyhaproxy.host.http=byjg.com.br \ + .... +``` + +## Redirect Example: + +```text +www.byjg.com.br=>http://byjg.com.br,byjg.com=>http://byjg.com.br +``` # Mapping custom .cfg files @@ -114,3 +118,18 @@ is the http error code (e.g. 503.http) docker build -t byjg/easy-haproxy . ``` + + +# Docker Swarm + + +# AWS + +Easy HAProxy will try to get tasks from the tasks running in the CLUSTER. +It is important that easy haproxy run in the ECS Cluster and can connect to the containers. + + +You'll have to pass also the `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` or setup a role in the instance in order and +the `ECS_CLUSTER` with the arn of the cluster + + diff --git a/discover.py b/discover.py new file mode 100644 index 0000000..a077fad --- /dev/null +++ b/discover.py @@ -0,0 +1,39 @@ +import os +import json + +path = os.path.dirname(os.path.realpath(__file__)) +with open(path + "/.docker_data", 'r') as content_file: + lineList = content_file.readlines() + +result = dict() + +for line in lineList: + line = line.strip() + i = line.find("=") + container = line[:i] + jsonStr = line[i+1:] + d = json.loads(jsonStr) + + if "com.byjg.easyhaproxy.definitions" in d.keys(): + definitions = d["com.byjg.easyhaproxy.definitions"].split(",") + + for definition in definitions: + port = d["com.byjg.easyhaproxy.port." + definition] if "com.byjg.easyhaproxy.port." + definition in d else "80" + if port not in result: + result[port] = dict() + result[port]["hosts"] = dict() + result[port]["redirect"] = dict() + result[port]["ssl_cert"] = None + + result[port]["hosts"][d["com.byjg.easyhaproxy.host." + definition] if "com.byjg.easyhaproxy.host." + definition in d else ""] = container + ":" + (d["com.byjg.easyhaproxy.localport." + definition] if "com.byjg.easyhaproxy.localport." + definition in d else "80") + + redirect = d["com.byjg.easyhaproxy.redirect." + definition] if "com.byjg.easyhaproxy.redirect." + definition in d else "" + for r in redirect.split(","): + r_parts = r.split("=>") + result[port]["redirect"][r_parts[0]] = r_parts[1] + + +print(result) +# print(jsonStr) + + diff --git a/discover.sh b/discover.sh new file mode 100644 index 0000000..d860b7f --- /dev/null +++ b/discover.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + + +#docker run \ +# -l com.byjg.easyhaproxy.definitions=http \ +# -l com.byjg.easyhaproxy.port.http=80 \ +# -l com.byjg.easyhaproxy.port.localport=80 \ +# -l com.byjg.easyhaproxy.host.http=byjg.com.br \ +# -l com.byjg.easyhaproxy.redirect.http=byjg.com.br%http://www.byjg.com.br,byjg.com%http://www.byjg.com.br \ +#-d byjg/php:7.3-fpm-nginx + + +touch ./.docker_data ./docker_data_old + +mv ./.docker_data ./docker_data_old + +CONTAINERS=$(docker node ps $(docker node ls -q) --format "{{ .Name }}" --filter desired-state=running | cut -d. -f1 | sort | uniq) + +for container in $CONTAINERS; do + docker inspect --format "{{ json .Spec.Labels }}" $container | xargs -I % echo $container=% >> ./.docker_data +done + + +if cmp -s ./.docker_data ./.docker_data_old ; then + echo "Nothing changed" +else + echo "Something changed" +fi + +# byjg_site={"com.byjg.easyhaproxy.definition":"http","com.byjg.easyhaproxy.host.http":"byjg.com.br","com.byjg.easyhaproxy.port.http":"80","com.byjg.easyhaproxy.redirect.http":"byjg.com.br%http://www.byjg.com.br,byjg.com%http://www.byjg.com.br","maintainer":"Joao Gilberto Magalhaes"} diff --git a/requirements.txt b/requirements.txt index 4818cc5..949d85b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -pyyaml \ No newline at end of file +pyyaml +docker \ No newline at end of file From f6e2351f9d83b5c3473e0b421aa1f97b3d14db9b Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Thu, 11 Jul 2019 01:28:07 -0500 Subject: [PATCH 02/17] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7ed7c32..c1c7ad8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ RUN apk add --no-cache bash python3 py-yaml COPY entrypoint.* / COPY conf.d /etc/haproxy/conf.d COPY assets/errors-custom/* /etc/haproxy/errors-custom/ -RUN pip install --upgrade pip \ +RUN pip3 install --upgrade pip \ && pip install -r requirements.txt ENTRYPOINT [ "/entrypoint.sh" ] From 417db0de7711737ce05f7f1ad356dc844af87a84 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Thu, 11 Jul 2019 01:31:35 -0500 Subject: [PATCH 03/17] Update README.md --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2efd1ec..a4f42b6 100644 --- a/README.md +++ b/README.md @@ -33,13 +33,14 @@ The `DISCOVER` environment variable will define where is located your containers # Tags to defined: -| Tag | Description | -|--------------------------------------------|---------------------------------------------------------------------------------------------------------| -| com.byjg.easyhaproxy.definitions | A Comma delimited list with the definitions. Each name requires the definition of the parameters below. | -| com.byjg.easyhaproxy.port. | What is the port that the HAProxy will listen to. | -| com.byjg.easyhaproxy.host. | What is the host that the HAProxy will listen to. | -| com.byjg.easyhaproxy.redirect. | Host redirects from connections in the port defined above. | -| com.byjg.easyhaproxy.sslcert. | Cert PEM Base64 encoded. | +| Tag | Description | +|---------------------------------------------|---------------------------------------------------------------------------------------------------------| +| com.byjg.easyhaproxy.definitions | A Comma delimited list with the definitions. Each name requires the definition of the parameters below. | +| com.byjg.easyhaproxy.port. | What is the port that the HAProxy will listen to. | +| com.byjg.easyhaproxy.localport. | What is the port that the container is listening. | +| com.byjg.easyhaproxy.host. | What is the host that the HAProxy will listen to. (Defaults to 80) | +| com.byjg.easyhaproxy.redirect. | Host redirects from connections in the port defined above. | +| com.byjg.easyhaproxy.sslcert. | Cert PEM Base64 encoded. | E.g. From eb102887172f98c8a025bb59b70964ee74fa0216 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Mon, 15 Jul 2019 23:22:51 -0500 Subject: [PATCH 04/17] Preparing for reloading haproxy --- Dockerfile | 24 +++++++++++++----- README.md | 1 + assets/crontab | 9 +++++++ assets/supervisord.conf | 45 ++++++++++++++++++++++++++++++++++ scripts/exit-event-listener.py | 18 ++++++++++++++ scripts/haproxy-reload.sh | 5 ++++ scripts/haproxy.sh | 7 ++++++ 7 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 assets/crontab create mode 100644 assets/supervisord.conf create mode 100755 scripts/exit-event-listener.py create mode 100755 scripts/haproxy-reload.sh create mode 100755 scripts/haproxy.sh diff --git a/Dockerfile b/Dockerfile index c1c7ad8..facbae7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,24 @@ FROM haproxy:1.9-alpine -RUN apk add --no-cache bash python3 py-yaml +WORKDIR /root -COPY entrypoint.* / -COPY conf.d /etc/haproxy/conf.d -COPY assets/errors-custom/* /etc/haproxy/errors-custom/ +RUN apk add --no-cache bash python3 py-yaml supervisor docker + +COPY requirements.txt /root RUN pip3 install --upgrade pip \ && pip install -r requirements.txt -ENTRYPOINT [ "/entrypoint.sh" ] -CMD ["haproxy", "-f", "/etc/haproxy/haproxy.cfg"] \ No newline at end of file + +COPY haproxy.cfg /etc/haproxy/haproxy.cfg +COPY conf.d /etc/haproxy/conf.d +COPY assets/errors-custom/* /etc/haproxy/errors-custom/ +COPY assets/supervisord.conf /etc/supervisord.conf +COPY assets/crontab /etc/crontabs/root + +COPY scripts/exit-event-listener.py /exit-event-listener.py +COPY scripts/haproxy.sh /haproxy.sh +COPY scripts/haproxy-reload.sh /haproxy-reload.sh + + +#CMD ["haproxy", "-f", "/etc/haproxy/haproxy.cfg"] +CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf" ] \ No newline at end of file diff --git a/README.md b/README.md index a4f42b6..687cb78 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ docker run \ -e HAPROXY_PASSWORD=password \ -e HAPROXY_STATS_PORT=1936 \ -e REFRESH_STATE=1 \ + -v /var/run/docker.sock:/var/run/docker.sock \ -d byjg/easy-haproxy ``` diff --git a/assets/crontab b/assets/crontab new file mode 100644 index 0000000..7b75766 --- /dev/null +++ b/assets/crontab @@ -0,0 +1,9 @@ +# do daily/weekly/monthly maintenance +# min hour day month weekday command +*/15 * * * * run-parts /etc/periodic/15min +0 * * * * run-parts /etc/periodic/hourly +0 2 * * * run-parts /etc/periodic/daily +0 3 * * 6 run-parts /etc/periodic/weekly +0 5 1 * * run-parts /etc/periodic/monthly +* * * * * /haproxy-reload.sh + diff --git a/assets/supervisord.conf b/assets/supervisord.conf new file mode 100644 index 0000000..5ad57e6 --- /dev/null +++ b/assets/supervisord.conf @@ -0,0 +1,45 @@ +[unix_http_server] +file=/dev/shm/supervisor.sock ; (the path to the socket file) + +[supervisord] +logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log) +logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB) +logfile_backups=10 ; (num of main logfile rotation backups;default 10) +loglevel=info ; (log level;default info; others: debug,warn,trace) +pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid) +nodaemon=false ; (start in foreground if true;default false) +minfds=1024 ; (min. avail startup file descriptors;default 1024) +minprocs=200 ; (min. avail process descriptors;default 200) +user=root ; + +; the below section must remain in the config file for RPC +; (supervisorctl/web interface) to work, additional interfaces may be +; added by defining them in separate rpcinterface: sections +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///dev/shm/supervisor.sock ; use a unix:// URL for a unix socket + +[program:haproxy] +command = /haproxy.sh +autostart=true +autorestart=false +priority=5 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:crond] +command=/usr/sbin/crond -f +autostart=true +autorestart=false +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[eventlistener:exit_on_any_fatal] +command=/exit-event-listener.py +events=PROCESS_STATE_FATAL,PROCESS_STATE_EXITED,PROCESS_STATE_STOPPED diff --git a/scripts/exit-event-listener.py b/scripts/exit-event-listener.py new file mode 100755 index 0000000..9b1826f --- /dev/null +++ b/scripts/exit-event-listener.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +import os +import signal + +from supervisor import childutils + +def main(): + while True: + headers, payload = childutils.listener.wait() + childutils.listener.ok() + events = ['PROCESS_STATE_FATAL', 'PROCESS_STATE_EXITED', 'PROCESS_STATE_STOPPED'] + if not (headers['eventname'] in events): + continue + os.kill(os.getppid(), signal.SIGTERM) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/haproxy-reload.sh b/scripts/haproxy-reload.sh new file mode 100755 index 0000000..402f4c6 --- /dev/null +++ b/scripts/haproxy-reload.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +echo "Reloading..." + +/usr/local/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf $(cat /run/haproxy.pid) & \ No newline at end of file diff --git a/scripts/haproxy.sh b/scripts/haproxy.sh new file mode 100755 index 0000000..f6a691f --- /dev/null +++ b/scripts/haproxy.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +/usr/local/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock + +while true; do + sleep 60 +done From b1969053209bc8bba85709243e5fde5386bffacb Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Wed, 17 Jul 2019 00:14:58 -0500 Subject: [PATCH 05/17] Directory restructuration --- Dockerfile | 18 ++- assets/{crontab => etc/crontabs/root} | 2 +- .../etc/haproxy/conf.d}/00-simple-comment.cfg | 0 .../etc/haproxy/conf.d}/README.md | 0 .../{ => etc/haproxy}/errors-custom/400.http | 0 .../{ => etc/haproxy}/errors-custom/403.http | 0 .../{ => etc/haproxy}/errors-custom/408.http | 0 .../{ => etc/haproxy}/errors-custom/500.http | 0 .../{ => etc/haproxy}/errors-custom/502.http | 0 .../{ => etc/haproxy}/errors-custom/503.http | 0 .../{ => etc/haproxy}/errors-custom/504.http | 0 assets/{ => etc}/supervisord.conf | 4 +- .../scripts}/exit-event-listener.py | 0 {scripts => assets/scripts}/haproxy-reload.sh | 0 {scripts => assets/scripts}/haproxy.sh | 0 easymapping/__init__.py | 12 ++ entrypoint.py | 109 ------------------ entrypoint.sh | 30 ----- requirements.txt | 3 +- static.py | 16 +++ discover.py => swarm.py | 25 ++-- discover.sh => swarm.sh | 6 +- templates/haproxy.cfg.j2 | 68 +++++++++++ 23 files changed, 127 insertions(+), 166 deletions(-) rename assets/{crontab => etc/crontabs/root} (86%) rename {conf.d => assets/etc/haproxy/conf.d}/00-simple-comment.cfg (100%) rename {conf.d => assets/etc/haproxy/conf.d}/README.md (100%) rename assets/{ => etc/haproxy}/errors-custom/400.http (100%) rename assets/{ => etc/haproxy}/errors-custom/403.http (100%) rename assets/{ => etc/haproxy}/errors-custom/408.http (100%) rename assets/{ => etc/haproxy}/errors-custom/500.http (100%) rename assets/{ => etc/haproxy}/errors-custom/502.http (100%) rename assets/{ => etc/haproxy}/errors-custom/503.http (100%) rename assets/{ => etc/haproxy}/errors-custom/504.http (100%) rename assets/{ => etc}/supervisord.conf (95%) rename {scripts => assets/scripts}/exit-event-listener.py (100%) rename {scripts => assets/scripts}/haproxy-reload.sh (100%) rename {scripts => assets/scripts}/haproxy.sh (100%) create mode 100644 easymapping/__init__.py delete mode 100644 entrypoint.py delete mode 100755 entrypoint.sh create mode 100644 static.py rename discover.py => swarm.py (55%) rename discover.sh => swarm.sh (94%) create mode 100644 templates/haproxy.cfg.j2 diff --git a/Dockerfile b/Dockerfile index facbae7..97c7a41 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,19 @@ FROM haproxy:1.9-alpine -WORKDIR /root +WORKDIR /scripts RUN apk add --no-cache bash python3 py-yaml supervisor docker -COPY requirements.txt /root +COPY requirements.txt /scripts RUN pip3 install --upgrade pip \ && pip install -r requirements.txt +COPY swarm.* /scripts/ +COPY static.* /scripts/ +COPY templates /scripts/templates/ +COPY easymapping /scripts/easymapping/ -COPY haproxy.cfg /etc/haproxy/haproxy.cfg -COPY conf.d /etc/haproxy/conf.d -COPY assets/errors-custom/* /etc/haproxy/errors-custom/ -COPY assets/supervisord.conf /etc/supervisord.conf -COPY assets/crontab /etc/crontabs/root - -COPY scripts/exit-event-listener.py /exit-event-listener.py -COPY scripts/haproxy.sh /haproxy.sh -COPY scripts/haproxy-reload.sh /haproxy-reload.sh +COPY assets / #CMD ["haproxy", "-f", "/etc/haproxy/haproxy.cfg"] diff --git a/assets/crontab b/assets/etc/crontabs/root similarity index 86% rename from assets/crontab rename to assets/etc/crontabs/root index 7b75766..cabb0aa 100644 --- a/assets/crontab +++ b/assets/etc/crontabs/root @@ -5,5 +5,5 @@ 0 2 * * * run-parts /etc/periodic/daily 0 3 * * 6 run-parts /etc/periodic/weekly 0 5 1 * * run-parts /etc/periodic/monthly -* * * * * /haproxy-reload.sh +* * * * * /scripts/haproxy-reload.sh diff --git a/conf.d/00-simple-comment.cfg b/assets/etc/haproxy/conf.d/00-simple-comment.cfg similarity index 100% rename from conf.d/00-simple-comment.cfg rename to assets/etc/haproxy/conf.d/00-simple-comment.cfg diff --git a/conf.d/README.md b/assets/etc/haproxy/conf.d/README.md similarity index 100% rename from conf.d/README.md rename to assets/etc/haproxy/conf.d/README.md diff --git a/assets/errors-custom/400.http b/assets/etc/haproxy/errors-custom/400.http similarity index 100% rename from assets/errors-custom/400.http rename to assets/etc/haproxy/errors-custom/400.http diff --git a/assets/errors-custom/403.http b/assets/etc/haproxy/errors-custom/403.http similarity index 100% rename from assets/errors-custom/403.http rename to assets/etc/haproxy/errors-custom/403.http diff --git a/assets/errors-custom/408.http b/assets/etc/haproxy/errors-custom/408.http similarity index 100% rename from assets/errors-custom/408.http rename to assets/etc/haproxy/errors-custom/408.http diff --git a/assets/errors-custom/500.http b/assets/etc/haproxy/errors-custom/500.http similarity index 100% rename from assets/errors-custom/500.http rename to assets/etc/haproxy/errors-custom/500.http diff --git a/assets/errors-custom/502.http b/assets/etc/haproxy/errors-custom/502.http similarity index 100% rename from assets/errors-custom/502.http rename to assets/etc/haproxy/errors-custom/502.http diff --git a/assets/errors-custom/503.http b/assets/etc/haproxy/errors-custom/503.http similarity index 100% rename from assets/errors-custom/503.http rename to assets/etc/haproxy/errors-custom/503.http diff --git a/assets/errors-custom/504.http b/assets/etc/haproxy/errors-custom/504.http similarity index 100% rename from assets/errors-custom/504.http rename to assets/etc/haproxy/errors-custom/504.http diff --git a/assets/supervisord.conf b/assets/etc/supervisord.conf similarity index 95% rename from assets/supervisord.conf rename to assets/etc/supervisord.conf index 5ad57e6..29e789b 100644 --- a/assets/supervisord.conf +++ b/assets/etc/supervisord.conf @@ -22,7 +22,7 @@ supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface serverurl=unix:///dev/shm/supervisor.sock ; use a unix:// URL for a unix socket [program:haproxy] -command = /haproxy.sh +command = /scripts/haproxy.sh autostart=true autorestart=false priority=5 @@ -41,5 +41,5 @@ stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 [eventlistener:exit_on_any_fatal] -command=/exit-event-listener.py +command=/scripts/exit-event-listener.py events=PROCESS_STATE_FATAL,PROCESS_STATE_EXITED,PROCESS_STATE_STOPPED diff --git a/scripts/exit-event-listener.py b/assets/scripts/exit-event-listener.py similarity index 100% rename from scripts/exit-event-listener.py rename to assets/scripts/exit-event-listener.py diff --git a/scripts/haproxy-reload.sh b/assets/scripts/haproxy-reload.sh similarity index 100% rename from scripts/haproxy-reload.sh rename to assets/scripts/haproxy-reload.sh diff --git a/scripts/haproxy.sh b/assets/scripts/haproxy.sh similarity index 100% rename from scripts/haproxy.sh rename to assets/scripts/haproxy.sh diff --git a/easymapping/__init__.py b/easymapping/__init__.py new file mode 100644 index 0000000..1b54af2 --- /dev/null +++ b/easymapping/__init__.py @@ -0,0 +1,12 @@ +from jinja2 import Environment, FileSystemLoader + + +class HaproxyConfigGenerator: + def __init__(self, mapping): + self.mapping = mapping + + def generate(self): + file_loader = FileSystemLoader('templates') + env = Environment(loader=file_loader) + template = env.get_template('haproxy.cfg.j2') + return template.render(data=self.mapping) diff --git a/entrypoint.py b/entrypoint.py deleted file mode 100644 index 7962ed2..0000000 --- a/entrypoint.py +++ /dev/null @@ -1,109 +0,0 @@ -import yaml -import sys - -if len(sys.argv) != 2: - print("You need to pass the easyconfig.cfg path") - exit(1) - - -def defaults(custom): - result = """ -defaults - log global - - timeout connect 3s - timeout client 10s - timeout server 10m -""" - if custom: - result += """ - errorfile 400 /etc/haproxy/errors-custom/400.http - errorfile 403 /etc/haproxy/errors-custom/403.http - errorfile 408 /etc/haproxy/errors-custom/408.http - errorfile 500 /etc/haproxy/errors-custom/500.http - errorfile 502 /etc/haproxy/errors-custom/502.http - errorfile 503 /etc/haproxy/errors-custom/503.http - errorfile 504 /etc/haproxy/errors-custom/504.http -""" - result += """ -global - log /dev/log local0 - maxconn 2000 - tune.ssl.default-dh-param 2048 -""" - return result - - -def stats(map): - return """ -frontend stats - bind *:{2} - mode http - stats enable - stats hide-version - stats realm Haproxy\ Statistics - stats uri / - stats auth {0}:{1} -# acl is_proxystats hdr(host) -i some.host.com -# default_backend srv_stats -# use_backend srv_stats if is_proxystats - default_backend srv_stats - -backend srv_stats - mode http - server Local 127.0.0.1:{2} -""".format(map["username"], map["password"], map["port"] if "port" in map else 1936) - - -def easymapping(o, salt): - port = o["port"] - ssl = " ssl crt " + o["ssl_cert"] if "ssl_cert" in o else "" - hosts = o["hosts"] if "hosts" in o else dict() - redir = o["redirect"] if "redirect" in o else dict() - - result = """ -frontend http_in_{0}_{1} - bind *:{0} {2} - mode http - -""".format(port, salt, ssl) - - for k in redir: - result += " redirect prefix " + redir[k] + " code 301 if { hdr(host) -i " + k + " }\n" - - result += "\n" - for k in hosts: - host = k.replace(".", "_") + "_{0}_{1}".format(port, salt) - result += " acl is_rule_{0}_1 hdr(host) -i {1}\n".format(host, k) - result += " acl is_rule_{0}_2 hdr(host) -i {1}:{2}\n".format(host, k, port) - result += " use_backend srv_{0} if is_rule_{0}_1 OR is_rule_{0}_2\n\n".format(host) - - for k in hosts: - host = k.replace(".", "_") + "_{0}_{1}".format(port, salt) - result += """ -backend srv_{0} - balance roundrobin - mode http - option forwardfor - http-request set-header X-Forwarded-Port %[dst_port]""".format(host) + """ - http-request add-header X-Forwarded-Proto https if { ssl_fc }""" + """ - server srv {0} check weight 1 -""".format(hosts[k]) - - return result - - -with open(sys.argv[1], 'r') as content_file: - parsed = yaml.load(content_file.read()) - -n = 0 - -print(defaults(parsed["customerrors"] if "customerrors" in parsed else False)) -if "stats" in parsed: - print(stats(parsed["stats"])) -if "easymapping" in parsed: - for k in parsed["easymapping"]: - n = n + 1 - print(easymapping(k, n)) - - diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index 9eb6d31..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -set -e - -echo " ______ _ _ _____ " -echo "| ____| | | | | /\ | __ \ " -echo "| |__ __ _ ___ _ _ | |__| | / \ | |__) | __ _____ ___ _ " -echo "| __| / _\` / __| | | | | __ | / /\ \ | ___/ '__/ _ \ \/ / | | |" -echo "| |___| (_| \__ \ |_| | | | | |/ ____ \| | | | | (_) > <| |_| |" -echo "|______\__,_|___/\__, | |_| |_/_/ \_\_| |_| \___/_/\_\\__, |" -echo " __/ | __/ |" -echo " |___/ |___/ " -echo "" - -HAPROXY_CFG="/etc/haproxy/haproxy.cfg" -EASYCONFIG_CFG="/etc/easyconfig/easyconfig.cfg" - -if [ ! -f "$EASYCONFIG_CFG" ] -then - echo "File '$EASYCONFIG_CFG' does not exist" - exit 1 -fi - - - -python3 /entrypoint.py "$EASYCONFIG_CFG" > $HAPROXY_CFG - - -/sbin/syslogd -O /proc/1/fd/1 -/docker-entrypoint.sh "$@" diff --git a/requirements.txt b/requirements.txt index 949d85b..fa90f6f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pyyaml -docker \ No newline at end of file +docker +jinja2 \ No newline at end of file diff --git a/static.py b/static.py new file mode 100644 index 0000000..87efb42 --- /dev/null +++ b/static.py @@ -0,0 +1,16 @@ +import yaml +import sys +from easymapping import HaproxyConfigGenerator + +if len(sys.argv) != 2: + print("You need to pass the easyconfig.cfg path") + exit(1) + + +with open(sys.argv[1], 'r') as content_file: + parsed = yaml.load(content_file.read()) + +cfg = HaproxyConfigGenerator(parsed) +print(cfg.generate()) + +exit(0) diff --git a/discover.py b/swarm.py similarity index 55% rename from discover.py rename to swarm.py index a077fad..dd2921a 100644 --- a/discover.py +++ b/swarm.py @@ -1,11 +1,14 @@ import os import json +from easymapping import HaproxyConfigGenerator path = os.path.dirname(os.path.realpath(__file__)) with open(path + "/.docker_data", 'r') as content_file: lineList = content_file.readlines() -result = dict() +result = { + "easymapping": [] +} for line in lineList: line = line.strip() @@ -19,19 +22,25 @@ for line in lineList: for definition in definitions: port = d["com.byjg.easyhaproxy.port." + definition] if "com.byjg.easyhaproxy.port." + definition in d else "80" - if port not in result: - result[port] = dict() - result[port]["hosts"] = dict() - result[port]["redirect"] = dict() - result[port]["ssl_cert"] = None + data = { + "port": port, + "hosts": dict(), + "redirect": dict(), + # "ssl_cert": "" + } - result[port]["hosts"][d["com.byjg.easyhaproxy.host." + definition] if "com.byjg.easyhaproxy.host." + definition in d else ""] = container + ":" + (d["com.byjg.easyhaproxy.localport." + definition] if "com.byjg.easyhaproxy.localport." + definition in d else "80") + data["hosts"][d["com.byjg.easyhaproxy.host." + definition] if "com.byjg.easyhaproxy.host." + definition in d else ""] = container + ":" + (d["com.byjg.easyhaproxy.localport." + definition] if "com.byjg.easyhaproxy.localport." + definition in d else "80") redirect = d["com.byjg.easyhaproxy.redirect." + definition] if "com.byjg.easyhaproxy.redirect." + definition in d else "" for r in redirect.split(","): r_parts = r.split("=>") - result[port]["redirect"][r_parts[0]] = r_parts[1] + data["redirect"][r_parts[0]] = r_parts[1] + result["easymapping"].append(data) + + +cfg = HaproxyConfigGenerator(result) +print(cfg.generate()) print(result) # print(jsonStr) diff --git a/discover.sh b/swarm.sh similarity index 94% rename from discover.sh rename to swarm.sh index d860b7f..1948b52 100644 --- a/discover.sh +++ b/swarm.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash - #docker run \ # -l com.byjg.easyhaproxy.definitions=http \ # -l com.byjg.easyhaproxy.port.http=80 \ @@ -22,9 +21,8 @@ done if cmp -s ./.docker_data ./.docker_data_old ; then - echo "Nothing changed" -else - echo "Something changed" + exit 0 fi +python3 swarm.py # byjg_site={"com.byjg.easyhaproxy.definition":"http","com.byjg.easyhaproxy.host.http":"byjg.com.br","com.byjg.easyhaproxy.port.http":"80","com.byjg.easyhaproxy.redirect.http":"byjg.com.br%http://www.byjg.com.br,byjg.com%http://www.byjg.com.br","maintainer":"Joao Gilberto Magalhaes"} diff --git a/templates/haproxy.cfg.j2 b/templates/haproxy.cfg.j2 new file mode 100644 index 0000000..3ac2805 --- /dev/null +++ b/templates/haproxy.cfg.j2 @@ -0,0 +1,68 @@ +defaults + log global + + timeout connect 3s + timeout client 10s + timeout server 10m +{% if data["customerrors"] %} + errorfile 400 /etc/haproxy/errors-custom/400.http + errorfile 403 /etc/haproxy/errors-custom/403.http + errorfile 408 /etc/haproxy/errors-custom/408.http + errorfile 500 /etc/haproxy/errors-custom/500.http + errorfile 502 /etc/haproxy/errors-custom/502.http + errorfile 503 /etc/haproxy/errors-custom/503.http + errorfile 504 /etc/haproxy/errors-custom/504.http +{% endif %} + +global + log /dev/log local0 + maxconn 2000 + tune.ssl.default-dh-param 2048 + +{% if "stats" in data %} +frontend stats + bind *:{{ data["stats"]["port"] | default(1936) }} + mode http + stats enable + stats hide-version + stats realm Haproxy\ Statistics + stats uri / + stats auth {{ data["stats"]["username"] }}:{{ data["stats"]["password"] }} +# acl is_proxystats hdr(host) -i some.host.com +# default_backend srv_stats +# use_backend srv_stats if is_proxystats + default_backend srv_stats + +backend srv_stats + mode http + server Local 127.0.0.1:{{ data["stats"]["port"] | default(1936) }} +{% endif %} + +{% for o in data["easymapping"] %} + {% set salt = loop.index %} +frontend http_in_{{ o["port"] }}_{{ salt }} + bind *:{{ o["port"] }} {{ " ssl crt " + o["ssl_cert"] if "ssl_cert" in o else "" }} + mode http + + {% for k in o["redirect"] -%} + redirect prefix {{ o["redirect"][k] }} code 301 if { hdr(host) -i {{ k }} } + {% endfor -%} + + {% for k in o["hosts"] %} + {% set host = k.replace(".", "_") + "_{0}_{1}".format(o["port"], salt) %} + acl is_rule_{{ host }}_1 hdr(host) -i {{ k }} + acl is_rule_{{ host }}_2 hdr(host) -i {{ k }}:{{ o["port"] }} + use_backend srv_{{ host }} if is_rule_{{ host }}_1 OR is_rule_{{ host }}_2 + {% endfor %} + + {% for k in o["hosts"] %} + {% set host = k.replace(".", "_") + "_{0}_{1}".format(o["port"], salt) %} +backend srv_{{ host }} + 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 {{ k }} check weight 1 + {% endfor %} +{% endfor %} From b7ec691e8fdcc19a4ef6972946ef4af914396943 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Wed, 17 Jul 2019 01:55:07 -0500 Subject: [PATCH 06/17] Reading from Docker and Swarm --- README.md | 3 +- assets/scripts/haproxy-reload.sh | 47 ++++++++++++++++++++++++++++++-- assets/scripts/haproxy.sh | 2 ++ static.py | 4 +-- swarm.py | 13 ++++++--- swarm.sh | 6 ++-- templates/haproxy.cfg.j2 | 2 +- 7 files changed, 63 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 687cb78..ac2d6a0 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,13 @@ docker run \ -e HAPROXY_USERNAME=admin \ -e HAPROXY_PASSWORD=password \ -e HAPROXY_STATS_PORT=1936 \ - -e REFRESH_STATE=1 \ -v /var/run/docker.sock:/var/run/docker.sock \ -d byjg/easy-haproxy ``` The `DISCOVER` environment variable will define where is located your containers (see below more details): - swarm -- awsecs +- static (maps to /etc/haproxy/easyconfig.yml) # Tags to defined: diff --git a/assets/scripts/haproxy-reload.sh b/assets/scripts/haproxy-reload.sh index 402f4c6..5088b90 100755 --- a/assets/scripts/haproxy-reload.sh +++ b/assets/scripts/haproxy-reload.sh @@ -1,5 +1,48 @@ #!/usr/bin/env bash -echo "Reloading..." +cd /scripts -/usr/local/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf $(cat /run/haproxy.pid) & \ No newline at end of file +RELOAD="true" + +if [[ "$DISCOVER" == "static" ]]; then + CONTROL_FILE="/etc/haproxy/haproxy.cfg" + cp ${CONTROL_FILE} ${CONTROL_FILE}.old + python3 static.py /etc/haproxy/easyconfig.yml > ${CONTROL_FILE} +else + CONTROL_FILE="/tmp/.docker_data" + touch ${CONTROL_FILE} + mv ${CONTROL_FILE} ${CONTROL_FILE}.old + touch ${CONTROL_FILE} + + if [[ "$DISCOVER" == "docker" ]]; then + CONTAINERS=$(docker ps -q) + LABEL_PATH=".Config.Labels" + else + CONTAINERS=$(docker node ps $(docker node ls -q) --format "{{ .Name }}" --filter desired-state=running | cut -d. -f1 | sort | uniq) + LABEL_PATH=".Spec.Labels" + fi + + for container in ${CONTAINERS}; do + docker inspect --format "{{ json $LABEL_PATH }}" ${container} | xargs -I % echo ${container}=% >> ${CONTROL_FILE} + done + + if cmp -s ${CONTROL_FILE} ${CONTROL_FILE}.old ; then + RELOAD="false" + else + python3 swarm.py > /etc/haproxy/haproxy.cfg + fi +fi + +if cmp -s ${CONTROL_FILE} ${CONTROL_FILE}.old ; then + RELOAD="false" +fi + +if [[ ! -z "$1" ]]; then + echo "Initial configuration" + RELOAD="false" +fi + +if [[ "$RELOAD" == "true" ]]; then + echo "Reloading..." + /usr/local/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf $(cat /run/haproxy.pid) & +fi diff --git a/assets/scripts/haproxy.sh b/assets/scripts/haproxy.sh index f6a691f..89dd3a9 100755 --- a/assets/scripts/haproxy.sh +++ b/assets/scripts/haproxy.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash +source /scripts/haproxy-reload.sh initial + /usr/local/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock while true; do diff --git a/static.py b/static.py index 87efb42..07054ef 100644 --- a/static.py +++ b/static.py @@ -3,12 +3,12 @@ import sys from easymapping import HaproxyConfigGenerator if len(sys.argv) != 2: - print("You need to pass the easyconfig.cfg path") + print("You need to pass the easyconfig.yml path") exit(1) with open(sys.argv[1], 'r') as content_file: - parsed = yaml.load(content_file.read()) + parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader) cfg = HaproxyConfigGenerator(parsed) print(cfg.generate()) diff --git a/swarm.py b/swarm.py index dd2921a..975e25d 100644 --- a/swarm.py +++ b/swarm.py @@ -2,13 +2,19 @@ import os import json from easymapping import HaproxyConfigGenerator -path = os.path.dirname(os.path.realpath(__file__)) -with open(path + "/.docker_data", 'r') as content_file: +# path = os.path.dirname(os.path.realpath(__file__)) +with open("/tmp/.docker_data", 'r') as content_file: lineList = content_file.readlines() result = { "easymapping": [] } +if os.getenv("HAPROXY_USERNAME"): + result["stats"] = { + "username": os.getenv("HAPROXY_USERNAME"), + "password": os.getenv("HAPROXY_PASSWORD"), + "port": os.getenv("HAPROXY_STATS_PORT"), + } for line in lineList: line = line.strip() @@ -33,7 +39,7 @@ for line in lineList: redirect = d["com.byjg.easyhaproxy.redirect." + definition] if "com.byjg.easyhaproxy.redirect." + definition in d else "" for r in redirect.split(","): - r_parts = r.split("=>") + r_parts = r.split("--") data["redirect"][r_parts[0]] = r_parts[1] result["easymapping"].append(data) @@ -42,7 +48,6 @@ for line in lineList: cfg = HaproxyConfigGenerator(result) print(cfg.generate()) -print(result) # print(jsonStr) diff --git a/swarm.sh b/swarm.sh index 1948b52..dc83f63 100644 --- a/swarm.sh +++ b/swarm.sh @@ -5,13 +5,13 @@ # -l com.byjg.easyhaproxy.port.http=80 \ # -l com.byjg.easyhaproxy.port.localport=80 \ # -l com.byjg.easyhaproxy.host.http=byjg.com.br \ -# -l com.byjg.easyhaproxy.redirect.http=byjg.com.br%http://www.byjg.com.br,byjg.com%http://www.byjg.com.br \ +# -l com.byjg.easyhaproxy.redirect.http=byjg.com.br--http://www.byjg.com.br,byjg.com--http://www.byjg.com.br \ #-d byjg/php:7.3-fpm-nginx -touch ./.docker_data ./docker_data_old +touch /tmp/.docker_data /tmp/docker_data_old -mv ./.docker_data ./docker_data_old +cp ./.docker_data ./docker_data_old CONTAINERS=$(docker node ps $(docker node ls -q) --format "{{ .Name }}" --filter desired-state=running | cut -d. -f1 | sort | uniq) diff --git a/templates/haproxy.cfg.j2 b/templates/haproxy.cfg.j2 index 3ac2805..96862f3 100644 --- a/templates/haproxy.cfg.j2 +++ b/templates/haproxy.cfg.j2 @@ -63,6 +63,6 @@ backend srv_{{ host }} option forwardfor http-request set-header X-Forwarded-Port %[dst_port] http-request add-header X-Forwarded-Proto https if { ssl_fc } - server srv {{ k }} check weight 1 + server srv {{ o["hosts"][k] }} check weight 1 {% endfor %} {% endfor %} From 174dc4ac9fe788b065ff375071683129eebd600b Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Wed, 17 Jul 2019 23:39:14 -0500 Subject: [PATCH 07/17] Update Documentation --- README.md | 186 +++++++++++++++++++---------- assets/etc/haproxy/certs/README.md | 3 + 2 files changed, 126 insertions(+), 63 deletions(-) create mode 100644 assets/etc/haproxy/certs/README.md diff --git a/README.md b/README.md index ac2d6a0..d03b23c 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,93 @@ # Easy HAProxy -This Docker image will create dynamically the `haproxy.cfg` based on very simple Yaml. +This Docker image will create dynamically the `haproxy.cfg` based on the labels defined in docker containers or from +a simple Yaml instead docker # Features - Enable or disable Stats on port 1936 with custom password - Discover and setup haproxy from Docker Tag - Discover and setup haproxy redirect from Docker Tag +- Setup HAProxy CFG from a Yaml file. # Basic Usage -Docker run: +The Easy HAProxy will create the `haproxy.cfg` automatically based on the containers or from a YAML provided. + +The basic command line to run is: ```bash -docker run \ - -p 80:80 \ - -p 8080:8080 \ - -p 1936:1936 \ - --name easy-haproxy-instance \ - -e DISCOVER=swarm \ - -e HAPROXY_USERNAME=admin \ - -e HAPROXY_PASSWORD=password \ - -e HAPROXY_STATS_PORT=1936 \ +docker run -d \ + --name easy-haproxy-container \ -v /var/run/docker.sock:/var/run/docker.sock \ - -d byjg/easy-haproxy + # + Environment Variables \ + # + ports mapped to the host \ + byjg/easy-haproxy ``` -The `DISCOVER` environment variable will define where is located your containers (see below more details): -- swarm -- static (maps to /etc/haproxy/easyconfig.yml) +The mapping to `/var/run/docker.sock` is necessary to discover the docker containers and get the labels; -# Tags to defined: +The environment variables will setup the HAProxy. + +| Environment Variable | Description | +|----------------------|------------------------------------------------------------------| +| DISCOVER | How `haproxy.cfg` will be created: `static`, `docker` or `swarm` | +| HAPROXY_USERNAME | The HAProxy username to the statistics | +| HAPROXY_PASSWORD | The HAProxy password to the statistics | +| HAPROXY_STATS_PORT | The HAProxy port to the statistics | +| HAPROXY_CUSTOMERRORS | If HAProxy will use custom HTML errors. true/false | + + + +The environment variable `DISCOVER` will define where is located your containers (see below more details): +- docker +- swarm +- static + +# DISCOVER: `docker` + +This method will use a regular docker installation to discover the containers and configure the HAProxy. + +The only requirement is that containers and easy-haproxy must be in the same docker network. + +The discover will occur every minute. + +e.g.: + +```bash +docker create networkd easyhaproxy + +docker run --network easyhaproxy byjg/easyhaproxy + +docker run --network easyhaproxy myimage +``` + +# DISCOVER: `swarm` + +This method requires a functional Docker Swarm Cluster. The system will search for the labels in all containers on all +swarm nodes. + +The discover will occur every minute. + +Important: easyhaproxy needs to be in the same network of the containers or otherwise will not access. + +## Tags to be attached in the Docker Container | Tag | Description | |---------------------------------------------|---------------------------------------------------------------------------------------------------------| | com.byjg.easyhaproxy.definitions | A Comma delimited list with the definitions. Each name requires the definition of the parameters below. | | com.byjg.easyhaproxy.port. | What is the port that the HAProxy will listen to. | -| com.byjg.easyhaproxy.localport. | What is the port that the container is listening. | -| com.byjg.easyhaproxy.host. | What is the host that the HAProxy will listen to. (Defaults to 80) | +| com.byjg.easyhaproxy.localport. | What is the port that the container is listening. (Defaults to 80) | +| com.byjg.easyhaproxy.host. | What is the host that the HAProxy will listen to. | | com.byjg.easyhaproxy.redirect. | Host redirects from connections in the port defined above. | | com.byjg.easyhaproxy.sslcert. | Cert PEM Base64 encoded. | E.g. -``` +### Single Definition: + +```bash docker run \ -l com.byjg.easyhaproxy.definitions=http \ -l com.byjg.easyhaproxy.port.http=80\ @@ -52,16 +95,70 @@ docker run \ .... ``` -## Redirect Example: +### Multiples Definitions on the same container: -```text -www.byjg.com.br=>http://byjg.com.br,byjg.com=>http://byjg.com.br +```bash +docker run \ + -l com.byjg.easyhaproxy.definitions=express,admin \ + + -l com.byjg.easyhaproxy.port.express=80 \ + -l com.byjg.easyhaproxy.localport.express=3000 \ + -l com.byjg.easyhaproxy.host.express=express.byjg.com.br \ + + -l com.byjg.easyhaproxy.port.admin=80 \ + -l com.byjg.easyhaproxy.localport.admin=3001 \ + -l com.byjg.easyhaproxy.host.admin=admin.byjg.com.br \ + .... +``` + +### Redirect Example: + +```bash +docker run \ + -l com.byjg.easyhaproxy.redirect.=www.byjg.com.br::http://byjg.com.br,byjg.com::http://byjg.com.br +``` + +# DISCOVER: `static` + +This method expects a YAML file to setup the `haproxy.cfg` + +Create a YAML file and map to `/etc/haproxy/easyconfig.yml` + +```yaml +stats: + username: admin + password: password + port: 1936 # Optional (default 1936) + +customerrors: true # Optional (default false) + +easymapping: + - port: 80 + hosts: + host1.com.br: container:5000 + host2.com.br: other:3000 + redirect: + www.host1.com.br: http://host1.com.br + + - port: 443 + ssl_cert: BASE64_PEM_CERTIFICATE + hosts: + host1.com.br: container:80 + + - port: 8080 + hosts: + host3.com.br: domain:8181``` +``` + +Running: + +```bash +docker run -v /my/config.yml:/etc/haproxy/easyconfig.yml .... byjg/easyhaproxy ``` # Mapping custom .cfg files -Just create a folder and put files with the extension .cfg. and map the volume to the container. -This will concatenate your config into the main haproxy.cfg +Map a folder containing valid HAProxy `.cfg` files to `/etc/haproxy/conf.d`. It will be concatenated to your HAProxy CFG. ```bash docker run \ @@ -70,29 +167,6 @@ docker run \ -d byjg/easy-haproxy ``` -Check if your config is ok: - -```bash -docker run \ - /* other parameters */ - -v /your/local/conf.d:/etc/haproxy/conf.d \ - -byjg/easy-haproxy -c -f /etc/haproxy/haproxy.cfg -``` - -# Docker Compose - -```yaml -version: "3.4" -services: - front: - image: byjg/easy-haproxy - volume: - - /path/to/local:/etc/easyconfig - ports: - - 80:80 - - 8080:8080 - - 1936:1936 -``` # Handling SSL @@ -110,8 +184,8 @@ Important: Different certificates need to be handled in different entries. # Setting Custom Errors -Map the volume : `/etc/haproxy/errors-custom/` and put a file named `ERROR_NUMBER.http` where ERROR_NUMBER -is the http error code (e.g. 503.http) +If enabled, map the volume : `/etc/haproxy/errors-custom/` to your container and put a file named `ERROR_NUMBER.http` +where ERROR_NUMBER is the http error code (e.g. 503.http) # Build @@ -120,17 +194,3 @@ docker build -t byjg/easy-haproxy . ``` - -# Docker Swarm - - -# AWS - -Easy HAProxy will try to get tasks from the tasks running in the CLUSTER. -It is important that easy haproxy run in the ECS Cluster and can connect to the containers. - - -You'll have to pass also the `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` or setup a role in the instance in order and -the `ECS_CLUSTER` with the arn of the cluster - - diff --git a/assets/etc/haproxy/certs/README.md b/assets/etc/haproxy/certs/README.md new file mode 100644 index 0000000..828a1d8 --- /dev/null +++ b/assets/etc/haproxy/certs/README.md @@ -0,0 +1,3 @@ +# Certs Folder + +Docker Easy HAProxy will save the SSL Certs here. From 8afffcebd5df7041151478c777e16562ab145a67 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Wed, 17 Jul 2019 23:40:17 -0500 Subject: [PATCH 08/17] Added SSL from docker label. --- swarm.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/swarm.py b/swarm.py index 975e25d..20b67f8 100644 --- a/swarm.py +++ b/swarm.py @@ -1,5 +1,7 @@ import os import json +import time +import base64 from easymapping import HaproxyConfigGenerator # path = os.path.dirname(os.path.realpath(__file__)) @@ -7,7 +9,8 @@ with open("/tmp/.docker_data", 'r') as content_file: lineList = content_file.readlines() result = { - "easymapping": [] + "easymapping": [], + "customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False } if os.getenv("HAPROXY_USERNAME"): result["stats"] = { @@ -27,6 +30,9 @@ for line in lineList: definitions = d["com.byjg.easyhaproxy.definitions"].split(",") for definition in definitions: + if "com.byjg.easyhaproxy.host." + definition not in d: + continue + port = d["com.byjg.easyhaproxy.port." + definition] if "com.byjg.easyhaproxy.port." + definition in d else "80" data = { "port": port, @@ -35,12 +41,19 @@ for line in lineList: # "ssl_cert": "" } - data["hosts"][d["com.byjg.easyhaproxy.host." + definition] if "com.byjg.easyhaproxy.host." + definition in d else ""] = container + ":" + (d["com.byjg.easyhaproxy.localport." + definition] if "com.byjg.easyhaproxy.localport." + definition in d else "80") + data["hosts"][d["com.byjg.easyhaproxy.host." + definition]] = container + ":" + (d["com.byjg.easyhaproxy.localport." + definition] if "com.byjg.easyhaproxy.localport." + definition in d else "80") - redirect = d["com.byjg.easyhaproxy.redirect." + definition] if "com.byjg.easyhaproxy.redirect." + definition in d else "" - for r in redirect.split(","): - r_parts = r.split("--") - data["redirect"][r_parts[0]] = r_parts[1] + if "com.byjg.easyhaproxy.sslcert." + definition in d: + filename = '/etc/haproxy/certs/' + d["com.byjg.easyhaproxy.host." + definition] + "." + str(time.time()) + ".pem" + data["ssl_cert"] = filename + with open(filename, 'wb') as file: + file.write(base64.b64decode(d["com.byjg.easyhaproxy.sslcert." + definition])) + + if "com.byjg.easyhaproxy.redirect." + definition in d: + redirect = d["com.byjg.easyhaproxy.redirect." + definition] if "com.byjg.easyhaproxy.redirect." + definition in d else "" + for r in redirect.split(","): + r_parts = r.split("--") + data["redirect"][r_parts[0]] = r_parts[1] result["easymapping"].append(data) From f283cb713134582de85c45874711c58ecde44565 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Thu, 18 Jul 2019 00:06:40 -0500 Subject: [PATCH 09/17] Stats access requires custom password --- README.md | 14 +++++++------- swarm.py | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index d03b23c..b09614e 100644 --- a/README.md +++ b/README.md @@ -30,13 +30,13 @@ The mapping to `/var/run/docker.sock` is necessary to discover the docker contai The environment variables will setup the HAProxy. -| Environment Variable | Description | -|----------------------|------------------------------------------------------------------| -| DISCOVER | How `haproxy.cfg` will be created: `static`, `docker` or `swarm` | -| HAPROXY_USERNAME | The HAProxy username to the statistics | -| HAPROXY_PASSWORD | The HAProxy password to the statistics | -| HAPROXY_STATS_PORT | The HAProxy port to the statistics | -| HAPROXY_CUSTOMERRORS | If HAProxy will use custom HTML errors. true/false | +| Environment Variable | Description | +|----------------------|--------------------------------------------------------------------| +| DISCOVER | How `haproxy.cfg` will be created: `static`, `docker` or `swarm` | +| HAPROXY_USERNAME | The HAProxy username to the statistics. Default: `admin` | +| HAPROXY_PASSWORD | The HAProxy password to the statistics. If not set disable stats. | +| HAPROXY_STATS_PORT | The HAProxy port to the statistics. Default: `1936` | +| HAPROXY_CUSTOMERRORS | If HAProxy will use custom HTML errors. true/false. Default: false | diff --git a/swarm.py b/swarm.py index 20b67f8..f180a26 100644 --- a/swarm.py +++ b/swarm.py @@ -12,11 +12,11 @@ result = { "easymapping": [], "customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False } -if os.getenv("HAPROXY_USERNAME"): +if os.getenv("HAPROXY_PASSWORD"): result["stats"] = { - "username": os.getenv("HAPROXY_USERNAME"), + "username": os.getenv("HAPROXY_USERNAME") if os.getenv("HAPROXY_USERNAME") else "admin", "password": os.getenv("HAPROXY_PASSWORD"), - "port": os.getenv("HAPROXY_STATS_PORT"), + "port": os.getenv("HAPROXY_STATS_PORT") if os.getenv("HAPROXY_STATS_PORT") else "1936", } for line in lineList: From e823471a44a8cca6b84621e84b64652c0002e92a Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Thu, 18 Jul 2019 00:17:27 -0500 Subject: [PATCH 10/17] Update HAProxy from 1.9.8 to 2.0.2 --- Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 97c7a41..616f926 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM haproxy:1.9-alpine +FROM haproxy:2.0.2-alpine WORKDIR /scripts @@ -15,6 +15,4 @@ COPY easymapping /scripts/easymapping/ COPY assets / - -#CMD ["haproxy", "-f", "/etc/haproxy/haproxy.cfg"] CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf" ] \ No newline at end of file From 7e2b6f0f01119ee49996cc4b911ebcf4328e86b0 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Thu, 18 Jul 2019 00:19:37 -0500 Subject: [PATCH 11/17] Add __pycache__ to .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4c78e58..d760fc3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea *~ venv -.docker_data \ No newline at end of file +.docker_data +__pycache__ From 8d7723e41556b42c04d4e395bddc43244b251a99 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Fri, 19 Jul 2019 00:04:55 -0500 Subject: [PATCH 12/17] Update README to support swarm --- README.md | 13 ++++++++++++- assets/scripts/haproxy-reload.sh | 1 + swarm.sh | 28 ---------------------------- 3 files changed, 13 insertions(+), 29 deletions(-) delete mode 100644 swarm.sh diff --git a/README.md b/README.md index b09614e..128e84e 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,18 @@ Important: easyhaproxy needs to be in the same network of the containers or othe | com.byjg.easyhaproxy.redirect. | Host redirects from connections in the port defined above. | | com.byjg.easyhaproxy.sslcert. | Cert PEM Base64 encoded. | -E.g. + +Note: if you are deploying a stack set labels at the `deploy` level: + +```yaml +services: + foo: + deploy: + labels: + com.byjg.easyhaproxy.definitions: "http,https" + ... +``` + ### Single Definition: diff --git a/assets/scripts/haproxy-reload.sh b/assets/scripts/haproxy-reload.sh index 5088b90..df9f603 100755 --- a/assets/scripts/haproxy-reload.sh +++ b/assets/scripts/haproxy-reload.sh @@ -6,6 +6,7 @@ RELOAD="true" if [[ "$DISCOVER" == "static" ]]; then CONTROL_FILE="/etc/haproxy/haproxy.cfg" + touch ${CONTROL_FILE} cp ${CONTROL_FILE} ${CONTROL_FILE}.old python3 static.py /etc/haproxy/easyconfig.yml > ${CONTROL_FILE} else diff --git a/swarm.sh b/swarm.sh deleted file mode 100644 index dc83f63..0000000 --- a/swarm.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -#docker run \ -# -l com.byjg.easyhaproxy.definitions=http \ -# -l com.byjg.easyhaproxy.port.http=80 \ -# -l com.byjg.easyhaproxy.port.localport=80 \ -# -l com.byjg.easyhaproxy.host.http=byjg.com.br \ -# -l com.byjg.easyhaproxy.redirect.http=byjg.com.br--http://www.byjg.com.br,byjg.com--http://www.byjg.com.br \ -#-d byjg/php:7.3-fpm-nginx - - -touch /tmp/.docker_data /tmp/docker_data_old - -cp ./.docker_data ./docker_data_old - -CONTAINERS=$(docker node ps $(docker node ls -q) --format "{{ .Name }}" --filter desired-state=running | cut -d. -f1 | sort | uniq) - -for container in $CONTAINERS; do - docker inspect --format "{{ json .Spec.Labels }}" $container | xargs -I % echo $container=% >> ./.docker_data -done - - -if cmp -s ./.docker_data ./.docker_data_old ; then - exit 0 -fi - -python3 swarm.py -# byjg_site={"com.byjg.easyhaproxy.definition":"http","com.byjg.easyhaproxy.host.http":"byjg.com.br","com.byjg.easyhaproxy.port.http":"80","com.byjg.easyhaproxy.redirect.http":"byjg.com.br%http://www.byjg.com.br,byjg.com%http://www.byjg.com.br","maintainer":"Joao Gilberto Magalhaes"} From cbcd59d6f754889be68c690d573b2b13976a3e60 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Sun, 21 Jul 2019 00:15:53 -0500 Subject: [PATCH 13/17] Fix redirect documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 128e84e..cd9b4d4 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ docker run \ ```bash docker run \ - -l com.byjg.easyhaproxy.redirect.=www.byjg.com.br::http://byjg.com.br,byjg.com::http://byjg.com.br + -l com.byjg.easyhaproxy.redirect.=www.byjg.com.br--http://byjg.com.br,byjg.com--http://byjg.com.br ``` # DISCOVER: `static` From 21686e160532158100bbb91bc6897dfac1b9cec1 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Sun, 21 Jul 2019 00:18:13 -0500 Subject: [PATCH 14/17] Group the bind to the same port. --- swarm.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/swarm.py b/swarm.py index f180a26..a74cef1 100644 --- a/swarm.py +++ b/swarm.py @@ -2,6 +2,7 @@ import os import json import time import base64 +import hashlib from easymapping import HaproxyConfigGenerator # path = os.path.dirname(os.path.realpath(__file__)) @@ -12,6 +13,8 @@ result = { "easymapping": [], "customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False } +easymapping = dict() + if os.getenv("HAPROXY_PASSWORD"): result["stats"] = { "username": os.getenv("HAPROXY_USERNAME") if os.getenv("HAPROXY_USERNAME") else "admin", @@ -34,18 +37,23 @@ for line in lineList: continue port = d["com.byjg.easyhaproxy.port." + definition] if "com.byjg.easyhaproxy.port." + definition in d else "80" - data = { - "port": port, - "hosts": dict(), - "redirect": dict(), - # "ssl_cert": "" - } + hash = hashlib.md5(d["com.byjg.easyhaproxy.sslcert." + definition].encode('utf-8')).hexdigest() if "com.byjg.easyhaproxy.sslcert." + definition in d else "" - data["hosts"][d["com.byjg.easyhaproxy.host." + definition]] = container + ":" + (d["com.byjg.easyhaproxy.localport." + definition] if "com.byjg.easyhaproxy.localport." + definition in d else "80") + key = port+hash + + if key not in easymapping: + easymapping[key] = { + "port": port, + "hosts": dict(), + "redirect": dict(), + # "ssl_cert": "" + } + + easymapping[key]["hosts"][d["com.byjg.easyhaproxy.host." + definition]] = container + ":" + (d["com.byjg.easyhaproxy.localport." + definition] if "com.byjg.easyhaproxy.localport." + definition in d else "80") if "com.byjg.easyhaproxy.sslcert." + definition in d: filename = '/etc/haproxy/certs/' + d["com.byjg.easyhaproxy.host." + definition] + "." + str(time.time()) + ".pem" - data["ssl_cert"] = filename + easymapping[key]["ssl_cert"] = filename with open(filename, 'wb') as file: file.write(base64.b64decode(d["com.byjg.easyhaproxy.sslcert." + definition])) @@ -53,9 +61,9 @@ for line in lineList: redirect = d["com.byjg.easyhaproxy.redirect." + definition] if "com.byjg.easyhaproxy.redirect." + definition in d else "" for r in redirect.split(","): r_parts = r.split("--") - data["redirect"][r_parts[0]] = r_parts[1] + easymapping[key]["redirect"][r_parts[0]] = r_parts[1] - result["easymapping"].append(data) + result["easymapping"] = easymapping.values() cfg = HaproxyConfigGenerator(result) From e678865421558a7ef7c463541d74dd480db7e9c5 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Sun, 21 Jul 2019 19:06:48 -0500 Subject: [PATCH 15/17] Update README.md --- README.md | 60 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index cd9b4d4..bb57c50 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ The basic command line to run is: docker run -d \ --name easy-haproxy-container \ -v /var/run/docker.sock:/var/run/docker.sock \ + -e DISCOVER="swarm|docker|static" \ # + Environment Variables \ # + ports mapped to the host \ byjg/easy-haproxy @@ -30,13 +31,13 @@ The mapping to `/var/run/docker.sock` is necessary to discover the docker contai The environment variables will setup the HAProxy. -| Environment Variable | Description | -|----------------------|--------------------------------------------------------------------| -| DISCOVER | How `haproxy.cfg` will be created: `static`, `docker` or `swarm` | -| HAPROXY_USERNAME | The HAProxy username to the statistics. Default: `admin` | -| HAPROXY_PASSWORD | The HAProxy password to the statistics. If not set disable stats. | -| HAPROXY_STATS_PORT | The HAProxy port to the statistics. Default: `1936` | -| HAPROXY_CUSTOMERRORS | If HAProxy will use custom HTML errors. true/false. Default: false | +| Environment Variable | Description | +|----------------------|-------------------------------------------------------------------------------| +| DISCOVER | How `haproxy.cfg` will be created: `static`, `docker` or `swarm` | +| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. Default: `admin` | +| HAPROXY_PASSWORD | The HAProxy password to the statistics. If not set disable stats. | +| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. Default: `1936` | +| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. Default: false | @@ -77,11 +78,11 @@ Important: easyhaproxy needs to be in the same network of the containers or othe | Tag | Description | |---------------------------------------------|---------------------------------------------------------------------------------------------------------| | com.byjg.easyhaproxy.definitions | A Comma delimited list with the definitions. Each name requires the definition of the parameters below. | -| com.byjg.easyhaproxy.port. | What is the port that the HAProxy will listen to. | -| com.byjg.easyhaproxy.localport. | What is the port that the container is listening. (Defaults to 80) | +| com.byjg.easyhaproxy.port. | (Optional) What is the port that the HAProxy will listen to. (Defaults to 80) | +| com.byjg.easyhaproxy.localport. | (Optional) What is the port that the container is listening. (Defaults to 80) | | com.byjg.easyhaproxy.host. | What is the host that the HAProxy will listen to. | -| com.byjg.easyhaproxy.redirect. | Host redirects from connections in the port defined above. | -| com.byjg.easyhaproxy.sslcert. | Cert PEM Base64 encoded. | +| com.byjg.easyhaproxy.redirect. | (Optional) Host redirects from connections in the port defined above. | +| com.byjg.easyhaproxy.sslcert. | (Optional) Cert PEM Base64 encoded. | Note: if you are deploying a stack set labels at the `deploy` level: @@ -119,7 +120,8 @@ docker run \ -l com.byjg.easyhaproxy.port.admin=80 \ -l com.byjg.easyhaproxy.localport.admin=3001 \ -l com.byjg.easyhaproxy.host.admin=admin.byjg.com.br \ - .... + .... \ + some/myimage ``` ### Redirect Example: @@ -158,7 +160,7 @@ easymapping: - port: 8080 hosts: - host3.com.br: domain:8181``` + host3.com.br: domain:8181 ``` Running: @@ -181,17 +183,33 @@ docker run \ # Handling SSL -HaProxy can handle SSL for you. in this case add the parameter pointing to file containing -the pem of certificates and key in only one file: +You can attach a valid SSL certificate to the request. -``` - - port: 443 - ssl_cert: /etc/easyconfig/mycert.pem - hosts: - host1.com.br: container:80 +1. First Create a single PEM file including CA. + +```bash +cat cert.pem priv.pem > single.pem + +cat single.pem + +-----BEGIN CERTIFICATE----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC5ZheHqmBnEJP+ +U9r1gxYWKLzdqrMrcxtQN6M1hIH9n0peuJeIrybdcV7sMbStMXI= +-----END CERTIFICATE----- + +-----BEGIN PRIVATE KEY----- +MIIEojCCA4qgAwIBAgIUegW2BimwuL4RzRZ2WYkHA6U5nkAwDQYJKoZIhvcNAQEL +3j4wz8/I5fdsk090j4s5KA== +-----END PRIVATE KEY----- ``` -Important: Different certificates need to be handled in different entries. +2. Convert it to BASE64 in a single line: + +```bash +cat single.pem | base64 -w0 +``` + +3. Use this string to define the label `com.byjg.easyhaproxy.sslcert.` # Setting Custom Errors From 0835560baaa70d654bf8cda67599bf9a708284ee Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Sun, 21 Jul 2019 19:14:01 -0500 Subject: [PATCH 16/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb57c50..bd225c9 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ You can attach a valid SSL certificate to the request. 1. First Create a single PEM file including CA. ```bash -cat cert.pem priv.pem > single.pem +cat example.com.crt example.com.key > single.pem cat single.pem From 57f336329e1e1acd8d18cb3d8dbdddfadb97d4df Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Tue, 23 Jul 2019 23:06:58 -0500 Subject: [PATCH 17/17] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bd225c9..0916837 100644 --- a/README.md +++ b/README.md @@ -78,11 +78,11 @@ Important: easyhaproxy needs to be in the same network of the containers or othe | Tag | Description | |---------------------------------------------|---------------------------------------------------------------------------------------------------------| | com.byjg.easyhaproxy.definitions | A Comma delimited list with the definitions. Each name requires the definition of the parameters below. | -| com.byjg.easyhaproxy.port. | (Optional) What is the port that the HAProxy will listen to. (Defaults to 80) | -| com.byjg.easyhaproxy.localport. | (Optional) What is the port that the container is listening. (Defaults to 80) | -| com.byjg.easyhaproxy.host. | What is the host that the HAProxy will listen to. | -| com.byjg.easyhaproxy.redirect. | (Optional) Host redirects from connections in the port defined above. | -| com.byjg.easyhaproxy.sslcert. | (Optional) Cert PEM Base64 encoded. | +| com.byjg.easyhaproxy.port.[definition] | (Optional) What is the port that the HAProxy will listen to. (Defaults to 80) | +| com.byjg.easyhaproxy.localport.[definition] | (Optional) What is the port that the container is listening. (Defaults to 80) | +| com.byjg.easyhaproxy.host.[definition] | What is the host that the HAProxy will listen to. | +| com.byjg.easyhaproxy.redirect.[definition] | (Optional) Host redirects from connections in the port defined above. | +| com.byjg.easyhaproxy.sslcert.[definition] | (Optional) Cert PEM Base64 encoded. | Note: if you are deploying a stack set labels at the `deploy` level: @@ -209,7 +209,7 @@ MIIEojCCA4qgAwIBAgIUegW2BimwuL4RzRZ2WYkHA6U5nkAwDQYJKoZIhvcNAQEL cat single.pem | base64 -w0 ``` -3. Use this string to define the label `com.byjg.easyhaproxy.sslcert.` +3. Use this string to define the label `com.byjg.easyhaproxy.sslcert.[definition]` # Setting Custom Errors