一、Docker数据管理
Docker镜像由多个只读层叠加而成,启动容器时,Docker会加载只读镜像层并在镜像栈顶部添加一个读写层
如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏,此即“写时复制(COW copy on write)"机制
如果将正在运行中的容器修改生成了新的数据,那么新产生的数据将会被复制到读写层,进行持久化保存,这个读写层也就是容器的工作目录,也为写时复制(COW) 机制。
COW机制节约空间,但会导致性低下,虽然关闭重启容器,数据不受影响,但会随着容器的删除,其对应的可写层也会随之而删除,即数据也会丢失.如果容器需要持久保存数据,并不影响性能可以用数据卷技术实现
如下图是将对根的数据写入到了容器的可写层,但是把/data 中的数据写入到了一个另外的volume 中用于数据持久化
1.1 容器的数据管理介绍
Docker镜像是分层设计的,镜像层是只读的,通过镜像启动的容器添加了一层可读写的文件系统,用户写入的数据都保存在这一层中。
1.1.1 Docker容器的分层
容器的数据分层目录
-
LowerDir: image 镜像层,即镜像本身,只读
-
UpperDir: 容器的上层,可读写 ,容器变化的数据存放在此处
-
MergedDir: 容器的文件系统,使用Union FS(联合文件系统)将lowerdir 和 upperdir 合并完成后给容器使用,最终呈现给用户的统一视图
-
WorkDir: 容器在宿主机的工作目录,挂载后内容会被清空,且在使用过程中其内容用户不可见
范例: 查看指定容器数据分层
1.1.2 哪些数据需要持久化
有状态的协议
有状态协议就是就通信双方要记住双方,并且共享一些信息。而无状态协议的通信每次都是独立的,与上一次的通信没什么关系。
"状态”可以理解为“记忆”,有状态对应有记忆,无状态对应无记忆
-
左侧是无状态的http请求服务,右侧为有状态
-
下层为不需要存储的服务,上层为需要存储的部分服务
1.1.3容器数据持久保存方式
如果要将写入到容器的数据永久保存,则需要将容器中的数据保存到宿主机的指定目录
Docker的数据类型分为两种:
-
数据卷(Data Volume): 直接将宿主机目录挂载至容器的指定的目录 ,推荐使用此种方式,此方式较常用
-
数据卷容器(Data Volume Container): 间接使用宿主机空间,数据卷容器是将宿主机的目录挂载至一个专门的数据卷容器,然后让其他容器通过数据卷容器读写宿主机的数据 ,此方式不常用
1.2数据卷(data volume)
1.2.1数据卷(data volume)
1.2.1.1数据卷使用场景
-
数据库
-
日志输出
-
静态web页面
-
应用配置文件
-
多容器间目录或文件共享
1.2.1.2 数据卷的特点
-
数据卷是目录或者文件,并且可以在多个容器之间共同使用,实现容器之间共享和重用对数据卷更改数据在所有容器里面会立即更新。
-
数据卷的数据可以持久保存,即使删除使用使用该容器卷的容器也不影响。
-
在容器里面的写入数据不会影响到镜像本身,即数据卷的变化不会影响镜像的更新
-
依赖于宿主机目录,宿主机出问题,上面容器会受影响,当宿主机较多时,不方便统一管理
-
匿名和命名数据卷在容器启动时初始化,如果容器使用的镜像在挂载点包含了数据,会拷贝到新初始化的数据卷中
1.2.1.3据卷使用方法
启动容器时,可以指定使用数据卷实现容器数据的持久化,数据卷有三种
-
指定宿主机目录或文件: 指定宿主机的具体路径和容器路径的挂载关系
-
容器和真机共用空间, 把容器的文件挂载到 真机的文件夹
-
-
匿名卷: 不指定数据名称,只指定容器内目录路径充当挂载点,docker自动指定宿主机的路径进行挂载
-
命名卷: 指定数据卷的名称和容器路径的挂载关系 和匿名卷在一个目录 , 只是取了一个自定义的名字
docker run 命令的以下格式可以实现数据卷
-v, --volume=[host-src:]container-dest[:<options>]
<options>
ro 从容器内对此数据卷是只读,不写此项默认为可读可写
rw 从容器内对此数据卷可读可写,此为默认值
方式1
#指定宿主机目录或文件格式:
-v <宿主机绝对路径的目录或文件>:<容器目录或文件>[:ro] #将宿主机目录挂载容器目录,两个目录
都可自动创建
例子:
[root@localhost ~]#docker run -it --name c1 -v /data:/opt centos:7
[root@78b724f7bf34 /]#
[root@78b724f7bf34 /]#
[root@78b724f7bf34 /]# cd /opt/
[root@78b724f7bf34 opt]#
[root@78b724f7bf34 opt]# ls
Centos-7.repo dockerfile env.list epel-7.repo
[root@78b724f7bf34 opt]#
方式2
#匿名卷,只指定容器内路径,没有指定宿主机路径信息,宿主机自动生成/var/lib/docker/volumes/<卷ID>/_data目录,并挂载至容器指定路径
-v <容器内路径>
#示例:
docker run --name nginx -v /etc/nginx nginx
例子:
docker run --name nginx -v /etc/nginx nginx
[root@localhost ~]#docker exec -it nginx bash
root@5d4d2f9f93f2:/# cd etc/nginx/
root@5d4d2f9f93f2:/etc/nginx# ls
conf.d fastcgi_params mime.types modules nginx.conf scgi_params uwsgi_params
root@5d4d2f9f93f2:/etc/nginx# touch aaa
root@5d4d2f9f93f2:/etc/nginx#
方式3
#命名卷将固定的存放在/var/lib/docker/volumes/<卷名>/_data
-v <卷名>:<容器目录路径>
#可以通过以下命令事先创建,如可没有事先创建卷名,docker run时也会自动创建卷
docker volume create <卷名>
#示例:
docker run -d -p 80:80 --name nginx01 -v vol1:/usr/share/nginx/html/ nginx
例子:
docker run -d -p 80:80 --name nginx01 -v vol1:/usr/share/nginx/html nginx
docker run -d -p 80:80 --name nginx01 -v vol1:/usr/share/nginx/html nginx
docker rm 的 -v 选项可以删除容器时,同时删除相关联的匿名卷
-v, --volumes Remove the volumes associated with the container
1.2.1.4管理卷的操作
docker volume COMMAND
Commands:
create Create a volume
inspect Display detailed information on one or more volumes
ls List volumes
prune Remove all unused local volumes
rm Remove one or more volumes
1.2.2实战案例:
1.2.2.1在宿主机创建容器所使用的目录
FROM centos:7.9.2009
LABEL author="zhou cloud" \
version="1.0" \
description="test"
RUN yum -y install gcc gcc-c++ make automake pcre pcre-devel zlib zlib-devel openssl openssl-devel wget
ADD nginx-1.18.0.tar.gz /usr/local/src
RUN cd /usr/local/src/nginx-1.18.0 && ./configure --prefix=/apps/nginx && make && make install
VOLUME [ "/apps/nginx/html" ]
COPY index.html /apps/nginx/html
#CMD ["/apps/nginx/sbin/nginx","-g","daemon off;"]
CMD [ "-g","daemon off;" ]
EXPOSE 80 443
#暴露多个端口
ENTRYPOINT ["/apps/nginx/sbin/nginx"]
#容器的默认进口
[root@ubuntu2204 centos7]#docker build -t c7-nginx:v1.0 .
#生成镜像
[root@ubuntu2204 centos7]#docker run -d -P --name web1 c7-nginx
c7-nginx c7-nginx:v1.0
[root@ubuntu2204 centos7]#docker run -d -P --name web1 c7-nginx:v1.0
510702d25a3e564ba25af928b47262fcfe4e3d78cc0fd55fa80ad258b16c4a08
[root@ubuntu2204 centos7]#docker port web1
80/tcp -> 0.0.0.0:32772
80/tcp -> [::]:32772
443/tcp -> 0.0.0.0:32771
443/tcp -> [::]:32771
[root@ubuntu2204 ~]#mkdir /data/testdir
[root@ubuntu2204 ~]#echo Test page on host > /data/testdir/index.html
[root@ubuntu2204 centos7]#docker run -d -p 80:80 -v /data/testdir:/apps/nginx/html/ --name web1 c7-nginx:v1.0
a8b518ab4dea55d3cd3aeb4e79b362dd099e04fca9e5d06fb3c4a1fdded6c4dc
[root@ubuntu2204 centos7]#curl 192.168.91.11
test page
1.2.3 命名卷实际操作
[root@ubuntu2204 ~]#docker volume create myvol
myvol
[root@ubuntu2204 ~]#ls /var/lib/docker/
buildkit/ engine-id network/ plugins/ swarm/ volumes/
containers/ image/ overlay2/ runtimes/ tmp/
[root@ubuntu2204 ~]#ls /var/lib/docker/volumes/
16fcd442f0af56ad78f7705fd76464663b278721e94ace76d77be2e1a41aa453
4d95eddce62251697618cfc4e67c16de37044ce4702db2a6249a5c235434c471
58878f12135aec890c17c7a9a9b6dcc7476d34045d4d770cc08a6fc9ceecae66
592b004312203d42d5a38440bb23cf801f96e16410d7f17469f45d18a5b636ac
67ea307b2f7cf8e844874b70d9399975e4c9fcb9125df995bcc76fef42984106
75ade89afd3669dae3fe62b02cefbaaa946d8058d4c7f2af8c5b84536c8f496a
ba95fb221966708f08b2965b6f4650f9a95bcb7871189057d0ad8b2a8aaacc62
backingFsBlockDev
ca6a5d221a8bb7b56604b19668707e22d9c9ca47355691fa5fcc725d8b48ee84
ccfd59693706edf979841106f1492f04c6d80d5793043989c7523b6da0986912
e9f411c2b7a4e73d0464ddedcedeff3354fd0d2c49c0fd23f17e1e761196d4b4
ea45f03d897456fcc69b81daf5f64519e7bb8bffbced9903c18e443943ad7195
metadata.db
myvol
[root@ubuntu2204 ~]#ls /var/lib/docker/volumes/myvol/
_data
启动容器
[root@ubuntu2204 ~]#docker run -d -p 80:80 --name web1 -v myvol:/apps/nginx/html c7-nginx:v1.0
6064564e2ffa1f3ed1026f9269b8711e3e4af5be07b6bfe798f8b37a362e044e
[root@ubuntu2204 ~]#ls /var/lib/docker/volumes/myvol/_data/
50x.html index.html
[root@ubuntu2204 ~]#curl 192.168.91.11
nginx in docker
[root@ubuntu2204 ~]#docker volume rm myvol
1.3 容器服务器数据卷
[root@localhost myvol]#docker run -it --name vo-server -v /data/testdir/:/mnt centos:7 bash
[root@960cf1b7f42a /]# cd /mnt/
[root@960cf1b7f42a mnt]#
[root@960cf1b7f42a mnt]# ls
[root@960cf1b7f42a mnt]# echo test page > index.html
[root@ubuntu2204 ~]#docker run -d --name vo-server -v /data/testdir/:/usr/share/nginx/html/ nginx
bcf5282ab055853dabe2a36cc1606d2088bdea9fe33f3f1b1cf33c1f39d9f2b8
[root@ubuntu2204 ~]#echo "test page" /data/testdir/index.html
test page
[root@ubuntu2204 ~]#docker run -d --name web1 --volumes-from vo-server -p 80:80 nginx
2ea41cb46a149e762809e367e429a852adc0539872db46eb269790c9e6c21810
[root@ubuntu2204 ~]#docker run -d --name c1 --volumes-from vo-server -p 80:80 c7-nginx:v1.0
2ea41cb46a149e762809e367e429a852adc0539872db46eb269790c9e6c21810
[root@ubuntu2204 ~]#cat /data/testdir/index.html
test page
[root@ubuntu2204 ~]#curl 192.168.91.11
test page
[root@ubuntu2204 ~]#docker run -d --name c2 --volumes-from vo-server -p 81:80 c7-nginx:v1.0
e954559313cb1e44cc35c103b6beb5060f8c88f21dabdc3b515dad6c522aebc1
[root@ubuntu2204 ~]#
[root@ubuntu2204 ~]#curl 192.168.91.11:81
test page
[root@ubuntu2204 ~]#vim /data/testdir/index.html
[root@ubuntu2204 ~]#
[root@ubuntu2204 ~]#curl 192.168.91.11:81
test page v1.0
[root@ubuntu2204 ~]#curl 192.168.91.11
test page v1.0
二、Docker网络管理
2.1 Docker的默认的网络通信
2.1.1 Docker安装后默认的网络设置
Docker服务安装完成之后,默认在每个宿主机会生成一个名称为docker0的网卡其IP地址都是172.17.0.1/16
[root@ubuntu1804 ~]#apt -y install bridge-utils
[root@ubuntu1804 ~]#brctl show
另外会额外创建三个默认网络,用于不同的使用场景:
root@docker-server1:~# docker network list
NETWORK ID NAME DRIVER SCOPE
438a9be14ef8 bridge bridge local #桥接网络,默认使用的模式,容器基于SNAT进行地址转换访问宿主机以外的环境
4c026356e4d1 host host local #host网络,直接使用宿主机的网络( 不创建net namespace),性能最好,但是容器端口不能冲突
8d70da095b8e none null local #空网络,容器不会分配有效的IP地址(只有一个回环网卡用于内部通信),用于离线数据处理等场景。
2.1.2 创建容器后的网络配置
每次新建容器后
-
宿主机多了一个虚拟网卡,和容器的网卡组合成一个网卡,比如: 137: veth8ca6d43@if136,而在容器内的网卡名为136,可以看出和宿主机的网卡之间的关联
-
容器会自动获取一个172.17.0.0/16网段的随机地址,默认从172.17.0.2开始,第二次容器为172.17.0.3,以此类推
-
容器获取的地址并不固定,每次容器重启,可能会发生地址变化
2.1.3 容器间的通信
2.1.3.1同一个宿主机的不同容器可以相互通信
默认情况下
同一个宿主机的不同容器之间可以相互通信
2.1.4修改默认网络设置
新建容器默认使用docker0的网络配置,可以修改默认指向自定义的网桥网络
范例: 用自定义的网桥代替默认的docker0
新建网桥
[root@localhost ~]#brctl addbr docker1
[root@localhost ~]#brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242112ce687 no veth180c8c0
veth80dd14b
vetha4eabfc
docker1 8000.000000000000 no
virbr0 8000.52540024c89d yes virbr0-nic
[root@localhost ~]#ip a a 192.168.1.1/24 dev docker1
#给网桥设置地址
[root@localhost ~]#ip a
422: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 12:2d:7e:3e:47:a8 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.1/24 scope global docker1
valid_lft forever preferred_lft forever
[root@localhost ~]#brctl
Usage: brctl [commands]
commands:
addbr <bridge> add bridge
delbr <bridge> delete bridge
addif <bridge> <device> add interface to bridge
delif <bridge> <device> delete interface from bridge
hairpin <bridge> <port> {on|off} turn hairpin on/off
setageing <bridge> <time> set ageing time
setbridgeprio <bridge> <prio> set bridge priority
setfd <bridge> <time> set bridge forward delay
sethello <bridge> <time> set hello time
setmaxage <bridge> <time> set max message age
setpathcost <bridge> <port> <cost> set path cost
setportprio <bridge> <port> <prio> set port priority
show [ <bridge> ] show a list of bridges
showmacs <bridge> show a list of mac addrs
showstp <bridge> show bridge stp info
stp <bridge> {on|off} turn stp on/off
让容器使用docker1 网桥
# 修改service 文件
[root@localhost ~]#vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -b docker1
# 在启动命令行加入 -b docker1
[root@localhost ~]#dockerd --help
#可以使用帮助查看
重启服务
[root@localhost ~]#systemctl daemon-reload
[root@localhost ~]#systemctl restart docker
[root@localhost ~]#docker run -it centos:7
#进入容器
[root@456c8d627c15 /]# ifconfig
bash: ifconfig: command not found
[root@456c8d627c15 /]# 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
192.168.1.2 456c8d627c15
[root@456c8d627c15 /]#
可以看网桥
[root@localhost ~]#docker run -d nginx
d7bbafa010567e89b5c756a3ff501000de4315d274b4e9461fb574e1627d96b2
[root@localhost ~]#brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242112ce687 no
docker1 8000.4eafb8e2140f no veth68380bf
virbr0 8000.52540024c89d yes virbr0-nic
2.1.5 修改docker 0 默认的网段
[root@localhost ~]#vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -bip 192.168.2.1/24
# 在启动命令行加入 -b docker1
[root@localhost ~]#systemctl daemon-reload
[root@localhost ~]#systemctl restart docker
2.2.1容器名称介绍
即在同一个宿主机上的容器之间可以通过自定义的容器名称相互访问,比如: 一个业务前端静态页面是使用nginx,动态页面使用的是tomcat,另外还需要负载均衡调度器,如: haproxy 对请求调度至nginx和tomcat的容器,由于容器在启动的时候其内部IP地址是DHCP 随机分配的,而给容器起个固定的名称,则是相对比较固定的,因此比较适用于此场景
2.2.2 容器名称实现
docker run 创建容器,可使用--link选项实现容器名称的引用
--link list #Add link to another container
格式:
docker run --name <容器名称> #先创建指定名称的容器
docker run --link <目标通信的容器ID或容器名称> #再创建容器时引用上面容器的名称
2.2.2.1 例子1
先建立一个带有名字的容器
[root@localhost ~]#docker run -it --name c1 centos:7 bash
[root@18e90943d242 /]#
在建立第二个容器和第一个 连接
[root@localhost ~]#docker run -it --name c2 --link c1 centos:7 bash
[root@fed8f5ad3e46 /]# ping c1
PING c1 (172.17.0.5) 56(84) bytes of data.
64 bytes from c1 (172.17.0.5): icmp_seq=1 ttl=64 time=0.163 ms
64 bytes from c1 (172.17.0.5): icmp_seq=2 ttl=64 time=0.084 ms
64 bytes from c1 (172.17.0.5): icmp_seq=3 ttl=64 time=0.109 ms
^C
--- c1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.084/0.118/0.163/0.035 ms
[root@fed8f5ad3e46 /]# 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.5 c1 18e90943d242
172.17.0.6 fed8f5ad3e46
2.2.2.2 例子实现LAMP
[root@localhost data]#vim env_mysql.list
MYSQL_ROOT_PASSWORD=123456
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=wppass
[root@localhost data]#vim env_wordpress.list
WORDPRESS_DB_HOST=mysql:3306
WORDPRESS_DB_NAME=wordpress
WORDPRESS_DB_USER=wpuser
WORDPRESS_DB_PASSWORD=wppass
WORDPRESS_TABLE_PREFIX=wp_
docker run -d --name mysql -v /data/mysql:/var/lib/mysql -p 3306:3306 --env-file= env_mysql.list mysql:5.7.32
[root@localhost data]#docker run --name mysql -v /data/mysql:/var/lib/mysql --env-file=/data/env_mysql.list -d -p 3306:3306 mysql:5.7.32
[root@localhost data]#docker run -d --name wordpress --link mysql --env-file=/data/env_wordpress.list -p 80:80 wordpress
2.3 docker网络连接模式
2.3.1网络模式介绍
Docker 的网络支持5种网络模式:
-
none
-
没有网络只有一块回环网卡
-
-
bridge
-
网桥模式 默认的模式 nat 地址转换
-
-
host
-
容器和真机共享网络 , 直连
-
-
container
-
容器和容器共享网络 两个容器共用一块网卡
-
-
network-name
-
·自定义模式
-
范例: 查看默认的网络模式
[root@localhost ~]#docker network ls
NETWORK ID NAME DRIVER SCOPE
83c513d933b3 bridge bridge local
fa46df3b8964 host host local
063fb463305f none null local
2.3.2 网络模式指定
默认新建的容器使用Bridge模式,创建容器时,docker run 命令使用以下选项指定网络模式
格式
docker run --network <mode>
docker run --net=<mode>
<mode>: 可是以下值
none
bridge
host
container:<容器名或容器ID>
<自定义网络名称>
2.3.2.1 host模式
容器和宿主机共享网络命名空间,即容器和宿主机使用同一个IP、端口范围(容器与宿主机/同模式的容器不可以相同的端口号)、路由、iptables规则等
docker run --network=host
2.3.2.2 container模式
和指定且已存在的容器共享网络命名空间,即两个使用同一个IP、端口范围(容器与宿主机/同模式的容器不可以相同的端口号)、路由、iptables规则等
docker run --network=host
2.3.2.3 none模式
每个容器都有独立的网络命名空间,但是容器没有eth0网卡、IP、端口等,只有lo网卡。
docker run --network=none
2.3.2.4 bridge模式
docker的默认网络模式。
此模式的每个容器都要独立的网络命名空间,即每个容器都有独立的IP、端口范围(每个容器可以相同的端口号)、路由、iptables规则等。
docker run [--network=bridge]
2.3.2.5 自定义网络
可以自定义创建一个网段、网桥、网络模式,从而可自定义容器IP来创建容器
docker network create --subnet 新网段 --opt "com.docker.network.bridge.name"="新网桥名" 新网络模式名
docker run --network 新网络模式名 --ip 自定义IP