Docker--高級網絡配置


Docker 高級網絡配置

當 Docker 啟動時,會自動在宿主機上創建一個 docker0 虛擬網橋,實際上是Linux 的一個 bridge,可以理解為一個軟件交換機。它會在掛載到它的網口之間進行轉發。

[root@server ~]# ifconfig 
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 0.0.0.0

同時,Docker 隨機分配一個本地未占用的私有網絡(在RFC1918中定義)中的一個地址給 docker0接口。比如典型的 172.17.0.1,掩碼為255.255.0.0。此后啟動的容器內的網口也會自動分配一個網段(172.17.0.0/16)的地址。

當創建一個 Docker 容器的時候,同時會創建一個 veth pair 接口(當數據包發送到一個接口時,另外一個接口也可以接收相同的數據包)。這對接口一端在容器內,即 eth0;另一端在本地並被掛載到docker0 網橋,名稱以veth 開頭(例如 veth4c45933)。通過這種方式,主機可以跟容器通信,容器之間也可以相互通信。Docker 就創建了在主機和所有容器之間的一個虛擬共享網絡。

快速配置指南

Docker 網絡相關的命令列表

只有在Docker 服務啟動的時候才能配置,而且不能馬上生效的有:

-b  BRIDGE  or --bridge=BRIDGE        --指定容器掛載的網橋
--bip=CIDR                     --定制docker0的掩碼
-H SOCKET... or --host=SOCKET...      --Docker服務端接收命令的通道
--icc=true|false                --是否支持容器之間進行通信
--ip-forward=true|false            --請看下文容器之間的通信
--iptables=true|false              --是否允許Docker添加iptables規則
--mtu=BYTES                     --容器網絡中的MTU

既可以在啟動服務時指定,也可以 Docker容器啟動(docker run)時候指定。在Docker 服務啟動的時候指定則會成為默認值,后面執行 docker run 時可以覆蓋設置的默認值:

--dns=IP_ADDRESS...          --使用指定的DNS服務器
--dns-search=DOMAIN...        --指定DNS搜索域
    

只有在 docker run 執行時使用,因為它是針對容器的特性內容:

-h  HOSTNAME  or  --hostname=HOSTNAME        --配置容器主機名
--link=CONTAINER_NAME:ALIAS              --添加到另一個容器的連接
--net=bridge|none|container:NAME_or_ID|host  --配置容器的橋接模式
-p  SPEC  or  --publish=SPEC              --映射容器端口到宿主主機
-P  or  --publish-all=true|false           --映射容器所有端口到宿主主機

配置 DNS

Docker 沒有為每個容器專門定制鏡像,那么怎么自定義配置容器的主機名和DNS配置?秘訣就是它利用虛擬文件來掛載到容器的3個相關的配置文件。

在容器中使用mount命令可以看到掛載信息:

[root@server ~]# docker run --rm --name myCentos -it centos /bin/bash   #創建一個執行完命令自動終止的容器
[root@fb589b253bba /]# mount
...
/dev/mapper/cl-root on /etc/resolv.conf type xfs (rw,relatime,attr2,inode64,noquota)
/dev/mapper/cl-root on /etc/hostname type xfs (rw,relatime,attr2,inode64,noquota)
/dev/mapper/cl-root on /etc/hosts type xfs (rw,relatime,attr2,inode64,noquota)
...

這種機制可以讓宿主機DNS信息發生更新后,所有Docker容器的 dns 配置通過 /etc/resolv.conf 文件立刻得到更新。

如果想要手動指定容器的配置,可以利用下面的選項。

-h HOSTNAME or --hostname=HOSTNAME 設定容器的主機名,它會被寫到容器的/etc/hostname 和 /etc/hosts。
但它在容器外部看不到,既不會在docker ps中顯示,也不會在其它容器的 /etc/hosts看到

[root@server ~]# docker run --rm -h mydocker_test --name myCentos -it centos /bin/bash   #創建一個指定 HOSTNAME的容器
[root@mydocker_test /]# cat /etc/hostname  #查看hostname文件信息
mydocker_test
[root@mydocker_test /]# cat /etc/hosts  #查看hosts文件信息 ...
172.17.0.5    mydocker_test
--link=CONTAINER_NAME:ALIAS   選項會在創建容器的時候,添加一個其他容器 的主機名到/etc/hosts 文件中,讓新容器的進程可以使用主機名 ALIAS 就可以 連接它。

實例參考:https://www.cnblogs.com/yanjieli/p/10218842.html#autoid-7-6-1
--dns=IP_ADDRESS 添加DNS服務器到容器的 /etc/resolv.conf中,讓容器利用這個服務器來解析所有不在 /etc/hosts 中的主機名

[root@server ~]# docker run --rm --dns=114.114.114.114 --name myCentos -it centos /bin/bash   #創建一個容器,並指定dns為114.114.114.114
[root@aa199cd14e2a /]# cat /etc/resolv.conf 
nameserver 114.114.114.114
--dns-search=DOMAIN 設定容器的搜索域,當設定搜索域為 .example.com時,再搜索一個名為 host的主機時,DNS不僅搜索host,還會搜索host.example.com。

