如何管理linux設備上的bridge(網橋)和docker bridge

什麼是bridge(網橋)?

bridge是一種技術,能夠把一個linux設備上的兩塊網卡橋接在一塊兒,如何對外表現爲一個大的網卡接口,這樣作有不少用途html

好比你有兩臺設備,可是又沒有路由器,那麼把他們橋接在一塊兒,能夠共享其中一臺的網絡,這樣兩臺均可以上網,這兩臺設備也能夠是vm,不必定是物理設備linux

還有一種用途,就是監控兩個設備的網絡流量,好比用wireshark來監控他們間的流量nginx

總的來說,橋接就是將一臺計算機插入到已經與較大網絡(例如Internet)創建鏈接的另外一臺計算機上,而後讓這臺橋接上去的計算機可使用聯網計算機的鏈接。git

哪些軟件使用了橋接技術

bridge在vm(virtual machine)的領域很是有用,好比docker, k8s, multipass等。github

docker能夠幫忙建立不少獨立的虛擬容器,那這些容器怎麼上網,相互之間怎麼通訊呢,docker提供多種途徑,其中一種就是bridge。docker

查看設備上有哪些bridge,能夠用brctl命令,若是你的沒有這個命令,可用apt或者yum安裝** sudo apt install bridge-utils**vim

查看bridge

brctl show數組

bridge name bridge id       STP enabled interfaces
docker0     8000.0242a8576763   no

上面這個是docker的bridge(若是你設備上裝有docker的話 就能夠看到)安全

k8s的bridge,k8s也能夠經過bridge來通訊服務器

bridge name bridge id       STP enabled interfaces
cbr0        8000.125a96a00d6c   no      veth461c2da3
                            veth4cd7f98e
                            veth62b53b2e
                            vethb0c3d926

k8s建立名爲cbr0的網橋,併爲每一個 pod 建立了一個 veth 對,每一個 pod 的主機端都鏈接到cbr0。 這個 veth 對的 pod 端會被分配一個 IP 地址,該 IP 地址隸屬於節點所被分配的 IP 地址範圍內。節點的 IP 地址範圍則經過配置或控制器管理器來設置。cbr0被分配一個 MTU,該 MTU 匹配主機上已啓用的正常接口的最小 MTU (來自k8s的官方說明)

multipass的bridge,它也是用bridge來通訊

bridge name bridge id       STP enabled interfaces      
mpqemubr0   8000.065897b80a53   no      mpqemubr0-dummy
                            tap-166f18d0005
                            tap-1e3511f1cea
                            tap-2cc8b34e9b0
                            tap-313bf980cb9
                            tap-416f9717aeb
                            tap-4524e0d94ea
                            tap-5abadd4a100
                            tap-62fe19d7992
                            tap-c1b397bb0d

建立一個bridge

brctl addbr br0

上面命令建立一個名爲br0的橋

接下來咱們能夠把已有的網卡接口綁定到這個橋上,在這以前能夠看看咱們有有哪些網卡接口

能夠用這個命令查看

ip addr show

假設上面查出來,有eth0和eth1兩個網卡接口,下面咱們把他們用命令綁定到一塊兒

brctl addif br0 eth0 eth1 # eth0和eth1的順序不重要,不影響結果

這是咱們再來綁定的結果

brctl show

bridge name     bridge id               STP enabled     interfaces
br0             8000.001ec952d26b       yes             eth0
                                                        eth1

就是說eth0和eth1綁定到了br0這個橋上了

上面命令是臨時建立這種關係,若是要永久保留下來,須要在/etc/network/interfaces這個配置文件裏面修改

sudo vim /etc/network/interfaces

# The loopback network interface
 auto lo 
 iface lo inet loopback
# 上面這兩行是系統默認有的, 意思是定義一個迴環設備,而且自動啓動

 # Set up interfaces manually, avoiding conflicts with, e.g., network manager
 iface eth0 inet manual # 定義eth0接口 手動啓動
 iface eth1 inet manual # 定義eth1接口 手動啓動

 # Bridge setup
 iface br0 inet dhcp # 定義br0這個接口,而且從dhcp獲取ip
    bridge_ports eth0 eth1 # 綁定上面定義那兩個接口進來

