当前位置:首页 > 文章列表 > 文章 > 软件教程 > Docker 容器启用只读根文件系统后,临时写目录怎么挂载

Docker 容器启用只读根文件系统后,临时写目录怎么挂载

来源:17golang原创 2026-10-08 10:19:02 0浏览 收藏

Docker 容器启用只读根文件系统后,不是把整个容器变成“完全不能写”,而是把写权限收缩到明确挂载的目录。最实用的分法是:/tmp、/run、进程锁和短期缓存使用 tmpfs;必须跨容器保留的数据使用 Docker volume;只有宿主机确实需要直接读写文件时才考虑 bind mount。

官方文档:https://docs.docker.com/

推荐组合:--read-only 锁住镜像根文件系统,只为应用真实写目录增加 tmpfs 或 volume。不要为了让程序启动而把整个工作目录重新挂成可写。

步骤1:先盘点应用到底写哪些目录

先把目录分成两类。重启后可以丢失的 PID、Socket、临时文件、上传中间文件和可重建缓存属于临时数据;数据库、用户上传成品、任务结果和状态文件属于持久数据。常见候选如下:

目录示例数据性质推荐挂载
/tmp、/run进程退出后可丢失tmpfs
/var/cache/myapp可重建缓存优先 tmpfs,并限制大小
/var/lib/myapp业务状态或用户数据Docker volume
/etc/myapp配置,运行时不应修改保持只读;必要时只读 bind mount

可见成功状态:每个写目录都被标记为“临时”或“持久”,没有把整个 /var、/app 之类的大目录笼统设为可写。

步骤2:先只启用只读根文件系统,暴露遗漏路径

先用最小配置启动一次,让应用报出真正缺少的写目录。下面的命令故意不添加写挂载:

# 先锁住根文件系统,用启动日志定位应用仍然尝试写入的路径
docker run --rm --read-only --name myapp-check myapp:latest

预期现象是应用正常启动,或明确报告某个目录为 Read-only file system。如果报错路径是日志目录,优先把日志改为标准输出;如果是 PID、Socket 或临时文件,再为那个具体目录配置 tmpfs。

原创容器管理界面中只读根文件系统开关已启用
图1:原创软件界面示意。容器安全设置中只读根文件系统已启用,状态区提示根目录禁止运行时修改;不是 Docker 官方界面截图。

可见成功状态:容器配置显示根文件系统为只读,错误信息只剩下具体写目录,而不是模糊的启动失败。

步骤3:用 tmpfs 挂载 /tmp 与 /run

tmpfs 的内容不会写入容器可写层,容器停止后数据消失,适合临时目录。下面同时限制大小和目录权限:

# 根文件系统保持只读,只开放两个内存临时目录
docker run --rm --read-only --name myapp \
  --mount type=tmpfs,dst=/tmp,tmpfs-size=67108864,tmpfs-mode=1777 \
  --mount type=tmpfs,dst=/run,tmpfs-size=16777216,tmpfs-mode=755 \
  myapp:latest

tmpfs-size 使用字节数;示例分别约为 64 MiB 和 16 MiB。/tmp 常用 1777,但专用目录应尽量改为 0700 或 0770。tmpfs 会占用容器内存额度,并且在宿主机启用交换分区时存在被换出的可能,因此它不是“敏感数据绝不落盘”的绝对保证。

可见成功状态:应用能够创建临时文件,容器停止并重新创建后这些文件不再存在。

步骤4:需要保留的数据改用 volume

如果 /var/lib/myapp 中保存的是业务数据,就不能用 tmpfs。先创建命名卷,再把它挂到唯一需要持久化的路径:

# 创建由 Docker 管理的持久卷
docker volume create myapp-data

# 临时目录使用 tmpfs,业务数据使用命名卷
docker run -d --read-only --name myapp \
  --mount type=tmpfs,dst=/tmp,tmpfs-size=67108864,tmpfs-mode=1777 \
  --mount type=tmpfs,dst=/run,tmpfs-size=16777216,tmpfs-mode=755 \
  --mount type=volume,src=myapp-data,dst=/var/lib/myapp \
  myapp:latest

