网络管理
容器访问控制
容器的访问控制,主要通过 Linux 上的 iptables
防火墙来进行管理和实现。iptables
是 Linux 上默认的防火墙软件,在大部分发行版中都自带。
容器访问外部网络
容器要想访问外部网络,需要本地系统的转发支持。在Linux 系统中,检查转发是否打开。
1 | $sysctl net.ipv4.ip_forward |
如果为 0,说明没有开启转发,则需要手动打开。
1 | $sysctl -w net.ipv4.ip_forward=1 |
如果在启动 Docker 服务的时候设定 --ip-forward=true
, Docker 就会自动设定系统的 ip_forward
参数为 1。
容器之间访问
容器之间相互访问,需要两方面的支持。
- 容器的网络拓扑是否已经互联。默认情况下,所有容器都会被连接到
docker0
网桥上。 - 本地系统的防火墙软件 –
iptables
是否允许通过。
访问所有端口
当启动 Docker 服务(即 dockerd)的时候,默认会添加一条转发策略到本地主机 iptables 的 FORWARD 链上。策略为通过(ACCEPT
)还是禁止(DROP
)取决于配置--icc=true
(缺省值)还是 --icc=false
。当然,如果手动指定 --iptables=false
则不会添加 iptables
规则。
可见,默认情况下,不同容器之间是允许网络互通的。如果为了安全考虑,可以在 /etc/docker/daemon.json
文件中配置 {"icc": false}
来禁止它。
访问指定端口
在通过 -icc=false
关闭网络访问后,还可以通过 --link=CONTAINER_NAME:ALIAS
选项来访问容器的开放端口。
例如,在启动 Docker 服务时,可以同时使用 icc=false --iptables=true
参数来关闭允许相互的网络访问,并让 Docker 可以修改系统中的 iptables
规则。
此时,系统中的 iptables
规则可能是类似
1 | $ sudo iptables -nL |
之后,启动容器(docker run
)时使用 --link=CONTAINER_NAME:ALIAS
选项。Docker 会在 iptable
中为 两个容器分别添加一条 ACCEPT
规则,允许相互访问开放的端口(取决于 Dockerfile
中的 EXPOSE
指令)。
当添加了 --link=CONTAINER_NAME:ALIAS
选项后,添加了 iptables
规则。
1 | $ sudo iptables -nL |
注意:--link=CONTAINER_NAME:ALIAS
中的 CONTAINER_NAME
目前必须是 Docker 分配的名字,或使用 --name
参数指定的名字。主机名则不会被识别。
端口映射
随机映射端口从外部访问容器应用, 使用
docker -P
,随机映射一个49000-49900的端口到内部容器1
[root@docker ~]# docker run -d -P nginx
使用
ip::端口
,绑定本地的任意端口到容器内部端口1
[root@docker ~]# docker run -d -p 127.0.0.1::5000 training/webapp python app.py
指定映射端口从外部访问容器应用: 使用
docker -p
,指定一个端口映射到内部容器,例如将本机 8080 端口映射到容器的 80 端口1
[root@docker ~]# docker run -d -p 8080:80 nginx
映射多个端口到容器,多次使用
-p
标记可以绑定多个端口1
[root@docker ~]# docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py
映射指定地址的指定端口: 使用
-p ip:端口: 容器端口
,可以指定映射一个特定的地址。1
[root@docker ~]# docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
映射指定udp端口: 使用
-p ip:端口: 容器端口/udp
,可以指定映射一个udp地址。1
[root@docker ~]# docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py
查看端口映射状态: 使用
docker port
来查看当前端口映射状态1
2[root@docker ~]# docker port 98d5c052a96
5000/tcp -> 127.0.0.1:5000
容器互联
容器互联可以让多个容器中的应用进行快速交互,他会在源和接收容器之间创建连接关系,接收容器可以通过容器名快速访问到源容器,而不用指定具体IP
–link
自定义容器名称
1
2
3[root@docker ~]# docker run -d -P --name web training/webapp python app.py
# 删除创建的web容器
[root@docker ~]# docker rm -f web容器互联: 使用
--link name:别名
,可以让容器互联1
2
3
4# 创建一个新的数据库容器 db
[root@docker ~]# docker run -d --name db training/postgres
# 创建一个新的web容器,并连接到db容器上
[root@docker ~]# docker run -d -P --name web --link db:db training/webapp python app.py使用env查看容器环境变量信息
1
2
3
4
5
6
7
8
9
10
11
12
13[root@docker ~]# docker exec -it web env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=fabb9c44bb04
TERM=xterm
DB_PORT=tcp://172.17.0.3:5432
DB_PORT_5432_TCP=tcp://172.17.0.3:5432
DB_PORT_5432_TCP_ADDR=172.17.0.3
DB_PORT_5432_TCP_PORT=5432
DB_PORT_5432_TCP_PROTO=tcp
DB_NAME=/web/db
DB_ENV_PG_VERSION=9.3
HOME=/root查看容器hosts文件信息
1
2
3
4
5
6
7
8
9[root@docker ~]# docker exec -it web 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.17.0.3 db 1b0ce139a2c1
172.17.0.4 fabb9c44bb04
docker network
由于Docker 所有容器都连接于默认的桥接网络上,因此默认情况下所有容器都是可以互联的,没有隔离,当然这样安全性不好。而服务发现,是在这种环境下发展出来的,通过修改容器内的 /etc/hosts 文件来完成的。凡是
--link
的主机的别名就会出现于 /etc/hosts 中,其地址由 Docker 引擎维护。因此容器间才可以通过别名互访。而不在同一网络中的容器,不可以互联。但是这种办法并不是好的解决方案
- 首先是因为使用
--link
就很可能还在用默认桥接网络,这很不安全,所有容器都没有适度隔离,用自定义网络才比较方便互联隔离。其次,修改 /etc/hosts 文件有很多弊病。比如,高频繁的容器启停环境时,容易产生竞争冒险,导致 /etc/hosts 文件损坏,出现访问故障;或者有些应用发现是来自于 /etc/hosts 文件后,就假定其为静态文件,而缓存结果不再查询,从而导致容器启停 IP 变更后,使用旧的条目而无法连接到正确的容器等。 - Docker 早在一年多以前就已经使用自定义网络了。在同一个网络中的容器,可以互联,并且,Docker 内置了 DNS,容器内的应用可以使用服务名、容器名、别名来进行服务发现,名称会经由内置的 DNS 进行解析,其结果是动态的;
- 首先是因为使用
查看docker默认的network网络
1
2
3
4
5[root@node1 docker]# docker network ls
NETWORK ID NAME DRIVER SCOPE
af09f33de965 bridge bridge local
73a79b3950dd host host local
99ccf146e61e none null local创建自定义网络
1
2
3
4
5
6
7
8[root@node1 docker]# docker network create net
521dd530656137081816f85ec3b7978020f8aff43fb0446ce9edb4505f41804e
[root@node1 docker]# docker network ls
NETWORK ID NAME DRIVER SCOPE
af09f33de965 bridge bridge local
73a79b3950dd host host local
521dd5306561 net bridge local
99ccf146e61e none null local启动两个测试容器,测试连通性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[root@node1 docker]# docker run -d -it --name=test1 --network net busybox /bin/sh
6ed3c9cb40195564f5895f906684db505578b75258efe1fef1c7bb30f6f88ac6
[root@node1 docker]# docker run -d -it --name=test2 --network net busybox /bin/sh
31fa1bbe74af38ab37eef3508b2d90e178314b7911c62dd4f509f02860799cce
[root@node1 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
31fa1bbe74af busybox "/bin/sh" 26 seconds ago Up 24 seconds test2
6ed3c9cb4019 busybox "/bin/sh" 40 seconds ago Up 38 seconds test1
[root@node1 docker]# docker exec -it test1 sh
/ # ping test2
PING test2 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.094 ms
64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.156 ms
64 bytes from 172.18.0.3: seq=2 ttl=64 time=0.197 ms
64 bytes from 172.18.0.3: seq=3 ttl=64 time=0.202 ms
^C
--- test2 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.094/0.162/0.202 ms
/ # 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.2 6ed3c9cb4019
/ # nslookup -type=a test2
Server: 127.0.0.11
Address: 127.0.0.11:53
Non-authoritative answer:
Name: test2
Address: 172.18.0.3
/ # cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0通过上述实验可知,通过network互联的容器,并没有像–link那样,在/etc/hosts中记录解析信息,而是通过Docker内置的DNS进行动态解析。
在默认的bridge网络创建容器,测试能否与net网络容器连接
- 即便bridge和net共用一个DRIVER,由于他们属于不同的隔离网络,即使通过IP也不能互联访问
1
2
3
4
5
6
7
8
9
10
11
12
13[root@node1 docker]# docker run --name test3 -d -it busybox /bin/sh
18a691bf2f77fe7a11505ea3f7db55b464358c8ea10b822f85d827b4dadb85fe
[root@node1 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
18a691bf2f77 busybox "/bin/sh" 13 seconds ago Up 11 seconds test3
31fa1bbe74af busybox "/bin/sh" 10 minutes ago Up 10 minutes test2
6ed3c9cb4019 busybox "/bin/sh" 10 minutes ago Up 10 minutes test1
[root@node1 docker]# docker exec -it test3 ping 172.18.0.3
PING 172.18.0.3 (172.18.0.3): 56 data bytes
^C
--- 172.18.0.3 ping statistics ---
9 packets transmitted, 0 packets received, 100% packet loss创建新容器,使用net网络,测试能否暴露端口提供访问
- 得知创建的自定义网络,通过bridge网桥,可以挂载主机端口对外提供访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34[root@tiaoban ~]# docker run -d --name=nginx -p 80:80 --network net nginx
09f31a3e73cacf9951ad763777f2d16e745d1f679b8d380e6a63218bc69c8ce7
[root@tiaoban ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
09f31a3e73ca nginx "/docker-entrypoint.…" 8 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx
18a691bf2f77 busybox "/bin/sh" About a minute ago Up About a minute test3
31fa1bbe74af busybox "/bin/sh" 11 minutes ago Up 11 minutes test2
6ed3c9cb4019 busybox "/bin/sh" 11 minutes ago Up 11 minutes test1
[root@node1 docker]# curl 127.0.0.1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed an d
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
添加SSH服务
基于commit
首先,获取centos镜像,并创建一个容器:
1
2[root@node1 docker]# docker pull centos
[root@docker sshd_centos]# docker run -it centos:latest bash安装和配置SSH服务
1
[root@71e353322f0c /]# yum install passwd openssh-server -y
修改root密码
1
[root@71e353322f0c ~]# passwd
生成秘钥
1
2
3[root@71e353322f0c ~]# ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
[root@71e353322f0c ~]# ssh-keygen -t rsa -f /etc/ssh/ssh_host_ecdsa_key
[root@71e353322f0c ~]# ssh-keygen -t rsa -f /etc/ssh/ssh_host_ed25519_key修改配置文件
1
2
3[root@71e353322f0c ~]# vi /etc/ssh/sshd_config
#禁用 PAM
UsePAM no编写服务启动脚本
1
2
3
4[root@71e353322f0c ~]# vi /run.sh
#!/bin/bash
/usr/sbin/sshd -D
[root@71e353322f0c ~]# chmod +x /run.sh退出容器,保存镜像
1
2
3
4[root@71e353322f0c /]# exit
[root@node1 docker]# docker commit 71e353322f0c ssh:centos
sha256:ad951b83040c77e322b7fbb6453cd1bdfa0b23c95604ebfb547e83c4d7e88209查看镜像
1
2
3
4[root@node1 docker]# docker images ssh
REPOSITORY TAG IMAGE ID CREATED SIZE
ssh centos ad951b83040c 27 seconds ago 276MB使用镜像,运行容器
1
2[root@node1 docker]# docker run -p 10086:22 -d ssh:centos /run.sh
9513f14498c0db4a3b44bbdebe3f78450147a44eaf43c4a6a6981662ce8dbf5bssh链接docker容器
1
[root@node1 docker]# ssh root@127.0.0.1 -p 10086
Dockerfile创建
创建工作目录及相关文件
1
2[root@docker ~]# mkdir sshd_centos
[root@docker ~]# touch sshd_centos/Dockerfile run.sh编写run.sh脚本,创建authorized_keys文件
1
2
3[root@docker ~]# vim run.sh
#! /bin/bash
/usr/sbin/sshd -D在宿主主机上生成SSH密钥对,并创建authorized_keys文件:
1
2[root@docker ~]# ssh-keygen -t rsa
[root@docker ~]# cat /root/.ssh/id_rsa.pub > authorized_keys编写Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#设置继承镜像
FROM centos:latest
#提供一些作者的信息
MAINTAINER docker_user (user@docker.com)
#安装 ssh 服务
RUN yum install openssh-server -y
#修改root用户密码
RUN /bin/echo "123.com" | passwd --stdin root
#修改配置信息
RUN /bin/sed -i 's/.*session.*required.*pam_loginuid.so.*/session optional pam_loginuid.so/g' /etc/pam.d/sshd \
&& /bin/sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config \
&& /bin/sed -i "s/#UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g" /etc/ssh/sshd_config
#生成密钥
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key \
&& ssh-keygen -t rsa -f /etc/ssh/ssh_host_ecdsa_key \
&& ssh-keygen -t rsa -f /etc/ssh/ssh_host_ed25519_key
#开放端口
EXPOSE 22
#设置自启动命令
CMD ["/usr/sbin/sshd","-D"]创建镜像, 在sshd_ubuntu目录下,使用docker build命令来创建镜像。这里用户需要注意在最后还有一个“.”,表示使用当前目录中的Dockerfile:
1
[root@docker sshd_centos]# docker build -t sshd:dockerfile .
查看生成的docker镜像
1
[root@docker sshd_centos]# docker images sshd:dockerfile
测试镜像,运行容器
1
[root@docker sshd_centos]# docker run -it -p 10010:22 sshd:dockerfile
Docker网络管理
启动过程
在主机上自动创建一个docker0虚拟网桥,实际上是一个Linux网桥。网桥可以理解为一个软件交换机,负责挂载其上的接口之间进行包转发。
创建一对虚拟接口,分别放到本地主机和新容器的命名空间中;
- 本地主机一端的虚拟接口连接到默认的docker0网桥或指定网桥上,并具有一个以veth开头的唯一名字,如veth1234;
- 另一端的虚拟接口将放到新创建的容器中,并修改名字作为eth0。这个接口只在容器的命名空间可见;
从网桥可用地址段中获取一个空闲地址分配给容器的eth0(例如172.17.0.2/16),并配置默认路由网关为docker0网卡的内部接口docker0的IP地址(例如172.17.42.1/16)。
网络相关参数
在Docker服务启动的时候才能配置,修改后重启生效
命令 说明 -b BRIDGE or –bridge=BRIDGE 指定容器挂载的网桥 –bip=CIDR 定制docker0的掩码 -H SOCKET… or –host=SOCKET Docker服务端接收命令的通道 –icc=true|false 是否支持容器之间进行通信 –ip-forward=true|false 启用net.ipv4.ip_forward,即打开转发功能 –iptables=true|false 禁止Docker添加iptables规则 –mtu=BYTES 容器网络中的MTU 既可以在启动服务时指定,也可以Docker容器启动(使用docker [con-tainer] run命令)时候指定。在Docker服务启动的时候指定则会成为默认值,后续执行该命令时可以覆盖设置的默认值:
命令 说明 –dns=IP_ADDRESS 使用指定的DNS服务器 –dns-opt=”” 指定DNS选项 –dns-search=DOMAIN 指定DNS搜索域 只能在docker [container] run命令执行时使用,因为它针对容器的配置
命令 说明 -h HOSTNAME or –hostname=HOSTNAME 配置容器主机名 -ip 指定容器内接口的IP地址 –link=CONTAINER_NAME:ALIAS 添加到另一个容器的连接 –net=bridge|none|container:NAME_or_ID|host|user_defined_network 配置容器的桥接模式 –network-alias 容器在网络中的别名 -p SPEC or –publish=SPEC 映射容器端口到宿主主机 -P or –publish-all=true|false 映射容器所有端口到宿主主机
–net选项
–net选项支持以下五种模式:
模式 说明 –net=bridge 默认配置。为容器创建独立的网络命名空间,分配网卡、IP地址等网络配置,并通过veth接口对将容器挂载到一个虚拟网桥(默认为docker0)上 –net=none 为容器创建独立的网络命名空间,但不进行网络配置,即容器内没有创建网卡、IP地址等 –net=container:NAME_or_ID 新创建的容器共享指定的已存在容器的网络命名空间,两个容器内的网络配置共享,但其他资源(如进程空间、文件系统等)还是相互隔离的 –net=host 不为容器创建独立的网络命名空间,容器内看到的网络配置(网卡信息、路由表、Iptables规则等)均与主机上的保持一致。注意其他资源还是与主机隔离的 –net=user_defined_network 用户自行用network相关命令创建一个网络,同一个网络内的容器彼此可见,可以采用更多类型的网络插件。
Docker网络命令
命令 | 说明 |
---|---|
create | 创建一个网络 |
connect | 将容器接入到网络 |
disconnect | 把容器从网络上断开 |
inspect | 查看网络的详细信息 |
ls | 列出所有的网络 |
prune | 清理无用的网络资源 |
rm | 删除一个网络 |
创建网络
creat命令用于创建一个新的容器网络。Docker内置了bridge(默认使用)和overlay两种驱动,分别支持单主机和多主机场景。Docker服务在启动后,会默认创建一个bridge类型的网桥bridge。不同网络之间默认相互隔离。
创建网络命令格式为
1
docker network create [OPTIONS] NETWORK
参数 说明 -attachable[=false] 支持手动容器挂载 -aux-address=map[] 辅助的IP地址 -config-from=”” 从某个网络复制配置数据 -config-only[=false] 启用仅可配置模式 -d, -driver=”bridge” 网络驱动类型,如bridge或overlay -gateway=[] 网关地址 -ingress[=false] 创建一个Swarm可路由的网状网络用于负载均衡,可将对某个服务的请求自动转发给一个合适的副本 -internal[=false] 内部模式,禁止外部对所创建网络的访问 -ip-range=[] 指定分配IP地址范围 -ipam-driver=”default” IP地址管理的插件类型 -ipam-opt=map[] IP地址管理插件的选项 -ipv6[=false] 支持IPv6地址 -label value 为网络添加元标签信息 -o, -opt=map[] 网络驱动所支持的选项 -scope=”” 指定网络范围 -subnet=[] 网络地址段,CIDR格式,如172.17.0.0/16。
接入网络
connect命令将一个容器连接到一个已存在的网络上。连接到网络上的容器可以跟同一网络中其他容器互通,同一个容器可以同时接入多个网络。也可以在执行docker run命令时候通过-net参数指定容器启动后自动接入的网络。
接入网络命令格式为
1
docker network connect [OPTIONS] NETWORK CONTAINER
支持参数包括:
参数 说明 -alias=[] 为容器添加一个别名,此别名仅在所添加网络上可见; -ip=”” 指定IP地址,需要注意不能跟已接入的容器地址冲突; -ip6=”” 指定IPv6地址; -link value 添加链接到另外一个容器; -link-local-ip=[] 为容器添加一个链接地址。
断开网络
disconnect命令将一个连接到网络上的容器从网络上断开连接。
命令格式为
1
docker network disconnect [OPTIONS] NETWORK CONTAINER
支持参数包括-f, -force: 强制把容器从网络上移除。
查看网络信息
inspect命令用于查看一个网络的具体信息(JSON格式),包括接入的容器、网络配置信息等。
命令格式为
1
docker network inspect [OPTIONS] NETWORK [NETWORK...]
支持参数包括:
参数 说明 -f, -format=""
给定一个Golang模板字符串,对输出结果进行格式化,如只查看地址配置可以用 -f '{{.IPAM.Config}}'
d-v, -verbose[=false] 输出调试信息
列出网络
ls命令用于列出网络。命令格式为
docker network ls [OPTIONS]
,其中支持的选项主要有:选项 说明 -f, -filter=”” 指定输出过滤器,如driver=bridge; -format=”” 给定一个golang模板字符串,对输出结果进行格式化; -no-trunc[=false] 不截断地输出内容; -q, -quiet[=false] 安静模式,只打印网络的ID。
清理无用网络
prune命令用于清理已经没有容器使用的网络。
命令格式为
docker network prune [OPTIONS] [flags]
,支持参数包括:选项 说明 -filter=”” 指定选择过滤器 -f, -force 强制清理资源
删除网络
- rm命令用于删除指定的网络。当网络上没有容器连接上时,才会成功删除。
- 命令格式为
docker network rm NETWORK [NETWORK...]
配置DNS和主机名
- Docker服务启动后会默认启用一个内嵌的DNS服务,来自动解析同一个网络中的容器主机名和地址,如果无法解析,则通过容器内的DNS相关配置进行解析。用户可以通过命令选项自定义容器的主机名和DNS配置
相关配置文件
容器中主机名和DNS配置信息可以通过三个系统配置文件来管理:
/etc/resolv.conf
/etc/hostname
/etc/hosts
启动一个容器,在容器中使用mount命令可以看到这三个文件挂载信息:
1
2
3
4
5[root@node1 ~]# docker run -it centos bash
[root@51adaac0dedf /]# mount | grep etc
/dev/mapper/centos-root on /etc/resolv.conf type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
/dev/mapper/centos-root on /etc/hostname type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
/dev/mapper/centos-root on /etc/hosts type xfs (rw,relatime,seclabel,attr2,inode64,noquota)/etc/resolv.conf记录了DNS服务器
1
2
3[root@51adaac0dedf /]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 192.168.61.2/etc/hostname文件则记录了容器的主机名
1
2[root@51adaac0dedf /]# cat /etc/hostname
51adaac0dedf/etc/hosts文件中默认只记录了容器自身的地址和名称:
1
2
3
4
5
6
7
8[root@51adaac0dedf /]# 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.17.0.4 51adaac0dedf
容器内修改配置文件
- 容器运行时,可以在运行中的容器里直接编辑/etc/hosts、/etc/hostname和/etc/resolve. conf文件。
- 但是这些修改是临时的,只在运行的容器中保留,容器终止或重启后并不会被保存下来,也不会被docker commit提交。
通过参数指定
注意一般不推荐与-net=host一起使用,会破坏宿主机上的配置信息
- 如果用户想要自定义容器的配置,可以在创建或启动容器时利用下面的参数指定
- 指定主机名
-h HOSTNAME
或者--hostname=HOSTNAME
: 设定容器的主机名。- 容器主机名会被写到容器内的/etc/hostname和/etc/hosts。
- 但这个主机名只有容器内能中看到,在容器外部则看不到,既不会在
docker ps
中显示,也不会在其他容器的/etc/hosts中看到;
--link=CONTAINER_NAME:ALIAS
: 记录其他容器主机名。- 在创建容器的时候,添加一个所连接容器的主机名到容器内/etc/hosts文件中。
- 这样,新建容器可以直接使用主机名与所连接容器通信;
--dns=IP_ADDRESS
: 指定DNS服务器。添加DNS服务器到容器的/etc/resolv.conf
中- 容器会用指定的服务器来解析所有不在/etc/hosts中的主机名;
--dns-option list
: 指定DNS相关的选项;--dns-search=DOMAIN
: 指定DNS搜索域。- 设定容器的搜索域,当设定搜索域为 .example.com 时
- 在搜索一个名为host的主机时,DNS不仅搜索host,还会搜索host.example.com。
防火墙访问控制
容器的访问控制主要通过Linux上的iptables防火墙软件来进行管理和实现。
容器访问外部网络
容器默认指定了网关为docker0网桥上的docker0内部接口。docker0内部接口同时也是宿主机的一个本地接口。因此,容器默认情况下可以访问到宿主机本地网络。如果容器要想通过宿主机访问到外部网络,则需要宿主机进行辅助转发。
在宿主机Linux系统中,检查转发是否打开,代码如下:
1
2
3
4
5[root@node1 ~]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
# 如果为0,说明没有开启转发,则需要手动打开:
[root@node1 ~]# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1Docker服务启动时会默认开启
--ip-forward=true
,自动配置宿主机系统的转发规则
容器之间访问
容器之间相互访问需要两方面的支持:
- 网络拓扑是否已经连通。默认情况下,所有容器都会连接到docker0网桥上,这意味着默认情况下拓扑是互通的;
- 本地系统的防火墙软件iptables是否允许访问通过。这取决于防火墙的默认规则是允许(大部分情况)还是禁止。
访问所有端口
当启动Docker服务时候,默认会添加一条“允许”转发策略到iptables的FORWARD链上。通过配置-
-icc=true|false
(默认值为true)参数可以控制默认的策略。为了安全考虑,可以在Docker配置文件中配置
DOCKER_OPTS="--icc=false"
来默认禁止容器之间的相互访问。1
2[root@node1 ~]# vim /etc/default/docker
DOCKER_OPTS="--icc=false"。同时,如果启动Docker服务时手动指定
--iptables=false
参数,则不会修改宿主机系统上的iptables规则。
访问指定端口
在通过-icc=false禁止容器间相互访问后,仍可以通过
--link=CONTAINER_NAME:ALIAS
选项来允许访问指定容器的开放端口。例如,在启动Docker服务时,可以同时使用
--icc=false --iptables=true
参数来配置容器间禁止访问,并允许Docker自动修改系统中的iptables规则。此时,系统中的iptables规则可能是类似如下规则,禁止所有转发流量:1
2
3
4
5
6
7
8
9
10# sudo iptables -L -n 查看iptables规则的情况
# sudo iptables -F 清空iptables规则设置
# sudo service docker restart 重新启用docker的服务
# sudo iptables -L -n 再来查看iptables的设置,docker的规则链已经在第一位
$ sudo iptables -nL
...
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DROP all -- 0.0.0.0/0 0.0.0.0/0
...启动容器时使用
--link=CONTAINER_NAME:ALIAS
选项。Docker会在iptable中为两个互联容器分别添加一条ACCEPT规则,允许相互访问开放的端口(取决于 Dockerfile 中的 EXPOSE 行)。当添加了
--link=CONTAINER_NAME:ALIAS
选项后,添加了iptables
规则。- 注意:
--link=CONTAINER_NAME:ALIAS
中的CONTAINER_NAME
目前必须是 Docker 分配的名字,或使用--name
参数指定的名字。主机名则不会被识别。
1
2
3
4
5
6
7
8[root@docker ~]# docker run -it --link=3782df38f864:centoslink centos
[root@docker ~]# 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- 注意:
网络分析
默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器。
容器访问外部网络
centos 位于 docker0 这个私有bridge 网络中(172.17.0.0/16),当 centos 从容器向外 ping 时,数据包通过NAT地址转换到达 baidu.com
查看一下 docker host 上的 iptables 规则:
1
2
3
4[root@node1 oracle]# iptables -t nat -S | grep docker0
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 10086 -j DNAT --to-destination 172.17.0.3:22在 NAT 表中,有这么一条规则:
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
其含义是: 如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理。而 MASQUERADE 的处理方式是将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换(NAT)。
通过 tcpdump 查看地址是如何转换的。先查看 docker host 的路由表:
1
2
3
4
5
6
7
8
9[root@node1 oracle]# ip r
default via 192.168.61.2 dev ens33 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.18.0.0/16 dev br-521dd5306561 proto kernel scope link src 172.18.0.1
172.20.0.0/16 dev br-852e88d73bf9 proto kernel scope link src 172.20.0.1
192.168.61.0/24 dev ens33 proto kernel scope link src 192.168.61.10 metric 100
[root@node1 oracle]# tcpdump -i docker0 -n icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes默认路由通过 ens33 发出去,所以我们要同时监控 ens33和 docker0 上的 icmp(ping)数据包。
当 docker的容器ping baidu.com 时,tcpdump 输出如下:
1
2
3
4
5
6
7[root@node1 oracle]# tcpdump -i docker0 -n icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
13:47:23.179156 IP 172.17.0.4 > 50.28.32.8: ICMP echo request, id 1, seq 1, length 64
13:47:23.438049 IP 50.28.32.8 > 172.17.0.4: ICMP echo reply, id 1, seq 1, length 64
13:47:24.232102 IP 172.17.0.4 > 50.28.32.8: ICMP echo request, id 1, seq 2, length 64
13:47:24.499137 IP 50.28.32.8 > 172.17.0.4: ICMP echo reply, id 1, seq 2, length 64docker0 收到 centos 的 ping包,源地址为容器 IP 172.17.0.4,这没问题,交给 MASQUERADE 处理。这时,在 ens33 上我们看到了变化:
1
2
3
4
5
6
7
8# ping 包的源地址变成了 ens33 的 IP 192.168.61.10
[root@node1 oracle]# tcpdump -i ens33 -n icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
13:53:55.595133 IP 192.168.61.10 > 50.28.32.8: ICMP echo request, id 2, seq 1, length 64
13:53:55.823131 IP 50.28.32.8 > 192.168.61.10: ICMP echo reply, id 2, seq 1, length 64
13:53:56.633403 IP 192.168.61.10 > 50.28.32.8: ICMP echo request, id 2, seq 2, length 64这就是 iptable NAT 规则处理的结果,从而保证数据包能够到达外网。这个过程:
- centos 发送 ping 包: 172.17.0.4 > www.baidu.com。
- docker0 收到包,发现是发送到外网的,交给 NAT 处理。
- NAT 将源地址换成 ens33 的 IP: 192.168.61.10 > www.baidu.com。
- ping 包从ens33 发送出去,到达 www.baidu.com。
外部网络访问容器
docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。容器启动时通过-p参数映射端口:
容器启动后,可通过 docker ps 或者 docker port 查看到 host 映射的端口。在上面的例子中,httpd 容器的 80 端口被映射到 host 49154上,这样就可以通过
<host ip>:<49154>
访问容器的 web 服务了。1
2
3
4
5
6
doc[root@node1 ~]# docker run -d --rm -P httpd
4d227b0ab1099b3cf2b857d042f4415975fe922e094dcd7e83d5466ac9f953f7
docker [root@node1 ~]# docker ps | grep 4d227
4d227b0ab109 httpd "httpd-foreground" 15 seconds ago Up 11 seconds 0.0.0.0:49154->80/tcp, :::49154->80/tcp interesting_thompson除了映射动态端口,也可在 -p 中指定映射到 host某个特定端口,例如可将 80 端口映射到 host 的 8080 端口:
1
2
3doc[root@node1 ~]# docker run -d --rm -p 80:80 httpd
# 指定一个端口范围
doc[root@node1 ~]#docker run -d -p 8000-9000:80 httpd每一个映射的端口,host 都会启动一个docker-proxy 进程来处理访问容器的流量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@node1 ~]# ps -ef |grep docker-proxt
root 83152 127169 0 18:51 pts/1 00:00:00 grep --color=auto docker-proxt
[root@node1 ~]# ps -ef |grep docker-proxy
root 47106 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.18.0.4 -container-port 80
root 47112 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 80 -container-ip 172.18.0.4 -container-port 80
root 55098 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 10086 -container-ip 172.17.0.3 -container-port 22
root 55104 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 10086 -container-ip 172.17.0.3 -container-port 22
root 83168 127169 0 18:51 pts/1 00:00:00 grep --color=auto docker-proxy
root 84278 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 1521 -container-ip 172.20.0.2 -container-port 1521
root 84292 20786 0 Sep23 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 1521 -container-ip 172.20.0.2 -container-port 1521
root 127976 20786 0 15:34 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 49154 -container-ip 172.17.0.5 -container-port 80
root 127982 20786 0 15:34 ? 00:00:00 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 49154 -container-ip 172.17.0.5 -container-port 80以 0.0.0.0:32770->80/tcp为例分析整个过程
- docker-proxy 监听 host 的 32770 端口。
- 当 curl 访问 10.0.2.15:32770 时,docker-proxy 转发给容器172.17.0.2:80。
- httpd 容器响应请求并返回结果。
网络类型
查看网络
1
2
3
4
5
6
7[root@node1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
81cc705d9ae4 bridge bridge local
dbefd1fb2d23 host host local
cbb819178e6e mysql_default bridge local
85844dc32a26 none null local
none网络
none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其他任何网卡。
容器创建时,可以通过 –network=none 指定使用 none 网络。
一些对安全性要求高并且不需要联网的应用可以使用 none 网络。比如某个容器的唯一用途是生成随机密码,就可以放到 none 网络中避免密码被窃取。
1
2
3
4
5
6
7
8
9
10
11[root@node1 ~]# docker run -it --network=none busybox
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
5cc84ad355aa: Pull complete
Digest: sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678
Status: Downloaded newer image for busybox:latest
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
host网络
连接到 host 网络的容器共享 Docker host的网络栈,容器的网络配置与 host 完全一样。
通过 –network=host 指定使用 host 网络。
直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host 上已经使用的端口就不能再用了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18[root@node1 ~]# docker run -it --network=host busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:fe:4d:57 brd ff:ff:ff:ff:ff:ff
inet 192.168.61.10/24 brd 192.168.61.255 scope global noprefixroute ens32
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fefe:4d57/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue
link/ether 02:42:e3:ed:a0:cf brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
bridge网络
Docker 安装时会创建一个命名为docker0 的 linux bridge。如果不指定–network,创建的容器默认都会挂到 docker0 上。
创建一个容器后,查看容器网络信息。
1
2
3
4
5
6
7
8
9
10
11
12
13[root@node1 ~]# docker run -it busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
/ # ip route
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 scope link src 172.17.0.21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26# 查看服[root@node1 ~]# ip a
[root@node1 ~]# 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: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:fe:4d:57 brd ff:ff:ff:ff:ff:ff
inet 192.168.61.10/24 brd 192.168.61.255 scope global noprefixroute ens32
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fefe:4d57/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:e3:ed:a0:cf brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:e3ff:feed:a0cf/64 scope link
valid_lft forever preferred_lft forever
...
8: vethe63fdb0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 9a:e6:32:fe:5b:ba brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::98e6:32ff:fefe:5bba/64 scope link
valid_lft forever preferred_lft forever
...一个新的网络接口vethe63fdb0被挂到了 docker0 上,vethe63fdb0就是新创建容器的虚拟网卡。eth0@if8和vethe63fdb0@if7是一对 veth pair。veth pair 是一种成对出现的特殊网络设备,可以把它们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0@if8)在容器中,另一头(vethe63fdb0@if7)挂在网桥 docker0 上,其效果就是将 eth0@if8 也挂在了 docker0 上。
每次创建一个新容器的时候,Docker从可用的地址段中选择一个空闲的IP地址分配给容器的eth0端口,并且使用本地主机上docker0接口的IP作为容器的默认网关
自定义网络
修改默认网桥
安装brctl软件包
1
[root@node1 ~]# yum -y install bridge-utils
停止docker服务并移除docker0网桥
1
2
3
4[root@node1 ~]# systemctl stop docker
[root@node1 ~]# ip link set dev docker0 down
[root@node1 ~]# brctl delbr docker0
[root@node1 ~]# iptables -t nat -F POSTROUTING创建自定义网桥,bridge0 可以换成其他名称, 192.168.0.0/24 也可以换成你喜欢的其它网段
1
2
3
4
5
6# 添加新网桥,bridge0为新网桥的名称
[root@node1 ~]# brctl addbr bridge0
# 为网桥配置IP地址,此处可以将IPv4地址替换为IPv6地址,也可分两次为该网桥分别添加IPv4和IPv6地址
[root@node1 ~]# ip addr add 192.168.0.0/24 dev bridge0
# 启动网桥
[root@node1 ~]# ip link set dev bridge0 up查看网桥信息
1
2
3
4
5
6
7
8[root@node1 ~]# ip addr show bridge0
11: bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether ee:30:6a:61:9f:db brd ff:ff:ff:ff:ff:ff
inet 192.168.0.0/24 scope global bridge0
valid_lft forever preferred_lft forever
inet6 fe80::ec30:6aff:fe61:9fdb/64 scope link
valid_lft forever preferred_lft forever修改配置,设置docker默认使用新的网桥
1
[root@node1 ~]# echo 'DOCKER_OPTS="-b=bridge0" ' >> /etc/sysconfig/docker
修改systemctl启动配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14#修改配置文件 vim /lib/systemd/system/docker.service
[Service]
Type=notify
EnvironmentFile=-/etc/sysconfig/docker # (<-- #添加配置文件 -代表ignore error)
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
# ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock (<-- 注释ExecStart)
ExecStart=/usr/bin/dockerd $DOCKER_OPTS # (<-- 设定网卡参数)
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always重启docker
1
2[root@node1 ~]# systemctl daemon-reload
[root@node1 ~]# systemctl start docker运行容器,测试效果
1
2
3
4
5
6
7
8
9
10
11[root@node1 ~]# docker run -it busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
38: eth0@if39: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:c0:a8:00:02 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.2/24 brd 192.168.0.255 scope global eth0
valid_lft forever preferred_lft forever
bridge创建自定义网桥
创建自定义网桥
1
2
3
4
5[root@node1 ~]# docker network create docker1 --subnet=192.17.1.0/16 -o com.docker.network.bridge.name=docker1
8fdfc08340243e1b6cbf0d0d3b0fb08ee60f611dd267675511dae5d73c3b8998
# --subnet=192.17.1.0/16: 指定IP网段
# -o com.docker.network.bridge.name=docker1: 指定设备名称
#Error response from daemon: Pool overlaps with other one on this address space 那就换一个网段查看网络
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15[root@node1 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
5d7b7033ec88 bridge bridge local
8fdfc0834024 docker1 bridge local
dbefd1fb2d23 host host local
cbb819178e6e mysql_default bridge local
85844dc32a26 none null local
[root@node1 ~]# ip addr show docker1
40: docker1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:4e:ea:a5:94 brd ff:ff:ff:ff:ff:ff
inet 192.17.0.1/16 brd 192.17.255.255 scope global docker1
valid_lft forever preferred_lft forever运行容器,使用docker1网络
1
2
3
4
5
6
7
8
9
10
11
12
13[root@node1 ~]# docker run -it --network docker1 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
41: eth0@if42: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:c0:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 192.17.0.2/16 brd 192.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
#运行容器,指定ip地址
[root@docker ~]# docker run --rm -it --network docker1 --ip 192.17.0.100 busybox删除自定义网桥
1
[root@node1 ~]# docker network rm docker1
自定义网桥与默认docker0通信
1
2
3
4# docker 在设计上就是要隔离不同的 netwrok。
# 由于iptables DROP 掉了网桥 docker0 之间双向的流量。只能采取为 容器添加一块docker1的网卡来实现通信
# docker network connect [OPTIONS] NETWORK CONTAINER
[root@node1 ~]# docker network connect docker1 506505点到点连接
用户有时候需要两个容器之间可以直连通信,而不用通过主机网桥进行桥接。
解决办法很简单: 创建一对peer接口,分别放到两个容器中,配置成点到点链路类型即可。
启动两个容器
1
2[root@node1 ~]# docker run -it --net=none --name=box1 busybox
[root@node1 ~]# docker run -it --net=none --name=box2 busybox创建网络命名空间的跟踪文件
1
2
3
4
5
6
7
8
9
10# 找到容器进程号,并创建网络命名空间的跟踪文件
[root@node1 ~]# docker inspect -f '{{.State.Pid}}' box1
68829
[root@node1 ~]# docker inspect -f '{{.State.Pid}}' box2
69215
[root@node1 ~]# mkdir -p /var/run/netns
[root@node1 ~]# ln -s /proc/69215/ns/net /var/run/netns/68829
[root@node1 ~]# ln -s /proc/69215/ns/net /var/run/netns/69215创建一对peer接口
1
2# 创建一对peer接口 创建虚拟网络接口A,并为A创建一个映射端设备B
[root@docker ~]# ip link add A1 type veth peer name B1添加IP地址和路由信息
1
2
3
4
5
6
7
8
9# 将设备A加入到创建的网络
[root@docker ~]# ip link set A1 netns 68829
[root@docker ~]# ip netns exec 68829 ip addr add 10.1.1.1/32 dev A1
[root@docker ~]# ip netns exec 68829 ip link set A1 up
[root@docker ~]# ip netns exec 68829 ip route add 10.1.1.2/32 dev A1
[root@docker ~]# ip link set B1 netns 69215
[root@docker ~]# ip netns exec 69215 ip addr add 10.1.1.2/32 dev B1
[root@docker ~]# ip netns exec 69215 ip link set B1 up
[root@docker ~]# ip netns exec 69215 ip route add 10.1.1.1/32 dev B1
docker网络模型
容器网络模型包括三种基本元素:
- 沙盒(Sandbox): 代表一个容器(准确地说,是其网络命名空间);
- 接入点(Endpoint): 代表网络上可以挂载容器的接口,会分配IP地址;
- 网络(Network): 可以连通多个接入点的一个子网。
对于使用CNM的容器管理系统来说,具体底下网络如何实现,不同子网彼此怎么隔离,有没有QoS,都不关心。只要插件能提供网络和接入点,只需把容器给接上或者拔下,剩下的都是插件驱动自己去实现,这样就解耦了容器和网络功能,十分灵活。
CNM的典型生命周期
首先,驱动注册自己到网络控制器,网络控制器使用驱动类型,来创建网络;然后在创建的网络上创建接口;最后把容器连接到接口上即可。销毁过程则正好相反,先把容器从接入口上卸载,然后删除接入口和网络即可。
CNM的典型生命周期目前CNM支持的驱动类型有四种:
- Null: 不提供网络服务,容器启动后无网络连接;
- Bridge: 就是Docker传统上默认用Linux网桥和Iptables实现的单机网络;
- Overlay: 是用vxlan隧道实现的跨主机容器网络;
- Remote: 扩展类型,预留给其他外部实现的方案,比如有一套第三方的SDN方案(如OpenStack Neutron)就可以接进来。
从位置上看,libnetwork往上提供容器支持,往下隐藏实现差异,自身处于十分关键的中间层。libnetwork可以类比为最核心的TCP/IP层。目前,已有大量的网络方案开始支持libnetwork。
示例:创建一个点到点连接
默认情况下,Docker 会将所有容器连接到由 docker0
提供的虚拟子网中。
用户有时候需要两个容器之间可以直连通信,而不用通过主机网桥进行桥接。
解决办法很简单:创建一对 peer
接口,分别放到两个容器中,配置成点到点链路类型即可。
首先启动 2 个容器:
1 | $ docker run -i -t --rm --net=none base /bin/bash |
找到进程号,然后创建网络命名空间的跟踪文件。
1 | $ docker inspect -f '{{.State.Pid}}' 1f1f4c1f931a |
创建一对 peer
接口,然后配置路由
1 | $ sudo ip link add A type veth peer name B |
现在这 2 个容器就可以相互 ping 通,并成功建立连接。点到点链路不需要子网和子网掩码。
此外,也可以不指定 --net=none
来创建点到点链路。这样容器还可以通过原先的网络来通信。
利用类似的办法,可以创建一个只跟主机通信的容器。但是一般情况下,更推荐使用 --icc=false
来关闭容器之间的通信。