容器網絡原理

之前我們介紹Network Namespace(以下簡稱netns)和veth pair時說過docker是使用這些技術來實現的網絡隔離,今天我們就來一探究竟,看下docker到底是如何做到的。

啓動一個無網絡的容器

首先我們使用 --net=none 參數啓動一個無網絡的容器,爲了方便調試,這裏我們使用了centos鏡像。

docker run -itd --name centos-test --net=none centos

啓動成功之後我們進入容器內部確認一下是否無網卡。

[root@localhost ~]# docker ps
CONTAINER ID   IMAGE          COMMAND       CREATED          STATUS          PORTS     NAMES
28dc2e8853df   centos         "/bin/bash"   24 seconds ago   Up 23 seconds             centos-test
[root@localhost ~]# docker exec -it 28dc2e8853df bash
[root@28dc2e8853df /]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever

可以看到確實只有一個本地環回網卡。

如何查看 docker 對應的 netns?

當容器啓動時,docker內部會自動爲這個容器創建一個netns用於網絡隔離,但當我們使用 ip netns list 查看時卻看不到任何數據,這是因爲dockernetns 創建在了其他地方,而ip netns list命令只能讀目錄/var/run/netns下面的數據。

我們可以通過以下命令來解決這個問題,方便我們學習docker網絡。

# 得到容器對應的進程
pid=docker inspect -f '{{.State.Pid}}' "$container_id"
# 手動創建防止文件夾不存在
mkdir -p /var/run/netns/
# 建立軟連接
ln -s /proc/$pid/ns/net /var/run/netns/$container_id

將上面的命令修改爲和當前環境一致並驗證 netns 中的網卡。

[root@localhost ~]# docker inspect -f '{{.State.Pid}}' "28dc2e8853df"
123624
[root@localhost ~]# mkdir -p /var/run/netns/
[root@localhost ~]# ln -s /proc/123624/ns/net /var/run/netns/28dc2e8853df
[root@localhost ~]# ip netns list
28dc2e8853df
[root@localhost ~]# ip netns exec 28dc2e8853df ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever

可以看到我們成功輸出了netns中的網卡信息,並且此信息和從docker容器中看到的一致。

給 docker 添加網卡

參考前面的Linux Bridge章節,我們首先創建一個網橋,然後創建一對veth pair,一端連接到網橋,一端移動到docker對應的netns中。

# 添加網橋
brctl addbr br0
# 啓動網橋
ip link set br0 up
# 新增一對veth
ip link add veth0-ns type veth peer name veth0-br
# 將veth的一端移動到docker對應的netns中
ip link set veth0-ns netns 28dc2e8853df
# 將netns中的本地環回和veth啓動並配置IP
ip netns exec 28dc2e8853df ip link set lo up
ip netns exec 28dc2e8853df ip link set veth0-ns up
ip netns exec 28dc2e8853df ip addr add 10.0.0.1/24 dev veth0-ns
# 將veth的另一端啓動並掛載到網橋上
ip link set veth0-br up
brctl addif br0 veth0-br

最後驗證netnsdocker容器中的網卡信息。

[root@localhost ~]# ip netns exec 28dc2e8853df ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
91: veth0-ns@if90: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 86:29:e6:0a:2a:cb brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.0.1/24 scope global veth0-ns
       valid_lft forever preferred_lft forever
[root@localhost ~]# docker exec -it 28dc2e8853df bash
[root@28dc2e8853df /]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
91: veth0-ns@if90: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 86:29:e6:0a:2a:cb brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.0.1/24 scope global veth0-ns
       valid_lft forever preferred_lft forever

可以看出我們在netns中添加的網卡在docker容器中也可以正確的顯示出來,由此證明了此netnsdocker容器的對應關係。當我們使用docker命令來創建容器時,docker爲我們隱藏了大量細節,輕鬆使用幾條命令便創建好了容器,但這種只知其然不知其所以然的方式對於我們掌握docker並不夠,只有瞭解了底層原理之後才能對其功能掌握的更加深入。

理解 docker 的四種網絡模式

瞭解了docker添加網卡的原理後再來理解docker的幾種網絡模式就十分簡單明瞭了,主要區別就在於是否具有獨立的netns

ZUbTYO

最後也不要忘記清理實驗環境哦。

# 刪除網橋
ip link del br0
# 刪除veth pair
ip link del veth0-br
# 刪除軟連接
rm -rf /var/run/netns/28dc2e8853df
# 刪除容器
docker rm 28dc2e8853df -f
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/xwWfSqHPA_k59IARAr0k3g