-
docker0網橋
當在一臺未經過特殊網絡配置的centos 或 ubuntu機器上安裝完docker之后泣懊,在宿主機上通過ifconfig命令可以看到多了一塊名為docker0的網卡砸彬,假設IP為 172.17.0.1/16瓤帚。有了這樣一塊網卡,宿主機也會在內核路由表上添加一條到達相應網絡的靜態(tài)路由醇王,可通過route -n查看:# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface ... 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 ...
此條路由表示所有目的IP地址為172.17.0.0/16的數據包從docker0網卡轉發(fā)敌卓。
然后使用docker run命令創(chuàng)建一個執(zhí)行shell(/bin/bash)的Docker容器遂黍,假設容器名稱為con1俐填。
?在con1容器中可以看到它有兩個網卡lo和eth0安接。lo設備不必多說,是容器的回環(huán)網卡英融;eth0即為容器與外界通信的網卡盏檐,eth0的ip 為 172.17.0.2/16歇式,和宿主機上的網橋docker0在同一個網段。
查看con1的路由表胡野,可以發(fā)現(xiàn)con1的默認網關正是宿主機的docker0網卡材失,通過測試, con1可以順利訪問外網和宿主機網絡给涕,因此表明con1的eth0網卡與宿主機的docker0網卡是相互連通的豺憔。
這時在來查看(ifconfig)宿主機的網絡設備额获,會發(fā)現(xiàn)有一塊以“veth”開頭的網卡够庙,如veth60b16bd,我們可以大膽猜測這塊網卡肯定是veth設備了抄邀,而veth pair總是成對出現(xiàn)的耘眨。veth pair通常用來連接兩個network namespace,那么另一個應該是Docker容器con1中的eth0了境肾。之前已經判斷con1容器的eth0和宿主機的docker0是相連的剔难,那么veth60b16bd也應該是與docker0相連的,不難想到奥喻,docker0就不只是一個簡單的網卡設備了偶宫,而是一個網橋。
真實情況正是如此环鲤,下圖即為Docker默認網絡模式(bridge模式)下的網絡環(huán)境拓撲圖纯趋,創(chuàng)建了docker0網橋,并以eth pair連接各容器的網絡冷离,容器中的數據通過docker0網橋轉發(fā)到eth0網卡上吵冒。
這里的網橋概念等同于交換機,為連在其上的設備轉發(fā)數據幀西剥。網橋上的veth網卡設備相當于交換機上的端口痹栖,可以將多個容器或虛擬機連接在上面,這些端口工作在二層瞭空,所以是不需要配置IP信息的揪阿。圖中docker0網橋就為連在其上的容器轉發(fā)數據幀,使得同一臺宿主機上的Docker容器之間可以相互通信咆畏。
?大家應該注意到docker0既然是二層設備南捂,它上面怎么設置了IP呢?docker0是普通的linux網橋鳖眼,它是可以在上面配置IP的黑毅,可以認為其內部有一個可以用于配置IP信息的網卡接口(如同每一個Open vSwitch網橋都有一個同名的內部接口一樣)。在Docker的橋接網絡模式中钦讳,docker0的IP地址作為連于之上的容器的默認網關地址存在矿瘦。
在Linux中枕面,可以使用brctl命令查看和管理網橋(需要安裝bridge-utils軟件包),比如查看本機上的Linux網橋以及其上的端口:
# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02420b69b449 no veth1b11267
更多關于brctl命令的功能和用法缚去,大家通過man brctl或brctl --help查閱潮秘。
docker0網橋是在Docker daemon啟動時自動創(chuàng)建的,其IP默認為172.17.0.1/16易结,之后創(chuàng)建的Docker容器都會在docker0子網的范圍內選取一個未占用的IP使用枕荞,并連接到docker0網橋上。
除了使用docker0網橋外搞动,還可以使用自己創(chuàng)建的網橋躏精,比如創(chuàng)建一個名為br0的網橋,配置IP:
# brctl addbr br0
# ifconfig br0 18.18.0.1
-
iptables規(guī)則
?Docker安裝完成后鹦肿,將默認在宿主機系統(tǒng)上增加一些iptables規(guī)則矗烛,以用于Docker容器和容器之間以及和外界的通信,可以使用iptables-save命令查看箩溃。其中nat表中的POSTROUTING鏈有這么一條規(guī)則:-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
參數說明:
-s :源地址172.17.0.0/16
-o:指定數據報文流出接口為docker0
-j :動作為MASQUERADE(地址偽裝)
上面這條規(guī)則關系著Docker容器和外界的通信瞭吃,含義是:將源地址為172.17.0.0/16的數據包(即Docker容器發(fā)出的數據),當不是從docker0網卡發(fā)出時做SNAT涣旨。這樣一來歪架,從Docker容器訪問外網的流量,在外部看來就是從宿主機上發(fā)出的霹陡,外部感覺不到Docker容器的存在和蚪。
?那么,外界想到訪問Docker容器的服務時該怎么辦呢穆律?我們啟動一個簡單的web服務容器惠呼,觀察iptables規(guī)則有何變化。
1峦耘、首先啟動一個 tomcat容器剔蹋,將其8080端口映射到宿主機上的8080端口上:
#docker run -itd --name tomcat01 -p 8080:8080 tomcat:latest
2、然后查看iptabels規(guī)則辅髓,省略部分無用信息:
#iptables-save
*nat
-A POSTROUTING -s 172.17.0.4/32 -d 172.17.0.4/32 -p tcp -m tcp --dport 8080 -j MASQUERADE
...
*filter
-A DOCKER -d 172.17.0.4/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 8080 -j ACCEPT
可以看到泣崩,在nat、filter的Docker鏈中分別增加了一條規(guī)則洛口,這兩條規(guī)則將訪問宿主機8080端口的流量轉發(fā)到了172.17.0.4的8080端口上(真正提供服務的Docker容器IP和端口)矫付,所以外界訪問Docker容器是通過iptables做DNAT(目的地址轉換)實現(xiàn)的。
?此外第焰,Docker的forward規(guī)則默認允許所有的外部IP訪問容器买优,可以通過在filter的DOCKER鏈上添加規(guī)則來對外部的IP訪問做出限制,比如只允許源IP192.168.0.0/16的數據包訪問容器,需要添加如下規(guī)則:
iptables -I DOCKER -i docker0 ! -s 192.168.0.0/16 -j DROP
不僅僅是與外界間通信杀赢,Docker容器之間互個通信也受到iptables規(guī)則限制烘跺。同一臺宿主機上的Docker容器默認都連在docker0網橋上,它們屬于一個子網脂崔,這是滿足相互通信的第一步滤淳。同時,Docker daemon會在filter的FORWARD鏈中增加一條ACCEPT的規(guī)則(--icc=true):
-A FORWARD -i docker0 -o docker0 -j ACCEPT
這是滿足相互通信的第二步砌左。當Docker datemon啟動參數--icc(icc參數表示是否允許容器間相互通信)設置為false時脖咐,以上規(guī)則會被設置為DROP,Docker容器間的相互通信就被禁止汇歹,這種情況下屁擅,想讓兩個容器通信就需要在docker run時使用 --link選項。
在Docker容器和外界通信的過程中煤蹭,還涉及了數據包在多個網卡間的轉發(fā)(如從docker0網卡轉發(fā)到宿主機ens160網卡)笔喉,這需要內核將ip-forward功能打開,即將ip_forward系統(tǒng)參數設1常挚。Docker daemon啟動的時候默認會將其設為1(--ip-forward=true),也可以通過命令手動設置:
# echo 1 > /proc/sys/net/ipv4/ip_forward
# cat /proc/sys/net/ipv4/ip_forward
1
-
Docker容器的DNS和主機名
?同一個Docker鏡像可以啟動很多Docker容器奄毡,通過查看折欠,它們的主機名并不一樣吼过,也即是說主機名并非是被寫入鏡像中的。實際上容器中/etc/目錄下有3個文件是容器啟動后被虛擬文件覆蓋的盗忱,分別是/etc/hostname、/etc/hosts趟佃、/etc/resolv.conf,通過在容器中運行mount命令可以查看:# docker exec -it tomcat01 bash root@3d95d30c69d3:/usr/local/tomcat# mount ... /dev/mapper/centos-root on /etc/resolv.conf type xfs (rw,relatime,attr2,inode64,noquota) /dev/mapper/centos-root on /etc/hostname type xfs (rw,relatime,attr2,inode64,noquota) /dev/mapper/centos-root on /etc/hosts type xfs (rw,relatime,attr2,inode64,noquota) ...
這樣能解決主機名的問題闲昭,同時也能讓DNS及時更新(改變resolv.conf)。由于這些文件的維護方法隨著Docker版本演進而不斷變化序矩,因此盡量不修改這些文件鸯绿,而是通過Docker提供的參數進行相關設置,配置方式如下:
-h HOSTNAME 或 --hostname=HOSTNAME:設置容器的主機名瓶蝴,此名稱會寫在/etc/hostname和/etc/hosts文件中幔烛,也會在容器的bash提示符看到。但是在外部囊蓝,容器的主機名是無法查看的饿悬,不會出現(xiàn)在其他容器的hosts文件中,即使使用docker ps命令也查看不到聚霜。此參數是docker run命令的參數狡恬,而非docker daemon的啟動參數。
--dns=IP_ADDRESS...:為容器配置DNS蝎宇,寫在/etc/resolv.conf中弟劲。該參數即可以在docker daemon 啟動的時候設置,也可以在docker run時設置姥芥,默認為8.8.8或8.8.4.4兔乞。
注意:對以上3個文件的修改不會被docker commit保存,也就是不會保存在鏡像中凉唐,重啟容器也會導致修改失效庸追。另外,在不穩(wěn)定的網絡環(huán)境下使用需要特別注意DNS的設置台囱。