Trivial API-gateway for docker, via HAProxy
This is a simple project which is designed to allow you to setup an API/gateway-host based upon a single machine running docker.
The docker-api-gateway deamon will react to containers being launched/terminated by docker, and update a global haproxy.cfg
configuration file - which haproxy will use.
The haproxy.cfg file, generated from a template, will bind to *:80
and route traffic to the appropriate backend based on the path requested - it is assumed the prefix will match the containers name.
foo/bar
on port 8000.foo/bar
on port 80.hello/world
on port 80hello/world
on port 8000.Our assumpions are:
Assuming you have no docker guests running you should launch the api-gateway like so:
go run ./docker-api-gateway.go
This will start the docker gateway running, and it will listen to events produced by docker (containers being launched or terminated).
At this point nothing will be running, but you can start a simple example by fetching the image crccheck/hello-world
and launching it in the background:
root@frodo:~# docker run -d crccheck/hello-world
df6aabd4b13363c979bbc64618a7e087e3c18c318f2eea626e8f79c84002bf0d
Once the image has downloaded it will be launched, and the docker-api-gateway
process will notice that a new container has been created. Because a new container has been created the file /etc/haproxy/haproxy.cfg
will be updated to include details of all the local instances.
The /etc/haproxy/haproxy.cfg
file will now look something like this:
..
acl crccheck_hello_world path_beg /crccheck/hello-world
use_backend crccheck_hello_world-backend if crccheck_hello_world
..
backend crccheck_hello_world-backend
reqrep ^(GET|HEAD|POST)\ /crccheck/hello-world(.*) \1\ /\2
server name 172.17.0.2:80 check inter 2000
server name 172.17.0.2:8000 check inter 2000
The first section defined a match based upon the path component of the URL, and the second section sends that to the IP of the docker guest - notice we're explicitly not setting up any port-forwarding.
You can now view the container's output via:
$ curl http://localhost/crccheck/hello-world
$ curl http://localhost/crccheck/hello-world/
If you stop the container you'll find that the backend, and ACL, will be removed from the haproxy.cfg
file, and that all accesses will return a 403
response, via the default-handler.
As a second-test you can spin up a different container, hosting PHP, via:
root@frodo:~# docker run -d ipunktbs/phpinfo
Now you should find PHPInfo() in all its glory:
$ curl http://localhost/ipunktbs/phpinfo/
You'll notice in both cases that the request made to the docker-container will have the image-name prefix stripped off it.
The following flags are supported:
-template-file
haproxy.tmpl
.-output-file
/etc/haproxy/haproxy.cfg
-reload-cmd
/bin/systemctl reload haproxy.service
.The most obvious way to change/improve this project is to switch from prefix-based routing to vhost-based.
For example rather than:
Allow:
Although bar
isn't a complete identifier it is unlikely you'd have
multiple containers running with the same suffix (I assume!) Doing this
would only involve rewriting the haproxy.cfg template - perhaps adding
a command-line flag to allow the user to choose which template to use
would make that easier.
This is a quick hack. I've tested it with several simple HTTP-based
containers which expose themselves on :8000
and it works well, but I don't
expect it to be universally useful as-is.
Feedback is welcome, whether good or bad :)