Running web applications in production is effectively ensured by Docker. However, when there is the need to run multiple applications on the same Docker host, setting up a reverse proxy is recommended. Why so? This is due to the fact that only ports 80 and 443 should be exposed to the rest of the world.
This tutorial will get you acquainted with Traefik, a Docker-aware reverse proxy. It has its own monitoring dashboard and is used to route requests to Adminer container and Wordpress container, each talking to a MySQL database. Following this tutorial, you will learn to configure Traefik to serve over HTTPS using Let’s Encrypt. It has its own monitoring dashboard and is used to route requests to Adminer container and Wordpress container, each talking to a MySQL database. Following this tutorial, you will learn to configure Traefik to serve over HTTPS using Let’s Encrypt.
Step One — Traefik Configuring and Running
Running Traefik in a Docker container is easy since the Traefik project has an official Docker image.
The first step is to create a configuration file and set up an encrypted password. This has to be done before to get access to the monitoring dashboard.
Encrypted password will be created with the htpasswd utility, which should be installed from the apache2-utils package:
$ sudo apt-get install apache2-utils
Use htpasswd to generate the password. After that,do not forget to insert the password of your choice for Traefic admin user instead of secure_password:
$ htpasswd -nb admin secure_password
Normally, you should see the following output:
When setting up the HTTP Basic Authentication for the Traefik health check and monitoring dashboard, the same output will be presented in the Traefik configuration file. Therefore, copy the output line to be able to paste it later.
In order to set the configurations of the Traefik server, we need to create traefik.toml, which is a new configuration file. For that, the TOML format will be used. It is a standardized configuration language that is similar to INI files. The TOML file is an instrument for configuring the Traefik server and various providers and integrations. This guide covers three major Traefik’s providers: docker, api, and acme (the latter is used for supporting TLS using Let’s Encrypt).
Use nano to open up your new file or you may use any other text editor:
$ nano traefik.toml
After that, add two named entry points, http and https. It is done to ensure access to all backends by default:
traefik.toml defaultEntryPoints = ["http", "https"]
The http and https will be configured later.
After that, you need to configure api. This provider provides access to a dashboard interface. Here you should paste the output from the htpasswd command:
traefik.toml ... [entryPoints] [entryPoints.dashboard] address = ":8080" [entryPoints.dashboard.auth] [entryPoints.dashboard.auth.basic] users = ["admin:your_encrypted_password"] [api] entrypoint="dashboard"
The dashboard , which is a separate web application, will function within the Traefik container on port 8080.
The connection with the api provider is configured by ehe entrypoints.dashboard section, while HTTP Basic Authentication for the dashboard is configured by entrypoints.dashboard.auth.basic section. Note, that for the value of the users entry, you need to use the output from the htpasswd command. To specify additional logins, use commas to separate them.
Since the first entryPoint has been defined,those for standard HTTP and HTTPS communication (which is not directed towards the api provider) should be defined, as well. The section of entryPoints sets the configurations for the addresses the proxied containers and Traefik and can listen on. So you need to insert these lines into the file underneath the heading entryPoints:
traefik.toml ... [entryPoints.http] address = ":80" [entryPoints.http.redirect] entryPoint = "https" [entryPoints.https] address = ":443" [entryPoints.https.tls] ...
Port 80 is handled by the http entry point. At the same time, the https works with port 443 for TLS/SSL. In order to ensure secure connections for all requests, all of the traffic on port 80 is automatically redirected to the https entry point.
To configure Let’s Encrypt certificate support for Traefik, add the section provided below:
traefik.toml ... [acme] email = "your_email@your_domain" storage = "acme.json" entryPoint = "https" onHostRule = true [acme.httpChallenge] entryPoint = "http"
The name of the section corresponds to the name of the ACME protocol, which is used for communication with Let’s Encrypt in order to manage certificates. The service Let’s Encrypt aks for registration using a valid email address. Thus, you need to set the email key to your email address for Traefik to generate certificates for our hosts. After that, you need to specify that the information received from Let’s Encrypt will be stored in a JSON file called acme.json. The entry point handling port 443 (namely the https entry point) should be chosen by the entryPoint key, which in our case is the https entry point.
The onHostRule key is responsible for generating certificates by Traefik. Thus, the onHostRule setting fetch our certificates as soon as we create our containers with specified hostnames.
Due to section acme.httpChallenge the user can specify how Let’s Encrypt verifies that the certificate should be generated. It is configured to serve a file as part of the challenge through the entrypoint http.
Lastly, the docker provider should be configured by adding the following lines to the file:
traefik.toml ... [docker] domain = "your_domain" watch = true network = "web"
Traefik is enabled to act as a proxy in front of Docker container by docker provider. It has been set to watch for new containers on the web network (which will be created in the section below). Also,it will expose them as subdomains of your_domain.
Note, that at this configuration point, traefik.toml must possess the contents provided below:
traefik.toml defaultEntryPoints = ["http", "https"] [entryPoints] [entryPoints.dashboard] address = ":8080" [entryPoints.dashboard.auth] [entryPoints.dashboard.auth.basic] users = ["admin:your_encrypted_password"] [entryPoints.http] address = ":80" [entryPoints.http.redirect] entryPoint = "https" [entryPoints.https] address = ":443" [entryPoints.https.tls] [api] entrypoint="dashboard" [acme] email = "your_email@your_domain" storage = "acme.json" entryPoint = "https" onHostRule = true [acme.httpChallenge] entryPoint = "http" [docker] domain = "your_domain" watch = true network = "web"
Save and exit the file and the text editor. Now, Traefik is ready to be fired up.
Step Two — Running the Traefik Container
The next step to be effected is creating a Docker network so that proxy could share with containers. The Docker network can be used with applications running with Docker Compose. All of that is called network web.
$ docker network create web
After its start, theTraefik container will be added to the network. Additional containers can also be later added for Traefik to proxy to.
The next step is to create an empty file. It is needed to hold the Let’s Encrypt information. You need to share it into the container to enable Traefik to use it:
$ touch acme.json
It is important to note that the file can be used by Traefik only if the unique read and write access is provided to root user inside of the container. To grant this permission, lock down the permissions on acme.json in order to ensure that only the file owner has read and write permission.
$ chmod 600 acme.json
As soon as the file is passed to Docker, inside the container, the owner will automatically change to the root user.
Lastly, use the following command to create the Traefik container:
$ docker run -d \ $ -v /var/run/docker.sock:/var/run/docker.sock \ $ -v $PWD/traefik.toml:/traefik.toml \ $ -v $PWD/acme.json:/acme.json \ $ -p 80:80 \ $ -p 443:443 \ $-l traefik.frontend.rule=Host:monitor.your_domain \ $ -l traefik.port=8080 \ $--network web \ $--name traefik \ $ traefik:1.7.21-alpine
Since the command is long, we will break it down in parts for better understanding.
The -d flag is used to run the container as a daemon in the background. After that, docker.sock file should be shared into the container to ensure the Traefik process to listen for changes made to containers. The acme.json file and traefik.toml configuration file created into the container are also to be shared.
Then, port 443 and port 80 of our Docker host are mapped to the same ports in the Traefik container. It is made to ensure that Traefik receives all HTTPS and HTTP traffic to the server.
The next step is setting up two Docker labels. Due to these labels Traefik directs traffic to monitor.your_domain hostname to port 8080 (which is inside the Traefik container) exposing the monitoring dashboard.
The container network name is set to web, and the container is named traefik.
Lastly, the traefik:1.7.21-alpine image is used for this container, due to its tiny size.
In case a container is created from the image, a Docker image’s ENTRYPOINT command is run. In this example, within the container, the command used is the traefik binary. Any additional arguments can be added to this command when launching the container, yet all settings have already been configured in traefik.toml file.
Now, when the container is started, you can access a dashboard to see the health status of your containers. It can also be used in visualizing the backends and frontends registered by Traefik. To access the monitoring dashboard point the browser you are using to https://monitor.your_domain. The system will ask your password and username (these are admin and password configured in Step 1).
After you log in, an interface alike will open:
Adding containers for Traefik to work with, you will see this page change.
Now, Traefik proxy is up and running. It is now configured properly to work with Docker and can monitor other Docker containers. To make Traefik act as a proxy, let's start more containers.
Step Three — How to Register Containers with Traefik
Having set the Traefik container up and running, the applications behind it can be run. The containers behind Traefik can be launched:
1. A database management server that uses the official Adminer image.
2. A blog that uses the official Wordpress image.
These two applications will be managed with Docker Compose through a docker-compose.yml file. Use your editor to open the file:
$ nano docker-compose.yml
You need to specify the networks and version that will be used.Thus, add the lines below to the file:
docker-compose.yml version: "3" networks: web: external: true internal: external: false
The newest version 3 of Docker Compose will be used.
In order to makeTraefik recognize our applications, we should make them part of the network. Our network was created manually, and we will introduce it. For this, we need to specify the network name of web and setting external to true. After that, another network should be defined to connect a database container with the exposed containers. This network will be called internal since it won't be exposed through Traefik.
After that, each of our services will be defined in turn. The blog container will be defined first. It will be based on the official image of WordPress . Insert this configuration to the file:
docker-compose.yml version: "3" ... services: blog: image: wordpress:4.9.8-apache environment: WORDPRESS_DB_PASSWORD: labels: - traefik.backend=blog - traefik.frontend.rule=Host:blog.your_domain - traefik.docker.network=web - traefik.port=80 networks: - internal - web depends_on: - mysql
The environment variables that will be set inside the container are specified due to the environment key. When we leave the value WORDPRESS_DB_PASSWORD not specified, we automatically make Docker Compose get it from our shell and pass it through while creating the container. Thus, this environment variable will be specified in our shell before starting the containers. Due to this step, we make the passwords not hard-code.
The labels section is where configuration values for Traefik are specified. It is important to note that Docker labels are not active by themselves; however, Traefik reads them to understand how to treat containers. See the functions of these labels:
- traefik.frontend.rule=Host:blog.your_domain. This label makes Traefik examine the requested host. If this host matches the pattern of blog.your_domain, the traffic will be routed to the blog container.
- traefik.backend is aimed at specifying the name of the backend service in Traefik (this points to the actual blog container).
- traefik.port works to specify the exposed port, which should be used by Traefik to route traffic to the container.
- traefik.docker.network=web determines the network to look under to let Traefik find the internal IP for the container. Potentially, Traefik container will take the IP for the internal network if this was not specified before since it has access to all of the Docker info.
Having configured the system, we can be sure that all traffic sent to port 80 of our Docker host will be routed to the blog container.
For Traefik to find this container via the web network , it is assigned to two different networks. Also, with these configurations in place, Traefic will be able to use the internal network to communicate with the database container.
Finally, due to the depends_on key, Docker Compose will know that this container should start after its dependencies are running. This needs to be so because WordPress needs a database to run; thus our mysql container should be launched before starting our blog container.
The MySQL service should be configured by adding the following configuration to your file:
docker-compose.yml services: ... mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: networks: - internal labels: - traefik.enable=false
For this container, the official MySQL 5.7 image will be used. Please notice, that an environment item is used without a value. The variables such as WORDPRESS_DB_PASSWORD and MYSQL_ROOT_PASSWORD should be set to the same value in order to ensure the WordPress container communication with MySQL. The mysql container should not be exposed to the outside world and Traefik; thus, it must be assigned to the internal network. However, Traefik has access to the Docker socket. It means that a frontend for the mysql container will be exposed by default. To avoid this, we need to add the label traefik.enable=false. This will prohibit Traefik from exposing this container.
Define the Adminer container by adding the below configuration:
docker-compose.yml services: ... adminer: image: adminer:4.6.3-standalone labels: - traefik.backend=adminer - traefik.frontend.rule=Host:db-admin.your_domain - traefik.docker.network=web - traefik.port=8080 networks: - internal - web depends_on: - mysql
Adminer image is the basis for this container. The depends_on configurations, as well as the network for this container, match those used for the blog container.
All the traffic to port 80 on our Docker host is directed to the blog container. Therefore, there is a need to configure the container in a different way to lead traffic to our adminer container. WIth the line traefik.frontend.rule=Host:db-admin.your_domain we make Traefik examine the host requested. If there is the match with the pattern of db-admin.your_domain, the traffic will be routed to the adminer container by Traefic.
At this stage, the following contents should be included in docker-compose.yml:
docker-compose.yml version: "3" networks: web: external: true internal: external: false services: blog: image: wordpress:4.9.8-apache environment: WORDPRESS_DB_PASSWORD: labels: - traefik.backend=blog - traefik.frontend.rule=Host:blog.your_domain - traefik.docker.network=web - traefik.port=80 networks: - internal - web depends_on: - mysql mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: networks: - internal labels: - traefik.enable=false adminer: image: adminer:4.6.3-standalone labels: - traefik.backend=adminer - traefik.frontend.rule=Host:db-admin.your_domain - traefik.docker.network=web - traefik.port=8080 networks: - internal - web depends_on: - mysql
Save and exit the file and the text editor.
The next step is to set values in your shell for the MYSQL_ROOT_PASSWORD and WORDPRESS_DB_PASSWORD variables before starting the containers:
$ export WORDPRESS_DB_PASSWORD=secure_database_password $ export MYSQL_ROOT_PASSWORD=secure_database_password
Put the database password of your choice instead of secure_database_password. Keep in mind that you need to use the same password for both MYSQL_ROOT_PASSWORD and WORDPRESS_DB_PASSWORD.
Having set the above variables, use docker-compose to run the containers:
$ docker-compose up -d
Check the Traefik admin dashboard. As you can see, a frontend and backend for the two exposed servers are now here:
Go to blog.your_domain and substitute your_domain with your domain. After that, the system will redirect you to a TLS connection, which means that you can complete the setup of Wordpress:
After that, use your browser to visit db-admin.your_domainaccess to access Adminer and substitute your_domain with your domain again. Although mysql container is not shared with the outside world, the adminer container can access it via the internal Docker network shared using the name of mysql container as a host name.
On login screen of Adminer, use mysql for the server, the username root, and the value that was set for MYSQL_ROOT_PASSWORD for the password. After logging in, Adminer user interface will open.
Now, the sites are working. Use the dashboard at monitor.your_domain to watch your applications.