ready to build your app

Load Balancing and Reverse Proxy With Traefik

Traefik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy. Traefik integrates with your existing infrastructure components (Docker, Swarm mode, Kubernetes, Marathon, Consul, Etcd, Rancher, Amazon ECS, …) and configures itself automatically and dynamically. Pointing Traefik at your orchestrator should be the only configuration step you need.

In this article, I will show you how to set up Traefik on Rancher infrastructure. It simplifies the process of exposing your applications and obtaining SSL/TLS certificates from “Let’s Encrypt” service.

This tutorial assumes that you have basic knowledge of Docker and Rancher.

Requirements:

  • Rancher (already configured and running)
  • Traefik – no worries, it’s free
  • Some web app to deploy

Let’s begin by creating a configuration file

As we will use it on Rancher, first we need to enable the Rancher provider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Enable Rancher Provider.
[rancher]

  # Default base domain used for the frontend rules.
  domain = "<BASE_DOMAIN>"

  # Expose Rancher services by default in Traefik.
  # If true all services will be accessed on <PROTOCOL>://<SERVICE_NAME>.<STACK_NAME>.<DOMAIN>:<HTTP_PORT>
  exposedByDefault = false

  # Expose only services with a healthy state.
  enableServiceHealthFilter = true

  # Credentials for Rancher API
  [rancher.api]

    # Endpoint to use when connecting to the Rancher API.
    # It is a little bit tricky as we will configure the domain for Rancher later in this article.
    endpoint = "https://<RANCHER_DOMAIN>/v1"

    # Credentials for Rancher API
    accessKey = "<RANCHER_ACCESS_KEY>"
    secretKey = "<RANCHER_SECRET_KEY>"

Credentials can be generated on the API/Keys page on Rancher. Just click the “Add Environment API Key” button.

Then we will configure entrypoints for all of the applications.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Entrypoints to be used by frontends that do not specify any entrypoint.
# Each frontend can specify its own entrypoints.
defaultEntryPoints = ["http", "https"]

# Entrypoints definition.
[entryPoints]

  # HTTP definition.
  [entryPoints.http]
         
    # Default 80 port for HTTP requests.
    address = ":80"

    # Enable gzip compression
    # Responses are compressed when:
    # - The response body is larger than 512 bytes
    # - And the Accept-Encoding request header contains gzip
    # - And the response is not already compressed
    compress: true

  # HTTPS definition.
  [entryPoints.https]

    # Default 443 port for HTTPS requests.
    address = ":443"

    # Enable TLS encryption on HTTPS endpoints.
    [entryPoints.https.tls]

In order to correctly obtain certificates from “Let’s encrypt”, we need to enable ACME (Automatic Certificate Management Environment) Protocol.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Enable ACME.
[acme]

# Email address used for registration on Let’s Encrypt.
email = "<EMAIL_ADDRESS>"

# File used for certificate storage.
storage = "/etc/traefik/acme/acme.json"

# Entrypoint to proxy acme apply certificates to.
entryPoint = "https"

# Enable certificate generation on frontend host rules.
onHostRule = true

# Use a HTTP-01 ACME challenge.
[acme.httpChallenge]

  # USE HTTP endpoint for this challenge.
  entrypoint = "http"

Now we can configure the domain for Rancher.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[file]

[backends]

  # Set name of backend describing Rancher. We name it "rancher".
  [backends.rancher]

    [backends.rancher.servers]

      [backends.rancher.servers.server0]

        # URL to Rancher server.
        url = "http://<RANCHER_IP>:<RANCHER_PORT>"
     
        # As this is a single instance Rancher, we can set the weight to 1. All requests will be handled by this server.
        weight = 1

[frontends]

  # Set name of frontend describing Rancher. We use the same name "rancher".
  [frontends.rancher]

    # Backend which will handle requests.
    backend = "rancher"

    # Forward client Host header to the backend.
    passHostHeader = true

      [frontends.rancher.routes]

        [frontends.rancher.routes.route0]

          # Use this frontend only when the request contains Host header with our domain.
          rule = "Host:<RANCHER_DOMAIN>"

Now Traefik should correctly handle all requests to Rancher server. Additionally, it should obtain a new certificate for <RANCHER_DOMAIN> and allows connection to it over encrypted HTTPS protocol.

We can also set an option for retrying to send requests in case of network errors.

1
[retry]

