1
0
Fork 0
docker-easy-haproxy/README.md
2022-08-17 14:34:06 +00:00

12 KiB

Easy HAProxy

Opensource ByJG Build Status GitHub source GitHub license GitHub release

Service discovery for HAProxy.

This Docker image will create dynamically the haproxy.cfg based on the labels defined in docker containers or from a simple Yaml.

Features

EasyHAProxy will discover the services based on the Docker Tags of the running containers in a Docker host or Docker Swarm cluster and setup the haproxy.cfg dynamically. The configurations can be set are:

  • Use Letsencrypt with HAProxy.
  • Balance traffic between multiple replicas
  • Set SSL according the most recent definitions to improve security and with
  • Include your own SSL certificate.
  • Setup HAProxy to listen TCP.
  • Add redirects.
  • Enable/disable Stats on port 1936 with custom password.
  • Enable/disable custom errors.

Also it is possible setup HAProxy from a simple Yaml file instead of setup haproxy.cfg dynamically.

Basic Usage

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:

docker run -d \
    --name easy-haproxy-container \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -e EASYHAPROXY_DISCOVER="swarm|docker|static" \
    # + Environment Variables \
    # + ports mapped to the host \
    byjg/easy-haproxy

The mapping to /var/run/docker.sock is necessary to discover the docker containers and get the labels;

The environment variables will setup the HAProxy.

Environment Variable Description
EASYHAPROXY_DISCOVER How haproxy.cfg will be created: static, docker or swarm
EASYHAPROXY_LABEL_PREFIX (Optional) The key will search to match resources. Default: easyhaproxy.
EASYHAPROXY_LETSENCRYPT_EMAIL (Optional) The email will be used to request certificate to letsencrypt
EASYHAPROXY_SSL_MODE (Optional) STRICT supports only the most recent TLS version; DEFAULT good SSL integration with recent browsers; LOOSE support all old SSL protocols for old browsers (not recommended).
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

The environment variable EASYHAPROXY_DISCOVER will define where is located your containers (see below more details):

  • docker
  • swarm
  • static

Automatic Discover Services

Easy HAProxy can discover automatically the container services running in the same network of Docker or in a Docker Swarm cluster.

EASYHAPROXY_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.:

docker create networkd easyhaproxy

docker run --network easyhaproxy byjg/easyhaproxy

docker run --network easyhaproxy myimage

EASYHAPROXY_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 (Swarm or Docker)

Tag Description Example
easyhaproxy.[definition].host Host(s) HAProxy is listening. More than one host use comma as delimiter somehost.com OR host1.com,host2.com
easyhaproxy.[definition].mode (Optional) Is this http or tcp mode in HAProxy. (Defaults to http) http
easyhaproxy.[definition].port (Optional) Port HAProxy will listen for the host. (Defaults to 80) 80
easyhaproxy.[definition].localport (Optional) Port container is listening. (Defaults to 80) 8080
easyhaproxy.[definition].redirect (Optional) JSON containing key/value pair from host/to url redirect. {"foo.com":"https://bla.com", "bar.com":"https://bar.org"}
easyhaproxy.[definition].sslcert (Optional) Cert PEM Base64 encoded. Do not use this if letsencrypt is enabled.
easyhaproxy.[definition].ssl (Optional) If true you need to provide certificate as a file. See below. Do not use with sslcert. true
easyhaproxy.[definition].health-check (Optional) ssl, enable health check via SSL in mode tcp (Defaults to "empty") ssl
easyhaproxy.[definition].letsencrypt (Optional) Generate certificate with letsencrypt. Do not use with sslcert. true OR yes OR false OR no
easyhaproxy.[definition].redirect-ssl (Optional) Redirect all requests to https true OR yes OR false OR no

Defining the labels in Docker Swarm

if you are deploying a stack in a Docker Swarm cluster set labels at the deploy level:

services:
 foo:
   deploy:
      labels:
         easyhaproxy.my.host: "www.example.org"
         easyhaproxy.my.localport: 8080
         ...

Single Definition

docker run \
    -l easyhaproxy.webapi.port=80\
    -l easyhaproxy.webapi.host=byjg.com.br \
    ....

Multiples Definitions on the same container

docker run \
    -l easyhaproxy.express.port=80 \
    -l easyhaproxy.express.localport=3000 \
    -l easyhaproxy.express.host=express.byjg.com.br \

    -l easyhaproxy.admin.port=80 \
    -l easyhaproxy.admin.localport=3001 \
    -l easyhaproxy.admin.host=admin.byjg.com.br \
    .... \
    some/myimage

TLS passthrough

Used to pass on SSL-termination to a backend. Alternatively, you can enable health-check via SSL on the backend with the optional health-check label:

docker run \
    -l easyhaproxy.example.mode=tcp \
    -l easyhaproxy.example.health-check=ssl \
    -l easyhaproxy.example.port=443
    .... \
    some/tcp-service

Redirect Example

