preface

My Ghost blog and self-built private cloud Nextcloud have been running stably for more than one year. During this period, I have experienced two server migrations and multiple version upgrades without data loss or other problems. During the migration process, I also shared how to safely migrate Docker Volumes across servers. At present, blog and web disk service are respectively built on two servers, and the other server is used for reverse generation.

In the anti-generation software, has been using Nginx, recently in the research of K8s, but also the more popular cloud native reverse proxy tool Traefik interested, so I spent some time on the anti-generation server Nginx changed to Traefik. One thing to note is that Traefik’s official documentation is so bad that it’s hard for first-time users to get things done just by looking at the official documentation, so I thought it was important to share it with other first-time users.

Traefik has been updated to version 2.1, and there is a big difference between version 2.x and 1.x. Therefore, this article will be based on the latest version of Traefik.

The main target

A reverse proxy server intercepts server traffic and forwards the traffic to target services or ADDRESSES. If multiple target services or addresses are forwarded, load balancing is performed. The images in Traefik’s official documentation are pretty cute. Here’s a good illustration of what Traefik, or anti-generation software in general, does:

My current requirements are relatively simple, that is, to forward the access traffic from pbeta.me and www.pbeta.me to blog IP: blog port, and to forward the access traffic from cloud.pbeta.me to web disk IP: port. In addition, considering the needs of demonstration, this paper will accomplish the following objectives:

  • Domain name access Traefik Dashboard
  • Forwards domain access to external IP: port
  • Forward domain access to the Docker service
  • Configuring HTTPS access (using LetsEncrypt)
  • Use Middlewares to automatically redirect HTTP access to HTTPS
  • Use Middlewares to add password authentication to Traefik Dashboard

Traefik Configuration description

Before installing Traefik, I thought it would be important to explain the Configuration of Traefik to get a clear idea of the basic Configuration. The following is a reference to the Configuration Introduction page in the official documentation.

Configuration type

In simple terms, Traefik configuration includes static configuration and dynamic configuration. The former is the configuration of Traefik and takes effect only after the Traefik restarts. The latter can be interpreted as the configuration of the proxy service and takes effect immediately.

If the configuration content does not have service differences, it can be unified in the static configuration. Otherwise, it needs to be added separately for each service in the dynamic configuration. Multiple services in the dynamic configuration can also share the same configuration content, for example, a basicAuth Middlewares can be configured to be shared by multiple services.

Configuration Content Location

There are two modes for static or dynamic configuration: CLI or independent file.

For static configuration, one is the Traefik image startup parameters, and the other is a separate configuration File(such as traefik.yml). The official documentation provides examples of both forms, corresponding to CLI and File(YAML) respectively. However, when viewing the official configuration examples, You’ll also find File(TOML), which is an improvement on YAML, which is used for the time being in this article and can be converted by the reader. In addition, the official documentation also states that static configuration can be implemented using environment variables, which is not used in this article.

The priority of the configuration item is Configuration file > Command line Parameters > Environment Variables. Note that the configuration file and command line parameters are mutually exclusive. If you choose to use the configuration file, you cannot use the command line parameters.

For dynamic configuration, you can use labels directly at the bottom of the docker-compose file for your service, for example:

# yaml
  whoami:
    # A container that exposes an API to show its IP address
    image: containous/whoami
    labels:
      - "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
Copy the code

You can also write it in a separate configuration file, which in Traefik is usually called dynamic.yml. You can write all the routing configuration in this file. For clarity, this article uses the latter method.

Configuring the Loading Mode

Using Traefik in Docker probably involves three files:

  • docker-compose.yml
  • Traefik. Yml (optional)
  • dynamic.yml

Again, if you use a separate configuration file to store static configuration, the command part of the docker-comemess.yml file will not take effect.

With the above files, how do you load them? The configuration in docker-comemage. yml doesn’t have to take this into account of course, and the loading of the other two files must be bound to the container.

Traefik. yml file loading

When Traefik starts, the configuration files will be searched in traefik.toml, traefik.yml, or traefik.yaml:

  • /etc/traefik/
  • $XDG_CONFIG_HOME/
  • $HOME/.config/
  • . (the working directory).

In addition, you can by adding a form like – configFile = foo/bar/myconfigfile toml launch parameters of override the default behavior.

According to the above description of the official documentation, considering that we are running Traefik in Docker environment, it is recommended to bind the directory where the configuration file is placed to the /etc/traefik directory of the container.

Dynamic. Yml file loading

