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

使用命令
docker network ls可以查看到容器所使用的網(wǎng)絡(luò),如下
訪(fǎng)問(wèn)宿主機(jī)
看上面的網(wǎng)絡(luò)配置docker0對(duì)應(yīng)的地址是172.17.0.1
方案一:直接使用172.17.0.1作用數(shù)據(jù)庫(kù)連接地址
方案二:docker 18.03 加入了一個(gè) feature,在容器中可以通過(guò) host.docker.internal來(lái)訪(fǎng)問(wèn)主機(jī) 。
Use your internal IP address or connect to the special DNS name host.docker.internal which will resolve to the internal IP address used by the host.
在 windows 下我們可以使用方案二,并在 host 文件中配置
127.0.0.1 host.docker.internal
端口測(cè)試
在容器中測(cè)試宿主機(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 #可以連接
部署方式
docker部署有很多種方式,如docker run直接運(yùn)行,也可用docker-compose編排,還可以打成image上傳云服務(wù)等,這里討論的不是單個(gè)服務(wù),而是多個(gè)服務(wù)。
最簡(jiǎn)單的網(wǎng)絡(luò)部署方式是使用host模式,這相當(dāng)于把docker容器當(dāng)暴露在宿主機(jī)了。
若使用docker-compose單個(gè)服務(wù)運(yùn)行,就會(huì)上面的網(wǎng)絡(luò)配置圖中顯示多個(gè)br-xxx容器網(wǎng)絡(luò),每個(gè)單獨(dú)的容器擁有自己的一個(gè)網(wǎng)絡(luò),這樣服務(wù)間的訪(fǎng)問(wèn)需要明確指定宿主機(jī)IP;另一種方式是讓所有的容器使用同一個(gè)網(wǎng)絡(luò),這樣所有容器都是一個(gè)內(nèi)網(wǎng),它們可以使用容器名訪(fǎng)問(wèn)。
首先,需要?jiǎng)?chuàng)建一個(gè)網(wǎng)絡(luò),如下:
# 創(chuàng)建網(wǎng)絡(luò)
docker network create <Network Name>局域網(wǎng)名字
# 查看已存在的網(wǎng)絡(luò)
docker network list
在需要加入同一局域網(wǎng)的容器 .yml或yaml文件中添加下面的代碼:
networks:
default:
external:
name: 局域網(wǎng)名字
以下是部署一個(gè)實(shí)際的應(yīng)用例子,例子中對(duì)內(nèi)網(wǎng)mq,redis容器訪(fǎng)問(wèn)使用的是容器名container_name,對(duì)宿主機(jī)mysql的訪(fǎng)問(wèn)使用的是docker0對(duì)應(yīng)的IP。
version: '3'
services:
producer:
hostname: yw-producer
container_name: yw-producer
image: yw-producer:1.0
ports:
- 8766:8766
- 8799:8799
restart: always
environment:
- PORT=8766
- HTTP_PORT=8799
- REDIS_PORT=6379
- REDIS_HOST=myredis
- MQ_URL=tcp://myactivemq:61616
- MAIN_DATASOURCE=jdbc:mysql://172.17.0.1:3306/xxx?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
volumes:
- ./logs:/app/logs
networks:
default:
external:
name: yw-network
firewall配置
當(dāng)執(zhí)行docker時(shí)報(bào)以下錯(cuò)誤:
[root@docker ~]# docker run -itd --name wordpress -p 88:80 wordpress:v1
b77482f8075042e9cc6723d6922a1211c37d99339678a00cc040396b23d40ef0
docker: Error response from daemon: driver failed programming external connectivity on endpoint wordpress (77cb6b1ea5387ac97b1b90178b2ccda831aa9713e0e9a83be057083fed66fc69): (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 88 -j DNAT --to-destination 172.17.0.2:80 ! -i docker0: iptables: No chain/target/match by that name.
(exit status 1)).
說(shuō)明可能是因?yàn)殚_(kāi)啟或關(guān)閉防火墻導(dǎo)致docker出問(wèn)題。
解決:systemctl restart docker
開(kāi)啟防火墻是很有必要的,以下是一些常用操作:
firewall-cmd --state 查看狀態(tài)
## 開(kāi)啟端口
## zone -- 作用域
## add-port=80/tcp -- 添加端口,格式為:端口/通訊協(xié)議
## permanent -- 永久生效,沒(méi)有此參數(shù)重啟后失效
firewall-cmd --zone=public --add-port=3306/tcp --permanent
重新加載:firewall-cmd --reload
firewall-cmd --zone=public --remove-port=80/tcp --permanent
firewall-cmd --zone=public --add-port=40000-42000/tcp --permanent
查看:firewall-cmd --zone=public --list-ports
啟動(dòng): systemctl start firewalld
查看狀態(tài): systemctl status firewalld
禁用,禁止開(kāi)機(jī)啟動(dòng): systemctl disable firewalld
停止運(yùn)行: systemctl stop firewalld
重啟:systemctl restart firewalld