Files
docker_practice/07_dockerfile/7.8_volume.md
T

249 lines
4.9 KiB
Go
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
## 7.8 VOLUME 定义匿名卷
### 7.8.1 基本语法
```docker
VOLUME ["/路径1", "/路径2"]
VOLUME /路径
```
`VOLUME` 指令创建挂载点并标记为外部挂载的卷
---
### 7.8.2 为什么使用 VOLUME
> **核心原则**容器存储层应该保持无状态任何运行时数据都应该存储在卷中
```mermaid
flowchart LR
subgraph NoVolume ["没有 VOLUME"]
direction TB
subgraph Container1 ["容器存储层"]
direction TB
Files["数据库文件 (问题)<br/>日志文件<br/>上传文件"]
end
Result1["容器删除 = 数据丢失"]
Container1 ~~~ Result1
end
subgraph UseVolume ["使用 VOLUME"]
direction TB
Container2["容器存储层<br/>(只读/无状态)"]
subgraph Volume ["数据卷"]
Data["持久化数据 (安全)"]
end
Container2 --> Volume
Result2["容器删除,数据保留"]
Volume ~~~ Result2
end
```
---
### 7.8.3 基本用法
#### 定义单个卷
```docker
FROM mysql:8.4
VOLUME /var/lib/mysql
```
#### 定义多个卷
```docker
FROM myapp
VOLUME ["/data", "/logs", "/config"]
```
---
### 7.8.4 VOLUME 的行为
#### 1. 自动创建匿名卷
如果运行时未指定挂载Docker 会自动创建匿名卷
```bash
$ docker run mysql:8.4
$ docker volume ls
DRIVER VOLUME NAME
local a1b2c3d4e5f6... # 自动创建的匿名卷
```
#### 2. 可被命名卷覆盖
```bash
## 使用命名卷替代匿名卷
$ docker run -v mysql_data:/var/lib/mysql mysql:8.4
```
#### 3. 可被 Bind Mount 覆盖
```bash
## 使用宿主机目录替代
$ docker run -v /my/data:/var/lib/mysql mysql:8.4
```
---
### 7.8.5 VOLUME 在构建时的特殊行为
> **重要**`VOLUME` 之后再写入该目录的构建语义取决于 builderlegacy builder 会丢弃这些修改BuildKit 会保留但运行容器时一旦该路径挂载了卷卷会遮蔽镜像内同路径的内容
```docker
FROM ubuntu
VOLUME /data
## ⚠️ legacy builder 会丢弃;BuildKit 会保留,但运行时挂载卷会遮蔽它
RUN echo "hello" > /data/test.txt
```
**原因** builder 会在构建过程中为该目录创建临时匿名卷后续写入发生在临时卷中BuildKit 则会把修改保留在镜像层为了避免不同 builder 下出现不同结果也为了避免运行时卷遮蔽镜像内初始化数据不要把必须存在的初始化文件写在 `VOLUME` 之后
#### 正确做法
```docker
FROM ubuntu
## ✅ 先写入文件
RUN mkdir -p /data && echo "hello" > /data/test.txt
## 再声明 VOLUME
VOLUME /data
```
---
### 7.8.6 常见使用场景
#### 数据库持久化
```docker
# 建议使用 postgres:16 或 postgres:latest,具体版本号根据数据库兼容性需求选择
FROM postgres:16
VOLUME /var/lib/postgresql/data
```
#### 日志目录
```docker
FROM nginx
VOLUME /var/log/nginx
```
#### 上传文件目录
```docker
FROM myapp
VOLUME /app/uploads
```
---
### 7.8.7 查看 VOLUME 定义
```bash
## 查看镜像定义的 VOLUME
$ docker inspect mysql:8.4 --format '{{json .Config.Volumes}}' | jq
{
"/var/lib/mysql": {}
}
## 查看容器挂载的卷
$ docker inspect mycontainer --format '{{json .Mounts}}' | jq
```
---
### 7.8.8 VOLUME vs docker run -v
| 特性 | Dockerfile VOLUME | docker run -v |
|------|-------------------|---------------|
| **定义时机** | 镜像构建时 | 容器运行时 |
| **默认行为** | 创建匿名卷 | 可指定命名卷或路径 |
| **灵活性** | (固定路径)| (可任意指定)|
| **适用场景** | 定义必须持久化的路径 | 灵活的数据管理 |
---
### 7.8.9 Compose
Compose 中配置如下
```yaml
services:
db:
# 建议使用 postgres:16 或其他具体版本,避免 latest
image: postgres:16
volumes:
# 命名卷(推荐)
- postgres_data:/var/lib/postgresql/data
# Bind Mount
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
postgres_data: # 声明命名卷
```
---
### 7.8.10 安全注意事项
#### 匿名卷可能导致数据丢失
```bash
## 使用 --rm 运行的容器,匿名卷会在容器删除时一起删除
$ docker run --rm mysql:8.4
## 容器停止后,数据丢失!
...
```
**解决**始终使用命名卷
```bash
$ docker run -v mysql_data:/var/lib/mysql mysql:8.4
```
---
### 7.8.11 最佳实践
#### 1. 定义必须持久化的路径
```docker
## 数据库必须使用卷
FROM postgres:16
VOLUME /var/lib/postgresql/data
```
#### 2. 不要在 VOLUME 后修改目录
```docker
## ❌ 避免
VOLUME /app/data
RUN cp init-data.json /app/data/
## ✅ 正确
RUN mkdir -p /app/data && cp init-data.json /app/data/
VOLUME /app/data
```
#### 3. 文档中说明 VOLUME 用途
```docker
## 持久化用户上传的文件
VOLUME /app/uploads
## 持久化数据库数据
VOLUME /var/lib/mysql
```
---