# 若是但願給這個bridge配置靜態ip,能夠用下面這個段配置代替上面的bridge配置
 #iface br0 inet static
 #   bridge_ports eth0 eth1
 #       address 192.168.1.2
 #       broadcast 192.168.1.255
 #       netmask 255.255.255.0
 #       gateway 192.168.1.1

上面個並設置的接口是手動啓動,要經過下面的命令來啓動

ifup br0

橋接無線網卡和以太網卡

有不少無線路由都會拒絕沒有認證過的幀,那麼咱們的以太網卡雖然能經過bridge借用無線網卡鏈接網絡,可是因爲它發的幀並無通過無線路由的認證(由於以太網卡發出去的frames裏面MAC地址不是無線網卡向無線路由註冊過的地址),因此會被拒絕掉。

這個時候要藉助一種叫作etable的程序,ebtables本質上相似於iptables,不一樣之處在於它在OSI模型的數據鏈路層的MAC子層而不是網絡層上運行。這容許更改全部幀的源MAC地址。

要實現這個功能,分兩步走

  1. 配置bridge-utils

2. 配置etable的規則

配置bridge-utils

也是在/etc/network/interfaces裏面配置

pre-up iwconfig wlan0 essid $YOUR_ESSID # wifi的essid(通常和ssid相同)
 bridge_hw $MAC_ADDRESS_OF_YOUR_WIRELESS_CARD # 無線網卡地址
  1. 這個配置的意思是啓動一個叫wlan0的無線網卡鏈接到essid上

2. 設置以太網網卡的MAC地址爲無線網卡的MAC地址

以上這些配置命令的說明能夠從bridge-tuils的文檔裏面找到

配置etable的規則

先安裝etable

apt install ebtables

配置規則(etable的規則和iptables差很少)

# 這條規則是將全部發送到AP的幀的源MAC地址設置爲網橋的MAC地址
ebtables -t nat -A POSTROUTING -o wlan0 -j snat --to-src $MAC_OF_BRIDGE --snat-arp --snat-target ACCEPT
# 下面這兩條配規則,要求你得知道網橋後面的每臺計算機的MAC和IP
ebtables -t nat -A PREROUTING -p IPv4 -i wlan0 --ip-dst $IP -j dnat --to-dst $MAC --dnat-target ACCEPT
ebtables -t nat -A PREROUTING -p ARP -i wlan0 --arp-ip-dst $IP -j dnat --to-dst $MAC --dnat-target ACCEPT

每次手工輸入比較麻煩,也能夠經過腳原本弄,參考

docker的bridge

docker容器的bridge比咱們上面認識到的bridge要複雜

首先docker的提供給容器用的網絡模式有好多種:

  • none: 沒有任何的網卡接口,除了容器自身的loobpack設備lo,一般這種容器用來鏈接定製的網絡驅動
  • host: 給獨立的容器直接使用宿主機的網絡,這種可能會有衝突,容器間沒有隔離。
  • overlay:覆蓋網絡,這種網絡能夠用在swarm集羣和獨立容器(如networking=host)或者多個docker守護進程上,把他們經過覆蓋來相互通訊。
  • macvlan: 這個涉及到vlan這種虛擬局域網的技術,經過macvlan能夠直接給容器分配MAC網卡地址,對於物理網絡來講,它就是一個物理的網卡設備(其實是一個虛擬網卡),容器的守護進程經過這個網卡地址準確的把流量路由給這個容器,至關於容器直接鏈接到咱們的物理網絡
  • third-party network plugin: 第三方網絡插件
  • bridge:docker的bridge,這個是咱們要個講的
container_namespaces.png

看了網上不少關於docker bridge的介紹,上面這個示意圖比較正確,(來自這裏)

  1. 首先docker會在宿主機裏面建立一個docker0的linux bridge
  2. 而後在建立容器的時候使用veth pair對的技術,會對應在宿主虛擬建立一個虛擬網卡,而後這個網卡和容器裏面的網卡一一對應起來,docker的守護(deamon)進程會正確的把發給虛擬網卡的數據路由給對應的容器(一一對應),同時,全部的虛擬網卡會綁定到docker0 這個linux bridge裏面(見上面關於linux bridge的介紹),這樣的話容器之間就能夠相互通訊了

