5 容器数据卷
5.1 什么是容器数据卷
docker 的理念回顾
将应用和运行的环境打包形成容器运行,运行可以伴随着容器,但是我们对于数据的要求,是希望能够持久化的!就好比,你安装一个MySQL,结果你把容器删了,就相当于删库跑路了,这TM也太扯了吧!
所以我们希望容器之间有可能可以共享数据,Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据作为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了!这样是行不通的!
为了能保存数据在Docker中我们就可以使用卷!让数据挂载到我们本地!这样数据就不会因为容器删除而丢失了!
数据?如果数据都在容器中,那么容器删除,数据就会丢失!需求:数据可以持久化
MySQL,容器删除了,数据则删除,删库跑路。需求:MySQL的数据存放到本地!
容器之间可以有一个数据共享的技术!Docker 容器中产生的数据,同步到本地!
这就是数据卷技术!目录的挂载,将我们容器内的目录,挂载到Linux系统指定的目录下。
作用:
卷就是目录或者文件,存在一个或者多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System,提供一些用于持续存储或共享数据的特性:
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。
特点:
1、数据卷可在容器之间共享或重用数据
2、卷中的更改可以直接生效
3、数据卷中的更改不会包含在镜像的更新中
4、数据卷的生命周期一直持续到没有容器使用它为止
总结:容器的数据持久化和同步操作!容器间也是可以数据共享的!
5.2 使用数据卷
方式一:直接使用命令挂载 -v 参数
docker run -it -v 主机目录:容器内的目录
范例:
$ docker run -it -d \
-v /data/ubuntu01:/home \
--privileged=true \
--name ubuntu01 ubuntu:20.04 /bin/bash
#查看容器的详细信息显示
#通过docker inpsect 容器ID 查看
#值得注意的是,主机目录会直接覆盖掉容器中要挂载的目录
$ docker inspect ubuntu01 | grep -A 9 "Mounts"
"Mounts": [
{
"Type": "bind",
"Source": "/data/ubuntu01", #主机内的目录路径
"Destination": "/home", #Docker容器内的目录路径
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
$ docker exec -it ubuntu01 /bin/bash
root@9afa7b8ee434:/# cd /home/
root@9afa7b8ee434:/home# mkdir host ; ls
host
root@9afa7b8ee434:/home# echo "Hello World!" > /home/docker.txt
root@9afa7b8ee434:/home# exit
$ ls /data/ubuntu01/
docker.txt host
#再次测试
#删除原有的ubuntu01容器,启动ubuntu02容器进行挂载该目录
$ docker rm -f ubuntu01
$ docker run -it -d \
--name ubuntu02 \
--privileged=true \
-v /data/ubuntu01:/home \
ubuntu:20.04 /bin/bash
$ docker exec -it ubuntu02 /bin/bash
root@2ac37ab7725a:/# ls /home/
docker.txt host
root@2ac37ab7725a:/# cat /home/docker.txt
Hello World!
数据卷好处:我们以后修改只需要修改本地修改即可,容器内会自动同步。类似共享文件是占用一份的存储。
5.3 实战:安装MySQL
思考:MySQL 的数据持久化的问题。
#获取并下载MySQL镜像
$ docker pull mysql:5.7.36
#官方测试 $ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql-root -d mysql:tag
#运行mysql容器,需要做数据挂载
#安装启动mysql,需要配置密码,注意事项
#-d 后台运行
#-p 端口映射
#-v 数据卷挂载
#-e 环境变量设置
#--name 容器名字
$ docker run -it -d \
--name mysql01 \
-p 3316:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v /data/docker/mysql01/conf:/etc/mysql/conf.d \
-v /data/docker/mysql01/data:/var/lib/mysql \
mysql:5.7.36
#如果在挂载时,外部是新建目录或空目录会将容器中映射目录也变成空目录
$ cat > /data/docker/mysql01/conf/my.cnf <<EOF
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
EOF
$ ls /data/docker/mysql01/conf/
my.cnf
$ ls /data/docker/mysql01/data/
auto.cnf client-cert.pem ib_buffer_pool ib_logfile1 performance_schema server-cert.pem
ca-key.pem client-key.pem ibdata1 ibtmp1 private_key.pem server-key.pem
ca.pem ib_logfile0 mysql public_key.pem sys
#默认 -v 是先主机挂载覆盖了,然后 MySQL 程序启动才自动创建的数据,会直接生成/var/lib/mysql/目录下的数据库数据
$ docker restart mysql01
mysql01
#启动成功之后,在本地使用Navicat等数据库连接工具进行连接即可。
#Navicat 连接到服务器 3316 --> 3316 端口是和容器的内部的 3306 映射,这个时候就可以连接上了
#在本地创建一个测试数据库dbtest,可以在挂载目录下查看到相应的目录
$ ls -l /data/docker/mysql01/data/dbtest/
total 4
-rw-r----- 1 polkitd input 67 Aug 25 17:10 db.opt
假设我们将容器删除了。
发现,我们挂载到本地的数据卷依旧没有丢失,这就实现了容器的数据持久化的功能!
5.4 具名挂载和匿名挂载
#匿名挂载
-v 容器内路径
$ docker run -it -d -P --name nginx01 -v /etc/nginx nginx
#查看所有 volume 的情况
$ docker volume ls
DRIVER VOLUME NAME
local b5c1c38d5935b1e1adc829c3eb28af15188368169745afe75f230858969b6df9
#这里发现,这种就是匿名挂载,我们在 -v 只写了容器内的路径,没有写容器外的路径
$ docker inspect nginx01 | grep -A 9 "Mounts"
"Mounts": [
{
"Type": "volume",
"Name": "b5c1c38d5935b1e1adc829c3eb28af15188368169745afe75f230858969b6df9",
"Source": "/var/lib/docker/volumes/b5c1c38d5935b1e1adc829c3eb28af15188368169745afe75f230858969b6df9/_data",
"Destination": "/etc/nginx",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
$ cd /var/lib/docker/volumes/b5c1c38d5935b1e1adc829c3eb28af15188368169745afe75f230858969b6df9/_data
$ ls
conf.d fastcgi_params mime.types modules nginx.conf scgi_params uwsgi_params
#具名挂载
$ docker run -it -d -P --name nginx02 -v nginx-volume:/etc/nginx nginx
62c42ad0e4ca2374ee30af5b18b48a40e197d41126225446716c241639c3dbf3
#通过 -v 卷名:容器内路径
#查看所有 volume 的情况
$ docker volume ls
DRIVER VOLUME NAME
local nginx-volume
#查看一下这个具名卷
$ docker volume inspect nginx-volume
[
{
"CreatedAt": "2022-08-25T20:41:56+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/nginx-volume/_data",
"Name": "nginx-volume",
"Options": null,
"Scope": "local"
}
]
所有的 Docker 容器内的卷,没有指定目录的情况下都是在 “/var/lib/docker/volumes/xxx/_data”
我们通过具名挂载可以方便的让我们找到对应的数据卷名,大多数情况是使用"具名挂载"
思考:如何请是具名挂载还是匿名挂载,还是指定路径挂载!
- -v 容器内路径 #匿名挂载
- -v 卷名:容器内路径 #具名挂载
- -v /宿主机路径:容器内路径 #指定路径挂载
拓展:
#通过 -v 容器内路径:ro rw 改变读写权限
ro readonly #只读
rw readwrite #可读可写
#一旦这个设置了容器,容器对我们挂载出来的内容就有限定了!
$ docker run -it -d -P --name nginx03 -v nginx-volume:/etc/nginx:ro nginx
$ docker run -it -d -P --name nginx03 -v nginx-volume:/etc/nginx:rw nginx
#默认是rw,如果是 ro 就说明只能通过宿主机来操作,容器内部是无法操作的
5.5 初识 Dockerfile
Dockerfile就是用来构建docker镜像的构建文件!命令脚本!先体验一下!
通过这个脚本可以生成镜像,镜像是一层一层的,对应的就是脚本一个个的。每一个命令都是一层!
$ mkdir -pv /root/docker/docker-volume ; cd /root/docker/docker-volume
#创建一个Dockerfile文件,名字可以随机 建议使用Dockerfile
#文件中的内容指令(大写) 参数
$ vim Dockerfile
FROM ubuntu:20.04
#匿名挂载
VOLUME ["/volume01", "/volume02"]
RUN echo "-----> Success ......"
CMD /bin/bash
#这里的每一个命令,就是镜像的一层
$ docker build -t kube-ubuntu:1.0 -f Dockerfile .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM ubuntu:20.04
---> ba6acccedd29
Step 2/4 : VOLUME ["/volume01", "/volume02"]
---> Running in f77e4a3d3e7f
Removing intermediate container f77e4a3d3e7f
---> 91315a9d10d0
Step 3/4 : RUN echo "-----> Success ......"
---> Running in fa440b3de704
-----> Success ......
Removing intermediate container fa440b3de704
---> ffe4ed184001
Step 4/4 : CMD /bin/bash
---> Running in f27e036a6862
Removing intermediate container f27e036a6862
---> 1f2a4e5e429d
Successfully built 1f2a4e5e429d
Successfully tagged kube-ubuntu:1.0
$ docker images kube-ubuntu:1.0
REPOSITORY TAG IMAGE ID CREATED SIZE
kube-ubuntu 1.0 1f2a4e5e429d About a minute ago 72.8MB
$ docker run -itd --name ku1 kube-ubuntu:1.0 /bin/bash
$ docker exec -it ku1 /bin/bash
#这个目录就是我们生成镜像的时候自动挂载的,数据卷目录
root@6a6138c411dd:/# echo "Hello volume01" > /volume01/container01.txt
root@6a6138c411dd:/# echo "Hello volume02" > /volume02/container02.txt
root@6a6138c411dd:/# ls -l /volume0*
/volume01:
total 4
-rw-r--r-- 1 root root 15 Aug 25 13:08 container01.txt
/volume02:
total 4
-rw-r--r-- 1 root root 15 Aug 25 13:08 container02.txt
root@6a6138c411dd:/# exit
exit
#这个数据卷和外部一定有一个同步的目录
$ docker inspect ku1 | grep -A 21 "Mounts"
"Mounts": [
{
"Type": "volume",
"Name": "604c9c1d68d41958287107effbd1dcfbd0bbd875c9cb7dd8b35982f446e35f68",
"Source": "/var/lib/docker/volumes/604c9c1d68d41958287107effbd1dcfbd0bbd875c9cb7dd8b35982f446e35f68/_data",
"Destination": "/volume01",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "f6262a1ec8581c5b6a6b78e3beacd4881b435a5d331f5c33d88e5c9e0b8bfb89",
"Source": "/var/lib/docker/volumes/f6262a1ec8581c5b6a6b78e3beacd4881b435a5d331f5c33d88e5c9e0b8bfb89/_data",
"Destination": "/volume02",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
#测试一下,刚才的文件是否同步了!
#这种方式我们未来使用的十分多,因为我们通常会构建自己的镜像!
$ cat /var/lib/docker/volumes/604c9c1d68d41958287107effbd1dcfbd0bbd875c9cb7dd8b35982f446e35f68/_data/container01.txt
Hello volume01
$ cat /var/lib/docker/volumes/f6262a1ec8581c5b6a6b78e3beacd4881b435a5d331f5c33d88e5c9e0b8bfb89/_data/container02.txt
Hello volume02
假设构建镜像时候没有挂载卷,要手动镜像挂载 -v 卷名:容器内路径!
5.6 数据卷容器
#启动3个容器,通过我们刚才自己写的镜像启动
$ docker images kube-ubuntu:1.0
REPOSITORY TAG IMAGE ID CREATED SIZE
kube-ubuntu 1.0 317c07f88b30 30 minutes ago 72.8MB
#写/bin/bash是规定镜像启动时执行这条指令,centos镜像启动时默认会执行/bin/bash,因此可以不写
$ docker run -itd --name docker01 kube-ubuntu:1.0 /bin/bash
$ docker run -itd --name docker02 --volumes-from docker01 kube-ubuntu:1.0 /bin/bash
#--volumes-from 实际上多个容器挂载的是同一个数据卷
#--volumes-from 就可以实现容器间的数据共享了
#在docker01容器中创建测试文件,检验docker02是否也有其测试文件
#docker01创建测试文件
$ docker exec -it docker01 /bin/bash
root@51ae030c5f2a:/# echo "Hello docker02 volume01" > /volume01/test01.txt
root@51ae030c5f2a:/# echo "Hello docker02 volume02" > /volume02/test02.txt
#docker02查看测试文件
$ docker exec -it docker02 /bin/bash
root@9807aeded26e:/# cat /volume01/test01.txt
Hello docker02 volume01
root@9807aeded26e:/# cat /volume02/test02.txt
Hello docker02 volume02
#docker03继承docker02的数据卷信息,并创建测试文件
$ docker run -it --name docker03 --volumes-from docker02 kube-ubuntu:1.0 /bin/bash
root@bdc47c45e893:/# echo "Hello I am docker03" > /volume01/docker03.txt
root@bdc47c45e893:/# echo "Hello I am docker03 Here" > /volume02/docker03.txt
#docker01可以查看到docker03的测试文件
$ docker exec -it docker01 /bin/bash
root@51ae030c5f2a:/# cat /volume01/docker03.txt
Hello I am docker03
root@51ae030c5f2a:/# cat /volume02/docker03.txt
Hello I am docker03 Here
#测试:可以删除 docker01,查看一下docker02和docker03是否还可以访问这个文件
#测试依旧可以访问
$ docker rm -f docker01
#三个容器都是挂载到宿主机的同一个目录下的,如果删掉宿主机下的这个目录的文件,三个容器的数据都会消失
#可以发现三个容器的匿名数据卷挂载的路径是一样的。
已验证,多个 MySQL 实现数据共享失败。原因后续继承该MySQL 配置和数据的方式依旧是父MySQL,会导致后续的MySQL 启动失败。数据卷容器 只能使用在 无状态服务。注意两个mysql虽然共用一个数据,但是只能同时在线一个MySQL容器,否则另外一个连接不上。
结论:
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。
但是一旦数据持久化到本地,这个时候,本地的数据是不会删除的!
数据卷是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除数据卷。如果需要在删除容器的同时移除数据卷,可以在删除容器的时候使用 docker rm -v 这个参数。
6 Docker 网络
6.1 理解 docker0
清空所有的 Docker 环境。
$ ip addr show
#本地回环网卡
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
#本机的内网IP地址
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:16:73:35 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.101/24 brd 10.0.0.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fe16:7335/64 scope link
valid_lft forever preferred_lft forever
#docker服务生成的docker0网卡
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:fc:db:df:7e 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:fcff:fedb:df7e/64 scope link
valid_lft forever preferred_lft forever
三个网络:
问题:docker 是如何处理容器网络访问的?
$ docker run -d -P --name tomcat01 tomcat:8.5.81
#查看容器的内部网络地址 ip addr show
#发现容器启动的时候会得到一个 eth0@xxx ip地址,docker DHCP分配
$ docker exec -it tomcat01 /bin/bash
root@f408a9817eee:/usr/local/tomcat# apt update
root@f408a9817eee:/usr/local/tomcat# apt install -y iproute2 iputils-ping &> /dev/null
root@767f7355e9eb:/usr/local/tomcat# ip addr
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
77: eth0@if78: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
#思考:Linux能不能ping通容器内部
$ ping -c1 -W1 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.034 ms
#Linux 可以ping通 docker容器内部
原理:
- 每启动一个 docker 容器,docker 就会给 docker 容器分配一个 IP 地址,安装了docker,就会又一个网卡 docker0
桥接模式,使用的技术是 veth-pair 技术
再次测试宿主机 ip addr
- 在启动一个 tomcat 容器测试,发现又多了一对网卡!
$ docker run -d -P --name tomcat02 tomcat
$ docker exec -it tomcat02 /bin/bash
root@15eb260c7094:/usr/local/tomcat# apt update
root@15eb260c7094:/usr/local/tomcat# apt install -y iproute2 iputils-ping &> /dev/null
root@15eb260c7094:/usr/local/tomcat# ip addr
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
79: eth0@if80: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
我们发现这个容器带来的网卡,都是一对一对的。
#veth-pair 就是一对虚拟设备接口,他们都是成对出现的,一段连着协议,一段彼此相连
#正因为有这个特性,veth-pair 充当了一个桥接,链接各种虚拟网络设备
#OpenStack Docker容器之间连接 OVS 连接 都是使用 veth-pair 技术。
- 我们来测试一下 tomcat01 和 tomcat02 是否可以ping 通
$ docker exec -it tomcat01 /bin/bash
root@767f7355e9eb:/usr/local/tomcat# ping -c 1 -W 1 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.069 ms
--- 172.17.0.3 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.069/0.069/0.069/0.000 ms
#结论:容器和容器之间是可以相互ping通的!
路由的定义是指路由寻址功能,跨网段的时候会生成一个路由表,转发到对应的接口属于三层;
二层交换只能在局域网(同网段)连接。
结论:tomcat01 和 tomcat02 是拥有一个公用的路由器 docker0
所有的容器不指定网络的情况下,都是 docker0 路由的,Docker 会给我们的容器分配一个默认的可用的IP地址
Docker 默认的网段是 172.17.0.0/16 B类地址默认可以存放 65535 个地址
小结:
Docker中 docker0 使用的是 Linux 的桥接技术,docker0 和 容器之间是使用 veth-pair 虚拟接口技术。
即宿主机中是一个 Docker 的网桥:docker0。Docker 中的所有的网络接口都是虚拟的。虚拟的转发效率高(内网传递数据)。
只要容器删除,对应的网桥就没了(即虚拟接口 veth-pair 也会消失)。
docker network inspect bridge
6.2 --link
思考一个场景:我们编写了一个微服务,database url=ip,项目不重启,数据库IP地址掉了,我们希望可以处理这个问题,可以通过名字来访问容器?
$ docker exec -it tomcat01 ping tomcat02
ping: tomcat02: Name or service not known
#默认是ping 不通名字
#如何进行解决呢?
#使用--link就可以解决网络连通问题
$ docker run -it -d --name tomcat03 --link tomcat02 tomcat
#tomcat03 ping tomcat02
$ docker exec -it tomcat03 ping -c 1 -W 1 tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.121 ms
--- tomcat02 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.121/0.121/0.121/0.000 ms
#tomcat02 ping tomcat03
$ docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known
#查看bridge网络模式下的详细信息
$ docker network inspect bridge
探究 docker network inspect
$ docker inspect tomcat03 | grep -A 3 "Links"
"Links": [
"/tomcat02:/tomcat03/tomcat02"
],
#说明了tomcat03 和 tomcat02 的额绑定关系,=/接收容器名/源容器名或者别名
其实这个 tomcat03 就是在本地配置了 tomcat02 的信息配置
#登录tomcat03 的容器
$ docker exec -it tomcat03 /bin/bash
root@868350cec9a0:/usr/local/tomcat# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 114.114.114.114
nameserver 8.8.8.8
root@868350cec9a0:/usr/local/tomcat# 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 tomcat02 15eb260c7094
172.17.0.4 868350cec9a0
root@868350cec9a0:/usr/local/tomcat# ping -c 1 -W 1 tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.096 ms
--- tomcat02 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.096/0.096/0.096/0.000 ms
#--link 就是在容器的 /etc/hosts 配置中增加了一个 172.17.0.3 tomcat02 15eb260c7094 的条目信息
#探究:--link 本质就是容器中 /etc/hosts 新增相应的条目。
现在 Docker 已经不建议使用 --link 了;目前推荐使用自定义网络。
默认 docker0 问题,不支持容器名连接访问
6.3 自定义网络
容器互联技术:–link 和 自定义网络
#查看所有的Docker网络信息
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
4490c3a848b4 bridge bridge local
2373b4b3186d host host local
5536bc79b4b6 none null local
Docker 网络模式
网络模型 | 简介 |
---|---|
bridge | 为每一个容器分配、设置IP等,并将容器连接到一个docker0 虚拟网桥,默认为该模式 |
host | 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口 |
none | 容器有独立的 Network Namespace(网络名称空间),但是并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,IP等。(基本上不会使用) |
container | 新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP ,端口范围等 |
docker network 主流的是 bridge、host、none,常用的是 bridge、host。
bridge:桥接 docker (默认,自定义网络也是 bridge 模式)
none:不配置网络
host:和宿主机共享网络
container:容器网络联通(局限性很大)
测试:
#直接启动命令默认会添加--net bridge,而这个就是docker0
$ docker run -it -d -P --name tomcat01 tomcat
#等价于
$ docker run -it -d -P --name tomcat01 --net bridge tomcat
#docker0特点:默认,容器间的域名是不通,--link可以打通连通
#可以创建自定义网络
#--driver bridge :管理网络的驱动程序(默认为“桥接”)
#--subnet 192.168.0.0/16 :CIDR格式的子网,表示一个网段
#--gateway 192.168.0.1 :主子网的IPv4或IPv6网关
$ docker network create \
--driver bridge \
--subnet 192.168.0.0/16 \
--gateway 192.168.0.1 mynet
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
e79095f379fb bridge bridge local
993e748549d7 host host local
3e91d8f383af mynet bridge local
4bf3eb064757 none null local
自定义网络就已经创建完毕了
$ docker run -it -d --name alpine01 --network mynet alpine
$ docker run -it -d --name alpine02 --network mynet alpine
$ docker inspect alpine02 | grep -A 18 "Networks"
"Networks": {
"mynet": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"348e6354072b"
],
"NetworkID": "5aa4b631f9ef3b6189496fd2bdccf673f18dcee69232b09cb4095870eef20387",
"EndpointID": "64fdcb4b0dd537b2aeef757b8f2114ed84187aec7c99ec3ea611d375b0646c2a",
"Gateway": "192.168.0.1",
"IPAddress": "192.168.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:c0:a8:00:03",
"DriverOpts": null
}
}
#再次测试 ping 连接
#首先使用alpine01 ping alpine02的IP地址
$ docker exec -it alpine01 ping -c 1 -W 1 192.168.0.3
PING 192.168.0.3 (192.168.0.3): 56 data bytes
64 bytes from 192.168.0.3: seq=0 ttl=64 time=0.094 ms
--- 192.168.0.3 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.094/0.094/0.094 ms
#再次直接使用 alpine01 ping alpine02
#现在不使用--link也可以 ping 名字!!!
$ docker exec -it alpine01 ping -c 1 -W 1 alpine02
PING alpine02 (192.168.0.3): 56 data bytes
64 bytes from 192.168.0.3: seq=0 ttl=64 time=0.127 ms
--- alpine02 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.127/0.127/0.127 ms
自定义的网络 Docker 都已经帮运维/开发 维护好了对应的关系,推荐使用自定义网络。
原理就是这两个容器都是连的自定义网络,连接在同一个自定义网络的容器之间端口会自动相互暴露,而且不会向以外的显示任何端口,这样就更好的进行了容器见相互通信和隔离
好处:不同的集群使用不同的自定义网络,保证集群是安全和健康的。
6.4 网络连通
$ docker run -it --name alpine01-docker0 --network bridge -d alpine
$ docker run -it --name alpine02-docker0 --network bridge -d alpine
$ docker run -it -d --name alpine01 --network mynet alpine
$ docker run -it -d --name alpine02 --network mynet alpine
#默认不同的网络命名空间是不能够相互通信的
$ docker exec -it alpine01-docker0 ping -c 1 -W 1 192.168.0.2
PING 192.168.0.2 (192.168.0.2): 56 data bytes
--- 192.168.0.2 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
$ docker exec -it alpine01-docker0 ping alpine01
ping: bad address 'alpine01'
#测试打通alpine01-docker0 - alpine01
$ docker network connect mynet alpine01-docker0
#联通之后,就是将 alpine01-docker0 放到了 mynet 网络下
$ docker inspect alpine01-docker0
#一个容器两个IP地址
#再次测试
$ docker exec -it alpine01-docker0 ping -c 1 -W 1 192.168.0.2
PING 192.168.0.2 (192.168.0.2): 56 data bytes
64 bytes from 192.168.0.2: seq=0 ttl=64 time=0.088 ms
--- 192.168.0.2 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.088/0.088/0.088 ms
$ docker exec -it alpine01-docker0 ping -c 1 -W 1 alpine01
PING alpine01 (192.168.0.2): 56 data bytes
64 bytes from 192.168.0.2: seq=0 ttl=64 time=0.117 ms
--- alpine01 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.117/0.117/0.117 ms
#alpine02-docker0 还是依旧无法联通alpine01 alpine02
结论:假设要跨网络操作其他容器,就需要使用 docker network connect 联通了!
6.5 实战:部署 Redis 集群
#创建网卡
$ docker network create redis --subnet 172.38.0.0/16
#批量创建六个 Redis的Docker容器
for port in {
1..6} ;do
mkdir -pv /data/redis/node-${port}/conf
touch /data/redis/node-${port}/conf/redis.conf
cat << EOF > /data/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
for port in {
1..6} ; do
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /data/redis/node-${port}/data:/data \
-v /data/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 \
/usr/local/bin/redis-server /etc/redis/redis.conf
done
#范例:
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /data/redis/node-1/data:/data \
-v /data/redis/node-1/conf/redis.conf:/etc/redis/redis.conf
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 \
/usr/local/bin/redis-server /etc/redis/redis.conf
范例:
#创建Redis集群
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 \
172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
$ docker exec -it redis-1 /bin/sh
/data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 \
> 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
M: 24a8d8f3302ce28f1b9b06ce3bec871adb8585df 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
M: 2eff279dc91be6f8840a59f990b58ac740ebfa01 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
M: 5090c8478f101c5f1520b9264cab5d602befd6a8 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
S: 2d2733a06379f90a2c51c0e131b0391160d8f6b8 172.38.0.14:6379
replicates 5090c8478f101c5f1520b9264cab5d602befd6a8
S: 55d41b51e6053d6dcf9b730a79ce6cca1314e363 172.38.0.15:6379
replicates 24a8d8f3302ce28f1b9b06ce3bec871adb8585df
S: f8d17750650743957b846ccaab8799fca7c301a9 172.38.0.16:6379
replicates 2eff279dc91be6f8840a59f990b58ac740ebfa01
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
...
>>> Performing Cluster Check (using node 172.38.0.11:6379)
M: 24a8d8f3302ce28f1b9b06ce3bec871adb8585df 172.38.0.11:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 2d2733a06379f90a2c51c0e131b0391160d8f6b8 172.38.0.14:6379
slots: (0 slots) slave
replicates 5090c8478f101c5f1520b9264cab5d602befd6a8
S: 55d41b51e6053d6dcf9b730a79ce6cca1314e363 172.38.0.15:6379
slots: (0 slots) slave
replicates 24a8d8f3302ce28f1b9b06ce3bec871adb8585df
M: 5090c8478f101c5f1520b9264cab5d602befd6a8 172.38.0.13:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 2eff279dc91be6f8840a59f990b58ac740ebfa01 172.38.0.12:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: f8d17750650743957b846ccaab8799fca7c301a9 172.38.0.16:6379
slots: (0 slots) slave
replicates 2eff279dc91be6f8840a59f990b58ac740ebfa01
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
/data # redis-cli -c
#查看Redis集群的信息
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:249
cluster_stats_messages_pong_sent:250
cluster_stats_messages_sent:499
cluster_stats_messages_ping_received:245
cluster_stats_messages_pong_received:249
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:499
#查看Redis集群节点健康
127.0.0.1:6379> cluster nodes
24a8d8f3302ce28f1b9b06ce3bec871adb8585df 172.38.0.11:6379@16379 myself,master - 0 1661526536000 1 connected 0-5460
2d2733a06379f90a2c51c0e131b0391160d8f6b8 172.38.0.14:6379@16379 slave 5090c8478f101c5f1520b9264cab5d602befd6a8 0 1661526537442 4 connected
55d41b51e6053d6dcf9b730a79ce6cca1314e363 172.38.0.15:6379@16379 slave 24a8d8f3302ce28f1b9b06ce3bec871adb8585df 0 1661526537000 5 connected
5090c8478f101c5f1520b9264cab5d602befd6a8 172.38.0.13:6379@16379 master - 0 1661526536000 3 connected 10923-16383
2eff279dc91be6f8840a59f990b58ac740ebfa01 172.38.0.12:6379@16379 master - 0 1661526536433 2 connected 5461-10922
f8d17750650743957b846ccaab8799fca7c301a9 172.38.0.16:6379@16379 slave 2eff279dc91be6f8840a59f990b58ac740ebfa01 0 1661526535526 6 connected
127.0.0.1:6379> set k1 v1
-> Redirected to slot [12706] located at 172.38.0.13:6379 #redis-3 的容器操作
OK
#redis-3暂停后再次查看
/data # redis-cli -c
127.0.0.1:6379> cluster nodes
24a8d8f3302ce28f1b9b06ce3bec871adb8585df 172.38.0.11:6379@16379 myself,master - 0 1661526692000 1 connected 0-5460
2d2733a06379f90a2c51c0e131b0391160d8f6b8 172.38.0.14:6379@16379 master - 0 1661526693000 7 connected 10923-16383
55d41b51e6053d6dcf9b730a79ce6cca1314e363 172.38.0.15:6379@16379 slave 24a8d8f3302ce28f1b9b06ce3bec871adb8585df 0 1661526693143 5 connected
5090c8478f101c5f1520b9264cab5d602befd6a8 172.38.0.13:6379@16379 master,fail - 1661526636272 1661526634000 3 connected
2eff279dc91be6f8840a59f990b58ac740ebfa01 172.38.0.12:6379@16379 master - 0 1661526692740 2 connected 5461-10922
f8d17750650743957b846ccaab8799fca7c301a9 172.38.0.16:6379@16379 slave 2eff279dc91be6f8840a59f990b58ac740ebfa01 0 1661526691733 6 connected
127.0.0.1:6379> get k1
-> Redirected to slot [12706] located at 172.38.0.14:6379 #redis-4 从容器变成Master
"v1"
#将redis-3 容器暂停
$ docker stop redis-3