目录
(2) 容器互联 -> docker run --name/link
1、网络访问
(1) 外部访问容器 -> docker run -p/P
容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P 或 -p 参数来指定端口映射。
当使用 -P 标记时,Docker 会随机映射一个 49000~49900 的端口到内部容器开放的网络端口。
使用 docker ps 可以看到,本地主机的 49155 被映射到了容器的 5000 端口。此时访问本机的 49155 端口即可访问容器内 web 应用提供的界面。
sudo docker run -d -P training/webapp python app.py
sudo docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bc533791f3f5 training/webapp:latest python app.py 5 seconds ago Up 2 seconds 0.0.0.0:49155->5000/tcp nostalgic_morse
同样的,可以通过 docker logs 命令来查看应用的信息。
$ sudo docker logs -f nostalgic_morse
* Running on http://0.0.0.0:5000/
10.0.2.2 - - [23/May/2014 20:16:31] "GET / HTTP/1.1" 200 -
10.0.2.2 - - [23/May/2014 20:16:31] "GET /favicon.ico HTTP/1.1" 404 -
-p(小写的)则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有:
ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort。
映射所有接口地址
使用 hostPort:containerPort 格式本地的 5000 端口映射到容器的 5000 端口,可以执行
$ sudo docker run -d -p 5000:5000 training/webapp python app.py
此时默认会绑定本地所有接口上的所有地址。
映射指定地址端口
可以使用 ip:hostPort:containerPort 格式指定映射使用一个特定地址,比如 localhost 地址 127.0.0.1
$ sudo docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
映射指定地址任意端口
使用 ip::containerPort 绑定 localhost 的任意端口到容器的 5000 端口,本地主机会自动分配一个端口。
$ sudo docker run -d -p 127.0.0.1::5000 training/webapp python app.py
还可以使用 udp 标记来指定 udp 端口
$ sudo docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py
查看映射端口配置
使用 docker port 来查看当前映射的端口配置,也可以查看到绑定的地址
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker port tomcat04
8081/tcp -> 127.0.0.1:8081
注意:
-
容器有自己的内部网络和 ip 地址(使用 docker inspect 可以获取所有的变量,Docker 还可以有一个可变的网络配置。)
-
-p 标记可以多次使用来绑定多个端口: sudo docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py
e.g. 本地tomcat示例
环境:本地电脑windows、远程虚拟机、虚拟机docker环境
# 本地tomcat镜像文件:
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat latest 2eb5a120304e 5 weeks ago 647MB
openjdk 8 b190ad78b520 5 weeks ago 510MB
# 使用-p将8080端口映射到本地32678端口
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker run -d -p 32678:8080 --name tomcat07 tomcat
a88df5f9906af537500b08955316c59744111837c89f9fd4619c6ac0b1d3d6fd
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a88df5f9906a tomcat "catalina.sh run" 25 minutes ago Up 25 minutes 0.0.0.0:32678->8080/tcp tomcat07
配置ECS安全组,允许32678入方向流量,否则无法本地Windows无法访问到虚机的32678端口。
配置Docker tomcat信息,默认的tomcat镜像没有首页,访问的时候404。
# 命令: docker exec -it 运行的tomcat容器ID /bin/bash 进入到tomcat的目录
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker exec -it tomcat07 /bin/bash
root@a88df5f9906a:/usr/local/tomcat# ls
BUILDING.txt CONTRIBUTING.md LICENSE NOTICE README.md RELEASE-NOTES RUNNING.txt bin conf lib logs native-jni-lib temp webapps webapps.dist work
# ls查看文件夹,发现有个空的webapp,但是有另外一个webapp.dist里面有相关的文件,将webapp.dist的文件拷贝过来即可。
root@a88df5f9906a:/usr/local/tomcat# cd webapps
root@a88df5f9906a:/usr/local/tomcat/webapps# ls
root@a88df5f9906a:/usr/local/tomcat/webapps# cd ..
# 复制指定目录下的全部文件到另一个目录中方法:cp -r webapps.dist/* ./webapps
root@a88df5f9906a:/usr/local/tomcat# cp -r webapps.dist/* ./webapps
root@a88df5f9906a:/usr/local/tomcat# cd webapps
root@a88df5f9906a:/usr/local/tomcat/webapps# ls
ROOT docs examples host-manager manager
root@a88df5f9906a:/usr/local/tomcat# exit
exit
# 重启容器
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a88df5f9906a tomcat "catalina.sh run" 26 minutes ago Up 26 minutes 0.0.0.0:32678->8080/tcp tomcat07
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker restart tomcat07
tomcat07
在本地Windows访问ECS虚机中docker容器,成功。
(2) 容器互联 -> docker run --name/link
容器的连接(linking)系统是除了端口映射外,另一种跟容器中应用交互的方式。
该系统会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息。
该系统会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息。
自定义容器命名
连接系统依据容器的名称来执行。因此,首先需要自定义一个好记的容器命名。
虽然当创建容器的时候,系统默认会分配一个名字。自定义命名容器有2个好处:
-
自定义的命名,比较好记,比如一个web应用容器我们可以给它起名叫web
-
当要连接其他容器时候,可以作为一个有用的参考点,比如连接web容器到db容器
使用 --name 标记可以为容器自定义命名。
$ sudo docker run -d -P --name web training/webapp python app.py
使用 docker ps 来验证设定的命名。
$ sudo docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aed84ee21bde training/webapp:latest python app.py 12 hours ago Up 2 seconds 0.0.0.0:49154->5000/tcp web
也可以使用 docker inspect 来查看容器的名字
$ sudo docker inspect -f "{{ .Name }}" aed84ee21bde
/web
注意:容器的名称是唯一的。如果已经命名了一个叫 web 的容器,当你要再次使用 web 这个名称的时候,需要先用docker rm 来删除之前创建的同名容器。
在执行 docker run 的时候如果添加 --rm 标记,则容器在终止后会立刻删除。注意,--rm 和 -d 参数不能同时使用。
容器互联
使用 --link 参数可以让容器之间安全的进行交互。
下面先创建一个新的数据库容器。
$ sudo docker run -d --name db training/postgres
删除之前创建的 web 容器
$ docker rm -f web
然后创建一个新的 web 容器,并将它连接到 db 容器
$ sudo docker run -d -P --name web --link db:db training/webapp python app.py
此时,db 容器和 web 容器建立互联关系。
--link 参数的格式为 --link name:alias,其中 name 是要链接的容器的名称,alias 是这个连接的别名。
使用 docker ps 来查看容器的连接
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
349169744e49 training/postgres:latest su postgres -c '/usr About a minute ago Up About a minute 5432/tcp db, web/db
aed84ee21bde training/webapp:latest python app.py 16 hours ago Up 2 minutes 0.0.0.0:49154->5000/tcp web
可以看到自定义命名的容器,db 和 web,db 容器的 names 列有 db 也有 web/db。这表示 web 容器链接到 db 容器,web 容器将被允许访问 db 容器的信息。
Docker 在两个互联的容器之间创建了一个安全隧道,而且不用映射它们的端口到宿主主机上。在启动 db 容器的时候并没有使用 -p 和 -P 标记,从而避免了暴露数据库端口到外部网络上。
Docker 通过 2 种方式为容器公开连接信息:
-
环境变量
-
更新 /etc/hosts 文件
进入容器后,使用 env 命令来查看 web 容器的环境变量
$ sudo docker run --rm --name web2 --link db:db training/webapp env
. . .
DB_NAME=/web2/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5000_TCP=tcp://172.17.0.5:5432
DB_PORT_5000_TCP_PROTO=tcp
DB_PORT_5000_TCP_PORT=5432
DB_PORT_5000_TCP_ADDR=172.17.0.5
其中 DB_ 开头的环境变量是供 web 容器连接 db 容器使用,前缀采用大写的连接别名。
除了环境变量,Docker 还添加 host 信息到父容器的 /etc/hosts 的文件。下面是父容器 web 的 hosts 文件
$ sudo docker run -t -i --rm --link db:db training/webapp /bin/bash
root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7 aed84ee21bde
. . .
172.17.0.5 db
这里有 2 个 hosts,第一个是 web 容器,web 容器用 id 作为他的主机名,第二个是 db 容器的 ip 和主机名。 可以在 web 容器中安装 ping 命令来测试跟db容器的连通。
root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-ping
root@aed84ee21bde:/opt/webapp# ping db
PING db (172.17.0.5): 48 data bytes
56 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.267 ms
56 bytes from 172.17.0.5: icmp_seq=1 ttl=64 time=0.250 ms
56 bytes from 172.17.0.5: icmp_seq=2 ttl=64 time=0.256 ms
用 ping 来测试db容器,它会解析成 172.17.0.5。
用户可以链接多个父容器到子容器,比如可以链接多个 web 到 db 容器上。
2、网络配置
(1) 快速配置指南☆
Docker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备(特别是 veth pair)。
基本原理
首先,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来收发数据包;此外,如果不同子网之间要进行通信,需要路由机制。
Docker 中的网络接口默认都是虚拟接口。虚拟接口的优势之一是转发效率较高。 Linux 通过在内核中进行数据复制来实现虚拟接口之间的数据转发,发送接口的发送缓存中的数据包被直接复制到接收接口的接收缓存中。对于本地系统和容器内系统看来就像是一个正常的以太网卡,只是它不需要真正同外部网络设备通信,速度要快很多。
Docker 容器网络就利用了这项技术。它在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做 veth pair)。
第一步、当Docker启动时,会自动在主机上创建一个docker0虚拟网桥,实际上是Linux的一个桥,可以理解为一个软件交换机。它会在挂载到它的网口之间进行转发。
第二步、同时,Docker随机分配一个本地未占用的私有网段(在[RFC1918](http://tools.ietf.org/html/rfc1918)中定义)中的一个地址给docker0接口。172.17.42.1比如典型的,掩码为255.255.0.0。此后启动的容器内的网口也会自动分配一个同一网段(172.17.0.0/16)的地址。
第三步、当创建一个Docker容器的时候,同时会创建了一对veth pair接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。会执行如下操作:
-
创建一对虚拟接口,分别放到本地主机和新容器中;
-
本地主机一端桥接到默认的 docker0 或指定网桥上,并具有一个唯一的名字,如 veth65f9;
-
容器一端放到新容器中,并修改名字作为 eth0,这个接口只在容器的命名空间可见;
-
从网桥可用地址段中获取一个空闲地址分配给容器的 eth0,并配置默认路由到桥接网卡 veth65f9。
接下来的部分将介绍在一些场景中,Docker所有的网络定制配置。以及通过Linux命令来调整,补充,甚至替换Docker默认的网络配置。
下面是一个跟Docker网络相关的命令列表。
其中有些命令选项只有在Docker服务启动的时候才能配置,而且不能马上生效。
-
-b BRIDGE or --bridge=BRIDGE - 指定容器挂载的网桥
-
--bip=CIDR - 定制docker0的掩码
-
-H SOCKET... or --host=SOCKET... --Docker服务端接收命令的通道
-
--icc=true|false - 是否支持容器之间进行通信
-
--ip-forward=true|false - 请看下文容器之间的通信
-
--iptables=true|false - 禁止Docker添加iptables规则
-
--mtu=BYTES - 容器网络中的MTU
下面2个命令选项既可以在启动服务时指定,也可以Docker容器启动(docker run)时候指定。在Docker服务启动的时候指定则会成为默认值,后面执行docker run时可以覆盖设置的默认值。
-
--dns=IP_ADDRESS... - 使用指定的DNS服务器
-
--dns-search=DOMAIN... - 指定DNS搜索域
这些选项在docker run执行时使用,因为它是针对容器的特性内容。
-
-h HOSTNAME or --hostname=HOSTNAME - 配置容器主机名
-
--link=CONTAINER_NAME:ALIAS - 添加到另一个容器的连接
-
--net=bridge|none|container:NAME_or_ID|host - 配置容器的桥接模式
-
-p SPEC or --publish=SPEC - 映射容器端口到宿主主机
-
-P or --publish-all=true|false - 映射容器所有端口到宿主主机
在 docker run 的时候通过 --net 参数来指定容器的网络配置,有4个可选值:
-
--net=bridge 这个是默认值,连接到默认的网桥。
-
--net=host 告诉 Docker 不要将容器网络放到隔离的命名空间中,即不要容器化容器内的网络。此时容器使用本地主机的网络,它拥有完全的本地主机接口访问权限。容器进程可以跟主机其它 root 进程一样可以打开低范围的端口,可以访问本地网络服务比如 D-bus,还可以让容器做一些影响整个主机系统的事情,比如重启主机。因此使用这个选项的时候要非常小心。如果进一步的使用 --privileged=true,容器会被允许直接配置主机的网络堆栈。
-
--net=container:NAME_or_ID 让 Docker 将新建容器的进程放到一个已存在容器的网络栈中,新容器进程有自己的文件系统、进程列表和资源限制,但会和已存在的容器共享 IP 地址和端口等网络资源,两者进程可以直接通过 lo 环回接口通信。
-
--net=none 让 Docker 将新容器放到隔离的网络栈中,但是不进行网络配置。之后,用户可以自己进行配置。
网络配置细节
用户使用 --net=none 后,可以自行配置网络,让容器达到跟平常一样具有访问网络的权限。通过这个过程,可以了解 Docker 配置网络的细节。
# 首先,启动一个 /bin/bash 容器,指定 --net=none 参数。
$ sudo docker run -i -t --rm --net=none base /bin/bash
# 在本地主机查找容器的进程 id,并为它创建网络命名空间。
sudo docker inspect -f '{{.State.Pid}}' 63f36fc01b5f 2778 pid=2778
sudo mkdir -p /var/run/netns
sudo ln -s /proc/pid/ns/net /var/run/netns/pid
# 检查桥接网卡的 IP 和子网掩码信息。
$ ip addr show docker0 21: docker0: ... inet 172.17.42.1/16 scope global docker0 ...
# 创建一对 “veth pair” 接口 A 和 B,绑定 A 到网桥 docker0,并启用它
sudo ip link add A type veth peer name B
sudo brctl addif docker0 A
sudo ip link set A up
# 将B放到容器的网络命名空间,命名为 eth0,启动它并配置一个可用 IP(桥接网段)和默认网关。
sudo ip link set B netns pid
sudo ip netns exec pid ip link set dev B name eth0
sudo ip netns exec pid ip link set eth0 up
sudo ip netns exec pid ip addr add 172.17.42.99/16 dev eth0
sudo ip netns exec pid ip route add default via 172.17.42.1
以上,就是 Docker 配置网络的具体过程。
当容器结束后,Docker 会清空容器,容器内的 eth0 会随网络命名空间一起被清除,A 接口也被自动从 docker0 卸载。
此外,用户可以使用 ip netns exec
命令来在指定网络命名空间中进行配置,从而配置容器内的网络。
网络配置文件
在Linux中网卡对应的其实就是文件,所以找到对应的网卡文件即可
。比如,可以使用以下命令查看网络配置文件。
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0
# Created by cloud-init on instance boot automatically, do not edit.
#
BOOTPROTO=dhcp
DEVICE=eth0
ONBOOT=yes
STARTMODE=auto
TYPE=Ethernet
USERCTL=no
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]#
网卡配置(网络接口)
查看网络接口信息
使用以下命令可以查看网络接口(网卡)信息:
-
ip link show
-
ls /sys/class/net
-
ip a
ip a命令 我们通过ip a在主机中(不是在容器中)查看网络并加以解读: [root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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 inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 00:16:3e:2e:b1:09 brd ff:ff:ff:ff:ff:ff inet 172.17.192.56/20 brd 172.17.207.255 scope global dynamic noprefixroute eth0 valid_lft 313195892sec preferred_lft 313195892sec inet6 fe80::216:3eff:fe2e:b109/64 scope link valid_lft forever preferred_lft forever 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:15:ac:6f:33 brd ff:ff:ff:ff:ff:ff inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:15ff:feac:6f33/64 scope link valid_lft forever preferred_lft forever 27: vetheca375d@if26: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether be:34:bc:96:43:b4 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::bc34:bcff:fe96:43b4/64 scope link valid_lft forever preferred_lft forever [root@iZ2zef4zu0mvk0hhwa0il1Z ~]#
上面配置代表的含义如下: BROADCAST 该接口支持广播 MULTICAST 该接口支持多播 UP 网络接口已启用 LOWER_UP 网络电缆已插入,设备已连接至网络 mtu 1500 最大传输单位(数据包大小)为1,500字节 qdisc pfifo_fast 用于数据包排队 state UP 网络接口已启用 group default 接口组 qlen 1000 传输队列长度 link/ether 00:1e:4f:c8:43:fc 接口的 MAC(硬件)地址 brd ff:ff:ff:ff:ff:ff 广播地址 inet 192.168.0.24/24 IPv4 地址 brd 192.168.0.255 广播地址 scope global 全局有效 dynamic enp0s25 地址是动态分配的 valid_lft 80866sec IPv4 地址的有效使用期限 preferred_lft 80866sec IPv4 地址的首选生存期 inet6 fe80::2c8e:1de0:a862:14fd/64 IPv6 地址 scope link 仅在此设备上有效 valid_lft forever IPv6 地址的有效使用期限 preferred_lft forever IPv6 地址的首选生存期
在ip a列出来的4个网络中,分别代表什么含义呢?
-
1: lo
回环地址就是127.0.0.1,也被称为本地回环地址,不属于任何一个有类别地址类。在不安装网卡前就可以ping通这个地址。可以用它来检查本地网络协议、基本数据接口等功能是否正常。
-
2: eth0
主机与外界通信的网络配置,包含了物理地址,IP地址和MTU等网络配置。
-
3: docker0
docker安装时会创建一个名为 docker0 的Linux bridge。新建的容器会自动桥接到这个docker0接口。
-
27: vetheca375d@if26
本地主机创建的网络接口,该接口会桥接到docker0上,负责和容器进行通信。每个容器都会创建单独的veth负责和容器进行通信,这就是网络命名空间的概念。
网卡添加IP地址
当然,这块可以直接修改ifcfg-*文件,但是我们通过命令添加试试
(1)增加IP地址
ip addr add 192.168.0.100/24 dev eth0
(2)删除IP地址
ip addr delete 192.168.0.100/24 dev eth0
网卡启动与关闭
重启网卡:
service network restart / systemctl restart network
启动/关闭某个网卡:
ifup/ifdown eth0 or ip link set eth0 up/down
(2) 配置DNS
Docker 没有为每个容器专门定制DNS,那么怎么自定义配置容器的主机名和 DNS 配置呢? 秘诀就是它利用虚拟文件来挂在到容器的 3 个相关配置文件。
在容器中使用 mount 命令可以看到挂载信息:
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42c282eee349 tomcat "catalina.sh run" 20 hours ago Up 20 hours 0.0.0.0:32678->8080/tcp tomcat03
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker exec -it tomcat03 /bin/bash
root@42c282eee349:/usr/local/tomcat# mount
....前面很多不要看,找到下面这三行....
/dev/vda1 on /etc/resolv.conf type xfs (rw,relatime,attr2,inode64,noquota)
/dev/vda1 on /etc/hostname type xfs (rw,relatime,attr2,inode64,noquota)
/dev/vda1 on /etc/hosts type xfs (rw,relatime,attr2,inode64,noquota)
....后面很多不要管....
这种机制可以让宿主主机 DNS 信息发生更新后,所有 Docker 容器的 dns 配置通过 /etc/resolv.conf 文件立刻得到更新。
如果用户想要手动指定容器的配置,可以利用下面的选项。
-h HOSTNAME or --hostname=HOSTNAME
设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts。但它在容器外部看不到,既不会在 docker ps 中显示,也不会在其他的容器的 /etc/hosts 看到。
--link=CONTAINER_NAME:ALIAS
选项会在创建容器的时候,添加一个其他容器的主机名到 /etc/hosts 文件中,让新容器的进程可以使用主机名 ALIAS 就可以连接它。
--dns=IP_ADDRESS
添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。
--dns-search=DOMAIN
设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的主机时,DNS 不仅搜索host,还会搜索 host.example.com。 注意:如果没有上述最后 2 个选项,Docker 会默认用主机上的 /etc/resolv.conf 来配置容器。
(3) 容器访问控制
容器的访问控制,主要通过 Linux 上的 iptables 防火墙来进行管理和实现。iptables 是 Linux 上默认的防火墙软件,在大部分发行版中都自带。
容器访问外部网络
容器要想访问外部网络,需要本地系统的转发支持。在Linux 系统中,检查转发是否打开。
sysctl net.ipv4.ip_forward net.ipv4.ip_forward = 1
如果为 0,说明没有开启转发,则需要手动打开。
sysctl -w net.ipv4.ip_forward=1
如果在启动 Docker 服务的时候设定--ip-forward=true
, Docker 就会自动设定系统的 ip_forward 参数为 1。
容器之间访问
容器之间相互访问,需要两方面的支持。
-
容器的网络拓扑是否已经互联。默认情况下,所有容器都会被连接到 docker0 网桥上。
-
本地系统的防火墙软件 -- iptables 是否允许通过。
访问所有端口
当启动 Docker 服务时候,默认会添加一条转发策略到 iptables 的 FORWARD 链上。策略为通过(ACCEPT)还是禁止(DROP)取决于配置--icc=true(缺省值)还是 --icc=false。当然,如果手动指定 --iptables=false 则不会添加 iptables 规则。
可见,默认情况下,不同容器之间是允许网络互通的。如果为了安全考虑,可以在 /etc/default/docker 文件中配置 DOCKER_OPTS=--icc=false 来禁止它。
访问指定端口
在通过 -icc=false 关闭网络访问后,还可以通过 --link=CONTAINER_NAME:ALIAS 选项来访问容器的开放端口。
例如,在启动 Docker 服务时,可以同时使用 icc=false --iptables=true 参数来关闭允许相互的网络访问,并让 Docker 可以修改系统中的 iptables 规则。
此时,系统中的 iptables 规则可能是类似
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy DROP)
target prot opt source destination
DOCKER-USER all -- 0.0.0.0/0 0.0.0.0/0
DOCKER-ISOLATION-STAGE-1 all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain DOCKER (1 references)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0/0 172.18.0.3 tcp dpt:8080
Chain DOCKER-ISOLATION-STAGE-1 (1 references)
target prot opt source destination
DOCKER-ISOLATION-STAGE-2 all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
Chain DOCKER-ISOLATION-STAGE-2 (1 references)
target prot opt source destination
DROP all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
Chain DOCKER-USER (1 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
之后,启动容器(docker run)时使用 --link=CONTAINER_NAME:ALIAS 选项。Docker 会在 iptable 中为 两个容器分别添加一条 ACCEPT 规则,允许相互访问开放的端口(取决于 Dockerfile 中的 EXPOSE 行)。
当添加了 --link=CONTAINER_NAME:ALIAS 选项后,添加了 iptables 规则。
$ sudo iptables -nL
...
Chain FORWARD (policy ACCEPT)
target prot opt source destination
ACCEPT tcp -- 172.17.0.2 172.17.0.3 tcp spt:80
ACCEPT tcp -- 172.17.0.3 172.17.0.2 tcp dpt:80
DROP all -- 0.0.0.0/0 0.0.0.0/0
注意:--link=CONTAINER_NAME:ALIAS 中的 CONTAINER_NAME 目前必须是 Docker 分配的名字,或使用 --name 参数指定的名字。主机名则不会被识别。
(4) 端口映射实现
默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器。
容器访问外部实现
容器所有到外部网络的连接,源地址都会被NAT成本地系统的IP地址。这是使用 iptables 的源地址伪装操作实现的。
查看主机的 NAT 规则。
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# iptables -t nat -nL
......
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.18.0.0/16 0.0.0.0/0
MASQUERADE tcp -- 172.18.0.3 172.18.0.3 tcp dpt:8080
......
其中,上述规则将所有源地址在 172.18.0.0/16 网段,目标地址为其他网段(外部网络)的流量动态伪装为从系统网卡发出。MASQUERADE 跟传统 SNAT 的好处是它能动态从网卡获取地址。
外部访问容器实现
容器允许外部访问,可以在 docker run 时候通过 -p 或 -P 参数来启用。
不管用那种办法,其实也是在本地的 iptable 的 nat 表中添加相应的规则。
使用 -P 时:
$ iptables -t nat -nL
...
Chain DOCKER (2 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:49153 to:172.17.0.2:80
使用 -p 80:80 时:
$ iptables -t nat -nL
Chain DOCKER (2 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
注意:
-
这里的规则映射了
0.0.0.0,意味着将接受主机来自所有接口的流量
。用户可以通过 -p IP:host_port:container_port 或 -p IP::port 来指定允许访问容器的主机上的 IP、接口等,以制定更严格的规则。 -
如果希望
永久绑定到某个固定的 IP 地址
,可以在 Docker 配置文件 /etc/default/docker 中指定 DOCKER_OPTS="--ip=IP_ADDRESS",之后重启 Docker 服务即可生效。
3、网络细节
(1) 配置docker0网桥
Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。
Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信,它还给出了 MTU(接口允许接收的最大传输单元),通常是 1500 Bytes,或宿主主机网络路由上支持的默认值。这些值都可以在服务启动的时候进行配置。
-
--bip=CIDR -- IP 地址加掩码格式,例如 192.168.1.5/24
-
--mtu=BYTES -- 覆盖默认的 Docker mtu 配置
也可以在配置文件中配置 DOCKER_OPTS,然后重启服务。 由于目前 Docker 网桥是 Linux 网桥,用户可以使用 brctl show 来查看网桥和端口连接信息。
$ sudo brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.3a1d7362b4ee no veth65f9
每次创建一个新容器的时候,Docker 从可用的地址段中选择一个空闲的 IP 地址分配给容器的 eth0 端口。使用本地主机上 docker0 接口的 IP 作为所有容器的默认网关。
$ sudo docker run -i -t --rm base /bin/bash
$ ip addr show eth0
24: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 32:6f:e0:35:57:91 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::306f:e0ff:fe35:5791/64 scope link
valid_lft forever preferred_lft forever
$ ip route
default via 172.17.42.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3
$ exit
(2) 自定义网桥
除了默认的 docker0 网桥,用户也可以指定网桥来连接各个容器。
在启动 Docker 服务的时候,使用 -b BRIDGE或--bridge=BRIDGE 来指定使用的网桥。
如果服务已经运行,那需要先停止服务,并删除旧的网桥。
[root@iZ2zef4zu0mvk0hhwa0il1Z bridge-utils-1.6]# ip addr show docker0
19: docker0: <BROADCAST,MULTICAST> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:3e:ed:5e:51 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
valid_lft forever preferred_lft forever
$ sudo service docker stop
$ sudo ip link set dev docker0 down
$ sudo brctl delbr docker0
[root@iZ2zef4zu0mvk0hhwa0il1Z bridge-utils-1.6]# ip add show docker0
Device "docker0" does not exist.
然后创建一个网桥 bridge1。
[root@iZ2zef4zu0mvk0hhwa0il1Z bridge-utils-1.6]# brctl addbr bridge1
[root@iZ2zef4zu0mvk0hhwa0il1Z bridge-utils-1.6]# ip addr add 192.168.5.1/24 dev bridge1
[root@iZ2zef4zu0mvk0hhwa0il1Z bridge-utils-1.6]# ip link set dev bridge1 up
查看确认网桥创建并启动。
[root@iZ2zef4zu0mvk0hhwa0il1Z bridge-utils-1.6]# ip add show bridge1
18: bridge1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether 42:6e:3b:8c:53:8b brd ff:ff:ff:ff:ff:ff
inet 192.168.5.1/24 scope global bridge1
valid_lft forever preferred_lft forever
inet6 fe80::406e:3bff:fe8c:538b/64 scope link
valid_lft forever preferred_lft forever
配置 Docker 服务,默认桥接到创建的网桥上。
$ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker
[root@iZ2zef4zu0mvk0hhwa0il1Z bridge-utils-1.6]# cat /etc/default/docker
DOCKER_OPTS="-b=bridge1"
DOCKER_OPTS="-b=bridge1"
$ sudo service docker start
启动 Docker 服务。 新建一个容器,可以看到它已经桥接到了 bridge1 上。
[root@iZ2zef4zu0mvk0hhwa0il1Z bridge-utils-1.6]# docker run -d --name tomcat12 tomcat
8d000e060a13b748a8b3a30aed79744a3dcae2f914cc47cdfb9bd191eb4d5972
[root@iZ2zef4zu0mvk0hhwa0il1Z bridge-utils-1.6]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8d000e060a13 tomcat "catalina.sh run" 3 seconds ago Up 1 second 8080/tcp tomcat12
可以继续用 brctl show 命令查看桥接的信息。另外,在容器中可以使用 ip addr 和 ip route 命令来查看 IP 地址配置和路由信息。
(3) 编辑网络配置文件
Docker 1.2.0 开始支持在运行中的容器里编辑 /etc/hosts, /etc/hostname 和 /etc/resolve.conf 文件。
但是这些修改是临时的,只在运行的容器中保留,容器终止或重启后并不会被保存下来。也不会被 docker commit 提交。
(4) 容器间点到点直连
默认情况下,Docker 会将所有容器连接到由 docker0 提供的虚拟子网中。
用户有时候需要两个容器之间可以直连通信,而不用通过主机网桥进行桥接。
解决办法很简单:创建一对 peer 接口,分别放到两个容器中,配置成点到点链路类型即可。
首先启动 2 个容器:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
31b1f42222d5 tomcat "catalina.sh run" 2 seconds ago Up 2 seconds 8080/tcp tomcat13
8d000e060a13 tomcat "catalina.sh run" 29 minutes ago Up 29 minutes 8080/tcp tomcat12’
找到进程号,然后创建网络名字空间的跟踪文件。
[root@iZ2zef4zu0mvk0hhwa0il1Z]# docker inspect -f '{{.State.Pid}}' 31b1f42222d5
13407
[root@iZ2zef4zu0mvk0hhwa0il1Z]# docker inspect -f '{{.State.Pid}}' 8d000e060a13
13032
[root@iZ2zef4zu0mvk0hhwa0il1Z]# mkdir -p /var/run/netns
[root@iZ2zef4zu0mvk0hhwa0il1Z]# ln -s /proc/13407/ns/net /var/run/netns/13407
[root@iZ2zef4zu0mvk0hhwa0il1Z]# ln -s /proc/13032/ns/net /var/run/netns/13032
创建一对 peer 接口,然后配置路由
[root@iZ2zef4zu0mvk0hhwa0il1Z]# ip link add A type veth peer name B
[root@iZ2zef4zu0mvk0hhwa0il1Z]# ip link set A netns 13407
[root@iZ2zef4zu0mvk0hhwa0il1Z]# ip netns exec 13407 ip addr add 10.1.1.1/32 dev A
[root@iZ2zef4zu0mvk0hhwa0il1Z]# ip netns exec 13407 ip link set A up
[root@iZ2zef4zu0mvk0hhwa0il1Z]# ip netns exec 13407 ip route add 10.1.1.2/32 dev A
[root@iZ2zef4zu0mvk0hhwa0il1Z]# ip link set B netns 13032
[root@iZ2zef4zu0mvk0hhwa0il1Z]# ip netns exec 13032 ip addr add 10.1.1.2/32 dev B
[root@iZ2zef4zu0mvk0hhwa0il1Z]# ip netns exec 13032 ip link set B up
[root@iZ2zef4zu0mvk0hhwa0il1Z]# ip netns exec 13032 ip route add 10.1.1.1/32 dev B
现在这 2 个容器就可以相互 ping 通,并成功建立连接。
[root@iZ2zef4zu0mvk0hhwa0il1Z]# docker exec -it tomcat12 /bin/bash
root@8d000e060a13:/usr/local/tomcat# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
25: eth0@if26: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
29: B@if30: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 86:c6:66:c3:61:ae brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet 10.1.1.2/32 scope global B
valid_lft forever preferred_lft forever
[root@iZ2zef4zu0mvk0hhwa0il1Z]# docker exec -it tomcat13 /bin/bash
root@31b1f42222d5:/usr/local/tomcat# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
27: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
30: A@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 12:e7:f8:3a:98:ce brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet 10.1.1.1/32 scope global A
valid_lft forever preferred_lft forever
root@31b1f42222d5:/usr/local/tomcat# ping 10.1.1.2
PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.041 ms
^C
--- 10.1.1.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 26ms
rtt min/avg/max/mdev = 0.037/0.039/0.041/0.002 ms
root@31b1f42222d5:/usr/local/tomcat# exit
exit
[root@iZ2zef4zu0mvk0hhwa0il1Z]# docker exec -it tomcat12 /bin/bash
root@8d000e060a13:/usr/local/tomcat# ping 10.1.1.1
PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data.
64 bytes from 10.1.1.1: icmp_seq=1 ttl=64 time=0.027 ms
64 bytes from 10.1.1.1: icmp_seq=2 ttl=64 time=0.038 ms
^C
--- 10.1.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 48ms
rtt min/avg/max/mdev = 0.027/0.032/0.038/0.007 ms
root@8d000e060a13:/usr/local/tomcat# exit
exit
点到点链路不需要子网和子网掩码。此外,也可以不指定 --net=none 来创建点到点链路。这样容器还可以通过原先的网络来通信。
利用类似的办法,可以创建一个只跟主机通信的容器。但是一般情况下,更推荐使用 --icc=false 来关闭容器之间的通信。
4、Container通信详解
(1) 网络namespace示例
每个Container都有自己的网络命名空间。创建两个不同的网络命名空间,并通过veth pair配置打通之间的网络。
veth pair :Virtual Ethernet Pair,是一个成对的端口,可以实现上述功能
# (1)创建一个network namespace
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns add ns1
# (2)查看该namespace下网卡的情况
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# (3)启动ns1上的lo网卡
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns1 ip link set lo up
# (4)查看该namespace下网卡的情况,可以发现state变成了UNKOWN
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
# (5)再次创建一个network namespace
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns add ns2
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns2 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# (6)创建Veth Pair让两个namespace网络连通起来
# (7)创建一对link,也就是接下来要通过veth pair连接的link,关注31和32
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip link add veth-ns1 type veth peer name veth-ns2
# (8)查看link情况
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 00:16:3e:2e:b1:09 brd ff:ff:ff:ff:ff:ff
18: bridge1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether 42:6e:3b:8c:53:8b brd ff:ff:ff:ff:ff:ff
24: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:72:c1:22:cd brd ff:ff:ff:ff:ff:ff
26: veth71cb411@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
link/ether ce:9e:5c:55:fc:e1 brd ff:ff:ff:ff:ff:ff link-netns 13032
28: vethb576888@if27: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
link/ether b6:88:c6:0d:6c:23 brd ff:ff:ff:ff:ff:ff link-netns 13407
31: veth-ns2@veth-ns1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 3e:4a:57:d7:80:12 brd ff:ff:ff:ff:ff:ff
32: veth-ns1@veth-ns2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 22:79:03:40:48:eb brd ff:ff:ff:ff:ff:ff
# (9)将veth-ns1加入ns1中,将veth-ns2加入ns2中
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip link set veth-ns1 netns ns1
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip link set veth-ns2 netns ns2
# (10)查看宿主机和ns1,ns2的link情况
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 00:16:3e:2e:b1:09 brd ff:ff:ff:ff:ff:ff
18: bridge1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether 42:6e:3b:8c:53:8b brd ff:ff:ff:ff:ff:ff
24: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:72:c1:22:cd brd ff:ff:ff:ff:ff:ff
26: veth71cb411@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
link/ether ce:9e:5c:55:fc:e1 brd ff:ff:ff:ff:ff:ff link-netns 13032
28: vethb576888@if27: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
link/ether b6:88:c6:0d:6c:23 brd ff:ff:ff:ff:ff:ff link-netns 13407
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns1 ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
32: veth-ns1@if31: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 22:79:03:40:48:eb brd ff:ff:ff:ff:ff:ff link-netns ns2
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns2 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
31: veth-ns2@if32: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 3e:4a:57:d7:80:12 brd ff:ff:ff:ff:ff:ff link-netns ns1
# (11)此时veth-ns1和veth-ns2还没有ip地址,显然通信还缺少点条件
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns1 ip addr add 192.168.1.11/24 dev veth-ns1
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns2 ip addr add 192.168.1.13/24 dev veth-ns2
# (12)再次查看,发现state是DOWN,并且还是没有IP地址
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns1 ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
32: veth-ns1@if31: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 22:79:03:40:48:eb brd ff:ff:ff:ff:ff:ff link-netns ns2
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns2 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
31: veth-ns2@if32: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 3e:4a:57:d7:80:12 brd ff:ff:ff:ff:ff:ff link-netns ns1
# (13)启动veth-ns1和veth-ns2
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns1 ip link set veth-ns1 up
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns2 ip link set veth-ns2 up
# (14)再次查看,发现state是UP,同时有IP
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
32: veth-ns1@if31: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 22:79:03:40:48:eb brd ff:ff:ff:ff:ff:ff link-netns ns2
inet 192.168.1.11/24 scope global veth-ns1
valid_lft forever preferred_lft forever
inet6 fe80::2079:3ff:fe40:48eb/64 scope link
valid_lft forever preferred_lft forever
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns2 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
31: veth-ns2@if32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 3e:4a:57:d7:80:12 brd ff:ff:ff:ff:ff:ff link-netns ns1
inet 192.168.1.13/24 scope global veth-ns2
valid_lft forever preferred_lft forever
inet6 fe80::3c4a:57ff:fed7:8012/64 scope link
valid_lft forever preferred_lft forever
# (15)此时两个network namespace互相ping一下,发现是可以ping通的
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns1 ping 192.168.1.13
PING 192.168.1.13 (192.168.1.13) 56(84) bytes of data.
64 bytes from 192.168.1.13: icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from 192.168.1.13: icmp_seq=2 ttl=64 time=0.039 ms
64 bytes from 192.168.1.13: icmp_seq=3 ttl=64 time=0.073 ms
^C
--- 192.168.1.13 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 49ms
rtt min/avg/max/mdev = 0.037/0.049/0.073/0.018 ms
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip netns exec ns2 ping 192.168.1.11
PING 192.168.1.11 (192.168.1.11) 56(84) bytes of data.
64 bytes from 192.168.1.11: icmp_seq=1 ttl=64 time=0.022 ms
64 bytes from 192.168.1.11: icmp_seq=2 ttl=64 time=0.037 ms
64 bytes from 192.168.1.11: icmp_seq=3 ttl=64 time=0.037 ms
^C
--- 192.168.1.11 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 40ms
rtt min/avg/max/mdev = 0.022/0.032/0.037/0.007 ms
(2) Container网络实现示例
# (1)查看centos的网络:ip a,可以发现
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:16:3e:2e:b1:09 brd ff:ff:ff:ff:ff:ff
inet 172.17.192.56/20 brd 172.17.207.255 scope global dynamic noprefixroute eth0
valid_lft 315095685sec preferred_lft 315095685sec
inet6 fe80::216:3eff:fe2e:b109/64 scope link
valid_lft forever preferred_lft forever
18: bridge1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether 42:6e:3b:8c:53:8b brd ff:ff:ff:ff:ff:ff
inet 192.168.5.1/24 scope global bridge1
valid_lft forever preferred_lft forever
inet6 fe80::406e:3bff:fe8c:538b/64 scope link
valid_lft forever preferred_lft forever
24: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:72:c1:22:cd brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:72ff:fec1:22cd/64 scope link
valid_lft forever preferred_lft forever
26: veth71cb411@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether ce:9e:5c:55:fc:e1 brd ff:ff:ff:ff:ff:ff link-netns 13032
inet6 fe80::cc9e:5cff:fe55:fce1/64 scope link
valid_lft forever preferred_lft forever
28: vethb576888@if27: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether b6:88:c6:0d:6c:23 brd ff:ff:ff:ff:ff:ff link-netns 13407
inet6 fe80::b488:c6ff:fe0d:6c23/64 scope link
valid_lft forever preferred_lft forever
# (2)查看容器tomcat12的网络:docker exec -it tomcat12 ip a
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker exec -it tomcat12 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
25: eth0@if26: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
29: B@if30: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 86:c6:66:c3:61:ae brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet 10.1.1.2/32 scope global B
valid_lft forever preferred_lft forever
# (3)在centos中ping一下tomcat01的网络,发现可以ping通
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# ping 172.18.0.2
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.070 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.050 ms
^C
--- 172.18.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 65ms
rtt min/avg/max/mdev = 0.050/0.060/0.070/0.010 ms
(4) 为什么可以ping通呢?centos和tomcat12属于不同的network namespace,他们是如何连接的呢?其实和上一个示例逻辑一样:
也就是说,在tomcat12中有一个eth0和centos的docker0中有一个veth是成对的,类似于前面的veth-ns1和veth-ns2,可以查看下网桥连接信息。
# (5)查看网络配置
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.024272c122cd no veth71cb411
vethb576888
那为什么不同的Container之前网络互通呢?看了下图就懂了。
# (6)这种网络连接方法我们称之为Bridge,其实也可以通过命令查看docker中的网络模式:docker network ls
bridge也是docker中默认的网络模式
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
61b1343ade74 bridge bridge local
ca642bc9f004 host host local
cec6c954312f none null local
(7)不妨检查一下bridge:docker network inspect bridge
[root@iZ2zef4zu0mvk0hhwa0il1Z ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "61b1343ade74cc17f964dcb6e93623b07ceaac8de8ebb583506c92c8328f338b",
"Created": "2020-07-19T17:00:55.054849538+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"31b1f42222d5de88dcc7aed35c86913eb7d49f0b6f53ace5426120c170ddcfdc": {
"Name": "tomcat13",
"EndpointID": "220130fad4a284ef15733d7c50899b16e0804513badd4d648d19a35d9d4b9a03",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
},
"8d000e060a13b748a8b3a30aed79744a3dcae2f914cc47cdfb9bd191eb4d5972": {
"Name": "tomcat12",
"EndpointID": "a1dbdbaf2f38ce6d81cedd894ef2fe12b91a4edd2f7070766c1c9ee6df952337",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.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": {}
}
]
那么容器是怎么联网的呢?
(3) 多机Container互联