Docker: безпечне очищення — поверніть місце без видалення потрібного

Було корисно?

Це завжди трапляється в найневідповідніший момент: нода здорова, деплой зелений, а диск раптом заповнюється на 100%.
Раптом Docker перестає тягнути образи, runtime не може записувати логи, і все виглядає «таємниче нестабільним».
Спокуса виконати героїчну команду docker system prune -a --volumes і сподіватися на краще дуже велика.

Надія — не стратегія очищення. Це практичний, безпечний для продакшну спосіб повернути місце в Docker, зберігаючи те, що вам справді потрібно: правильні образи, правильні томи та можливість пояснити, що сталося пізніше.

Ментальна модель: де Docker зберігає дані (і чому це дивує)

Використання дискового простору Docker — це не «одна річ». Це кілька відер, які ростуть з різних причин, і «очищення Docker»
означає вибрати, які відра ви готові спорожнити.

Відра, що мають значення

  • Образи: шари, завантажені з реєстрів, плюс локально зібрані образи.
  • Контейнери: записуваний шар (зміни поверх образу) і метадані контейнера.
  • Томи: стійкі дані. Бази даних їх люблять. Так само й випадкові помилки.
  • Кеш збірки: проміжні шари та метадані кешу. У CI це перетворюється на компостну купу.
  • Логи: логи контейнерів (зазвичай JSON-файли) і логи додатків, які пишуться всередині контейнерів.
  • «Інше»: мережі, плагіни, об’єкти swarm і залишки від старих драйверів збереження.

Більшість аварій походять від плутанини між цими відрами. Люди ставляться до томів як до кешу. Це не кеш.
Люди вважають, що кеш збірки «сам себе врегулює». Ні. Люди ігнорують логи, поки ті не стануть атакою доступності.

Чому overlay2 створює відчуття «моторошного» використання диску

На Linux найпоширеніший драйвер збереження Docker — overlay2. Він зберігає шари образів і записувані шари контейнера як директорії під Docker data root (часто /var/lib/docker).
Та директорія може стати гігантською, і не очевидно, який контейнер «володіє» якими її частинами.

OverlayFS ефективний, але дуже буквальний: кожна зміна файлом у файловій системі контейнера — це новий файл у його записуваному шарі. Якщо ваш додаток пише 50GB «тимчасових» даних у /tmp всередині контейнера,
це не тимчасово. Це 50GB на хості, і ви знову зустрінете їх, коли диск заповниться.

Правило: якщо ви хочете, щоб дані пережили видалення контейнера — покладіть їх у том. Якщо ви хочете, щоб дані було легко видаляти — кладіть їх у місце, де можна детерміновано видаляти: бажано в том, який ви зможете навмисно зруйнувати, або в шлях на хості з політикою збереження.

Цитата, яку варто тримати на видному місці. Фраза Джина Кранца відома в колах надійності: «Failure is not an option.» Це також нагадування, що очищення — це частина операцій, а не хобі після інциденту.

Цікаві факти та коротка історія (бо це пояснює сьогоднішній безлад)

  1. Початкові значення драйверів збереження Docker змінювалися з часом. Старіші системи використовували AUFS або Device Mapper; сучасні дистрибуції часто обрали overlay2 за кращу продуктивність і простоту.
  2. «Dangling images» з’явилися через те, що шари спільні. Docker зберігає шари, які можуть ще використовуватися. Він не видалить їх, поки не буде впевнений, що вони невикористовувані.
  3. BuildKit змінив поняття «кеш збірки». З BuildKit кеш може існувати в різних формах і може експортуватися/імпортуватися, що чудово — поки ви ніколи ним не займаєтесь.
  4. За замовчуванням логи контейнерів часто записуються у JSON-файли. Це зручно, але файли пишуться без обмеження, якщо не налаштувати ротацію.
  5. Помилки через заповнений диск на хостах контейнерів часто є вторинними. Першопричина — «якийсь процес записав надто багато», Docker лише зробив це легше сховати.
  6. Команди prune з’явилися, бо ручне очищення було занадто ризикованим. Docker додав високорівневі prune-операції, щоб уникнути прямої маніпуляції /var/lib/docker (що й досі погана ідея).
  7. Повторне використання шарів — і оптимізація, і пастка одночасно. Ребілди образів з великою кількістю унікальних шарів іноді пришвидшують pull/build, але завжди пришвидшують ріст диску.
  8. CI-раннери випадково повернули «пет-хости» в моду. «Безстанні» раннери, які ніколи не перевстановлюються, стають музеями кешованих шарів.

