Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Commit 4b34260

Browse files
author
Feng Honglin
committed
Merge pull request #28 from docker/staging
v1.3
2 parents 3b12995 + b3fb2a1 commit 4b34260

File tree

9 files changed

+184
-71
lines changed

9 files changed

+184
-71
lines changed

Dockerfile

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,36 @@
1-
FROM ubuntu:trusty
1+
FROM alpine:edge
22
MAINTAINER Feng Honglin <[email protected]>
33

4-
# Install pip and haproxy
5-
RUN echo 'deb http://ppa.launchpad.net/vbernat/haproxy-1.5/ubuntu trusty main' >> /etc/apt/sources.list && \
6-
echo 'deb-src http://ppa.launchpad.net/vbernat/haproxy-1.5/ubuntu trusty main' >> /etc/apt/sources.list && \
7-
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 505D97A41C61B9CD && \
8-
apt-get update && \
9-
apt-get install -y --no-install-recommends haproxy python-pip && \
10-
apt-get clean && \
11-
rm -rf /var/lib/apt/lists/*
4+
# Install tini, haproxy, pip and the dockercloud-haproxy python package:
5+
RUN apk --no-cache add \
6+
tini \
7+
haproxy \
8+
py-pip \
9+
&& apk --no-cache add --virtual deps git \
10+
&& pip install --upgrade \
11+
pip \
12+
&& apk del deps \
13+
# Clean up obsolete files:
14+
&& rm -rf \
15+
# Clean up any temporary files:
16+
/tmp/* \
17+
# Clean up the pip cache:
18+
/root/.cache \
19+
# Remove any compiled python files (compile on demand):
20+
`find / -regex '.*\.py[co]'`
1221

1322
COPY reload.sh /reload.sh
1423
COPY . haproxy-src/
1524
RUN cd /haproxy-src/ && \
16-
pip install .
25+
pip install . \
26+
# Clean up obsolete files:
27+
&& rm -rf \
28+
# Clean up any temporary files:
29+
/tmp/* \
30+
# Clean up the pip cache:
31+
/root/.cache \
32+
# Remove any compiled python files (compile on demand):
33+
`find / -regex '.*\.py[co]'`
1734

1835
ENV RSYSLOG_DESTINATION=127.0.0.1 \
1936
MODE=http \
@@ -28,4 +45,5 @@ ENV RSYSLOG_DESTINATION=127.0.0.1 \
2845
HEALTH_CHECK="check"
2946

3047
EXPOSE 80 443 1936
48+
ENTRYPOINT ["tini", "--"]
3149
CMD ["dockercloud-haproxy"]

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ Settings in this part is immutable, you have to redeploy HAProxy service to make
138138
|EXTRA_BIND_SETTINGS|<empty>|comma-separated string(`<port>:<setting>`) of extra settings, and each part will be appended to the related port bind section in the configuration file. To escape comma, use `\,`. Possible value: `443:accept-proxy, 80:name http`|
139139
|EXTRA_DEFAULT_SETTINGS|<empty>|comma-separated string of extra settings, and each part will be appended to DEFAULT section in the configuration file. To escape comma, use `\,`|
140140
|EXTRA_GLOBAL_SETTINGS|<empty>|comma-separated string of extra settings, and each part will be appended to GLOBAL section in the configuration file. To escape comma, use `\,`. Possible value: `tune.ssl.cachesize 20000, tune.ssl.default-dh-param 2048`|
141-
|EXTRA_SSL_CERTS||List of extra certificate names separated by comma, eg. `CERT1, CERT2, CERT3`. You also need to specify each certificate as separate env variables like so: `CERT1="<cert-body1>"`, `CERT2="<cert-body2>"`, `CERT3="<cert-body3>"`|
141+
|EXTRA_SSL_CERTS||list of extra certificate names separated by comma, eg. `CERT1, CERT2, CERT3`. You also need to specify each certificate as separate env variables like so: `CERT1="<cert-body1>"`, `CERT2="<cert-body2>"`, `CERT3="<cert-body3>"`|
142142
|HEALTH_CHECK|check|set health check on each backend route, possible value: "check inter 2000 rise 2 fall 3". See:[HAProxy:check](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#5.2-check)|
143143
|HTTP_BASIC_AUTH|<empty>|a comma-separated list of credentials(`<user>:<pass>`) for HTTP basic auth, which applies to all the backend routes. To escape comma, use `\,`. *Attention:* DO NOT rely on this for authentication in production|
144144
|MAXCONN|4096|sets the maximum per-process number of concurrent connections.|
@@ -152,16 +152,19 @@ Settings in this part is immutable, you have to redeploy HAProxy service to make
152152
|STATS_AUTH|stats:stats|username and password required to access the Haproxy stats.|
153153
|STATS_PORT|1936|port for the HAProxy stats section. If this port is published, stats can be accessed at `http://<host-ip>:<STATS_PORT>/`
154154
|TIMEOUT|connect 5000, client 50000, server 50000|comma-separated list of HAProxy `timeout` entries to the `default` section.|
155+
|ADDITIONAL_SERVICES||list of additional services to balance (es: `prj1:web,prj2:sql`). Discovery will be based on `com.docker.compose.[project|service]` container labels. This environment variable only works on compose v2, and the referenced services must be on a network accessible to this containers.|
156+
|CA_CERT_FILE|the path of a ca-cert file. This allows you to mount your ca-cert file directly from a volume instead of from envvar. If set, `CA_CERT` envvar will be ignored. Possible value: `/cacerts/cert0.pem`|
157+
|CERT_FOLDER|the path of certificates. This allows you to mount your certificate files directly from a volume instead of from envvars. If set, `DEFAULT_SSL_CERT` and `SSL_CERT` from linked services are ignored. Possible value:`/certs/`|
155158

156159
### Settings in linked application services###
157160

158161
Settings here can overwrite the settings in HAProxy, which are only applied to the linked services. If run in Docker Cloud, when the service redeploys, joins or leaves HAProxy service, HAProxy service will automatically update itself to apply the changes
159162

160163
|env var|description|
161164
|:-----:|:----------|
162-
|APPSESSION|sticky session option. possible value `JSESSIONID len 52 timeout 3h`. See:[HAProxy:appsession](http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4-appsession)|
165+
|APPSESSION|sticky session option, possible value `JSESSIONID len 52 timeout 3h`. See:[HAProxy:appsession](http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4-appsession)|
163166
|BALANCE|load balancing algorithm to use. Possible values include: `roundrobin`, `static-rr`, `source`, `leastconn`. See:[HAProxy:balance](https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4-balance)|
164-
|COOKIE|sticky session option. possible value `SRV insert indirect nocache`. See:[HAProxy:cookie](http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4-cookie)|
167+
|COOKIE|sticky session option. Possible value `SRV insert indirect nocache`. See:[HAProxy:cookie](http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4-cookie)|
165168
|DEFAULT_SSL_CERT|similar to SSL_CERT, but stores the pem file at `/certs/cert0.pem` as the default ssl certs. If multiple `DEFAULT_SSL_CERT` are specified in linked services and HAProxy, the behavior is undefined|
166169
|EXCLUDE_PORTS|comma separated port numbers(e.g. 3306, 3307). By default, HAProxy will add all the ports exposed by the application services to the backend routes. You can exclude the ports that you don't want to be routed, like database port|
167170
|EXTRA_SETTINGS|comma-separated string of extra settings, and each part will be appended to either related backend section or listen session in the configuration file. To escape comma, use `\,`. Possible value: `balance source`|
@@ -344,6 +347,13 @@ Use the following:
344347
docker run -d -e FORCE_SSL=yes -e SSL_CERT="YOUR_CERT_TEXT" --name webapp dockercloud/hello-world
345348
docker run -d --link webapp:webapp -p 443:443 dockercloud/haproxy
346349

350+
#### I want to load my SSL certificate from volume instead of passing it through environment variable
351+
352+
You can use `CERT_FOLDER` envvar to specify which folder the certificates are mounted in the container, using the following:
353+
354+
docker run -d --name webapp dockercloud/hello-world
355+
docker run -d --link webapp:webapp -e CERT_FOLDER="/certs/" -v $(pwd)/cert1.pem:/certs/cert1.pem -p 443:443 dockercloud/haproxy
356+
347357
#### I want to set up virtual host routing by domain
348358

349359
Virtual hosts can be configured by the proxy reading linked container environment variables (`VIRTUAL_HOST`). Here is an example:
@@ -426,5 +436,3 @@ In most cases, `dockercloud/haproxy` will configure itself automatically when th
426436

427437
* `docker exec <haproxy_id> /reload.sh`, if you are on the node where dockercloud/haproxy deploys
428438
* `docker-cloud exec <haproxy_uuid> /reload.sh`, if you use docker-cloud cli
429-
430-
Note: when `reload.sh` is invoked, it doesn't necessarily mean that HAProxy will be restarted. In fact, `dockercloud/haproxy` will try to get the current information of the the service and calculate a new configuration. HAProxy will only be restarted when the newly generated configuration differs from the current one.

haproxy/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ def parse_extra_bind_settings(extra_bind_settings):
3737
HAPROXY_CONTAINER_URI = os.getenv("DOCKERCLOUD_CONTAINER_API_URI")
3838
HAPROXY_SERVICE_URI = os.getenv("DOCKERCLOUD_SERVICE_API_URI")
3939
API_AUTH = os.getenv("DOCKERCLOUD_AUTH")
40+
CA_CERT_FILE = os.getenv("CA_CERT_FILE")
41+
CERT_FOLDER = os.getenv("CERT_FOLDER")
42+
ADDITIONAL_SERVICES = os.environ.get("ADDITIONAL_SERVICES")
4043
DEBUG = os.getenv("DEBUG", False)
4144
LINK_MODE = ""
4245

haproxy/eventhandler.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ def on_websocket_close():
4545

4646

4747
def on_user_reload(signum, frame):
48-
run_haproxy("User reload")
48+
Haproxy.cls_cfg = None
49+
if config.LINK_MODE == "legacy":
50+
logger.info("User reload is not supported in legacy link mode")
51+
else:
52+
run_haproxy("User reload")
4953

5054

5155
def listen_dockercloud_events():

haproxy/haproxycfg.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class Haproxy(object):
3030
cls_cfg = None
3131
cls_process = None
3232
cls_certs = []
33+
cls_ca_certs = []
3334

3435
def __init__(self, link_mode="", msg=""):
3536
logger.info("==========BEGIN==========")
@@ -80,6 +81,16 @@ def _init_new_links():
8081
logger.info("Docker API error, regressing to legacy links mode: ", e)
8182
return None
8283
links, Haproxy.cls_linked_services = NewLinkHelper.get_new_links(docker, haproxy_container)
84+
85+
try:
86+
if ADDITIONAL_SERVICES:
87+
additional_services = ADDITIONAL_SERVICES.split(",")
88+
NewLinkHelper.get_additional_links(docker, additional_services, haproxy_container,
89+
links, Haproxy.cls_linked_services)
90+
except Exception as e:
91+
logger.info("Error loading ADDITIONAL_SERVICES: %s" % str(e))
92+
return None
93+
8394
logger.info("Linked service: %s", ", ".join(NewLinkHelper.get_service_links_str(links)))
8495
logger.info("Linked container: %s", ", ".join(NewLinkHelper.get_container_links_str(links)))
8596
return links
@@ -121,8 +132,16 @@ def _update_haproxy(self, cfg):
121132

122133
def _config_ssl(self):
123134
ssl_bind_string = ""
124-
ssl_bind_string += self._config_ssl_certs()
125-
ssl_bind_string += self._config_ssl_cacerts()
135+
if CERT_FOLDER:
136+
ssl_bind_string += "ssl crt %s" % CERT_FOLDER
137+
else:
138+
ssl_bind_string += self._config_ssl_certs()
139+
140+
if CA_CERT_FILE:
141+
ssl_bind_string += " ca-file %s verify required" % CA_CERT_FILE
142+
else:
143+
ssl_bind_string += self._config_ssl_cacerts()
144+
126145
if ssl_bind_string:
127146
self.ssl_bind_string = ssl_bind_string
128147

@@ -149,8 +168,8 @@ def _config_ssl_cacerts(self):
149168
if DEFAULT_CA_CERT:
150169
cacerts.append(DEFAULT_CA_CERT)
151170
if cacerts:
152-
if set(cacerts) != set(Haproxy.cls_certs):
153-
Haproxy.cls_certs = copy.copy(cacerts)
171+
if set(cacerts) != set(Haproxy.cls_ca_certs):
172+
Haproxy.cls_ca_certs = copy.copy(cacerts)
154173
self.ssl_updated = True
155174
SslHelper.save_certs(CACERT_DIR, cacerts)
156175
logger.info("SSL CA certificates are updated")

haproxy/helper/new_link_helper.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,43 @@ def get_new_links(docker, haproxy_container):
1616
return links, ["%s_%s" % (project, service) for service in linked_compose_services]
1717

1818

19+
def get_additional_links(docker, additional_services, haproxy_container, links, linked_services):
20+
networks_data = docker.networks()
21+
haproxy_networks_ids = _find_container_networks_ids(haproxy_container, networks_data)
22+
for _container in docker.containers():
23+
container_id = _container.get("Id", "")
24+
container = docker.inspect_container(container_id)
25+
compose_project = container.get("Config", {}).get("Labels", {}).get("com.docker.compose.project", "")
26+
compose_service = container.get("Config", {}).get("Labels", {}).get("com.docker.compose.service", "")
27+
for _service in additional_services:
28+
terms = _service.strip().split(":")
29+
if len(terms) == 2:
30+
if terms[0].strip() == compose_project and terms[1].strip() == compose_service:
31+
container_networks_ids = _find_container_networks_ids(container, networks_data)
32+
if set(container_networks_ids).intersection(haproxy_networks_ids):
33+
if _service not in linked_services:
34+
linked_services.append(_service)
35+
container_name = container.get("Name").lstrip("/")
36+
container_evvvars = _get_container_envvars(container)
37+
endpoints = _get_container_endpoints(container, container_name)
38+
links[container_id] = {"service_name": _service,
39+
"container_envvars": container_evvvars,
40+
"container_name": container_name,
41+
"endpoints": endpoints,
42+
"compose_service": compose_service,
43+
"compose_project": compose_project}
44+
else:
45+
logger.info("Ignoring container '%s': no shared network with haproxy")
46+
47+
48+
def _find_container_networks_ids(container, networks_data):
49+
ids = []
50+
for network in networks_data:
51+
if container['Id'] in network['Containers'].keys():
52+
ids.append(network['Id'])
53+
return ids
54+
55+
1956
def _calc_links(docker, linked_compose_services, project):
2057
links = {}
2158
for _container in docker.containers():
@@ -76,7 +113,9 @@ def _get_linked_compose_services(networks, project):
76113

77114
haproxy_links = []
78115
for network in networks.itervalues():
79-
haproxy_links.extend(network.get("Links", []))
116+
network_links = network.get("Links", [])
117+
if network_links:
118+
haproxy_links.extend(network_links)
80119

81120
linked_services = []
82121
for link in haproxy_links:

test.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ pip install -r test-requirements.txt
88
nosetests -v --with-coverage --cover-package haproxy
99

1010
if [ "$(uname -s)" != "Darwin" ]; then
11-
echo ==================== Integration Test =======================
12-
tests/integration_test.sh
11+
echo ==================== Integration Test on Legacy links =======================
12+
tests/test_legacy_links.sh
1313
fi

0 commit comments

Comments
 (0)