那麼宿主機又是怎麼樣把外部的請求正確的發給veth呢,這是由於在docker0 bridge前面還有一層NAT的技術,經過網絡地址端口轉換的技術,把發給特定端口的數據,轉發給容器,既然如此,那麼就必須綁定docker對外暴露什麼端口,這就是咱們平時定義docker的時候的端口bind乾的事

docker run -p 80:8080 nginx # 把容器的8080端口綁定到宿主機的80端口

如上面這個,把容器的8080端口綁定到宿主機的80端口,那麼在進行NAT路由的時候,就能夠準確找到容器對應的veth:xx,而後veth又對應到容器的eth0:xx,docker deamon就能夠把數組準確的路由進容器裏。

如何查看veth pair的對應關係

查找veth的關係能夠經過ip命令來查

1.在容器裏面執行:

> ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
55: eth0@if56: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

容器裏面編號爲55的接口eth0是@if56

2. 在宿主機裏面也執行ip a

> ip a | grep veth
56: veth243b8f2@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default

宿主機裏面56編號的這個接口是@if55

二者恰好是對應關係,這樣看比較麻煩,能夠用github上這個項目,一個命令列出對應關係

[root@dockervisor-1 ~]# dockerveth
CONTAINER ID    VETH        NAMES
60d27ce962ff    vethe353e93 hopeful_bhaskara
d07a2979e69a    vethe4c3cee silly_meitner
1e8656e195ba    veth1ce04be thirsty_meitner

在宿主機裏面查看DNAT路由規則

下面這條就是個人主機上創建的一個8081到容器80端口的DNAT規則,宿主機就是根據這條規則準確的把數據路由到172.17.0.2這臺容器

> sudo iptables -S -t nat | grep 8081
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80

這個時候,這臺容器是插入到docker0這個橋的,docker0的ip是172.17.0.1

不推薦在生成環境使用docker0 bridge

官方不推薦在生成使用docker0這個bridge,取而代之,建議在建立容器的時候使用--network來自動本身建立的network bridge.

  1. 先在宿主機建立自定義的bridge
docker network create cluster1-net-bridge

2. 建立容器的時候指定bridge

docker run --name nginx -p 80:8080 -d nginx --network cluster1-net-bridge
docker run --name mongo -d mongo:tag --network cluster1-net-bridge

爲何官方會這樣推薦呢,緣由是:

  1. docker0在全部的容器裏面共享配置,包括MTU和防火牆規則等,對於不一樣的集羣咱們可能但願可隔離開來
  2. docker0默認不能經過容器的名稱來通訊,而本身建立的橋,如cluster1-net-bridge,能夠能夠容許經過名稱來通訊,也就是本身建立的bridge具有dns解析功能,而docker0若是須要有這種功能須要用--link來在建立容器的時候指定,若是有複雜的容器關係,那會很是難以維護。
  3. 自定義橋能夠熱插拔,在生產狀態下改變

--link 這個參數在官方文檔中已經被標記爲過時的參數,再也不建議使用

如上面的兩個容器,若是不指定—network,而是用默認的docker0,那麼

在nginx裏面 ping mongo是不行的,而若是掛到cluster-net-bridge這個橋,ping mongo 是能夠的

也就是我能夠在nginx這個容器裏面直接經過 "mongo" 這個名稱來鏈接mongo服務,而不須要經過子網的ip來連.那麼這種默認的約定在運維的時候會很是方便,再結合使用swam和docker-compose.yaml一塊兒來啓動,維護會更方便。

建立docker bridge network

接下來咱們試一下建立兩個docker bridge network, 而後建立新的容器來加入到這兩個network.

docker network create -d bridge --subnet 192.168.5.0/24 --gateway 192.168.5.1 test_bridge1
docker network create -d bridge --subnet 192.168.6.0/24 --gateway 192.168.6.1 test_bridge2

用途 **brctl **看看宿主機連的橋

