Docker et ses networks

Arrivé avec la version 1.9 en octobre 2015, les networks de Docker ont apporté une meilleur gestion de la configuration réseaux et remplace aisément l’ancienne fonctionnalités de linking de Docker.

La gestion des networks a été égalementajoutée à la version 2 de docker-compose, ce qui aurait dû en accélérer l’adoption. Pourtant, on trouve beaucoup d’exemples récents n’utilisant pas encore les networks, et trop de personnes ne savent pas utiliser pleinement.

Je vous propose donc un petit tour d’horizon des fonctionnalités apportées par Docker Network

Un seul network bridge et la fonction link

Par défaut, il existe un network appelé bridge (l’interface docker0 quand vous faites un ifconfig). Ce network est créé en même temps que l’installation de docker. Il est toujours utilisé par défaut si vous ne spécifiez de réseaux au lancement d’un container (–network=custom_network).

Dans ce réseau bridge créé par défaut, tous les containers peuvent communiquer entre eux, et donc se faire pirater par le premier container venu.

Si vous voulez interdire ce fonctionnement, il faut mettre l’option icc à false dans le fichier de configuration de docker (voir l’article Docker Sécurité : 10 bonnes pratiques chapitre Interdire les communications entre les containers)

Historiquement, la fonction link propose 4 fonctionnalités

  • Résolution de nom entre les containers
  • Créer des alias
  • Ouvrir les connections entre certains container (quand l’isolation était activé, via –icc=false)
  • Injecter les variables d’environnement d’un container à un autre

Pour assurer la retro-compatibilité avec les anciennes version, le fonctionnement de link reste inchangé quand on utilise encore le réseau bridge crée par défaut. Docker appelle ça le legacy Link

Le Legacy Link avait quelques défauts, il ne supportait pas les restarts des containers par exemple. Les links étaient simplement ajouté dans le fichier /etc/hosts au démarrage du container et n’était pas remis à jour pour prendre en compte d’eventuel changement d’ip.

J’ai réalisé une image w3blogfr/docker-topology-network qui liste toutes les adresses ip accessible depuis le container lancé. Je vous invite à la lancer chez vous si vous avez des containers lancés, elle permet de se rendre compte que par défaut, tous containers peuvent se voir.

docker run --rm --name docker-topology-network w3blogfr/docker-topology-network
172.17.0.1
172.17.0.2
172.17.0.3
f11c0c7d6b99 (172.17.0.4)

Docker networks

La principale fonctionnalités de Docker network est déjà de simplement pouvoir créer plusieurs networks. Cela vous permet de regrouper plusieurs containers entre eux, les groupes ainsi créés sont isolés les uns des autres, ce qui limite les risques et les conflits. A noter qu’un container peux appartenir à zéro ou plusieurs réseaux.

Si vous utiliser la version 2 de docker-compose, docker-compose créé par défaut un nouveau réseau pour regrouper et isoler tous les containers lancés par le même fichier yml

docker-compose up
Creating network "w3blogdemo_default" with the default driver

Docker Compose utilise le nom du repertoire pour définir le nom de son réseau. Des containers lancés par docker-compose dans plusieurs répertoire différents seront donc incapable de communiquer entre eux.

Vous pouvez voir la liste des networks grâce à la commande

docker network ls
NETWORK ID   NAME               DRIVER SCOPE
752abe418caa bridge             bridge local 
0044b07d8d26 host               host   local 
c73c9f7727f2 none               null   local 
869d12ce609c w3blogdemo_default bridge local

Vous les verrez également apparaître avec la commande ifconfig, ce sont toutes les interfaces commençant par ‘br-‘

Attention, le legacy link ne s’applique qu’au network bridge créé par défaut, celui avec pour nom « bridge ». Les autres réseaux créés par la suite utilisent toutes les fonctionnalités de docker network et le nouveau système de link (même si le driver s’appelle également bridge)

Pour les réseaux créés manuellement par l’utilisateur ou par docker-compose,

  • Les noms sont résolus en utilisant un DNS propre à chaque réseau, le fichier /etc/hosts n’est plus utilisé)
  • la fonction link ne sert qu’à créer des alias et ne gère plus la communication entre les containers
  • Les variables d’environnement ne sont plus injectés entre les containers (même si vous utilisez link)

Si je relance mon image w3blogfr/docker-topology-network  avec l’option –network

docker run --rm --network=w3blogdemo_default --name docker-topology-network w3blogfr/docker-topology-network
172.22.0.1
nginx.w3blogdemo_default (172.22.0.2)
w3blogdemo_tomcat2_1.w3blogdemo_default (172.22.0.3)
w3blogdemo_tomcat_1.w3blogdemo_default (172.22.0.4)
3a05ba2e0ecf (172.22.0.5)

Seul les containers liés à mon réseau apparaissent, on voit d’ailleurs que docker utilise une autre tranche d’adresse IP en 172.22.

Mon utilitaire a également pu résoudre une partie des noms des containers grâce au DNS embarqué par Docker Network

Docker Compose et Network

Pour utiliser docker network avec docker-compose, il ne suffit pas d’installer la version 2. il faut ajouter une ligne dans vos fichiers yml pour forcer l’utilisation de la version 2.

version: "2"

services:
  nginx:
    image: nginx:1.11
    ports:
      - "80:80"
    networks:
     - front-network
     - back-network
  tomcat:
    image: tomcat:8.0
    networks:
     - back-network
  tomcat2:
    image: tomcat:8.0
    networks:
     - back-network
networks:
  front-network:
    driver: bridge
  back-network:
    driver: bridge

On peux définir directement les réseaux depuis docker-compose, et sélectionner pour chaque service le ou les réseaux souhaités. Si ce n’est pas nécessaire, un container peux n’avoir aucun réseau, il sera alors isolé de tous les réseaux existants.

Cela apporte une meilleur isolation des containers, mais permet également une gestion avancée des connexions réseaux. Dans l’exemple, nous avons créé 2 réseaux: front et back. Nginx est utilisé comme frontal et il doit communiquer avec le backend, il doit donc appartenir aux 2 réseaux.

Quand l’expert réseau intervient

Les réseaux étant bien identifié fonctionnellement par le développeur, un expert réseaux pourra facilement passer derrière lui pour sécuriser et optimiser les connexions grâce à son outil préféré : iptables

Il va alors s’appuyer sur les interfaces réseaux créé par docker (visible avec ifconfig) pour interdire certains accès et/ou rediriger le trafic sur la bonne interface réseaux physique ou virtuel.

  • Dans le cas d’un serveur physique, on pourrait envisager que les accès via l’ip public du serveur ne puisse accéder qu’au réseau front-network et donc à nginx. Le réseau back-network serait de son côté rattaché au réseau interne de l’entreprise via une autre réseau physique.
  • Dans le cas d’un déploiement sur une solution virtualisée (vSphere par exemple), le réseau back-network pourrait communiquer avec les autres serveurs virtualisés directement par le réseau interne de vSphere, ce qui permet d’optimiser les performances.

Docker Network apporte une solution qui peux convenir aussi bien aux développeurs qu’aux administrateurs systèmes et réseaux.

Dans cet article, nous avons parlé principalement du driver bridge, il existe également un autre driver appelé overlay qui permet de gérer des réseaux Docker multi-host via différentes solutions : Docker Swarm, Kubernetes, Fleet. J’essayerais de vous en parler dans un prochain article, mais le sujet est très vaste.

Pour aller plus loin, je vous conseille la documentation de Docker : Understand Docker container networks