Files
2025-08-27 14:13:17 +08:00

315 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 05.Docker数据管理
## 05.Docker数据管理
### 1. Docker数据管理
### 2. 数据管理
#### 2.1 UnionFS联合文件系统
Docker 中的 UnionFS联合文件系统是一种分层、轻量级且高性能的文件系统其核心功能是将多个目录内容联合挂载到同一个目录下形成一个统一的文件系统视图。以下是其主要特点和作用
##### 2.1.1 核心特点
- **分层存储**UnionFS 可以将不同层次的文件和目录合并成单一的目录树。在 Docker 中,每一层可以代表一个镜像层,最低层通常是只读的基础镜像层,上面的层是可写的容器层。
- **写时复制Copy-on-Write, CoW**当容器需要修改一个文件时UnionFS 不会直接更改底层只读镜像中的文件,而是将更改写入到一个可写的上层。这样,原始镜像层保持不变,可以被多个容器共享。
- **隔离性**:由于底层的只读镜像层保持不变,每个容器看到的是一个完整的文件系统视图,包括它们的改动,但这些改动仅存在于它们自己的可写层中。
- **性能优化**UnionFS 通过仅对发生更改的文件进行操作,以及通过页面缓存共享等机制,减少了磁盘 I/O 操作,加快了容器启动时间。
##### 2.1.2 在 Docker 中的应用
- **镜像分层**Docker 镜像是由多个只读层叠加而成的每一层都代表了镜像构建过程中的一个状态。UnionFS 将这些层联合起来,形成一个完整的文件系统。
- **容器运行时的文件系统**当启动一个容器时Docker 会在镜像的最顶层添加一个新的可写层。容器的所有写操作都在这个可写层中进行,而不会影响到镜像的只读层。
- **镜像继承与共享**:由于镜像的分层结构,新的镜像可以基于已有的镜像构建,只需添加新的层即可。同时,多个容器可以共享同一个镜像的只读层,极大地节省了磁盘空间。
##### 2.1.3 常见的 UnionFS 实现
虽然 UnionFS 是一个理念,但 Linux 内核中有多种具体的实现方式Docker 默认使用的是 Overlay2。Overlay2 是一种先进的联合文件系统实现,具有更好的性能和一些先进的功能,如页面缓存共享。
#### 2.2 镜像层与可写层
在 Docker 中镜像层和容器可写层是基于联合文件系统UnionFS实现的关键概念。它们共同构成了容器的文件系统结构使得 Docker 能够高效地管理和运行容器。以下是对镜像层和容器可写层的详细解释:
##### 2.2.1 一、镜像层
镜像层是 Docker 镜像的组成部分,每个镜像由多个只读层组成,这些层是不可修改的。
1. **镜像层的生成**
- 当你构建一个 Docker 镜像时Dockerfile 中的每一条指令(如 `RUN``COPY``ADD` 等)都会生成一个新的镜像层。
- 例如,一个简单的 Dockerfile 如下:
```dockerfile
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y nginx
COPY ./my_nginx.conf /etc/nginx/nginx.conf
```
- 第一层是基础镜像层,如 `ubuntu:20.04`,这是从 Docker Hub 拉取的预构建镜像。
- 第二层是通过 `RUN` 指令安装 Nginx 生成的层,包含了安装过程中产生的所有文件和目录变化。
- 第三层是通过 `COPY` 指令将本地的 `my_nginx.conf` 文件复制到镜像中的 `/etc/nginx/nginx.conf` 位置生成的层。
- 每一层都是基于前一层构建的,新的层只包含与前一层的差异部分。
2. **镜像层的特点**
- **只读性**:镜像层是只读的,一旦创建就不能修改。如果需要修改镜像层中的内容,必须通过构建新的镜像层来实现。
- **共享性**:多个容器可以共享同一个镜像层,因为镜像层是不可变的,这大大节省了磁盘空间。
- **不可变性**:镜像层的不可变性保证了镜像的一致性和可重复性,无论在什么环境下运行,基于同一镜像启动的容器都具有相同的文件系统结构。
3. **镜像层的作用**
- **构建高效**通过分层构建Docker 可以缓存中间层,避免重复构建相同的操作。例如,如果基础镜像层和安装 Nginx 的层已经构建过,再次构建时可以直接使用缓存的层,而不是重新执行命令。
- **节省空间**多个容器可以共享镜像层减少了磁盘空间的占用。例如10 个基于同一个基础镜像的容器,只需要存储一份基础镜像层,而不是每份都单独存储。
- **便于分发**:镜像层的结构使得镜像可以方便地分发和共享。用户可以从 Docker Hub 等镜像仓库拉取镜像,而无需关心镜像的具体构建过程。
##### 2.2.2 二、容器可写层
容器可写层是容器运行时特有的一个层,它是可写的,用于存储容器运行时产生的所有变更。
1. **容器可写层的生成**
- 当你启动一个容器时Docker 会在镜像的最顶层添加一个新的可写层。这个可写层是容器独有的,用于存储容器运行时的文件系统变更。
- 例如,如果你在容器中创建了一个新文件、修改了一个现有文件或删除了一个文件,这些操作都会反映在容器可写层中,而不会影响到镜像层。
2. **容器可写层的特点**
- **可写性**:容器可写层是可写的,容器运行时的所有文件系统操作(如创建、修改、删除文件)都在这个层中进行。
- **独立性**:每个容器都有自己的可写层,容器之间的可写层是隔离的。一个容器的变更不会影响到其他容器。
- **临时性**:容器可写层的内容在容器被删除时也会被删除。如果需要持久化数据,需要将数据存储在外部存储(如卷)中。
3. **容器可写层的作用**
- **隔离性**:容器可写层保证了容器之间的隔离性。每个容器在自己的可写层中进行操作,不会影响到其他容器或镜像层。
- **灵活性**:容器可写层允许容器在运行时动态地修改文件系统,而不会影响到镜像的原始状态。这使得容器可以灵活地运行各种应用程序。
- **数据持久化**虽然容器可写层的内容在容器删除时会被删除但你可以通过挂载卷Volumes将数据持久化到宿主机或其他存储设备中从而实现数据的持久化存储。
##### 2.2.3 三、镜像层与容器可写层的关系
镜像层和容器可写层通过联合文件系统UnionFS联合起来形成了容器的完整文件系统视图。
1. **联合挂载**
- Docker 使用联合文件系统将镜像层和容器可写层联合挂载到同一个目录下。从容器的角度来看,它看到的是一个完整的文件系统,包含了镜像层和容器可写层的内容。
- 例如当你在容器中访问一个文件时Docker 会先在容器可写层中查找该文件。如果找不到,就会在镜像层中查找。如果在镜像层中找到了文件,就会将该文件的内容返回给容器。
2. **写时复制Copy-on-Write**
- 当容器需要修改一个文件时Docker 会使用写时复制机制。具体来说Docker 会将文件从镜像层复制到容器可写层,然后在可写层中进行修改。这样,镜像层保持不变,而容器可写层存储了修改后的文件。
- 例如,假设镜像层中有一个文件 `/etc/nginx/nginx.conf`容器需要修改这个文件。Docker 会将该文件从镜像层复制到容器可写层,然后在可写层中进行修改。从容器的角度来看,它看到的是修改后的文件,而镜像层中的文件保持不变。
3. **删除操作**
- 当容器删除一个文件时Docker 会在容器可写层中记录一个删除操作,而不是真正删除镜像层中的文件。这样,镜像层保持不变,而容器可写层记录了文件的删除状态。
- 例如,假设容器删除了一个文件 `/etc/nginx/nginx.conf`Docker 会在容器可写层中记录一个删除标记。从容器的角度来看,该文件已经被删除,而镜像层中的文件仍然存在。
##### 2.2.4 总结
- **镜像层**:是 Docker 镜像的组成部分,是只读的、不可变的,多个容器可以共享镜像层。镜像层通过分层构建,提高了构建效率、节省了磁盘空间,并保证了镜像的一致性和可重复性。
- **容器可写层**:是容器运行时的可写层,用于存储容器运行时的文件系统变更。容器可写层是独立的、可写的、临时的,保证了容器之间的隔离性和运行时的灵活性。
- **联合文件系统UnionFS**将镜像层和容器可写层联合挂载形成了容器的完整文件系统视图。通过写时复制机制Docker 在不影响镜像层的情况下,允许容器在可写层中进行文件系统操作。
#### 2.3 查看镜像的详细信息
##### 2.3.1 一、查看镜像数据信息
```bash
[root@localhost nginx]# docker inspect nginx_games:v1
"Architecture": "amd64",
"Os": "linux",
"Size": 685997316,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/ilm8ifbi2gcrqg156w7kbu22f/diff:/var/lib/docker/overlay2/u51fr5eul9csiwghxmnm1acsi/diff:/var/lib/docker/overlay2/lo6sgoxbliga9l2qqqxcu2tvp/diff:/var/lib/docker/overlay2/bi4orr0yujicfufabnw7u8tym/diff:/var/lib/docker/overlay2/e51d5c1c74a99e1b1e41980bb9270fcba562976e40ae7dbf65ff585689a783d2/diff",
"MergedDir": "/var/lib/docker/overlay2/talgtlohecyw3kt8d8idpzw4g/merged",
"UpperDir": "/var/lib/docker/overlay2/talgtlohecyw3kt8d8idpzw4g/diff",
"WorkDir": "/var/lib/docker/overlay2/talgtlohecyw3kt8d8idpzw4g/work"
LoweDirimage镜像层本身只读
UpperDir容器的上层读写层
MergeDir容器的文件系统使用Union FS(联合文件系统)将镜像层和容器层合并给容器使用
WorkDir容器在宿主机的工作目录
```
##### 2.3.2 二、查看镜像层构建过程
```bash
[root@localhost nginx]# docker history nginx_games:v1
IMAGE CREATED CREATED BY SIZE COMMENT
9c86d96dfba0 25 hours ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0
<missing> 25 hours ago EXPOSE map[443/tcp:{} 80/tcp:{}] 0B buildkit.dockerfile.v0
<missing> 25 hours ago ADD games.tar.gz /usr/local/nginx/html/ # bu… 164MB buildkit.dockerfile.v0
<missing> 25 hours ago RUN /bin/sh -c useradd -s /sbin/nologin ngin… 297kB buildkit.dockerfile.v0
<missing> 25 hours ago RUN /bin/sh -c cd /usr/local/src/nginx-1.22.… 16.6MB buildkit.dockerfile.v0
<missing> 25 hours ago ADD nginx-1.22.0.tar.gz /usr/local/src/ # bu… 6.46MB buildkit.dockerfile.v0
<missing> 25 hours ago RUN /bin/sh -c yum install -y vim wget unzip… 323MB buildkit.dockerfile.v0
<missing> 25 hours ago MAINTAINER Eagle_nls 2898485992@qq.com 0B buildkit.dockerfile.v0
<missing> 16 months ago CMD ["/bin/bash"] 0B buildkit.dockerfile.v0
<missing> 16 months ago ADD layer.tar.xz / # buildkit 176MB buildkit.dockerfile.v0
```
如果想要缩小所构建镜像的大小,我们可以尝试少创建镜像层,在一层中多做一些事情,尽可能减少**RUN命令**
### 3. 数据卷
#### 3.1 什么是数据卷?
数据卷实际上就是宿主机上的目录或者是文件可以被直接mount到容器当中使用。
实际生产环境中,需要针对不同类型的服务、不同类型的数据存储要求做相应的规划,最终保证服务的可扩展性、稳定性以及数据的安全性。
![img](05.Docker数据管理/u=1718952600,4159508250&fm=26&fmt=auto&gp=0.jpg)
#### 3.2 数据卷案例
- 创建目录并准备页面
```bash
[root@docker-server1 ~]# mkdir -p /data/web
[root@docker-server1 ~]# echo 'eaglslab nginx test!' > /data/web/index.html
[root@docker-server1 ~]# cat /data/web/index.html
eaglslab nginx test
```
- 启动两个容器并验证数据
```bash
[root@docker-server1 ~]# docker run -d -it --name web1 -v /data/web/:/usr/share/nginx/html/ -p 8080:80 nginx
588c494dc9098e0a43e15bce3162c34676dd981609edc32d46bf4beb59b9cf19
[root@docker-server1 ~]# docker run -d -it --name web2 -v /data/web/:/usr/share/nginx/html/ -p 8081:80 nginx
ff6b3731a9ba3e0f91d2c8d89bb6573eb5e5a9b840163bc1122a9e5678d108b7
[root@docker-server1 ~]# curl 192.168.175.10:8080
eaglslab nginx test!
[root@docker-server1 ~]# curl 192.168.175.10:8081
eaglslab nginx test!
[root@docker-server1 ~]# echo 'hello world!' > /data/web/index.html
[root@docker-server1 ~]# curl 192.168.175.10:8080
hello world!
[root@docker-server1 ~]# curl 192.168.175.10:8081
hello world!
```
- 进入到容器内测试写入数据
```bash
[root@docker-server1 ~]# docker exec -it web1 bash
root@588c494dc909:/# echo 'docker test!' > /usr/share/nginx/html/index.html
[root@docker-server1 ~]# curl 192.168.175.10:8080
docker test!
[root@docker-server1 ~]# curl 192.168.175.10:8081
docker test!
```
- 尝试只读挂载
```bash
# 删除容器的时候不会删除宿主机的目录
[root@docker-server1 ~]# docker rm -fv web1
web1
[root@docker-server1 ~]# docker rm -fv web2
web2
[root@docker-server1 ~]# cat /data/web/index.html
docker test!
# 通过只读方式挂载以后,在容器内部是不允许修改数据的
[root@docker-server1 ~]# docker run -d -it --name web1 -v /data/web/:/usr/share/nginx/html/:ro -p 8080:80 nginx
a395b27958ca0cdcf52a86bd17813dcbcda4ed774895adcc99e85fc114ab84ff
[root@docker-server1 ~]# docker exec -it web1 bash
root@a395b27958ca:/# echo 123 > /usr/share/nginx/html/index.html
bash: /usr/share/nginx/html/index.html: Read-only file system
```
- 文件挂载
```bash
[root@docker-server1 ~]# docker run -d -it --name web2 -v /data/web/index.html:/usr/share/nginx/html/index.html:ro -p 8081:80 nginx
4b34c957372d314cdb0f85d7e2c65b095615adfe3051ae6b4266b7bacd50f374
[root@docker-server1 ~]# curl 192.168.175.10:8081
docker test!
```
#### 3.3 数据卷特点
1. 数据卷是宿主机的目录或者文件,并且可以在**多个容器之间共同使用**
2. 在宿主机对数据卷更改数据后会在所有容器里面会**立即更新**
3. 数据卷的数据可以**持久保存**,即使删除使用该数据卷卷的容器也不影响
4. 在容器里面写入数据不会影响到镜像本身(**隔离性**)。
5. 需要挂载多个目录或者文件的时候可以使用多个-v参数指定
6. 数据卷使用场景包括日志输出、静态web页面、应用配置文件、多容器间目录或文件共享
#### 3.4 数据卷容器
##### 3.4.1 什么是数据卷容器
数据卷容器是一个普通的容器,专门用于提供数据卷供其他容器挂载。它允许用户创建一个专门用于数据存储的容器,并将其数据卷挂载到其他容器中。数据卷容器的主要用途是将数据卷的生命周期与应用容器分离,使数据可以在容器之间共享和持久化。
##### 3.4.2 数据卷容器的用途
1. **数据持久化**:数据卷容器可以确保数据即使在容器被删除后也不会丢失。例如,对于数据库应用,数据卷容器可以持久化数据库文件,避免因容器删除而导致数据丢失。
2. **数据共享**:多个容器可以通过挂载同一个数据卷容器来共享数据。这在多容器应用中非常有用,例如在微服务架构中,多个服务容器可以共享数据库数据。
3. **备份与恢复**:数据卷容器可以用于备份和恢复数据。通过将数据卷容器中的数据卷备份到宿主机或其他存储设备,可以在需要时恢复数据。
4. **迁移数据**:数据卷容器可以方便地迁移数据。通过将数据卷容器中的数据卷迁移到其他主机,可以快速实现数据的迁移。
##### 3.4.3 数据卷容器与本地数据挂载的区别
| 特性 | 数据卷容器 | 本地数据挂载 |
| :--------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- |
| **目录来源** | Docker 自动创建的目录 | 宿主机上已存在的目录 |
| **适用场景** | 数据持久化、容器间共享数据 | 开发调试、快速文件同步 |
| **容器间共享** | 支持 | 不支持 |
| **宿主机文件系统依赖** | 无依赖 | 依赖宿主机文件系统 |
| **数据持久化** | 数据卷的生命周期独立于容器,即使容器被删除,数据卷仍然存在 | 如果容器被删除,挂载的目录和文件仍然存在,但需要手动管理 |
| **数据初始化** | 如果宿主机没有对应的文件,数据卷容器会自动将容器运行所需的文件复制到数据卷中 | 如果宿主机没有对应的文件,容器可能会因缺少运行所需的文件而出错 |
| **数据覆盖** | 数据卷中的数据会覆盖容器内的数据 | 容器内的数据会覆盖宿主机上的数据 |
##### 3.4.4 实例演示
- 先启动一个卷容器Server
```bash
[root@docker-server1 ~]# docker run -d --name nginx-web -v /data/web/:/usr/share/nginx/html/:ro -p 8081:80 nginx
```
- 启动两个客户端容器
```bash
[root@docker-server1 ~]# docker run -d --name web1 -p 8082:80 --volumes-from nginx-web nginx:latest
ac22faa405ec07c065042465cd7f9d456be891effdd5d13d9571b96ef9c550f7
[root@docker-server1 ~]# docker run -d --name web2 -p 8083:80 --volumes-from nginx-web nginx:latest
e084845475b01dedfdae7362f6fbece7b5ab57ff6289c8c9bf08251f5ba448ed
```
- 访问测试
```bash
[root@docker-server1 ~]# curl 192.168.175.10:8081
docker test!
[root@docker-server1 ~]# curl 192.168.175.10:8082
docker test!
[root@docker-server1 ~]# curl 192.168.175.10:8083
docker test!
```
- 停止卷容器可以创建新容器
```bash
[root@docker-server1 ~]# docker stop nginx-web
nginx-web
[root@docker-server1 ~]# docker run -d -it --name web3 -p 8084:80 --volumes-from nginx-web nginx:latest
6ebd95c132ee1a9e4b43d1849efc628ca7185187a59d70b3816ff16dd47b6e8e
[root@docker-server1 ~]# curl 192.168.175.10:8084
docker test!
```
- 删除卷容器之后不可以再创建新容器
```bash
[root@docker-server1 ~]# docker rm -fv nginx-web
nginx-web
[root@docker-server1 ~]# docker run -d -it --name web4 -p 8085:80 --volumes-from nginx-web nginx:latest
docker: Error response from daemon: No such container: nginx-web.
See 'docker run --help'.
# 但是之前已经创建好的容器不会有任何影响
[root@docker-server1 ~]# curl 192.168.175.10:8082
docker test!
```
##### 3.4.5 总结
在当前环境下即使把提供卷的容器Server删除已经运行的容器Client依然可以使用挂载的卷因为容器是通过挂载的方式访问数据的但是无法创建新的卷容器客户端但是再把卷容器Server创建后即可正常创建卷容器client此方式可以用于线上共享数据目录等环境因为即使数据卷容器被删除了其他已经运行的容器依然可以挂载使用
数据卷容器可以作为共享的方式为其他容器提供文件共享,可以在容器生成中启动一个实例挂载本地的目录,然后其他的容器分别挂载此容器的目录,即可保证各个容器之间的数据一致性。