Жарт №1: Використання диску Docker — як шухляда зі сміттям — все в ній було «тимчасово» на той час.

Швидкий план діагностики: перша/друга/третя перевірки

Коли диск «кричить», у вас немає часу на філософські дебати про шари. Потрібна швидка послідовність, яка скаже, де байти і що можна безпечно видалити.

Перше: підтвердьте, що хост дійсно заповнений (і де саме)

  • Перевірте використання файлової системи (df) і використання інодів (df -i).
  • Визначте, який маунт повний. Якщо /var окремий, Docker може бути в порядку, а логи — ні.

Друге: виміряйте «відра» Docker перед тим, як щось видаляти

  • docker system df для отримання загального розподілу.
  • Зверніть увагу на найбільших «внесників»: образи, контейнери, томи, кеш збірки.

Третє: ідентифікуйте «неочікуваних записувачів»

  • Великі логи контейнерів (JSON-файли) і логи додатку всередині записуваних шарів контейнера.
  • Томи, що розростаються, бо база даних або черга накопичили дані.
  • Зростання кешу збірки на CI-раннерах.

Якщо це справжня надзвичайна ситуація (диск 100%), пріоритет — відновити можливість запису: обмежте/обріжте логи, зупиніть основних «порушників» або перемістіть дані з хоста. Потім виконайте безпечне очищення з аудитом.

Практичні завдання (команди + що означає вивід + рішення)

Ці завдання передбачають, що у вас є shell-доступ до Docker-хоста. Виконуйте їх послідовно, коли можете.
Якщо ви в моменті «диск повний, все горить», переходьте до завдань, позначених як дружні до надзвичайної ситуації.

Завдання 1: Перевірте, яка файлова система повна

cr0x@server:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2   80G   78G  1.2G  99% /
tmpfs            16G   92M   16G   1% /run
/dev/nvme1n1p1  200G   40G  160G  20% /data

Що це означає: Коренева файлова система майже повна. Зазвичай там живе /var/lib/docker.
Рішення: Зосередьтеся на Docker і системних логах на /, а не на просторому маунті /data.

Завдання 2: Перевірте виснаження інодів (хитро і часто)

cr0x@server:~$ df -i
Filesystem       Inodes  IUsed   IFree IUse% Mounted on
/dev/nvme0n1p2  5242880 5110000 132880   98% /

Що це означає: Ви майже вичерпали іноди. Це може статися через мільйони дрібних файлів (кеш збірки, node_modules тощо).
Рішення: «Вивільнення місця» може не вирішити проблему; потрібно видаляти багато дрібних файлів, зазвичай кеш або артефакти збірки.

Завдання 3: Знайдіть Docker data root

cr0x@server:~$ docker info --format '{{ .DockerRootDir }}'
/var/lib/docker

Що це означає: Це директорія, за ростом якої ви слідкуєте.
Рішення: Якщо ваша коренева файлова система мала, заплануйте міграцію Docker data root пізніше. Наразі діагностуйте використання всередині неї.

Завдання 4: Отримайте власну звітність Docker про диск

cr0x@server:~$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          52        12        38.6GB    21.4GB (55%)
Containers      19        7         2.4GB     1.1GB (45%)
Local Volumes   34        15        96.2GB    18.0GB (18%)
Build Cache     184       0         22.8GB    22.8GB (100%)

Що це означає: Найбільшу частку займають томи (96GB), але кеш збірки повністю можна повернути (22GB).
Рішення: Почніть з prune кешу збірки (досить безпечно), потім зображення/контейнери, що не використовуються. Томам — особливе ставлення.

Завдання 5: Перелічіть найбільші образи (знайдіть підозрюваних)

cr0x@server:~$ docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.Size}}' | head
REPOSITORY            TAG        IMAGE ID       SIZE
myapp/api             prod       9c1a2d4e0b2f   1.21GB
myapp/api             staging    62f13c9d1a11   1.19GB
postgres              15         2b6d1f2aa0c1   413MB
node                  20         1c2d0a41d2aa   1.11GB

Що це означає: У вас є кілька великих тегів для одного й того ж репозиторію. Часто так буває, коли деплоять кожен коміт.
Рішення: Залиште ті, що зараз працюють; видаліть старі теги, якщо вони не використовуються. Пізніше: впровадьте політику збереження в pipeline.

Завдання 6: Ідентифікуйте контейнери, що все ще використовують ці образи

cr0x@server:~$ docker ps --format 'table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Names}}'
CONTAINER ID   IMAGE                 STATUS          NAMES
c8b2d9f2a4aa   myapp/api:prod        Up 6 days       api-prod-1
e12a9a0110bb   postgres:15           Up 12 days      pg-main

Що це означає: Активно використовується лише myapp/api:prod; staging може бути зайвим.
Рішення: Не видаляйте образи, які використовуються запущеними контейнерами. Видаляйте невикористовувані лише після підтвердження, що інші хости не залежать від них локально.

Завдання 7: Показати зупинені контейнери та їх розміри

cr0x@server:~$ docker ps -a --size --format 'table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Size}}\t{{.Names}}' | head
CONTAINER ID   IMAGE              STATUS                      SIZE                NAMES
a1b2c3d4e5f6   myapp/api:staging  Exited (137) 3 days ago     1.2GB (virtual 2GB) api-staging-1
f1e2d3c4b5a6   node:20            Exited (0) 9 days ago       600MB (virtual 1GB) build-job-77

Що це означає: Зупинені контейнери можуть утримувати великі записувані шари.
Рішення: Видаляйте старі зупинені контейнери, якщо вам не потрібна їхня файловая система для форензики.

Завдання 8: Видалити зупинені контейнери (низький ризик)

cr0x@server:~$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
a1b2c3d4e5f6
f1e2d3c4b5a6

Total reclaimed space: 1.8GB

Що це означає: Ви повернули записувані шари для контейнерів, що не працювали.
Рішення: Добрий перший крок очищення. Якщо місця все ще мало — переходьте до prune образів/кешу збірки.

Завдання 9: Prune кеш збірки (зазвичай безпечно, інколи дорого)

cr0x@server:~$ 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:
l8p4m2n7k0q1
u2v3w4x5y6z7

Total reclaimed space: 22.8GB

Що це означає: Ви видалили невикористаний кеш збірки. Збирання може стати повільнішим, поки кеш відновиться.
Рішення: На CI-раннерах це часто правильний обмін. На збіркових серверах розгляньте планове prune за порогами.

Завдання 10: Попередній перегляд prune образів перед виконанням

cr0x@server:~$ docker image prune --all --force --filter 'until=240h'
Deleted Images:
untagged: myapp/api@sha256:1a2b...
deleted: sha256:9c1a2d4e0b2f...

Total reclaimed space: 12.3GB

Що це означає: Видалено образи, які не використовувалися жодними контейнерами і старші за 10 днів (240h).
Рішення: Використовуйте фільтри часу в продакшні. «Видалити все невикористане» — ок; «видалити все, що не запущене прямо зараз» — як ви зруйнуєте наступний деплой.

Завдання 11: Перегляньте використання томів і знайдіть ризикові кандидати

cr0x@server:~$ docker volume ls
DRIVER    VOLUME NAME
local     pgdata_main
local     redisdata
local     myapp_cache
local     old_test_volume_42

Що це означає: Томи існують незалежно від контейнерів. Деякі явно продакшн-дані.
Рішення: Ніколи не робіть prune томів навмання. Ідентифікуйте, які контейнери їх монтують, і що вони зберігають.

Завдання 12: Зіставте томи з контейнерами (щоб уникнути видалення живих даних)

cr0x@server:~$ docker ps -a --format '{{.ID}} {{.Names}}' | while read id name; do
>   echo "== $name ($id) =="
>   docker inspect --format '{{range .Mounts}}{{.Name}} -> {{.Destination}} ({{.Type}}){{"\n"}}{{end}}' "$id"
> done
== api-prod-1 (c8b2d9f2a4aa) ==
myapp_cache -> /var/cache/myapp (volume)

== pg-main (e12a9a0110bb) ==
pgdata_main -> /var/lib/postgresql/data (volume)

Що це означає: Тепер ви знаєте, які томи монтуються якими контейнерами.
Рішення: Розглядайте видалення тільки тих томів, які (a) відмонтовані і (b) підтверджено, що їх можна викинути.

Завдання 13: Виміряйте реальні розміри томів на диску

cr0x@server:~$ sudo du -sh /var/lib/docker/volumes/*/_data 2>/dev/null | sort -h | tail
2.1G  /var/lib/docker/volumes/myapp_cache/_data
18G   /var/lib/docker/volumes/redisdata/_data
71G   /var/lib/docker/volumes/pgdata_main/_data

Що це означає: Postgres — важкий. Це може бути очікувано або ознака надмірного утримання.
Рішення: Не «чистіть Docker», щоб вирішити зростання бази даних. Виправте політику збереження в базі і сплануйте сховище.

Завдання 14 (дружнє до надзвичайної ситуації): Знайдіть надвеликий JSON-лог контейнера

cr0x@server:~$ sudo find /var/lib/docker/containers -name '*-json.log' -printf '%s %p\n' 2>/dev/null | sort -n | tail
21474836480 /var/lib/docker/containers/c8b2d9f2a4aa.../c8b2d9f2a4aa...-json.log
5368709120  /var/lib/docker/containers/e12a9a0110bb.../e12a9a0110bb...-json.log

Що це означає: Один лог контейнера — 20GB. Це не «обсервабіліті», це ситуація, коли лог утримує хост у заручниках.
Рішення: Налаштуйте ротацію логів. У крайньому випадку ви можете усічити файл (див. наступне завдання), але це — крайній захід.

Завдання 15 (break-glass): Усічіть розбіглий JSON-лог без перезапуску контейнера

cr0x@server:~$ sudo truncate -s 0 /var/lib/docker/containers/c8b2d9f2a4aa.../c8b2d9f2a4aa...-json.log

Що це означає: Ви миттєво повернули місце. Docker продовжує писати в той самий дескриптор файлу.
Рішення: Робіть це лише при критичному тиску на диск. Потім налаштуйте ротацію логів, щоб більше так не робити.

Завдання 16: Налаштуйте ротацію логів Docker (реальне виправлення)

cr0x@server:~$ sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "50m",
    "max-file": "5"
  }
}
EOF
cr0x@server:~$ sudo systemctl restart docker

Що це означає: Нові контейнери матимуть обмежений ріст логів. Існуючі контейнери можуть потребувати перезапуску, щоб підхопити опції.
Рішення: Впровадьте це як базову практику скрізь. Якщо хочете централізовані логи — відправляйте їх, але все одно обмежуйте локальний ріст.

Завдання 17: Визначте зростання всередині записуваних шарів контейнера

cr0x@server:~$ docker exec c8b2d9f2a4aa sh -lc 'du -sh /tmp /var/tmp /var/log 2>/dev/null | sort -h'
120M  /var/log
5.6G  /tmp
32M   /var/tmp

Що це означає: Додаток використовує файлову систему контейнера для об’ємних тимчасових даних.
Рішення: Перенесіть цей шлях у том або tmpfs, або виправте очищення в додатку. Інакше це знову зростатиме після кожного «очищення».

Завдання 18: Використайте system prune з фільтром за часом (обережно, але практично)

cr0x@server:~$ docker system prune --force --filter 'until=168h'
Deleted Networks:
old_ci_net

Deleted Images:
deleted: sha256:62f13c9d1a11...

Total reclaimed space: 6.4GB

Що це означає: Ви почистили невикористані об’єкти старші за 7 днів. Томи не зачіпаються, якщо не додати --volumes.
Рішення: Це «безпечний за замовчуванням» prune для багатьох хостів. Зменшує ризик, при цьому контролює ріст.

Завдання 19: Перевірте відновлення простору та підтвердіть стабільність хоста

cr0x@server:~$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2   80G   52G   25G  68% /

Що це означає: Ви повернули собі здоровий запас місця.
Рішення: Не зупиняйтесь на цьому. Впровадьте запобіжні заходи: ротацію логів, плановий prune і політику кешу збірки.

Три корпоративні міні-історії з фронту переповнених дисків

1) Інцидент через хибне припущення: «Томи — це просто кеш, чи не так?»

Компанія середнього розміру тримала невеликий флот Docker-хостів для внутрішніх сервісів. Нічого складного: контейнер Postgres,
веб-додаток, background worker. Це не було Kubernetes. Це було «просто». Це слово має жертви.

Хост досяг 95% диску. Інженер зайшов, подивився docker system df і помітив, що томи — найвелика частка.
Вони нещодавно почистили кеш збірки та образи, а число томів залишалося великим. Отже вони зробили те, що інтернет радить, коли втомлений: docker system prune -a --volumes.

Команда виконалась швидко. Диск звільнився значно. Дашборди видихнули. Потім знову задзвонив телефон on-call, голосніше — бо база даних повернулася порожньою. Контейнер Postgres перезапустився з новим каталогом даних. Веб-додаток почав створювати перші адміністративні акаунти. Це був не новий функціонал.

Корінь проблеми не був у Docker. Корінь — припущення, що томи безпечні для видалення. У тій інфраструктурі томи були єдиним сховищем даних. Рішення було процедурним і технічним: маркувати томи за призначенням, зіставляти їх зі сервісами і захистити продакшн-томи від prune-операцій. Також вони впровадили бекапи хостів, бо покладатися на «ми не виконаємо ту команду» — це не стратегія.

Висновок: найнебезпечніша команда очищення — та, яку ви виконуєте в стресі, думаючи, що знаєте, що вона робить.

2) Оптимізація, що вистрілила собі в ногу: «Кешуймо всі збірки назавжди»

Інша команда працювала з монорепо і важкими Docker-збірками. Вони включили BuildKit і доклали зусиль для продуктивності:
кеш-монти, multi-stage builds і агресивне повторне використання шарів. CI прискорився. Керівництво було задоволене. Всі аплодували «оптимізованому» pipeline.

Через шість тижнів CI почав падати серіями. Не стабільно — випадково. Іноді не вдавалось тягнути образи. Збірки падали посередині. Іноді apt не міг записати тимчасові файли. Іноді Docker не міг створити шари.
Збої переміщувалися між раннерами. Інженери звинувачували реєстр. Потім мережу. Потім космічні промені.

Справжня причина: раннери розглядалися як «тварини», але ніхто їх не перевстановлював. Кеш збірки зростав без обмежень.
Docker data root заповнився, поки файлова система не досягла зарезервованих блоків і все погіршилося. Оптимізація не була поганою; вона була неповною. Робота над продуктивністю, яка ігнорує ємність, перетворюється на роботу над надійністю.

Відмова була культурною: команда вірила, що кеш збірки лише приносить користь і не шкодить продакшну. Вони ставили диск як безкінечний ресурс, бо він невидимий. Коли диск заповнився — їхні «швидкі збірки» зупинилися повністю.

Виправлення було нудним і ефективним: встановити розклад prune з порогами, додати моніторинг /var/lib/docker, і періодично перевстановлювати раннери. Вони зберегли більшість вигод продуктивності, і дивні збої припинилися.

3) Нудна але правильна практика, що врятувала день: «Виміряй, потім почисть, потім перевір»

Сервіс, що працював з платіжною суміжною системою, мав кілька Docker-хостів за балансувальником. Команда мала мало глянцю, але дисципліну. Кожен хост мав невеликий runbook: перевірити диск, перевірити відра Docker, перевірити логи і лише потім видаляти. Також ротація логів була налаштована за замовчуванням.

Одного вікенду трафік підскочив через проблему партнера. Сервіс не впав через CPU; він впав через логи.
Додаток почав логувати помилку на кожен запит. Така помилка не делікатна: вона конвертує трафік користувача в використання диску на масштабі.

Інженер on-call помітив тренд росту диску. Перш ніж він досяг 100%, вони обмежили кровотечу, розгорнувши зміну конфігурації, що зменшила деталізацію логів. Оскільки логи Docker ротаційались, хост не помер. Вони потім використали docker system df і в контрольований спосіб почистили кеш збірки та старі образи, з фільтрами за часом, по всьому флоту.

Ніяких героїчних північних команд. Жодного ручного редагування файлів у /var/lib/docker. Просто виміряна реакція, що зберегла запас і відновила нормальну роботу. Нудна, правильна практика знову врятувала день.

Безпечна стратегія очищення для продакшну

Правильний план очищення залежить від типу хоста. CI-раннер — не те саме, що хост бази даних.
Ноут розробника — не те саме, що продакшн-нода. Важливо зіставити збереження з призначенням хоста.

Визначте, що ніколи не має видалятися автоматично

У більшості продакшн-середовищ це «захищені класи»:

  • Іменовані томи з даними (бази даних, черги, завантаження).
  • Образи, потрібні для відкату (принаймні одна попередня версія).
  • Докази під час інциденту (зупинені контейнери, які планують дослідити).

Якщо ви не можете перерахувати це для свого середовища, ви не готові до агресивного pruning. Почніть з time-filtered prune, що не зачіпає томи.

Використовуйте фільтри за часом, щоб зменшити сюрпризи

Найкращий трюк для безпечнішого очищення — це --filter until=.... Воно створює буфер: «не видаляти нічого, створеного нещодавно».
Цей буфер покриває поточні деплои, вікно відкату і те, що люди забувають, що запустили минулого вівторка.

Виберіть позицію очищення за роллю хоста

Позиція для CI-раннера

  • Агресивно чистіть кеш збірки (щоденно або за порогом).
  • Чистіть образи старші за коротке вікно (кілька днів), бо їх можна відтворити.
  • Переважайте періодичне перевстановлення раннерів; це найчистіший «збирач сміття».
  • Тримайте томи мінімальними; вважайте будь-який том підозрілим.

Позиція для продакшн-хоста застосунку

  • Увімкніть ротацію логів у конфігурації daemon Docker.
  • Використовуйте time-filtered system prune без томів, за графіком.
  • Зберігайте образи для відкату в межах визначеного вікна.
  • Сповіщайте про використання диску та про ріст Docker root.

Позиція для стану збереження (бази даних у Docker)

  • Ніколи не чистіть томи автоматично. Ніколи. Якщо мусите — використовуйте явні allowlist-и.
  • Плануйте ємність росту томів. «Очищення Docker» — це не політика збереження бази даних.
  • Використовуйте бекапи і протестовані відновлення. Помилки очищення тут шкодять кар’єрі.

Жарт №2: Якщо ви тримаєте бази даних у Docker і ваша стратегія бекапів — «ми будемо обережні», ви не обережні — ви оптимістичні.

Поширені помилки: симптом → корінь проблеми → виправлення

1) Симптом: Диск повний, але docker system df не показує достатньо використання

Корінь: Місце знаходиться не в Docker-шляхах (системні логи, core dumps), або Docker data root не там, де ви думаєте, або файлова система вичерпала іноди.

Виправлення: Перевірте df -h і df -i. Знайдіть топ-директорії за допомогою sudo du -xhd1 /var (і інших маунтів). Підтвердіть Docker root через docker info.

2) Симптом: /var/lib/docker/containers/*-json.log файли дуже великі

Корінь: Немає ротації логів для json-file логера Docker, або додаток логить надто багато.

Виправлення: Налаштуйте опції логів у daemon.json; зменшіть детальність логів додатку; відправляйте логи поза хостом, якщо потрібно. Усічення — лише як крайній захід.

3) Симптом: Очищення не звільняє місце негайно

Корінь: Видалені файли все ще відкриті процесом (часто з логами), або зарезервовані блоки файлової системи, або особливості thin-provisioning у деяких драйверів.

Виправлення: Знайдіть відкриті видалені файли за допомогою lsof (не показано в завданнях, але це хід). Перезапустіть процес, якщо безпечно. Перевірте звільнене місце через df, а не надію.

4) Симптом: Ви почистили образи і тепер деплой не може тягнути/зібрати

Корінь: Ви видалили локальні образи, від яких залежали швидкі відкатні операції, або ваш реєстр/мережа повільні чи ненадійні, або ви пройшли занадто агресивно під час вікна деплою.

Виправлення: Використовуйте фільтри часу; координуйте розклади prune поза вікнами деплоїв; тримайте вікно відкату образів; забезпечте надійний доступ до реєстру.

5) Симптом: Диск зростає стійко, хоча ви чистите щотижня

Корінь: Зростання у томах (стан) або всередині записуваних шарів контейнерів (додатки пишуть тимчасові дані), а не в образах/кеші.

Виправлення: Виміряйте розміри томів; виправте шляхи додатків, щоб використовували томи/tmpfs; впровадьте політику збереження на шарі додатку/даних.

6) Симптом: Після docker system prune сервіс втрачає дані

Корінь: Томи були видалені (--volumes) або «іменований» том був насправді одноразовим і не мав бекапу; немає запобіжних заходів.

Виправлення: Ніколи не використовуйте --volumes у продакшні без явного зіставлення і бекапів. Використовуйте лейбли, інвентар і бекапи з тестами відновлення.

7) Симптом: Хост в порядку, але Docker-операції падають з «no space left on device»

Корінь: Файлова система Docker (або метадані драйвера збереження) обмежені, або іноди вичерпані, або маунт /var повний, хоч / — ні.

Виправлення: Підтвердьте маунти; перевірте іноди; переконайтесь, що Docker root на достатньо великій файловій системі; розгляньте переміщення Docker data root.

Чеклісти / покроковий план

Аварійний чекліст (диск > 95%, вплив на продакшн)

  1. Зупиніть кровотечу: ідентифікуйте безконтрольних записувачів (логи, тимчасові файли). Якщо це логи — обмежте детальність і ротацію/усічення як крайній захід.
  2. Отримайте запас: почистіть зупинені контейнери, потім кеш збірки, потім невикористовувані образи з фільтром часу.
  3. Перевірте: підтвердіть за допомогою df -h, що ви дійсно отримали вільне місце.
  4. Стабілізуйте: додайте ротацію логів Docker, якщо її не вистачає. Плануйте подальші роботи.

Режим безпечного очищення для продакшну (щотижня або щодня залежно від churn)

  1. Виміряйте: docker system df і збережіть його (навіть у тікеті). Тренди краще за здогадки.
  2. Очистіть: docker system prune --force --filter 'until=168h' (без томів).
  3. Окремо почистіть кеш збірки, якщо багато збірок: docker builder prune --force --filter 'until=168h'.
  4. Перегляньте: перелікуйте топ-образи і підтвердіть, що політика збереження відповідає реальності.
  5. Перевірте: подивіться df -h і налаштуйте пороги сповіщень.

План уміцнення, щоб вас більше не будили

  1. Увімкніть ротацію логів на рівні daemon Docker.
  2. Сповіщайте про диск (використання файлової системи) і про ріст Docker root.
  3. Встановіть політики для CI-раннерів: періодичне перевстановлення + агресивне очищення кешу збірки.
  4. Маркуйте та інвентаризуйте томи: знайте, які з них дані, а які — тимчасові.
  5. Зробіть відкат явним: тримайте останні N образів, решту чистіть за часом.
  6. Плануйте ємність для stateful томів: якщо БД росте — це рішення продукту, не Docker.

FAQ

1) Чи безпечний docker system prune у продакшні?

З запобіжними заходами: так, часто. Використовуйте --filter until=... і не додавайте --volumes, якщо ви не дуже впевнені.
За замовчуванням docker system prune видаляє зупинені контейнери, невикористовувані мережі, dangling-образи й кеш збірки.

2) У чому різниця між dangling і unused образами?

«Dangling» образи зазвичай — неназвані шари, що залишилися після збірок. «Unused» означає, що образ не посилається жоден контейнер.
docker image prune за замовчуванням цілиться в dangling; docker image prune -a — у unused теж.

3) Чому Docker зберігає образи, якими я не користувався тижнями?

Бо Docker не знає ваших намірів. Образи можуть бути потрібні для швидких відкатів або майбутніх запусків. Якщо хочете політику збереження — задайте її: фільтри часу, плановий prune або політики CI.

4) Чи можна видаляти файли прямо з /var/lib/docker, щоб зекономити місце?

Не робіть цього. Ви розсинхронізуєте метадані Docker і реальність та створите складніші для діагностики проблеми. Використовуйте команди Docker.
Єдиний виняток у надзвичайних ситуаціях — усічення логів, і навіть його треба замінити правильною ротацією.

5) Чому pruning не звільнив місця, хоча повідомив про повернуті GB?

Часто тому, що видалені файли все ще відкриті, особливо логи. Файлова система не відновить простір, поки останній дескриптор не закриється. Також перевірте, чи ви дивитесь на правильний маунт і чи іноди не є обмеженням.

6) Чи варто використовувати docker volume prune?

Лише коли ви точно розумієте, що означає «невикористовуваний том» у вашому середовищі. Томи, що наразі не посилаються контейнерами, вважаються невикористовуваними — навіть якщо вони містять єдину копію важливих даних.

7) Яка хороша стандартна ротація логів для Docker?

Для багатьох сервісів: max-size близько 50–100MB і max-file 3–10. Налаштуйте під ваші потреби реагування на інциденти.
Якщо логи важливі — відправляйте їх кудись. Якщо логи — спам, виправте спам насамперед.

8) Чому кеш збірки так розростається на CI?

CI породжує багато унікальних шарів: різні коміти, різні залежності, кеш-монти, паралельні збірки.
Без очищення або перевстановлення кеш тільки росте. Він не старіє автоматично.

9) Краще чистити регулярно чи перевстановлювати хости?

Для CI-раннерів: перевстановлення відмінне, бо гарантує чисту базу. Для стабільних продакшн-хостів: регулярний консервативний prune плюс моніторинг зазвичай краще, ніж часте перевстановлення. Поєднуйте обидва підходи, коли це має сенс.

10) Скільки вільного місця варто тримати на Docker-хості?

Тримайте запас для найбільшого очікуваного pull/build плюс операційні сплески (логи, деплои). Практично: прагніть мінімум 15–25% вільного на файловій системі, що хостить Docker data root; більше для нод з інтенсивними збірками.

Наступні кроки, які ви реально можете зробити цього тижня

Якщо хочете менше сюрпризів із заповненими дисками, зробіть це в порядку:

  1. Увімкніть ротацію логів Docker у /etc/docker/daemon.json і перезапустіть Docker під час вікна технічного обслуговування.
  2. Додайте docker system df у вашу рутини моніторингу: відстежуйте зростання образів, томів і кеша збірки з часом.
  3. Впровадьте pruning з фільтром часу за розкладом: почніть з until=168h і підлаштовуйте відповідно до циклів деплою.
  4. Зробіть томи явними: знайте, які з них дані, хто за них відповідає і як робиться бекап.
  5. Виправте справжніх записувачів: якщо ваш додаток пише великі тимчасові дані в файлову систему контейнера або логить безупинно — очищення стає лише повторюваною нарадою з тією ж проблемою.

Диск дешевий. Аварії — ні. Чистіть Docker так, ніби ви обслуговуєте продакшн-систему: спочатку виміряйте, потім видаліть, завжди перевіряйте.

← Попередня
Інтерфейс Toast-сповіщень на CSS: стекування, анімації, варіанти розміщення
Наступна →
Швидкість відновлення в Proxmox: налаштування PBS, вибір стиснення та чому відновлення повільні

Залишити коментар