docker run \
    -l easyhaproxy.[definition].redirect='{"www.byjg.com.br":"http://byjg.com.br","byjg.com":"http://byjg.com.br"}'

EASYHAPROXY_DISCOVER: static

This method expects a YAML file to setup the haproxy.cfg

Create a YAML file and map to /etc/haproxy/easyconfig.yml

stats:
  username: admin
  password: password
  port: 1936         # Optional (default 1936)

customerrors: true   # Optional (default false)

easymapping:
  - port: 80
    hosts:
      host1.com.br: 
        containers:
          - container:5000
        letsencrypt: true
        redirect-ssl: true
      host2.com.br: 
        containers:
          - other:3000
        ssl: false
    redirect:
      www.host1.com.br: http://host1.com.br

  - port: 443
    ssl_cert: /path/to/ssl/certificate
    hosts:
      host1.com.br: 
        containers:
          - container:80
        redirect-ssl: false

  - port: 8080
    hosts:
      host3.com.br: 
        containers: 
          - domain:8181

Running:

docker run -v /my/config.yml:/etc/haproxy/easyconfig.yml .... byjg/easyhaproxy

Letsencrypt

This HAProxy can issue a letsencrypt certificate. The command is as below:

Run the EasyHAProxy:

docker run \
    -e EASYHAPROXY_LETSENCRYPT_EMAIL=john@doe.com
    .... \
    byjg/easy-haproxy

Run your container:

docker run \
    -l easyhaproxy.express.port=80 \
    -l easyhaproxy.express.localport=3000 \
    -l easyhaproxy.express.host=example.org \
    -l easyhaproxy.express.letsencrypt=true \
    .... \
    some/myimage

Caveats:

  • Your container must listen to the port 80. Besides no error, the certificate won't be issued if in a different port.
  • The port 2080 is reserved for the certbot and should not be exposed.
  • You cannot set the port 443 for the container with the Letsencrypt. EasyHAProxy will handle this automatically once the certificate is issued.
  • If you don't run the EasyHAProxy with the parameter EASYHAPROXY_LETSENCRYPT_EMAIL no certificate will be issued.
  • Be aware about issue limits - https://letsencrypt.org/docs/duplicate-certificate-limit/ and https://letsencrypt.org/docs/rate-limits/

Exposing Ports

  • You need to expose at least the ports 80 and 443 when you run the byjg/easy-haproxy image.
  • If you enable the HAProxy statistics you must also expose the port defined in HAPROXY_STATS_PORT environment variable.
  • Every port defined in easyhaproxy.[definitions].port also should be enabel.

e.g.

docker run \
    /* other parameters */
    -p 80:80 \
    -p 443:443 \
    -p 1936:1936 \
    -d byjg/easy-haproxy

Also, you need to expose these ports in the firewall.

Mapping custom .cfg files

Map a folder containing valid HAProxy .cfg files to /etc/haproxy/conf.d. It will be concatenated to your HAProxy CFG.

docker run \
    /* other parameters */
    -v /your/local/conf.d:/etc/haproxy/conf.d \
    -d byjg/easy-haproxy

Mapping ssl certificates volumes

EasyHAProxy stores the certificates inside the folder /certs/haproxy and /certs/letsencrypt.

  • If you want to preserve the letsencrypt certificates between reloads, just map the folder /certs/letsencrypt to your volume.
  • If you want to provide your own certificates as a file instead a Base64 parameter, just map the folder /certs/haproxy to your volume and instead of use easyhaproxy.[definition].sslcert use easyhaproxy.[definition].ssl: true
docker run \
    /* other parameters */
    -v /your/certs/letsencrypt:/certs/letsencrypt \
    -d byjg/easy-haproxy

Handling SSL

You can attach a valid SSL certificate to the request.

  1. First Create a single PEM file including CA.
cat example.com.crt example.com.key > single.pem

cat single.pem

-----BEGIN CERTIFICATE-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC5ZheHqmBnEJP+
U9r1gxYWKLzdqrMrcxtQN6M1hIH9n0peuJeIrybdcV7sMbStMXI=
-----END CERTIFICATE-----

-----BEGIN PRIVATE KEY-----
MIIEojCCA4qgAwIBAgIUegW2BimwuL4RzRZ2WYkHA6U5nkAwDQYJKoZIhvcNAQEL
3j4wz8/I5fdsk090j4s5KA==
-----END PRIVATE KEY-----
  1. Convert it to BASE64 in a single line:
cat single.pem | base64 -w0
  1. Use this string to define the label easyhaproxy.[definition].sslcert

Setting Custom Errors

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

docker build -t byjg/easy-haproxy .

Limitations

EasyHAProxy has some limitations when there is more than one easy-haproxy container running:

  • Replicas can be out-of-sync for a few seconds, because each replica will discover the pods separatedly.
  • Each replica will request a Letsencrypt certificate and it can fail because the letsencrypt challenge can be directed to the other replica.

Open source ByJG