【Docker】關於Docker網絡隔離與通信詳解


一、Docker的網絡概念

docker受一個github上的issue啟發,引入了容器網絡模型(container network model,CNM),容器網絡模型主要包含了3個概念

  • network:網絡,可以理解為一個Driver,是一個第三方網絡棧,包含多種網絡模式:單主機網絡模式(none、host、bridge,joined container),多主機網絡模式(overlay、macvlan、flannel)

  • sandbox:沙盒,它定義了容器內的虛擬網卡、DNS和路由表,是network namespace的一種實現,是容器的內部網絡棧

  • endpoint:端點,用於連接sandbox和network

我們可以類比傳統網絡模型,將network比作交換機,sandbox比作網卡,endpoint比作接口和網線,另外,docker在創建容器時,先調用控制器創建sandbox對象,再調用容器運行時為容器創建network namespace

二、Docker的網絡模式

這里我們先討論docker的單主機網絡模式,它包括以下4類:host、bridge、none、joined-containe

2.1、Host模式

docker不會為容器創建獨有的network namespace;使用宿主機的默認網絡命名空間,共享一個網絡棧;表現為容器內和宿主機的IP一致;這種模式用於網絡性能較高的場景,但安全隔離性相對差一些。

2.2、Bridge模式

橋接模式,有點類型VM-NAT,dockerd進程啟動時會創建一個docker0網橋,容器內的數據通過這個網卡設備與宿主機進行數據傳輸。

虛擬網橋的工作方式和物理交換機類似,這樣主機上的所有容器就通過交換機連在了一個二層網絡中。從 docker0子網中分配一個 IP 給容器使用,並設置 docker0 的 IP 地址為容器的 默認網關。在主機上創建一對虛擬網卡 veth pair設備,Docker 將 veth pair 設備的一端放在新創建的容器中,並命名為 eth0(容器的網卡),另一端放在主機中,以 vethxxx這樣類似的名字命名,並將這個網絡設備加入到 docker0 網橋中。 bridge模式是 docker 的默認網絡模式,不寫 –net參數,就是 bridge模式。使用 docker run -p時,docker 實際是在 iptables做了 DNAT規則,實現端口轉發功能。可以使用 iptables -vnL查看。

docker會為容器創建獨有的network namespace,也會為這個命名空間配置好虛擬網卡,路由,DNS,IP地址與iptables規則(也就是sandbox的內容)。

2.3、None模式

none模式可以說是橋接模式的一種特例,docker會為容器創建獨有的network namespace ,但不會為這個命名空間准備虛擬網卡,IP地址,路由等,需要用戶自己配置。

2.4、joined-container 模式

容器共享模式,這種模式是host模式的一種延伸,一組容器共享一個network namespace;對外表現為他們有共同的IP地址,共享一個網絡棧;kubernetes的pod就是使用的這一模式。

關於跨主機的docker網絡通信,包含overlay、macvaln,又包含calico、flannel、weave等方案,不過跨主機的docker網絡管理更多的是交給kubernetes或swarm等編排工具去實現了。

三、Docker網絡對象和網絡模式的關系

回顧docker網絡對象和網絡模式的關系其實就是下面這一張表格,每個容器在network namespace中的占比決定了采用哪種網絡模式

四、Iptables的使用

4.1、iptables語法

[root@localhost ~]# iptables -h
iptables v1.4.21

Usage: iptables -[ACD] chain rule-specification [options]
       iptables -I chain [rulenum] rule-specification [options]
       iptables -R chain rulenum rule-specification [options]
       iptables -D chain rulenum [options]
       iptables -[LS] [chain [rulenum]] [options]
       iptables -[FZ] [chain] [options]
       iptables -[NX] chain
       iptables -E old-chain-name new-chain-name
       iptables -P chain target [options]
       iptables -h (print this help information)

Commands:
Either long or short options are allowed.
  --append  -A chain        Append to chain
  --check   -C chain        Check for the existence of a rule
  --delete  -D chain        Delete matching rule from chain
  --delete  -D chain rulenum
                Delete rule rulenum (1 = first) from chain
  --insert  -I chain [rulenum]
                Insert in chain as rulenum (default 1=first)
  --replace -R chain rulenum
                Replace rule rulenum (1 = first) in chain
  --list    -L [chain [rulenum]]
                List the rules in a chain or all chains
  --list-rules -S [chain [rulenum]]
                Print the rules in a chain or all chains
  --flush   -F [chain]      Delete all rules in  chain or all chains
  --zero    -Z [chain [rulenum]]
                Zero counters in chain or all chains
  --new     -N chain        Create a new user-defined chain
  --delete-chain
            -X [chain]      Delete a user-defined chain
  --policy  -P chain target
                Change policy on chain to target
  --rename-chain
            -E old-chain new-chain
                Change chain name, (moving any references)