volume 独立于容器生命周期,删除并重建容器后仍可重新挂载。若镜像在挂载点预置了文件,挂载会遮住该路径原有内容;上线前要确认初始化逻辑,不要把“看不到镜像内文件”误判为文件被删除。

可见成功状态:重建容器后,/var/lib/myapp 的业务数据仍然存在,而 /tmp 中的临时文件已经清空。

步骤5:在 Docker Compose 中固化配置

确认目录边界后,把配置写入 Compose,避免每次手工拼接启动参数:

services:
  app:
    image: myapp:latest
    read_only: true # 根文件系统保持只读
    tmpfs:
      - /tmp:size=64m,mode=1777 # 通用临时文件
      - /run:size=16m,mode=755 # PID、Socket 等运行时文件
    volumes:
      - app-data:/var/lib/myapp # 需要跨容器保留的业务数据

volumes:
  app-data: {} # 使用 Docker 管理的命名卷
# 按 Compose 配置创建或更新服务
docker compose up -d

非 root 进程若仍提示 Permission denied,先核对容器用户的 UID、GID 和目录 mode。不要用 chmod 777 一把放开;应让目录所有者和应用用户匹配,或只赋予目标组写权限。

原创容器部署界面中配置 tmpfs 与命名卷挂载
图2:原创软件界面示意。部署表单将 /tmp、/run 设置为 tmpfs,并把 /var/lib/myapp 设置为持久卷;字段对应 Compose 中的路径、大小和权限。

可见成功状态:服务详情同时显示“根文件系统:只读”、两个 tmpfs 挂载和一个 volume 挂载,容器健康状态正常。

步骤6:做一次正反向写入验证

最终验证要同时证明两件事:根文件系统不能写,显式挂载目录可以写。镜像包含 shell 时可执行:

# 查看实际挂载类型、目标路径和读写属性
docker inspect myapp --format '{{json .Mounts}}'

# /etc 写入应失败;/tmp、/run 与数据卷写入应成功
docker exec myapp sh -c '
  touch /etc/should-fail && echo "异常:/etc 可写" || echo "正确:/etc 只读"
  touch /tmp/tmp-ok && echo "正确:/tmp 可写"
  touch /run/run-ok && echo "正确:/run 可写"
  touch /var/lib/myapp/data-ok && echo "正确:数据卷可写"
'

精简镜像可能没有 shell,此时应通过应用健康检查、专用诊断子命令或测试接口验证,不要为了测试临时给生产镜像安装 shell。Docker inspect 的挂载列表应能区分 tmpfs 与 volume,根文件系统只读状态则由容器配置确认。

原创容器详情界面显示只读根目录与可写挂载验证通过
图3:原创软件界面示意。根目录写入被阻止,/tmp、/run 与数据卷检查通过,挂载类型和健康状态同时可见。

可见成功状态:/etc 的写入失败,三个显式挂载点写入成功;重建容器后临时文件消失、数据卷文件保留。

常见问题

tmpfs 和 volume 应该怎么选?

问自己“容器重建后这个数据必须保留吗”。答案为否时用 tmpfs;答案为是时用 volume。缓存虽然可以重建,但若体积较大或重建成本高,也可以选择 volume,并单独设计清理策略。

为什么挂了 tmpfs 仍然 Permission denied?

只读问题已经解决,但目录权限可能与应用用户不匹配。检查容器 UID、GID 和挂载 mode。多用户共享目录可用组权限,专用目录则优先收紧到应用用户。

可以把整个 /var 挂成可写吗?

不建议。大范围可写会削弱只读根文件系统的价值,还会隐藏镜像中的原始目录。应从错误日志和应用配置中找出最小写路径,一个目录一个目录开放。

tmpfs 会不会把容器内存占满?

有可能。为每个 tmpfs 设置与业务峰值相符的上限,并把它纳入容器内存预算。上传临时文件和缓存尤其需要限额与清理机制,否则应用会从“磁盘写满”变成“内存压力”。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
以临时文件加原子重命名安全更新配置以临时文件加原子重命名安全更新配置
上一篇
以临时文件加原子重命名安全更新配置
os.DirFS 的路径为什么不能包含上级跳转,安全边界是什么
下一篇
os.DirFS 的路径为什么不能包含上级跳转,安全边界是什么
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    375次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    445次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    452次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    398次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    224次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码