嘿哈布爾! 我提請您注意這篇文章的翻譯
今天我們將討論 Docker 如何使用主機的磁碟空間,我們也將弄清楚如何從未使用的映像和容器的碎片中釋放該空間。
總消費
Docker 是一個很酷的東西,今天可能很少有人懷疑它。 就在幾年前,該產品為我們提供了一種全新的方式來建置、交付和運行任何環境,使我們能夠大幅節省 CPU 和 RAM 資源。 除此之外(對某些人來說這將是最重要的)Docker 使我們能夠大幅簡化和統一生產環境的生命週期管理。
然而,現代生活的所有這些樂趣都是有代價的。 當我們運行容器、下載或建立自己的鏡像以及部署複雜的生態系統時,我們必須付費。 除此之外,我們還用磁碟空間來支付費用。
如果您從未考慮過 Docker 在您的電腦上實際佔用了多少空間,您可能會對以下命令的輸出感到驚訝:
$ docker system df
這顯示了 Docker 在不同上下文中的磁碟使用情況:
- 圖像 – 從圖像存儲庫下載並在您的系統上建立的圖像的總大小;
- 容器 – 運作容器所使用的磁碟空間總量(指所有容器讀寫層的總體積);
- 本機磁碟區 – 安裝到容器的本機儲存磁碟區;
- 建置快取 – 映像建置過程產生的臨時檔案(使用 BuildKit 工具,從 Docker 版本 18.09 開始可用)。
我敢打賭,在這個簡單的轉移之後,您會渴望清除磁碟上的垃圾並讓寶貴的千兆位元組重新煥發活力(注意:特別是如果您每月為這些千兆位元組支付租金)。
容器的磁碟使用情況
每次在宿主機上建立容器時,都會在 /var/lib/docker 目錄下建立幾個檔案和目錄,其中值得注意的是:
- 目錄 /var/lib/docker/containers/container_ID – 使用標準日誌記錄驅動程式時,這是以 JSON 格式儲存事件日誌的位置。 過於詳細的日誌以及無人讀取或以其他方式處理的日誌通常會導致磁碟變滿。
- /var/lib/docker/overlay2 目錄包含容器讀寫層(overlay2 是大多數 Linux 發行版中的首選驅動程式)。 如果容器將資料儲存在其檔案系統中,則資料將放置在該目錄中。
讓我們想像一個安裝了原始 Docker 的系統,它從未參與啟動容器或建置映像。 其磁碟空間使用報告如下所示:
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 0 0 0B 0B
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
讓我們啟動一些容器,例如 NGINX:
$ docker container run --name www -d -p 8000:80 nginx:1.16
磁碟會發生什麼情況:
- 映像佔用126 MB,這與我們在容器中啟動的NGINX相同;
- 容器佔據了荒謬的 2 個位元組。
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 1 126M 0B (0%)
Containers 1 1 2B 0B (0%)
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
從結論來看,我們還沒有任何可以釋放的空間。 由於 2 個位元組完全是無意義的,讓我們想像一下,我們的 NGINX 意外地在某個地方寫入了 100 MB 的數據,並在其內部創建了一個恰好這個大小的檔案 test.img。
$ docker exec -ti www
dd if=/dev/zero of=test.img bs=1024 count=0 seek=$[1024*100]
讓我們再次檢查一下主機上的磁碟空間使用情況。 我們將看到該容器(多個容器)佔據了 100 MB 的空間。
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 1 126M 0B (0%)
Containers 1 1 104.9MB 0B (0%)
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
我想你好奇的大腦已經想知道我們的 test.img 檔案位於哪裡。 我們來找:
$ find /var/lib/docker -type f -name test.img
/var/lib/docker/overlay2/83f177...630078/merged/test.img
/var/lib/docker/overlay2/83f177...630078/diff/test.img
無需詳細說明,我們可以注意到 test.img 檔案很方便地位於讀寫級別,由overlay2 驅動程式控制。 如果我們停止容器,主機會告訴我們原則上可以釋放該空間:
# Stopping the www container
$ docker stop www
# Visualizing the impact on the disk usage
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 1 126M 0B (0%)
Containers 1 0 104.9MB 104.9MB (100%)
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
我們該怎麼做? 透過刪除容器,需要清除讀寫層級的對應空間。
使用以下命令,您可以一舉刪除所有已安裝的容器,並清除磁碟上由它們建立的所有讀寫檔案:
$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
5e7f8e5097ace9ef5518ebf0c6fc2062ff024efb495f11ccc89df21ec9b4dcc2
Total reclaimed space: 104.9MB
因此,我們透過刪除容器釋放了 104,9 MB。 但由於我們不再使用先前下載的圖像,它也成為刪除和釋放資源的候選者:
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 1 0 126M 126M (100%)
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
注意:只要該鏡像至少被一個容器使用,您就無法使用此技巧。
我們上面使用的 prune 子命令僅對停止的容器有效。 如果我們不僅想刪除已停止的容器,還想刪除正在運行的容器,我們應該使用以下命令之一:
# Historical command
$ docker rm -f $(docker ps –aq)
# More recent command
$ docker container rm -f $(docker container ls -aq)
附註:如果在啟動容器時使用 -rm 參數,那麼當容器停止時,它所佔用的所有磁碟空間將會被釋放。
使用磁碟映像
幾年前,數百兆位元組的映像大小是完全正常的:Ubuntu 映像重達 600 兆位元組,Microsoft .Net 映像重達數千兆位元組。 在那些雜亂的日子裡,即使您在映像之間共享關卡,僅下載一張映像也可能會嚴重佔用您的可用磁碟空間。 今天——讚美偉大的人——圖像的重量要輕得多,但即便如此,如果您不採取一些預防措施,您也可以很快填滿可用資源。
有幾種類型的圖像對最終用戶來說不直接可見:
- 中間圖像,在此基礎上收集其他圖像 - 如果您使用基於這些“其他”圖像的容器,則無法刪除它們;
- 懸空圖像是任何正在運行的容器都沒有引用的中間圖像 - 它們可以被刪除。
- 使用以下命令,您可以檢查系統上是否存在懸空圖像:
$ docker image ls -f dangling=true
REPOSITORY TAG IMAGE ID CREATED SIZE
none none 21e658fe5351 12 minutes ago 71.3MB
您可以透過以下方式刪除它們:
$ docker image rm $(docker image ls -f dangling=true -q)
我們也可以使用 prune 子命令:
$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:143407a3cb7efa6e95761b8cd6cea25e3f41455be6d5e7cda
deleted: sha256:738010bda9dd34896bac9bbc77b2d60addd7738ad1a95e5cc
deleted: sha256:fa4f0194a1eb829523ecf3bad04b4a7bdce089c8361e2c347
deleted: sha256:c5041938bcb46f78bf2f2a7f0a0df0eea74c4555097cc9197
deleted: sha256:5945bb6e12888cf320828e0fd00728947104da82e3eb4452f
Total reclaimed space: 12.9kB
如果我們突然想用一個命令完全刪除所有圖像(而不僅僅是懸空的圖像),那麼我們可以這樣做:
$ docker image rm $(docker image ls -q)
磁碟使用量(按磁碟區)
卷用於在容器檔案系統外部儲存資料。 例如,如果我們想要保存應用程式的結果以便以其他方式使用它們。 一個常見的例子是資料庫。
讓我們啟動一個 MongoDB 容器,安裝容器外部的捲,並從中還原資料庫備份(我們在 bck.json 檔案中提供了它):
# Running a mongo container
$ docker run --name db -v $PWD:/tmp -p 27017:27017 -d mongo:4.0
# Importing an existing backup (from a huge bck.json file)
$ docker exec -ti db mongoimport
--db 'test'
--collection 'demo'
--file /tmp/bck.json
--jsonArray
資料將位於主機上的 /var/lib/docker/volumes 目錄中。 但為什麼不在容器的讀寫層級呢? 因為在 MongoDB 映像的 Dockerfile 中,/data/db 目錄(MongoDB 預設儲存資料的位置)被定義為磁碟區。
附註:許多必須產生資料的影像使用磁碟區來儲存該資料。
當我們使用 MongoDB 足夠多並停止(甚至刪除)容器時,磁碟區將不會被刪除。 它將繼續佔用我們寶貴的磁碟空間,直到我們使用以下命令明確刪除它:
$ docker volume rm $(docker volume ls -q)
好吧,或者我們可以使用我們已經熟悉的 prune 子命令:
$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
d50b6402eb75d09ec17a5f57df4ed7b520c448429f70725fc5707334e5ded4d5
8f7a16e1cf117cdfddb6a38d1f4f02b18d21a485b49037e2670753fa34d115fc
599c3dd48d529b2e105eec38537cd16dac1ae6f899a123e2a62ffac6168b2f5f
...
732e610e435c24f6acae827cd340a60ce4132387cfc512452994bc0728dd66df
9a3f39cc8bd0f9ce54dea3421193f752bda4b8846841b6d36f8ee24358a85bae
045a9b534259ec6c0318cb162b7b4fca75b553d4e86fc93faafd0e7c77c79799
c6283fe9f8d2ca105d30ecaad31868410e809aba0909b3e60d68a26e92a094da
Total reclaimed space: 25.82GB
luc@saturn:~$
使用磁碟作為鏡像建構緩存
在 Docker 18.09 中,由於 BuildKit 工具,映像建立過程發生了一些變化。 這個東西提高了處理速度並優化了資料儲存和安全管理。 在這裡,我們不會考慮這個精彩工具的所有細節;我們只會關注它如何解決磁碟空間使用問題。
假設我們有一個完全簡單的 Node.Js 應用程式:
- index.js 檔案啟動一個簡單的 HTTP 伺服器,該伺服器對收到的每個請求回應一行:
- package.json檔案定義了依賴項,其中僅使用expressjs來執行HTTP伺服器:
$ cat index.js
var express = require('express');
var util = require('util');
var app = express();
app.get('/', function(req, res) {
res.setHeader('Content-Type', 'text/plain');
res.end(util.format("%s - %s", new Date(), 'Got Request'));
});
app.listen(process.env.PORT || 80);
$ cat package.json
{
"name": "testnode",
"version": "0.0.1",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.14.0"
}
}
用於建置映像的 Dockerfile 如下所示:
FROM node:13-alpine
COPY package.json /app/package.json
RUN cd /app && npm install
COPY . /app/
WORKDIR /app
EXPOSE 80
CMD ["npm", "start"]
讓我們以通常的方式建立鏡像,而不使用 BuildKit:
$ docker build -t app:1.0 .
如果我們檢查磁碟空間使用情況,我們可以看到只有基礎鏡像(node:13-alpine)和目標鏡像(app:1.0)佔據了空間:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 2 0 109.3MB 109.3MB (100%)
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
讓我們使用 BuildKit 來建立應用程式的第二個版本。 為此,我們只需將 DOCKER_BUILDKIT 變數設為 1:
$ DOCKER_BUILDKIT=1 docker build -t app:2.0 .
如果我們現在檢查磁碟使用情況,我們將看到建置快取(buid-cache)現在參與其中:
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 2 0 109.3MB 109.3MB (100%)
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 11 0 8.949kB 8.949kB
若要清除它,請使用以下命令:
$ docker builder prune
WARNING! This will remove all dangling build cache.
Are you sure you want to continue? [y/N] y
Deleted build cache objects:
rffq7b06h9t09xe584rn4f91e
ztexgsz949ci8mx8p5tzgdzhe
3z9jeoqbbmj3eftltawvkiayi
Total reclaimed space: 8.949kB
全部清除!
因此,我們研究了清理容器、映像和磁碟區所佔用的磁碟空間。 prune 子命令可以幫助我們完成此任務。 但它也可以在 docker 系統層級使用,並且它將清理它可以清理的所有內容:
$ docker system prune
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- all dangling build cache
Are you sure you want to continue? [y/N]
如果您出於某種原因要在執行 Docker 的電腦上節省磁碟空間,那麼定期執行此命令應該成為一種習慣。
來源: www.habr.com