Browse Source

Add support for load-balancing containers.

Closes csmith/docker-automatic-nginx-letsencrypt#8

This changes the generated config to create an `upstream` block for each
service and then reference that within the `server` block.

Multiple containers can be grouped together (so that all their hosts are
added within the same `upstream` block) by adding a new label to the container
with the key "com.chameth.proxy.loadbalance" and the value as the name of the
`upstream` block (This will be prefixed with lb_ for uniqueness)

If no `loadbalance` label is provided, then the container name is used for
the `upstream` block name (prefixed with `ct_` for uniqueness)

The `server` block (which defines the certificate name and the vhosts) is
built based on the first container found with a given `upstream` block name,
so it is reccomended that all containers that are being balanced together
should have the same vhosts.
pull/4/head
Shane Mc Cormack 6 years ago
parent
commit
eb038ebaa0
3 changed files with 25 additions and 10 deletions
  1. 2
    0
      README.md
  2. 15
    8
      generate.py
  3. 8
    2
      nginx.tpl

+ 2
- 0
README.md View File

26
 * `com.chameth.vhost=<host>` -- the virtual host that the proxy will accept
26
 * `com.chameth.vhost=<host>` -- the virtual host that the proxy will accept
27
   connections on. You can specify alternate hosts/aliases by separating them
27
   connections on. You can specify alternate hosts/aliases by separating them
28
   with commas.
28
   with commas.
29
+* `com.chameth.proxy.loadbalance=<gruop name>` -- Load balance this container
30
+  with other containers that have the same group name.
29
 
31
 
30
 ## Usage
32
 ## Usage
31
 
33
 

+ 15
- 8
generate.py View File

22
 
22
 
23
 while True:
23
 while True:
24
   wroteConfig = False;
24
   wroteConfig = False;
25
-  services = []
25
+  services = {}
26
   domains = {k: v.split(',') for k, v in fetcher.get_label('com.chameth.vhost').items()}
26
   domains = {k: v.split(',') for k, v in fetcher.get_label('com.chameth.vhost').items()}
27
   protocols = fetcher.get_label('com.chameth.proxy.protocol')
27
   protocols = fetcher.get_label('com.chameth.proxy.protocol')
28
   defaults = fetcher.get_label('com.chameth.proxy.default')
28
   defaults = fetcher.get_label('com.chameth.proxy.default')
29
+  loadbalance = fetcher.get_label('com.chameth.proxy.loadbalance')
29
   for container, values in fetcher.get_label('com.chameth.proxy').items():
30
   for container, values in fetcher.get_label('com.chameth.proxy').items():
30
     networks = fetcher.get_networks(container)
31
     networks = fetcher.get_networks(container)
31
     certfile = args.cert_path % domains[container][0];
32
     certfile = args.cert_path % domains[container][0];
33
+    up = 'lb_' + loadbalance[container] if container in loadbalance else 'ct_' + container
32
     if os.path.isfile(certfile):
34
     if os.path.isfile(certfile):
33
-      services.append({
34
-        'protocol': protocols[container] if container in protocols else 'http',
35
-        'vhosts': domains[container],
35
+      if not up in services:
36
+        services[up] = {
37
+          'protocol': protocols[container] if container in protocols else 'http',
38
+          'vhosts': domains[container],
39
+          'hosts': [],
40
+          'certificate': args.cert_path % domains[container][0],
41
+          'trusted_certificate': args.trusted_cert_path % domains[container][0],
42
+          'certificate_key': args.cert_key_path % domains[container][0],
43
+          'default': container in defaults,
44
+        }
45
+
46
+      services[up]['hosts'].append({
36
         'host': next(iter(networks.values())), # TODO: Pick a bridge sensibly?
47
         'host': next(iter(networks.values())), # TODO: Pick a bridge sensibly?
37
         'port': values,
48
         'port': values,
38
-        'certificate': args.cert_path % domains[container][0],
39
-        'trusted_certificate': args.trusted_cert_path % domains[container][0],
40
-        'certificate_key': args.cert_key_path % domains[container][0],
41
-        'default': container in defaults,
42
       })
49
       })
43
 
50
 
44
   if wroteConfig or len(services) > 0 or not os.path.isfile('/nginx-config/vhosts.conf'):
51
   if wroteConfig or len(services) > 0 or not os.path.isfile('/nginx-config/vhosts.conf'):

+ 8
- 2
nginx.tpl View File

1
-{% for service in services %}
1
+{% for srvname, service in services.items() %}
2
+upstream {{ service.upstream }} {
3
+{% for upstream in service.hosts %}
4
+        server {{ upstream.host }}:{{ upstream.port }};
5
+{% endfor %}
6
+}
7
+
2
 server {
8
 server {
3
     server_name {{ ' '.join(service.vhosts) }};
9
     server_name {{ ' '.join(service.vhosts) }};
4
     listen [::]:443{{ ' default_server' if service.default }} ssl http2;
10
     listen [::]:443{{ ' default_server' if service.default }} ssl http2;
11
     include /etc/nginx/conf.d/{{ service.vhosts[0] }}/*.conf;
17
     include /etc/nginx/conf.d/{{ service.vhosts[0] }}/*.conf;
12
 
18
 
13
     location / {
19
     location / {
14
-        proxy_pass {{ service.protocol }}://{{ service.host }}:{{ service.port }};
20
+        proxy_pass {{ service.protocol }}://{{ service.upstream }};
15
         proxy_set_header Host $host;
21
         proxy_set_header Host $host;
16
         proxy_set_header X-Forwarded-For $remote_addr;
22
         proxy_set_header X-Forwarded-For $remote_addr;
17
     }
23
     }

Loading…
Cancel
Save