Linux中容器文件操作和数据卷使用以及目录挂载
一、容器文件操作
1.1 基础知识
在Docker环境中,管理容器内部的文件是一个常见的需求。
无论是为了配置应用、备份数据还是调试问题,了解如何高效地进行文件操作都是非常重要的。
docker cp
命令提供了一种简单的方法来在宿主主机和容器之间复制文件或目录。
快速文件传输:
-
当我们需要快速地在宿主主机和容器之间传输文件,而不想通过网络或构建新的镜像时,
docker cp
提供了一种简单直接的方法。
临时性文件操作:
-
对于临时性的文件修改或者调试,比如临时修改配置文件、测试新功能等,
docker cp
可以让我们迅速地将文件传入传出容器,无需重启容器或重新构建镜像。
其基本语法如下:
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH
-
CONTAINER
: 目标容器的名称或ID。 -
SRC_PATH
: 源路径,可以是宿主主机上的路径或是容器内的路径。 -
DEST_PATH
: 目标路径,同样可以是宿主主机上的路径或是容器内的路径。
1.2 nginx案例
启动Nginx容器
首先,我们需要启动一个Nginx容器:
docker run -name mynginx -d nginx
从宿主主机复制文件到Nginx容器
假设我们有一个名为index.html
的静态网页文件想要放到Nginx的默认网页目录中(通常是/usr/share/nginx/html
)
我们可以使用以下命令:
docker cp index.html mynginx:/usr/share/nginx/html
从Nginx容器复制文件到宿主主机
如果我们想从容器内获取某个文件(例如修改后的index.html
),我们可以这样做:
docker cp mynginx:/usr/share/nginx/html/index.html ./
这将把容器内的index.html
文件复制到当前工作目录下。
1.3 mysql案例
准备一个 init.sql 文件
-- init.sql 示例内容
CREATE DATABASE IF NOT EXISTS testdb;
USE testdb;CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(100)
);INSERT INTO users (name) VALUES ('Alice'), ('Bob');
启动MySQL容器
使用官方的MySQL镜像启动一个新的容器,并设置root用户的密码:
docker run --name my-mysql \-e MYSQL_ROOT_PASSWORD=123456 \-p 13306:3306 \-d mysql:8.0.22 \--default-authentication-plugin=mysql_native_password \--character-set-server=utf8mb4 \--collation-server=utf8mb4_unicode_ci
参数 | 含义 |
---|---|
--name my-mysql | 给容器起个名字叫 my-mysql |
-e MYSQL_ROOT_PASSWORD=123456 | 设置MySQL的root用户密码为 123456 |
-p 13306:3306 | 将宿主机的 13306 端口映射到容器的 3306 端口(避免与本机MySQL冲突) |
-d mysql:8.0.22 | 后台运行名为 mysql:8.0.22 的镜像 |
--default-authentication-plugin=mysql_native_password | 使用兼容性更好的认证插件 |
--character-set-server=utf8mb4 | 设置默认字符集为 utf8mb4 支持中文和表情符号 |
--collation-server=utf8mb4_unicode_ci | 设置排序规则 |
将本地 SQL 文件复制到容器中
# 假设我们的 init.sql 在 /root/mysql_demo 目录下
docker cp /root/mysql_demo/init.sql my-mysql:/tmp/init.sql
在容器中执行 SQL 文件导入数据
docker exec -it my-mysql mysql -u root -p123456 -e "source /tmp/init.sql"
导出数据库为新的 SQL 文件(mysqldump是用于逻辑备份的命令)
docker exec my-mysql mysqldump -u root -p123456 --all-databases > /root/mysql_demo/backup_full.sql
docker exec my-mysql mysqldump -u root -p123456 testdb > /root/mysql_demo/backup_testdb.sql
也可以先在容器里生成备份再拷贝出来
# 在容器内生成备份
docker exec my-mysql sh -c 'mysqldump -u root -p"123456" testdb > /tmp/testdb_backup.sql'# 把备份文件复制到宿主机
docker cp my-mysql:/tmp/testdb_backup.sql /root/mysql_demo/testdb_backup.sql
docker cp
适用于小规模的文件传输。如果涉及到大量数据的导入导出,这种方法可能不够高效,且容易出错。例如,在处理数据库备份时,虽然可以先通过命令行工具生成备份文件再用
docker cp
拷贝出来,但对于非常大的数据库,这并不是最理想的选择。
如果依赖
docker cp
来进行所有文件交换,那么随着容器的销毁,这些手动拷贝的数据也会丢失。因此,对于需要长期存储的数据,应该考虑使用数据卷(data volumes)或绑定挂载(bind mounts),而不是仅依赖
docker cp
。
1.4 容器持久化存储
写时复制机制
Docker镜像由多个只读层叠加而成,启动容器时,Docker会加载只读镜像层并在镜像栈顶部添加一个读写层。
如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本依然存在,只是已经被读写层中该文件的副本所隐藏,此即"写时复制(COW)"机制。
我们知道,容器在创建之后,实际上我们在容器中创建和修改的文件,实际上是被容器的分层机制保存在最顶层的容器层进行操作的,为了保护下面每一层的镜像不被修改,所以才有了这样的CopyOnWrite特性。
原因 | 结果 |
---|---|
Docker 使用 Copy-on-Write 机制 | 文件修改只发生在容器层,不影响镜像层 |
容器层与容器生命周期绑定 | 容器销毁时,容器层也被删除 |
但是这样也会导致容器在销毁时数据的丢失,当我们销毁容器重新创建一个新的容器时,所有的数据全部丢失,直接回到梦开始的地方。
在某些情况下,我们可能希望对容器内的某些文件进行持久化存储,就需要使用到docker中提供给我们的持久化存储几种方式。
存储方式 | 特点 | 是否推荐 |
---|---|---|
数据卷(Data Volume) | 由 Docker 管理,独立于容器生命周期 | ✅ 推荐 |
数据卷容器(Data Volume Container) | 使用一个专门容器提供数据卷供其他容器挂载 | ❌ 旧版用法,现已被简化 |
目录挂载(绑定挂载) | 将宿主机目录直接挂载到容器中 | ✅ 推荐 |
tmpfs 挂载 | 仅存在于内存中,重启后丢失 | ❌ 不适合持久化 |
二、数据卷
2.1 基础知识
数据卷是 Docker 提供的一种持久化存储机制,它独立于容器的生命周期存在。
这意味着即使删除了容器,只要不删除数据卷,其中的数据依然可以被新创建的容器使用。
数据卷的特点
-
独立性:数据卷与容器分离,不受容器生命周期影响。
-
共享性:支持在多个容器之间共享数据。
-
高效性:绕过联合文件系统(UnionFS),提供更快的读写速度。
-
管理便捷:通过 Docker 命令直接管理,简化操作流程。
数据卷的位置
默认情况下,Docker 将数据卷存储在主机的 /var/lib/docker/volumes/
目录下。
每个数据卷都有一个唯一的名称和对应的目录结构。
使用场景:
-
存储数据库数据(如 MySQL、PostgreSQL)
-
存放日志文件
-
开发环境中映射源码目录(虽然更推荐 bind mount)
-
在多个服务之间共享配置文件或缓存
2.2 数据卷操作命令
创建数据卷
docker volume create v1
这会创建一个名为 v1 的数据卷。
查看已有数据卷
docker volume ls
输出示例:
DRIVER VOLUME NAME
local v1
local v2
查看数据卷详细信息
[root@localhost volumes]# docker volume inspect v1
[{"CreatedAt": "2025-05-30T15:57:40+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/v1/_data","Name": "my_volume","Options": null,"Scope": "local"}
]
可以看到数据卷在宿主机上的实际路径(一般为 /var/lib/docker/volumes/my_volume/_data
)。
删除数据卷
docker volume rm v1
注意:如果还有容器在使用这个数据卷,必须先停止并删除这些容器才能删除数据卷。
2.3 使用方式
在运行容器时挂载数据卷
格式:
docker run -v <volume_name>:<container_path> ...
例如:
docker run -d \--name n1 \-v v1:/usr/share/nginx/html \-p 80:80 \nginx
这条命令表示:
创建一个名为 v1 的数据卷
如果指定的 volume 不存在:
Docker 会自动为我们 创建一个新的命名数据卷(Named Volume)
名字就是我们写的那个
nginx_html
然后把这个新创建的数据卷挂载到容器中的指定路径
其挂载到容器的 /usr/share/nginx/html
路径下
我挂载了一个空的数据卷(volume)到容器的某个目录(如
/usr/share/nginx/html
),请问容器中的/usr/share/nginx/html
是否会被清空?我运行了 docker run -v my_volume:/path/in/container ...
my_volume
是一个 新创建的空数据卷容器镜像在
/path/in/container
这个位置原本有文件那么 Docker 会自动把镜像中原路径下的内容复制到数据卷中,然后才挂载这个数据卷进容器。
这是 Docker 的一种保护机制,目的是:
防止用户误操作导致服务启动失败。
如果直接挂载一个空卷,而容器期望某些配置或静态资源存在,会导致容器启动后服务不可用。
所以 Docker 在第一次挂载空卷时,会自动将镜像中原有的内容“迁移”过去,确保服务能正常运行。
如果我们不希望容器原有的内容被复制进来,可以这样做:
方法一:手动初始化数据卷
提前准备好我们自己的内容放入数据卷,这样就不会触发自动复制。
方法二:使用绑定挂载(Bind Mount)
如果我们希望完全控制内容,可以使用绑定挂载代替命名卷。
2.4 Nginx + 数据卷
步骤 1:创建并启动 Nginx 容器
docker run -d \--name mynginx \-v nginx_html:/usr/share/nginx/html \-p 80:80 \nginx
步骤 2:向数据卷中添加网页内容
# 创建测试文件
echo "Hello from Data Volume" > index.html# 将文件复制进容器(自动写入数据卷)
docker cp index.html mynginx:/usr/share/nginx/html/
访问浏览器:http://自己的虚拟机ip/
,应能看到页面显示“Hello from Data Volume”。
步骤 3:删除容器并重建
docker stop mynginx && docker rm mynginxdocker run -d \--name mynginx_new \-v nginx_html:/usr/share/nginx/html \-p 80:80 \nginx
再次访问浏览器:http://自己的虚拟机ip/
,页面显示“Hello from Data Volume”。
2.5 MySQL + 数据卷
步骤 1:创建并启动 MySQL 容器
docker run -d \--name mysql_db \-v mysql_data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=123456 \mysql:8.0.22
如果没有mysql镜像可以复制上面的mysql案列
步骤 2:初始化数据库
docker exec -it mysql_db bashmysql -uroot -p123456CREATE DATABASE testdb;USE testdb;CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100));INSERT INTO users (name) VALUES ('Alice'), ('Bob');
步骤 3:删除容器并重建
docker stop mysql_db && docker rm mysql_dbdocker run -d \--name mysql_db_new \-v mysql_data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=123456 \mysql:8.0.22
这里去到mysql容器,查询表数据,还是跟之前插入的数据一致
docker exec -it mysql_db_new bashmysql -uroot -p123456;use testdb;select * from users;
方式 | 类比 |
---|---|
docker exec -it xxx bash | 打开电脑 → 登录系统 → 手动输入命令 |
docker exec -it xxx 命令 | 直接远程执行某个程序,不需要登录系统 |
docker exec -it mysql_db_new mysql -uroot -p123456 -e "SELECT * FROM testdb.users;"
2.6 总结
操作 | 命令 | 说明 |
---|---|---|
创建数据卷 | docker volume create myvol | 创建一个名为 myvol 的数据卷 |
列出数据卷 | docker volume ls | 查看所有已存在的数据卷 |
查看数据卷详情 | docker volume inspect myvol | 查看数据卷的元数据及宿主机路径 |
删除数据卷 | docker volume rm myvol | 删除指定数据卷(需先删除使用它的容器) |
挂载数据卷到容器 | -v myvol:/path/in/container | 在运行容器时挂载数据卷 |
多个数据卷挂载 | -v vol1:/path1 -v vol2:/path2 | 支持同时挂载多个数据卷 |
创建一个名为
app_logs
的数据卷,并挂载到容器的/app/logs
路径下。向数据卷中写入日志文件,然后删除容器再重建,验证日志是否还在。
尝试使用两个不同的容器挂载同一个数据卷,实现数据共享。
三、目录挂载
3.1 基础知识
Bind mounts
允许你将宿主机上的一个文件或目录直接挂载到容器中。
它不同于命名数据卷,因为它直接指向宿主机上的具体路径。
特点:
-
灵活性高:可以指定任意宿主机路径
-
实时同步:对挂载目录的任何更改都会立即反映在容器和宿主机之间
-
适合开发环境:便于快速迭代代码或配置文件
与 Named Volumes 对比:
卷是容器的持久性数据存储,由 码头工人。您可以使用该命令显式创建卷,或者 Docker 可以在容器或服务创建期间创建卷。
docker volume create
当您创建卷时,它存储在 Docker 上的目录中 主机。当您将卷挂载到容器中时,此目录就是 挂载到容器中。这类似于 bind mounts 的工作方式 不同之处在于卷由 Docker 管理并与核心隔离 主机的功能。
什么时候使用这种方式
绑定挂载适用于以下类型的用例:
在开发环境之间共享源代码或构建工件Docker主机和容器。当您想要在容器中创建或生成文件并持久化文件复制到主机的文件系统上。
将配置文件从主机共享到容器。就是这样默认情况下,Docker通过从主机挂载到每个容器来为容器提供DNS解析。./etc/resolv.conf绑定挂载也可用于构建:您可以从将主机放入Build容器中,以测试、lint或编译项目。
数据卷(Named Volumes):是 Docker 内部管理的一种持久化存储机制,独立于容器的生命周期存在。它通常位于 /var/lib/docker/volumes/
下,并且由 Docker 自动管理。
目录挂载(Bind Mounts):允许你将宿主机上的任意文件或目录直接挂载到容器中。这意味着你可以指定具体的宿主机路径,而不是让 Docker 自动管理路径。
数据卷的优势
-
自动化管理:由 Docker 自动管理路径和生命周期,减少了手动干预。
-
更好的隔离性:适合生产环境下的多用户或多应用部署。
-
更高的性能:绕过了联合文件系统,提供了更快的读写速度。
目录挂载的优势
-
灵活性:能够直接操作宿主机文件系统,适合开发调试和特定路径需求。
-
实时更新:任何对宿主机文件的修改都会即时反映在容器内,非常适合快速迭代。
-
精确控制:可以将容器内的目录映射到宿主机上的任意位置,满足复杂的部署需求。
3.2 基本操作
挂载目录到容器:
-v
这是较早的方式,语法相对简单直接
当你指定一个宿主机路径(如 /home/user/data
)时,它会创建一个绑定挂载(bind mount)。
如果只提供一个名称(如 my_volume
),则 Docker 会将其视为一个命名卷。
docker run -v /host/path:/container/path ...
--mount
选项这是更新的方式,提供了更清晰、更详细的语法,并且支持更多选项
docker run --mount type=bind,source=<host_path>,target=<container_path> ...
或者用于命名卷:
docker run --mount type=volume,source=my_volume,target=/path/in/container ...
-
type
可以是bind
或volume
,分别表示绑定挂载和命名卷。 -
source
或src
指定宿主机上的源路径(对于绑定挂载)或卷名(对于命名卷)。 -
target
或dst
指定容器内的目标路径。
优点:
-
更加明确,尤其是在处理复杂的挂载需求时(例如设置只读权限、传播模式等)。
-
支持更多的高级功能,比如 SELinux 标签、只读模式等。
使用 -v
创建绑定挂载:
docker run -d \--name mynginx \-v $(pwd)/html:/usr/share/nginx/html \nginx
使用 --mount
创建相同的绑定挂载:
docker run -d \--name mynginx \--mount type=bind,source=$(pwd)/html,target=/usr/share/nginx/html \nginx
使用 -v
创建命名卷:
docker run -d \--name mynginx \-v nginx_html:/usr/share/nginx/html \nginx
使用 --mount
创建相同的命名卷:
docker run -d \--name mynginx \--mount type=volume,source=nginx_html,target=/usr/share/nginx/html \nginx
参数名 | 含义 | 示例 |
---|---|---|
type | 挂载类型,这里是 bind | type=bind |
source 或 src | 宿主机上要挂载的文件或目录路径 | source=/home/user/html |
target 或 dst | 容器内挂载的目标路径 | target=/usr/share/nginx/html |
readonly | 设置为只读模式(可选) | 添加 readonly 表示只读 |
挂载多个目录
docker run -d \--name mynginx \--mount type=bind,source=$(pwd)/html,target=/usr/share/nginx/html \--mount type=bind,source=$(pwd)/conf.d,target=/etc/nginx/conf.d \--mount type=bind,source=$(pwd)/logs,target=/var/log/nginx \-p 80:80 \nginx
适用于需要多目录挂载的复杂服务。
设置只读挂载
docker run -d \--name mynginx \--mount type=bind,source=$(pwd)/html,target=/usr/share/nginx/html,readonly \-p 80:80 \nginx
-
容器内的
/usr/share/nginx/html
目录变为只读 -
防止容器程序意外修改宿主机文件