Posted on :: ::

在 Synology NAS 的 Container Manager 應用程式查看映像檔頁面時,有時會看到部分映像檔的「標籤」顯示為 <none>。此時,該映像檔並不見得是正在被某些容器引用,而是單純由於過去更新映像檔或重新建置程式後,原本的標籤可能轉移到新版本,舊映像檔便留所導致。倘若使用者嘗試對該映像檔點擊「刪除」按鈕或是使用「未使用映像檔」按鈕來進行移除時,會發現無法正常移除。在這種情況下,我們可能就需要使用 SSH 登入來進行手動操作,以此清除這些殘留的映像檔——僅限於具備管理員權限的帳號才得以執行此操作 1。

一、列出目前的映像檔

在 Synology 常見的預設權限設定下,執行 Docker 指令需要加上 sudo,以取得存取 Docker 服務所需的權限。因此,本文統一使用 sudo docker ...。若出現密碼提示,請輸入目前登入 NAS 的帳號密碼——於終端機輸入密碼時不顯示字元是正常現象,切莫驚慌。

除了 Container Manager 應用程式映像檔頁面所顯示的映像檔清單之外,實際上還可能有更多的映像檔存在。我們可以使用以下指令列出目前 NAS 上的所有映像檔,並且查看每個映像檔的名稱、標籤、映像檔 ID、建立時間與大小:

sudo docker images

上該指令的輸出可能像這樣:

REPOSITORY             TAG       IMAGE ID       CREATED       SIZE
linuxserver/freshrss    latest    3cde83130d3e   9 hours ago   117MB
<none>                 <none>    33bde938ab3e   38 hours ago  1.98GB
gotson/komga            <none>    3ce1ec113eba   3 weeks ago   483MB
python                 <none>    691aea61c5aa   8 weeks ago   1.12GB

關於上述表格的各個欄位的說明如下:

欄位用途說明
REPOSITORY映像檔的儲存庫名稱,例如 python、gotson/komga。
TAG映像檔標籤,例如 latest 或版本號碼。
IMAGE ID映像檔識別碼,後續可用它查詢引用容器或移除映像檔。
CREATED映像檔建立至今的時間,不是下載到 NAS 的時間。
SIZE映像檔及其所含各層的大小。不同映像檔可能共享層,不能直接相加當作可回收空間。

我們可以觀察到當 REPOSITORY 為 <none> 的項目,並不會於Container Manager 應用程式映像檔頁面所顯示。通常當某個映像檔的 REPOSITORY 值和 TAG 值都是 <none> 時,是因為更新或重新建置後留下的。若只有 TAG 是 <none>,則不一定;可能是透過 digest(內容摘要識別碼)保留的映像檔 2,不能單憑這個欄位判定是否可刪除。

二、清理符合條件的懸空映像檔

我們可以從上一小節的內容中知道 <none> 不等於「未被使用」,它只表示對應欄位沒有名稱或標籤,仍可能有容器引用該映像檔。因此,正確的處理方式是列出映像檔後,根據符合條件的映像檔進行初步清理;最後再查詢剩餘映像檔是否有容器使用,手動進行個別移除。以下為初步清理的對應指令,他會清理被 Docker 判定為 dangling(懸空),且沒有被任何容器引用的映像檔:

sudo docker image prune

執行後會出現確認提示:

WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N]

確認要清理後,輸入 y 並按 Enter 即可。Docker 會列出清理結果,最後顯示 Total reclaimed space,代表這次實際釋放的空間。這個指令會保留執行中或已停止容器仍引用的映像檔,也不會刪除容器、資料卷或掛載資料夾。不過,若有需要保留的本地建置成果或回退版本,應先加上標籤或備份,再進行清理。

三、查詢是哪個容器使用映像檔

執行完上一節的述指令後,我們可以確認清理後還有哪些映像檔:

sudo docker images

此時可能仍有 REPOSITORY 值和 TAG 值都是 <none> 的映像檔存在,這並不代表該映像檔一定正在使用;藉由將下述指令中的 xxxx 換成上一個指令所看到的 IMAGE ID 可接著查詢容器的引用狀況,找出使用指定映像檔,或使用其衍生映像檔的容器,並以表格列出容器 ID、名稱、映像檔及狀態:

sudo docker ps -a --filter ancestor=xxxx --format "table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}"

關於上述指令中的各個指令或參數的說明如下:

指令或參數用途說明
sudo以提升的權限執行指令。
docker ps列出容器。
-a包含所有狀態的容器,尤其是已停止但尚未移除的容器。
--filter ancestor=xxxx篩選使用指定映像檔或其衍生映像檔的容器。
--format "table ..."指定表格欄位;{{.ID}}、{{.Names}}、{{.Image}}、{{.Status}} 分別是容器 ID、名稱、映像檔與狀態,\t 用來分隔欄位。

例如,查詢前面清單中的 Python:

# 查詢 Python 映像檔的引用容器
sudo docker ps -a --filter ancestor=691aea61c5aa --format "table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}"

查詢結果中的 NAMES 是容器名稱,STATUS 則顯示目前狀態。Up 表示正在執行,Exited 表示已停止;已停止的容器仍可能引用映像檔。

如果只有以下表頭而沒有任何資料列,則表示目前查詢的 Docker 服務中,沒有找到符合條件的容器:

CONTAINER ID   NAMES   IMAGE   STATUS

若有查到結果,則應先確認該容器的用途;由於 ancestor 也會匹配衍生映像檔,結果不一定全部直接使用同一個 IMAGE ID。

四、個別移除不再需要的映像檔

可以將下述指令中的 xxxx 換成 IMAGE ID 來移除經上一節指令確認不再需要的映像檔:

sudo docker rmi xxxx

上述指令中的 docker rmi 等同於 docker image rm,與用來移除容器的 docker rm 不同例如,且可以一次指定兩個 ID。範例如下:

sudo docker rmi 691aea61c5aa 3ce1ec113eba

我們可以於最後執行以下第一節所提出的指令,確認目標映像檔已從清單中移除:

sudo docker images

參考資料

Table of Contents