一個因Docker容器掛載引發(fā)的事故
背景
使用 docker 部署的 nginx,并且已經配置了文件掛載,參數如下:
- -v /deploy/nginx/conf.d/doc.crt:/etc/nginx/conf.d/doc.crt
- -v /deploy/nginx/conf.d/doc.key:/etc/nginx/conf.d/doc.key
- -v /deploy/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
后續(xù)因為技術原因,需要將一個 location 加上 *.html 禁用緩存,如下配置
if ($request_filename ~* .*\\.(htm|html)$) {
add_header Cache-Control "no-store";
}
于是就在宿主機上直接修改 /deploy/nginx/conf.d/default.conf 文件。
然后運行 docker exec -it pc-nginx nginx -s reload,使配置生效。
但是實際測試結果并沒有生效
還以為是配置加的不對,花了好長時間,改了幾種方式,結果都不行。
直到進入容器內,查看容器內的文件發(fā)現,文件根本就沒有改動!!!
重啟了這個容器,配置文件才進行了更新,問題得到解決。
思考
docker容器的掛載難道不是生效,而是要容器啟動的時候才會更新?
肯定不是這樣,容器內產生的日志內容,在容器外可以實時查看,難道是內->外是實時,外->內 是啟動的時候才加載?
如果是這樣,那我一直以為的docker掛載是理解錯了嗎,越想越不對???
探索
經過一番查閱資料,發(fā)現了這個:
Docker 中,mount volume 的原理是借用了 Linux Namespace 中的 Mount NameSpace,隔離系統中不同進程的掛載點視圖,實際文件是沒有變化。
在container中,bash 實際就是一個運行在宿主機上的進程,被Docker用Linux分別隔離了 Mount Namespace、UTS Namespace、IPC Namespace、PID Namespace、Network Namespace和User Namespace,使得它看上去好像運行在了一個獨立的、相對隔離的系統上,但實際它的一切資源都是宿主機在不同Namespace中的一個投影,文件也不例外。
Linux中,證明文件是否相同的根本途徑是,使用 stat命令,判斷其 inode,如果兩個文件的inode相同,兩個文件必定為同一文件,從而兩個文件的內容也必然相同。
docker本身不支持直接映射文件,使用docker映射文件時可能會出現問題 。
實踐
復現場景,驗證問題
- 創(chuàng)建文件
mkdir -p /opt/nginx
cd /opt/nginx
vi demo.conf
nginx內容如下:
server {
listen 80;
server_name gateway.cn;
location / {
proxy_pass http://localhost:7001/;
}
}
- 創(chuàng)建2個容器,一個映射目錄,一個映射文件
docker run --name n1-dir -v /opt/nginx:/etc/nginx/conf.d -d nginx
docker run --name n2-file -v /opt/nginx/demo.conf:/etc/nginx/conf.d/demo.conf -d nginx
- 再開啟兩個 shell ,進入容器內,查看文件
docker exec -ti n1-dir /bin/bash
docker exec -ti n2-file /bin/bash
cat /etc/nginx/conf.d/demo.conf
- 修改外部文件
- 簡單修改,加一行注釋# fadsff
- 重新查看文件
- 但是得到了相同的結果,這與預期不符,使用stat命令,容器外和2個容器內的demo.conf 文件都是同一個inode
和線上的環(huán)境得到的結果不一致,這就很令人費解。想到線上的環(huán)境不是用的root賬號部署的,難道和用戶也有關系?
創(chuàng)建demo用戶,再次嘗試
圖片
重試剛才的步驟,得到結果:
n3
圖片
n4
圖片
得到結果,在掛載conf.d 目錄時,文件可以得到正常更新,但是如果直接掛載文件,文件的內容并不會實時更新。
小結
docker部署容器需要進行掛載時,使用掛載目錄的方式,不要直接掛載文件。 掛載目錄不會出現宿主機文件更新,而容器中文件沒有更新的問題。