Traefik, un reverse-proxy pour vos conteneurs

traefik

Dans un précédent article nous avons parlé de Consul et notamment de Consul-template, un binaire permettant de recharger une configuration pour que celle ci reflète les conteneurs up ou down à un moment donné. Ce n'était pas propre, mais à l'époque, ni Nginx, ni HAProxy ne savait directement parler à Consul et n'avait aucun moyen d'updater leur conf autrement.

Et bien aujourd'hui vous savez quoi ? C'est toujours pas possible.

Mais ! Y'a Traefik ! Et Traefik est un reverse-proxy un peu plus récent et donc un peu plus intelligent que Nginx ;)

On cherche à faire quoi déjà ?

La problématique est un peu toujours la même, les conteneurs ça apparait et ça disparait assez vite, on ne veut pas avoir à maitriser le port de l'host sur lequel ils écoutent et quand il s'agit de webservices, ça devient vite problématique...
Consul et les discovery services en général solutionnent une partie du problème, on a un outil qui permet de savoir ce qui tourne et où. C'est bien, mais si on pouvait exploiter ces infos ça serait mieux.

Dans le cas de webservices, on cherche à avoir un reverse-proxy qui sache en "temps réel" où se trouve nos conteneurs et sache sur quel groupe de conteneurs forwarder les requêtes destinées à une URL donnée. Si on peut loadbalancer entre plusieurs conteneurs et disposer de plusieurs backends, ça serait top.


Contactez nos Experts !


Traefik

Je présente ici Traefik. A ma connaissance, Vulcand propose le même type de service mais d'après la doc, il ne peut se backer que sur etcd. Ah et Traefik a été écrit par Emile Vauge, c'est français, bien de chez nous, donc c'est mieux.

Traefik est donc un reverse-proxy et un loadbalancer fait pour déployer principalement des microservices (ie conteneurs). Il est nativement simple puisque sa configuration propre est extrêmement limitée étant donné que celle ci est majoritairement "déléguée" à ses backends. Et parmis ces backends, on compte Docker, Consul, k8s, mesos, etcd etc. Personne ne manque à l'appel. Traefik peut même être backé par de simples fichiers statiques et se comporter comme un reverse-proxy classique.

Pour notre première démonstration, on va utiliser directement le backend Docker.

Comme pour Registrator et Consul, il faudra donner à notre conteneur Traefik accès au socket Docker afin que celui ci puisse accéder aux évènements.

Uilisons docker-compose pour créer les conteneurs nécessaires :

traefik:
  image: containous/traefik:experimental
  command: --web --docker --logLevel=DEBUG
  ports:
    - "80:80"
    - "8080:8080"
  volumes:
    - "/var/run/docker.sock:/var/run/docker.sock"
    - "$PWD/traefik.yml:/traefik.toml"

webapp:
  image: dockercloud/hello-world
  labels:
    - "traefik.port=80"
    - "traefik.backend=hello"
    - "traefik.frontend.rule=Host:hello.docker"

Rien de spécial du coté du conteneur Traefik. Une interface web est dispo sur le port 8080.

