什麼是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地址。
要實現這個功能,分兩步走
- 配置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 # 無線網卡地址
- 這個配置的意思是啓動一個叫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,這個是咱們要個講的
看了網上不少關於docker bridge的介紹,上面這個示意圖比較正確,(來自這裏)
- 首先docker會在宿主機裏面建立一個docker0的linux bridge
- 而後在建立容器的時候使用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.
- 先在宿主機建立自定義的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
爲何官方會這樣推薦呢,緣由是:
- docker0在全部的容器裏面共享配置,包括MTU和防火牆規則等,對於不一樣的集羣咱們可能但願可隔離開來
- docker0默認不能經過容器的名稱來通訊,而本身建立的橋,如cluster1-net-bridge,能夠能夠容許經過名稱來通訊,也就是本身建立的bridge具有dns解析功能,而docker0若是須要有這種功能須要用--link來在建立容器的時候指定,若是有複雜的容器關係,那會很是難以維護。
- 自定義橋能夠熱插拔,在生產狀態下改變
--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了
瞭解了以上這些原理以後,那麼咱們能夠設計一個這樣的網絡,把前先後臺的容器網絡隔離開來,保證安全性