注意:如果沒有--dns 和--dns-search選項時,Docker會默認用宿主機上的 /etc/resolv.conf 來配置容器。

容器訪問控制

容器的訪問控制,主要通過Linux上的 iptables 防火牆來進行管理和實現。 

容器訪問外部網絡

容器要想訪問外部網絡,需要本地系統的轉發支持,在linux系統中,檢查轉發是否打開

[root@server ~]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

如果為0,說明沒有開啟轉發,則需要手動打開

[root@server ~]# sysctl -w net.ipv4.ip_forward=1

如果在啟動Docker服務的時候指定 --ip-forward=true,Docker就會自動設定系統的 ip_forward 參數為1。

[root@server ~]# docker run --rm --name myCentos -it centos /bin/bash   #啟動一個容器
[root@2bdfb90cb3e0 /]# ping qq.com  #測試是否能夠訪問外部網絡
PING qq.com (180.163.26.39) 56(84) bytes of data.
64 bytes from 180.163.26.39 (180.163.26.39): icmp_seq=1 ttl=52 time=29.6 ms
64 bytes from 180.163.26.39 (180.163.26.39): icmp_seq=2 ttl=52 time=29.3 ms

容器之間訪問

容器之間互相訪問,需要兩方面的支持。

  • 容器的網絡拓撲是否已經互聯。默認情況下,所有容器都會被連接到 docker0 網橋上
  • 本地系統的 iptables 是否允許通過

默認情況下,容器之間是可以相互通信的

訪問所有的端口

當啟動Docker 服務時,默認會添加一條轉發策略到iptables 的FORWARD鏈上。策略為通過(ACCEPT)還是禁止(DROP)取決於配置 --icc=true 還是 --icc=false。如果手動指定 --iptables=false 則不會添加iptables 規則。

可見,默認情況下,不同容器之間是允許網絡互通的。如果是為了安全考慮,可以在/etc/default/docker 文件中配置 DOCKER_OPTS=--icc=false 來禁止它。

容器訪問外部網絡

容器所有到外部網絡的連接,源地址都會被NAT成本地系統的IP地址,這是使用iptables 的源地址偽裝操作實現的。

查看主機的NAT規則

[root@server ~]# iptables -t nat -nL  #查看防火牆nat表的規則
...
Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0 
...

其中,上述規則將所有源地址在 172.17.0.0/16 網段,目標地址為其它網絡(外部網絡)的流量動態偽裝為從系統網卡發出。MASQUERADE 跟傳統的 SNAT的好處就是它能動態從網卡獲取地址。

外部網絡訪問容器

容器允許外部訪問,可以在docker run 時候通過 -p 或 -P 參數來啟用。不論用哪種,其實也是在本地的 iptables 的nat表中添加相應的規則。使用 -P 時:

[root@server ~]# iptables -t nat -nL  #查看防火牆nat表的規則
...
Chain DOCKER (2 references)
target     prot opt source               destination                  
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:32770 to:172.17.0.3:443
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:32771 to:172.17.0.3:80
...

使用 -p 88:80 時:

[root@server ~]# iptables -t nat -nL  #查看防火牆規則
Chain DOCKER (2 references)
target     prot opt source               destination         
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:88 to:172.17.0.5:80

注意:這里規則映射了 0.0.0.0,意味着將接收主機來自所有的接口的流量。

配置 docker0 網橋

Docker 服務默認會創建一個 docker0 網橋(其實有一個 docker0 內部接口),它在內核層連通了其它的物理網卡或虛擬網卡,這就將所有容器和本地主機都放到同一個物理網絡。

Docker 默認指定了 docker0 接口的 IP 地址和子網掩碼,讓主機和容器之間可以通過網橋相互通信,它還給出了MTU(接口允許接收的最大傳輸單元),通常是1500Bytes,或宿主機網絡路由上支持的默認值,這些值都可以在服務啟動的時候進行配置。

--bip=CIDR   --IP地址加掩碼格式,例如 192.168.1.2/24
--mtu=BYTES  --覆蓋默認的 Docker mtu配置

也可以在配置文件中配置 DOCKER_OPTS, 然后重啟服務,由於目前Docker網橋是Linux網橋,可以使用 brctl show 來查看網橋和端口連接信息

[root@server ~]# brctl show  #查看網橋和端口信息
bridge name    bridge id        STP enabled    interfaces
docker0        8000.0242d8da46c0    no        veth07c5f5c
                                       veth0bed0f7

每次創建一個新容器的時候,Docker 從可用的地址段中選擇一個空閑的 IP 地址分配給容器的 eth0 端口。使用本地主機上 docker0 接口的IP 作為所有容器的默認網關

[root@server ~]# docker run -it --rm --name myCentos centos /bin/bash
[root@4636797a8e7c /]# ifconfig 
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.6  netmask 255.255.0.0  broadcast 172.17.255.255
...

[root@4636797a8e7c /]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.17.0.1      0.0.0.0         UG    0      0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

自定義網橋

