Docker网络
一、单机中Docker网络
1、理解Docker默认网桥
安装Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。
**Docker 安装时会自动在 host 上创建三个网络:none、host、bridge。**使用docker network ls 命令查看:
[root@pdai ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
b8c5abdb0bec bridge bridge local
84e86bc93121 host host local
8e521527a897 none null local
通过 ip a就可以看到三个网络所对应的网络IP各不相同。再用docker network inspect指令查看bridge网络:其Gateway就是网卡/接口docker0的IP地址:172.17.0.1。
[root@pdai ~]# 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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:08:c1:ea brd ff:ff:ff:ff:ff:ff
inet 172.31.165.194/20 brd 172.31.175.255 scope global dynamic eth0
valid_lft 310072401sec preferred_lft 310072401sec
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:70:3f:9d:02 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
45: veth4ad3278@if44: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether c2:77:e4:ea:f1:33 brd ff:ff:ff:ff:ff:ff link-netnsid 0
49: veth0004826@if48: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 6e:c9:1a:7c:18:b1 brd ff:ff:ff:ff:ff:ff link-netnsid 1
[root@pdai ~]# ip route
default via 172.31.175.253 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.31.160.0/20 dev eth0 proto kernel scope link src 172.31.165.194
[root@pdai ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "b8c5abdb0becacfa1bfa1d72e2e663fb0157b62a9b8bee37e2607211722713cc",
"Created": "2020-02-17T14:10:10.424119543+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": {
"1cbc9aeba2a8a826d460ecb49de17ddf8ac336e150c752a3c762fd38a3e15254": {
"Name": "web",
"EndpointID": "adb47ed0c60c6b80a442c71a5f35d63378cecca9598e0cef8409a6719552f4c2",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"d992e3c761e00649eb436b88c737adc54093b76119af0fb7878596b523f743ca": {
"Name": "db",
"EndpointID": "6a3dab5c545dd26e0ca6e36d928a32fd0a6197c8dbf4eeb718a4560e219575ed",
"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的配置信息和容器信息。
2、理解容器创建时的IP分配
为了理解容器创建时的IP分配,这里需要清理所有已经启动的环境,然后再启动容器,看前后对比
[root@pdai ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@pdai ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
b8c5abdb0bec bridge bridge local
84e86bc93121 host host local
8e521527a897 none null local
[root@pdai ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "b8c5abdb0becacfa1bfa1d72e2e663fb0157b62a9b8bee37e2607211722713cc",
"Created": "2020-02-17T14:10:10.424119543+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": {},
"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": {}
}
]
[root@pdai ~]# 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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:08:c1:ea brd ff:ff:ff:ff:ff:ff
inet 172.31.165.194/20 brd 172.31.175.255 scope global dynamic eth0
valid_lft 310069965sec preferred_lft 310069965sec
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:70:3f:9d:02 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
[root@pdai ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242703f9d02 no
3、创建容器
Docker 在创建一个容器的时候,会执行如下操作:
- 创建一对虚拟接口/网卡,也就是veth pair,分别放到本地主机和新容器中;
- 本地主机一端桥接到默认的 docker0 或指定网桥上,并具有一个唯一的名字,如 vethxxxxx;
- 容器一端放到新容器中,并修改名字作为 eth0,这个网卡/接口只在容器的名字空间可见;
- 从网桥可用地址段中(也就是与该bridge对应的network)获取一个空闲地址分配给容器的 eth0,并配置默认路由到桥接网卡 vethxxxx。
看下变化:
[root@pdai ~]# docker run -d --name db training/postgres
0ffd1092cd962bdbe335ce042b93d0f2082559600cacc82bbef40b8b66395e57
[root@pdai ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242703f9d02 no vethd93e2ad
[root@pdai ~]# ip a | grep vethd93e2ad
51: vethd93e2ad@if50: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
[root@pdai ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "b8c5abdb0becacfa1bfa1d72e2e663fb0157b62a9b8bee37e2607211722713cc",
"Created": "2020-02-17T14:10:10.424119543+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": {
"0ffd1092cd962bdbe335ce042b93d0f2082559600cacc82bbef40b8b66395e57": {
"Name": "db",
"EndpointID": "a90cb50031effc99b9254fe4f1231bfbac8c4bb23d94c5a1425c1e116ac452dc",
"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": {}
}
]
**如果不指定–network,创建的容器默认都会挂到 docker0 上,使用本地主机上 docker0 接口的 IP 作为所有容器的默认网关。**当有多个容器创建后,容器网络拓扑结构如下:
4、容器和docker0的虚拟网卡的配对
可以利用ethtool来确认这种对应关系:分别在host和container中运行指令ethtool -S <interface>
:
[root@pdai ~]# ethtool -S vethd93e2ad
NIC statistics:
peer_ifindex: 50
[root@pdai ~]# docker exec -it 0ffd1092cd96 /bin/bash
root@0ffd1092cd96:/# ip a | grep 50
50: eth0@if51: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
root@0ffd1092cd96:/#
小结
**安装Docker 服务默认会创建一个 docker0 网桥,它在内核层连通了其中的物理网络。而Docker 安装时会自动在 host 上创建三个网络:none、host、bridge,每个网络所对应的IP都不同。**而在创建容器的时候,会创建唯一的veth pair分别放到本地主机和新容器中。主机会把其桥接到默认docker0网桥或者是指定网桥并具有唯一的名字;容器也会将其放到新容器中,并给其指定专属名字,其名字内部可见。之后会给新容器在网桥的可用地址中随机分配个空闲地址给其使用,变更其相关配置信息。
二、多物理机之间互联
如果在企业内部应用,或者做多个物理主机的集群,可能需要将多个物理主机的容器组到一个物理网络中来,那么就需要将这个网桥桥接到我们指定的网卡上。
1、拓扑图
主机 A 和主机 B 的网卡一都连着物理交换机的同一个 vlan 101,这样网桥一和网桥三就相当于在同一个物理网络中了,而容器一、容器三、容器四也在同一物理网络中了,他们之间可以相互通信,而且可以跟同一 vlan 中的其他物理机器互联。
2、如何实现多个主键的容器联网
2-1)创建多个主机的容器联网,需要专门创建自己的网桥,编辑 /etc/network/interface 文件
auto br0
iface br0 inet static
address 192.168.7.31
netmask 255.255.240.0
gateway 192.168.7.254
bridge_ports em1
bridge_stp off
dns-nameservers 8.8.8.8 192.168.6.1
**2-2)将 Docker 的默认网桥绑定到这个新建的 br0 上面,这样就将这台机器上容器绑定到 em1 这个网卡所对应的物理网络上了。**ubuntu 修改 /etc/default/docker 文件,添加最后一行内容
# Docker Upstart and SysVinit configuration file
# Customize location of Docker binary (especially for development testing).
#DOCKER="/usr/local/bin/docker"
# Use DOCKER_OPTS to modify the daemon startup options.
#DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4"
# If you need Docker to use an HTTP proxy, it can also be specified here.
#export http_proxy="http://127.0.0.1:3128/"
# This is also a handy place to tweak where Docker's temporary files go.
#export TMPDIR="/mnt/bigdrive/docker-tmp"
DOCKER_OPTS="-b=br0"
2-3)在启动 Docker 的时候 使用 -b 参数 将容器绑定到物理网络上。重启 Docker 服务后,再进入容器可以看到它已经绑定到你的物理网络上了。
root@pdai:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
58b043aa05eb desk_hz:v1 "/startup.sh" 5 days ago Up 2 seconds 5900/tcp, 6080/tcp, 22/tcp yanlx
root@pdai:~# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.7e6e617c8d53 no em1
vethe6e5
这样就直接把容器暴露到物理网络上了,多台物理主机的容器也可以相互联网了。需要注意的是,这样就需要自己来保证容器的网络安全了。
Docker数据卷
一、数据卷
数据卷(Data Volume)是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
- 数据卷可以在容器之间共享和重用
- 对数据卷的修改会立马生效
- 对数据卷的更新,不会影响镜像
- 卷会一直存在,直到没有容器使用
1、建一个数据卷
**在用 docker run 命令的时候,使用 -v 标记来创建一个数据卷并挂载到容器里。在一次 run 中多次使用可以挂载多个数据卷。**例子如下:
1)创建一个 web 容器,并加载一个数据卷到容器的 /webapp-data 目录。
[root@pdai ~]# docker run -d -P --name web -v /webapp-data training/webapp python app.py
e331e83e59486a131919cba8698b24eaee051a947838bb1c15c03df8b3464b97
2)看下容器内部是否生成/webapp-data目录
[root@pdai ~]# docker exec -it web /bin/bash
root@e331e83e5948:/opt/webapp# cd /webapp-data
root@e331e83e5948:/webapp-data# ll
total 8
drwxr-xr-x 2 root root 4096 Feb 20 01:24 ./
drwxr-xr-x 1 root root 4096 Feb 20 01:24 ../
root@e331e83e5948:/webapp-data#
注意:也可以在 Dockerfile 中使用 VOLUME 来添加一个或者多个新的卷到由该镜像创建的任意容器。
2、挂载一个主机目录作为数据卷
使用 -v 标记也可以指定挂载一个本地主机的目录到容器中去。
[root@pdai ~]# docker rm -f web
web
[root@pdai opt]# docker run -d --name web -v /opt/webapp-data5:/opt/webapp2 training/webapp
fce27f6ea9ce9699864644a48aed6db8b772c96be36f46bee6154d2e2c9915b9
验证下:
[root@pdai opt]# docker exec -it web /bin/bash
root@fce27f6ea9ce:/opt/webapp# cd ..
root@fce27f6ea9ce:/opt# ls
webapp webapp2
root@fce27f6ea9ce:/opt# cd webapp2
root@fce27f6ea9ce:/opt/webapp2# mkdir test
root@fce27f6ea9ce:/opt/webapp2# exit
exit
[root@pdai opt]# cd webapp-data5
[root@pdai webapp-data5]# ll
total 4
drwxr-xr-x 2 root root 4096 Feb 20 10:12 test
上面的命令加载主机的 /opt/webapp-data5 目录到容器的 /opt/webapp2 目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,如果目录不存在 Docker 会自动为你创建它。
注意:Dockerfile 是不支持这种用法,这是因为 Dockerfile 是为了移植和分享用的, 因为不同操作系统的路径格式不一样
删除容器,看主机上数据不会被删除
[root@pdai opt]# docker rm -f web
web
[root@pdai opt]# cd /opt/webapp-data5
[root@pdai webapp-data5]# ll
total 4
drwxr-xr-x 2 root root 4096 Feb 20 10:12 test
3、挂载一个本地主机文件作为数据卷
-v 标记也可以从主机挂载单个文件到容器中,挂载进去之后就可以记录在容器输入过的命令了。
[root@pdai ~]# docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash
root@79eca07938db:/# ll | grep .bash_history
-rw------- 1 root root 19549 Feb 19 10:28 .bash_history
root@79eca07938db:/# exit
exit
注意:
如果直接挂载一个文件,很多文件编辑工具,包括
vi
或者sed --in-place
,可能会造成文件 inode 的改变,从 Docker 1.1 .0起,这会导致报错误信息。所以最简单的办法就直接挂载文件的父目录。
二、数据卷容器
数据卷容器,其实就是一个正常的容器,专门用来提供数据卷供其它容器挂载的。-volumes-from 可以多次使用来 mount 多个conatainer里的多个volumes。
[root@pdai ~]# docker run -d -v /dbdata --name dbdata training/postgres
70966085a85b05dd741a44a96725e2e44f146cc404b1b4e3aa3e519cd546c6b4
[root@pdai ~]# docker run -d --volumes-from dbdata --name db1 training/postgres
4c92240096d919724b233e1a5cfca94b5ceb0505e43262a7121cb83cfd8542f6
[root@pdai ~]# docker run -d --volumes-from dbdata --name db2 training/postgres
25246ebfae2f8437316b10d7eac3b34c1bd1522f50ba81651aec198bc79415a2
[root@pdai ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
70966085a85b training/postgres "su postgres -c '/us…" 46 seconds ago Up 45 seconds 5432/tcp dbdata
25246ebfae2f training/postgres "su postgres -c '/us…" About a minute ago Up About a minute 5432/tcp db2
4c92240096d9 training/postgres "su postgres -c '/us…" 2 minutes ago Up 2 minutes 5432/tcp db1
在db1 中通过 --volumes-from mount进来的 volume可以继续被其他container使用
[root@pdai ~]# docker run -d --name db3 --volumes-from db1 training/postgres
44d0719377e86e3080b26d22adcb6055de93033dc9509ca2ecd8be2c93dc33b5
[root@pdai ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
44d0719377e8 training/postgres "su postgres -c '/us…" 3 seconds ago Up 2 seconds 5432/tcp db3
70966085a85b training/postgres "su postgres -c '/us…" 3 minutes ago Up 3 minutes 5432/tcp dbdata
25246ebfae2f training/postgres "su postgres -c '/us…" 4 minutes ago Up 4 minutes 5432/tcp db2
4c92240096d9 training/postgres "su postgres -c '/us…" 4 minutes ago Up 4 minutes 5432/tcp db1
使用 --volumes-from 参数所挂载数据卷的容器自己并不需要保持在运行状态。
如果删除了挂载的容器(包括 dbdata、db1 和 db2),数据卷并不会被自动删除。如果要删除一个数据卷,必须在删除最后一个还挂载着它的容器时使用 docker rm -v
命令来指定同时删除关联的容器。 这可以让用户在容器之间升级和移动数据卷。
三、数据备份、恢复、迁移数据卷
1、数据备份、恢复、迁移数据卷
可以利用数据卷对其中的数据进行进行备份、恢复和迁移。
**1)备份。首先使用 --volumes-from 标记来创建一个加载 dbdata 容器卷的容器,并从本地主机挂载当前到容器的 /backup 目录。容器启动后,使用了 tar 命令来将 dbdata 卷备份为本地的 /backup/backup.tar。**命令如下:
[root@pdai ~]# docker run --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
tar: Removing leading `/' from member names
/dbdata/
[root@pdai ~]# ll | grep backup.tar
-rw-r--r-- 1 root root 10240 Feb 20 12:39 backup.tar
[root@pdai ~]#
2-1)如果要恢复数据到一个容器,首先创建一个带有数据卷的容器 dbdata2
[root@pdai ~]# docker run -v /dbdata --name dbdata2 ubuntu /bin/bash
2-2)然后创建另一个容器,挂载 dbdata2 的容器,并使用 untar 解压备份文件到挂载的容器卷中。
[root@pdai ~]# docker run --volumes-from dbdata2 -v $(pwd):/backup ubuntu tar xvf /backup/backup.tar
dbdata/
DockerFile
DockerFile是用来构建Docker镜像的文本文件,其中包括了一条条构建镜像所需要的指令和参数。
DockerFile基础
1、每条保留字(至于保留字是什么后面会提)必须为大写字母且后面要跟随至少一个参数
2、指令按照从从上到下,依次执行
3、#表示注释
4、每条指令都会创建一个新的镜像层,并对镜像进行提交
Docker执行DockerFile的流程
1、docker以基础镜像运行一个容器 (有了类,才能new一个对象)
2、执行一条指令并对容器做出修改
3、执行类似于Docker commit的操作提交一个新的镜像层
4、Docker再基于刚刚提交的镜像运行一个新的容器
5、执行DockerFile中的下一条指令,直到所有指令执行完成
小结
从应用软件的角度来看,DockerFile、Docker镜像与Docker容器分别代表软件的三个不同阶段,可以看做是它的生命周期。DockerFile是软件的原材料;Docker镜像是软件的交付品;Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例。
DockerFile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。
DockerFile。需要定义一个DockerFile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;
Docker镜像。在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时会真正开始提供服务;
Docker容器,容器是直接提供服务的。