Docker從入門到精通(六)——容器通訊

YSOcean發表於2021-12-20

想要變成 Docker 的高階玩家,搞懂 Docker 的容器通訊是必不可少的。

1、需求

通常一個 Web 專案上線,我們會把開發完成的服務部署在Tomcat 伺服器裡面,然後需要的持久化資料會存放在資料庫 Mysql,那麼在服務執行時,少不了 Tomcat 和 Mysql 的互動。

對應的,應用到 Docker 中,就是 Tomcat 容器和 Mysql 容器間的互動,那麼問題來了:

兩個容器之間怎麼通訊呢?

1.1 準備兩個映象

①、Tomcat

FROM tomcat:latest
MAINTAINER itcoke
WORKDIR /usr/local/tomcat/webapps
COPY ./webapps/ /usr/local/tomcat/webapps/
RUN apt update && apt install -y iproute2 && apt install -y iputils-ping && apt install -y vim

這是製作 Tomcat 映象的 Dockerfile,因為目前最新版的官方 Tomcat 映象沒有一個網路檢視命令,所以需要手動安裝。

構建映象命令:

docker build -f Dockerfile -t itcoke/mytomcat8:1.0 .

②、MySQL

FROM mysql:8.0
MAINTAINER itcoke
RUN apt update && apt install -y iproute2 && apt install -y iputils-ping && apt install -y vim

構建MySQL映象命令:

docker build -f Dockerfile -t itcoke/mysql8:1.0 .

1.2 啟動容器

①、啟動並進入Tomcat容器

docker run -it -p 8080:8080 --name tomcat1 itcoke/mytomcat8:1.0 /bin/bash

②、啟動並進入MySQL容器

docker run -it -p 3306:3306 --name mysql1 itcoke/mysql8:1.0 /bin/bash

1.3 通過 IP 通訊

容器建立好了,想要進行通訊,我們第一時間會想到通過 IP,我們通過如下命令檢視容器 IP 地址:

ip addr

Tomcat 容器IP:

MySQL 容器IP:

可以看到容器是有 IP的,我們在 Tomcat容器ping MySQL容器:

自此大功告成,我們可以說容器間通訊使用 IP 就行。

2、問題

通過 IP 通訊,我們看似解決了容器間通訊的問題,但在實際生產中,我們容器是會經常重新啟動的,而上面的容器 IP 是Docker 分配的虛擬IP,這個IP是會變得,假設我們每次重新構建一個容器,那就要重新修改服務配置IP,生產環境會有幾十個幾百個容器,都要進行修改,這將是很麻煩的。

那麼怎麼辦呢?熟悉 IP-域名解析的同學,可能會一下想到,保證域名不變的情況,IP 無論怎麼變,通過 DNS 解析都是能正確訪問到網頁的,於是:

有沒有辦法通過容器名來通訊呢?

3、容器名通訊

前面我們驗證了可以通過容器IP來進行通訊,但是容器重新構建IP會發生變化,這給我們造成很大的麻煩,於是我們想到通過容器名來進行通訊,下面測試一下:

PS:前面啟動容器時,我們給Tomcat容器命名為 tomcat1,給MySQL容器命名為mysql1。

我們發現直接通過容器名是不能夠通訊的。那麼應該怎麼辦呢?

3.1 容器單向通訊

啟動容器的時候通過增加 --link 容器名 引數:

比如:

docker run -it -p 8080:8080 --name tomcat1 --link mysql1 3336fdaf451a /bin/bash

然後,我們在 tomcat1 容器ping mysql1 :

為什麼說是單向通訊,如果啟動 mysql1 容器的時候沒有增加--link 引數,則 mysql1 訪問不了 tomcat1。

PS:如果你檢視tomcat1 容器的 /etc/hosts 檔案,發現 --link 就是增加了名字解析:

而mysql1 容器的 /etc/hosts 則沒有名字解析:

4、通訊原理

知道了容器之間可以通訊,但是為什麼能夠通訊呢?

