深入理解docker的link機制

jeff216發表於2016-06-22

什麼是docker的link機制

同一個宿主機上的多個docker容器之間如果想進行通訊,可以通過使用容器的ip地址來通訊,也可以通過宿主機的ip加上容器暴露出的埠號來通訊,前者會導致ip地址的硬編碼,不方便遷移,並且容器重啟後ip地址會改變,除非使用固定的ip,後者的通訊方式比較單一,只能依靠監聽在暴露出的埠的程式來進行有限的通訊。通過docker的link機制可以通過一個name來和另一個容器通訊,link機制方便了容器去發現其它的容器並且可以安全的傳遞一些連線資訊給其它的容器。其使用方式如下:

1.執行一個容器,通過–name指定一個便於記憶的名字,這個容器被稱為source container,也就是要連線的容器

docker run --name db -e MYSQL_ROOT_PASSWORD=server -d mysql

上面通過傳遞環境變數MYSQL_ROOT_PASSWORD=server,來設定mysql服務的密碼為server

2.執行另外一個容器,並link到上面啟動的容器,這個容器被稱為received container

sudo docker run -d --name web --link db:aliasdb nginx

上面通過--link連線名為db的容器,併為其設定了別名aliasdb
完成了上面的兩個步驟後,在nginx的容器中就可以使用db或者aliasdb作為連線地址來連線mysql服務,即使容器重啟了,地址發生了變化,不會影響兩個容器之間的連線。

link機制的連線資訊傳遞

雖然通過使用link機制nginx可以和mysql進行通訊了,但是如何知道mysql的埠是多少呢,雖然說是固定的是
3306,但是也不排除更改埠號的問題,並且對應一些非固定埠的應用來說,只要要連線的容器的埠資訊也是尤為重要的,link機制通過環境變數的方式提供了這些資訊,除此之外像db的密碼這些資訊也會通過環境變數提供,docker將source container中定義的環境變數全部匯入到received container中,在received container中可以通過環境變數來獲取連線資訊下面是db中提供的環境變數:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/mysql/bin:/usr/local/mysql/scripts
HOSTNAME=c1a7c7f091eb
MYSQL_ROOT_PASSWORD=server
MYSQL_MAJOR=5.5
MYSQL_VERSION=5.5.48
HOME=/root

注: 使用docker exec db env命令來獲得上面的結果
下面我們來看看在web這個容器中,這些變數是如何被匯入的。

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=6337c0044215
ALIASDB_PORT=tcp://172.17.0.8:3306
ALIASDB_PORT_3306_TCP=tcp://172.17.0.8:3306
ALIASDB_PORT_3306_TCP_ADDR=172.17.0.8
ALIASDB_PORT_3306_TCP_PORT=3306
ALIASDB_PORT_3306_TCP_PROTO=tcp
ALIASDB_NAME=/web/aliasdb
ALIASDB_ENV_MYSQL_ROOT_PASSWORD=server
ALIASDB_ENV_MYSQL_MAJOR=5.5
ALIASDB_ENV_MYSQL_VERSION=5.5.48
NGINX_VERSION=1.9.10-1~jessie
HOME=/root

上面的變數被分成了五個部分:

  • 第一個部分是web容器自身提供的一些環境變數,如NGINX_VERSION,HOSTNAME,HOME,PATH等.
  • 第二個部分則是ALIASDB_ENV開頭的變數,這些都是從source container中匯入的,變數來源於Dockerfile中使用ENV命令定義的變數,或者是docker run的時候通過-e 新增的環境變數。
  • 第三個部分是ALIASDB_NAME 這個變數,這變數記錄了link的兩個容器的組合,這裡就是/web/db
  • 第四個部分就是ALIASDB_PORT開頭的一系列變數,這些變數會有很組,每組變數的命名格式如下
<alias>_PORT_<port>_<protocol>
<alias>_PORT_<port>_<protocol>_PORT
<alias>_PORT_<port>_<protocol>_PROTO
<alias>_PORT_<port>_<protocol>_ADDR

其中<port>是在Dockerfile中使用EXPOSE匯出的埠,還有docker run 的時候使用-p匯出的埠。<protocol>則是這些埠對應的協議。

  • 第五個部分就是ALIASDB_PORT這個變數,這個變數是EXPOSE匯出埠中的第一個埠對應的連線url,
    如果有EXPOSE匯出的埠,還有docker run -p指定匯出的埠,那麼通過-p指定的埠是第一個被匯出的埠

link機制和/etc/hosts

使用了link機制後,可以通過指定的名字來和目標容器通訊,這其實是通過給/etc/hosts中加入名稱和IP的解析關係來實現的,下面是名為web的容器中的/etc/hosts資訊.

172.17.0.10 6337c0044215
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.8  aliasdb c1a7c7f091eb db

通過上面的資訊可以看出,link機制給received container(這裡是名為web的容器)新增了一條關於db容器的名稱解析。有了這個名稱解析後就可以不使用ip來和目標容器通訊了,除此之外當目標容器重啟,docker會負責更新/etc/hosts檔案,因此可以不用擔心容器重啟後IP地址發生了改變,解析無法生效的問題。但是很不幸的是,環境變數無法更新,上文中提到了link機制會通過環境變數將一些db容器的資訊匯入到web容器中,這種匯入是一次性的,此後這個容器更新了環境變數的資訊是無法在web容器中更新的。

link機制和網路新特性

通過上文中對link機制的介紹,可以發現link機制提供瞭如下幾個功能

  • 名稱解析
  • 對link的容器可以使用別名
  • 安全的容器間連線通訊
  • 環境變數的注入

安全的容器間連線通訊,這個需要結合docker daemon的-icc=false 這個選項,預設同一個宿主機上的所有容器可以互相通訊,當使用-icc=false 的時候所有容器之間是無法進行互相通訊的(具體原因會單獨出篇文章分析),但是使用link機制後,即使使用了-icc=false 兩個容器之間也可以進行基於埠的通訊。很不幸的是當docker引入網路新特性後,link機制變的有些多餘,但是為了相容早期版本,–link機制在預設網路上的功能依舊沒有發生變化,docker引入網路新特性後,內建了一個DNS Server,但是隻有使用者建立了自定義網路後,這個DNS Server才會起作用。在網路新特性為未引入之前,有三種網路,第一種就是docker0這種橋接網路,用的也是最多的,第二個則是複用主機網路,稱為HOST網路,第三種就是none網路,只建立了一個空的網路名稱空間,沒有網路介面,無法和外界通訊,可以讓使用者自己去構建網路。當網路新特性引入後,有了overlay網路,有了使用者自定義網路。使用者自定義網路下,使用者可以通過docker的network子命令建立一個自定義的橋接網路,這個自定義橋接的網路和預設的docker0橋接網路基本功能都是一致的,只是在這個自定義橋接網路中擁有一些特性,可以替代link機制。這些特性包括如下幾個方面:

  • 基於DNS的名稱自動解析
  • 安全的隔離環境
  • 動態的附加或者脫離一個網路
  • 支援使用–link設定別名

在使用者自定義網路下,不使用link機制就可以實現名稱解析功能了,不再是通過link機制追加名稱解析關係到/etc/hosts檔案中了。並且在預設的docker0橋接網路和自定義網路下使用link機制的效果是不一樣的,在自定義網路中link機制只是負責設定別名的,不再提供環境變數注入的功能了。自定義網路中同時也提供了--net-alias功能和link機制提供別名功能是一樣的。保留link機制目的是為了相容。

參考文獻

Legacy container links
Linking containers in user-defined networks


相關文章