Skip to content

Commit

Permalink
Merge pull request #6 from byjg/2.2
Browse files Browse the repository at this point in the history
Release 2.2
  • Loading branch information
byjg authored Aug 10, 2021
2 parents cf85ec1 + 7e242d1 commit 7057965
Show file tree
Hide file tree
Showing 36 changed files with 905 additions and 149 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
venv
.docker_data
__pycache__
.pytest_cache
26 changes: 26 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
language: python

services:
- docker


jobs:
include:
- stage: test
if: (type IN (pull_request))
install:
- pip install -r requirements.txt
script:
- pytest -s tests/

- stage: build docker
if: (branch = master) AND (NOT (type IN (pull_request)))
install:
- docker pull byjg/k8s-ci
script:
- docker run --privileged -v /tmp/z:/var/lib/containers -it --rm -v $PWD:/work -w /work -e DOCKER_USERNAME=$DOCKER_USERNAME -e DOCKER_PASSWORD=$DOCKER_PASSWORD -e DOCKER_REGISTRY=$DOCKER_REGISTRY byjg/k8s-ci /work/build-multiarch.sh

- stage: documentation
if: (branch = master) AND (NOT (type IN (pull_request)))
install: skip
script: "curl https://opensource.byjg.com/add-doc.sh | bash /dev/stdin devops docker-easy-haproxy"
11 changes: 7 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
FROM haproxy:2.1-alpine
FROM alpine:3.14

WORKDIR /scripts

RUN apk add --no-cache bash python3 py-yaml supervisor docker
RUN apk add --no-cache haproxy bash python3 py3-pip py-yaml supervisor docker \
&& ln -s /usr/bin/python3 /usr/bin/python

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 tests/ /scripts/tests/

COPY assets /

RUN pytest -s tests/

CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf" ]
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.PHONY: build
build:
docker build -t byjg/easy-haproxy -t byjg/easy-haproxy:local .

.PHONY: test
test:
pytest tests/
93 changes: 54 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# Easy HAProxy
# Easy HAProxy

This Docker image will create dynamically the `haproxy.cfg` based on the labels defined in docker containers or from
a simple Yaml instead docker
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
## Features

- Enable or disable Stats on port 1936 with custom password
- Discover and setup haproxy from Docker Tag
- Discover and setup haproxy from Docker Tag
- Discover and setup haproxy redirect from Docker Tag
- Setup HAProxy CFG from a Yaml file.
- Setup HAProxy CFG from a Yaml file.


# Basic Usage
## Basic Usage

The Easy HAProxy will create the `haproxy.cfg` automatically based on the containers or from a YAML provided.
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 -d \
docker run -d \
--name easy-haproxy-container \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DISCOVER="swarm|docker|static" \
Expand All @@ -29,9 +29,8 @@ docker run -d \

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.
The environment variables will setup the HAProxy.

{:.table}
| Environment Variable | Description |
|----------------------|-------------------------------------------------------------------------------|
| DISCOVER | How `haproxy.cfg` will be created: `static`, `docker` or `swarm` |
Expand All @@ -47,11 +46,11 @@ The environment variable `DISCOVER` will define where is located your containers
- swarm
- static

# DISCOVER: docker
## DISCOVER: docker

This method will use a regular docker installation to discover the containers and configure the HAProxy.
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 only requirement is that containers and easy-haproxy must be in the same docker network.

The discover will occur every minute.

Expand All @@ -65,27 +64,27 @@ docker run --network easyhaproxy byjg/easyhaproxy
docker run --network easyhaproxy myimage
```

# DISCOVER: swarm
## DISCOVER: swarm

This method requires a functional Docker Swarm Cluster. The system will search for the labels in all containers on all
swarm nodes.
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.
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

{:.table}
| 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.[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. |
### Tags to be attached in the Docker Container (Swarm or Docker)

| Tag | Description | Example |
|---------------------------------------------|---------------------------------------------------------------------------------------------------------|--------------|
| com.byjg.easyhaproxy.definitions | A Comma delimited list with the definitions. Each name requires the definition of the parameters below. | http,https |
| com.byjg.easyhaproxy.mode.[definition] | (Optional) Is this http or tcp mode in HAProxy. (Defaults to http) | http |
| com.byjg.easyhaproxy.port.[definition] | (Optional) What is the port that the HAProxy will listen to. (Defaults to 80) | 80 |
| com.byjg.easyhaproxy.localport.[definition] | (Optional) What is the port that the container is listening. (Defaults to 80) | 8080 |
| com.byjg.easyhaproxy.host.[definition] | What is the host that the HAProxy will listen to. | somehost.com |
| com.byjg.easyhaproxy.redirect.[definition] | (Optional) Host redirects from connections in the port defined above. | foo.com--https://bla.com,bar.com--https://bar.org |
| com.byjg.easyhaproxy.sslcert.[definition] | (Optional) Cert PEM Base64 encoded. | |
| com.byjg.easyhaproxy.health-check.[definition] | (Optional) `ssl`, enable health check via SSL in `mode tcp` (Defaults to "empty") | |

Note: if you are deploying a stack set labels at the `deploy` level:

Expand Down Expand Up @@ -126,14 +125,30 @@ docker run \
some/myimage
```

### TLS passthrough

Used to pass on SSL-termination to a backend:

```bash
docker run \
-l com.byjg.easyhaproxy.defintions=tcp-service \
-l com.byjg.easyhaproxy.mode.tcp-service=tcp \
-l com.byjg.easyhaproxy.health-check.tcp-service=ssl \
-l com.byjg.easyhaproxy.port.tcp-service=443
.... \
some/tcp-service
```

