1. 程式人生 > 實用技巧 >Docker容器網路基礎

Docker容器網路基礎

網路概述


1,Linux的namespace+cgroup

namespace和cgroup是Linux 核心的兩大特性,namespace的誕生據說就是為了支援容器技術,那麼這倆特性到底幹了啥呢?

-namespace:linux支援多種型別的namespace,包括Network,IPC,PID, Mount, UTC, User。建立不同型別的namespace就相當於從不同資源維度在主機上作隔離。

-cgroup:為了不讓某個程序一家獨大,而其他程序餓死,所以它的作用就是控制各程序分配的CPU,Memory,IO等。

- namespace+cgroup也適用程序組,即多程序執行在一個單獨的ns中,此時該ns下的程序就可以互動了。

參考:https://coolshell.cn/articles/17010.html

2,容器


容器實際上是結合了namespace 和 cgroup 的一般核心程序,注意,容器就是個程序

所以,當我們使用Docker起一個容器的時候,Docker會為每一個容器建立屬於他自己的namespaces,即各個維度資源都專屬這個容器了,此時的容器就是一個孤島,也可以說是一個獨立VM就誕生了。當然他不是VM,網上關於二者的區別和優劣有一對資料.

更進一步,也可以將多個容器共享一個namespace,比如如果容器共享的是network 型別的namespace,那麼這些容器就可以通過 localhost:[埠號]來通訊了。因為此時的兩個容器從網路的角度看,和宿主機上的兩個核心程序沒啥區別。

在下面的詳解部分會有試驗來驗證這個理論

Docker容器網路詳解


從範圍上分:
單機網路:none,host, bridge
跨主機網路:overlay,macvlan,flannel等

從生成方式分:
原生網路:none,host, bridge
自定義網路:
使用docker原生實現的驅動自定義的網路:bridge(自定義),overlay,macvlan,
使用第三方驅動實現的自定義網路:flannel等

在學習網路的時候肯定遇到過關於CNM這個概念,所以首先,我們一起學習下CNM&libnetwork

CNM&libnetwork
libnetwork是Docker團隊將Docker的網路功能從Docker的核心程式碼中分離出來形成的一個單獨的庫,libnetwork通過外掛的形式為Docker提供網路功能。基於程式碼層面再昇華一下,可以將docker的網路抽象出一個模型來,就叫CNM(Container Networking Model),該模型包含三大塊:

  • Sandbox:容器的網路棧,包含interface,路由表,DNS設定等,可以看做就是linux network型別的namespace本身,該有的網路方面的東西都要有,另外還包含一些用於連線各種網路的endpoint
  • Endpoint : 用來將sandbox接入到network中。典型的實現是Veth pair技術(Veth pair是Linux固有的,是一個成對的介面,用來做連線用)
  • Network : 具體的網路實現,比如是brige,VLAN等,同樣它包含了很多endpoint(那一頭)

一句話:sandbox代表容器,network代表容器外的網路驅動形成的網路,endpoint連線了二者

另外,CMN還提供了2個可插拔的介面,讓使用者可以自己實現驅動然後接入該介面,支援驅動有兩類:網路驅動和IPAM驅動,看看這倆類驅動幹什麼的?

  • Network Drivers: 即真正的網路實現,可以為Docker Engine或其他型別的叢集網路同時提供多種驅動,但是每一個具體的網路只能例項化一個網路驅動。細分為本地網路驅動和遠端網路驅動:

          - 本地網路驅動:對應前面說到的原生網路
          - 遠端網路驅動:對應前面說的自定義網路

    IPAM Drivers — 構建docker網路的時候,每個docker容器如果不手動指定的話是會被分配ip地址的,這個分配的任務就是由該驅動完成的,同樣的,Docker Engine還是給我們提供了預設的實現。

整個的原理模型圖如下,參見官網:

參考:https://success.docker.com/article/networking
(一定要好好看看這篇文章,我英文不行看了整整2天,很有收穫)

好了,收,開始真正進入docker網路的學習,我們挑2個代表性的網路一起研究下

單機網路---brige型別的網路

原理如下圖(摘自https://success.docker.com/article/networking):

接下來聽我慢慢道來,我們先按照步驟走一遍,然後再細摳裡面的原理

實操:在主機上起兩個docker容器,使用預設網路即bridge網路,容器要使用有作業系統的映象,要不不方便驗證

1)進入任一個容器內
sh-4.2# ip addr
13: eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
inet 172.17.0.3/16 scope global eth0

  附:容器中可能缺少諸多命令,可以在啟動後安裝如下工具:
  yum install net-tools
  yum install iputils
  yum install iproute *

