docker容器互联

有一个问题,同一宿主机上的容器之间如何进行通信。我觉得最原始的方法可以在run容器的时候指定一个端口映射,其他容器访问本机这个被映射的端口即可实现。但这个方法似乎有些不太友好,当宿主机上存在大量的容器我们要配置大量的端口映射,且这样会导致所有的容器之间都可以互相通信,无法做到网络隔离。如何做到不同集群之间网络隔离且同集群容器之间可以互相通信呢?答案是自定义网络即docker network

docker的桥接网络模式

docker具有五种网络模式,具体可以参考:初探Docker的网络模式
当我们run一个镜像的时候可以指定-- net host指定网络,如果不指定使用的是默认的bridge网络。该网络即接在是ip a下docker0虚拟网卡上的

# 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的网段是172.17.0.0/16,网关是172.17.0.1。且这个网络下挂了一个app1的容器,该容器ip是172.17.0.2。
网段是16的话该段下可以分配2^8 * 2^8 -1=65535个ip, 如果网段是24的话可以分配2^8-1=255个ip

创建网络

# docker network create --help  # 参数
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
# # 创建一个叫mynet1的网络,默认是bridge模式,该参数可省略
# docker network create --subnet 172.18.0.0./16 --gateway 172.18.0.1 --driver bridge mynet1  

创建完后宿主机上ip a会发现多了一个网卡

# 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

容器互联

这里摘自docker中文社区的一段文字

在同一个网络中的容器,可以互联,并且,Docker 内置了 DNS,容器内的应用可以使用服务名、容器名、别名来进行服务发现,名称会经由内置的 DNS 进行解析,其结果是动态的;而不在同一网络中的容器,不可以互联。现在早就不用 --link 了,而且非常不建议使用。

这里说明了为什么不用link,原文链接
这里我使用了docker-compose官方文档(链接)上的例子(我做了修改)。两个镜像,创建了三个容器,其中app1在bridge网络即172.17下,app2和redis在mynet网络即172.18下,app1和app2都需要连接redis。

创建镜像与启动容器

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 .  # 构建镜像
# docker run -d -P --name app1 app  # app1在bridge网络下
# docker run -d -P --net mynet1 --name app2 app  # app2在mynet1网络下
# docker pull redis
# docker run -d -P --net mynet1 --name redis redis  # redis,在mynet1网络下
# 
# 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

网络打通

app, ip, 端口对于关系如下

容器 宿主机端口 容器内端口 ip
app1 49159 5000 172.17.0.2
app2 49157 5000 172.18.0.4
redis 6379 172.18.0.3

注意,这里我没有开启redis的端口映射
此时app2和redis在同一网络下,访问app2正常。app1与redis不在同一网段,因此访问app1将会报错。

# curl  127.0.0.1:49157
{"code":200,"message":"Hello World! I have been seen 13 times"}
# # 宿主机和子网是通的
# 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网段,即现在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发现下面多了一个mynet1网络,并为其分配了一个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"}

这个时候回发现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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容