virli/tutorial/4/networkns.md

237 lines
7.0 KiB
Markdown
Raw Normal View History

2016-10-19 03:24:05 +00:00
\newpage
2017-11-09 00:30:41 +00:00
Le *namespace* `network` {#net-ns}
2016-10-19 03:24:05 +00:00
========================
## 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
2017-11-09 00:30:41 +00:00
environnement qui n'a plus qu'une interface de *loopback* :
2016-10-19 03:24:05 +00:00
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```shell
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
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
2017-11-09 00:30:41 +00:00
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
2016-10-19 03:24:05 +00:00
dans l'espace principal, ne le sont plus dans le conteneur : il est donc
2017-11-09 00:30:41 +00:00
possible de lancer un serveur web sans qu'il n'entre en conflit avec celui d'un
2016-10-19 03:24:05 +00:00
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`.
2017-11-09 00:30:41 +00:00
Nous pouvons tout d'abord créer un nouvel espace de nom :
2016-10-19 03:24:05 +00:00
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```shell
2017-11-09 00:30:41 +00:00
42sh# ip netns add virli
2016-10-19 03:24:05 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
La technique utilisée ici pour avoir des *namespaces* nommés est la même que
2017-11-09 00:30:41 +00:00
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.
2016-10-19 03:24:05 +00:00
2017-11-09 00:30:41 +00:00
Maintenant que notre *namespace* est créé, nous pouvons regarder s'il contient
des interfaces :
2016-10-19 03:24:05 +00:00
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```sh
2017-11-09 00:30:41 +00:00
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
2016-10-19 03:24:05 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
2017-11-09 00:30:41 +00:00
Cette commande ne nous montre que l'interface de *loopback*, car nous n'avons
pour l'instant pas encore attaché la moindre interface.
2016-10-19 03:24:05 +00:00
D'ailleurs, cette interface est rapportée comme étant désactivée, activons-là
via la commande :
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```shell
2017-11-09 00:30:41 +00:00
42sh# ip netns exec virli ip link set dev lo up
2016-10-19 03:24:05 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
2017-11-09 00:30:41 +00:00
À ce stade, nous pouvons déjà commencer à lancer un `ping` sur cette interface:
2016-10-19 03:24:05 +00:00
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```shell
2017-11-09 00:30:41 +00:00
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
...
2016-10-19 03:24:05 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
## *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` :
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```
2017-11-09 00:30:41 +00:00
42sh# ip link add veth0 type veth peer name veth1
2016-10-19 03:24:05 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
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
2017-11-09 00:30:41 +00:00
donc reçus par `veth1` et les paquets envoyés à `veth1` sont reçus par `veth0`.
2016-10-19 03:24:05 +00:00
Dans cette configuration, ces deux interfaces ne sont pas très utiles, mais si
2017-11-09 00:30:41 +00:00
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.
2016-10-19 03:24:05 +00:00
Pour déplacer `veth1` dans notre *namespace* `virli` :
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```shell
2017-11-09 00:30:41 +00:00
42sh# ip link set veth1 netns virli
2016-10-19 03:24:05 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
2017-11-09 00:30:41 +00:00
Il ne reste maintenant plus qu'à assigner une IP à chacune des interfaces :
2016-10-19 03:24:05 +00:00
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```shell
2017-11-09 00:30:41 +00:00
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
2016-10-19 03:24:05 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
2017-11-09 00:30:41 +00:00
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`.
2016-10-19 03:24:05 +00:00
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```shell
2017-11-09 00:30:41 +00:00
42sh# ping 10.10.10.42
- et -
42sh# ip netns exec virli ping 10.10.10.41
2016-10-19 03:24:05 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
Il ne reste donc pas grand chose à faire pour fournir Internet à notre
2017-11-09 00:30:41 +00:00
conteneur : via un peu de NAT ou grâce à un pont Ethernet.
2016-10-19 03:24:05 +00:00
## Les autres types d'interfaces
Le bridge ou le NAT obligera tous les paquets à passer à travers de nombreuses
2017-11-09 00:30:41 +00:00
couches du noyau. Utiliser les interfaces *veth* est plutôt simple et disponible
2016-10-19 03:24:05 +00:00
partout, mais c'est loin d'être la technique la plus rapide ou la moins
gourmande.
### VLAN
2017-11-09 00:30:41 +00:00
Il est possible d'attribuer juste une interface de VLAN, si l'on a switch
supportant la technologie [802.1q](https://fr.wikipedia.org/wiki/IEEE_802.1Q).
2016-10-19 03:24:05 +00:00
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```
2017-11-09 00:30:41 +00:00
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
2016-10-19 03:24:05 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
### MACVLAN
2017-11-09 00:30:41 +00:00
<!-- 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
2016-10-19 03:24:05 +00:00
MACVLAN. S'il est activé dans votre noyau, vous allez avoir le choix entre l'un
2017-11-09 00:30:41 +00:00
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.
2016-10-19 03:24:05 +00:00
#### VEPA
2017-11-09 00:30:41 +00:00
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)).
2016-10-19 03:24:05 +00:00
Pour construire une nouvelle interface de ce type :
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```
2017-11-09 00:30:41 +00:00
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 puisse parler à un
conteneur de la même machine.
<div lang="en-US">
```
42sh# ip link add link eth0 mac1 type macvlan mode private
2016-10-19 03:24:05 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
#### *Bridge*
2017-11-09 00:30:41 +00:00
À 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.
2016-10-19 03:24:05 +00:00
Pour construire une nouvelle interface de ce type :
2017-10-17 06:29:07 +00:00
<div lang="en-US">
2016-10-19 03:24:05 +00:00
```
2017-11-09 00:30:41 +00:00
42sh# ip link add link eth0 mac2 type macvlan mode bridge
2016-10-19 03:24:05 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-19 03:24:05 +00:00
## 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).