Souvent quand on introduit docker, on le présente comme un outil qui utilise des fonctionnalités de Linux : les cgroups et les namespaces… Je sais je l’ai fait plusieurs fois sur la chaine Xavki. Bon c’est bien et ça buzz bien mais bon si on montre jamais comment ça marche on passe à côté d’un truc. Aujourd’hui on va comprendre comment docker simplifie les choses avec son bridge Docker0.
Eh oui on va installer un bridge, 2 namespaces et 2 vethernets.
Alors le but c’est de créer un script qui va nous permettre de créer la structure suivante :
Et on va faire original, on va créer un bridge nommé Xavki0 !! La gloire éternel merci Linux !!
Première choses les variables de tout ce petit monde…
Alors voici les variables que l’on définit dans la partie haute de notre script bash :
#!/usr/bin/bash ## Variables Namespace NS1="x1" NS2="x2" ## Variables vethernet VETH1="xeth1" VETH2="xeth2" ## Variables interface des conteneurs VPEER1="xpeer1" VPEER2="xpeer2" # Variables ip des conteneurs VPEER_ADDR1="10.11.0.10" VPEER_ADDR2="10.11.0.20" ## Variables du bridge BR_ADDR="10.11.0.1" BR_DEV="xavki0"
Première chose les 2 namespaces x1 et x2 qui sont les deux boites isolées d’un point de vue réseau. Et c’est important, aujourd’hui on ne voit que l’isolation réseau. Donc on va considérer que cela pourrait s’apparenter à 2 conteneurs si on ne regarde que la partie réseau.
Ensuite pour connecter ces deux namespaces à notre bridge, il faut un cable. Ce cable, il est virtuellement forcément, ce sont ce que l’on appelle des vethernets : xeth1 et xeth2.
Et pour chaque namespace comme dans nos conteneurs, il nous faut une interface virtuelle les vpeers : xpeer1 et xpeer2.
Pour chacune de ces interfaces, il nous faut une ip pour chaque : 10.11.0.10 et 10.11.0.20.
Bon et enfin, il nous faut un bridge xavki0 et l’adresse de ce bridge, en l’occurence l’ip1 de notre cidr (range d’ip).
Maintenant créons tout ces éléments réseaux
## Création des namespaces ip netns add $NS1 ip netns add $NS2 ##Création des vethernet (cables) & interfaces ip link add ${VETH1} type veth peer name ${VPEER1} ip link add ${VETH2} type veth peer name ${VPEER2} ## Ajout des interfaces au namespace ip link set ${VPEER1} netns ${NS1} ip link set ${VPEER2} netns ${NS2} ## Activation des vethernet ip link set ${VETH1} up ip link set ${VETH2} up
A noter particulièrement que pour la création des vethernets on précise le nim mais aussi les vpeers. nos fameuses interfaces vituelles de chaque namespace. Ensuite, on va ajouter ces vpeers à nos namespaces. Et enfin on va activer nos vethernet.
Vous remarquerez que l’on a pas tout créé 😉
Ajout des ip dans les namespaces
Alors pour l’instant c’est bien tout cela mais on a pas d’ip dans nos namespaces : ni loopback (127.0.0.1), ni nos fameuses ip définies dans nos variables au début. Bon ben go…
## Activation des interfaces dans les namespaces ip netns exec ${NS1} ip link set lo up ip netns exec ${NS2} ip link set lo up ip netns exec ${NS1} ip link set ${VPEER1} up ip netns exec ${NS2} ip link set ${VPEER2} up
Et là vous voyez que notre commande ip avec son argument netns ne sert pas qu’à créer des namespaces… Non en fait elle permet de lancer les commandes de votre choix dans le contexte du namespace que vous allez préciser. On pourrait lancer un LS mais là on décide d’activer nos fameuses ip… Donc pour vérifier on pourrait passer un “ip a” plutôt que ip link set…
Maintenant ces ip on va les attribuer à nos interfaces de chacun de nos namespaces :
## Ajout des ip pour chaque interface ip netns exec ${NS1} ip addr add ${VPEER_ADDR1}/16 dev ${VPEER1} ip netns exec ${NS2} ip addr add ${VPEER_ADDR2}/16 dev ${VPEER2}
Et je dirais que classiquement on fait cela avec ‘ip addr add’ et le device en question. Remarquez aussi que l’on choisit ici de définir un masque /16.
Et notre bridge associé à nos namespaces ???
Ah quand même on vient pour avoir un bridge et on l’a toujours pas créé. Alors voilà ce que l’on fait :
## Création et activation du bridge ip link add ${BR_DEV} type bridge ip link set ${BR_DEV} up ## Ajout des vethernet au bridge ip link set ${VETH1} master ${BR_DEV} ip link set ${VETH2} master ${BR_DEV} ## Ajout de l'ip du bridge ip addr add ${BR_ADDR}/16 dev ${BR_DEV}
On créé le bridge et on le rend up (active).
On y branche nos deux câbles virtuels (souvenez vous ils sont déjà branchés de l’autre côté à l’interface de nos namespaces).
Et ensuite on définit le CIDR ou range d’ip c’est à dire notre /16 et du coup l’ip qui sera attribué à ce bridge au niveau de notre host.
Router le traffic via le bridge
Alors c’est bien d’avoir de IP, des vethernet et tout le bazard mais quand je suis dans un namespace je ne sait pas par où passer pour communiquer à l’extérieur… Et ien on va ajouter des routes dans nos namespaces et donc dans notre cas pour faire simple on va juste avoir des routes par défaut :
## Ajout des routes poru chaque namespace pour passer par le bridge ip netns exec ${NS1} ip route add default via ${BR_ADDR} ip netns exec ${NS2} ip route add default via ${BR_ADDR}
Et pour accéder à internet !!! (à l’extérieur du host plutôt)
Oui c’est toujours cette fameuse question : comment je fais pour accéder à internet.
Et bien on va autoriser l’ip_forwarding au niveau de notre noyau linux. Et surtout on va utiliser iptables pour faire ce que l’on appelle un masquerade pour encapsuler nos paquets qui sortent et savoir dans quel namespace il doivent rentrer (par quelle interface plus précisément).
## Accès externe echo 1 > /proc/sys/net/ipv4/ip_forward iptables -t nat -A POSTROUTING -s ${BR_ADDR}/16 ! -o ${BR_DEV} -j MASQUERADE
Et bim c’est terminé !!! Tout le script est ici.
Voilà j’espère que cela a été assez clair. Vous avez bien sûr la vidéo. Et n’hésitez pas à faire connaître : le blog, la chaine et le podcast !!!