一、網絡啟動與配置參數
Docker啟動時會在主機上自動創建一個docker0虛擬網橋,實際上是一個Linux網橋,
可以理解為一個軟件交換機,它會在掛載其上的接口之間進行數據轉發。
同時,Docker隨機分配一個本地未占用的私有網段(在RFC1918中定義)中的一個地址給docker0接口。
當創建一個Docker容器時,同時會創建一對veth pair接口,
當數據包發送到一個接口時,另外一個接口也可以收到相同的數據包。
這對接口一端在容器內,即eth0,另一端在本地並被掛載到docker0網橋,
名稱以veth開頭,通過這種方式,主機可以跟容器通訊,容器之間也可以相互通信。
如此一來,Docker就創建了在主機和所有容器之間的一個虛擬共享網絡。
下面是Docker網絡相關的參數命令,其中有些命令選項只有在Docker服務啟動的時候才能配置,而且不能馬上生效。
-b BRIDGE or --bridge=BRIDGE 指定容器掛載的網橋
--bip=CIDR 定制docker0的掩碼
-H SOCKET ... or --host=SOCKET... docker服務端接收命令的通道
--icc=true|false 是否支持容器之間進行通信
--ip-forward=true|false 啟用net.ipv4.ip_forward
--iptables=true|false 禁止Docker添加iptables規則
--mtu=BYTRS 容器網絡中的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|user_defined_network 配置容器的橋接模式
-p SPEC or --publish=SPEC 映射容器端口到宿主主機
-P or --publish-all=true|false 映射容器所有端口到宿主主機
其中 --net選項支持五種模式,如下所示:
--net=bridge 默認選項,為容器創建一個獨立的網絡命名空間,分配網卡、ip地址等網絡配置。
並通過veth接口對將容器掛載到一個虛擬網橋(默認為docker0)上。
--net=none 為容器創建一個獨立的網絡命名空間,但不進行網絡配置,即容器內沒有創建網卡、IP等
--net=container:NAME_or_ID 意味着新創建的容器共享指定的已存在容器的網絡命名空間,
兩個容器內的網絡配置共享,但其它資源(進程空間、文件系統等)還是隔離的。
--net=host 意味着不為容器創建獨立的網絡命名空間,容器內看到的網絡配置均與主機保持一致。
--net=user_defined_network 用戶自行用network相關命令創建一個網絡,
同一個網絡內的容器彼此可見,可以采用更多類型的網絡插件。
二、配置容器DNC和主機名
Docker支持自定義容器的主機名和DNS配置。
1.相關配置文件
實際上,容器中主機名和DNS配置信息都是通過三個系統配置文件來維護的:/etc/resolv.conf、/etc/hostname、/etc/hosts。
啟動一個容器,在容器中使用mount命令可以看到這三個文件的掛載信息。
其中,/etc/resolv.conf文件在創建容器的時候,默認會與宿主機/etc/resolv.conf文件內容保持一致。
這樣也可以說明容器和宿主的文件系統是共享的。
/etc/hosts文件默認中只記錄容器自身的一些地址和名稱。
2.容器內修改配置文件
Docker1.2.0開始支持在運行中的容器里直接編輯/etc/hosts、/etc/hostname和/etc/resolv.conf文件。
但是這些修改只是臨時的,只是在運行的容器中保留,容器終止或重啟后並不會被保存下來。也不會被docker commit提交。
3.通過參數指定
如果用戶想要自定義容器的配置,可以在創建或啟動容器的時候利用下面的參數指定。
指定主機名
-h HOSTNAME或--hostname=HOSTNAME。設定容器的主機名,它會被寫到容器內的/etc/hosts和/etc/hostname文件中。
但是這個主機名只有在容器內才能看到,在容器外看不到,既不會在docker ps中顯示,也不會在其它容器的/etc/hosts中看到。
記錄其它容器主機名
--link=CONTAINER_NAME:ALLAS。在創建容器的時候,添加一個所連接容器的主機名到容器內/etc/hosts文件中。
這樣新創建容器可以直接使用主機名來與所連接容器通信。
指定DNS服務器
--dns=IP_ADDRESS。添加DNS服務器到容器的/etc/resolv.con中,
容器會用指定的服務器來解析所有不在/etc/hosts中的主機名。
指定DNS搜索域
--dns-search=DOMAIN。設定容器的搜索域,當設定搜索域為.example.com時,在搜索一個名為host的主機時,
DNS不僅搜索host,還會搜索host.example.com。
三、容器訪問控制
容器的訪問控制主要通過Linux上的iptables防火牆軟件來進行管理和實現。
iptables是系統流行的防火牆軟件,在大部分發行版本中都會自帶。
1.容器訪問外部網絡
容器默認指定了網關為docker0網橋上的docker0內部接口。
docker0內部接口也是宿主機的一個本地接口,因此默認情況下是可以訪問到宿主機本地的。
容器想要通過宿主機訪問到外部網絡,需要宿主機進行轉發。
在Linux系統中,檢查轉發是否打開(默認是打開的):
如果為0,說明沒有開啟轉發,則需要手動打開:
更簡單的,在啟動Docker服務的時候設定--ip-forward=true,docker服務會自動打開宿主機系統的轉發服務。
2.容器之間的訪問
容器之間相互訪問,需要兩方面的支持:
網絡拓朴是否已經連通。默認情況下,所有容器都會連接到docker0網橋上,這意味着默認情況下拓撲是互通的;
本地系統的防火牆軟件iptables是否允許訪問通過。這取決於防火牆的默認規則是允許還是禁止。
下面分兩種情況介紹容器之間的訪問。
(1)訪問所有端口
當啟動Docker服務的時候,默認會添加一條“允許”轉發策略到iptables的FORWARD鏈上。
通過配置--icc=true|false(默認為true)參數可以控制默認的策略。
為了安全考慮,可以在Docker配置文件中配置DOCKER_OPTS=--icc=false來默認禁止容器之間的相互訪問。
同時,如果啟動Docker服務時手動指定--iptables=false參數則不會修改宿主機系統上的iptables規則。
(2)訪問指定端口
在通過-icc=false禁止容器間相互訪問后,仍可以通過--link=CONTAINER_NAME:ALIAS選項來允許訪問指定容器的開放端口。
--link=CONTAINER_NAME:ALIAS中的CONTAINER_NAME必須是docker自動分配的主機名或使用--name指定的主機名,不能使用-h參數配置的主機名。
四、映射容器端口到宿主機的實現
默認情況下,容器可以主動訪問到外部網絡的連接,但是外部網絡無法訪問到容器。
1.容器訪問外部實現
假設容器內部的網絡地址為172.12.0.2,本地網絡地址為10.0.2.2。
容器要能訪問外部網絡,源地址不能為172.12.0.2,需要進行源地址映射(Source NAT,SNAT),
修改為本地系統的IP地址10.0.2.2。
映射是通過iptables的源地址偽裝操作實現的。
查看主機nat表上POSTROUTING鏈的規則。該鏈負責網包離開主機之前,還寫其源地址。
其中,上述規則將所有源地址為172.12.0.0/16網段,而不是從docker0接口發出的流量,動態偽裝為系統網卡發出。
MASQUERADE行動跟傳統SNAT行動相比,好處是它能從網卡動態獲取地址。
2.外部訪問容器實現
容器允許外部訪問,可以在docker run時候通過-p或-P參數來啟用。
不管用那種方法,其實也是在本地的iptable的nat表中添加相應的規則,
將訪問外部IP地址的網包進行目標地址DANT,將目標地址修改為容器的IP地址。
以一個開放的8080端口為例,使用-P會自動映射本地的一個隨即端口到容器的8080端口。
可以看到,nat中涉及兩條鏈,PREROUTING鏈負責包到達網絡接口時,改寫其目標地址。
其中規則將所有流量都仍到DOCKER鏈。而DOCKER鏈中所有不是從docker0進來的網包(意味着不是本地主機產生),
將目標端口為49153的,修改目標地址為172.17.0.11,目標端口修改為80。
需要注意的是,這里的而規則映射了0.0.0.0,意味着將接收主機來自所有網絡接口上的流量。
可以通過-p IP:host_port:container_port或-p IP::port來指定綁定的外部網絡接口,以制定更嚴格的訪問規則。
如果希望映射永久綁定到某個固定的IP地址,可以在docker配置文件/etc/default/docker中指定DOCKER_OPTS="--ip=IP_ADDRESS",之后重啟Docker服務即可生效。
五、配置docker0網橋
Docker服務默認會創建一個名稱為docker0的Linux網橋,它在內核層連通了其它的物理或虛擬網卡。
這就將所有容器和本地主機都放到了同一個物理網絡。用戶使用Docker創建多個自定義網絡時可能會出現多個容器網橋。
Docker默認制定了docker0接口的IP地址和子網掩碼,讓主機和容器之間可以通過網橋相互通信。
它還給出了MTU(接口允許接收的最大傳輸單元),通常是1500字節,或宿主網絡路由上支持的默認值。
這些值都可以在服務啟動的時候進行配置:
--bip=CIDR:IP地址加掩碼格式,例如192.168.1.5/24
--mtu=BYTES:覆蓋默認的Docker mtu配置。
也可以在配置文件中配置DOCKER_OPTS,然后重啟服務。
由於目前Docker網橋是Linux網橋,用戶可以使用brctl show來查看網橋和端口連接信息。
每次創建一個新的容器時,Docker從可用的地址段中選擇一個空閑的IP地址來分配給容器的eth0端口。
並且使用本地主機上docker0接口的IP作為容器的默認網關:
六、自定義網橋
除了默認使用docker0網橋,用戶也可以指定網橋來連接各個容器。
在啟動docker服務的時候,使用-b BRIDGE或--bridge=BRIDGE來指定使用的網橋。
停止服務:
service docker stop
關閉docker網橋:
ip link set dev docker0 down
刪除舊的網橋:
brctl delbr docker0
新建網橋:
brctl addbr bridge0
給新的網橋配置參數:
ip addr add 192.168.5.1/24 dev bridge0
ip link set dev bridge0 up
ip addr show bridge0
這是傳統的方法,現在直接使用docker create network。
七、使用OpenvSwitch網橋
Docker默認使用的是Linux自帶的網橋實現,實際上,OpenvSwitch項目作為一個成熟的虛擬交換機,具備更豐富的功能。
1.安裝OpenvSwitch
安裝依賴包:
yum -y install openssl-devel wget kernel-devel
安裝開發工具:
yum groupinstall "Development Tools"
下載源碼:
wget http://openvswitch.org/releases/openvswitch-2.3.1.tar.gz
解壓:
tar xfz openvswitch-2.3.1.tar.gz
創建編譯目錄:
mkdir -p ~/rpmbuild/SOURCES
從spec文件中刪除openvswitch-kmod的依賴包,並創建一個新的spec文件:
sed 's/openvswitch-kmod, //g' openvswitch-2.3.1/rhel/openvswitch.spec > openvswitch-2.3.1/rhel/openvswitch_no_kmod.spec
開始編譯:
cp openvswitch-2.3.1.tar.gz rpmbuild/SOURCES
rpmbuild -bb --without=check ~/openvswitch-2.3.1/rhel/openvswitch_no_kmod.spec
安裝編譯生成的rpm文件:
yum localinstall /home/ovswitch/rpmbuild/RPMS/x86_64/openvswitch-2.3.1-1.x86_64.rpm
啟動服務:systemctl start openvswitch.service
查看服務狀態:systemctl -l status openvswitch.service
2.配置容器連接到OpenvSwitch
目前OpenvSwitch網橋還不能直接支持掛載容器,需要手動在OpenSwitch網橋上創建虛擬網口被掛載到容器中。
步驟如下:
(1)創建無網絡容器
docker run --net=noen --privileged=true -it base /bin/bash
查看網絡信息:
只有一個本地網卡lo
(2)手動為容器添加網絡
下載輔助腳本ovs-docker:
wget https://github.com/openvswitch/ovs/raw/master/utilities/ovs-docker
chmod a+x ovs-docker
添加網橋br1,並掛載:
ovs-vsctl add-br br1
ovs-vsctl show(查看)
./ovs-docker add-port br1 eth0 c26f1bbdb3f5 --ipaddress=172.17.0.2/24
可以看到容器內多了一個網卡:
在容器外,為br1配置接口地址。
ifconfig br1 172.17.0.10/24
八、創建一個點到點的連接
默認情況下,Docker會將所有容器連接由docker0提供的虛擬子網中,
有時候需要兩個容器之間可以直接通訊,而不同通過主機網橋進行橋接。
創建一對peer接口,分別放到兩個容器中,配置成點到點鏈路類型即可。
啟動兩個容器:
docker run -it --rm --net=none centos /bin/bash
找到進程號,然后創建網絡命名空間的跟蹤文件:
docker inspect -f "{{.State.Pid}}" d3e573778246 docker inspect -f "{{.State.Pid}}" 3882bde4d974 mkdir -p /var/run/netns ln -s /proc/25974/ns/net /var/run/netns/25974 ln -s /proc/26023/ns/net /var/run/netns/26023 ip link add AA type veth peer name BB
添加IP地址和路由信息:
ip link set AA netns 25974 ip netns exec 25974 ip addr add 10.1.1.1/32 dev AA ip netns exec 25974 ip link set AA up ip netns exec 25974 ip route add 10.1.1.2/32 dev AA ip link set BB netns 26023 ip netns exec 26023 ip addr add 10.1.1.2/32 dev BB ip netns exec 26023 ip link set BB up ip netns exec 26023 ip route add 10.1.1.1/32 dev BB
ping:
此外也可以不指定--net=none來創建點鏈路。