我們啟動了一個 Tomcat1容器,啟動了一個 MySQL1容器,下面我們看下宿主機IP:

①、本機迴環地址

lo,127.0.0.1,不屬於任何一個有類別地址類。它代表裝置的本地虛擬介面,通常在安裝網路卡前就可以ping通這個本地迴環地址。

一般用來測試本機的網路配置,能PING通 127.0.0.1 說明本機的IP協議安裝沒有問題。

②、伺服器內網地址

ens33,192.168.88.2,這也是我建立docker宿主機的真實IP地址。

注意:我這裡是安裝虛擬機器,如果是真實物理機,這個名字可能是eth0,eth0表示第一塊網路卡,同理eth2表示第二塊網路卡。

③、docker0

Docker啟動的時候會在主機上自動建立一個docker0網橋(注意名字一定是docker0,會有docker1,docker2之類),實際上是一個 Linux 網橋,所有容器的啟動如果在docker run的時候沒有指定網路模式的情況下都會掛載到docker0網橋上。

④、容器地址

在宿主機檢視執行ip addr,可以看到

tomcat1 的名稱是:45: veth8eb364e@if44

mysql1的名稱是:49: veth02cb24d@if48

我們進入到容器tomcat1內部,檢視ip:

44: eth0@if45

同理,進入到容器 mysql1 內部,檢視ip:

48: eth0@if49

不知道大家注沒注意到這一串名稱的數字其實是關聯的,這就是大名鼎鼎的 veth-pair 技術。

4.1 veth-pair

veth-pair 就是一對的虛擬裝置介面,它都是成對出現的。一端連著協議棧,一端彼此相連著,因為這個特性,它常常充當著一個橋樑,連線著各種虛擬網路裝置,典型的例子像“兩個 namespace 之間的連線”,“Bridge、OVS 之間的連線”,“Docker 容器之間的連線” 等等,以此構建出非常複雜的虛擬網路結構,比如 OpenStack Neutron。

image-20211111075145408

多個容器之間通訊依賴 veth-pair 技術:

5、容器間雙向通訊

其實就是利用網橋連結新建立的容器和宿主機,上面圖片的 docker0 就是一個網橋。

docker network ls #檢視網橋

①、host:容器將不會虛擬出自己的網路卡,配置自己的IP等,而是使用宿主機的IP和埠。

②、none:該模式關閉了容器的網路功能。

③、bridge:此模式會為每一個容器分配、設定IP等,並將容器連線到一個docker0虛擬網橋,通過docker0網橋以及Iptables nat表配置與宿主機通訊。

下面我們就自定義一個網橋,利用自定義bridge模式進行雙向通訊。

其實 docker0 就是一個預設網橋,為什麼我們還要自定義呢?

使用自定義的網橋可以控制哪些容器可以互相通訊,可以通過容器名通訊(自動DNS解析名稱到IP地址,這個docker0是不支援的)。

一、建立自定義網橋

docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 myBridge

②、啟動容器

docker run -it -p 8080:8080 --name tomcat1 --net myBridge 3336fdaf451a /bin/bash

docker run -it -p 3306:3306 --name mysql1 --net myBridge adaa6a5d739c /bin/bash

大功告成,我們發現通過容器名稱是可以 ping 通了。

5.1 不同bridge 網路通訊

docker0 和 myBridge 裡面的容器可以互相ping通嗎?

答案是不行的,那麼如何打通呢?我們只需要將容器連結到另一個網橋即可。

docker network connect [OPTIONS] network container

比如,我們要把預設網橋 docker0 上面的 tomcat1-docker0 容器能連結 myBridge 網橋裡面的容器,只需要執行以下命令即可。

docker network connect myBridge tomcat1-docker0

然後進入 tomcat1-docker0 容器,發現可以 ping 通 myBridge 網橋裡面的容器了。

並且檢視 tomcat1-docker0 容器的ip,你會發現有兩個 ip了,也就是一個容器,多個ip。

相關文章