2)在宿主機上檢視介面資訊:

   [root@centos network-scripts]# ip addr
      4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
           inet 172.17.0.1/16 scope global docker0
     14: vetha470484@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
     16: veth25dfcae@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 


3) 在host上檢視docker預設會建立的三個網路

 [root@centos ~]# docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    451a2ff68c71        bridge              bridge              local
    7bd661f0c17f        host                host                local
    6c9bb2d42d95        none                null                local

4)再看下支撐網路背後的驅動,即這個叫“bridge”的bridge型別網路使用的驅動:一個名叫docker0的bridge(網橋)。網橋上掛兩個interface:veth25dfcae, vetha470484

  [root@centos network-scripts]# brctl show
  bridge name     bridge id             STP enabled       interfaces
  docker0         8000.0242dee689ea     no                veth25dfcae
                                                          vetha470484

附:可能缺少命令,可以在啟動後安裝如下工具:

# yum install bridge-utils

#ln -s /var/run/docker/netns/ /var/run/netns ---用來在host上檢視所有的namespace,預設情況下ip netns show顯示的是/var/run/netns中的內容,但是Docker啟動後會清除

解析】

看容器(Sandbox), 介面的number是13的那個,他名字是eth0, 然後他@if14,這個就是endpoint,那麼這個if14是誰?

看主機,有個網橋叫做docker0,有兩個interface 他們的master是docker0,並且這兩個interface的number分別是14,16,並且分別@if13和@if15,是的,if13正是容器中的介面,同理if14也是另一個容器中的介面,也就是說在host上的veth介面(NO.14)和容器中的eth介面(NO.13)正是一對veth pair,至此Endpoint作為容器和nework的連線的任務達成了。而docker0正是名叫bridge的Network的驅動。

最後,看一下路由吧
容器1:

sh-4.2# ip route
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3


表示:目的是172.17的流量交給eth0出去,然後交給閘道器172.17.0.1,也就是docker0

宿主機:

[root@centos ~]# ip route
default via 192.168.12.2 dev ens33 proto dhcp metric 100 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
192.168.12.0/24 dev ens33 proto kernel scope link src 192.168.12.132 metric 100


表示:目的是172.17的流量從docker0出去,
預設的交給ens33介面給閘道器192.168.12.2(因為我的宿主機是個虛擬機器,所以還是個小網ip),也就是說如果訪問的是同網段(如加入同一網路的其他容器)則交給網橋docker0內部轉發,否則走向世界

另外:詳細的可以 看一下bridge網路,可以網路中有兩個容器,ip,mac都有

