想要變成 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。
多個容器之間通訊依賴 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。