To load dynamic configuration files, use Traefik providers in the following format:

  • CLIIn the form of
--providers.file.directory=/etc/traefik
--providers.file.filename=dynamic.yml
--providers.file.watch=true
Copy the code
  • Configuration file format
providers:
  file:
    directory: "/etc/traefik"
    filename: "dynamic.yml"
    watch: true
Copy the code

I did not notice the default directory in the official document, so we must specify the directory and configuration file name. The watch=true in the last line will enable the dynamic configuration item to take effect immediately and no need to restart the container.

Similar to loading traefik. Yml files, in the case of traefik running in Docker, we need to bind the directory that holds dynamic.yml to the container’s /etc/traefik directory. This eliminates the need to bind the two directories separately.

From the above description, the reader should have a basic understanding of how the Traefik configuration works, and you can study the official documentation for more advanced uses.

The installation of Traefik

We used Docker to install Traefik. The following will use the relevant examples in the official documentation as a template to correct the errors and complete the initial configuration and installation.

Docker-compose with let’s encrypt: TLS Challenge Docker-compose with let’s encrypt: TLS Challenge

version: "3.3"

services:

  traefik:
    image: "Traefik: v2.0.0 -rc3." "
    container_name: "traefik"
    command:
      #- "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
      #- "--certificatesresolvers.mytlschallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
      - "--certificatesresolvers.mytlschallenge.acme.email=postmaster@mydomain.com"
      - "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
    ports:
      - "443:443"
      - "8080:8080"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  whoami:
    image: "containous/whoami"
    container_name: "simple-service"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.mydomain.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls.certresolver=mytlschallenge"
Copy the code

This example in the official document uses the CLI to configure static configuration. However, this example has the following problems:

  • The port 80 bond is missing and only HTTPS can be used
  • Not enabling Dashboard
  • No dynamic profile directory is bound

Let’s make specific changes to it, and for clarity, we’ll run the WHOAMI application provided in the example as a separate application later.

According to the previous description of Traefik configuration, we know that we can choose to configure the docker-comemage. yml file or use the independent traefik.yml configuration, I recommend using the independent Traefik. The following is only provided in this way, once familiar with the reader can switch.

You can refer to the official documentation for static configuration

Our configuration file reads as follows:

  • docker-compose.ymlfile
version: "3.3"
services:
  traefik:
    image: "traefik:latest" # We deploy the latest version directly and adjust it by ourselves
    container_name: "traefik"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      We need to create a letsENCRYPT directory in the current directory
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      Bind our configuration directory
      - "./config:/etc/traefik"
Copy the code
  • ./config/traefik.ymlfile
providers:
  docker: {}
  file:
    directory: "/etc/traefik"
    filename: "dynamic.yml"
    watch: true

log:
  level: DEBUG

entryPoints:
  web:
    address: ": 80"
  websecure:
    address: ": 443"

certificatesResolvers:
  mytlschallenge:
    acme:
      email:  "yourname@domain"
      storage:  "/letsencrypt/acme.json"
      tlsChallenge: {}

api:
  dashboard: true
Copy the code
  • ./config/dynamic.ymlfile

This file is temporarily empty because we are not forwarding any services at this time.

Start our docker-comemage.yml file:

docker-compose up -d
Copy the code

After a few moments, you can see that the service has been created successfully. Using Docker PS, you can see that the container is working properly.

Configure domain names to access the Traefik Dashboard

Traefik’s Dashboard is actually a graphical display of its API functions, so we need to enable the API parameters and Dashboard parameters in the configuration, refer to the official documentation:

If you enable the API, a new special service named api@internal is created and can then be referenced in a router. And then define a routing configuration on Traefik itself with the dynamic configuration.

If the API is enabled, a service called api@internal will be created, and the traefik.yml configuration we used above already has the API enabled, as well as dashboard enabled.

In the official configuration, there was a line – “–api.insecure=true” that we ignored. If you’re interested, read it here.

We then add this dynamic route to Traefik itself as described in the official documentation, so we modify the dynamic.yml file:

http:
  routers:
    api:
      entryPoints:
        - "web"
      rule: "Host(`traefik.domain.com`)"
      service: api@internal
Copy the code

In addition to Host, Rule can also be configured with Path, PathPrefix and other logical operators. For details, see here. In addition, resolve the domain name to the server IP address in advance. If the test is performed on a local VM, modify the hosts file by yourself.

Docker-compose up -d: docker-compose up -d: docker-compose up -d: docker-compose up -d

Configure domain names to be forwarded to the Docker service

