5 種快速查找容器文件系統(tǒng)中文件的方法
在下方公眾號(hào)后臺(tái)回復(fù):面試手冊(cè),可獲取杰哥匯總的 3 份面試 PDF 手冊(cè)。
如果你經(jīng)常使用容器,那么你很有可能希望在某個(gè)時(shí)刻查看正在運(yùn)行的容器的文件系統(tǒng)。也許容器無(wú)法正常運(yùn)行,你想讀取一些日志,也許你想檢查容器內(nèi)部的一些配置文件…或者,你可能像我一樣,想在該容器中的二進(jìn)制文件上放置一些 eBPF 探針(稍后將詳細(xì)介紹)。
不管原因是什么,在這篇文章中,我們將介紹一些可以用來(lái)檢查容器中的文件的方法。
我們將從研究容器文件系統(tǒng)的簡(jiǎn)單和通常推薦的方法開(kāi)始,并討論為什么它們不能總是工作。接下來(lái),我們將對(duì) Linux 內(nèi)核如何管理容器文件系統(tǒng)有一個(gè)基本的了解,我們將利用這一了解以不同但仍然簡(jiǎn)單的方式檢查文件系統(tǒng)。
方法一:Exec 到容器中
如果你快速搜索如何檢查容器的文件系統(tǒng),你會(huì)發(fā)現(xiàn)一個(gè)常見(jiàn)的解決方案是使用 Docker 命令:
docker?exec?-it?mycontainer?/bin/bash
這是一個(gè)很好的開(kāi)始。如果它能滿足你的所有需求,你應(yīng)該繼續(xù)使用它。
然而,這種方法的一個(gè)缺點(diǎn)是,它需要在容器中存在一個(gè) shell。如果容器中沒(méi)有/bin/bash、/bin/sh 或其他 shell,那么這種方法將不起作用。例如,我們?yōu)?Pixie 項(xiàng)目構(gòu)建的許多容器都是基于無(wú) distroless 的,并且沒(méi)有包含一個(gè) shell 來(lái)保持鏡像較小。在這些情況下,這種方法不起作用。
即使 shell 可用,你也無(wú)法訪問(wèn)所有你習(xí)慣使用的工具。因此,如果容器中沒(méi)有安裝 grep,那么你也不能訪問(wèn) grep。這是另一個(gè)找更好工作的理由。
方法二:使用 nsenter
如果你再深入一點(diǎn),就會(huì)意識(shí)到容器進(jìn)程與 Linux 主機(jī)上的其他進(jìn)程一樣,只是在命名空間中運(yùn)行,以使它們與系統(tǒng)的其他部分隔離。
所以你可以使用 nsenter 命令來(lái)輸入目標(biāo)容器的命名空間,使用類(lèi)似這樣的東西:
#?Get?the?host?PID?of?the?process?in?the?container
PID=$(docker?container?inspect?mycontainer?|?jq?'.[0].State.Pid')
#?Use?nsenter?to?go?into?the?container’s?mount?namespace.
sudo?nsenter?-m?-t?$PID?/bin/bash
它進(jìn)入目標(biāo)進(jìn)程的掛載(-m)命名空間(-t $PID),并運(yùn)行/bin/bash。進(jìn)入掛載命名空間本質(zhì)上意味著我們獲得容器所看到的文件系統(tǒng)視圖。
這種方法似乎比 docker 的 exec 方法更有前途,但也遇到了類(lèi)似的問(wèn)題:它要求目標(biāo)容器中包含/bin/bash(或其他 shell)。如果我們輸入的不是掛載命名空間,我們?nèi)匀豢梢栽L問(wèn)主機(jī)上的文件,但是因?yàn)槲覀兪窃趫?zhí)行/bin/bash(或其他 shell)之前輸入掛載命名空間,所以如果掛載命名空間中沒(méi)有 shell,我們就不走運(yùn)了。
方法三:使用 docker 復(fù)制
解決這個(gè)問(wèn)題的另一種方法是簡(jiǎn)單地將相關(guān)文件復(fù)制到主機(jī),然后使用復(fù)制的文件。
要從正在運(yùn)行的容器中復(fù)制選定的文件,可以使用:
docker?cp?mycontainer:/path/to/file?file
也可以用以下方法來(lái)快照整個(gè)文件系統(tǒng):
docker?export?mycontainer?-o?container_fs.tar
這些命令使你能夠檢查文件,當(dāng)容器可能沒(méi)有 shell 或你需要的工具時(shí),這些命令比前兩種方法有了很大的改進(jìn)。
方法四:在主機(jī)上查找文件系統(tǒng)
復(fù)制方法解決了我們的許多問(wèn)題,但是如果你試圖監(jiān)視日志文件呢?或者,如果你試圖將 eBPF 探針部署到容器中的文件中,又該怎么辦呢?在這些情況下,復(fù)制是不起作用的。
我們希望直接從主機(jī)訪問(wèn)容器的文件系統(tǒng)。容器的文件應(yīng)該在主機(jī)的文件系統(tǒng)中,但是在哪里呢?
Docker 的 inspect 命令給了我們一個(gè)線索:
docker?container?inspect?mycontainer?|?jq?'.[0].GraphDriver'
這給我們:
{
??"Data":?{
????"LowerDir":?"/var/lib/docker/overlay2/63ec1a08b063c0226141a9071b5df7958880aae6be5dc9870a279a13ff7134ab-init/diff:/var/lib/docker/overlay2/524a0d000817a3c20c5d32b79c6153aea545ced8eed7b78ca25e0d74c97efc0d/diff",
????"MergedDir":?"/var/lib/docker/overlay2/63ec1a08b063c0226141a9071b5df7958880aae6be5dc9870a279a13ff7134ab/merged",
????"UpperDir":?"/var/lib/docker/overlay2/63ec1a08b063c0226141a9071b5df7958880aae6be5dc9870a279a13ff7134ab/diff",
????"WorkDir":?"/var/lib/docker/overlay2/63ec1a08b063c0226141a9071b5df7958880aae6be5dc9870a279a13ff7134ab/work"
??},
??"Name":?"overlay2"
}
讓我們來(lái)分析一下:
LowerDir:包含容器內(nèi)所有層的文件系統(tǒng),最后一層除外
UpperDir:容器最上層的文件系統(tǒng)。這也是反映任何運(yùn)行時(shí)修改的地方。
MergedDir:文件系統(tǒng)所有層的組合視圖。
WorkDir:用于管理文件系統(tǒng)的內(nèi)部工作目錄。

