場景描述
在 centos7 運行 docker 容器應(yīng)用時,需要連接宿主機(jī)的 mysql 的 3306 端口,發(fā)現(xiàn)連接不上,docker 容器無法訪問宿主機(jī)的 mysql 數(shù)據(jù)庫。但是,在容器內(nèi)訪問外部網(wǎng)絡(luò)是可以 ping 通的。
原因分析
在 centos7 上部署 docker 容器,其網(wǎng)絡(luò)模式采用的是 bridge 模式。
啟動 docker 時,docker 進(jìn)程會創(chuàng)建一個名為 docker0 的虛擬網(wǎng)橋,用于宿主機(jī)與容器之間的通信。當(dāng)啟動一個 docker 容器時,docker 容器將會附加到虛擬網(wǎng)橋上,容器內(nèi)的報文通過 docker0 向外轉(zhuǎn)發(fā)。
如果 docker 容器訪問宿主機(jī),那么 docker0 網(wǎng)橋?qū)笪闹苯愚D(zhuǎn)發(fā)到本機(jī),報文的源地址是 docker0 網(wǎng)段的地址。而如果 docker 容器訪問宿主機(jī)以外的機(jī)器,docker 的 SNAT 網(wǎng)橋會將報文的源地址轉(zhuǎn)換為宿主機(jī)的地址,通過宿主機(jī)的網(wǎng)卡向外發(fā)送。
因此,當(dāng) docker 容器訪問宿主機(jī)時,如果宿主機(jī)服務(wù)端口會被防火墻攔截,那么就無法連通宿主機(jī),出現(xiàn) No route to host 的錯誤。
而訪問宿主機(jī)所在局域網(wǎng)內(nèi)的其他機(jī)器,由于報文的源地址是宿主機(jī) ip,因此,不會被目的機(jī)器防火墻攔截,所以可以訪問。
解決問題
首先設(shè)置了 mysql 的配置文件,保證 mysql 可以被任何 ip 訪問:
[mysqld]
bind-address = 0.0.0.0
修改完配置文件重啟生效。
但為了安全考慮,防火墻的 3306 端口仍然是不開放外網(wǎng)訪問的。
容器訪問宿主機(jī)的地址使用 eth0 的地址,即宿主機(jī)內(nèi)網(wǎng) ip 地址。
運行 ipconfig 命令,查看網(wǎng)絡(luò)的虛擬網(wǎng)橋相關(guān)信息。
注意:宿主機(jī)會把容器 ip 地址段當(dāng)成外網(wǎng) ip。(當(dāng)前說明是 centos7 環(huán)境)
編輯防火墻文件 /etc/firewalld/zones/public.xml,添加下面 docker0 地址段到配置:
<rule family="ipv4">
<source address="172.18.0.0/16"/>
<accept/>
</rule>
重啟防火墻,docker 容器即可正常訪問宿主機(jī)端口。
service firewalld restart
?? 如果有用到 docker-compose 命令,則會自動創(chuàng)建一個名為 br-"docker network id" 的虛擬網(wǎng)橋。
?? 此時同樣需要將虛擬網(wǎng)橋地址段配置到防火墻白名單,才能正常訪問,添加配置:
<rule family="ipv4">
<source address="172.20.0.0/16"/>
<accept/>
</rule>
測試端口
在容器中測試宿主機(jī)端口是否可以連接,可以使用 wget 內(nèi)網(wǎng)ip:端口 命令。
$ wget 172.17.25.162:3306
wget: can not connect to remote host (172.17.25.162): Host is unreachable #不可以連接
$ wget 172.17.25.162:3306
wget: bad header line: 5.7.29-log #可以連接