When we start a Docker service, Traefik can discover the service and create a default route for it. It is important to note that if Traefik is to be able to communicate with the new Docker service, it must be added to the same network. When we start Traefik with docker-comemage. yml, we create the Traefik_default network, and new containers need to be manually added to the network.

Docker run: –network traefik_default: docker-comemage.yml: docker-comemage.yml: docker-comemage.yml: docker-comemage.yml: docker-comemage.yml: docker-comemage.yml

networks:
  - traefik_default
Copy the code

Here we use the WhoAMI app provided by Traefik to test and start the app with docker Run:

docker run -d -P --name whoami --net traefik_default containous/whoami
Copy the code

We then look at the Dashboard and see that Traefik has automatically discovered a service named whoami@docker. To configure the route for this service, open our./config/dynamic.yml file and add the whoAMI route configuration at the bottom:

http:
  routers:
    api:
      entryPoints:
        - "web"
      rule: "Host(`traefik.pbeta.cn`)"
      service: api@internal
    whoami:
      entryPoints:
        - "web"
      rule: "Host(`traefik.pbeta.cn`) && Path(`/whoami`)"
      service: whoami@docker
Copy the code

After configuration is complete and saved in the Dashboard can see the newly added routing, visit http://traefik.pbeta.cn/whoami at this time we can access to our services:

You’ll notice that we don’t tell you which port to forward the service to. This is because Traefik is smart. If the service only exposes one port, Traefik will automatically select that port, but if the service exposes more than one port, you have to specify it manually.

Configure the forwarding of external services

We use native to simulate this requirement, running a Ghost service on our server, but instead of joining the Traefik_default network, forwarding through the server’s Internet IP: service port in Traefik.

Start a Ghost container:

docker run -d -p 2368:2368 --name ghost  ghost
Copy the code

We exposed port 2368 of the server, and now access the server IP directly from outside: port can access the website (if you have port 2368 open on your firewall)

Let’s configure Traefik to forward traefik.pbeta.cn/ghost to that address and continue to modify our dynamic.yml file by adding a service and a route respectively. The complete dynamic.yml file is as follows:

http:
  routers:
    api:
      entryPoints:
        - "web"
      rule: "Host(`traefik.pbeta.cn`)"
      service: api@internal
    whoami:
      entryPoints:
        - "web"
      rule: "Host(`traefik.pbeta.cn`) && Path(`/whoami`)"
      service: whoami@docker
    ghost:
      entryPoints:
        - "web"
      rule: "Host(`ghost.pbeta.cn`) "
      service: ghost

  services:
    ghost:
      loadBalancer:
        servers:
        - url: "http://47.74.154.202:2368/"
Copy the code

After saving, visit http://ghost.pbeta.cn to open our newly launched Ghost website:

Configuring HTTPS Access

So far we have been using the Web portal, which can be easily configured to access services through HTTPS. There are many examples on the official pages for your reference. This article will only show you how to automatically configure HTTPS using Let’s Encrypt.

The related configuration is already included in traefik.yml provided earlier:

certificatesResolvers:
  mytlschallenge:
    acme:
      email:  "yourname@domain"
      storage:  "/letsencrypt/acme.json"
      tlsChallenge: {}
Copy the code

We named the certificatesRsolver as MyTLschallenge, and then we can use the resolver to add TLS in the routing configuration, so we open dynamic.yml and edit it to add an additional routing configuration for the service:

api-tls:
  entryPoints:
    - "websecure"
  rule: "Host(`traefik.pbeta.cn`)"
  service: api@internal
  tls:
    certResolver: "mytlschallenge"
Copy the code

After saving, wait a moment, and HTTPS is configured:

The same certificatesResolver can be used on multiple sites, but to avoid conflicts, option is supported for differentiation. A complete configuration example is as follows:

http:
  routers:
    api:
      entryPoints:
        - "web"
      rule: "Host(`traefik.pbeta.cn`)"
      service: api@internal
    api-tls:
      entryPoints:
        - "websecure"
      rule: "Host(`traefik.pbeta.cn`)"
      service: api@internal
      tls:
        certResolver: "mytlschallenge"
        options: traefik

    whoami:
      entryPoints:
        - "web"
      rule: "Host(`traefik.pbeta.cn`) && Path(`/whoami`)"
      service: whoami@docker
    whoami-tls:
      entryPoints:
        - "websecure"
      rule: "Host(`traefik.pbeta.cn`) && Path(`/whoami`)"
      service: whoami@docker
      tls:
        certResolver: "mytlschallenge"
        options: traefik
    ghost:
      entryPoints:
        - "web"
      rule: "Host(`ghost.pbeta.cn`)"
      service: ghost
    ghost-tls:
      entryPoints:
        - "websecure"
      rule: "Host(`ghost.pbeta.cn`)"
      service: ghost
      tls:
        certResolver: "mytlschallenge"
        options: ghost

  services:
    ghost:
      loadBalancer:
        servers:
        - url: "http://47.74.154.202:2368/"
