dockercompose设置静态ip和link与depends_on的区别
在容器中设置静态ip,这似乎又是一个过时的老话题,但是在讨论群中仍然有朋友为此感到困惑。我致力于解决这些小问题和在使用中容器落地的问题。为此,我又写了这篇文章来描述容器中使用静态ip,和不使用静态ip link的技巧。
在正式配置docker-compose之前,我们需要先了解link,因为在我看来在容器中使用固定ip是件没有必要的事情,使用ip只是我们脑中长久的一个使用习惯。而在docker中link已经帮我们解决了这个麻烦事,并提供了更简单的方式。
那么,通常来讲,在这个问题上产生疑问的,必然是在使用两个以上的容器。那就有必要了解depends_on。在使用link的同时,我当然也会叙述另外一个常用的选项depends_on,它非常有用。并且我会做简单的比较。
阅读本篇文章,你将了解,docker-compose中3版本的使用,以及link使用方式和depends_on的技巧。
容器间互联
尽管,我们要解决的是单机网络,也不妨先简单介绍下跨主机和不跨主机容器间互联的区别
- 不跨主机互相访问
不跨主机互联可以采用默认的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转发访问。
[root@linuxea.com ~]# telnet 172.25.50.250 6379 Trying 172.25.50.250... Connected to 172.25.50.250. Escape character is '^]'.
当然,也有例外,如果是叠加的方式就不需要在物理机做端口映射,直接通过隧道访问对端ip和端口
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本身。
[root@linuxea.com /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
因为在同一个网络内。
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:
我非常有必要提醒,启动和准备就绪是两个概念 ,启动并不意味着一定就启动完成,就像点击开机并不意味着马上就进入桌面。其中的就绪状态则是另外的问题。请参阅启动顺序策略。
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如下
[root@linuxea.com /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进行访问
[root@linuxea.com /opt/2019/net]# docker-compose -f ./docker-compose.yaml up -d Creating redis ... done Creating softether4.27 ... done
[root@linuxea.com /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,并且我能够在配置文件中对这些地址进行硬编码。
延伸阅读
linuxea:白话容器之虚拟化网络与容器网络(8)
linuxea:白话容器之docker网络(9)
学习更多
学习如何使用Docker CLI命令,Dockerfile命令,使用这些命令可以帮助你更有效地使用Docker应用程序。查看Docker文档和我的其他帖子以了解更多信息。
- docker目录
- 白话容器
- docker-compose