使用docker部署是非常方便的,特別是一些大型系統(tǒng)的部署,下面來起看看
場景說明
系統(tǒng)部署時(shí)墅拭,數(shù)據(jù)庫沒有使用docker部署方式淤齐,這樣就需要docker容器能夠訪問宿主機(jī)的進(jìn)程股囊,目前問題是無法訪問宿主機(jī)。
原因分析
在 centos7 上部署 docker 容器更啄,其網(wǎng)絡(luò)模式采用的是 bridge 模式稚疹。
啟動 docker 時(shí),docker 進(jìn)程會創(chuàng)建一個(gè)名為 docker0 的虛擬網(wǎng)橋祭务,用于宿主機(jī)與容器之間的通信内狗。當(dāng)啟動一個(gè) docker 容器時(shí),docker 容器將會附加到虛擬網(wǎng)橋上义锥,容器內(nèi)的報(bào)文通過 docker0 向外轉(zhuǎn)發(fā)柳沙。
如果 docker 容器訪問宿主機(jī),那么 docker0 網(wǎng)橋?qū)?bào)文直接轉(zhuǎn)發(fā)到本機(jī)拌倍,報(bào)文的源地址是 docker0 網(wǎng)段的地址赂鲤。而如果 docker 容器訪問宿主機(jī)以外的機(jī)器,docker 的 SNAT 網(wǎng)橋會將報(bào)文的源地址轉(zhuǎn)換為宿主機(jī)的地址柱恤,通過宿主機(jī)的網(wǎng)卡向外發(fā)送数初。
因此,當(dāng) docker 容器訪問宿主機(jī)時(shí)梗顺,如果宿主機(jī)服務(wù)端口會被防火墻攔截泡孩,那么就無法連通宿主機(jī),出現(xiàn) No route to host 的錯(cuò)誤寺谤。
而訪問宿主機(jī)所在局域網(wǎng)內(nèi)的其他機(jī)器仑鸥,由于報(bào)文的源地址是宿主機(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 地址谷市。
運(yùn)行 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)建一個(gè)名為 br-"docker network id" 的虛擬網(wǎng)橋括蝠。
此時(shí)同樣需要將虛擬網(wǎng)橋地址段配置到防火墻白名單鞠抑,才能正常訪問,添加配置:
<rule family="ipv4">
<source address="172.20.0.0/16"/>
<accept/>
</rule>
使用ifconfig
查看宿主機(jī)的網(wǎng)絡(luò)
使用命令
docker network ls
可以查看到容器所使用的網(wǎng)絡(luò)忌警,如下訪問宿主機(jī)
看上面的網(wǎng)絡(luò)配置docker0對應(yīng)的地址是172.17.0.1
方案一:直接使用172.17.0.1作用數(shù)據(jù)庫連接地址
方案二:docker 18.03 加入了一個(gè) feature搁拙,在容器中可以通過 host.docker.internal來訪問主機(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
端口測試
在容器中測試宿主機(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ù)。
最簡單的網(wǎng)絡(luò)部署方式是使用host模式犀忱,這相當(dāng)于把docker容器當(dāng)暴露在宿主機(jī)了募谎。
若使用docker-compose單個(gè)服務(wù)運(yùn)行,就會上面的網(wǎng)絡(luò)配置圖中顯示多個(gè)br-xxx容器網(wǎng)絡(luò)阴汇,每個(gè)單獨(dú)的容器擁有自己的一個(gè)網(wǎng)絡(luò)数冬,這樣服務(wù)間的訪問需要明確指定宿主機(jī)IP;另一種方式是讓所有的容器使用同一個(gè)網(wǎng)絡(luò)搀庶,這樣所有容器都是一個(gè)內(nèi)網(wǎ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)用例子,例子中對內(nèi)網(wǎng)mq,redis容器訪問使用的是容器名container_name咆蒿,對宿主機(jī)mysql的訪問使用的是docker0對應(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)).
說明可能是因?yàn)殚_啟或關(guān)閉防火墻導(dǎo)致docker出問題蚂子。
解決:systemctl restart docker
開啟防火墻是很有必要的,以下是一些常用操作:
firewall-cmd --state 查看狀態(tài)
## 開啟端口
## zone -- 作用域
## add-port=80/tcp -- 添加端口缭黔,格式為:端口/通訊協(xié)議
## permanent -- 永久生效食茎,沒有此參數(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
啟動: systemctl start firewalld
查看狀態(tài): systemctl status firewalld
禁用,禁止開機(jī)啟動: systemctl disable firewalld
停止運(yùn)行: systemctl stop firewalld
重啟:systemctl restart firewalld