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

Commit 228e7ee

Browse files
author
Feng Honglin
committed
Merge pull request #72 from docker/staging
v1.5
2 parents 707f7e4 + a37529e commit 228e7ee

19 files changed

Lines changed: 171 additions & 109 deletions

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ ENV RSYSLOG_DESTINATION=127.0.0.1 \
2525
HEALTH_CHECK="check inter 2000 rise 2 fall 3"
2626

2727
EXPOSE 80 443 1936
28-
ENTRYPOINT ["tini", "--"]
28+
ENTRYPOINT ["/sbin/tini", "--"]
2929
CMD ["dockercloud-haproxy"]

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ Settings in this part is immutable, you have to redeploy HAProxy service to make
170170

171171
|Environment Variable|Default|Description|
172172
|:-----:|:-----:|:----------|
173-
|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.|
173+
|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 resolvable and accessible to this containers.|
174174
|BALANCE|roundrobin|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)|
175175
|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`|
176176
|CA_CERT| |CA cert for haproxy to verify the client. Use the same format as `DEFAULT_SSL_CERT`|
@@ -180,6 +180,7 @@ Settings in this part is immutable, you have to redeploy HAProxy service to make
180180
|EXTRA_DEFAULT_SETTINGS| |comma-separated string of extra settings, and each part will be appended to DEFAULT section in the configuration file. To escape comma, use `\,`|
181181
|EXTRA_FRONTEND_SETTINGS_\<PORT\>| |comma-separated string of extra settings, and each part will be appended frontend section with the port number specified in the name of the envvar. To escape comma, use `\,`. E.g. `EXTRA_FRONTEND_SETTINGS_80=balance source, maxconn 2000`|
182182
|EXTRA_GLOBAL_SETTINGS| |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`|
183+
|EXTRA_ROUTE_SETTINGS| |a string which is append to the each backend route after the health check, can be over written in the linked services. Possible value: "send-proxy"|
183184
|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>"`|
184185
|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)|
185186
|HTTP_BASIC_AUTH| |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|
@@ -207,6 +208,7 @@ Settings here can overwrite the settings in HAProxy, which are only applied to t
207208
|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)|
208209
|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|
209210
|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|
211+
|EXTRA_ROUTE_SETTINGS| |a string which is append to the each backend route after the health check,possible value: "send-proxy"|
210212
|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`|
211213
|FORCE_SSL|if set(any value) together with ssl termination enabled. HAProxy will redirect HTTP request to HTTPS request.
212214
|GZIP_COMPRESSION_TYPE|enable gzip compression. The value of this envvar is a list of MIME types that will be compressed, possible value: `text/html text/plain text/css`|

haproxy/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def parse_extra_frontend_settings(envvars):
4343
EXTRA_FRONTEND_SETTINGS = parse_extra_frontend_settings(os.environ)
4444
EXTRA_GLOBAL_SETTINGS = os.getenv("EXTRA_GLOBAL_SETTINGS")
4545
EXTRA_SSL_CERT = os.getenv("EXTRA_SSL_CERTS")
46+
EXTRA_ROUTE_SETTINGS=os.getenv("EXTRA_ROUTE_SETTINGS", "")
4647
HAPROXY_CONTAINER_URI = os.getenv("DOCKERCLOUD_CONTAINER_API_URI")
4748
HAPROXY_SERVICE_URI = os.getenv("DOCKERCLOUD_SERVICE_API_URI")
4849
HEALTH_CHECK = os.getenv("HEALTH_CHECK", "check inter 2000 rise 2 fall 3")

haproxy/eventhandler.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import logging
33
import time
4+
import os
45

56
import dockercloud
67
from compose.cli.docker_client import docker_client
@@ -26,7 +27,7 @@ def on_cloud_event(message):
2627
# When service scale up/down or container start/stop/terminate/redeploy, reload the service
2728
if event.get("state", "") not in ["In progress", "Pending", "Terminating", "Starting", "Scaling", "Stopping"] and \
2829
event.get("type", "").lower() in ["container", "service"] and \
29-
len(set(Haproxy.cls_linked_services).intersection(set(event.get("parents", [])))) > 0:
30+
len(Haproxy.cls_linked_services.intersection(set(event.get("parents", [])))) > 0:
3031
msg = "Docker Cloud Event: %s %s is %s" % (
3132
event["type"], get_uuid_from_resource_uri(event.get("resource_uri", "")), event["state"].lower())
3233
run_haproxy(msg)
@@ -74,7 +75,12 @@ def listen_dockercloud_events():
7475

7576
def listen_docker_events():
7677
try:
77-
docker = docker_client()
78+
79+
try:
80+
docker = docker_client()
81+
except:
82+
docker = docker_client(os.environ)
83+
7884
docker.ping()
7985
for event in docker.events(decode=True):
8086
logger.debug(event)

haproxy/haproxycfg.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def run_haproxy(msg=None):
2626

2727

2828
class Haproxy(object):
29-
cls_linked_services = []
29+
cls_linked_services = set()
3030
cls_cfg = None
3131
cls_process = None
3232
cls_certs = []
@@ -43,6 +43,7 @@ def __init__(self, link_mode="", msg=""):
4343
self.routes_added = []
4444
self.require_default_route = False
4545
self.specs = None
46+
self.tcp_ports = set()
4647

4748
self.specs = self._initialize(self.link_mode)
4849

@@ -76,23 +77,24 @@ def _init_cloud_links():
7677
@staticmethod
7778
def _init_new_links():
7879
try:
79-
docker = docker_client()
80+
try:
81+
docker = docker_client()
82+
except:
83+
docker = docker_client(os.environ)
84+
8085
docker.ping()
8186
container_id = os.environ.get("HOSTNAME", "")
8287
haproxy_container = docker.inspect_container(container_id)
8388
except Exception as e:
84-
logger.info("Docker API error, regressing to legacy links mode: ", e)
89+
logger.info("Docker API error, regressing to legacy links mode: %s" % e)
8590
return None
8691
links, Haproxy.cls_linked_services = NewLinkHelper.get_new_links(docker, haproxy_container)
8792

88-
try:
89-
if ADDITIONAL_SERVICES:
90-
additional_services = ADDITIONAL_SERVICES.split(",")
91-
NewLinkHelper.get_additional_links(docker, additional_services, haproxy_container,
92-
links, Haproxy.cls_linked_services)
93-
except Exception as e:
94-
logger.info("Error loading ADDITIONAL_SERVICES: %s" % str(e))
95-
return None
93+
if ADDITIONAL_SERVICES:
94+
additional_links, additional_services= NewLinkHelper.get_additional_links(docker, ADDITIONAL_SERVICES)
95+
if additional_links and additional_services:
96+
links.update(additional_links)
97+
Haproxy.cls_linked_services.update(additional_services)
9698

9799
logger.info("Linked service: %s", ", ".join(NewLinkHelper.get_service_links_str(links)))
98100
logger.info("Linked container: %s", ", ".join(NewLinkHelper.get_container_links_str(links)))
@@ -258,11 +260,12 @@ def _config_tcp_sections(self):
258260
tcp_ports = TcpHelper.get_tcp_port_list(details, services_aliases)
259261

260262
for tcp_port in set(tcp_ports):
261-
tcp_section, port_num = self.get_tcp_section(details, services_aliases, tcp_port)
263+
tcp_section, port_num = self._get_tcp_section(details, services_aliases, tcp_port)
264+
self.tcp_ports.add(port_num)
262265
cfg["listen port_%s" % port_num] = tcp_section
263266
return cfg
264267

265-
def get_tcp_section(self, details, services_aliases, tcp_port):
268+
def _get_tcp_section(self, details, services_aliases, tcp_port):
266269
tcp_section = []
267270
enable_ssl, port_num = TcpHelper.parse_port_string(tcp_port, self.ssl_bind_string)
268271
bind_string = get_bind_string(enable_ssl, port_num, self.ssl_bind_string, EXTRA_BIND_SETTINGS)
@@ -287,6 +290,11 @@ def _config_frontend_sections(self):
287290
monitor_uri_configured = False
288291
if vhosts:
289292
cfg, monitor_uri_configured = FrontendHelper.config_frontend_with_virtual_host(vhosts, ssl_bind_string)
293+
for port in self.tcp_ports:
294+
port_str = "frontend port_%s" % port
295+
if port_str in cfg:
296+
del cfg[port_str]
297+
290298
else:
291299
self.require_default_route = FrontendHelper.check_require_default_route(self.specs.get_routes(),
292300
self.routes_added)

haproxy/helper/backend_helper.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import re
22

3-
from haproxy.config import HEALTH_CHECK, HTTP_BASIC_AUTH
3+
from haproxy.config import HEALTH_CHECK, HTTP_BASIC_AUTH, EXTRA_ROUTE_SETTINGS
44
from haproxy.utils import get_service_attribute
55

66

@@ -14,28 +14,34 @@ def get_backend_section(details, routes, vhosts, service_alias, routes_added):
1414
backend.extend(backend_settings)
1515

1616
route_health_check = get_route_health_check(details, service_alias, HEALTH_CHECK)
17-
backend_routes = get_backend_routes(route_health_check, is_sticky, routes, routes_added, service_alias)
17+
extra_route_settings = get_extra_route_settings(details, service_alias, EXTRA_ROUTE_SETTINGS)
18+
route_setting = " ".join([route_health_check, extra_route_settings]).strip()
19+
backend_routes = get_backend_routes(route_setting, is_sticky, routes, routes_added, service_alias)
1820
backend.extend(backend_routes)
1921
return backend
2022

2123

22-
def get_backend_routes(route_health_check, is_sticky, routes, routes_added, service_alias):
24+
def get_backend_routes(route_setting, is_sticky, routes, routes_added, service_alias):
2325
backend_routes = []
2426
for _service_alias, routes in routes.iteritems():
2527
if not service_alias or _service_alias == service_alias:
28+
addresses_added = []
2629
for route in routes:
2730
# avoid adding those tcp routes adding http backends
2831
if route in routes_added:
2932
continue
33+
address = "%s:%s" % (route["addr"], route["port"])
34+
if address not in addresses_added:
35+
addresses_added.append(address)
36+
backend_route = ["server %s %s" % (route["container_name"], address)]
37+
if is_sticky:
38+
backend_route.append("cookie %s" % route["container_name"])
3039

31-
backend_route = ["server %s %s:%s" % (route["container_name"], route["addr"], route["port"])]
32-
if is_sticky:
33-
backend_route.append("cookie %s" % route["container_name"])
40+
if route_setting:
41+
backend_route.append(route_setting)
3442

35-
if route_health_check:
36-
backend_route.append(route_health_check)
43+
backend_routes.append(" ".join(backend_route))
3744

38-
backend_routes.append(" ".join(backend_route))
3945
return sorted(backend_routes)
4046

4147

@@ -45,6 +51,12 @@ def get_route_health_check(details, service_alias, default_health_check):
4551
return health_check
4652

4753

54+
def get_extra_route_settings(details, service_alias, default_extra_route_settings):
55+
extra_route_settings = get_service_attribute(details, "extra_route_settings", service_alias)
56+
extra_route_settings = extra_route_settings if extra_route_settings else default_extra_route_settings
57+
return extra_route_settings
58+
59+
4860
def get_websocket_setting(vhosts, service_alias):
4961
websocket_setting = []
5062
for v in vhosts:
@@ -155,4 +167,4 @@ def get_basic_auth_setting(basic_auth):
155167
if basic_auth:
156168
setting.append("acl need_auth http_auth(haproxy_userlist)")
157169
setting.append("http-request auth realm haproxy_basic_auth if !need_auth")
158-
return setting
170+
return setting

haproxy/helper/frontend_helper.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from collections import OrderedDict
22

3-
from haproxy.config import EXTRA_BIND_SETTINGS, EXTRA_FRONTEND_SETTINGS, MONITOR_URI, MONITOR_PORT, MAXCONN, SKIP_FORWARDED_PROTO
3+
from haproxy.config import EXTRA_BIND_SETTINGS, EXTRA_FRONTEND_SETTINGS, MONITOR_URI, MONITOR_PORT, MAXCONN, \
4+
SKIP_FORWARDED_PROTO
45

56

67
def check_require_default_route(routes, routes_added):

haproxy/helper/new_link_helper.py

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,36 +13,24 @@ def get_new_links(docker, haproxy_container):
1313
linked_compose_services = _get_linked_compose_services(networks, project)
1414

1515
links = _calc_links(docker, linked_compose_services, project)
16-
return links, ["%s_%s" % (project, service) for service in linked_compose_services]
16+
return links, set(["%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(":")
19+
def get_additional_links(docker, additional_services):
20+
links = {}
21+
services = set()
22+
for additional_service in additional_services.split(","):
23+
terms = additional_service.strip().split(":")
2924
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")
25+
project = terms[0]
26+
service = terms[1]
27+
link = _calc_links(docker, [service], project)
28+
if link:
29+
links.update(link)
30+
services.add("%s_%s" % (project, service))
31+
else:
32+
logger.info("Cannot find the additional service: %s" % additional_service.strip())
33+
return links, services
4634

4735

4836
def _find_container_networks_ids(container, networks_data):

haproxy/helper/tcp_helper.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import re
22

3-
from haproxy.config import HEALTH_CHECK
3+
from haproxy.config import HEALTH_CHECK, EXTRA_ROUTE_SETTINGS
44
from haproxy.utils import get_service_attribute
55

66

@@ -27,6 +27,7 @@ def parse_port_string(port, ssl_bind_string):
2727
def get_tcp_routes(details, routes, port, port_num):
2828
tcp_routes = []
2929
routes_added = []
30+
addresses_added = []
3031
if port != port_num and port != port_num + "/ssl":
3132
return tcp_routes, routes_added
3233

@@ -35,11 +36,17 @@ def get_tcp_routes(details, routes, port, port_num):
3536
if tcp_ports and port in tcp_ports:
3637
for route in routes:
3738
if route["port"] == port_num:
38-
tcp_route = ["server %s %s:%s" % (route["container_name"], route["addr"], route["port"])]
39-
health_check = get_healthcheck_string(details, _service_alias)
40-
tcp_route.append(health_check)
41-
tcp_routes.append(" ".join(tcp_route))
39+
address = "%s:%s" % (route["addr"], route["port"])
40+
if address not in addresses_added:
41+
addresses_added.append(address)
42+
tcp_route = ["server %s %s" % (route["container_name"], address)]
43+
health_check = get_healthcheck_string(details, _service_alias)
44+
extra_route_settings = get_extra_route_settings_string(details, _service_alias)
45+
route_setting = " ".join([health_check, extra_route_settings]).strip()
46+
tcp_route.append(route_setting)
47+
tcp_routes.append(" ".join(tcp_route))
4248
routes_added.append(route)
49+
4350
return tcp_routes, routes_added
4451

4552

@@ -49,6 +56,12 @@ def get_healthcheck_string(details, service_alias):
4956
return health_check
5057

5158

59+
def get_extra_route_settings_string(details, service_alias):
60+
extra_route_settings = get_service_attribute(details, "extra_route_settings", service_alias)
61+
extra_route_settings = extra_route_settings if extra_route_settings else EXTRA_ROUTE_SETTINGS
62+
return extra_route_settings
63+
64+
5265
def get_service_aliases_given_tcp_port(details, service_aliases, tcp_port):
5366
services = []
5467
for service_alias in service_aliases:

haproxy/main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ def check_link_mode(container_uri, service_uri, api_auth):
6868
link_mode = "new"
6969
reason = ""
7070
try:
71-
docker = docker_client()
71+
try:
72+
docker = docker_client()
73+
except:
74+
docker = docker_client(os.environ)
7275
docker.ping()
7376
except Exception as e:
7477
reason = "unable to connect to docker daemon %s" % e

0 commit comments

Comments
 (0)