数据管理
参考文档
容器数据的目录分层
- **Lower Dir:**镜像层,即镜像本身,只读层
- **Upper Dir:**容器的上层,容器中变化的文件存放于此处,读写层
- **Merged Dir:**容器的文件系统,使用的是联合文件系统(Union FS) 将 lowerdir 和 upperdir 合并完成后给容器使用,最终呈现给用户的统一视图
- **WorkDir:**容器在宿主机的工作目录,在使用过程中其内容用户不可见,挂载后内容会被清空
查看容器的数据分层
docker inspect name(GraphDriver 字段)
范例一
未有数据修改前
# docker history busybox:stable
IMAGE CREATED CREATED BY SIZE COMMENT
334e4a014c81 10 days ago /bin/sh -c #(nop) CMD ["sh"] 0B
<missing> 10 days ago /bin/sh -c #(nop) ADD file:ac7a70cec126c0804… 4.86MB
# docker run -d --name busybox -d busybox:stable tail -f /etc/hosts
# docker inspect busybox
...
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1-init/diff:/var/lib/docker/overlay2/08c35ade32977f0f78381997288ef5b0842432a283cdb2e2a3acfddd3d98874c/diff",
"MergedDir": "/var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/merged",
"UpperDir": "/var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/diff",
"WorkDir": "/var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/work"
},
"Name": "overlay2"
},
...
# LowerDir,因为镜像分了两层,所以产生了两个LowerDir。
# ls /var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1-init/diff
dev etc proc sys
# ls /var/lib/docker/overlay2/08c35ade32977f0f78381997288ef5b0842432a283cdb2e2a3acfddd3d98874c/diff
bin dev etc home lib lib64 root tmp usr var
# cat /var/lib/docker/overlay2/08c35ade32977f0f78381997288ef5b0842432a283cdb2e2a3acfddd3d98874c/diff/etc/localtime
TZif2UTCTZif2UTC
UTC0
# UpperDir,读写层没有数据修改,因此没有数据。
# ls /var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/diff
# MergedDir,将所有LowerDir和UpperDir合并到一起
# ls /var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/merged
bin dev etc home lib lib64 proc root sys tmp usr var
# WorkDir,没有数据,即使有数据,在容器中也是不可见的
# tree /var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/work
/var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/work
└── work有数据修改后
# docker exec -it busybox sh
/ # dd if=/dev/zero of=/root/test.img bs=1M count=10
/ # ls -l /root/test.img -h
-rw-r--r-- 1 root root 10.0M Dec 17 15:32 /root/test.img
/ # echo 'UTC+8' >> /etc/localtime
# LowerDir,因为是只读层,所以无变化
# ls /var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1-init/diff
dev etc proc sys
# ls /var/lib/docker/overlay2/08c35ade32977f0f78381997288ef5b0842432a283cdb2e2a3acfddd3d98874c/diff
bin dev etc home lib lib64 root tmp usr var
# cat /var/lib/docker/overlay2/08c35ade32977f0f78381997288ef5b0842432a283cdb2e2a3acfddd3d98874c/diff/etc/localtime
TZif2UTCTZif2UTC
UTC0
# UpperDir,读写层发生了数据修改
# tree /var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/diff
/var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/diff
├── etc
│ └── localtime # 从只读层复制过来并进行了数据添加(修改)
└── root
└── test.img # 直接写入到了读写层
# cat /var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/diff/etc/localtime
TZif2UTCTZif2UTC
UTC0
UTC+8
# MergedDir,因为有了Union FS的存在,所以看起来还是一个整体
# ls /var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/merged
bin dev etc home lib lib64 proc root sys tmp usr var
# cat /var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/merged/etc/localtime
TZif2UTCTZif2UTC
UTC0
UTC+8
# WorkDir,没有数据,即使有数据,在容器中也是不可见的
# tree /var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/work
/var/lib/docker/overlay2/6f416501797d99899f520c77774448afe0c7ed20de23e968c927eb665b566be1/work
└── work范例二
# docker history nginx:1.23
IMAGE CREATED CREATED BY SIZE COMMENT
3964ce7b8458 3 days ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 3 days ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
<missing> 3 days ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 3 days ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
<missing> 3 days ago /bin/sh -c #(nop) COPY file:e57eef017a414ca7… 4.62kB
<missing> 3 days ago /bin/sh -c #(nop) COPY file:abbcbf84dc17ee44… 1.27kB
<missing> 3 days ago /bin/sh -c #(nop) COPY file:5c18272734349488… 2.12kB
<missing> 3 days ago /bin/sh -c #(nop) COPY file:7b307b62e82255f0… 1.62kB
<missing> 3 days ago /bin/sh -c set -x && addgroup --system -… 61.3MB
<missing> 3 days ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B
<missing> 3 days ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.9 0B
<missing> 3 days ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.23.3 0B
<missing> 11 days ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
<missing> 11 days ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 11 days ago /bin/sh -c #(nop) ADD file:1f1efd56601ebc26a… 80.5MB
# docker run -d --name nginx nginx:1.23
# docker inspect nginx
...
"GraphDriver": {
"Data": {
# 因为镜像有很多分层,所以会产生很多的LowerDir(每个目录以":"为分隔符显示)
"LowerDir": "/var/lib/docker/overlay2/94d22d89b8fa020dbb17290f5c5684000202ac1b58f634a0f0f70877ec16ad9c-init/diff:/var/lib/docker/overlay2/98d2ab81673615b7cf8aeb797dd146d518eb0bfde0d09df27c1b20e9f33cf8c0/diff:/var/lib/docker/overlay2/44a0f9233d0165e521c43c32cc08d747a2becfc0efbfcfe6ecd44ee7b4aac09c/diff:/var/lib/docker/overlay2/431b57061d42f526085599cee1e2e0d3a469d67679410ea0c2a3d7f1960f3b36/diff:/var/lib/docker/overlay2/a5370a8bc35cb3eea7a9f8f61f7288a55b57b01d4dcb37c3d855dd2a81a606ed/diff:/var/lib/docker/overlay2/58740eec574cd2e7ab620df0cccfa7a1c94cf0b2d5de8182547787712c8551d0/diff:/var/lib/docker/overlay2/5a6d1455634f092cede6488935884d64276fe8f7aeff2e73d8c8d8e18005a1ef/diff",
"MergedDir": "/var/lib/docker/overlay2/94d22d89b8fa020dbb17290f5c5684000202ac1b58f634a0f0f70877ec16ad9c/merged",
"UpperDir": "/var/lib/docker/overlay2/94d22d89b8fa020dbb17290f5c5684000202ac1b58f634a0f0f70877ec16ad9c/diff",
"WorkDir": "/var/lib/docker/overlay2/94d22d89b8fa020dbb17290f5c5684000202ac1b58f634a0f0f70877ec16ad9c/work"
},
"Name": "overlay2"
},
...容器数据存放过程
- docker 镜像是由多个只读层叠加而成,启动容器时,docker 会在只读层的基础上添加一个读写层(就是添加一个可读可写的文件系统),进而生成容器,用户写入的数据都保存在读写层中
- 如果运行中的容器修改了一个现有已存在的文件:
- 那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏
- 本质上就是利用 写时复制机制(COW copy on write)
- 如果运行中的容器生成了新的数据:
- 那么新产生的数据将会被复制到读写层,进行持久化保存,这个读写层就是容器的工作目录
- 本质上就是利用 写时复制机制(COW copy on write)
容器数据持久保存方式
- 数据卷(Data Volume)
- 直接将宿主机目录挂载至容器的指定目录,推荐使用此种方式
- 常用
- 数据卷容器(Data Volume Container)
- 间接使用宿主机空间,将宿主机的目录挂载至一个专门的数据卷容器,然后让其他容器通过数据卷容器读写宿主机的数据
- 不常用
数据卷
- 相当于k8s中的hostPath?
数据卷的特点
-
数据卷实际上就是宿主机上的目录或者是文件,可以被直接mount到容器当中使用
-
数据卷是目录或者文件,并且可以在多个容器之间共同使用,实现容器之间共享和重用
-
对数据卷更改数据在所有容器里会立即更新
-
数据卷的数据可以持久保存,即使删除容器 数据卷中的数据也不会消失
-
在容器中写入数据不会影响到镜像本身,即数据卷的变化不会影响镜像的更新
-
依赖于宿主机目录,宿主机出现问题,上面的容器会受到影响,当宿主机较多时,不方便统一管理
-
匿名和命名数据卷在容器启动时初始化,如果容器使用的镜像在挂载点包含了数据,会拷贝到新初始化的数据卷中
数据卷的使用场景
实际生产环境中,需要针对不同类型的服务、不同类型的数据存储要求做相应的规划,最终保证服务的可扩展性、稳定性以及数据的安全性
- 数据库
- 日志输出
- 静态web页面
- 应用配置文件
- 多容器间目录或文件共享
数据卷的缺点
- **使容器的迁移性较差:**如多个nginx使用数据卷共享配置文件,如果涉及到迁移 将会导致配置文件不能整体的迁移到其他主机
数据卷的类型
-
**目录数据卷:**将宿主机目录挂载容器目录
-
**命名卷:**指定数据卷的名称和容器路径的挂载关系
-
**匿名卷:**不指定数据卷名称,只指定容器内目录路径充当挂载点,docker会自动指定宿主机的路径进行挂载,默认方式
数据卷注意事项
- 必须在构建镜像的Dockerfile中指定了VOLUME 才能使用数据卷,否则指定容器会出现无法启动等问题
数据卷的实现
语法
docker run -v [localhost-src:]container-dest[:option]
#:option
:ro #只读挂载,即容器内数据只读不能修改
:rw #读写挂载,即容器内数据可读可写,此为默认值目录数据卷的实现
#将宿主机目录挂载容器目录,两个目录都可以自动创建
-v <宿主机绝对路径的目录或文件>:<容器内绝对路径的目录或文件>[:ro]
#范例:
docker run -d -p 80:80 --name nginx -v /data/nginx/html:/apps/nginx/html nginx命名卷的实现
- 命名卷的默认存放路径:/var/lib/docker/volumes/<卷名>/_data
-v <卷名>:<容器内目录路径>
#也可以通过以下命令事先创建命名卷,如事先未创建 docker run时也会自动创建
docker volume create <卷名>
#范例:
docker run -d -p 80:80 --name nginx -v vol1:/apps/nginx/html nginx匿名卷的实现
- 匿名卷的默认存放路径:/var/lib/docker/volumes/<卷ID>/_data
-v <容器内路径>
#范例:
docker run -d -p 80:80 --name nginx -v /apps/nginx/html nginx数据卷的管理
docker rm -v container #加上-v,删除容器时,同时删除数据卷
#数据卷管理命令
Usage: docker volume COMMAND
Manage volumes
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
Run 'docker volume COMMAND --help' for more information on a command.
#范例:
docker inspect --format="{{.Mounts}}" <容器ID> #查看数据卷的挂载关系
docker volume rm `docker volume ls -q` #删除所有数据卷数据卷实战案例
注意事项
- 做映射的宿主机目录必须为空,否则将出现无法启动容器等问题
读写挂载目录数据卷
nginx使用目录数据卷
#临时启动容器观察网页主目录的存放位置
[root@docker ~]# docker run -it --rm nginx:latest bash
root@2367c5428c7a:/# cat /etc/nginx/conf.d/default.conf
...
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
...
#在宿主机准备所需目录和文件(不准备也可以,docker会自动创建)
[root@docker ~]# mkdir -p /data/docker/nginx/html
[root@docker ~]# echo 'test page on local host v1' > /data/docker/nginx/html/index.html
#启动容器并指定宿主机目录和容器目录的映射关系
[root@docker ~]# docker run -d -p 80:80 -v /data/docker/nginx/html/:/usr/share/nginx/html nginx:latest
#测试访问
[root@docker ~]# curl 10.0.0.8
test page on local host v1
#在启动一个容器并指定同样的映射关系
[root@docker ~]# docker run -d -p 81:80 -v /data/docker/nginx/html/:/usr/share/nginx/html nginx:latest
#测试访问
[root@docker ~]# curl 10.0.0.8
test page on local host v1
[root@docker ~]# curl 10.0.0.8:81
test page on local host v1
#模拟版本升级(随意选择一个容器修改)
[root@docker ~]# docker exec -it 09d5d0c84a24 sh
# echo 'test page on local host v2' > /usr/share/nginx/html/index.htm
#测试访问(因为默认是读写挂载,所以在任何容器中都可以修改)
[root@docker ~]# curl 10.0.0.8
test page on local host v2
[root@docker ~]# curl 10.0.0.8:81
test page on local host v2
#宿主机文件也同时被修改
[root@docker ~]# cat /data/docker/nginx/html/index.html
test page on local host v2mysql使用目录数据卷
注意:
- MySQL挂载数据卷时不能开启只读选项,否则数据库将无法写入数据
- MySQL不进行数据卷挂载的话 容器删除后数据将丢失
#观察mysql的数据存放路径
[root@docker ~]# docker run -it --rm mysql:5.7.30 sh
# cat /etc/mysql/mysql.conf.d/mysqld.cnf
...
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql #数据存放路径
#log-error = /var/log/mysql/error.log
# By default we only accept connections from localhost
#bind-address = 127.0.0.1
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
#指定目录数据卷启动容器
[root@docker ~]# docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=12345 -v /data/docker/mysql:/var/lib/mysql mysql:5.7.30
#观察宿主机是否生成文件
[root@docker ~]# ll /data/docker/mysql/
total 188484
...
-rw-r----- 1 systemd-coredump input 50331648 Jan 31 02:28 ib_logfile0
-rw-r----- 1 systemd-coredump input 50331648 Jan 31 02:28 ib_logfile1
-rw-r----- 1 systemd-coredump input 12582912 Jan 31 02:28 ibtmp1
drwxr-x--- 2 systemd-coredump input 4096 Jan 31 02:28 mysql
...
#在容器中创建数据库
[root@docker ~]# docker exec -it 61006 sh
# mysql -uroot -p12345
...
mysql> create database testdb;
Query OK, 1 row affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| testdb |
+--------------------+
5 rows in set (0.00 sec)
#在宿主机查看是否生成
[root@docker ~]# ll -tr /data/docker/mysql/
...
drwxr-x--- 2 systemd-coredump input 20 Jan 31 02:33 testdb只读挂载目录数据卷
- 默认数据卷为可读可写,加上ro选项,可以实现容器只读挂载
- 对于不希望容器修改的数据 如:配置文件、脚本等 可以使用此方式挂载
- 只读挂载后将无法在容器内直接修改挂载点的数据,但容器中非挂载点的数据仍可修改
[root@docker ~]# docker run -d -p 80:80 -v /data/docker/nginx/html/:/usr/share/nginx/html:ro nginx:latest
1c838d1381754a5c3cc7f62cedc7c802e2a9052ed3691f5e2479721aea7e832e
[root@docker ~]# docker exec -it 1c838d bash
root@1c838d138175:/# echo 'test page on local host v3' > /usr/share/nginx/html/index.html
bash: /usr/share/nginx/html/index.html: Read-only file system文件数据卷
tomcat使用文件数据卷
- 文件挂载用于很少更改文件内容的场景,如:nginx、tomcat的配置文件等
#同时实现三个数据卷的挂载,只读方式挂载文件数据卷,读写方式挂载目录数据卷
[root@docker ~]# docker run -d -v /data/bin/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v /data/testapp:/data/tomcat/webapps/testapp -v /data/lgos:/apps/tomcat/logs -p 8080:8080 tomcat-web:app1匿名数据卷
- 删除容器不会删除匿名数据卷,如要删除 需执行 docker volume rm 匿名卷ID
nginx实现匿名数据卷
#利用匿名数据卷创建容器
[root@docker ~]# docker run -d -p 80:80 --name nginx01 -v /usr/share/nginx/html nginx:latest
#访问主页
[root@docker ~]# curl 10.0.0.8
...
<body>
<h1>Welcome to nginx!</h1>
...
#查看自动生成的匿名卷
[root@docker ~]# docker volume ls
DRIVER VOLUME NAME
local 5bef99058cc79351f9145be0851044d6f1a832bf7cd2263f68cb03191b3662aa
#查看自动生成的匿名卷详细信息
[root@docker ~]# docker volume inspect 5bef99058cc79351f9145be0851044d6f1a832bf7cd2263f68cb03191b3662aa
[
{
"CreatedAt": "2022-01-31T03:40:01+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/5bef99058cc79351f9145be0851044d6f1a832bf7cd2263f68cb03191b3662aa/_data",
"Name": "5bef99058cc79351f9145be0851044d6f1a832bf7cd2263f68cb03191b3662aa",
"Options": null,
"Scope": "local"
}
]
[root@docker ~]# docker inspect --format="{{.Mounts}}" nginx01
[{volume 5bef99058cc79351f9145be0851044d6f1a832bf7cd2263f68cb03191b3662aa /var/lib/docker/volumes/5bef99058cc79351f9145be0851044d6f1a832bf7cd2263f68cb03191b3662aa/_data /usr/share/nginx/html local true }]
#查看匿名卷的文件
[root@docker ~]# ls /var/lib/docker/volumes/5bef99058cc79351f9145be0851044d6f1a832bf7cd2263f68cb03191b3662aa/_data
50x.html index.html
#修改宿主机匿名卷的内容
[root@docker ~]# echo 'test page v1' > /var/lib/docker/volumes/5bef99058cc79351f9145be0851044d6f1a832bf7cd2263f68cb03191b3662aa/_data/index.html
#测试访问
[root@docker ~]# curl 10.0.0.8
test page v1
#删除容器
[root@docker ~]# docker rm -f nginx01
nginx01
#删除容器后匿名卷不会随之删除
[root@docker ~]# docker volume ls
DRIVER VOLUME NAME
local 5bef99058cc79351f9145be0851044d6f1a832bf7cd2263f68cb03191b3662aa
#手动删除匿名卷
[root@docker ~]# docker volume rm 5bef99058cc79351f9145be0851044d6f1a832bf7cd2263f68cb03191b3662aa
5bef99058cc79351f9145be0851044d6f1a832bf7cd2263f68cb03191b3662aa命名数据卷
nginx实现命名数据卷
#实现命名数据卷方式一
#命名数据卷直接创建容器
[root@docker ~]# docker run -d -p 80:80 --name nginx01 -v vol1:/usr/share/nginx/html nginx:latest
#实现命名数据卷方式二
#先创建命名数据卷
[root@docker ~]# docker volume create vol1
vol1
#通过创建的命名数据卷创建容器
[root@docker ~]# docker run -d -p 80:80 --name nginx01 -v vol1:/usr/share/nginx/html nginx:latest
#查看创建的命名数据卷
[root@docker ~]# docker volume inspect vol1
[
{
"CreatedAt": "2022-01-31T04:03:53+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/vol1/_data",
"Name": "vol1",
"Options": {},
"Scope": "local"
}
]
[root@docker ~]# docker inspect --format="{{.Mounts}}" nginx01
[{volume vol1 /var/lib/docker/volumes/vol1/_data /usr/share/nginx/html local z true }]
#查看命名数据卷文件
[root@docker ~]# ls /var/lib/docker/volumes/vol1/_data/
50x.html index.html
[root@docker ~]# cat /var/lib/docker/volumes/vol1/_data/index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
#修改命名数据卷文件
[root@docker ~]# echo 'test page v1' > /var/lib/docker/volumes/vol1/_data/index.html
#测试访问
[root@docker ~]# curl 10.0.0.8
test page v1
#利用现在的命名数据卷创建新容器,可以实现和原有容器共用一个数据卷
[root@docker ~]# docker run -d -p 81:80 --name nginx02 -v vol1:/usr/share/nginx/html nginx:latest
41268a0883a4dc22f5f251b80a4e3b9ed511a5d3fdec5456d34dbcd06ae60b19
[root@docker ~]# curl 10.0.0.8:81
test page v1
#删除命名数据卷
#删除指定的命名数据卷
[root@docker ~]# docker volume rm vol1
#清理全部不再使用的卷(-f表示强制 非交互操作)
[root@docker ~]# docker volume prune -f数据卷容器
数据卷容器前言
- 数据卷容器是将宿主机的目录挂载至一个专门的数据卷容器,然后让其他容器通过数据卷容器读写宿主机的数据,此方式不常用
- 在Dockerfile中创建的是匿名数据卷,无法实现多个容器间的数据共享
- 数据卷容器最大的作用是让数据在多个docker容器内共享
- 相当于先要创建一个后台运行的容器作为 server,用于提供数据卷,这个卷可以为其他容器提供数据存储服务,其他使用此卷的容器作为client端
- 关闭数据卷容器后,仍然可以基于关闭的数据卷容器创建新容器及访问旧的client容器(因为虽然数据卷容器关闭,但是挂载关系在宿主机仍然存在)只要数据卷容器在就可以
- 删除数据卷容器后,旧的client容器仍能访问,但无法再创建新的client容器
- 重新创建容器卷容器后,还可以继续创建client容器
- 数据卷容器的 Server 和 Client 可以不使用同一个镜像生成
数据卷容器的使用
- 启动容器时指定数据卷容器
docker run --volumes-from <数据卷容器>数据卷容器实战案例
准备数据
[root@docker ~]# mkdir -p /data/docker/nginx/html/
[root@docker ~]# echo 'test page on volume-server v1' > /data/docker/nginx/html/index.html
[root@docker ~]# cat /data/docker/nginx/html/index.html
test page on volume-server v1创建一个数据卷容器 Server
- 并挂载宿主机的数据目录,容器无需启动也可以,数据卷容器一般无需端口映射
[root@docker ~]# docker run -d --name volume-server -v /data/docker/nginx/html/:/usr/share/nginx/html nginx:latest 启动多个数据卷容器 Client
[root@docker ~]# docker run --name client1 -d -p 80:80 --volumes-from volume-server nginx:latest
e37c3ee2cfecfb82713927df02c09aa32306b8cc5207d230a602f51781b5a05b
[root@docker ~]# docker run --name client2 -d -p 81:80 --volumes-from volume-server nginx:latest
c279c85ffa6a3d1b1e1fda6da1e2d4043aa71caabe91e8fed6c57561690a76a7
[root@docker ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c279c85ffa6a nginx:latest "/docker-entrypoint.…" 3 seconds ago Up 1 second 0.0.0.0:81->80/tcp, :::81->80/tcp client2
e37c3ee2cfec nginx:latest "/docker-entrypoint.…" 10 seconds ago Up 8 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp client1
011bef7c757b nginx:latest "/docker-entrypoint.…" 7 minutes ago Up 7 minutes 80/tcp volume-server测试访问
[root@docker ~]# curl 10.0.0.8
test page on volume-server v1
[root@docker ~]# curl 10.0.0.8:81
test page on volume-server v1进入容器测试读写
- 读写权限依赖于源数据卷Server容器
#进入 Server 容器修改数据
[root@docker ~]# docker exec -it volume-server bash
root@011bef7c757b:/# echo 'test page v1' > /usr/share/nginx/html/index.html
root@011bef7c757b:/# curl 10.0.0.8
test page v1
#进入 Client 容器修改数据
[root@docker ~]# docker exec -it client1 bash
root@e37c3ee2cfec:/# echo 'test page v2' > /usr/share/nginx/html/index.html
root@e37c3ee2cfec:/# curl 10.0.0.8
test page v2宿主机测试读写
[root@docker ~]# echo 'test page v3' > /data/docker/nginx/html/index.html
[root@docker ~]# curl 10.0.0.8
test page v3关闭容器卷server测试
- **结论:**关闭数据卷容器后,仍然可以基于关闭的数据卷容器创建新容器及访问旧的client容器(因为虽然数据卷容器关闭,但是挂载关系在宿主机仍然存在)
[root@docker ~]# docker run --name client3 -d -p 82:80 --volumes-from volume-server nginx:latest
891a6aba9a64227e639c7c295bef7450b1c7f9c0827614629ba37f66a33ba2c0
[root@docker ~]# curl 10.0.0.8:82
test page v3
[root@docker ~]# curl 10.0.0.8:81
test page v3
[root@docker ~]# curl 10.0.0.8
test page v3
[root@docker ~]# echo 'test page v6' > /data/docker/nginx/html/index.html
[root@docker ~]# curl 10.0.0.8
test page v6
[root@docker ~]# curl 10.0.0.8:81
test page v6
[root@docker ~]# curl 10.0.0.8:82
test page v6删除容器卷server测试
- **结论:**删除数据卷容器后,旧的client容器仍能访问,但无法再创建新的client容器
[root@docker ~]# docker rm -fv volume-server
volume-server
[root@docker ~]# curl 10.0.0.8
test page v6
[root@docker ~]# curl 10.0.0.8:81
test page v6
[root@docker ~]# curl 10.0.0.8:82
test page v6
[root@docker ~]# docker run --name client4 -d -p 83:80 --volumes-from volume-server nginx:latest
docker: Error response from daemon: No such container: volume-server.
See 'docker run --help'.重新创建容器卷server测试
- 重新创建容器卷容器后,还可以继续创建client容器
[root@docker ~]# docker run --name client4 -d -p 83:80 --volumes-from volume-server nginx:latest
docker: Error response from daemon: No such container: volume-server.
See 'docker run --help'.
[root@docker ~]# docker run -d --name volume-server -v /data/docker/nginx/html/:/usr/share/nginx/html nginx:latest
f98c630cd07873cb71eea3bb095d790e3d22370f65f199a8624d40c63597c06b
[root@docker ~]# docker run --name client4 -d -p 83:80 --volumes-from volume-server nginx:latest
c1726679bcce5369b1351b45304bf2f7d16775e410791071efce0e1b671a79ca
[root@docker ~]# curl 10.0.0.8:83
test page v6利用数据卷容器备份指定容器的数据卷
- 由于匿名数据卷在宿主机中的存储位置不确定,所以为了方便的备份匿名数据卷,可以利用数据卷容器实现数据卷的备份
备份还原概述
#说明
[container name] #表示需要备份|还原的容器
[container data volume] #表示容器内的需要备份|还原的数据卷对应的目录
$(pwd) #为宿主机映射的目录,充当备份目录
#在执行备份命令容器上 执行备份的方式
docker run -it --rm --volumes-from [container name] -v $(pwd):/backup ubuntu
#tar cvf /backup/backup.tar [container data volume]
#还原方式
docker run -it --rm --volumes-from [container name] -v $(pwd):/backup ubuntu
#tar xvf /backup/backup.tar -C [container data volume]范例:
...清理数据卷
- Docker 在长时间使用的情况下,经常需要删除旧的容器并创建新的容器,长此以往,Docker 的数据卷 volumes 会产生了非常多的僵尸文件,而这些将是文件大都是未绑定容器的目录
查询
# 查看 Docker 的磁盘使用情况
# docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 18 1 902.2MB 902.2MB (99%)
Containers 1 1 0B 0B
Local Volumes 3 0 418.6MB 418.6MB (100%)
Build Cache 0 0 0B 0B
# Local Volumes 本质上是查询的此目录的使用量
# du -sh /var/lib/docker/volumes
400M /var/lib/docker/volumes
# 查询可回收的数据卷
# docker volume ls -qf dangling=true
2126f494f73e95becd33ae0a420e3d7149002f6a71040a6628946e00bdfd8115
bb18b9a989b5f01b9751b8e7279d5533fd794f63be94042d5a8512b269e9ae98
dc93a67b68099d2ee43f40c8bf624a47753f69caf857854ced90ecd24209394f
# 本质上也是查询的此目录下的内容
# ls /var/lib/docker/volumes
2126f494f73e95becd33ae0a420e3d7149002f6a71040a6628946e00bdfd8115
backingFsBlockDev
bb18b9a989b5f01b9751b8e7279d5533fd794f63be94042d5a8512b269e9ae98
dc93a67b68099d2ee43f40c8bf624a47753f69caf857854ced90ecd24209394f
metadata.db清理
# 删除所有dangling数据卷(即无用的Volume,僵尸文件)
# docker volume rm $(docker volume ls -qf dangling=true)
2126f494f73e95becd33ae0a420e3d7149002f6a71040a6628946e00bdfd8115
bb18b9a989b5f01b9751b8e7279d5533fd794f63be94042d5a8512b269e9ae98
dc93a67b68099d2ee43f40c8bf624a47753f69caf857854ced90ecd24209394f
# ls /var/lib/docker/volumes
backingFsBlockDev metadata.db
---
# 删除所有dangling镜像(即无tag的镜像)
# docker rmi $(docker images | grep "^<none>" | awk "{print $3}")
# 删除所有关闭的容器
# docker ps -a | grep Exit | cut -d ' ' -f 1 | xargs docker rm删除关闭的容器、无用的数据卷和网络,以及dangling镜像(即无tag的镜像)
- 注意,所有关闭的容器都会被删除,请核查是否存在关闭运行但是需要保留的容器
# 删除关闭的容器、无用的数据卷和网络
# docker system prune
# 删除更彻底,可以将没有容器使用Docker镜像都删掉
# docker system prune -aDocker 相关文件
/var/lib/docker/overlay2/
- 此目录中存放镜像、容器等相关文件
- 此目录还存储驱动相关的文件,如果将其下的内容全部删除将无法拉取镜像
# 没有镜像和容器时次目录占用的空间很小
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
root@gitlab-server:~# du -sh /var/lib/docker/overlay2/
8.0K /var/lib/docker/overlay2/存在镜像时占用的空间
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx 1.23 3964ce7b8458 12 hours ago 142MB
# du -sh /var/lib/docker/overlay2/
150M /var/lib/docker/overlay2/
# 由于镜像本身存在多层,因此每层会对应一个目录
# ll /var/lib/docker/overlay2/
total 36
drwx--x--- 9 root root 4096 Dec 14 21:49 ./
drwx--x--- 13 root root 4096 Dec 14 17:57 ../
drwx--x--- 4 root root 4096 Dec 14 21:49 431b57061d42f526085599cee1e2e0d3a469d67679410ea0c2a3d7f1960f3b36/
drwx--x--- 4 root root 4096 Dec 14 21:49 44a0f9233d0165e521c43c32cc08d747a2becfc0efbfcfe6ecd44ee7b4aac09c/
drwx--x--- 4 root root 4096 Dec 14 21:49 58740eec574cd2e7ab620df0cccfa7a1c94cf0b2d5de8182547787712c8551d0/
drwx--x--- 3 root root 4096 Dec 14 21:49 5a6d1455634f092cede6488935884d64276fe8f7aeff2e73d8c8d8e18005a1ef/
drwx--x--- 4 root root 4096 Dec 14 21:49 98d2ab81673615b7cf8aeb797dd146d518eb0bfde0d09df27c1b20e9f33cf8c0/
drwx--x--- 4 root root 4096 Dec 14 21:49 a5370a8bc35cb3eea7a9f8f61f7288a55b57b01d4dcb37c3d855dd2a81a606ed/
drwx------ 2 root root 4096 Dec 14 21:49 l/
# ll /var/lib/docker/overlay2/ | wc -l
10存在镜像并创建容器时的空间
# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx 1.23 3964ce7b8458 13 hours ago 142MB
# du -sh /var/lib/docker/overlay2/
150M /var/lib/docker/overlay2/
# ll /var/lib/docker/overlay2/ | wc -l
10
---
# 创建容器后就相当于把现有的镜像相关文件复制了一份,所以会加上源镜像的大小
# docker run -d --name nginx01 nginx:1.23
92181b473faa70946f73f29393a56845ba0dc2582eacc03ab46248e0683fd0d7
# ll /var/lib/docker/overlay2/ | wc -l
12
# du -sh /var/lib/docker/overlay2/
298M /var/lib/docker/overlay2/
# 新生成的文件
# ll /var/lib/docker/overlay2/ -t
total 44
drwx--x--- 5 root root 4096 Dec 14 21:53 1022605727bbe7994588c886ae5c9d00d42324ea8570f705025c1c3e43703497/
drwx--x--- 11 root root 4096 Dec 14 21:53 ./
drwx--x--- 4 root root 4096 Dec 14 21:53 1022605727bbe7994588c886ae5c9d00d42324ea8570f705025c1c3e43703497-init/
...
# 再次创建新的容器后
# docker run -d --name nginx02 nginx:1.23
33639bb998a4eb4ba40993b3ab4eef58a2c12fe3dc908cffed2b7d3303743282
# du -sh /var/lib/docker/overlay2/
445M /var/lib/docker/overlay2/
# ll /var/lib/docker/overlay2/ | wc -l
14
# ll /var/lib/docker/overlay2/ -t
total 52
drwx--x--- 5 root root 4096 Dec 14 21:57 8feabaa39bf21ae33dd47e73d8a0f6a5370f927d717c62d80f1652097de7e028/
drwx--x--- 4 root root 4096 Dec 14 21:57 8feabaa39bf21ae33dd47e73d8a0f6a5370f927d717c62d80f1652097de7e028-init/
.../var/lib/docker/containers/
- 存放容器的配置信息
容器数据持久化 -v
使用 -v jenkins_home:/var/jenkins_home 的方式运行容器时,jenkins_home 是一个 Docker 卷(volume),用于将主机文件系统上的数据挂载到容器中的某个目录(在这里是 /var/jenkins_home)。这种卷有以下几种不同的行为:
1. Docker 自动创建的卷
如果你使用了 -v jenkins_home:/var/jenkins_home,但没有在主机上显式定义 jenkins_home 目录,Docker 会自动为你创建一个名为 jenkins_home 的卷。这个卷是由 Docker 自行管理的,存储在 Docker 容器引擎的默认路径中,通常是在以下路径:
- Linux/MacOS:
/var/lib/docker/volumes/ - Windows: 在 Docker for Windows 的虚拟机中
在这个路径中,你会看到一个以卷名 jenkins_home 命名的目录。这个目录中的内容会被挂载到容器内的 /var/jenkins_home,以便 Jenkins 在容器中运行时可以持久化数据。
2. 命名卷的好处
使用命名卷(比如 jenkins_home)的主要好处是,数据会被持久化,即使容器被删除,卷中的数据不会丢失。你可以重新启动或者创建新的容器,并挂载同样的卷,数据依然会保留。
3. 主机路径 vs 卷
如果你想要明确将主机上的某个目录与容器中的目录绑定,应该使用绝对路径,比如:
-v /path/on/host:/var/jenkins_home这样,/path/on/host 就会对应容器内的 /var/jenkins_home 目录。
总结来说,-v jenkins_home:/var/jenkins_home 这种方式意味着 Docker 会在其默认的卷存储路径中创建或使用名为 jenkins_home 的卷,且这个卷中的数据会映射到容器内的 /var/jenkins_home。
—
docker run -v 命令用于运行 Docker 容器。它允许你指定各种选项和参数来自定义容器的行为。其中一个重要的选项是 -v 标志,用于挂载容器的卷。
docker run -v 实现的是持久卷(Persistent Volume)而不是临时卷(Temporary Volume)。
通过使用 -v 或 --volume 选项,你可以将主机上的一个目录或文件挂载到容器中,从而实现数据在容器和主机之间的持久性存储。这意味着即使容器停止运行或被删除,挂载的卷仍然存在于主机上,数据不会丢失。
持久卷对于许多应用场景非常有用,例如数据库持久化存储、日志文件存储和配置文件的共享等。它允许容器与主机之间进行数据交换,并且在容器的生命周期中保持数据的一致性和持久性。
-v 选项说明
-v <主机路径>:<容器路径>[:<选项>]
<主机路径>:这指定了你想要从主机机器上挂载卷的路径。它可以是绝对路径或相对路径。<容器路径>:这指定了你想要将卷挂载到容器内的路径。它也可以是绝对路径或相对路径。容器将能够访问该位置上的文件和目录。<选项>(可选):这个字段允许你指定卷挂载的附加选项。一些常见的选项包括只读 (ro),允许容器读取卷但不修改它,以及指定特定的卷驱动程序。
范例
# 将主机上的目录挂载到容器:
docker run -v /主机路径:/容器路径 ...
# 将主机的当前工作目录挂载到容器:
docker run -v $(pwd):/容器路径 ...
# 只读挂载卷:
docker run -v /主机路径:/容器路径:ro ...
# 使用命名卷(用逗号分隔的键值对提供选项):
docker run -v 卷名称:/容器路径:选项1=值1,选项2=值2 ...