DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

systemd ExecStartPre 写入 tmpfs,ExecStart 却读不到

TemporaryFileSystem的挂载视图按Exec进程建立。交换运行期数据用明确RuntimeDirectory路径,隔离/etc的动态初始化需同视图包装或受控映射。

unit 设置了 TemporaryFileSystem=,准备命令在 ExecStartPre 中生成文件,主程序启动时却看到空目录。文件能在准备命令内读回来,也不保证随后 ExecStart 看到的是同一个 tmpfs。

systemd 为管理器启动的各个执行进程分别建立文件系统命名空间。准备进程退出后,其命名空间中的挂载不会作为同一挂载视图传给后续主进程。正式 systemd 262 文档仍说明这一边界,不能把更换目录权限当作修复。

确认文件消失发生在两个执行阶段之间

核对目标 unit 的 TemporaryFileSystem、各条 Exec 命令、运行身份,以及生成和读取的完整路径。先区分准备命令生成失败、清理逻辑删除文件、路径写错与新挂载覆盖目录。

这里处理的是同一服务两条 Exec 命令之间的临时挂载问题。服务与宿主终端看到不同 /tmp 的 PrivateTmp 情况,不能直接解释所有跨 Exec 的新 tmpfs。日志只记录阶段、路径和受控文件标记,不打印生成的密钥或配置全文。

只需要交换运行期文件时用专用 RuntimeDirectory

准备和主程序只需交换一份运行期数据,并且都能显式配置读取路径时,可由 systemd 管理一个专用运行目录。例如,固定 User、Group 的系统服务,在本机确认支持这些设置后合入已有服务块:

[Service]
RuntimeDirectory=example-runtime
RuntimeDirectoryMode=0700
RuntimeDirectoryPreserve=no

它在系统服务的 /run/ 下准备目录,并将实际路径放入 RUNTIME_DIRECTORY。让准备程序和主程序都使用这一目录;设置不会自动把原先写向 /etc 或另一个 tmpfs 的路径重定向过去。用户服务的基准位置不同,多个运行目录的环境变量也可能是冒号分隔列表,不能把整个列表当作单个目录。

示例选用新的专用目录,保留原运行用户与权限。若已有同名共享目录,systemd 的所有权处理可能影响其中内容,应先核对用途,不能随意占用。目录 0700 也不替代其中敏感文件自己的读取权限和应用访问控制。

默认生命周期内,运行目录不会因准备进程正常退出而立刻清空,但服务停止会清理它。RuntimeDirectoryPreserve=restart 可以用于明确要求跨自动或手动重启保留的契约,yes 会改变停止后的保留行为;系统重启仍会丢失 /run 数据。按实际需要选择,不为“文件能读到”长期保留秘密,也不把运行目录当作永久备份。

动态初始化隔离的 /etc 需要保留同一视图

如果目标是用 TemporaryFileSystem=/etc 隐藏宿主配置,再动态生成主程序必须从 /etc 读取的文件,单独增加 RuntimeDirectory 不会解决原路径问题。

一种实现方式是在同一启动包装程序中完成初始化,检查全部必要步骤成功后,再 exec 原主程序。二者继承同一个挂载视图,初始化失败就停止,不把半成品交给主程序。包装逻辑要保留原参数、信号、退出状态、运行身份与实际就绪协议,不能套一个固定脚本给所有服务。

另一个方向是准备受控的宿主来源文件,并按已批准契约用 BindPaths 或 BindReadOnlyPaths 映射进去。此时要单独处理生成时序、来源权限、模板实例隔离和停止后的生命周期;不能因为挂载进来了,就假设动态配置已经正确生成。

覆盖发行版的 ExecStart 还会影响将来接收发行版命令变更,需记录完整原命令及更新维护方式。不要直接移除文件系统隔离,或改写宿主 /etc 来绕过这个问题。

主程序读到正确文件才算完成

保存原配置与仍需保留的数据,在本机验证选择的目录或包装方案后,按原流程重新加载 unit 并在维护窗口启动。检查准备阶段实际成功、主程序从预期视图读取当前文件,业务初始化和就绪状态也正确。

还要检查初始化失败时不启动业务、重启时文件是否按所选生命周期重建,以及停止后的清理符合要求。回退恢复本次目录和 Exec 配置,保留仍有用的诊断;不能假设已被销毁的旧 tmpfs 可在重启后恢复。