前言
在實踐中,自己會遇到2個容器之間互相訪問通信的問題,這個時候就用到了docker run --link選項。自己也花了一段時間泡官網(wǎng)研究了--link的用法,把自己對--link的理解分享下。注意!docker官方已不推薦使用docker run --link來鏈接2個容器互相通信,隨后的版本中會刪除--link,但了解其原理,對如何使2個容器之間互相通信還是有幫助的。
1. docker run --link的作用
docker run --link可以用來鏈接2個容器,使得源容器(被鏈接的容器)和接收容器(主動去鏈接的容器)之間可以互相通信,并且接收容器可以獲取源容器的一些數(shù)據(jù),如源容器的環(huán)境變量。
--link的格式:
--link <name or id>:alias
其中,name和id是源容器的name和id,alias是源容器在link下的別名。
eg:
源容器
docker run -d --name selenium_hub selenium/hub
創(chuàng)建并啟動名為selenium_hub的容器。
接收容器
docker run -d --name node --link selenium_hub:hub selenium/node-chrome-debug
創(chuàng)建并啟動名為node的容器,并把該容器和名為selenium_hub的容器鏈接起來。其中:
--link selenium_hub:hub
selenium_hub是上面啟動的1cbbf6f07804容器的名字,這里作為源容器,hub是該容器在link下的別名(alias),通俗易懂的講,站在node容器的角度,selenium_hub和hub都是1cbbf6f07804容器的名字,并且作為容器的hostname,node用這2個名字中的哪一個都可以訪問到1cbbf6f07804容器并與之通信(docker通過DNS自動解析)。我們可以來看下:
進(jìn)入node容器:
docker exec -it node /bin/bash
root@c4cc05d832e0:~# ping selenium_hub
PING hub (172.17.0.2) 56(84) bytes of data.
64 bytes from hub (172.17.0.2): icmp_seq=1 ttl=64 time=0.184 ms
64 bytes from hub (172.17.0.2): icmp_seq=2 ttl=64 time=0.133 ms
64 bytes from hub (172.17.0.2): icmp_seq=3 ttl=64 time=0.216 ms
root@c4cc05d832e0:~# ping hub
PING hub (172.17.0.2) 56(84) bytes of data.
64 bytes from hub (172.17.0.2): icmp_seq=1 ttl=64 time=0.194 ms
64 bytes from hub (172.17.0.2): icmp_seq=2 ttl=64 time=0.218 ms
64 bytes from hub (172.17.0.2): icmp_seq=3 ttl=64 time=0.128 ms
可見,selenium_hub和hub都指向172.17.0.2。
2. --link下容器間的通信
按照上例的方法就可以成功的將selenium_hub和node容器鏈接起來,那這2個容器間是怎么通信傳送數(shù)據(jù)的呢?另外,前言中提到的接收容器可以獲取源容器的一些信息,比如環(huán)境變量,又是怎么一回事呢?
源容器和接收容器之間傳遞數(shù)據(jù)是通過以下2種方式:
- 設(shè)置環(huán)境變量
- 更新/etc/hosts文件
2.1 設(shè)置環(huán)境變量
- 當(dāng)使用--link時,docker會自動在接收容器內(nèi)創(chuàng)建基于--link參數(shù)的環(huán)境變量:
docker會在接收容器中設(shè)置名為<alias>_NAME的環(huán)境變量,該環(huán)境變量的值為:
<alias>_NAME=/接收容器名/源容器alias
我們進(jìn)入node容器,看下此環(huán)境變量:
docker exec -it node /bin/bash
seluser@c4cc05d832e0:/$ env | grep -i hub_name
HUB_NAME=/node/hub
可見,確實有名為HUB_NAME=/node/hub的環(huán)境變量存在。
另外,docker還會在接收容器中創(chuàng)建關(guān)于源容器暴露的端口號的環(huán)境變量,這些環(huán)境變量有一個統(tǒng)一的前綴名稱:
<name>PORT<port>_<protocol>
其中:
<name>表示鏈接的源容器alias
<port>是源容器暴露的端口號
<protocol>是通信協(xié)議:TCP or UDP
docker用上面定義的前綴定義3個環(huán)境變量:
<name>PORT<port>_<protocol>ADDR
<name>PORT<port><protocol>PORT
<name>PORT<port><protocol>_PROTO
注意,若源容器暴露了多個端口號,則每1個端口都有上面的一組環(huán)境變量(包含3個環(huán)境變量),即若源容器暴露了4個端口號,則會有4組12個環(huán)境變量。
查看selenium/hub的Dockerfile,可見只暴露了4444端口號:
EXPOSE 4444
我們進(jìn)入node容器,看這些此環(huán)境變量:
docker exec -it node /bin/bash
seluser@c4cc05d832e0:/$ env | grep -i HUB_PORT_4444_TCP_
HUB_PORT_4444_TCP_PROTO=tcp
HUB_PORT_4444_TCP_ADDR=172.17.0.2
HUB_PORT_4444_TCP_PORT=4444
可見,確實有3個以<name>PORT<port><protocol>為前綴的環(huán)境變量存在。
另外,docker還在接收容器中創(chuàng)建1個名為<alias>_PORT的環(huán)境變量,值為源容器的URL:源容器暴露的端口號中最小的那個端口號。
我們進(jìn)入node容器,看下此環(huán)境變量:
docker exec -it node /bin/bash
seluser@c4cc05d832e0:/$ env | grep -i HUB_PORT=
HUB_PORT=tcp://172.17.0.2:4444
可見,此環(huán)境變量的確存在。
- 接收容器還會獲取源容器暴露的環(huán)境變量,這些變量包括:
- 源容器Dockerfile中ENV標(biāo)簽設(shè)置的環(huán)境變量
- 源容器用docker run命令創(chuàng)建,命令中包含的 -e或--env或--env-file設(shè)置的環(huán)境變量
docker會在接收容器中創(chuàng)建一些環(huán)境變量,這些環(huán)境變量是的值是關(guān)于源容器本身的環(huán)境變量的值。這些環(huán)境變量的定義格式為:
<alias>ENV<name>
查看selenium/hub的Dockerfile,可見Dockerfile中ENV標(biāo)簽設(shè)置的環(huán)境變量有:
# As integer, maps to "maxSession"
ENV GRID_MAX_SESSION 5
# In milliseconds, maps to "newSessionWaitTimeout"
ENV GRID_NEW_SESSION_WAIT_TIMEOUT -1
# As a boolean, maps to "throwOnCapabilityNotPresent"
ENV GRID_THROW_ON_CAPABILITY_NOT_PRESENT true
# As an integer
ENV GRID_JETTY_MAX_THREADS -1
# In milliseconds, maps to "cleanUpCycle"
ENV GRID_CLEAN_UP_CYCLE 5000
# In seconds, maps to "browserTimeout"
ENV GRID_BROWSER_TIMEOUT 0
# In seconds, maps to "timeout"
ENV GRID_TIMEOUT 30
# Debug
ENV GRID_DEBUG false
我們進(jìn)入selenium_hub容器,看下這些環(huán)境變量:
root@ubuntu:~# docker exec -it selenium_hub /bin/bash
seluser@1cbbf6f07804:/$ env | grep -i grid_
GRID_DEBUG=false
GRID_TIMEOUT=30
GRID_CLEAN_UP_CYCLE=5000
GRID_MAX_SESSION=5
GRID_JETTY_MAX_THREADS=-1
GRID_BROWSER_TIMEOUT=0
GRID_THROW_ON_CAPABILITY_NOT_PRESENT=true
GRID_NEW_SESSION_WAIT_TIMEOUT=-1
我們再進(jìn)入node容器,看下node容器中關(guān)于selenium_hub的<alias>ENV<name>環(huán)境變量:
docker exec -it node /bin/bash
seluser@c4cc05d832e0:/$ env | grep -i hub_env
HUB_ENV_GRID_DEBUG=false
HUB_ENV_GRID_TIMEOUT=30
HUB_ENV_DEBCONF_NONINTERACTIVE_SEEN=true
HUB_ENV_GRID_CLEAN_UP_CYCLE=5000
HUB_ENV_GRID_MAX_SESSION=5
HUB_ENV_TZ=UTC
HUB_ENV_GRID_JETTY_MAX_THREADS=-1
HUB_ENV_DEBIAN_FRONTEND=noninteractive
HUB_ENV_GRID_BROWSER_TIMEOUT=0
HUB_ENV_GRID_THROW_ON_CAPABILITY_NOT_PRESENT=true
HUB_ENV_GRID_NEW_SESSION_WAIT_TIMEOUT=-1
可見,selenium_hub容器中的GRID_* 環(huán)境變量均在node容器中被創(chuàng)建,只不過名稱變?yōu)镠UB_ENV_GRID_* 而已。
環(huán)境變量的注意事項
注意,接收容器環(huán)境變量中存儲的源容器的IP,不會自動更新,即,若源容器重啟,則接收容器環(huán)境變量中存儲的源容器的IP很可能就失效了。所以,docker官方建議使用/etc/hosts來解決上述的IP失效問題。
2.2 更新/etc/hosts文件
docker會將源容器的host更新到目標(biāo)容器的/etc/hosts中:
我們再進(jìn)入node容器,查看node容器中的/etc/hosts文件的內(nèi)容:
docker exec -it node /bin/bash
seluser@c4cc05d832e0:/$ cat /etc/hosts
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.2 hub 1cbbf6f07804 selenium_hub
172.17.0.3 c4cc05d832e0
其中172.17.0.3是node容器的ip,并使用node容器的容器id作為host name。另外,源容器的ip和hostname也寫進(jìn)來了,172.17.0.2是selenium_hub容器的ip,hub是容器在link下的alias,后面是hub容器的容器id。
如果重啟了源容器,接收容器的/etc/hosts會自動更新源容器的新ip。
總結(jié)
在--link標(biāo)簽下,接收容器就是通過設(shè)置環(huán)境變量和更新/etc/hosts文件來獲取源容器的信息,并與之建立通信和傳遞數(shù)據(jù)的。
在docker的后續(xù)版本中,會取消docker run中的--link選項,但了解其如何在2個容器之間建立通信的原理是非常有用的,因為這有助于理解如何用官方推薦的所有容器在同一個network下來通信的方法,以及用docker-compose來鏈接2個容器來通信的方法。
9月初就用--link方法連接了seleniumhub和seleniumnode容器,但是不明白--link的作用,最近花了幾天時間讀官方文檔,終于算搞清楚了,也把自己的理解在這里分享下。