目錄
- Docker 網絡
- 網絡類型
- none 網絡
- host 網絡
- bridge 網絡
- 自定義網絡
- 容器間通信
- IP 通信
- Docker DNS Server
- joined 容器
- 容器與外部通信
- 容器訪問外部
- 外部訪問容器
Docker 網絡
- 學習Docker提供的幾種原生網絡
- 如何創建自定義網絡
- 容器間通信,容器于外界交互
Docker 安裝時會自動在 host 上創建三個網絡,查看:
docker network ls
# bridge host none
網絡類型
none 網絡
none網絡就是什么都沒有的網絡。掛在這個網絡下的容器除了lo,沒有其他任何網卡。容器創建時,可以通過 --network=none指定使用none網絡
使用場景:封閉隔離,一些對安全性要求高并且不需要聯網的應用可以使用,比如某個容器的唯一用途是生成隨機密碼,就可以放到none網絡中避免密碼被竊取。
host 網絡
連接到 host 網絡的容器共享 Docker host 的網絡,容器的網絡配置與 host 完全一樣。通過 --network=host 指定使用 host 網絡。
docker run -it --network=host httpdip l # 查看
- 直接使用Docker host的網絡最大的好處就是性能,如果容器對網絡傳輸效率有較高要求,則可以選擇host網絡。
- 這樣就犧牲一些靈活性,比如要考慮端口沖突問題,Docker host上已經使用的端口就不能再用了。
- Docker host 的另一個用途是讓容器可以直接配置host網路,比如某些跨host的網絡解決方案,其本身也是以容器方式運行的,這些方案需要對網絡進行配置,比如管理iptables
bridge 網絡
Docker安裝時會創建一個命名為 docker0 的 Linux bridge。如果不指定 --network,創建的容器默認都會掛到docker0上。
brctl showip a
創建容器后,會有一個新的網絡接口(比如veth28)被掛到 docker0 上,這個接口就是新容器的虛擬網卡。
容器本身會有一個網卡(比如eth0@if34)。
實際上 eth0@if34 和 veth28 是一對 veth pair。veth pair是一種成對出現的特殊網絡設備,可以把它們想象成由一根虛擬網線連接起來的一對網卡,網卡的一頭(eth0@if34)在容器中,另一頭(veth28)掛在網橋 docker0 上,其效果就是將 eth0@if34 也掛在了 docker0 上。
自定義網絡
除了以上自動創建的網絡,用戶也可以根據業務需要創建user-defined網絡。
Docker 提供三種 user-defined 網絡驅動:bridge、overlay 和 macvlan。
通過 bridge 驅動創建類似前面默認的bridge網絡:
docker network create --driver bridge my_net
docker network insepect my_net
# 自己指定網段: 在創建網段時指定 --subnet和 --gateway參數
docker network create --driver bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_net2
容器要使用新的網絡,需要在啟動時通過 --network指定:
docker run -it --network=my_net2 容器ID/Name
容器的IP都是 docker 自動從 subnet 中分配,指定靜態 IP: 通過 --ip 指定(只有使用 --subnet 創建的網絡才能指定靜態 IP)。
docker run -it --network=my_net2 --ip 172.22.16.8 容器ID/Name
如果兩個容器都掛在my_net2上,可以互通。所以,同一網絡中的容器、網關之間都是可以通信的。
my_net2與默認bridge網絡能通信嗎?
兩個網絡屬于不同的網橋,是不能通信的。
host上對每個網絡都有一條路由,同時操作系統上打開了ip forwarding, host就成了一個路由器,掛接在不同網橋上的網絡就能夠相互通信。
# 查看 host 上的路由表
ip r
# 查看 ip forwarding:
sysctl net.ipv4.ip_forward # net.ipv4.ip_forward = 1 表示已經啟動
# 查看 iptables
iptables-save
# -A DOCKER-ISOLATION -i br-5d863e9f78b6-o docker0-j DROP -A DOCKER-ISOLATION -i docker0-o br-5d863e9f78b6-j DROPiptables
# DROP掉了網橋docker0與br-5d863e9f78b6之間雙向的流量# 為容器添加一塊net_my2的網卡,就能互通。這個可以通過docker network connect命令實現
docker network connect my_net2 容器ID
容器間通信
IP 通信
從前面的例子可以得出這樣一個結論:兩個容器要能通信,必須要有屬于同一個網絡的網卡。
滿足這個條件后,容器就可以通過 IP 交互了。具體做法是在容器創建時通過 --network 指定相應的網絡,或者通過 docker network connect將現有容器加入到指定網絡。
Docker DNS Server
通過 IP 訪問容器雖然滿足了通信的需求,但還是不夠靈活。因為在部署應用之前可能無法確定 IP,部署之后再指定要訪問的 IP 會比較麻煩。這個問題,可以通過 docker 自帶的 DNS 服務解決。
docker daemon 實現了一個內嵌的 DNS server,使容器可以直接通過“容器名”通信。只要在啟動時用 --name 為容器命名就可以了。
使用 docker DNS 有個限制:只能在 user-defined 網絡中使用。默認的 bridge 網絡是無法使用 DNS 的。
docker run -it --network=my_net2 --name=bbox1 busybox
docker run -it --network=my_net2 --name=bbox2 busyboxping -c 3 bbox1
joined 容器
joined 容器是另一種實現容器間通信的方式。
joined 容器非常特別,它可以使兩個或多個容器共享一個網絡棧,共享網卡和配置信息,joined 容器之間可以通過 127.0.0.1 直接通信。
使用場景:
- 不同容器中的程序希望通過 loopback 高效快速地通信,比如 Web Server 與 App Server。
- 希望監控其他容器的網絡流量,比如運行在獨立容器中的網絡監控程序。
# 創建web1容器
docker run -d -it --name=web1 httpd
# 創建busybox容器并通過 --network=container:web1指定joined容器為web1
docker run -it --network=container:web1 busybox
busybox 和 web1 的網卡 mac 地址與 IP 完全一樣,它們共享了相同的網絡棧。busybox 可以直接用 127.0.0.1 訪問 web1 的 http 服務。
容器與外部通信
容器訪問外部
如果 docker host 可以訪問外網,容器默認就能訪問外網(這里外網指的是容器網絡以外的網絡環境,并非特指Internet)。
通過NAT, docker實現了容器對外網的訪問。
# host測試
ping -c 3 www.baidu.com
# 登錄容器測試
docker run -it busybox
ping -c 3 www.baidu.com
busybox 位于 docker0 這個私有bridge網絡中(172.17.0.0/16)?,當 busybox 從容器向外 ping 時,數據包是怎樣到達bing.com的呢?
這里的關鍵就是NAT。docker host上的iptables規則:
iptables -t nat -S
# -A POSTROUTING -s 172.17.0.0/16 ! -o docker0-j MASQUERADE
# 如果網橋docker0收到來自172.17.0.0/16網段的外出包,把它交給MASQUERADE處理。而MASQUERADE的處理方式是將包的源地址替換成host的地址發送出去,即做了一次網絡地址轉換(NAT)?。
網絡地址轉換(NAT):是一種在IP網絡中用于重用地址空間并隱藏內部網絡結構的技術。
解決IPv4地址短缺問題,同時提高內網安全性,通過單一或少量的公共IP地址代表整個內部網絡與外部通信。
內到外:當內部設備訪問外部網絡時,NAT設備(通常是路由器或防火墻)將源IP地址從私有地址轉換為公有地址。
外到內:響應數據包到達NAT設備時,根據轉換表將目標地址從公有地址轉換回相應的私有地址。
端口地址轉換(PAT):最常用的NAT形式,多個內部設備可以共享一個公有IP地址,通過不同的端口號區分不同的連接。
類型:
靜態NAT:一對一映射,每個內部IP地址永久映射到一個特定的外部IP地址,適用于需要直接從外部訪問的服務器。
動態NAT:內部私有地址池與外部公有地址池之間進行一對一臨時映射,每次連接時分配,連接結束后釋放。
端口地址轉換(PAT):一對多映射,多個內部IP地址通過單一或少量公有IP地址的不同端口對外通信,是最節省IP地址的方式。
通過tcpdump查看地址是如何轉換的。先查看docker host的路由表:
# 查看 default 默認路由通過enp0s3發出去,所以我們要同時監控enp0s3和docker0上的icmp(ping)數據包
ip rtcpdump -i docker0 -n icmp
docker0收到busybox的ping包,源地址為容器IP 172.17.0.2,這沒問題,交給MASQUERADE處理。這時,在enp0s3上我們看到了變化:
# ping包的源地址變成了enp0s3的IP 10.0.2.15
tcpdump -i enp0s3 -n icmp
這就是iptable NAT規則處理的結果,從而保證數據包能夠到達外網:
- busybox 發送ping包:172.17.0.2 > www.baidu.com。
- docker0 收到包,發現是發送到外網的,交給NAT處理。
- NAT將源地址換成 enp0s3 的IP:10.0.2.15 > www.baidu.com。
- ping包從 enp0s3 發送出去,到達 www.baidu.com。
外部訪問容器
外網訪問容器:通過端口映射
docker可將容器對外提供服務的端口映射到host的某個端口,外網通過該端口訪問容器。容器啟動時通過-p參數映射端口:
docker run -d -p 80 httpd
容器啟動后,可通過docker ps或者docker port查看到host映射的端口。在上面的例子中,httpd容器的80端口被映射到host 32773上,這樣就可以通過:<32773>訪問容器的Web服務了
除了映射動態端口,也可在 -p中指定映射到host某個特定端口,例如可將80端口映射到host的8080端口
docker run -d -p 8080:80 httpd
curl hostId:8080
每一個映射的端口,host都會啟動一個docker-proxy進程來處理訪問容器的流量,host 查看:
ps -ef | grep docker-proxy
過程是這樣的:
- docker-proxy監聽host的32773端口
- 當curl訪問10.0.2.15:32773時,docker-proxy轉發給容器172.17.0.2:80
- httpd容器響應請求并返回結果