单个文件挂进容器,改了宿主机文件容器里却没变
场景
Docker Compose 里把一个配置文件单独挂进容器:
services:
caddy:
image: caddy:2-alpine
volumes:
- /etc/myapp/Caddyfile:/etc/caddy/Caddyfile:ro
在宿主机上用 sed -i 改了
Caddyfile,再让容器重新加载配置,发现什么都没变。进容器
cat /etc/caddy/Caddyfile 一看,还是改之前的内容。
原因
每个文件在磁盘上有一个编号,叫 inode。单个文件挂载时,容器绑定的是挂载那一刻那个编号对应的文件,跟路径名无关。
sed -i
和不少编辑器保存文件的做法是:先写一个新文件,再改名盖掉原来的。新文件的编号变了,宿主机上同一个路径已经指向新文件,容器里挂着的却还是旧的那个。
用 ls -i 能看到编号,改之前和改之后各看一次就清楚了:
$ ls -i Caddyfile
1312234 Caddyfile
$ sed -i 's/old/new/' Caddyfile
$ ls -i Caddyfile
1312240 Caddyfile # 编号变了
几种解决办法
-
原地写,不换文件:
cat Caddyfile.new > Caddyfile。用重定向往原文件里写内容,编号不变,容器里马上能看到。 - 改完重启或重建容器。容器启动时会按路径重新挂一次,自然挂上新文件。
- 挂整个目录,别挂单个文件。目录挂进去以后,目录里的文件怎么替换,容器里都能看到最新的。
目录也有同样的问题
挂目录时,如果把整个目录用 mv
换掉(比如发布静态网页时“新目录改名替换旧目录”),容器里看到的还是旧目录,道理一样。
我现在的做法是挂上一级目录,比如把 /srv/site-root 挂进去,网页放在它下面的
site/ 里。发布时先把新文件解压到 site.new/,再改名替换
site/。挂载点本身没换,容器里能看到新内容,替换也是一瞬间完成。