在容器中設置靜態ip,這似乎又是一個過時的老話題,但是在討論羣中仍然有朋友爲此感到困惑。我致力於解決這些小問題和在使用中容器落地的問題。爲此,我又寫了這篇文章來描述容器中使用靜態ip,和不使用靜態ip link的技巧。
在正式配置docker-compose之前,我們需要先了解link,因爲在我看來在容器中使用固定ip是件沒有必要的事情,使用ip只是我們腦中長久的一個使用習慣。而在docker中link已經幫我們解決了這個麻煩事,並提供了更簡單的方式。
那麼,通常來講,在這個問題上產生疑問的,必然是在使用兩個以上的容器。那就有必要了解depends_on。在使用link的同時,我當然也會敘述另外一個常用的選項depends_on,它非常有用。並且我會做簡單的比較。
閱讀本篇文章,你將瞭解,docker-compose中3版本的使用,以及link使用方式和depends_on的技巧。
I. 容器間互聯
儘管,我們要解決的是單機網絡,也不妨先簡單介紹下跨主機和不跨主機容器間互聯的區別
不跨主機互聯可以採用默認的bridge網絡,docker0橋在物理機上,而後創建的容器,容器內有eth0,另外一側在物理機的docker0,而docker0可以理解成一個虛擬交換機。這樣同一個交換機內的容器就可以直接進行互聯。
除此之外,還可以使用host網絡模式,共用宿主機網絡,這樣一來容器和宿主機使用同一個網絡,不隔離網絡名稱空間,網卡信息,就不存在網絡上的問題。
最後還可以採用聯盟式容器解決網絡問題
容器跨主機訪問實際上是做了DNAT,將容器發佈出去,這些規則在iptables中可以看到
Chain DOCKER (3 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT tcp -- !br-77c0aabda308 br-77c0aabda308 0.0.0.0/0 172.18.0.2 tcp dpt:26379
0 0 ACCEPT tcp -- !br-77c0aabda308 br-77c0aabda308 0.0.0.0/0 172.18.0.2 tcp dpt:6379
0 0 ACCEPT tcp -- !br-77c0aabda308 br-77c0aabda308 0.0.0.0/0 172.18.0.3 tcp dpt:1194
0 0 ACCEPT tcp -- !br-77c0aabda308 br-77c0aabda308 0.0.0.0/0 172.18.0.3 tcp dpt:443
而對於互相雙方來講,是看不到後面的容器的,訪問的是通過端口轉發到真正的容器端口上。如果你要跨主機訪問,就不能使用容器的ip,只能使用宿主機的ip和容器映射的端口通過iptables轉發訪問。
[[email protected] ~]# telnet 172.25.50.250 6379
Trying 172.25.50.250...
Connected to 172.25.50.250.
Escape character is '^]'.
當然,也有例外,如果是疊加的方式就不需要在物理機做端口映射,直接通過隧道訪問對端ip和端口
II. link連接
我們瞭解到在容器網絡中的分配ip是不固定的,倘若我在第一次使用的ip地址在後面使用中發生了改變,那可能不無法正常使用了!這顯然並不是我們想要的。link就解決了這個問題。links似乎將會被棄用,因爲它並不重要(不使用link仍然可以通過容器名稱訪問),我們主要來看這裏的別名操作
docker-compose如下:
version: '3'
services:
redis:
image: marksugar/redis:5.0.0
container_name: redis
restart: always
privileged: true
environment:
- REDIS_CONF=on
- REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh
- MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh
- MAXCLIENTS_NUM=600
- MAXMEMORY_SIZE=4096M
volumes:
- /etc/localtime:/etc/localtime:ro
- /data/redis-data:/data/redis:Z
- /data/logs:/data/logs
ports:
- '6379:6379'
- '26379:26379'
softether:
image: marksugar/softether:4.27
links:
- "redis:redisdb"
container_name: softether4.27
restart: always
ports:
- '443:443'
- '1194:1194'
請注意,其中
links:
- "redis:redisdb"
softether和redis的容器ip地址分別是172.18.0.2和172.25.8.0.3,假設此時我們並不知道ip
這裏在softether中鏈接了redis,並設置了別名,redisdb。那也就是說我們可以使用redisdb來訪問redis本身。
[[email protected] /opt/2019/net]# docker exec -it softether4.27 sh
/ # apk update
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
v3.8.2-56-g4d33ed061d [http://dl-cdn.alpinelinux.org/alpine/v3.8/main]
v3.8.2-53-g53558ad6fc [http://dl-cdn.alpinelinux.org/alpine/v3.8/community]
OK: 9559 distinct packages available
/ # apk add redis
(1/1) Installing redis (4.0.11-r0)
Executing redis-4.0.11-r0.pre-install
Executing redis-4.0.11-r0.post-install
Executing busybox-1.28.4-r0.trigger
OK: 68 MiB in 35 packages
/ # redis-cli -a OTdmOWI4ZTM4NTY1M2M4OTZh -h redisdb info
Warning: Using a password with '-a' option on the command line interface may not be safe.
# Server
redis_version:5.0.0
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:a7a8d032c5a69a3f
redis_mode:standalone
os:Linux 4.18.12-1.el7.elrepo.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:6.4.0
process_id:10
run_id:c6162ba2b02d70c1defda6073f863af1ccb207a6
tcp_port:6379
uptime_in_seconds:263
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:7302247
這說明了什麼?
我們完全可以使用容器的名稱來進行訪問,並不需要使用ip地址來指定。因爲ip會變,名稱卻不會變,這是因爲容器的hosts
/ # 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.18.0.3 d374cbffc3b0
在hosts中,已經寫了ip和容器id的對應關係。你完全可以使用容器名稱來進行訪問。
tips
你可以不使用別名,直接使用容器名稱進行訪問,你會看到redis容器的ip地址
/ # ping redis
PING redis (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.093 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.143 ms
64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.106 ms
64 bytes from 172.18.0.2: seq=3 ttl=64 time=0.152 ms
因爲在同一個網絡內。
III. depends_on
我們在看另外一個場景,假如此刻softether依賴於redis,在啓動的時候就需要讀取redis或者寫入,通常情況下,redis必然要先啓動,redis啓動,softether在啓動。這纔是正確的方式,如果softether先啓動,而redis還沒有就緒,程序必然會報錯,甚至崩潰。此時depends_on就有了用武之地
如上場景中那般,配置如下:
redis:
image: marksugar/redis:5.0.0
...
softether:
image: marksugar/softether:4.27
...
depends_on:
- redis
這樣,softether會在redis啓動之後啓動。
如果你有多個依賴,就可以按照順序往後寫,比如mysql
redis:
image: marksugar/redis:5.0.0
...
mysql:
image:
...
softether:
image: marksugar/softether:4.27
...
depends_on:
- redis
- mysql
這樣的順序就是,softether會等待,先啓動redis,在啓動mysql,依次啓動纔到softether。由此可見,depends_on和links完全是兩個不同的東西。
- tips:
我非常有必要提醒,啓動和準備就緒是兩個概念 ,啓動並不意味着一定就啓動完成,就像點擊開機並不意味着馬上就進入桌面。其中的就緒狀態則是另外的問題。請參閱啓動順序策略。
IV. docker-compose 靜態ip
默認情況下,docker會爲容器分配隨機(某種......)IP地址。通過使用鏈接,您可以將條目添加到容器的hosts文件中,並使用其IP地址映射另一個容器的名稱。這樣您就不需要知道其IP地址,只需使用其名稱即可通過網絡訪問它。這種通過容器的hosts文件繼而使用容器名稱進行訪問,這似乎已經解決了一大半人的問題。
但是,我們仍然可以使用靜態ip。上面我提到,網絡是會發生改變的,爲了徹底解決這一點,我們將網關,子網都設置好。
在上面的默認網絡中使用的ip是172.18.0.0網段。現在,我們修改它
version: '3.7'
services:
redis:
networks:
linuxea:
ipv4_address: 172.2.0.10
...
softether:
networks:
linuxea:
ipv4_address: 172.2.0.11
...
networks:
linuxea:
ipam:
driver: default
config:
- subnet: 172.2.0.0/24
docker-compose如下
[[email protected] /opt/2019/net]# cat docker-compose.yaml
version: '3.7'
services:
redis:
image: marksugar/redis:5.0.0
container_name: redis
restart: always
privileged: true
environment:
- REDIS_CONF=on
- REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh
- MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh
- MAXCLIENTS_NUM=600
- MAXMEMORY_SIZE=4096M
volumes:
- /etc/localtime:/etc/localtime:ro
- /data/redis-data:/data/redis:Z
- /data/logs:/data/logs
ports:
- '6379:6379'
- '26379:26379'
networks:
linuxea:
ipv4_address: 172.2.0.10
softether:
image: marksugar/softether:4.27
links:
- "redis:redisdb"
container_name: softether4.27
restart: always
ports:
- '443:443'
- '1194:1194'
networks:
linuxea:
ipv4_address: 172.2.0.11
networks:
linuxea:
ipam:
driver: default
config:
- subnet: 172.2.0.0/24
現在,你就可以使用靜態的ip進行訪問
[[email protected] /opt/2019/net]# docker-compose -f ./docker-compose.yaml up -d
Creating redis ... done
Creating softether4.27 ... done
[[email protected] /opt/2019/net]# docker exec -it softether4.27 sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
293: eth0@if294: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:02:00:0b brd ff:ff:ff:ff:ff:ff
inet 172.2.0.11/24 brd 172.2.0.255 scope global eth0
valid_lft forever preferred_lft forever
安裝redis-client
/ # apk update
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
v3.8.2-56-g4d33ed061d [http://dl-cdn.alpinelinux.org/alpine/v3.8/main]
v3.8.2-53-g53558ad6fc [http://dl-cdn.alpinelinux.org/alpine/v3.8/community]
OK: 9559 distinct packages available
/ # apk add redis
(1/1) Installing redis (4.0.11-r0)
Executing redis-4.0.11-r0.pre-install
Executing redis-4.0.11-r0.post-install
Executing busybox-1.28.4-r0.trigger
OK: 68 MiB in 35 packages
通過靜態ip鏈接
/ # redis-cli -a OTdmOWI4ZTM4NTY1M2M4OTZh -h 172.2.0.10
Warning: Using a password with '-a' option on the command line interface may not be safe.
172.2.0.10:6379> info
# Server
redis_version:5.0.0
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:a7a8d032c5a69a3f
redis_mode:standalone
os:Linux 4.18.12-1.el7.elrepo.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:6.4.0
process_id:10
run_id:cbf1e8eacc74da75c3dfcf797d104a8a2f95076e
使用ipam
可以爲新網絡定義特定的CIDR塊,然後將每個容器連接到該網絡,可以在該範圍上指定其IP地址。
現在,redis始終使用IP地址172.2.0.10運行,softether運行172.2.0.11,並且我能夠在配置文件中對這些地址進行硬編碼。
V. 延伸閱讀
VI. 學習更多
學習如何使用Docker CLI命令,Dockerfile命令,使用這些命令可以幫助你更有效地使用Docker應用程序。查看Docker文檔和我的其他帖子以瞭解更多信息。