We will save the configuration file under /home/docker/traefik/traefik.toml directory on the server. The whole configuration file for Traefik:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
defaultEntryPoints = ["http", "https"]

[rancher]
  domain = "<BASE_DOMAIN>"
  exposedByDefault = false
  enableServiceHealthFilter = true

  [rancher.api]
    endpoint = "https://<RANCHER_DOMAIN>/v1"
    accessKey = "<RANCHER_ACCESS_KEY>"
    secretKey = "<RANCHER_SECRET_KEY>"

[entryPoints]

  [entryPoints.http]
    address = ":80"

  [entryPoints.https]
    address = ":443"

    [entryPoints.https.tls]

[acme]
  email = "<EMAIL_ADDRESS>"
  storage = "/etc/traefik/acme/acme.json"
  entryPoint = "https"
  onHostRule = true

  [acme.httpChallenge]
    entrypoint = "http"

[retry]

[file]

[backends]
  [backends.rancher]
    [backends.rancher.servers]
      [backends.rancher.servers.server0]
        url = "http://<RANCHER_IP>:<RANCHER_PORT>"
        weight = 1

[frontends]
  [frontends.rancher]
    backend = "rancher"
    passHostHeader = true

      [frontends.rancher.routes]
        [frontends.rancher.routes.route0]
          rule = "Host:<RANCHER_DOMAIN>"

After saving this configuration file on the server, create a new empty file acme.json next to it. Now we are ready to create a new stack on Rancher for Traefik.

Create a new stack for Traefik

Important note: You have to disable any other publicly available reverse proxies and to be able to do that you have to open Rancher via IP address.

After stopping the previously working reverse proxy and making sure that there are no containers with publicly exposed ports 80 or 443, click on “create a new stack”. Upload following docker-compose.yml file (of course update volumes accordingly to the directory where you saved the configuration file):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: '2'
services:
  traefik:
    image: traefik
    stdin_open: true
    volumes:
    - /home/docker/traefik/traefik.toml:/etc/traefik/traefik.toml
    - /home/docker/traefik/acme.json:/etc/traefik/acme/acme.json
    tty: true
    ports:
    - 80:80/tcp
    - 443:443/tcp
    labels:
      io.rancher.container.pull_image: always

After deploying it, we are ready to use Traefik to handle requests made by our own applications. Also, Rancher should be available on encrypted URL: https://<RANCHER_DOMAIN>

Use Traefik on our own applications

In the last step, we need a web application. I will use a very sophisticated Hello World application.

I will create a new stack for it. Things that are important for Traefik are labels set for a specific container.

Enable this container in Traefik:

1
Traefik.enable: 'true'

Sets the container name of the backend:

1
traefik.backend: node-hello-world

Registers port on which the application is listening (internal docker port):

1
traefik.port: '8080'

Use this container only when the request contains Host header with our domain:

1
traefik.frontend.rule: Host:<DOMAIN>

Sets the domain:

1
traefik.domain: <DOMAIN>

Overrides the default HTTP protocol:

1
traefik.frontend.protocol: https

Sets redirect from HTTP to encrypted HTTPS protocol:

1
traefik.frontend.redirect.entryPoint: https

Returns 301 instead of 302 for redirect:

1
traefik.frontend.redirect.permanent: 'true'

After setting correct labels to our application, it will be accessible on provided domain. The docker-compose.yml file that I will use for my Hello World application looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
version: '2'
services:
  node-hello-world:
    image: heroku/nodejs-hello-world
    environment:
      PORT: '8080'
    stdin_open: true
    tty: true
    labels:
      io.rancher.container.pull_image: always
      traefik.enable: 'true'
      traefik.backend: node-hello-world
      traefik.port: '8080'
      traefik.frontend.rule: Host:<DOMAIN>
      traefik.domain: <DOMAIN>
      traefik.frontend.protocol: https
      traefik.frontend.redirect.entryPoint: https
      traefik.frontend.redirect.permanent: 'true'

Summary

Traefik is a reverse proxy that allows developers to expose their application on domains with ease. Built-in support for “Let’s Encrypt” will automatically discover new services and obtain SSL/TLS certificates. Also, it continuously updates its configuration, so no restarts are needed.

More information about Traefik can be found on the official page: https://docs.traefik.io/

Adam Gołąb

Adam Gołąb is a JavaScript Full Stack Developer at Brainhub (a software house building awesome Node.js web and mobile apps).