bridge name bridge id       STP enabled interfaces
br-6edc31410314     8000.02423e98f193   no      
br-c9608a22917c     8000.0242206cc645   no

用docker network ls 看

> docker network ls
brctlNETWORK ID          NAME                DRIVER              SCOPE
6edc31410314        test_bridge1        bridge              local
c9608a22917c        test_bridge2        bridge              local

network Id對應的就是上面bridge name的id部分

用docker network inspect 一個看看, 其實br-6edc31410314就是Id前面部分

> docker network inspect test_bridge1
[
    {
        "Name": "test_bridge1",
        "Id": "6edc31410314709178acb728cd448b17f32f2f6a7646299f0c67369329e81a64",
        "Created": "2020-03-08T20:33:25.480769489+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.5.0/24",
                    "Gateway": "192.168.5.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

一個容器如何連到多個橋接網絡

如今咱們建立4個新的容器分別加入到這兩個network.

# box 1
docker run --name box1 -p 8082:80 -it --rm  --network=test_bridge1 busybox sh
# box 2
docker run --name box2 -p 8083:80 -it --rm  --network=test_bridge1 busybox sh
# box 3
docker run --name box3 -p 8084:80 -it --rm  --network=test_bridge2 busybox sh
# box 4
docker run --name box4 -p 8085:80 -it --rm  --network=test_bridge2 busybox sh

container依次從network獲取的dhcp獲取到ip,依次爲:

box 1: 192.168.5.2 
box 2: 192.168.5.3
box 3: 192.168.6.2
box 4: 192.168.6.3

那麼box 1和box 2是相互通的,box 3和box 4互通

在box 1 ping box 2的ip,能夠通訊

/ # ping 192.168.5.3
PING 192.168.5.3 (192.168.5.3): 56 data bytes
64 bytes from 192.168.5.3: seq=0 ttl=64 time=0.211 ms

在box 1 ping 的容器的名稱 box2,也能夠通訊

/ # ping box2
PING box2 (192.168.5.3): 56 data bytes
64 bytes from 192.168.5.3: seq=0 ttl=64 time=0.177 ms
64 bytes from 192.168.5.3: seq=1 ttl=64 time=0.172 ms

在box1 ping box3的ip, 不能夠通訊

/ # ping 192.168.6.2
PING 192.168.6.2 (192.168.6.2): 56 data bytes

若是box1 要和box3要通訊怎麼辦?把box 3加入到box 1的bridge

docker network connect test_bridge1 box3

再來看看box 3的網卡, box 3多了一個網卡,ip是192.168.5.4

/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:C0:A8:06:02  
          inet addr:192.168.6.2  Bcast:192.168.6.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:172 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:28211 (27.5 KiB)  TX bytes:0 (0.0 B)

eth1      Link encap:Ethernet  HWaddr 02:42:C0:A8:05:04  
          inet addr:192.168.5.4  Bcast:192.168.5.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:43 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:6368 (6.2 KiB)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

box3和box1經過test_bridge1也橋接起來了,能夠經過brctl看看

➜  bin brctl show
bridge name                 bridge id                       STP enabled interfaces
br-6edc31410314     8000.02423e98f193   no                         veth4003153
                            veth8e06926
                                                                                                       vethc2edaec
br-c9608a22917c     8000.0242206cc645   no                         veth5ff4051
                                                                                                       veth62200c6

能夠看到,bridge1橋接了3個網卡,bridge2橋接了2個網卡

/ # ping box3
PING box3 (192.168.5.4): 56 data bytes
64 bytes from 192.168.5.4: seq=0 ttl=64 time=0.210 ms
64 bytes from 192.168.5.4: seq=1 ttl=64 time=0.206 ms
64 bytes from 192.168.5.4: seq=2 ttl=64 time=0.138 ms

這樣 box1就能夠ping 通 box3了

瞭解了以上這些原理以後,那麼咱們能夠設計一個這樣的網絡,把前先後臺的容器網絡隔離開來,保證安全性


0_f39Sdh-13He13vn7.jpg

擴展閱讀:
bridged-network
新手該如何選擇雲服務器
我的玩家和中小企業雲服務器選擇對比

相關文章
相關標籤/搜索