Docker容器网络

前言

Docker有四种网络连接方式.

  1. 单机模式: bridgehostnone.
  2. 多机模式: overlay
    Docker会自动创建3个网络.
    1
    2
    3
    4
    5
    docker network ls
    # NETWORK ID NAME DRIVER SCOPE
    # 7b083adc391a bridge bridge local
    # 5855c878dc68 host host local
    # 9895a4ab897d none null local

Docker Container 之间的网络连接

如果我有一个应用的容器, 需要连接到MySQL的容器, 那么就需要建立两个容器之间的网络连接.
Docker提供了一个--link <container>的参数, 用来连接到其他的container.

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 创建一个 bridge1 容器, 并查看ip地址
docker run -d --name bridge1 alpine /bin/ping 127.0.0.1
docker exec -it bridge1 hostname -i
# 172.17.0.4

# 2. 创建一个 bridge2 容器, 并 ping bridge1 的 ip 地址
docker run -d --name bridge2 --link bridge1 alpine /bin/ping 127.0.0.1
docker exec -it bridge2 /bin/ping -c 3 172.17.0.4
docker exec -it bridge2 /bin/ping -c 3 bridge1

# 3. 默认的 bridge 是单向的, bridge1 ping bridge2 失败
docker exec -it bridge1 /bin/ping bridge2

发现bridge2可以直接ping bridge1, 不用输入IP地址.
因为--link bridge1相当于给bridge2添加了一条DNS解析.
而且, 默认的 bridge 是单向的, 这句话先记着, 后面讲

那这两个容器, 是用哪个network连接的呢?
答案是默认创建的名为bridgebridge类型的network. 执行下面语句

1
2
3
4
5
# docker network inspect <network name>
# docker network inspect bridge # 输出详细信息
docker network inspect -f '{{range .Containers}}{{.Name}}{{println}}{{else}}With No Containers{{end}}' bridge
# bridge2
# bridge1

可以看到bridge1bridge2这两个容器都绑在名为bridgebridge类型的network上了.
那如果我想做网络隔离, 绑定到自定义的bridge上呢?

单机 Docker 网络

bridge 网络隔离

我们可以自己创建一个bridge, 做网络隔离.

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 创建自己的 bridge
docker network create -d bridge my-bridge
docker network ls

# 2. 创建一个新的 container 连接到 my-bridge
docker run -d --name bridge3 --link bridge1 --network my-bridge alpine /bin/ping 127.0.0.1
docker exec -it bridge3 hostname -i
# 172.18.0.2

# 3. ping bridge1 失败
docker exec -it bridge3 /bin/ping 172.17.0.4
docker exec -it bridge3 /bin/ping bridge1

可以看到, 即使我们手动指定了--link bridge1, bridge3容器仍然访问不了bridge1容器.
因为bridge3容器和bridge1容器使用的不是同一个docker网络.

bridge1容器连接到my-bridge上, 注意, 此时bridge1仍然存在于默认的网络bridge上.

1
2
3
4
5
6
7
8
9
10
# 1. 把 bridge1 连接到 my-bridge
docker network connect my-bridge bridge1

# 2. ping bridge1 成功
docker exec -it bridge3 /bin/ping -c 3 172.18.0.3
docker exec -it bridge3 /bin/ping -c 3 bridge1

# 3. ping bridge3 成功
docker exec -it bridge1 /bin/ping -c 3 172.18.0.2
docker exec -it bridge1 /bin/ping -c 3 bridge3

注意, 我们没有给bridge1添加--link bridge3, 只是连接到 my-bridge, 却可以ping bridge3.
因为和默认的bridge不同, 自己创建的bridge默认是双向的.

host 共享 network namespace

如果我们用Docker启动了一个nginx服务器.
此时, 只有宿主机可以访问Docker容器里的nginx服务器.

1
2
3
4
5
6
7
8
# 1. 运行本地服务器 nginx
docker run -d --name web-local nginx
# 2. 查看container的容器地址, 默认连接到 bridge 上
# 看 Containers 下的 IPv4Address, 得知 IP 地址为 172.17.0.2
docker network inspect bridge
# 3. 测试连接 nginx, 访问成功
ping 172.17.0.2
curl http://172.17.0.2

在宿主机里可以访问这个nginx, 但是在局域网的其他机器上, 却不能访问http://172.17.0.2.
我们需要把宿主机里的nginx暴露到外部来, 使用端口映射达到这个目的.
container80端口, 映射到宿主机的8080端口, 这样, 我们直接访问宿主机的8080端口, 就可以间接的访问到nginx.

1
docker run -d -p 8080:80 --name web-port-map nginx

以上文字和host毫无关联, 只是为下文做铺垫.
host可以共享宿主机的network namespace, 关于Linux network namespace可以看我的另一篇文章.
把它想象成一个网络沙箱即可.
还是用nginx做个简单例子, 我们从上面可以知道部署nginx需要做端口映射.
但是使用host共享宿主机的network namespace就可以不用做端口映射.

1
docker run -d --name web-host --network host nginx

我们在外部使用宿主机的IP地址, 可以访问到80端口的nginx.
但是, 这个用法有个缺陷, 如果我想在一台机器上同时启动两个nginx服务, 就会发生端口冲突, 它们会去争夺80端口.

none 断网操作

一般用于安全性, 保密性较高的程序.
直接隔离网络, 断网.

1
2
3
4
5
6
docker run -d --name net-none --network none alpine /bin/ping 127.0.0.1
docekr exec -it net-none ip a
# 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
# 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

可以看到没有分配ip地址, 只有一个本地的回环地址127.0.0.1.

多机 Docker 网络

Docker 1.9之前, 要实现多机Docker容器之间的网络通信, 都是通过端口映射或者host网络, 将Docker容器的端口暴露到宿主机.
通过宿主机之间的网络通信, 间接完成了多机Docker容器之间的网络通信.

TODO 待续