- enable health-check via SSL on the backend with the optional `health-check` label

### Redirect Example:

```bash
docker run \
-l com.byjg.easyhaproxy.redirect.<defintion>=www.byjg.com.br--http://byjg.com.br,byjg.com--http://byjg.com.br
```

# DISCOVER: static
## DISCOVER: static

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

Expand All @@ -149,14 +164,14 @@ customerrors: true # Optional (default false)

easymapping:
- port: 80
hosts:
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
ssl_cert: /path/to/ssl/certificate
hosts:
host1.com.br: container:80

Expand All @@ -173,10 +188,10 @@ docker run -v /my/config.yml:/etc/haproxy/easyconfig.yml .... byjg/easyhaproxy

# 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.
Map a folder containing valid HAProxy `.cfg` files to `/etc/haproxy/conf.d`. It will be concatenated to your HAProxy CFG.

```bash
docker run \
docker run \
/* other parameters */
-v /your/local/conf.d:/etc/haproxy/conf.d \
-d byjg/easy-haproxy
Expand All @@ -185,9 +200,9 @@ docker run \

# Handling SSL

You can attach a valid SSL certificate to the request.
You can attach a valid SSL certificate to the request.

1. First Create a single PEM file including CA.
1. First Create a single PEM file including CA.

```bash
cat example.com.crt example.com.key > single.pem
Expand Down Expand Up @@ -215,8 +230,8 @@ cat single.pem | base64 -w0

# 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)
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

Expand Down
7 changes: 6 additions & 1 deletion assets/scripts/exit-event-listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@

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

print(headers)
print(payload)
os.kill(os.getppid(), signal.SIGTERM)


if __name__ == "__main__":
main()
main()
12 changes: 8 additions & 4 deletions assets/scripts/haproxy-reload.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ else
if [[ "$DISCOVER" == "docker" ]]; then
CONTAINERS=$(docker ps -q)
LABEL_PATH=".Config.Labels"

for container in ${CONTAINERS}; do
docker inspect --format "{{ json $LABEL_PATH }}" ${container} | xargs -I % echo ${container}=% >> ${CONTROL_FILE}
done
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
for container in ${CONTAINERS}; do
docker service inspect --format "{{ json $LABEL_PATH }}" ${container} | xargs -I % echo ${container}=% >> ${CONTROL_FILE}
done
fi

if cmp -s ${CONTROL_FILE} ${CONTROL_FILE}.old ; then
RELOAD="false"
Expand Down
2 changes: 1 addition & 1 deletion assets/scripts/haproxy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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
/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock

while true; do
sleep 60
Expand Down
File renamed without changes.
24 changes: 24 additions & 0 deletions assets/scripts/swarm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import os
from easymapping import HaproxyConfigGenerator

# path = os.path.dirname(os.path.realpath(__file__))
with open("/tmp/.docker_data", 'r') as content_file:
lineList = content_file.readlines()

result = {
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False
}

if os.getenv("HAPROXY_PASSWORD"):
result["stats"] = {
"username": os.getenv("HAPROXY_USERNAME") if os.getenv("HAPROXY_USERNAME") else "admin",
"password": os.getenv("HAPROXY_PASSWORD"),
"port": os.getenv("HAPROXY_STATS_PORT") if os.getenv("HAPROXY_STATS_PORT") else "1936",
}

cfg = HaproxyConfigGenerator(result)
print(cfg.generate(lineList))

# print(jsonStr)


33 changes: 33 additions & 0 deletions build-multiarch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash

set -e

# Start k8s-ci before run this command
# docker run --privileged -v /tmp/z:/var/lib/containers -it --rm -v $PWD:/work -w /work byjg/k8s-ci

if [ -z "$DOCKER_USERNAME" ] || [ -z "$DOCKER_PASSWORD" ] || [ -z "$DOCKER_REGISTRY" ]
then
echo You need to setup \$DOCKER_USERNAME, \$DOCKER_PASSWORD and \$DOCKER_REGISTRY before run this command.
exit 1
fi

buildah login --username $DOCKER_USERNAME --password $DOCKER_PASSWORD $DOCKER_REGISTRY

podman run --rm --events-backend=file --cgroup-manager=cgroupfs --privileged docker://multiarch/qemu-user-static --reset -p yes

VERSIONS="latest $TRAVIS_TAG"
for VERSION in $VERSIONS
do
DOCKERFILE=Dockerfile

buildah manifest create byjg/easy-haproxy:$VERSION

buildah bud --arch arm64 --os linux --iidfile /tmp/iid-arm64 -f $DOCKERFILE -t byjg/easy-haproxy:$VERSION-arm64 .
buildah bud --arch amd64 --os linux --iidfile /tmp/iid-amd64 -f $DOCKERFILE -t byjg/easy-haproxy:$VERSION-amd64 .

buildah manifest add byjg/easy-haproxy:$VERSION --arch arm64 --os linux --variant v8 $(cat /tmp/iid-arm64)
buildah manifest add byjg/easy-haproxy:$VERSION --arch amd64 --os linux --os=linux $(cat /tmp/iid-amd64)

buildah manifest push --all --format v2s2 byjg/easy-haproxy:$VERSION docker://byjg/easy-haproxy:$VERSION
done

Loading

0 comments on commit 7057965

Please sign in to comment.