> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cooree.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker 存储

> 理解 Volume、bind mount 与 tmpfs 的区别,掌握数据持久化与备份恢复实战

## 为什么需要持久化

容器的文件系统是分层叠加的。最上层是可写容器层。容器删除后,容器层一起被删除,数据全部丢失。

<Warning>
  任何写进容器内的数据都不可依赖。数据库、上传文件、日志都必须放进挂载的存储里。
</Warning>

Docker 提供三种方式把数据放到容器层之外:volumes、bind mounts 和 tmpfs。

## 三种挂载方式对比

| 方式         | 数据位置                       | 管理方       | 适用场景           |
| ---------- | -------------------------- | --------- | -------------- |
| Volume     | `/var/lib/docker/volumes/` | Docker 管理 | 数据库、持久化数据,生产首选 |
| Bind mount | 宿主机任意路径                    | 用户自己管理    | 开发环境挂载源码、配置文件  |
| tmpfs      | 宿主机内存,不落盘                  | 临时存在      | 敏感临时数据、高速缓存    |

<Tip>
  不确定选哪种时选 Volume。它由 Docker 统一管理,支持备份、驱动扩展,也不依赖宿主机目录结构。
</Tip>

## volume 命令全表

| 命令                              | 作用                 |
| ------------------------------- | ------------------ |
| `docker volume create my-data`  | 创建 volume          |
| `docker volume ls`              | 列出所有 volume        |
| `docker volume inspect my-data` | 查看 volume 详情与挂载点   |
| `docker volume rm my-data`      | 删除指定 volume        |
| `docker volume prune`           | 删除所有未被容器使用的 volume |

```bash theme={null}
# 创建并检查一个 volume
docker volume create my-data
docker volume inspect my-data
```

## -v 与 --mount 语法对照

两种语法效果相同。`--mount` 更直观,`-v` 更简短。

```bash theme={null}
# -v 语法:volume名:容器路径
docker run -d --name db1 -v my-data:/var/lib/mysql mysql:8

# --mount 语法:字段一一对应,可读性更好
docker run -d --name db2 \
  --mount type=volume,source=my-data,target=/var/lib/mysql \
  mysql:8
```

bind mount 的两种写法:

```bash theme={null}
# -v 语法:宿主机绝对路径:容器路径
docker run -d -v /home/user/app:/app nginx

# --mount 语法
docker run -d \
  --mount type=bind,source=/home/user/app,target=/app \
  nginx
```

<Note>
  使用 `-v` 时,如果 source 是不存在的路径,Docker 会自动创建目录。`--mount` 则会直接报错。这个差异常引发困惑。
</Note>

## bind mount 开发场景:代码热挂载

开发时把本地源码挂进容器。改代码立即生效,不用重新构建镜像。

```bash theme={null}
# 把当前目录挂到容器的 /app
docker run -d --name dev \
  -v $(pwd):/app \
  -p 3000:3000 \
  node:20 \
  node /app/server.js
```

<Steps>
  <Step title="启动容器并挂载源码">
    执行上面的命令。容器里的 `/app` 就是本地项目目录。
  </Step>

  <Step title="在本地编辑器修改代码">
    保存文件。容器内立即看到变化,因为两边是同一个目录。
  </Step>

  <Step title="重启进程生效">
    配合 nodemon 等热重载工具,保存即重启,无需碰镜像。
  </Step>
</Steps>

## volume 备份与恢复

用临时容器把 volume 打包成 tar 文件。

<Steps>
  <Step title="备份">
    ```bash theme={null}
    # 启动临时容器,同时挂载 volume 和当前目录
    # 把 volume 内容打包到当前目录的 backup.tar
    docker run --rm \
      -v my-data:/data \
      -v $(pwd):/backup \
      busybox \
      tar czf /backup/backup.tar -C /data .
    ```
  </Step>

  <Step title="恢复">
    ```bash theme={null}
    # 创建新 volume,把 tar 解包进去
    docker volume create my-data-restored

    docker run --rm \
      -v my-data-restored:/data \
      -v $(pwd):/backup \
      busybox \
      tar xzf /backup/backup.tar -C /data
    ```
  </Step>

  <Step title="验证">
    ```bash theme={null}
    # 查看恢复后的内容
    docker run --rm -v my-data-restored:/data busybox ls /data
    ```
  </Step>
</Steps>

## 清理悬空 volume

容器删除后,它用过的 volume 不会被自动删除。这些无人使用的 volume 叫悬空 volume,会持续占用磁盘。

```bash theme={null}
# 查看悬空 volume
docker volume ls -f dangling=true

# 一键清理所有未使用的 volume
docker volume prune
```

<Warning>
  `docker volume prune` 会删除所有当前没有容器引用的 volume,数据不可恢复。执行前先用 `docker volume ls` 逐个确认。删除的 volume 无法用任何方式找回。
</Warning>

## 延伸阅读

* [Docker 容器](/docker/docker-容器):容器生命周期与数据的关系
* [Docker 网络](/docker/docker-网络):多容器互联配合存储使用
* [Docker Compose](/docker/docker-compose):用 YAML 声明 volume
* [Kubernetes 配置与存储](/kubernetes/kubernetes-配置与存储):PV 与 PVC 持久化方案
