Docker學習總結之跨主機進行link
Docker的功能非常強大,但要想駕馭好Docker卻不是一件很容易的事情。下面就介紹一種日常工作中會遇到的一個user case。比如現在有兩台host,分別標記為hostA和hostB。hostA用來運行oracle服務,hostB用來運行app服務。
hostB中app產生的數據需要實時寫入hostA中的oracle數據庫。也就是hostB中的docker container需要link hostA中的docker container。
為了解決這個問題,有兩個解決方案:
方案一:
將hostA中的oracle container對外expose 1521(我們假定此處對外expose 1521),然后在hostB中的app container中修改/etc/hosts文件,將hostA的IP添加到hosts文件中。
Docker 部署圖如下:

這種方案的優點就是可以根據實際情況自由配置,"自己的app掌控在自己手中"。
但是缺點也很嚴重,首先每次run container時都需要修改hosts文件,而且每次host環境發生變化,都需要維護hosts文件,因此后續的維護成本很高。其次,如果遇到其他人開發的docker image,我們未必有權限來修改hosts文件。
所以此方案也僅僅用作開發測試使用,不推薦正式采用。
方案二:
Docker官方提供了一種ambassador的agent方案。此方案借助一個名為svendowideit/ambassador的image,將不同host進行解耦合。
Docker 部署圖如下:

具體實施步驟如下:
首先我們要在hostA和hostB之間pull svendowideit/ambassador。
1 docker pull svendowideit/ambassador
根據部署圖可得知,是hostB要link hostA的container,因此我們需要先啟動hostA的container(此時可以理解hostA為server段,hostB為client段)。
docker run -d --name oracle tirtool/oracle11g:latest
然后啟動hostA中的ambassador container。
docker run -d --link oracle:oracle -p 1521:1521 --name ambassador svendowideit/ambassador:latest
標紅的部分是需要重點注意的地方,這個命令也就是將hostA中的oracle container直接暴露在ambassador之中,這樣ambassador才能將訪問請求轉發到oracle container中。
此刻,hostA中的事情都已經處理完了,我們開始處理hostB。
與hostA的container啟動順序不用,我們需要先start hostB的ambassador。因為依據部署圖可知,hostB中是app container調用ambassador container,所以需要先保證ambassador已經啟動,才能啟動app container。
docker run -d --name ambassador-oracle --expose 1521 -e ORACLE_PORT_1521_TCP=tcp://<<hostA IP>>:1521 svendowideit/ambassador
標紅的部分很重要,直接決定了hostB和hostA是否可以直接通訊。稍后,我們可以來解讀一下這部分參數。
當hostB中的ambassador啟動成功后,我們開始啟動app container。
docker run --link ambassador-oracle:oracle --name bw base/ubuntu:14.04
大家注意到我們在app container中將ambassador-oracle alias為oracle,這樣在app container中的/etc/hosts文件中會出現一條記錄:
10.1.0.3 oracle
此刻app container中app產生數據后,如果調用oracle:1521,那么首先將請求發往hostB的ambassador的1521端口。hostB的ambassador會將數據轉發到hostA的1521端口。而此時hostA中的ambassador在listen 1521端口,接收到請求后會將數據轉發至hostA的oracle container中。oracle container處理完畢后將response返回值ambassador,ambassador再依次回傳。從而達到不同host中container相互訪問的目的。
我們再看一下在hostB中執行 --expose 1521 -e ORACLE_PORT_1521_TCP=tcp://<<hostA IP>>:1521 時發生了什么事情。ambassador最重要的一項任務就是將hostB的1521端口同hostA的1521端口進行了端口映射。
其實執行的是下面的命令:
socat TCP4-LISTEN:1521,fork,reuseaddr TCP4:<hostA IP>:1521
這條命令采取fork模式,將本機的1521的數據轉發到hostA的1521端口。而這條命令所需的參數來自於一個shell腳本:
env | grep _TCP= | sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' | sh && top
這條命令在env中找尋所有*_TCP的環境變量,我們在啟動時設定-e ORACLE_PORT_1521_TCP=tcp://<<hostA IP>>:1521 所以可以找到ORACLE_PORT_1521_TCP這條變量。找到后執行正則表達式"s/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/" 替換后的shell command就是socat TCP4-LISTEN:1521,fork,reuseaddr TCP4:<hostA IP>:1521。
因此ambassador方案就是很巧妙的將不同host的port進行了橋接,而這些對docker使用者都是透明的。但這個方案也是有一些瑕疵的,就是如果新增container之后,需要重啟或者新增ambassador,所以如果一個ambassador同時對應多個container,那么在維護上面就會稍許麻煩些,但維護成本比方案一低了很多。
