Docker容器訪(fǎng)問(wèn)宿主機(jī)

使用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ò)

image.png

使用命令docker network ls可以查看到容器所使用的網(wǎng)絡(luò),如下
network.png

訪(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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容