Docker网络
-
Overlay Network
Overlay 在网络技术领域,指的是一种网络架构上叠加的虚拟化技术模式,其大体框架是对基础网络不进行大规模修改的条件下,实现应用在网络上的承载,并能与其它网络业务分离,并且以基于IP的基础网络技术为主。Overlay 技术是在现有的物理网络之上构建一个虚拟网络,上层应用只与虚拟网络相关
在以往IT建设中,硬件服务器上运行的是虚拟层的计算,物理网络为了与虚拟服务器对接,需要网络自己进行调整,以便和新的计算层对接。Overlay 是在传统网络上虚拟出一个虚拟网络来,传统网络不需要在做任何适配,这样物理层网络只对应物理层的计算(物理机、虚拟化层管理网),虚拟的网络只对应虚拟计算(虚拟机的业务IP)
-
使用网络
容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P
或 -p
参数来指定端口映射。
当使用 -P 标记时,Docker 会随机映射一个 49000~49900
的端口到内部容器开放的网络端口。
bridge
bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上。
当 Docker server 启动时,会在主机上创建一个名为 docker0 的虚拟网桥,此主机上启动的 Docker 容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配 IP 了,Docke r会从 RFC918 所定义的私有IP网段中,选择一个和宿主机不同的IP地址和子网分配给 docker0,连接到 docker0 的容器就从这个子网中选择一个未占用的IP使用。如一般 Docke r会使用 172.17.0.0/16 这个网段,并将 172.17.42.1/16 分配给 docker0 网桥
bridge工作流程
在主机上创建一对虚拟网卡 veth pair 设备。veth 设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth 设备常用来连接两个网络设备
Docker 将 veth pair 设备的一端放在新创建的容器中,并命名为 eth0。另一端放在主机中,以 veth******* 这样的名字命名,并将这个网络设备加入到 docker0 网桥中,可以通过 brctl show 命令查看
[root@CentOS74 ~]#brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.024220c0a37a no vethbc2d1dc
vethcf47b7e
从 docker0 子网中分配一个IP给容器使用,并设置 docker0 的 IP 地址为容器的默认网关
在物理机上使用 ip 命令调用内核参数,模拟 docker 网络中 brigde 的工作流程
[root@CentOS174 ~]# ip netns add testns #新增一个Net名称空间
[root@CentOS174 ~]# ip netns list
testns
[root@CentOS174 ~]# ip link add name veth10 type veth peer name veth01 #新键一对虚拟网卡
[root@CentOS174 ~]# ip link list
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
link/ether 00:0c:29:3d:70:fc brd ff:ff:ff:ff:ff:ff
3: ens37: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT qlen 1000
link/ether 00:0c:29:3d:70:06 brd ff:ff:ff:ff:ff:ff
4: veth01@veth10: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 #虚拟网卡总是相会对应
link/ether 22:d3:7d:fc:43:bb brd ff:ff:ff:ff:ff:ff
5: veth10@veth01: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
link/ether 6a:3e:84:a3:9c:af brd ff:ff:ff:ff:ff:ff
[root@CentOS174 ~]# ip link set veth10 netns testns #将虚拟网卡中的一块分配给指定Net名称空间
[root@CentOS174 ~]# ip link set veth01 up #启动主机一端的网卡
[root@CentOS174 ~]# ip addr add 172.17.0.1/16 dev veth01 #为其配置ip地址
[root@CentOS174 ~]# ip addr show veth01
4: veth01@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
link/ether 22:d3:7d:fc:43:bb brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.1/16 scope global veth01
valid_lft forever preferred_lft forever
inet6 fe80::20d3:7dff:fefc:43bb/64 scope link
valid_lft forever preferred_lft forever
[root@CentOS174 ~]# ip netns exec testns ip link set dev veth10 up #启动testns空间中一端的网卡
[root@CentOS174 ~]# ip netns exec testns ip addr add 172.17.0.2/16 dev veth10 #为期配置ip地址
valid_lft forever preferred_lft forever
[root@CentOS174 ~]# ip netns exec testns ip a show veth10
5: veth10@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
link/ether 6a:3e:84:a3:9c:af brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 scope global veth10
valid_lft forever preferred_lft forever
inet6 fe80::683e:84ff:fea3:9caf/64 scope link
valid_lft forever preferred_lft forever
在创建容器时,使用 --network bridge 指定使用 bridge 模式网络,并使用 -p 选项暴露容器的指定端口,当需要暴露的端口有多个时,可以多次使用 -p 选项
[root@CentOS74 ~]# docker run --name nettest -d --rm --network bridge -p 80 miriam:CentOS-Nginx
使用 ss -ntl 或者 iptables -t nat -vnL 亦或者 docker port 容器名 查看本地的端口映射
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 32769 -j DNAT --to-destination 172.17.0.3:80
[root@CentOS74 ~]# docker port nettest
80/tcp -> 0.0.0.0:32769
也可以映射本机的指定端口,或者指定地址的指定端口,或者指定地址的动态端口
[root@CentOS74 ~]# docker run --name nettest -d --rm --network bridge -p 80:80 miriam:CentOS-Nginx
b09da5fb40aaf3921a0c6707f030d1eaa3b81e2f35873e6284d188f3bf8508df
[root@CentOS74 ~]# docker port nettest
80/tcp -> 0.0.0.0:80
[root@CentOS74 ~]# docker run --name nettest -d --rm --network bridge -p 192.168.30.74:80:80 miriam:CentOS-Nginx
aa447e2264905ac36b7a84dfa96c1e06e56c1118943163fd05170542460b6749
[root@CentOS74 ~]# docker port nettest
80/tcp -> 192.168.30.74:80
使用 -P 选项暴露镜像文件中声明的监听端口
[root@CentOS74 ~]# docker run --name nettest -d --rm --network bridge -P miriam:CentOS-Nginx
888f6602e968eda1a32f269dc7f0856b253b74114bc8a0d1e7f66384517cab47
Host
当我们在容器中执行任何类似ifconfig命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用10.10.101.105:80即可,不用任何NAT转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
在创建容器时,使用 --network host 指定使用 host 模式网络
[root@CentOS74 ~]# docker run --name nettest -d --rm --network host miriam:CentOS-Nginx
bf46888a3079997e35aa919d7de9fe5127a8d8a30a23062dc02e147fc003a459
[root@CentOS74 ~]#⮀⮀ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:80 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 172.20.116.3:10010 *:*
LISTEN 0 128 :::80 :::*
LISTEN 0 128 :::22 :::*
LISTEN 0 100 ::1:25 :::*
container
这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
[root@CentOS74 ~]# docker run --name netbbox -it --rm --network container:test jiangbowen/httpbbox:v0.1-1
Null
这个模式和前两个不同。在这种模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
在创建容器时,使用 --network none 指定使用 null 模式网络
-
修改网络
配置文件修改daemon属性
当需要修改 docker0 桥使用的 ip 地址,则需要修改 /etc/docker/daemon.json
其中有许多配置项,可以在 Docler 配置文件 中查看具体配置
{
"api-cors-header": "",
"authorization-plugins": [],
"bip": "",
"bridge": "",
"cgroup-parent": "",
"cluster-store": "",
"cluster-store-opts": {},
"cluster-advertise": "",
"debug": true, #启用debug的模式,启用后,可以看到很多的启动信息。默认 false
"default-gateway": "",
"default-gateway-v6": "",
"default-runtime": "runc",
"default-ulimits": {},
"disable-legacy-registry": false,
"dns": ["192.168.80.1"], // 设定容器DNS的地址,在容器的 /etc/resolv.conf 文件中可查看。
"dns-opts": [], // 参考:Docker的启动参数
"dns-search": [], // 参考:Docker的启动参数
"exec-opts": [],
"exec-root": "",
"fixed-cidr": "",
"fixed-cidr-v6": "",
"graph": "/var/lib/docker", #已废弃,使用data-root代替
"data-root": "/var/lib/docker", /*Docker运行时使用的根路径,默认/var/lib/docker*/
"group": "", #Unix套接字的属组,仅指/var/run/docker.sock
"hosts": [], #设置容器hosts
"icc": false,
"insecure-registries": [],
"ip": "0.0.0.0",
"iptables": false,
"ipv6": false,
"ip-forward": false, // 参考:Docker的启动参数
"ip-masq": false,
"labels": ["nodeName=node-121"], //参考:Docker的启动参数
"live-restore": true,
"log-driver": "",
"log-level": "",
"log-opts": {},
"max-concurrent-downloads": 3,
"max-concurrent-uploads": 5,
"mtu": 0,
"oom-score-adjust": -500,
"pidfile": "", #Docker守护进程的PID文件
"raw-logs": false,
"registry-mirrors": ["xxxx"], //镜像加速的地址,增加后在 docker info 中可查看。
"runtimes": {
"runc": {
"path": "runc"
},
"custom": {
"path": "/usr/local/bin/my-runc-replacement",
"runtimeArgs": ["--debug"]
}
},
"selinux-enabled": false, #参考:Docker的启动参数
"storage-driver": "",
"storage-opts": [],
"swarm-default-advertise-addr": "",
"tls": true, #参考:Docker的启动参数
"tlscacert": "", #参考:Docker的启动参数
"tlscert": "", #参考:Docker的启动参数
"tlskey": "", #参考:Docker的启动参数
"tlsverify": true, #参考:Docker的启动参数
"userland-proxy": false,
"userns-remap": ""
}
其中修改网络配置中最重要的选项是 "bip": "" ,当修改此项后,其他的网络参数也将会自动根据其修改
[root@CentOS74 ~]# ip addr show docker0
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
link/ether 02:42:f9:9b:b7:50 brd ff:ff:ff:ff:ff:ff
inet 192.168.20.1/24 brd 192.168.20.255 scope global docker0
valid_lft forever preferred_lft forever
修改 hosts 选项使本地 Docker daemon 可以被外部主机通过网络连接
[root@CentOS74 ~]#⮀⮀cat /etc/docker/daemon.json
{
"registry-mirrors": ["https://xet4hxm8.mirror.aliyuncs.com"],
"bip": "192.168.20.1/24",
"default-gateway": "192.168.20.174",
"dns": ["114.114.114.144","8.8.8.8"],
"mtu": 1500,
"hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"]
}
这时便可以使用 docker 的 -H 选项连接远程主机的 Docker daemon
创建自定义网络
使用 docker network create 命令创建新的 docker 网络
[root@CentOS74 ~]# docker network create -d "bridge" --subnet "192.168.10.0/24" --gateway "192.168.10.1" dcoker1
be7c49653f4d505a6daba179854dac551212a74a74472b3a5dfc5c9698204be0
创建完成后可以在创建容器时通过 --network 选项使用指定网络
[root@CentOS74 ~]# docker container run -it --rm --network dcoker1 jiangbowen/httpbbox:v0.1-1
当通主机上的两个 docker 网络进行通信时,则需要修改 iptables 的 filter 表中的 DOCKER-ISOLATION-STAGE-2 链,并打开内核中的路由转发
[root@CentOS74 ~]# iptables -R DOCKER-ISOLATION-STAGE-2 1 -o docker0 -j ACCEPT
[root@CentOS74 ~]# iptables -R DOCKER-ISOLATION-STAGE-2 2 -o docker0 -j ACCEPT
[root@CentOS74 ~]# echo 1 > /proc/sys/net/ipv4/ip_forward
数据卷
数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
数据卷可以在容器之间共享和重用
对数据卷的修改会立马生效
对数据卷的更新,不会影响镜像
卷会一直存在,直到没有容器使用
数据卷的使用,类似于 Linux 下对目录或文件进行 mount
-
创建数据卷
绑定挂载卷
用户同时指定容器中目录与宿主机目录路径
[root@CentOS74 CentOS-Nginx]# echo "This is Host HomePage" > /usr/share/nginx/html/index.html
[root@CentOS74 CentOS-Nginx]# cat /usr/share/nginx/html/index.html
This is Host HomePage
[root@CentOS74 CentOS-Nginx]# docker container run -d -v /usr/share/nginx/html/index.html:/usr/share/nginx/html/index.html miriam:CentOS-Nginx
68db5858815724fea05ebae4c77795db13b7e3d353587efcb0eaf702b72a6e71
[root@CentOS74 CentOS-Nginx]# curl http://192.168.20.2
This is Host HomePage
这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,如果目录不存在 Docker 会自动为你创建它。
注意:Dockerfile 中不支持这种用法,这是因为 Dockerfile 是为了移植和分享用的。然而,不同操作系统的路径格式不一样,所以目前还不能支持。
Docker管理卷
用户只用指定容器中的目录,而宿主机上的目录则由 Docker 自动在默认路径下创建。但是当第二次使用容器时,为了保证数据的持久性,则必须使用绑定挂载卷的模式启动容器
使用 Docker 管理卷的模式挂载数据卷
[root@CentOS74 CentOS-Nginx]# docker container run -d -v /usr/share/nginx/html/ miriam:CentOS-Nginx
7ff6531e7e9248b9e9adb9ab8a10bbb415f7d957e4a43a0545a31337a3f051eb
容器创建后,通过 docker container inspect 容器名 查看 Mounts 段记录的本地文件路径
"Mounts": [
{
"Type": "volume",
"Name": "51b3120f4d9d8787ceefdfea14f8971a4d11cc021d3c4e4889c6db98b2394c45",
"Source": "/var/lib/docker/volumes/51b3120f4d9d8787ceefdfea14f8971a4d11cc021d3c4e4889c6db98b2394c45/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
在宿主机上修改数据卷中的文件,在容器中查看
[root@CentOS74 share]# curl http://192.168.20.2
This is Miriam HomePage
[root@CentOS74 share]# curl http://192.168.20.2
This is Miriam VOLUME
注意:如果直接挂载一个文件,很多文件编辑工具,可能会造成文件 inode 的改变,从 Docker 1.1.0起,这会导致报错误信息。所以最简单的办法就直接挂载文件的父目录。
Docker 挂载数据卷的默认权限是读写,用户也可以通过 :ro 指定为只读。
[root@CentOS74 ~]# docker container run -d -v /data/:/usr/share/nginx/html/:ro miriam:CentOS-Nginx
bc537c2574f567d45806b85465f8cae9df199a669caa669f68a7ca5782e0de6f
-
数据卷容器
如果你有一些持续更新的数据需要在容器之间共享,最好创建数据卷容器。
数据卷容器,其实就是一个正常的容器,专门用来提供数据卷供其它容器挂载的。
使用 --volumes-from 容器名 来共享指定容器的数据卷配置
[root@CentOS74 ~]# docker container run -d --rm --volumes-from tender_wozniak miriam:CentOS-Nginx
2833be95ae1cd8be2ab6012affef7fdd0a45d847fbb1a7eb567d8820df24dfe1