宿主机路径与容器路径

我想确认 php.ini 里到底有没有手写的扩展声明。先在 shell 里把容器名存成变量:

bash 复制代码
export PHP_CTN="php84"   # 换成你在 1Panel 里给 PHP 运行环境起的名字(= 容器名)

然后我敲了这一条,想在容器里读那个配置:

bash 复制代码
docker exec -i "$PHP_CTN" grep -E '^\s*(zend_)?extension\s*=' /opt/1panel/runtime/php/$PHP_CTN/conf/php.ini

回显是这样的:

bash 复制代码
grep: /opt/1panel/runtime/php/php84/conf/php.ini: No such file or directory

看着就像 php.ini 没了。这是个很容易把人带偏的报错,于是我把 docker exec 去掉,在宿主机上直接读同一个路径:

bash 复制代码
cat /opt/1panel/runtime/php/$PHP_CTN/conf/php.ini

完整的 php.ini 打出来了。

同一个路径,一边 No such file or directory,一边成功输出。文件没丢,那就只剩一种可能:这个路径本来就不该在 docker exec 里用。

环境先交代一下。这台机器是 1Panel v2,PHP 8.4.25 跑在容器里,镜像 1panel-php-fpm:8.4.25,宿主机是 Ubuntu。下面这些回显都是 2026-09-17 那次部署里实跑的,路径和内容原样留着。

原因说穿了不复杂。容器的文件系统不是宿主机的文件系统。你在容器里看到的那棵树,是镜像层加容器可写层拼出来的,这也是为什么容器一重建,你后来补装的东西全没了。

docker exec 的语义是在容器里启动一个进程。所以上面那条命令的执行者是容器里的进程,它被关在自己的命名空间里,根本看不到宿主机的 /opt/1panel/。对这个进程来说,那个路径不存在。

听上去很直白。可到了「我想读个配置文件」这种日常动作里,特别容易忘。

因为 docker exec -i "$PHP_CTN" grep ... 读起来跟在本地敲命令没两样。grep、ls、cat 全是熟工具,只是前面挂了个前缀。而那个前缀真正干的事,是把整条命令搬进另一个文件系统里执行。

凡是「熟命令 + 陌生前缀」这种组合,我现在都先停一下,确认它到底在哪执行。

这里还有个更绕的地方:那两个路径并不是两份文件,而是同一个文件的两种访问路径。

1Panel 用的是 bind mount,把宿主机的

bash 复制代码
/opt/1panel/runtime/php/<运行环境名>/conf/php.ini

挂到容器内的

bash 复制代码
/usr/local/etc/php/php.ini

同一份字节。从宿主机看它在 /opt/1panel/.../conf/php.ini,从容器看它就是 /usr/local/etc/php/php.ini。

这条推论很实用。在宿主机上用 vim 改这个文件,等于改了容器里 PHP 读的那份配置。改完 docker restart "$PHP_CTN" 让 FPM 重读就行,不用进容器。

它也解释了另一个常见困惑:面板上编辑的 php.ini,和 php --ini 打出来的路径为什么完全对不上------

ruby 复制代码
# 容器视角的路径(docker exec 里执行 php --ini)
Loaded Configuration File:  /usr/local/etc/php/php.ini
#                      ↑ 面板上看到的是宿主机路径 /opt/1panel/runtime/php/php84/conf/php.ini

两个路径指向同一个文件。配置没错,是两套视角。

这次实跑的几个对照,一条一条说。

php.ini 宿主机在 /opt/1panel/runtime/php/<名>/conf/php.ini,容器里是 /usr/local/etc/php/php.ini。

php-fpm.conf 宿主机在同一个 conf/ 下,容器里是 /usr/local/etc/php-fpm.conf。它不在 php/ 子目录里,跟 php.ini 不是同一层。

扩展 ini 目录宿主机还是那个 conf/,容器里是 /usr/local/etc/php/conf.d/。

网站代码目录这条最坑。宿主机 /opt/1panel/www/sites/<域名>/,容器里是 /www/sites/<域名>/。

记忆点就两个。

宿主机侧的固定前缀是 /opt/1panel/,看到它就知道这是面板生成的、要在宿主机上读。容器侧的前缀是 /usr/local/etc/php/,那是官方 PHP 镜像的标准路径,跟面板没关系。

网站目录这一条不是同路径挂载。宿主机的 /opt/1panel/www 被整体映射到容器内的 /www。所以在容器里 cd /opt/1panel/www/sites/... 必然报 No such file or directory。这是实测出来的,回执在后面。

上面这些是本次实测值。换一个运行环境名、改一次镜像版本,值就可能变。想要权威答案,去问 Docker 自己:

bash 复制代码
# 列出该容器所有的「宿主机路径 -> 容器内路径」映射
docker inspect "$PHP_CTN" --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'

它会把每一对 Source -> Destination 打出来。这是判断「一个路径属于哪一侧」的权威依据,比记表靠谱。