基于 overlayfs 的容器文件系統(tǒng)結(jié)構(gòu)
因此,要查看容器中的文件,只需查看 MergedDir 路徑。
sudo?ls?/var/lib/docker/overlay2/63ec1a08b063c0226141a9071b5df7958880aae6be5dc9870a279a13ff7134ab/merged
如果你想了解文件系統(tǒng)工作的更多細(xì)節(jié),你可以查看 Martin Heinz 關(guān)于 overlay 文件系統(tǒng)的博客文章:https://martinheinz.dev/blog/44。
方法五:/proc//root
把最好的留到最后,還有一種從主機(jī)找到容器文件系統(tǒng)的更簡(jiǎn)單的方法。使用容器內(nèi)進(jìn)程的宿主 PID,你可以簡(jiǎn)單地運(yùn)行:
sudo?ls?/proc//root
Linux 已經(jīng)為你提供了進(jìn)程掛載命名空間的視圖。
此時(shí),你可能會(huì)想:為什么我們不采用這種方法,并將其變成一篇只有一行字的博客文章呢?但這都是關(guān)于旅程,對(duì)吧?
彩蛋:/proc/
出于好奇,方法四中討論的關(guān)于容器 overlay 文件系統(tǒng)的所有信息也可以直接從 Linux /proc 文件系統(tǒng)中發(fā)現(xiàn)。如果你查看/proc/
2363?1470?0:90?/?/?rw,relatime?master:91?-?overlay?overlay?rw,lowerdir=/var/lib/docker/overlay2/l/YZVAVZS6HYQHLGEPJHZSWTJ4ZU:/var/lib/docker/overlay2/l/ZYW5O24UWWKAUH6UW7K2DGV3PB,upperdir=/var/lib/docker/overlay2/63ec1a08b063c0226141a9071b5df7958880aae6be5dc9870a279a13ff7134ab/diff,workdir=/var/lib/docker/overlay2/63ec1a08b063c0226141a9071b5df7958880aae6be5dc9870a279a13ff7134ab/work
2364?2363?0:93?/?/proc?rw,nosuid,nodev,noexec,relatime?-?proc?proc?rw
2365?2363?0:94?/?/dev?rw,nosuid?-?tmpfs?tmpfs?rw,size=65536k,mode=755,inode64
…
在這里,你可以看到容器已經(jīng)掛載了一個(gè)覆蓋文件系統(tǒng)作為它的根。它還報(bào)告與 docker inspect 報(bào)告相同類(lèi)型的信息,包括容器文件系統(tǒng)的 LowerDir 和 UpperDir。它沒(méi)有直接顯示 MergedDir,但你可以直接使用 UpperDir 并將 diff 改為 merged,這樣你就可以看到容器的文件系統(tǒng)了。
我們?cè)?Pixie 怎么用這個(gè)
在本博客的開(kāi)頭,我提到了 Pixie 項(xiàng)目需要如何在容器上放置 eBPF 探針。為什么和如何?
Pixie 內(nèi)部的 Stirling 模塊負(fù)責(zé)收集可觀察數(shù)據(jù)。由于是 k8s 原生的,所以收集的很多數(shù)據(jù)都來(lái)自于在容器中運(yùn)行的應(yīng)用程序。Stirling 還使用 eBPF 探針從它監(jiān)視的進(jìn)程中收集數(shù)據(jù)。例如,Stirling 在 OpenSSL 上部署 eBPF 探針來(lái)跟蹤加密的消息(如果你想了解更多有關(guān)這方面的細(xì)節(jié),請(qǐng)參閱SSL 跟蹤博客[1])。
由于每個(gè)容器都捆綁了自己的 OpenSSL 和其他庫(kù),因此 Stirling 部署的任何 eBPF 探針都必須位于容器內(nèi)的文件上。因此,Stirling 使用本文中討論的技術(shù)在 K8s 容器中找到感興趣的庫(kù),然后從主機(jī)將 eBPF 探針部署到這些二進(jìn)制文件上。
下圖概述了在另一個(gè)容器中部署 eBPF 探針的工作方式。

Stirling 通過(guò)掛載主機(jī)文件系統(tǒng)在其他容器上部署 eBPF 探針,然后在主機(jī)上找到目標(biāo)容器文件系統(tǒng)。
總結(jié)
下次當(dāng)你需要檢查容器中的文件時(shí),希望你能?chē)L試一下這些技巧。一旦你體驗(yàn)到不再受容器有沒(méi)有 shell 限制的自由,你可能就再也不會(huì)回去了。只需要訪問(wèn)/proc/
參考資料
[1] SSL 跟蹤博客:https://blog.px.dev/ebpf-openssl-tracing/
英文原文地址:https://blog.px.dev/container-filesystems/
轉(zhuǎn)載:「CNCF」
原文:https://tinyurl.com/56nu8xra
推薦閱讀
MySQL 性能優(yōu)化的 9 種知識(shí),面試再也不怕了!
分享 18 個(gè) 實(shí)用 Linux 運(yùn)維命令及知識(shí)!