除了默認的 docker0 網橋,也可以指定網橋來連接各個容器

在啟動Docker 服務的時候, 使用 -b BRIDGE --bridge=BRIDGE 來指定使用的網橋

(1)先創建網橋

[root@server ~]# systemctl stop docker  #停止docker服務
[root@server ~]# ip link set dev docker0 down  #停止docker0網橋
[root@server ~]# brctl delbr docker0  #刪除docker0網橋 
[root@server ~]# brctl addbr bridge0  #新建bridge0網橋
[root@server ~]# ip addr add 192.168.2.1/24 dev bridge0  #綁定ip給bridge0網橋
[root@server ~]# ip link set dev bridge0 up  #啟動bridge0網橋
[root@server ~]# brctl show   #查看網橋信息
bridge name    bridge id        STP enabled    interfaces
bridge0        8000.000000000000    no        
virbr0        8000.525400caf93e    yes        virbr0-nic
[root@server ~]# ifconfig bridge0  #查看bridge0網橋信息
bridge0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.2.1  netmask 255.255.255.0  broadcast 0.0.0.0

(2)配置Docker 服務

[root@server ~]# vim /lib/systemd/system/docker.service  #由於新版本的沒有/etc/default/docker配置文件,so 需要自己添加。
ExecStart=/usr/bin/dockerd -H unix:// $DOCKER_OPTS  #在ExecStart末尾添加 $DOCKER_OPTS
EnvironmentFile=-/etc/default/docker    #指定配置文件的路徑

[root@server ~]# vim /etc/default/docker  #自定義編輯配置文件,寫入啟動指定網橋的網橋信息
DOCKER_OPTS="-b=bridge0"

[root@server ~]# systemctl start docker  #啟動docker服務
[root@server ~]# docker run --rm -ti --name Mycentos centos /bin/bash  #創建一個容器

[root@0a13bd05faae /]# ifconfig  #查看容器的ip地址,檢查是否橋接到birdge0上面
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.2.2  netmask 255.255.255.0  broadcast 192.168.2.255

[root@0a13bd05faae /]# ping 192.168.2.1  #測試和網橋是否通
PING 192.168.2.1 (192.168.2.1) 56(84) bytes of data.
64 bytes from 192.168.2.1: icmp_seq=1 ttl=64 time=0.094 ms

說明:在老版本docker上面默認就有/etc/default/docker配置文件,直接編輯即可, 在新版本上面沒有,所以需要自己指定。

創建一個點到點的連接

默認情況下,Docker 會將所有的容器連接到由 docker0 提供的虛擬子網中。

如果我們需要兩個容器之間可以通信,而不通過宿主機網橋進行橋接。

解決辦法:創建一對 peer 接口,分別放到兩個容器中,配置成點對點鏈路類型即可。

(1)首先啟動2個容器:

[root@server ~]# docker run -i -t --rm --name myCentos01 --net=none centos /bin/bash  #在第一個終端啟動第一個容器

[root@server ~]# docker run -i -t --rm --name myCentos02 --net=none centos /bin/bash  #在第二個終端啟動第二個容器

(2)找到進程號,然后創建網絡命名空間的跟蹤文件(在第三個終端操作)

[root@server ~]# docker inspect -f '{{.State.Pid}}' myCentos01  #找到myCentos01的進程號 16614
[root@server ~]# docker inspect -f '{{.State.Pid}}' myCentos02  #找到myCentos02的進程號 16713
[root@server ~]# mkdir -p /var/run/netns
[root@server ~]# ln -s /proc/16614/ns/net /var/run/netns/16614
[root@server ~]# ln -s /proc/16713/ns/net /var/run/netns/16713

(3)創建一對 peer 接口,然后配置路由(在第三個終端操作)

[root@server ~]# ip link add A type veth peer name B
[root@server ~]# 
[root@server ~]# ip link set A netns 16614
[root@server ~]# ip netns exec 16614 ip addr add 10.1.1.1/32 dev A
[root@server ~]# ip netns exec 16614 ip link set A up
[root@server ~]# ip netns exec 16614 ip route add 10.1.1.2/32 dev A
[root@server ~]# 
[root@server ~]# ip link set B netns 16713
[root@server ~]# ip netns exec 16713 ip addr add 10.1.1.2/32 dev B
[root@server ~]# ip netns exec 16713 ip link set B up
[root@server ~]# ip netns exec 16713 ip route add 10.1.1.1/32 dev B

(4)測試兩個容器之間的通信

[root@90709b31ef86 /]# ping 10.1.1.2  #在myCentos01上面測試與myCentos02之間的通信
PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.075 ms

[root@96af03d9ad15 /]# ping 10.1.1.1  #在myCentos02上面測試與myCentos01之間的通信
PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data.
64 bytes from 10.1.1.1: icmp_seq=1 ttl=64 time=0.075 ms

通過測試,這兩個容器可以互相ping通,並成功建立連接。點到點鏈路不需要子網和子網掩碼。

此外,也可以不指定 --net=none 來創建點到點鏈路。這樣容器還可以通過原先的網絡來通信。

 


免責聲明!

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



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