Docker网络和存储原理
文章目录
docker装的和宿主机的优缺点:
优点:docker的移植性、便捷性高于在宿主机部署、进程隔离、很方便的资源限制
缺点:docker虚拟化技术,损失不到3%的性能。
镜像:容器;
镜像(Image):固定不变的。一个镜像可以启动很多容器
容器(Container):文件系统可能logs经常变化的,一个镜像可以启动很多容器。
docker在底层使用自己的存储驱动。来组件文件内容 storage drivers。docker 基于 AUFS(联合文件系统);
1. Docker存储
1. 镜像如何存储
运行docker history可以查看Nginx镜像中image标签下的每一条指令,如图中所示,每执行一条语句创建一层,最终呈现的镜像是由多层组合起来形成的。
Nginx这个镜像具体是怎么存储的可以使用docker image inspect nginx来查看
# 用户文件
/data/docker/overlay2//data/docker/overlay2/f74a29c43966ccd7fef6b7cd1658c7ff7eb01b7f687b10601eef31e971f00bbd/diff:/data/docker/overlay2/4ec3585005357ac67d49000367ba4c7f18f5ef6f7572d406c0369d997a413d9b/diff",;
# nginx的启动命令放在这里
/data/docker/overlay2/32fdb193c11d783745d833f50059411e403c9244690b5ff75b7b60e0ee499717/diff:
# nginx的配置文件在这里
/data/docker/overlay2/2b6db4bd459ac431a89958cb17b2ef487472ee1b384dae522b4a0d4dc3d05de3/diff:
# 小linux系统
/data/docker/overlay2/8c4cc1171ecc63fefb9059f9ebc901a604468f87f4c99cbf0acf702e0aee43c1/diff:
- LowerDir :底层目录; diw (只是存储不同);包含小型linux和装好的软件
容器会自己建立层;如果想要改东西,把改的内容复制到容器层即可 docker inspect container
"LowerDir": "/data/docker/overlay2/f74a29c43966ccd7fef6b7cd1658c7ff7eb01b7f687b10601eef31e971f00bbd/diff:/data/docker/overlay2/32fdb193c11d783745d833f50059411e403c9244690b5ff75b7b60e0ee499717/diff:/data/docker/overlay2/2b6db4bd459ac431a89958cb17b2ef487472ee1b384dae522b4a0d4dc3d05de3/diff:/data/docker/overlay2/8c4cc1171ecc63fefb9059f9ebc901a604468f87f4c99cbf0acf702e0aee43c1/diff:/data/docker/overlay2/4ec3585005357ac67d49000367ba4c7f18f5ef6f7572d406c0369d997a413d9b/diff",
"MergedDir": "/data/docker/overlay2/8d55aa1f861d61e23a3ddc7ad8bb63d11d1b88e68dc3780c9890bda0acfe27c5/merged",
"UpperDir": (镜像的上层可以感知变 化)"/data/docker/overlay2/8d55aa1f861d61e23a3ddc7ad8bb63d11d1b88e68dc3780c9890bda0acfe27c5/diff",【容器的修改后的文件,保存再宿主机哪里呀。 容器删除后,那些容器目录还存在吗?一定不再】
"WorkDir": "/data/docker/overlay2/8d55aa1f861d61e23a3ddc7ad8bb63d11d1b88e68dc3780c9890bda0acfe27c5/work"
- MergedDir :合并目录;容器最终的完整工作目录全内容都在合并层;数据卷在容器层产生;所有的增删改都在容器层;
-
UpperDir :上层目录;
-
WorkDir :工作目录(临时层),pid;
# 注
LowerDir(底层)\UpperDir(上层)\MergedDir\WorkDir(临时东西)
docker底层的 storage driver完成了以上的目录组织结果;
1. Images and layers
Docker映像由一系列层组成。 每层代表图像的Dockerfile中的一条指令。 除最后一层外的每一层都是只读的。 如以下Dockerfile:
Dockerfile文件里面几句话,镜像就有几层
FROM ubuntu:15.04
COPY . /app
RUN make /app
CMD python /app/app.py
# 每一个指令都可能会引起镜像改变,这些改变类似git的方式逐层叠加。
1. 该Dockerfile包含四个命令,每个命令创建一个层。
2. FROM语句从ubuntu:15.04映像创建一个图层开始。
3. COPY命令从Docker客户端的当前目录添加一些文件。
4. RUN命令使用make命令构建您的应用程序。
5. 最后,最后一层指定要在容器中运行的命令。
6. 每一层只是与上一层不同的一组。 这些层彼此堆叠。
7. 创建新容器时,可以在基础层之上添加一个新的可写层。 该层通常称为“容器层”。 对运行中的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入此薄可写容器层。
2. Container and layers
1. 容器和镜像之间的主要区别是可写顶层。
2. 在容器中添加新数据或修改现有数据的所有写操作都存储在此可写层中。
3. 删除容器后,可写层也会被删除。 基础图像保持不变。 因为每个容器都有其自己的可写容器层,并且所有更改都存储在该容器层中,所以多个容器可以共享对同一基础映像的访问,但具有自己的数据状态。
3. 磁盘容量预估
docker ps -s
size:用于每个容器的可写层的数据量(在磁盘上)。
virtual size:容器使用的用于只读图像数据的数据量加上容器的可写图层大小。
多个容器可以共享部分或全部只读图像数据。
从同一图像开始的两个容器共享100%的只读数据,而具有不同图像的两个容器(具有相同的层)共享这些公共层。
因此,不能只对虚拟大小进行总计。这高估了总磁盘使用量,可能是一笔不小的数目。
4. 镜像如何挑选
busybox:是一个集成了一百多个最常用Linux命令和工具的软件。linux工具里的瑞士军刀
alpine:Alpine操作系统是一个面向安全的轻型Linux发行版经典最小镜像,基于busybox,功能比 Busybox完善。
slim:docker hub中有些镜像有slim标识,都是瘦身了的镜像。也要优先选择
无论是制作镜像还是下载镜像,优先选择alpine类型
5. Copy On Write
-
写时复制是一种共享和复制文件的策略,可最大程度地提高效率。
-
如果文件或目录位于映像的较低层中,而另一层(包括可写层)需要对其进行读取访问,则它仅使用现有文件。
-
另一层第一次需要修改文件时(在构建映像或运行容器时),将文件复制到该层并进行修改。 这样可以将I / O和每个后续层的大小最化。
2. 容器如何挂载
每一个容器里面的内容,支持三种挂载方式:
1)docker自动在外部创建文件夹自动挂载容器内部指定的文件夹内容【Dockerfile VOLUME指令的作用】
2)自己在外部创建文件夹,手动挂载
3)可以把数据挂载到内存中。
--mount 挂载到 linux宿主机,手动挂载(不用了)
-v 可以自动挂载,到linux主机或者docker自动管理的这一部分区域
-
Volumes(卷) :存储在主机文件系统的一部分中,该文件系统由Docker管理(在Linux上是“ / var / lib / docker / volumes /”)。 非Docker进程不应修改文件系统的这一部分。 卷是在Docker中持久存储数据的最佳方法。
-
Bind mounts(绑定挂载) 可以在任何地方 存储在主机系统上。 它们甚至可能是重要的系统文件或目录。 Docker主机或Docker容器上的非Docker进程可以随时对其进行修改。
-
tmpfs mounts(临时挂载) 仅存储在主机系统的内存中,并且永远不会写入主机系统的文件系统
1. volume(卷)
- 匿名卷使用
docker run -dP -v :/etc/nginx nginx
# docker将创建出匿名卷,并保存容器/etc/nginx下面的内容
# -v 宿主机:容器里的目录
# -P 随机端口号
- 具名卷使用
docker run -dP -v nginx:/etc/nginx nginx
#docker将创建出名为nginx的卷,并保存容器/etc/nginx下面的内容
# 注
-v 宿主机绝对路径:Docker容器内部绝对路径:叫挂载;这个有空挂载问题
-v 不以/开头的路径:Docker容器内部绝对路径:叫绑定(docker会自动管理,docker不会把他当前目录,而把它当前卷)
如果自己开发测试,用 -v 绝对路径的方式
如果是生产环境建议用卷
除非特殊 /bin/docker 需要挂载主机路径的则操作 绝对路径挂载
nginx测试html挂载几种不同情况:
-
不挂载 效果:访问默认欢迎页
-
-v /root/html:/usr/share/nginx/html 效果:访问forbidden
-
-v html:/usr/share/nginx/html:ro 效果:访问默认欢迎页
-
-v /usr/share/nginx/html 效果:匿名卷 (什么都不写也不要加冒号,直接写容器内的目录)
-
原因:
- -v html:/usr/share/nginx/html; docker inspect 容器的时候; docker自动管理的方式
# -v不以绝对路径方式;
### 1、先在docker底层创建一个你指定名字的卷(具名卷) html
### 2、把这个卷和容器内部目录绑定
### 3、容器启动以后,目录里面的内容就在卷里面存着;
##### -v nginxhtml:/usr/share/nginx/html 也可以以下操作
## 1、 docker create volume nginxhtml 如果给卷里面就行修改,容器内部的也就改了。
## 2、 docker volume inspect nginxhtml
## 3、docker run -d -P -v nginxhtml:/usr/share/nginx/html --name=nginx777 nginx
# 可以看到
"Mounts": [
{ "Type": "volume", //这是个卷
"Name": "html", //名字是html
"Source": "/var/lib/docker/volumes/html/_data", //宿主机的目录。容器里面的哪两个文件都在 "Destination": "/usr/share/nginx/html", //容器内部
"Driver": "local",
"Mode": "z",
"RW": true, //读写模式
"Propagation": ""
}
]
# 卷:就是为了保存数据 docker volume
# 可以对docker自己管理的卷目录进行操作; /var/lib/docker/volumes(卷的根目录)
2. bind mount
如果将绑定安装或非空卷安装到存在某些文件或目录的容器中的目录中,则这些文件或目录会被安装遮盖,就像您将文件保存到Linux主机上的/ mnt中一样,然后 将USB驱动器安装到/ mnt中。在卸载USB驱动器之前,/ mnt的内容将被USB驱动器的内容遮盖。 被遮盖的文件不会被删除或更改,但是在安装绑定安装或卷时将无法访问。
总结:外部目录覆盖内部容器目录内容,但不是修改。所以谨慎,外部空文件夹挂载方式也会导致容器内部是空文件夹
docker run -dP -v /my/nginx:/etc/nginx:ro nginx
# bind mount和 volumes 的方式写法区别在于
# 所有以/开始的都认为是 bind mount ,不以/开始的都认为是 volumes.
警惕bind mount 方式,文件挂载没有在外部准备好内容而导致的容器启动失败问题
# 一行命令启动nginx,并且配置文件和html页面。需要知道卷的位置才能改
docker run -d -P -v nginxconf:/etc/nginx/ -v nginxpage:/usr/share/nginx/html nginx
# 想要实现 docker run -d -P -v /root/nginxconf:/etc/nginx/ -v /root/nginxhtml:/usr/share/nginx/html --name=nginx999 nginx
### 1、提前准备好东西 目录nginxconf,目录里面的配置we年都放里面,再调用命令
### 2、docker cp nginxdemo:/etc/nginx /root/nginxconf #注意/的使用
### 3、docker run -d -P -v /root/nginxconf:/etc/nginx/ -v /root/nginxhtml:/usr/share/nginx/html --name=nginx999 nginx
3. 管理卷
docker volume create xxx:创建卷名
docker volume inspect xxx:查询卷详情
docker volume ls: 列出所有卷
docker volume prune: 移除无用卷
4. docker cp
cp的细节
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|- :把容器里面的复制出来
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH:把外部的复制进去
-
SRC_PATH 指定为一个文件
-
DEST_PATH 不存在:文件名为 DEST_PATH ,内容为SRC的内容
-
DEST_PATH 不存在并且以 / 结尾:报错
-
DEST_PATH 存在并且是文件:目标文件内容被替换为SRC_PATH的文件内容。
-
DEST_PATH 存在并且是目录:文件复制到目录内,文件名为SRC_PATH指定的名字
-
-
SRC_PATH 指定为一个目录
-
DEST_PATH 不存在: DEST_PATH 创建文件夹,复制源文件夹内的所有内容
-
DEST_PATH 存在是文件:报错
-
DEST_PATH 存在是目录
-
-
SRC_PATH 不以 /. 结束:源文件夹复制到目标里面
-
SRC_PATH 以 /. 结束:源文件夹里面的内容复制到目标里面
自动创建文件夹不会做递归。把父文件夹做好
[root@docker ~]# docker cp index.html mynginx4:/usr/share/nginx/html
[root@docker ~]# docker cp mynginx4:/etc/nginx/nginx.conf nginx.conf
2. Docker网络
1. 端口映射
docker create -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name hello-mysql mysql:5.7
2. 容器互联
–link name:alias ,name连接容器的名称,alias连接的别名
场景:我们无需暴露mysql的情况下,让web应用使用mysql;
docker run -d -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
docker run -d --link mysql01:mysql --name tomcat tomcat:7
docker exec -it tomcat bash
cat /etc/hosts
ping mysql
容器互联
容器互联linking是除了端口映射外另一种可以与容器中应用进行交互的方式,它会在源和接收容器之间创建隧道,接收容器可以看到源容器的信息
导入mysql镜像
[root@docker ~]# docker load < /docker/docker-images/mysql5-7-1.tar
源容器mysql
[root@docker ~]# docker run -d --name mysqldb -e MYSQL_ROOT_PASSWORD=123456 mysql
5b59dcbdd017e1b13e88d033216471fb73af5d1e1895c59a3d150bd4bd23d2c8
[root@docker ~]# docker exec mysqldb ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
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
36: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:13 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.19/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:13/64 scope link
valid_lft forever preferred_lft forever
[root@docker ~]# docker exec -it mysqldb env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=5b59dcbdd017
MYSQL_ROOT_PASSWORD=123456
MYSQL_MAJOR=5.7
MYSQL_VERSION=5.7.9-1debian8
HOME=/root
[root@docker ~]# mysql -h 172.17.0.19 -uroot -p123456
接收容器 nginx
[root@docker ~]# docker run -d -p 80:80 -p 443:443 --name web2 --link mysqldb:mysql nginx
0f0c72938b557720cf913a952e40b01f4f36b029bb2589159620fefcf02618af
[root@docker ~]# docker run -it -d --name web1 --link mysql:mysql centos /bin/bash
7d85e176917436d0b2885c738136a925f90922d001032b056a3c27fb59c7ad3b
[root@docker ~]#
[root@docker ~]# docker exec -it web2 /bin/bash
root@0f0c72938b55:/# env
HOSTNAME=0f0c72938b55
MYSQL_ENV_MYSQL_ROOT_PASSWORD=123456
MYSQL_PORT_3306_TCP_PORT=3306
MYSQL_PORT_3306_TCP=tcp://172.17.0.19:3306
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
NGINX_VERSION=1.9.7-1~jessie
MYSQL_ENV_MYSQL_VERSION=5.7.9-1debian8
SHLVL=1
HOME=/root
MYSQL_NAME=/web2/mysql
MYSQL_PORT_3306_TCP_PROTO=tcp
MYSQL_PORT_3306_TCP_ADDR=172.17.0.19
MYSQL_ENV_MYSQL_MAJOR=5.7
MYSQL_PORT=tcp://172.17.0.19:3306
_=/usr/bin/env
root@0f0c72938b55:/#
root@0f0c72938b55:/# cat /etc/hosts
172.17.0.20 0f0c72938b55
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.19 mysql 5b59dcbdd017 mysqldb
解决的就是容器ip地址会发生变化,导致不能访问,容器互联可以通过环境变量访问容器地址,环境变量会随着ip地址的变化而更改
实验:
[root@docker101 ~]# docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=robin123 --restart=always mysql:5.7
--restart=alway docker重启后,容器自动启动
[root@docker101 ~]# docker run --name nginx1 -d -v /webroot:/usr/share/nginx/html --restart=always -P nginx
[root@docker101 ~]# docker run -it -d -p 80:80 --name web2 --link mysql:mysql --restart=always centos7.6 /bin/bash
通过查询
mysql 172.17.0.2
nginx1 172.17.0.3
web2 172.17.0.4
当docker发生意外重启,后 容器的ip可能更启动顺序的问题,导致ip地址发生变更,如果在用ip地址访问mysql就访问不到了
容器互联即可解决该问题
3. 自定义网络
1. 默认网络原理
Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
Docker容器网络就很好的利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
Docker中的网络接口默认都是虚拟的接口。虚拟接口的优势就是转发效率极高(因为Linux是在内核中进行数据的复制来实现虚拟接口之间的数据转发,无需通过外部的网络设备交换),对于本地系统和容器系统来说,虚拟接口跟一个正常的以太网卡相比并没有区别,只是他的速度快很多。
原理:
1、每一个安装了Docker的linux主机都有一个docker0的虚拟网卡。桥接网卡
2、每启动一个容器linux主机多了一个虚拟网卡。
3、docker run -d -P --name tomcat --net bridge tomcat:8
2. 网络模式
网络模式 | 配置 | 说明 |
---|---|---|
bridge模式 | –net=bridge | 默认值,在Docker网桥docker0上为容器创建新的网络栈 |
none模式 | –net=none | 不配置网络,用户可以稍后进入容器,自行配置 |
container模式 | ----net=container:name/id | 容器和另外一个容器共享Network namespace。kubernetes中的pod就是多个容器共享一个Network namespace。 |
host模式 | –net=host | 容器和宿主机共享Network namespace; |
用户自定义 | –net=mynet | 用户自己使用network相关命令定义网络,创建容器的时候可以指定为自己定义的网络 |
1 host模式
众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
例如,我们在10.10.101.105/24的机器上用host模式启动一个含有web应用的Docker容器,监听tcp80端口。当我们在容器中执行任何类似ifconfig命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用10.10.101.105:80即可,不用任何NAT转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
进入创建的web1中,使用ip addr s命令发现可以使用,不仅可以看见宿主机的网络地址,还可以看见创建的web的地址
[root@docker ~]# docker exec -it web1 /bin/bash
在启用的web1下安装Apache服务,并在宿主机进行测试
由此可以发现,在宿主机上并没有暗转Apache服务,访问本机ip地址却可以访问web1上的Apache服务。由此可以说明,宿主机网络与容器网络共享。
注意:只是网络共享其他并不共享,比如说宿主机上有ifconfig命令而容器内没有ifconfig命令。
2 container模式
在理解了host模式后,这个模式也就好理解了。这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
[root@docker ~]# docker run -it -d --name web2 centos:latest /bin/bash
b9ea63cf1b2a56670fdfbcabf07a7886c17e0ae2cda767939e97c49738ed1aa4
[root@docker ~]# docker run -it -d --name web3 --net=container:web2 centos:latest /bin/bash
96b19dd1df0ce70d3716b6d7ed9179fb80ced69e09a7f3b28a9ddcc3e785ae2b
[root@docker ~]#
在宿主机上查看ip如图所示
分别登web2,web3中查看
web2
web3
注意:此种模式只是共享两台容器的网络,新创建的容器不会创建自己的网卡,不会配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
测试:
在web2上安装httpd,然后启动,使用宿主机和web3分别访问
使用web3访问会发现,web3的80端口也是处于放的状态
所以从这里访问自己的ip可以看见写入网页的内容
而宿主机不用说也是可以访问的
此时为了对比,刚才web2上安装的apache,所以在web3上安装mysql用来测试
3 none模式
这个模式和前两个不同。在这种模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
[root@docker ~]# docker run -it -d --name web4 --net=none centos:latest /bin/bash
76ee6e00c15411a19bc5e86f2a557f0577c94bad594a51ac2d2eeeb50e9afa6d
4 bridge模式
bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上。下面着重介绍一下此模式。
4.1 bridge模式的拓扑
当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配IP了,Docker会从RFC1918所定义的私有IP网段中,选择一个和宿主机不同的IP地址和子网分配给docker0,连接到docker0的容器就从这个子网中选择一个未占用的IP使用。如一般Docker会使用172.17.0.0/16这个网段,并将172.17.0.1/16分配给docker0网桥(在主机上使用ifconfig命令是可以看到docker0的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)。单机环境下的网络拓扑如下,主机地址为10.10.101.105/24。
Docker完成以上网络配置的过程大致是这样的:
-
在主机上创建一对虚拟网卡veth pair设备。veth设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth设备常用来连接两个网络设备。
-
Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0。另一端放在主机中,以veth65f9这样类似的名字命名,并将这个网络设备加入到docker0网桥中,可以通过brctl show命令查看。
- 从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。网络拓扑介绍完后,接着介绍一下bridge模式下容器是如何通信的。
bridge模式下容器的通信
在bridge模式下,连在同一网桥上的容器可以相互通信(若出于安全考虑,也可以禁止它们之间通信,方法是在DOCKER_OPTS变量中设置–icc=false,这样只有使用–link才能使两个容器通信)。
容器也可以与外部通信,我们看一下主机上的Iptable规则,可以看到这么一条
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
这条规则会将源地址为172.17.0.0/16的包(也就是从Docker容器产生的包),并且不是从docker0网卡发出的,进行源地址转换,转换成主机网卡的地址。这么说可能不太好理解,举一个例子说明一下。假设主机有一块网卡为eth0,IP地址为10.10.101.105/24,网关为10.10.101.254。从主机上一个IP为172.17.0.1/16的容器中ping百度(180.76.3.151)。IP包首先从容器发往自己的默认网关docker0,包到达docker0后,也就到达了主机上。然后会查询主机的路由表,发现包应该从主机的eth0发往主机的网关10.10.105.254/24。接着包会转发给eth0,并从eth0发出去(主机的ip_forward转发应该已经打开)。这时候,上面的Iptable规则就会起作用,对包做SNAT转换,将源地址换为eth0的地址。这样,在外界看来,这个包就是从10.10.101.105上发出来的,Docker容器对外是不可见的。
那么,外面的机器是如何访问Docker容器的服务呢?我们首先用下面命令创建一个含有web应用的容器,将容器的80端口映射到主机的80端口。
docker run -d --name web -p 80:80 fmzhen/simpleweb
然后查看Iptable规则的变化,发现多了这样一条规则:
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.5:80
此条规则就是对主机eth0收到的目的端口为80的tcp流量进行DNAT转换,将流量发往172.17.0.5:80,也就是我们上面创建的Docker容器。所以,外界只需访问10.10.101.105:80就可以访问到容器中得服务。
除此之外,我们还可以自定义Docker使用的IP地址、DNS等信息,甚至使用自己定义的网桥,但是其工作方式还是一样的。
3. 自建网络测试
#1、docker0网络的特点
默认、域名访问不通、--link 域名通了,但是删了又不行
#2、可以让容器创建的时候使用自定义网络,用自定义
1、自定义创建的默认default "bridge"
2、自定义创建一个网络网络 docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
所有东西实时维护好,直接域名ping通
docker network connect [OPTIONS] NETWORK CONTAINER
#3、跨网络连接别人就用。把tomcat加入到mynet网络
docker network connect mynet tomcat
效果:
1、自定义网络,默认都可以用主机名访问通
2、跨网络连接别人就用 docker network connect mynet tomcat
#4、命令
1、容器启动,指定容器ip。 docker run --ip 192.168.0.3 --net 自定义网络
2、创建子网。docker network create --subnet 指定子网范围 --driver bridge 所有东西实时 维护好,直接域名ping通
3、docker compose 中的网络默认就是自定义网络方式。 docker run -d -P --network 自定义网络名(提前创建)
步骤1: 创建自定义网络
docker network create --subnet=172.18.0.0/16 mynetwork
查询当前网络
docker network ls
步骤2: 创建Docker容器
docker run --name web2 --privileged -d --net mynetwork --ip 172.18.0.10 centos7.5 /sbin/init
docker run --name web2 -it --net mynetwork --ip 172.18.0.10 centos7.5 /bin/bash
docker run --name web2 -d --net mynetwork --ip 172.18.0.10 centos7.5
docker exec -it web2 /bin/bash
脚本:
[root@docker101 ~]# cat set-none-ip.sh
#!/bin/bash
read -p "输入设置ip的container名字: " name
read -p "输入设置ip的container地址: " ip
cid=`docker ps -f name=test3 -q | sed -r 's/(.*)..$/\1/'`
pid=`docker inspect -f "{{.State.Pid}}" $name`
mkdir /var/run/netns &>/dev/null
ln -s /proc/$pid/ns/net /var/run/netns/$pid
ip link add veth$cid type veth peer name X
brctl addif docker0 veth$cid
ip link set veth$cid up
ip link set X netns $pid
ip netns exec $pid ip link set dev X name eth0
ip netns exec $pid ip link set dev eth0 up
ip netns exec $pid ip addr add $ip/16 dev eth0
ip netns exec $pid ip route add default via 172.17.0.1
[root@docker101 ~]#