diff --git a/README.md b/README.md index f00af27..33c62f5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Easy HAProxy +# EasyHAProxy [![Opensource ByJG](https://img.shields.io/badge/opensource-byjg-success.svg)](http://opensource.byjg.com) [![Build Status](https://github.com/byjg/docker-easy-haproxy/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/byjg/docker-easy-haproxy/actions/workflows/build.yml) @@ -6,10 +6,11 @@ [![GitHub license](https://img.shields.io/github/license/byjg/docker-easy-haproxy.svg)](https://opensource.byjg.com/opensource/licensing.html) [![GitHub release](https://img.shields.io/github/release/byjg/docker-easy-haproxy.svg)](https://github.com/byjg/docker-easy-haproxy/releases/) +![EasyHAProxy](logo.png) + ## Service discovery for HAProxy -This Docker image will dynamically create the `haproxy.cfg` based on the labels defined in docker containers or from -a simple Yaml. +The main objective of EasyHAProxy is dynamically create the `haproxy.cfg` based on the labels defined in docker containers. EasyHAProxy can detect and configure automatically HAProxy on the folowing platforms: @@ -30,476 +31,23 @@ EasyHAProxy will discover the services based on the Docker Tags of the running c - Enable/disable Stats on port 1936 with a custom password. - Enable/disable custom errors. -Also, it is possible to set up HAProxy from a simple Yaml file instead of creating `haproxy.cfg` file. +Also, it is possible to set up HAProxy from a simple Yaml file instead of creating `haproxy.cfg` file. -## Basic Usage +## How It Works? -The Easy HAProxy will automatically create the `haproxy.cfg` file based on the containers or a YAML provided. +You don't need to change your current infrastructure and don't need to learn the HAProxy configuration. -The basic command line to run is: +You need run the EasyHAProxy container, add some labels to your existing container and EasyHAProxy will +automatically detect them and setup HAProxy for you. -```bash -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 -``` +## Detailed Instructions -The mapping to `/var/run/docker.sock` is necessary to discover the docker containers and get the labels; +For detailed instructions on how to use EasyHAProxy follow the instructions for the platform you want to use: -The environment variables will setup the HAProxy. +| Kubernetes | Docker Swarm | Docker | Static +|------------|--------------|--------|--------- +| [![Kubernetes](kubernetes.png)](docs/kubernetes.md) | [![Docker Swarm](swarm.png)](docs/swarm.md) | [![Docker](docker.png)](docs/docker.md) | [![Static](static.png)](docs/static.md) -| Environment Variable | Description | Default | -|-------------------------------|-------------------------------------------------------------------------------------------------|------------------| -| EASYHAPROXY_DISCOVER | How the services will be discovered to create `haproxy.cfg`: `static`, `docker`, `swarm` or `kubernetes` | **required** | -| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. | `easyhaproxy` | -| EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request the certificate to Letsencrypt | *empty* | -| EASYHAPROXY_SSL_MODE | (Optional) `strict` supports only the most recent TLS version; `default` good SSL integration with recent browsers; `loose` supports all old SSL protocols for old browsers (not recommended). | `default`| -| EASYHAPROXY_REFRESH_CONF | (Optional) Check configuration every N seconds. | 10 | -| EASYHAPROXY_LOG_LEVEL | (Optional) The log level for EasyHAproxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | -| CERTBOT_LOG_LEVEL | (Optional) The log level for Certbot messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | -| HAPROXY_LOG_LEVEL | (Optional) The log level for HAProxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | -| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. | `admin` | -| HAPROXY_PASSWORD | (Optional) The HAProxy password to the statistics. If not set, statistics will be available with no password | *empty* | -| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. If set to `false`, disable statistics | `1936` | -| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. | `false` | - -The environment variable `EASYHAPROXY_DISCOVER` will define where is located your containers (see below for more details): - -- docker -- swarm -- static - -## Automatic Discover Services - -Easy HAProxy can automatically discover the container services running in the same network of Docker or in a Docker Swarm cluster. - -### EASYHAPROXY_DISCOVER: docker - -This method will use a standard 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 discovery will occur every minute. - -e.g.: - -```bash -docker create network easyhaproxy - -docker run --network easyhaproxy byjg/easyhaproxy - -docker run --network easyhaproxy myimage -``` - -or, if the container is already created you can join it using the command: - -```bash -docker network connect easyhaproxy mycontainer -``` - -### 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 discovery will occur every minute. - -Important: easyhaproxy needs to be in the same network of the containers or otherwise will not access. - -### EASYHAPROXY_DISCOVER: kubernetes (experimental and limited) - -This will query all `ingress` in the kubernetes cluster and check the annotation `kubernetes.io/ingress.class: easyhaproxy-ingress`. - -e.g.: - -```yaml -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: easyhaproxy-ingress - name: example-ingress - namespace: example -spec: - rules: - - host: example.org - http: - paths: - - backend: - service: - name: example-service - port: - number: 8080 - pathType: ImplementationSpecific - - host: www.example.org - http: - paths: - - backend: - service: - name: example-service - port: - number: 8080 - pathType: ImplementationSpecific -``` - -Caveats: - -- At this point, the implementation don't support all ingress properties nor wildcard domains. -- The ingress will publish externally only the ports 80 and 443, plus 1936 if stats is enable. -- The system will read `spec.rules[].host` and `spec.rules[].http.paths[0].port.number` and ignore the other parameters. -- Only the first path `spec.rules[].http.paths[0]` will be parsed. -- There are specific annotations can be added as described bellow. - -### Kubernetes annotations - -| annotation | Description | Default | Example | -|-----------------------------|-----------------------------------------------------------------------------------------|--------------|--------------| -| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress -| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to https. | false | true or false -| easyhaproxy.letsencrypt | (optional) Boolean. It will request letsencript certificates for the ingresses domains. | false | true or false -| easyhaproxy.redirect | (optional) Json. Specific a domain and its destination. | *empty* | {"domain":"redirect_url"} -| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp - -**Important**: The annotations are per ingress and applied to all hosts in that ingress configuration. - -### Kubernetes and Letsencrypt: - -It is necessary add the annotation `easyhaproxy.letsencrypt` to the ingress configuration: - -```yaml -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: easyhaproxy-ingress - easyhaproxy.letsencrypt: 'true' - name: example-ingress - namespace: example -spec: - .... -``` - -Make sure your cluster is accessible both through ports 80 and 443. - -### Kubernetes and SSL - -You need to create a secret with your certificate and key, and associate them in your ingress. - -```yaml ---- -apiVersion: v1 -kind: Secret -metadata: - name: host2-tls - namespace: default -data: - tls.crt: base64 of your certificate - tls.key: base64 of your certificate private key -type: kubernetes.io/tls - ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: easyhaproxy-ingress - name: tls-example - namespace: default -spec: - tls: - - hosts: - - host2.local - secretName: host2-tls - rules: - ... -``` - -### Container (Docker or Swarm) labels - -| Tag | 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].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].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].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `letsencrypt` 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].health-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl | -| easyhaproxy.[definition].letsencrypt | (Optional) Generate certificate with letsencrypt. Do not use with `sslcert` parameter. | 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 `letsencrypt` parameters | false | true OR false | - -### Defining the labels in Docker Swarm - -if you are deploying a stack in a Docker Swarm cluster, set labels at the `deploy` level: - -```yaml -services: - foo: - deploy: - labels: - easyhaproxy.my.host: "www.example.org" - easyhaproxy.my.localport: 8080 - ... -``` - -```bash -docker stack deploy --compose-file docker-compose.yml mystack -``` - -### Single Definition - -```bash -docker run \ - -l easyhaproxy.webapi.port=80\ - -l easyhaproxy.webapi.host=byjg.com.br \ - .... -``` - -### Multiples Definitions on the same container - -```bash -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 -``` - -### Multiples hosts on the same container - -```bash -docker run \ - -l easyhaproxy.express.port=80 \ - -l easyhaproxy.express.localport=3000 \ - -l easyhaproxy.express.host=express.byjg.com.br,admin.byjg.com.br \ - .... \ - some/myimage -``` - -If you are using docker-compose you can use this way: - -```yaml -version: "3" - -services: - mycontainer: - image: some/myimage - labels: - easyhaproxy.express.port: 80 - easyhaproxy.express.localport: 3000 - easyhaproxy.express.host: >- - express.byjg.com.br, - admin.byjg.com.br -``` - -### 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: - -```bash -docker run \ - -l easyhaproxy.example.mode=tcp \ - -l easyhaproxy.example.health-check=ssl \ - -l easyhaproxy.example.port=443 - .... \ - some/tcp-service -``` - -### Redirect Example - -```bash -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` - -```yaml -stats: - username: admin - password: password - port: 1936 # Optional (default 1936) - -customerrors: true # Optional (default false) - -ssl_mode: default - -letsencrypt: { - "email": "acme@example.org" -} - -easymapping: - - port: 80 - hosts: - host1.com.br: - containers: - - container:5000 - letsencrypt: true - redirect_ssl: true - host2.com.br: - containers: - - other:3000 - redirect: - www.host1.com.br: http://host1.com.br - - - port: 443 - hosts: - host1.com.br: - containers: - - container:80 - redirect_ssl: false - ssl: true - - - port: 8080 - hosts: - host3.com.br: - containers: - - domain:8181 -``` - -Running: - -```bash -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: - -```bash -docker run \ - -e EASYHAPROXY_LETSENCRYPT_EMAIL=john@doe.com - .... \ - byjg/easy-haproxy -``` - -Run your container: - -```bash -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 because 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 of Letsencrypt issue limits - https://letsencrypt.org/docs/duplicate-certificate-limit/ and https://letsencrypt.org/docs/rate-limits/ - -## Exposing Ports - -You must expose some ports on the EasyHAProxy container and in the firewall. However, you don't need to expose the other container ports because EasyHAProxy will handle that. - -- The ports `80` and `443`. -- If you enable the HAProxy statistics, you must also expose the port defined in `HAPROXY_STATS_PORT` environment variable (default 1936). Be aware that statististics are enabled by default with no password. -- Every port defined in `easyhaproxy.[definitions].port` also should be exposed. - -e.g. - -```bash -docker run \ - /* other parameters */ - -p 80:80 \ - -p 443:443 \ - -p 1936:1936 \ - -d byjg/easy-haproxy -``` - -## Mapping custom .cfg files - -You can concatenate valid HAProxy `.cfg` files to the dynamically generated `haproxy.cfg` by mapping the folder `/etc/haproxy/conf.d`. - -```bash -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, map the folder `/certs/letsencrypt` to your volume. -- If you want to provide your certificates as a file instead of a Base64 parameter, map the folder `/certs/haproxy` to your volume, and instead of use `easyhaproxy.[definition].sslcert`, use `easyhaproxy.[definition].ssl: true` - -```bash -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. - -```bash -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----- -``` - -2. Convert it to BASE64 in a single line: - -```bash -cat single.pem | base64 -w0 -``` - -3. 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 - -```bash -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 separately. -- Each replica will request a Letsencrypt certificate and can fail because the letsencrypt challenge can be directed to the other replica. ---- [Open source ByJG](http://opensource.byjg.com) diff --git a/deploy/kubernetes/easyhaproxy.yml b/deploy/kubernetes/easyhaproxy.yml index b4ee2ad..cec1d40 100644 --- a/deploy/kubernetes/easyhaproxy.yml +++ b/deploy/kubernetes/easyhaproxy.yml @@ -100,6 +100,15 @@ spec: labels: app: easyhaproxy-ingress spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: easyhaproxy/node + operator: In + values: + - master serviceAccountName: easyhaproxy-ingress containers: - image: byjg/easy-haproxy:test diff --git a/docker.png b/docker.png new file mode 100644 index 0000000..536bacf Binary files /dev/null and b/docker.png differ diff --git a/docs/container-labels.md b/docs/container-labels.md new file mode 100644 index 0000000..97988d5 --- /dev/null +++ b/docs/container-labels.md @@ -0,0 +1,97 @@ +# Container Labels + +## Container (Docker or Swarm) labels + +| Tag | 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].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].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].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `letsencrypt` 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].health-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl | +| easyhaproxy.[definition].letsencrypt | (Optional) Generate certificate with letsencrypt. Do not use with `sslcert` parameter. | 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 `letsencrypt` parameters | false | true OR false | + +The `definition` is a string that will group all configurations togethers. Different `definition` will create different configurations. + +The container can have more than one defintion. + +## Configuations + +### Single Definition + +```bash +docker run \ + -l easyhaproxy.webapi.port=80\ + -l easyhaproxy.webapi.host=byjg.com.br \ + .... +``` + +### Multiples Definitions on the same container + +```bash +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 +``` + +### Multiples hosts on the same container + +```bash +docker run \ + -l easyhaproxy.express.port=80 \ + -l easyhaproxy.express.localport=3000 \ + -l easyhaproxy.express.host=express.byjg.com.br,admin.byjg.com.br \ + .... \ + some/myimage +``` + +If you are using docker-compose you can use this way: + +```yaml +version: "3" + +services: + mycontainer: + image: some/myimage + labels: + easyhaproxy.express.port: 80 + easyhaproxy.express.localport: 3000 + easyhaproxy.express.host: >- + express.byjg.com.br, + admin.byjg.com.br +``` + +### TCP Mode + +Set `easyhaproxy.[definition].mode=tcp` if your application uses TCP protocol instead of HTTP. + +```bash +docker run \ + -l easyhaproxy.example.mode=tcp \ + -l easyhaproxy.example.port=3306 + -l easyhaproxy.example.localport=3306 + .... \ + some/tcp-service +``` + +### Redirect Domains + +```bash +docker run \ + -l easyhaproxy.[definition].redirect='{"www.byjg.com.br":"http://byjg.com.br","byjg.com":"http://byjg.com.br"}' +``` + +---- +[Open source ByJG](http://opensource.byjg.com) diff --git a/docs/docker-environment.md b/docs/docker-environment.md new file mode 100644 index 0000000..1ed6288 --- /dev/null +++ b/docs/docker-environment.md @@ -0,0 +1,21 @@ +# Docker environment variables + +| Environment Variable | Description | Default | +|-------------------------------|-------------------------------------------------------------------------------------------------|------------------| +| EASYHAPROXY_DISCOVER | How the services will be discovered to create `haproxy.cfg`: `static`, `docker`, `swarm` or `kubernetes` | **required** | +| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. | `easyhaproxy` | +| EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request the certificate to Letsencrypt | *empty* | +| EASYHAPROXY_SSL_MODE | (Optional) `strict` supports only the most recent TLS version; `default` good SSL integration with recent browsers; `loose` supports all old SSL protocols for old browsers (not recommended). | `default`| +| EASYHAPROXY_REFRESH_CONF | (Optional) Check configuration every N seconds. | 10 | +| EASYHAPROXY_LOG_LEVEL | (Optional) The log level for EasyHAproxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | +| CERTBOT_LOG_LEVEL | (Optional) The log level for Certbot messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | +| HAPROXY_LOG_LEVEL | (Optional) The log level for HAProxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | +| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. | `admin` | +| HAPROXY_PASSWORD | (Optional) The HAProxy password to the statistics. If not set, statistics will be available with no password | *empty* | +| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. If set to `false`, disable statistics | `1936` | +| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. | `false` | + + + +---- +[Open source ByJG](http://opensource.byjg.com) diff --git a/docs/docker.md b/docs/docker.md new file mode 100644 index 0000000..a195129 --- /dev/null +++ b/docs/docker.md @@ -0,0 +1,65 @@ +# Docker + +## Setup Docker EasyHAProxy + +This method will use a docker standalone installation to discover the containers and configure the HAProxy. + +The only requirement is that containers and EasyHAProxy must be in the same docker network in order to HAProxy be able direct the traffic to the containers. + +e.g.: + +```bash +docker create network easyhaproxy +``` + +And then run the EasyHAProxy + +```bash +docker run -d \ + --name easy-haproxy-container \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -e EASYHAPROXY_DISCOVER="docker" \ + # + Environment Variables \ + -p 80:80 \ + -p 443:443 \ + -p 1936:1936 \ + --network easyhaproxy + byjg/easy-haproxy +``` + +The mapping to `/var/run/docker.sock` is necessary to discover the docker containers and get the labels; + +## Running containers + +To make your containers "discoverable" by EasyHAProxy that is minimum configuration you need: + +```bash +docker run -d \ + -e easyhaproxy.http.host=example.org \ + -e easyhaproxy.http.port=80 \ + -e easyhaproxy.http.localport=8080 \ + --network easyhaproxy + my/image:tag +``` + +Once the container is running EasyHAProxy will detect automatically and start to redirect all traffic from `example.org:80` to your container. + +You don't need to expose any port in your container. + +There a list of other parameters you can to configure your container. Please follow the [docker label configuration](container-labels.md) + +## Setup the EasyHAProxy container + +You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables please follow the [docker container environment](docker-environment.md) + + +## Setup certificates with Letsencrypt + +Follow [this link](letsencrypt.md) + +## Setup your own certificates + +Follow [this link](ssl.md) + +---- +[Open source ByJG](http://opensource.byjg.com) diff --git a/docs/kubernetes.md b/docs/kubernetes.md new file mode 100644 index 0000000..143ca49 --- /dev/null +++ b/docs/kubernetes.md @@ -0,0 +1,141 @@ +# Kubernetes + +## Setup Kubernetes EasyHAProxy + +EasyHAProxy is a Daemonset and it will query all ingress definitions with the annotation `kubernetes.io/ingress.class: easyhaproxy-ingress`. + +Once find the annotation, it will immediatelly setup HAProxy and start to serve it. + +To install the daemonset in your cluster follow these steps: + +1. Identify the node where your EasyHAProxy container will run. + +EasyHAProxy is a daemonset but it will be limited to a single node. To understand that see [limitations](limitations.md) page. + +```bash +$ kubectl get nodes + +NAME STATUS ROLES AGE VERSION +node-01 Ready 561d v1.21.13-3 +node-02 Ready 561d v1.21.13-3 +``` + +2. Add the EasyHAProxy label to the node + +```bash +kubectl label nodes node-01 "easyhaproxy/node=master" +``` + +3. Install EasyHAProxy + +```bash +kubectl apply -f \ + https://raw.githubusercontent.com/haproxytech/kubernetes-ingress/master/deploy/haproxy-ingress-daemonset.yaml +``` + +You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables please follow the [docker container environment](docker-environment.md) + +## Running containers + +The only requirement is that you have an ingress properly setup and with the annotation `kubernetes.io/ingress.class: easyhaproxy-ingress`. + +e.g. + +```yaml +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: easyhaproxy-ingress + name: example-ingress + namespace: example +spec: + rules: + - host: example.org + http: + paths: + - backend: + service: + name: example-service + port: + number: 8080 + pathType: ImplementationSpecific +``` + +Once the container is running EasyHAProxy will detect automatically and start to redirect all traffic from `example.org:80` to your container. + +You don't need to expose any port in your container. + +Caveats: + +- At this point, the implementation don't support all ingress properties nor wildcard domains. +- The ingress will publish externally only the ports 80 and 443, plus 1936 if stats is enable. +- EasyHAProxy will read all `spec.rules[].host` spec, however it will parse only the first path `spec.rules[].http.paths[0].port.number` for each rule, and ignore the other paths. + +## Kubernetes annotations + +| annotation | Description | Default | Example | +|-----------------------------|-----------------------------------------------------------------------------------------|--------------|--------------| +| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress +| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to https. | false | true or false +| easyhaproxy.letsencrypt | (optional) Boolean. It will request letsencript certificates for the ingresses domains. | false | true or false +| easyhaproxy.redirect | (optional) Json. Specific a domain and its destination. | *empty* | {"domain":"redirect_url"} +| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp + +**Important**: The annotations are per ingress and applied to all hosts in that ingress configuration. + +## Letsencrypt + +It is necessary add the annotation `easyhaproxy.letsencrypt` to the ingress configuration: + +```yaml +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: easyhaproxy-ingress + easyhaproxy.letsencrypt: 'true' + name: example-ingress + namespace: example +spec: + .... +``` + +Make sure your cluster is accessible both through ports 80 and 443. + +## Custom SSL Certificates + +You need to create a secret with your certificate and key, and associate them in your ingress. + +```yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: host2-tls + namespace: default +data: + tls.crt: base64 of your certificate + tls.key: base64 of your certificate private key +type: kubernetes.io/tls + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: easyhaproxy-ingress + name: tls-example + namespace: default +spec: + tls: + - hosts: + - host2.local + secretName: host2-tls + rules: + ... +``` + + + + +---- +[Open source ByJG](http://opensource.byjg.com) diff --git a/docs/letsencrypt.md b/docs/letsencrypt.md new file mode 100644 index 0000000..6dfcc38 --- /dev/null +++ b/docs/letsencrypt.md @@ -0,0 +1,52 @@ +# Letsencrypt + +EasyHAProxy can issue a letsencrypt certificate. The command is as below: + +Run the EasyHAProxy: + +```bash +docker run \ + -e EASYHAPROXY_LETSENCRYPT_EMAIL=john@doe.com + .... \ + byjg/easy-haproxy +``` + +Run your container: + +```bash +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 +``` + +Requirements: + +- Your container **must** listen to the port 80. Besides no error, the certificate won't be issued if in a different port. +- You cannot set the port 443 for the container with the Letsencrypt because EasyHAProxy will handle this automatically once the certificate is issued. +- You have to setup the `EASYHAPROXY_LETSENCRYPT_EMAIL` environment variable on EasyHAProxy. If you don't setup, EasyHAProxy **will not request** a certificate. + +Be aware of Letsencrypt issue limits - https://letsencrypt.org/docs/duplicate-certificate-limit/ and https://letsencrypt.org/docs/rate-limits/ + +## Persist your Letsencrypt certificates + +It is a good idea to store the letsencrypt certificate in a persistent storage, even you knowing you can issue again in case your lost the certificate. + +However, there is a limit in how many certificates can be issue for the same domain in a period of time. + +To avoid this, map the folder `/certs/letsencrypt` to a docker volume. + +```bash +docker volume create certs_letsencrypt + +docker run \ + /* other parameters */ + -v certs_letsencrypt:/certs/letsencrypt \ + -d byjg/easy-haproxy +``` + +---- +[Open source ByJG](http://opensource.byjg.com) diff --git a/docs/limitations.md b/docs/limitations.md new file mode 100644 index 0000000..3f64701 --- /dev/null +++ b/docs/limitations.md @@ -0,0 +1,16 @@ +# Limitations + +EasyHAProxy currently expects to work in a single replica. + +If more than one replica is running, EasyHAProxy will continue to work, however each replica will discover +the services independently. + +It means replicas can be out-of-sync for a few seconds because each replica will discover the pods +separately. + +For Letsencrypt this is worse because each replica will a Letsencrypt certificate and can fail because the +letsencrypt challenge can be directed to the other replica. Also, you can hit the certificate issue limit. +So if you intend to run multiple replicas **do not** activate letsencrypt. + +---- +[Open source ByJG](http://opensource.byjg.com) \ No newline at end of file diff --git a/docs/logo.xcf b/docs/logo.xcf new file mode 100644 index 0000000..ab58b08 Binary files /dev/null and b/docs/logo.xcf differ diff --git a/docs/other.md b/docs/other.md new file mode 100644 index 0000000..5f93002 --- /dev/null +++ b/docs/other.md @@ -0,0 +1,39 @@ +# Other configurations + +## Exposing Ports + +You must expose some ports on the EasyHAProxy container and in the firewall. However, you don't need to expose the other container ports because EasyHAProxy will handle that. + +- The ports `80` and `443`. +- If you enable the HAProxy statistics, you must also expose the port defined in `HAPROXY_STATS_PORT` environment variable (default 1936). Be aware that statististics are enabled by default with no password. +- Every port defined in `easyhaproxy.[definitions].port` also should be exposed. + +e.g. + +```bash +docker run \ + /* other parameters */ + -p 80:80 \ + -p 443:443 \ + -p 1936:1936 \ + -d byjg/easy-haproxy +``` + +## Mapping custom .cfg files + +You can concatenate valid HAProxy `.cfg` files to the dynamically generated `haproxy.cfg` by mapping the folder `/etc/haproxy/conf.d`. + +```bash +docker run \ + /* other parameters */ + -v /your/local/conf.d:/etc/haproxy/conf.d \ + -d byjg/easy-haproxy +``` + +## 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`) + +---- +[Open source ByJG](http://opensource.byjg.com) \ No newline at end of file diff --git a/docs/ssl.md b/docs/ssl.md new file mode 100644 index 0000000..9baffd3 --- /dev/null +++ b/docs/ssl.md @@ -0,0 +1,75 @@ +# Setup your own certificates + +You can use your certificates with EasyHAProxy. + +There is two ways to do that. + +## Setup certificate as a label definition in docker container + +1. First, Create a single PEM from the certificate and the key. + +```bash +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----- +``` + +2. Convert the `single.pem` to BASE64 in a single line: + +```bash +cat single.pem | base64 -w0 +``` + +3. Use this string to define the label `easyhaproxy.[definition].sslcert` + +## Map the certificate as docker volume + +EasyHAProxy stores the certificates inside the folder `/certs/haproxy`. + +1. Run EasyHAProxy with the volume for the certificates: + +```bash +docker volume create certs_haproxy + +docker run \ + /* other parameters */ + -v certs_haproxy:/certs/haproxy \ + -d byjg/easy-haproxy +``` + +2. Create a single PEM from the certificate and the key. + +```bash +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----- +``` + +3. Copy this certificate to EasyHAProxy volume + +```bash +docker cp single.pem easyhaproxy:/certs/haproxy +``` + +---- +[Open source ByJG](http://opensource.byjg.com) diff --git a/docs/static.md b/docs/static.md new file mode 100644 index 0000000..8506234 --- /dev/null +++ b/docs/static.md @@ -0,0 +1,109 @@ +# Docker + +## Setup Docker EasyHAProxy + +This method will use a static configuration, simpler and easier than HAProxy to create the `haproxy.cfg` + +You can use this configuration to setup external servers not related to docker or kubernetes. + +Another advantage is that EasyHAProxy will monitor for changes in this file and automatically reconfigure HAProxy and changes are detected. + +First, Create a YAML: + +```yaml +stats: + username: admin + password: password + port: 1936 # Optional (default 1936) + +customerrors: true # Optional (default false) + +ssl_mode: default + +letsencrypt: { + "email": "acme@example.org" +} + +easymapping: + - port: 80 + hosts: + host1.com.br: + containers: + - container:5000 + letsencrypt: true + redirect_ssl: true + host2.com.br: + containers: + - other:3000 + redirect: + www.host1.com.br: http://host1.com.br + + - port: 443 + hosts: + host1.com.br: + containers: + - container:80 + redirect_ssl: false + ssl: true + + - port: 8080 + hosts: + host3.com.br: + containers: + - domain:8181 +``` + +Then map this file to `/etc/haproxy/easyconfig.yml` in your EasyHAProxy container as: + +```bash +docker run -d \ + --name easy-haproxy-container \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v /my/config.yml:/etc/haproxy/easyconfig.yml + -e EASYHAPROXY_DISCOVER="static" \ + # + Environment Variables \ + -p 80:80 \ + -p 443:443 \ + -p 1936:1936 \ + --network easyhaproxy + byjg/easy-haproxy +``` + +You can find other informations on [docker label configuration](container-labels.md) and [docker container environment](docker-environment.md) + +## Yaml Definition + +```yaml +stats: + username: admin # Optional (default "admin") + password: password # If stats or stats.password is omitted, stats will be public with no password + port: 1936 # Optional (default 1936) + +customerrors: true # Optional (default false) + +ssl_mode: default # Optional + +letsencrypt: { # Optional. If you enable `letsencrypt` will need to setu0p this, + # otherwise the certificate will be issued + "email": "acme@example.org" +} + +easymapping: + - port: 80 # Listen port + mode: http # Optional. Default `http`. Can be http or tcp + hosts: + host1.com.br: # Hostname + containers: + - container:5000 # Endpoints of the hostname above (ip, dns, container, etc) + letsencrypt: true # Optional. it will request a letsencrypt certiticate + redirect_ssl: true # Optional. It will redirect this site to it SSL. + ssl: true # Optional. Inform this port will listen to SSL, instead of HTTP + clone_to_ssl: true # Optional. Default False. You clone these hosts to it equivalent SSL. + redirect: + www.host1.com.br: http://host1.com.br +``` + +*Note*: The only way to pass SSL certificates is to map the certificates to EasyHAProxy as a docker volume. Refer to the [SSL documentation](ssl.md) to learn how to do it. + +---- +[Open source ByJG](http://opensource.byjg.com) diff --git a/docs/swarm.md b/docs/swarm.md new file mode 100644 index 0000000..9d26245 --- /dev/null +++ b/docs/swarm.md @@ -0,0 +1,96 @@ +# Swarm + +## Setup Docker EasyHAProxy + +This method will use a docker swarm installation to discover the containers and configure the HAProxy. +The advantage of this method is that you can discover container in other nodes from cluster. + +The only requirement is that containers and EasyHAProxy must be in the same docker swarm network in order to HAProxy be able direct the traffic to the containers. + +e.g.: + +```bash +docker create network easyhaproxy +``` + +And then deploy the EasyHAProxy stack: + +```yaml +version: "3" + +services: + haproxy: + image: byjg/easy-haproxy + volumes: + - /var/run/docker.sock:/var/run/docker.sock + deploy: + replicas: 1 + environment: + EASYHAPROXY_DISCOVER: swarm + EASYHAPROXY_SSL_MODE: "loose" + HAPROXY_CUSTOMERRORS: "true" + HAPROXY_USERNAME: admin + HAPROXY_PASSWORD: password + HAPROXY_STATS_PORT: 1936 + ports: + - "80:80/tcp" + - "443:443/tcp" + - "1936:1936/tcp" + networks: + - easyhaproxy + +networks: + easyhaproxy: + external: true +``` + +and then: + +```bash +docker stack deploy --compose-file docker-compose.yml easyhaproxy +``` + +The mapping to `/var/run/docker.sock` is necessary to discover the docker containers and get the labels; + +**Do not** add more than one replica for EasyHAProxy. To understand that see [limitations](limitations.md) page. + +## Running containers + +To make your containers "discoverable" by EasyHAProxy that is minimum configuration you need: + +```yaml +version: "3" + +services: + container: + image: my/image:tag + deploy: + replicas: 1 + labels: + easyhaproxy.http.host: host1.local + easyhaproxy.http.port: 80 + easyhaproxy.http.localport: 8080 + networks: + - easyhaproxy + +networks: + easyhaproxy: + external: true +``` + +Once the container is running EasyHAProxy will detect automatically and start to redirect all traffic from `example.org:80` to your container. + +You don't need to expose any port in your container. + +There a list of other parameters you can to configure your container. Please follow the [docker label configuration](container-labels.md) + +## Setup the EasyHAProxy container + +You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables please follow the [docker container environment](docker-environment.md) + +## More information + +You can refer the [Docker Documentation](docker.md) to get other detailed instructions. + +---- +[Open source ByJG](http://opensource.byjg.com) diff --git a/kubernetes.png b/kubernetes.png new file mode 100644 index 0000000..c78d327 Binary files /dev/null and b/kubernetes.png differ diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..49f9bfb Binary files /dev/null and b/logo.png differ diff --git a/static.png b/static.png new file mode 100644 index 0000000..9666f88 Binary files /dev/null and b/static.png differ diff --git a/swarm.png b/swarm.png new file mode 100644 index 0000000..9978cc3 Binary files /dev/null and b/swarm.png differ