tls:
  options:
    traefik: {}
    ghost: {}
Copy the code

You’ll notice that we used 2 options, because traefik.pbeta.cn and Ghost.pbeta.cn require different certificates, so we have to distinguish between them. You can also add configuration information in Options according to the documentation.

The use of Middlewares

Traefik provides a wide range of middleware that can do most of what you need, with simple permission authentication and HTTP auto-redirect to HTTPS as examples.

To avoid excessively long configuration files, the following uses Only Dashboard as an example.

Automatically jump to HTTPS

Middlewares is very simple to use. Add Middlewares under HTTP (or TCP) in a dynamic configuration file and then use it in each route. We add Middleware that automatically redirect to HTTPS:

  middlewares:
    redirect:
      redirectScheme:
        scheme: https
Copy the code

We then add this middleware to a Web-entry service:

http:
  routers:
    api:
      entryPoints:
        - "web"
      rule: "Host(`traefik.pbeta.cn`)"
      service: api@internal
      middlewares:
        - redirect
    api-tls:
      entryPoints:
        - "websecure"
      rule: "Host(`traefik.pbeta.cn`)"
      service: api@internal
      tls:
        certResolver: "mytlschallenge"
        options: traefik

  middlewares:
    redirect:
      redirectScheme:
        scheme: https

tls:
  options:
    traefik: {}
Copy the code

After saving the file, accessing http://traefik.pbeta.cn will automatically redirect to https://traefik.pbeta.cn.

Implementing Password Authentication

The middleware to be used here is BasicAuth. For details, see the official documentation.

In K8s, we use Secret resource to manage passwords, but in Docker, we cannot manage passwords in this way. We need to use users or usersFile parameter to manage passwords. Here, we use users.

To create a password, use htpasswd as follows:

  • CentOS
yum -y install httpd-tools
Copy the code
  • Ubuntu
apt install apache2-utils
Copy the code

We use the htpasswd command to generate a password pair with user name test and password test1234:

echo $(htpasswd -nb test test1234).Copy the code

The result is test:$APR1 $K.xibhJV $VdavfNvly69vNZvkKpB2j0.

Note that since we are using it in a separate configuration file, we do not need to escape it. If we use it in CLI form, all $symbols must be written twice. Can use the command to generate escaped in the official document password echo $(htpasswd – nb user password) | sed -e s / $/ \ \ \ \ $\ \ $/ g

To create our BasicAuth Middleware, open the dynamic.yml file and add the following to the redirect created above:

test-auth:
  basicAuth:
    users:
      - "test:$apr1$k.xiBHjv$VdavfNvly69vNZvkKpB2j0"
Copy the code

Then add test-auth to middlewares, the complete dynamic.yml file is as follows:

http:
  routers:
    api:
      entryPoints:
        - "web"
      rule: "Host(`traefik.pbeta.cn`)"
      service: api@internal
      middlewares:
        - redirect
        - test-auth
    api-tls:
      entryPoints:
        - "websecure"
      rule: "Host(`traefik.pbeta.cn`)"
      service: api@internal
      middlewares:
        - test-auth
      tls:
        certResolver: "mytlschallenge"
        options: traefik

  middlewares:
    redirect:
      redirectScheme:
        scheme: https
    test-auth:
      basicAuth:
        users:
          - "test:$apr1$k.xiBHjv$VdavfNvly69vNZvkKpB2j0"
tls:
  options:
    traefik: {}
Copy the code

After saving the file, visit http://traefik.pbeta.cn again and you will find that you need a password to log in:

After entering the password, you can access it normally.

conclusion

This gives us a good understanding of how Traefik is configured to achieve our goals. The official documentation for Traefik is cluttered but detailed, and it should not be difficult to implement advanced features using Traefik with the basic knowledge provided here and the official documentation.

During the configuration process, you can ask questions in the comments section, I will do my best to help.

Reference documentation

  1. Traefik official documentation
  2. What api.insecure do exactly?
  3. Comparison of Traefik 1.7 and Traefik 2.0 with Docker