Options:
    --ipv4  -4      Nothing (line is ignored by ip6tables-restore)
    --ipv6  -6      Error (line is ignored by iptables-restore)
[!] --protocol  -p proto    protocol: by number or name, eg. `tcp'
[!] --source    -s address[/mask][...]
                source specification
[!] --destination -d address[/mask][...]
                destination specification
[!] --in-interface -i input name[+]
                network interface name ([+] for wildcard)
 --jump -j target
                target for rule (may load target extension)
  --goto      -g chain
                              jump to chain with no return
  --match   -m match
                extended match (may load extension)
  --numeric -n      numeric output of addresses and ports
[!] --out-interface -o output name[+]
                network interface name ([+] for wildcard)
  --table   -t table    table to manipulate (default: `filter')
  --verbose -v      verbose mode
  --wait    -w [seconds]    maximum wait to acquire xtables lock before give up
  --wait-interval -W [usecs]    wait time to try to acquire xtables lock
                default is 1 second
  --line-numbers        print line numbers when listing
  --exact   -x      expand numbers (display exact values)
[!] --fragment  -f      match second or further fragments only
  --modprobe=<command>      try to insert modules using this command
  --set-counters PKTS BYTES set the counter during insert/append
[!] --version   -V      print package version.

4.2、阻止其他主機的ping請求

iptables -A INPUT -p icmp -j REJECT

4.3、開放本機的9501端口

iptables -I INPUT -p tcp --dport 9501 -j ACCEPT
iptables -I INPUT -p udp --dport 9501 -j ACCEPT

4.4、禁止本機訪問外部web服務

iptables -A OUTPUT -p tcp --dport 80 -j REJECT

使用 iptables -L 可以查看已設置的規則,iptables -D 可以刪除規則,iptables 命令執行完是即時生效的,但是如果主機重啟,已設置的規則就會丟失,這里可以使用 iptables-saveiptables-restore 。iptables-save 將現有規則保存成文件,iptables-restore 從文件中恢復規則。

4.5、docker容器

docker run -d --name redis01 -p 6380:6379 redis

該命令執行后,docker 會在 iptables 自定義鏈 DOCKER 中定義轉發規則,如果此時系統的 net.ipv4.ip_forward 為0,該命令執行完會提示:WARNING: IPv4 forwarding is disabled. Networking will not work,只需打開該配置就行了,無需重啟容器。此時查看 DOCKER 鏈可以看到添加了一條允許所有來源轉發到6379端口的流量,用 redis-cli 也可以順利連上

開發中,經常會遇到容器里面放問宿主機的情況,除了使用 host.docker.internal 之外,還可以配置 extra_hosts 解決,因為 docker0 與 宿主機是相通的,直接用 ifconfig 查看宿主機 en0 網卡的ip地址,配置到 extra_hosts 即可,如:

version: '3'

networks:
  web-network:
    driver: bridge

services:
  fpm:
    build:
      context: ./fpm
    ports:
      - '8080:8080'
    networks:
      - web-network
    extra_hosts:
      - "test.com:192.168.1.100"

五、Docker與Iptables

Docker提供了bridge, host, overlay等多種網絡,同一個Docker宿主機上同時存在多個不同類型的網絡。位於不同網絡中的容器,彼此之間是無法通信的。Docker容器的跨網絡隔離與通信,是借助了iptables的機制。我們知道,iptables的filter表中默認划分為IPNUT, FORWARD和OUTPUT共3個鏈,詳情請參考 iptables及其過濾規則。Docker在FORWARD鏈中,還額外提供了自己的鏈,以實現bridge網絡之間的隔離與通信。

5.1、 Docker在iptables的filter表中的鏈

在2015.12之前,Docker只額外提供了DOCKER鏈。在此之后,直到Docker 17.06.0(2017.6)之前的版本中,Docker提供了如下2個鏈:

DOCKER
DOCKER-ISOLATION

在Docker 17.06.0(2017.6)及之后,Docker 18.03.1(2018.4)及之前的版本中,Docker提供了如下3個鏈:

DOCKER
DOCKER-ISOLATION
DOCKER-USER