实测回执节选(2026-09-18):

bash 复制代码
/opt/1panel/www => /www
/opt/1panel/runtime/php/<运行环境名>/conf/php.ini => /usr/local/etc/php/php.ini
/opt/1panel/runtime/php/<运行环境名>/conf/php-fpm.conf => /usr/local/etc/php-fpm.conf
/opt/1panel/runtime/php/<运行环境名>/conf/conf.d => /usr/local/etc/php/conf.d
/opt/1panel/runtime/php/<运行环境名>/extensions => /usr/local/lib/php/extensions
/opt/1panel/runtime/php/<运行环境名>/composer => /tmp/composer
/opt/1panel/runtime/php/<运行环境名>/log => /var/log/php

第一行就是刚才那条结论的来源,/opt/1panel/www 被整体映射成了 /www。

这条命令没法靠猜。在写这篇之前,我以为网站目录是同路径挂载,容器里也走 /opt/1panel/www/...,实测才知道不是。凡是「容器内路径是什么」这种问题,答案只能从 docker inspect 拿。

不想查挂载配置的话,还有个更直接的试探法:

bash 复制代码
# 容器内到底有没有这个路径?有输出就是有,报错就是没有
docker exec -i "$PHP_CTN" ls -l /usr/local/etc/php/php.ini

两侧各试一次,哪个路径该配哪条命令,马上就清楚。

流程我画成这样:

flowchart TD A[&#34;同一个配置文件<br/>由 bind mount 两侧共享&#34;] --> B[&#34;宿主机侧路径<br/>/opt/1panel/runtime/php/php84/conf/php.ini&#34;] A --> C[&#34;容器内侧路径<br/>/usr/local/etc/php/php.ini&#34;] B --> D[&#34;宿主机上直接 cat / vim&#34;] C --> E[&#34;docker exec 里核验&#34;] D --> F[&#34;两条路径混用<br/>docker exec 用宿主机路径 → No such file&#34;] E --> F style A fill:#eaf2fd,stroke:#4a7fd4,color:#1f2328 style F fill:#fdecec,stroke:#d1242f,color:#d1242f

三步走。第一步看路径前缀,/opt/1panel/ 是宿主机侧,/usr/local/etc/ 是容器侧。第二步拿不准就查挂载,用 docker inspect --format 看 Source 到 Destination。第三步还拿不准就试探,两侧各 ls 一次,哪侧有输出就是哪侧的路径。

这个坑是双向的。上面那两条路径共享同一个文件,但也有只在容器一侧存在的文件,尤其是你在容器里 apt-get 装的东西。

比如某个扩展的依赖库:

bash 复制代码
# 只在容器侧存在(apt-get 装进的是容器可写层)
/usr/lib/x86_64-linux-gnu/libzip.so.5

这一份在宿主机上并不存在。除非宿主机自己也装了同名包,那它就是两个互不相干的副本。

在宿主机上反查一下:

bash 复制代码
# 宿主机上查不到 ------ 因为这个包从没在宿主机上装过
dpkg -S libzip.so.5

它告诉你没有文件匹配。这不是包数据库坏了,而是这个文件属于容器的可写层,宿主机的包管理器对它一无所知。

所以「路径该在哪一侧用」要双向记。1Panel 面板生成的配置,两侧共享,bind mount 的同一份字节,宿主机上 cat 或 vim,容器内用映射后的路径核验。镜像自带的文件,两侧都有,但可能是不同副本,按需选一侧,别假设改一边另一边就跟着变。容器里 apt-get 补装的包只有容器侧,只能在 docker exec 里访问和核验。

第三条是排查扩展问题时最常撞上的:你补的那些库,宿主机上根本不存在。想在宿主机侧看它们,看不到。

还有一句得说清楚。报错信息里的 No such file or directory 只说明这个进程在当前命名空间里找不到它,不说明文件不存在。先确认这句话是谁在什么位置说的,再决定去哪找。

相关推荐
量化分析码农1 小时前
【Python量化系统工程实战 #01】数据存哪里不崩?CSV/SQLite/MySQL 量化存储方案对比与 SQLite 实战建库
后端
弈栈录1 小时前
Spring MVC 请求处理流程与核心源码解析
后端·架构·mvc
Sylven1 小时前
【DevOps 开发流程】什么是CI/CD?不同的阶段应当配置哪些CI/CD自动化流程?
后端
鶴哥只手遮天1 小时前
从零搭建光电仿真引擎(五):探测器成像与工程实践
后端
鶴哥只手遮天1 小时前
从零搭建光电仿真引擎(三):大气与热特性系统
后端
明华0491 小时前
保险Agent开发记录
后端
imDwAaY1 小时前
Redis List 是链表吗?从 Ziplist 到 Quicklist 揭开底层实现
redis·后端
_风不会停息1 小时前
剥开 Agent 开发:参照 pi-agent 实现一个小 Agent
人工智能·后端