Pour notre webapp de test, j'ai repris l'image dockercloud/hello-world de Tutum (racheté par Docker depuis d'où le changement de userspace) qui nous permettra de vérifier la load balancing simplement.
Les labels servent à donner les informations à Traefik.

  • Un backend représente ici un service. Si traefik décide de forwarder les requêtes web à un backend, celles ci seront load balancées entre tous les conteneurs appartenant à ce backend.
  • Un frontend peut être assimiler à la directive server_name chez Nginx. Cela permet de contrôler le trafic entrant. Ici la règle est simple et stipule que le header http "Host" doit être à hello.docker pour faire parti de ce frontend.

D'autres labels peuvent être utilisés pour spécifier différents paramètres à Traefik comme le poids du conteneur au sein du backend.

Pas besoin d'exposer de port, nous sommes sur un seul host, le trafic se fera au sein du bridge docker0.

Ah et la conf Traefik !

[docker]
domain = "docker"
endpoint = "unix:///var/run/docker.sock"
watch = true

Ouais c'est tout.

On peut ensuite lancer tout ça : docker-compose up -d

Pour le test, j'ai ajouté 127.0.0.1 hello.docker dans mon fichier /etc/hosts.

Vous pouvez tester l'URL http://hello.docker dans votre navigateur. La page du conteneur dockercloud/hello-world devrait fonctionner.

Mais ce qui est vraiment intéressant c'est que si on augmente le nombre de conteneur dockercloud/hello-world

docker-compose scale webapp=5

Et qu'on recharge plusieurs fois notre page web, on voit bien le round-robin s'effectuer !

La même chose avec un discovery service existant

Dans l'exemple précédent nous avons donné accès à Traefik directement au socket Docker. Mais il est possible que votre infra dispose déjà d'un cluster Consul par exemple et que vous souhaitiez vous en servir comme backend pour Traefik.

Avec Consul, on a deux options, soit utiliser son KV Store ou son catalogue. De base, le KV Store est vide. Registrator ne peuple pas le KV Store mais le catalogue, le KV Store est juste là si vous en avez besoin mais il est vide de base.

Le catalogue :

curl localhost:8500/v1/catalog/services | jq .
{
  "consul": [],
  "hello": [],
  "dockercloud/hello-world": [
    "traefik.backend=hello2",
    "traefik.frontend.rule=Host:hello2.osones.com"
  ]
}

Le KV Store

curl http://localhost:8500/v1/kv/\?recurse

curl -X PUT -d 'good' http://localhost:8500/v1/kv/web/osones
curl  http://localhost:8500/v1/kv/\?recurse | jq .
[
  {
    "CreateIndex": 35,
    "ModifyIndex": 35,
    "LockIndex": 0,
    "Key": "web/osones",
    "Flags": 0,
    "Value": "Z29vZA=="
  }
]

Le KV doit être rempli pour être utilisé. Il y'a donc peu de chance que vous ayez privilégié cette méthode alors que des outils comme registrator peuvent faire le travail pour vous et peupler le catalogue.

Repartons sur un docker-compose.yml :

version: "2"
services:
  consul:
    image: progrium/consul
    command: -server -bootstrap
    ports:
      - "8500:8500"
    labels:
      SERVICE_IGNORE: "true"

  registrator:
    depends_on:
      - consul
    image: gliderlabs/registrator
    command: -internal consul://172.17.0.1:8500
    volumes:
      - "/var/run/docker.sock:/tmp/docker.sock"

  traefik:
    depends_on:
      - consul
    image: containous/traefik:experimental
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "$PWD/traefik.toml:/traefik.toml"
    labels:
      SERVICE_IGNORE: "true"

  hello:
    image: dockercloud/hello-world
    labels:
      SERVICE_NAME: "hello"
  hello2:
    image: dockercloud/hello-world
    labels:
      SERVICE_TAGS: "traefik.backend=hello2,traefik.frontend.rule=Host:hello2.osones.com"

Et le traefik.toml

logLevel = "DEBUG"

[web]
address = ":8080"

[consulCatalog]
endpoint = "172.17.0.1:8500"
domain = "consul.local"
prefix = "traefik"

Prenons le conteneur "hello" :
Si aucun SERVICE_NAME n'est défini, une URL de la forme "nom_image"."traefik_domain" sera créee.
SERVICE_NAME permet d'override le nom de l'image et vous pouvez override toute l'URL avec traefil.frontend.rule.
Les paramètres Traefik ne sont plus passés directement au conteneur mais sont passés à Consul via SERVICE_TAGS :

curl localhost:8500/v1/catalog/service/helloworld | jq .
[
  {
    "Node": "128688b27e1f",
    "Address": "172.18.0.3",
    "ServiceID": "0a2be7713001:consul_hello2_1:80",
    "ServiceName": "helloworld",
    "ServiceTags": [
      "traefik.backend=hello2",
      "traefik.frontend.rule=Host:hello2.osones.com"
    ],
    "ServiceAddress": "172.18.0.4",
    "ServicePort": 80
  }
]

Deux URL sont donc disponibles, hello.consul.local et hello2.osones.com donnant respectivement accès aux backends hello et hello2.

Conclusion

Bien que consul-template permettait déjà d'effectuer beaucoup d'update de configuration de façon automatique, Traefik apporte énormément de simplicité et de puissance ainsi qu'une compatibilité avec énorment de produit.

Une présentation des Ingress Controllers sous Kubernetes ainsi qu'une intérgration avec Traefik et Let's Encrypt en bonus est disponible ici !


Aller plus loin :

(Re)découvrez les derniers articles Osones !

Si vous les avez loupés, voici les nouveaux articles sur le Blog Osones :



Rejoignez vous aussi la conversation !

  • Questions, remarques, suggestions... Contactez-nous directement sur Twitter sur @osones !
  • Pour discuter avec nous de vos projets, nous sommes disponibles directement via contact@osones.com !


Romain Guichard

La discussion continue !

Nous attendons vos questions, remarques & mots doux sur notre Twitter :