查看Docker的iptables如下:

Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER-USER all -- 0.0.0.0/0 0.0.0.0/0
DOCKER-ISOLATION-STAGE-1 all -- 0.0.0.0/0 0.0.0.0/0
DOCKER all -- 0.0.0.0/0 0.0.0.0/0

在Docker 18.05.0(2018.5)及之后的版本中,提供如下4個chain:

DOCKER
DOCKER-ISOLATION-STAGE-1
DOCKER-ISOLATION-STAGE-2
DOCKER-USER

目前,Docker默認對宿主機的iptables設置規則完整一覽:

iptables -N DOCKER
iptables -N DOCKER-ISOLATION-STAGE-1
iptables -N DOCKER-ISOLATION-STAGE-2
iptables -N DOCKER-USER
iptables -A FORWARD -j DOCKER-USER
iptables -A FORWARD -j DOCKER-ISOLATION-STAGE-1
iptables -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -o docker0 -j DOCKER
iptables -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
iptables -A FORWARD -i docker0 -o docker0 -j ACCEPT
iptables -A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
iptables -A DOCKER-ISOLATION-STAGE-1 -j RETURN
iptables -A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
iptables -A DOCKER-ISOLATION-STAGE-2 -j RETURN
iptables -A DOCKER-USER -j RETURN

5.2、Docker的DOCKER鏈

僅處理從宿主機到docker0的IP數據包。

5.3、Docker的DOCKER-ISOLATION鏈

可以看到,為了隔離在不同的bridge網絡之間的容器,Docker提供了兩個DOCKER-ISOLATION階段實現。DOCKER-ISOLATION-STAGE-1鏈過濾源地址是bridge網絡(默認docker0)的IP數據包,匹配的IP數據包再進入DOCKER-ISOLATION-STAGE-2鏈處理,不匹配就返回到父鏈FORWARD。在DOCKER-ISOLATION-STAGE-2鏈中,進一步處理目的地址是bridge網絡的IP數據包,匹配的IP數據包表示該IP數據包是從一個bridge網絡的網橋發出,到另一個bridge網絡的網橋,這樣的IP數據包來自其他bridge網絡,將被直接DROP;不匹配的IP數據包就返回到父鏈FORWARD繼續進行后續處理。

5.4、Docker的DOCKER-USER鏈

Docker啟動時,會加載DOCKER鏈和DOCKER-ISOLATION(現在是DOCKER-ISOLATION-STAGE-1)鏈中的過濾規則,並使之生效。絕對禁止修改這里的過濾規則。

如果用戶要補充Docker的過濾規則,強烈建議追加到DOCKER-USER鏈。DOCKER-USER鏈中的過濾規則,將先於Docker默認創建的規則被加載,從而能夠覆蓋Docker在DOCKER鏈和DOCKER-ISOLATION鏈中的默認過濾規則。例如,Docker啟動后,默認任何外部source IP都被允許轉發,從而能夠從該source IP連接到宿主機上的任何Docker容器實例。如果只允許一個指定的IP訪問容器實例,可以插入路由規則到DOCKER-USER鏈中,從而能夠在DOCKER鏈之前被加載。示例如下:

只允許192.168.1.1訪問容器

iptables -A DOCKER-USER -i docker0 ! -s 192.168.1.1 -j DROP

只允許192.168.1.0/24網段中的IP訪問容器

iptables -A DOCKER-USER -i docker0 ! -s 192.168.1.0/24 -j DROP

只允許192.168.1.1-192.168.1.3網段中的IP訪問容器(需要借助於iprange模塊)

iptables -A DOCKER-USER -m iprange -i docker0 ! --src-range 192.168.1.1-192.168.1.3 -j DROP

5.5、Docker在iptables的nat表中的規則

為了能夠從容器中訪問其他Docker宿主機,Docker需要在iptables的nat表中的POSTROUTING鏈中插入轉發規則,示例如下:

iptables -t nat -A POSTROUTING -s 172.18.0.0/16 -j MASQUERADE

上述配置,還進一步限制了容器實例的IP范圍,這是為了區分Docker宿主機上有多個bridge網絡的情況。

5.6、Docker中禁止修改iptables過濾表

dockerd啟動時,參數--iptables默認為true,表示允許修改iptables路由表。要禁用該功能,可以有兩個選擇:設置啟動參數--iptables=false

修改配置文件/etc/docker/daemon.json,設置"iptables": "false";然后執行systemctl reload docker重新加載

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM