상황
작업 트리를 많이 만들어 쓰는 호스트에서는 디스크가 조용히 찬다. 트리마다 빌드 산출물 폴더가 수 기가바이트씩 붙기 때문이다. 경보가 울리면 한동안 아무도 쓰지 않은 트리의 산출물 폴더를 골라 지우는데, 이때 지우기 전에 폴더마다 du를 재서 합계를 내 두는 경우가 많다. 그 합계가 곧 "확보한 공간"으로 보고된다.
무엇이 어긋나는가
정리가 끝난 뒤 실제 여유 공간을 보면 합계보다 훨씬 적게 늘어나 있다. 계산이 틀린 게 아니라 질문이 틀렸다. du는 "이 경로 아래에 몇 바이트가 보이는가"를 답할 뿐, "이걸 지우면 몇 바이트가 돌아오는가"를 답하지 않는다. 두 질문의 답이 같은 건 모든 파일이 그 경로에만 존재할 때뿐이다.
왜 차이가 나는가
하드링크로 만든 트리나 캐시를 공유하는 빌드 도구는 같은 내용을 여러 경로에서 가리킨다. du를 폴더마다 따로 실행하면 같은 inode가 폴더 수만큼 세어진다. 한 번의 du 호출은 같은 inode를 한 번만 세지만, 사람이 결과를 모아 더하는 순간 그 보호가 사라진다. 게다가 지운 폴더 밖에 링크가 하나라도 남아 있으면 그 파일의 블록은 해제되지 않는다. 열린 채로 지워진 파일도 프로세스가 놓아줄 때까지 공간을 붙잡고 있다.
올바른 측정
정리 직전과 직후에 같은 마운트 지점의 df 여유 공간을 기록하고, 그 차이를 확보량으로 보고한다. 다른 작업이 동시에 디스크를 쓰고 있다면 그 사실도 함께 적는다. 폴더별 du는 무엇을 지울지 고르는 우선순위로만 쓰고, 결과 보고에는 쓰지 않는다. 합계가 필요하면 대상 폴더를 한 번의 du 호출에 모두 넘겨 중복 inode를 한 번만 세게 한다.
기대치 조정
하드링크를 많이 쓰는 환경일수록 정리의 효과는 원본이 함께 사라질 때 나타난다. 파생 트리의 산출물만 지우면 기대보다 적게 돌아오는 게 정상이다. 경보 기준선 아래로 확실히 내려가야 한다면, 무엇이 실제로 공간을 독점하고 있는지부터 링크 수 1인 파일을 기준으로 찾는다.
확인 방법
보고서에 적는 숫자는 df 전후 값 두 개와 그 차이 하나면 충분하다. 다음 경보에서 누군가 "지난번엔 80기가를 비웠다는데 왜 또 찼지"라고 물을 때, 처음부터 진짜 숫자가 적혀 있으면 헛된 추적을 하지 않아도 된다.
The setup
On a host that spins up lots of worktrees, the disk fills quietly: every tree grows a build-output folder of several gigabytes. When the alert fires, you pick the output folders of trees nobody has touched in a while and delete them, and it is common to run du on each one first and total the results. That total then gets reported as "space reclaimed".
What goes wrong
After the cleanup, actual free space has grown by far less than the total. The arithmetic is fine; the question was wrong. du answers "how many bytes are visible under this path", not "how many bytes come back if I delete it". The two answers only agree when every file exists at that path and nowhere else.
Why they differ
Hardlinked trees and build tools that share caches point at the same content from many paths. Run du separately on each folder and the same inode is counted once per folder. A single du invocation counts a shared inode only once, but that protection disappears the moment a person adds the separate results together. On top of that, if even one link survives outside the deleted folders, that file's blocks are not freed. A file deleted while still open also holds its space until the process lets go.
Measure it properly
Record df free space on the same mount point immediately before and after the cleanup, and report the difference as the amount reclaimed. If other work is writing to the disk at the same time, say so alongside the number. Use per-folder du only to rank what to delete, never to report the result. If you do need a total, pass every target folder to one du invocation so shared inodes are counted once.
Adjust expectations
The more an environment relies on hardlinks, the more a cleanup's effect depends on the originals going away too. Deleting only the outputs of derived trees will reclaim less than you hoped, and that is normal. If you must land firmly below the alert line, first find what actually owns the space by looking at files with a link count of one.
How to check
The numbers in the report can be just two df readings and their difference. The next time an alert fires and someone asks "we freed 80 gigabytes last time, why is it full again", having the real number written down from the start saves a pointless investigation.
场景
在频繁创建工作树的主机上,磁盘会悄无声息地被填满:每棵树都会长出一个几 GB 的构建产物目录。告警一响,人们就挑出那些很久没人动的树,删掉它们的产物目录;而且常常会在删除前逐个目录跑一遍 du,把结果加起来。这个总和随后就被当作“腾出的空间”上报。
哪里出了偏差
清理完再看实际可用空间,增加的量远小于那个总和。算术没错,是问题问错了。du 回答的是“这个路径下能看到多少字节”,而不是“删掉它能回收多少字节”。只有当每个文件都只存在于这个路径、别处没有时,两个答案才一致。
为什么会不同
用硬链接搭的工作树,以及共享缓存的构建工具,会让多个路径指向同一份内容。对每个目录分别运行 du,同一个 inode 就会按目录数被重复计算。单次 du 调用只会把共享的 inode 计一次,但人工把几次结果相加时,这层保护就没了。此外,只要被删目录之外还留着任意一个链接,这个文件的数据块就不会被释放。仍被打开却已删除的文件,也会一直占着空间,直到进程放手。
正确的测法
在清理之前和之后,立刻记录同一挂载点的 df 可用空间,用两者之差作为回收量上报。如果同时还有别的任务在写这块盘,也要在数字旁边注明。逐目录的 du 只用来决定先删什么,不用来汇报结果。确实需要总量时,把所有目标目录交给同一次 du 调用,让共享的 inode 只计一次。
调整预期
越是依赖硬链接的环境,清理的效果就越取决于原始文件是否也一起消失。只删派生树的产物,回收得比预期少是正常的。如果必须稳稳降到告警线以下,先按链接数为 1 的文件去找,弄清到底是谁真正独占着空间。
如何确认
报告里的数字,只需要前后两次 df 读数和它们的差值。下次告警时如果有人问“上次不是说腾出了 80 GB 吗,怎么又满了”,一开始就写下真实数字,能省掉一场白费力气的追查。
状況
ワークツリーを大量に作るホストでは、ディスクは静かに埋まっていく。ツリーごとに数ギガバイトのビルド成果物フォルダが育つからだ。警報が鳴ると、しばらく誰も触っていないツリーの成果物フォルダを選んで消す。その前にフォルダごとに du を測って合計しておくことが多く、その合計がそのまま「空けた容量」として報告される。
何が食い違うか
片付けの後に実際の空き容量を見ると、合計よりずっと少ししか増えていない。計算が間違っているのではなく、問いが間違っている。du が答えるのは「このパスの下に何バイト見えるか」であって、「これを消すと何バイト戻るか」ではない。二つの答えが一致するのは、すべてのファイルがそのパスにだけ存在するときに限られる。
なぜ差が出るのか
ハードリンクで作ったツリーや、キャッシュを共有するビルドツールは、同じ中身を複数のパスから指している。フォルダごとに別々に du を実行すると、同じ inode がフォルダの数だけ数えられる。一回の du 呼び出しなら共有 inode は一度しか数えないが、人が結果を集めて足した瞬間にその保護は消える。さらに、消したフォルダの外にリンクが一つでも残っていれば、そのファイルのブロックは解放されない。開いたまま削除されたファイルも、プロセスが手放すまで容量を握り続ける。
正しい測り方
片付けの直前と直後に、同じマウントポイントの df の空き容量を記録し、その差を確保量として報告する。同時に別の作業がディスクに書いているなら、それも数字の横に書く。フォルダごとの du は何を消すかの優先順位づけにだけ使い、結果の報告には使わない。合計が必要なら、対象フォルダをすべて一回の du 呼び出しに渡し、共有 inode を一度だけ数えさせる。
期待値の調整
ハードリンクに頼る環境ほど、片付けの効果は元のファイルも一緒に消えるかどうかで決まる。派生ツリーの成果物だけを消して、期待より少なく戻るのは正常だ。警報ラインを確実に下回る必要があるなら、まずリンク数が 1 のファイルを手がかりに、何が本当に容量を独占しているのかを探す。
確かめ方
報告に書く数字は、前後二回の df の値とその差だけで足りる。次の警報で誰かが「前回 80 ギガ空けたはずなのに、なぜまた埋まったのか」と聞いたとき、最初から本当の数字が書いてあれば、無駄な追跡をせずに済む。