virli/tutorial/4/networkns.md

242 lines
7.2 KiB
Markdown

\newpage
Le *namespace* `network` {#net-ns}
========================
## Introduction
L'espace de noms `network`, comme son nom l'indique permet de virtualiser tout
ce qui est en lien avec le réseau : les interfaces, les ports, les routes, les
règles de filtrage, etc.
En entrant dans un nouvel espace de nom `network`, on se retrouve dans un
environnement qui n'a plus qu'une interface de *loopback* :
<div lang="en-US">
```
42sh# unshare -n ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
```
</div>
Bien que portant le même nom que l'interface de *loopback* de notre
environnement principal, il s'agit bien de deux interfaces isolées l'une de
l'autre.
Qui dit nouvelle pile réseau, dit également que les ports qui sont assignés
dans l'espace principal, ne le sont plus dans le conteneur : il est donc
possible de lancer un serveur web sans qu'il n'entre en conflit avec celui d'un
autre espace de noms.
## Premiers pas avec `ip netns`
La suite d'outils `iproute2` propose une interface simplifiée pour utiliser le
*namespace* `network` : `ip netns`.
Nous pouvons tout d'abord créer un nouvel espace de nom :
<div lang="en-US">
```bash
42sh# ip netns add virli
```
</div>
La technique utilisée ici pour avoir des *namespaces* nommés est la même que
celle que nous avons vue dans
[la première partie sur les *namespaces*](#ns-lifetime) : via un `mount --bind`
dans le dossier `/var/run/netns/`. Cela permet de faire persister le namespace
malgré le fait que plus aucun processus ne s'y exécute.
Maintenant que notre *namespace* est créé, nous pouvons regarder s'il contient
des interfaces :
<div lang="en-US">
```
42sh# ip netns exec virli ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
```
</div>
Cette commande ne nous montre que l'interface de *loopback*, car nous n'avons
pour l'instant pas encore attaché la moindre interface.
D'ailleurs, cette interface est rapportée comme étant désactivée, activons-là
via la commande :
<div lang="en-US">
```bash
42sh# ip netns exec virli ip link set dev lo up
```
</div>
À ce stade, nous pouvons déjà commencer à lancer un `ping` sur cette interface:
<div lang="en-US">
```
42sh# ip netns exec virli ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.038 ms
...
```
</div>
## *Virtual Ethernet*
Étant donné qu'une interface réseau ne peut être présente que dans un seul
espace de noms à la fois, il n'est pas bien pratique d'imposer d'avoir une
interface physique par conteneur, d'autant plus si l'on a plusieurs
centaines de conteneurs à gérer.
Une technique couramment employée consiste à créer une interface virtuelle de
type `veth` :
<div lang="en-US">
```
42sh# ip link add veth0 type veth peer name veth1
```
</div>
Une interface `veth` se comporte comme un tube bidirectionnel : tout ce qui
entre d'un côté sort de l'autre et inversement. La commande précédente a donc
créé deux interfaces `veth0` et `veth1` : les paquets envoyés sur `veth0` sont
donc reçus par `veth1` et les paquets envoyés à `veth1` sont reçus par `veth0`.
Dans cette configuration, ces deux interfaces ne sont pas très utiles, mais si
l'on place l'une des deux extrêmités dans un autre *namespace* `network`, il
devient alors possible de réaliser un échange de paquets entre les deux.
Pour déplacer `veth1` dans notre *namespace* `virli` :
<div lang="en-US">
```bash
42sh# ip link set veth1 netns virli
```
</div>
Il ne reste maintenant plus qu'à assigner une IP à chacune des interfaces :
<div lang="en-US">
```bash
42sh# ip netns exec virli ip a add 10.10.10.42/24 dev veth1
42sh# ip a add 10.10.10.41/24 dev veth0
```
</div>
Dès lors[^linkdown], nous pouvons `ping`er chaque extrêmité :
[^linkdown]: Il peut être nécessaire d'activer chaque lien, via `ip link set
vethX up`.
<div lang="en-US">
```
42sh# ping 10.10.10.42
- et -
42sh# ip netns exec virli ping 10.10.10.41
```
</div>
Il ne reste donc pas grand chose à faire pour fournir Internet à notre
conteneur : via un peu de NAT ou grâce à un pont Ethernet.
## Les autres types d'interfaces
Le bridge ou le NAT obligera tous les paquets à passer à travers de nombreuses
couches du noyau. Utiliser les interfaces *veth* est plutôt simple et disponible
partout, mais c'est loin d'être la technique la plus rapide ou la moins
gourmande.
### VLAN
Il est possible d'attribuer juste une interface de VLAN, si l'on a un switch
supportant la technologie [802.1q](https://fr.wikipedia.org/wiki/IEEE_802.1Q)
derrière notre machine.
<div lang="en-US">
```
42sh# ip link add link eth0 name eth0.100 type vlan id 100
42sh# ip link set dev eth0.100 up
42sh# ip link set eth0.100 netns virli
```
</div>
### MACVLAN
<!-- https://hicu.be/bridge-vs-macvlan -->
Lorsque l'on n'a pas assez de carte ethernet et que le switch ne supporte pas
les VLAN, le noyau met à disposition un routage basé sur les adresses MAC : le
MACVLAN. S'il est activé dans votre noyau, vous allez avoir le choix entre l'un
des quatre modes : *private*, VEPA, *bridge* ou *passthru*.
Quelque soit le mode choisi, les paquets en provenance d'autres machines et à
destination d'un MAC seront délivrés à l'interface possédant la MAC. Les
différences entre les modes se trouvent au niveau de la communication entre les
interfaces.
#### VEPA
Dans ce mode, tous les paquets sortants sont directement envoyés sur
l'interface Ethernet de sortie, sans qu'aucun routage préalable n'ait été
effectué. Ainsi, si un paquet est à destination d'un des autres conteneurs de
la machine, c'est à l'équipement réseau derrière la machine de rerouter le
paquet vers la machine émettrice (par exemple un switch
[802.1Qbg](http://www.ieee802.org/1/pages/802.1bg.html)).
Pour construire une nouvelle interface de ce type :
<div lang="en-US">
```
42sh# ip link add link eth0 mac0 type macvlan mode vepa
```
</div>
#### *Private*
À la différence du mode *VEPA*, si un paquet émis par un conteneur à
destination d'un autre conteneur est réfléchi par un switch, le paquet ne sera
pas délivré.
Dans ce mode, on est donc assuré qu'aucun conteneur ne pourra parler à un autre
conteneur de la même machine.
<div lang="en-US">
```
42sh# ip link add link eth0 mac1 type macvlan mode private
```
</div>
#### *Bridge*
À l'inverse des modes *VEPA* et *private*, les paquets sont routés selon leur
adresse MAC : si jamais une adresse MAC est connue, le paquet est délivré à
l'interface MACVLAN correspondante ; dans le cas contraire, le paquet est
envoyé sur l'interface de sortie.
Pour construire une nouvelle interface de ce type :
<div lang="en-US">
```
42sh# ip link add link eth0 mac2 type macvlan mode bridge
```
</div>
## Aller plus loin {-}
Pour approfondir les différentes techniques de routage, je vous
recommande cet article :
[Linux Containers and Networking](https://blog.flameeyes.eu/2010/09/linux-containers-and-networking).
Appliqué à Docker, vous apprécirez cet article : [Understanding Docker
Networking Drivers and their use
cases](https://www.docker.com/blog/understanding-docker-networking-drivers-use-cases/).