[root@centos ~]# docker network inspect bridge
    {
        "Name": "bridge",
        "Driver": "bridge", 
        "IPAM": {     //負責給容器分配ip地址 
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },

        "Containers": {
            "9161f717c07ac32f96b1ede19d21a56a63f17fb69a63627f66704f5cec01ca27": {
                "Name": "server.1.oeep0sn0121wrvrw3aunmf9ww",
                "EndpointID": "5083992493b0a69fedb2adc02fe9c0aa61e59b068e16dd9371ec27e28d7d088c",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",""
            },
            "fb67b65aa43619779d0d4f9d2005815aea90586f0aba295436431f688239562b": {
                "Name": "fervent_ritchie",
                "EndpointID": "e402fa0f99f60199c8ba50263173ef3bc14ca75dbb597d2cbcd813dd4f8706f7",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
            }
        },
View Code

插播:bridge的原理
在容器技術中,bridge扮演了一個非常重要的角色,懂得bridge的原理可以很好的定位網路問題,這裡就不展開討論,在我的[爬坑]系列有講述,你只需要記住:不要把bridge想複雜,bridge是一個橋作為master,可以往橋上掛很多型別及個數的interface介面,當橋上有一個介面接收到資料後,只要不是給橋所在的宿主機本身,則橋會內部轉發,資料會從其餘介面同步冒出來


】:
1,容器連線外網--OK

sh-4.2# ping www.baidu.com
PING www.a.shifen.com (115.239.210.27) 56(84) bytes of data.
64 bytes from 115.239.210.27 (115.239.210.27): icmp_seq=1 ttl=127 time=5.38 ms


注:這裡面要說明一下,從容器發外網的egress流量之所以能順利得到應答,是因為它在出去的時候經過了iptables的NAT表,就是這裡:

# iptables -t nat -L
...
Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.17.0.0/16        anywhere            
MASQUERADE  all  --  172.18.0.0/16        anywhere 


這裡如果對iptables的原理不是很清楚的,推薦這位大牛的部落格:
http://www.zsythink.net/archives/1199/


2,容器連線另一個容器--OK

sh-4.2# ping 172.17.0.2 -c 2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=95.9 ms


3,容器如果想要被外網可以訪問的到,需要在起容器的時候指定--publish/-p,即在host上找一個port與容器對映,這個host將代表容器對外打交道,這裡就不展開了,網上資料很多,上面貼出的連結中也有講解。

單機網路---overlay型別的網路

上一個章節我們沒有細說容器和linux的namespace之間的關係,因為在overlay型別的網路中,大量使用了linux的namespace技術,所以我們再展開講。

overlay這個詞不是docker家的,一直以來就有overlay型別的網路,意/譯為"覆蓋"型網路,其中VxLan技術就可以認為是一種overlay型別網路的實現,而在docker的overlay網路,使用的正是Vxlan技術。所以我們再跑偏一下,講講VxLAN

https://www.cnblogs.com/shuiguizi/p/10923841.html

水鬼子:簡言之,可以暫時這麼理解,宿主機上有一個叫做VTEP的元件。它的ip就是宿主機的ip,負責和其他宿主機連通形成隧道。這樣宿主機上的VM就可以利用該隧道和其他host上的VM溝通了,然後還並不感知該隧道的存在。也許這麼說並不嚴謹,但是有助於對overlay網路的理解。

另外,為了學習vxlan網路以及容器對vxlan網路的應用,我做了大量的實驗,遇到了很多很多的坑,需要的可以參考:.[爬坑系列]之VXLan網路實現

oK,再“收”,回到docker 網路

前情提要:
我們也可以直接使用docker 提供的overlay驅動建立overlay網路,然後建立容器加入到該網路,但如果是Docker Engine 1.12之前,還需要一個k-v型別的儲存介質。Docker Engine 1.12之後的由於集成了一個叫做“網路控制平面(control plane)”的功能,則不需要額外的儲存介質了。在這裡為了更好的理解,我們選擇前者,大致的步驟如下:

操作】
1,安裝啟動etcd,安裝方法
2,在一個host上建立overlay型別的網路,並建立容器使用該網路
3,在另一個容器上也可以看到該網路,然後也加入這個網路

root@master ~]# docker network ls
NETWORK IDNAMEDRIVERSCOPE
731d1b63b387ov_net2overlayglobal

分別在兩個節點上建立兩個docker 容器
master:
docker run -ti -d --network=ov_net2 --name=centos21 centos:wxy /bin/sh

minion:
docker run -d --name nginx --network=ov_net2 nginx22

注:更詳細的步驟網上很多,另外想要看我自己搭建的過程中遇到的坑等,請參見【爬坑系列】之docker的overlay網路配置

這裡我只想展示overlay網路的深層實現,包括和namespace,vxlan的關係等

【解析】

1)看一下namespace以及配置
ln -s /var/run/docker/netns/ /var/run/netns

//一個容器建立完了,就多了兩個namespace
[root@master ~]# ip netns
a740da7c2043 (id: 9)
1-731d1b63b3 (id: 8)

第一個namespace:其實就是docker容器本身,它有兩個介面,都是veth pair型別,對應的兄弟介面分別位於另一個namespace和系統namespace

root@master ~]# ip netns exec a740da7c2043 ip addr
44: eth0@if45: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default 
    link/ether 02:42:0a:00:01:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.1.2/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:fe00:102/64 scope link 
       valid_lft forever preferred_lft forever
46: eth1@if47: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:12:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet 172.18.0.4/16 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe12:4/64 scope link 
       valid_lft forever preferred_lft forever

第二個namespace:他的作用是專門用來做vxlan轉發的,核心是一個bro橋,橋上有兩個介面,一個與容器(第一個ns )相連,另一個vxlan1介面充當 vxlan網路的vtep

[root@master ~]# ip netns exec 1-731d1b63b3 ip addr
2: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default 
    link/ether 12:54:57:62:92:74 brd ff:ff:ff:ff:ff:ff
    inet 10.0.1.1/24 scope global br0
       valid_lft forever preferred_lft forever
    inet6 fe80::842a:a1ff:fec8:da3b/64 scope link 
       valid_lft forever preferred_lft forever
43: vxlan1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UNKNOWN group default 
    link/ether 12:54:57:62:92:74 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::1054:57ff:fe62:9274/64 scope link 
       valid_lft forever preferred_lft forever
45: veth2@if44: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UP group default 
    link/ether ae:a1:58:8c:c2:0a brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::aca1:58ff:fe8c:c20a/64 scope link 
       valid_lft forever preferred_lft forever

結論:docker的overlay網路 = 2namespace + vxlan