有一個問題,同一宿主機上的容器之間如何進行通信诫舅。我覺得最原始的方法可以在run容器的時候指定一個端口映射循狰,其他容器訪問本機這個被映射的端口即可實現(xiàn)。但這個方法似乎有些不太友好痊末,當宿主機上存在大量的容器我們要配置大量的端口映射,且這樣會導致所有的容器之間都可以互相通信哩掺,無法做到網(wǎng)絡隔離凿叠。如何做到不同集群之間網(wǎng)絡隔離且同集群容器之間可以互相通信呢?答案是自定義網(wǎng)絡即docker network
docker的橋接網(wǎng)絡模式
docker具有五種網(wǎng)絡模式,具體可以參考:初探Docker的網(wǎng)絡模式
當我們run一個鏡像的時候可以指定-- net host
指定網(wǎng)絡盒件,如果不指定使用的是默認的bridge網(wǎng)絡蹬碧。該網(wǎng)絡即接在是ip a
下docker0虛擬網(wǎng)卡上的
# ip a
1. ...
2. ...
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 02:42:91:b9:20:9f brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "937b66d796bdb5d16b4f7fb80cae0ff55e40a021db763d64b4cb4bfa3de671ef",
"Created": "2021-04-18T13:10:46.978091586+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"5aecaa04db4f7effe826b60e4a6a9c4f7e695c8de32f5fe73566f57209b32968": {
"Name": "app1",
"EndpointID": "f0633f5548b4290f7b057d6b08aea3d81e70084fbe4a06b151a2797fd27f33cb",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
這里可以看出我的bridge的網(wǎng)段是172.17.0.0/16,網(wǎng)關(guān)是172.17.0.1炒刁。且這個網(wǎng)絡下掛了一個app1的容器恩沽,該容器ip是172.17.0.2。
網(wǎng)段是16的話該段下可以分配個ip翔始, 如果網(wǎng)段是24的話可以分配
個ip
創(chuàng)建網(wǎng)絡
# docker network create --help # 參數(shù)
Usage: docker network create [OPTIONS] NETWORK
Create a network
Options:
--attachable Enable manual container attachment
--aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])
--config-from string The network from which to copy the configuration
--config-only Create a configuration only network
-d, --driver string Driver to manage the Network (default "bridge")
--gateway strings IPv4 or IPv6 Gateway for the master subnet
--ingress Create swarm routing-mesh network
--internal Restrict external access to the network
--ip-range strings Allocate container ip from a sub-range
--ipam-driver string IP Address Management Driver (default "default")
--ipam-opt map Set IPAM driver specific options (default map[])
--ipv6 Enable IPv6 networking
--label list Set metadata on a network
-o, --opt map Set driver specific options (default map[])
--scope string Control the network's scope
--subnet strings Subnet in CIDR format that represents a network segment
# # 創(chuàng)建一個叫mynet1的網(wǎng)絡罗心,默認是bridge模式,該參數(shù)可省略
# docker network create --subnet 172.18.0.0./16 --gateway 172.18.0.1 --driver bridge mynet1
創(chuàng)建完后宿主機上ip a
會發(fā)現(xiàn)多了一個網(wǎng)卡
# ip a
......
4: br-a32d7755e777: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 02:42:e3:50:de:6f brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 scope global br-a32d7755e777
valid_lft forever preferred_lft forever
容器互聯(lián)
這里摘自docker中文社區(qū)的一段文字
在同一個網(wǎng)絡中的容器城瞎,可以互聯(lián)渤闷,并且,Docker 內(nèi)置了 DNS脖镀,容器內(nèi)的應用可以使用服務名飒箭、容器名、別名來進行服務發(fā)現(xiàn)蜒灰,名稱會經(jīng)由內(nèi)置的 DNS 進行解析弦蹂,其結(jié)果是動態(tài)的;而不在同一網(wǎng)絡中的容器卷员,不可以互聯(lián)∮遥現(xiàn)在早就不用 --link 了,而且非常不建議使用毕骡。
這里說明了為什么不用link削饵,原文鏈接
這里我使用了docker-compose官方文檔(鏈接)上的例子(我做了修改)。兩個鏡像未巫,創(chuàng)建了三個容器窿撬,其中app1在bridge網(wǎng)絡即172.17下,app2和redis在mynet網(wǎng)絡即172.18下叙凡,app1和app2都需要連接redis劈伴。
創(chuàng)建鏡像與啟動容器
app.py
import time
import redis
from flask import Flask, jsonify
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
data = {'code': 200, 'message': 'Hello World! I have been seen {} times'.format(count)}
return jsonify(data)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Dockerfile
FROM python:3.7
WORKDIR /root
COPY app.py /root/
COPY requirements.txt /root/requirements.txt
RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
EXPOSE 5000
CMD ["python", "app.py"]
run
# docker build -t app . # 構(gòu)建鏡像
# docker run -d -P --name app1 app # app1在bridge網(wǎng)絡下
# docker run -d -P --net mynet1 --name app2 app # app2在mynet1網(wǎng)絡下
# docker pull redis
# docker run -d -P --net mynet1 --name redis redis # redis,在mynet1網(wǎng)絡下
#
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5aecaa04db4f app "python app.py" 2 hours ago Up 2 hours 0.0.0.0:49159->5000/tcp, :::49159->5000/tcp app1
62d8e19385c1 app "python app.py" 2 hours ago Up 2 hours 0.0.0.0:49157->5000/tcp, :::49157->5000/tcp app2
0e22a3a9d2ab de974760ddb2 "docker-entrypoint.s…" 2 hours ago Up 2 hours 6379/tcp redis
網(wǎng)絡打通
app, ip, 端口對于關(guān)系如下
容器 | 宿主機端口 | 容器內(nèi)端口 | ip |
---|---|---|---|
app1 | 49159 | 5000 | 172.17.0.2 |
app2 | 49157 | 5000 | 172.18.0.4 |
redis | 6379 | 172.18.0.3 |
注意握爷,這里我沒有開啟redis的端口映射
此時app2和redis在同一網(wǎng)絡下跛璧,訪問app2正常。app1與redis不在同一網(wǎng)段新啼,因此訪問app1將會報錯追城。
# curl 127.0.0.1:49157
{"code":200,"message":"Hello World! I have been seen 13 times"}
# # 宿主機和子網(wǎng)是通的
# curl 172.18.0.4:5000
{"code":200,"message":"Hello World! I have been seen 14 times"}
# # app1與redis不通, 查看app的日志可以看到連接不上redis
# docker logs app1
redis.exceptions.ConnectionError: Error -2 connecting to redis:6379. Name or service not known.
怎么使app1與redis通呢燥撞?我們可以讓app1連接上mynet1網(wǎng)段座柱,即現(xiàn)在app1有兩個ip迷帜,一個在bridge,一個mynet1色洞。
# docker network connet --help
Usage: docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
# docker network connect mynet1 app1
這個時候查看docker inspect app1
的Networks發(fā)現(xiàn)下面多了一個mynet1網(wǎng)絡戏锹,并為其分配了一個172.18.0.2的ip
# curl 172.18.0.2:5000
{"code":200,"message":"Hello World! I have been seen 15 times"}
# curl 172.17.0.2:5000
{"code":200,"message":"Hello World! I have been seen 16 times"}
# curl 127.0.0.1:49159
{"code":200,"message":"Hello World! I have been seen 17 times"}
這個時候回發(fā)現(xiàn)app1與app2直接也是互相通的
# docker exec -it app1 ping app2
PING app2 (172.18.0.4) 56(84) bytes of data.
64 bytes from app2.mynet1 (172.18.0.4): icmp_seq=1 ttl=64 time=0.069 ms
64 bytes from app2.mynet1 (172.18.0.4): icmp_seq=2 ttl=64 time=0.077 ms
# docker exec -it app2 ping app1
PING app1 (172.18.0.2) 56(84) bytes of data.
64 bytes from app1.mynet1 (172.18.0.2): icmp_seq=1 ttl=64 time=0.050 ms
64 bytes from app1.mynet1 (172.18.0.2): icmp_seq=2 ttl=64 time=0.072 ms