Выберите продукт

GitLab CI storage: cache, artifacts и контроль роста диска

Cache и artifacts ускоряют GitLab CI, но легко раздувают диски на Runner и в GitLab. Разбираем отличия, правильные cache key, настройку expire_in, где лежат данные и как чистить кэши, артефакты и Docker-слои без поломки пайплайнов.
GitLab CI storage: cache, artifacts и контроль роста диска

В GitLab CI тема «storage» почти всегда всплывает не в момент настройки, а когда внезапно заканчивается место: Runner перестаёт стартовать job’ы, Docker падает на pull/overlay2, а GitLab раздувается из‑за артефактов и отчётов. При этом cache и artifacts — ключевые инструменты ускорения сборок, просто ими нужно управлять как ресурсом: сроками жизни, ключами, лимитами и уборкой.

Ниже — практический разбор: чем отличаются cache и artifacts, как работает expire_in, что такое cache:key и runner cache, где это хранится и как делать cleanup так, чтобы не «убить скорость» и не потерять нужные файлы.

Cache vs artifacts: что хранить и зачем

GitLab CI даёт два похожих по виду механизма, но с разной логикой и последствиями для диска. Если перепутать их назначение, вы получите либо вечный рост хранилища GitLab (из‑за артефактов), либо непредсказуемые сборки (из‑за конфликтующего кэша).

Cache: ускоряем повторяющиеся шаги

Cache предназначен для переиспользования между пайплайнами и/или ветками: зависимости, кэши менеджеров пакетов, промежуточные каталоги компилятора. Он сильнее всего влияет на скорость, потому что экономит скачивания и пересборки.

  • Обычно не является «результатом», который нужно скачивать пользователю.
  • Может быть общим между job’ами и пайплайнами при совпадении ключей.
  • Его удаление почти безрисковое: максимум — пайплайн станет медленнее.

Artifacts: переносим результаты между стадиями и сохраняем доказательства

Artifacts — это выходные файлы job’ы: собранные архивы, отчёты тестов, coverage, JUnit, результаты сканирования и т.д. Они нужны либо следующим стадиям, либо людям (скачать сборку), либо GitLab (показать отчёты в UI).

  • Привязаны к конкретной job’е/пайплайну.
  • Часто используются в деплое или для расследования инцидентов.
  • Слишком раннее удаление может ломать релизный процесс и диагностику.

Простое правило: всё, что можно восстановить автоматически (зависимости, кэши компилятора), — это cache. Всё, что является результатом работы пайплайна и нужно «как факт», — artifacts, но с осмысленным сроком жизни.

Где именно растёт диск: GitLab vs Runner

Прежде чем «чистить всё», важно понять, где заканчивается место: на сервере GitLab или на машинах GitLab Runner. Симптомы похожие (ошибки записи, падение job’ов), но лечение разное.

Хранилище GitLab: artifacts и отчёты

В self-managed GitLab артефакты живут на стороне GitLab (файловое хранилище или объектное — зависит от конфигурации). Даже если Runner отдельный, artifacts всё равно «утекают» в GitLab-storage.

Что чаще всего раздувает GitLab:

  • длинные сроки хранения artifacts без expire_in;
  • артефакты из веток и merge request’ов, которые никто не чистит;
  • логи/отчёты, сохраняемые «на всякий случай» в каждом пайплайне;
  • параллельно может расти registry/packages, но это отдельный контур контроля.

Runner cache и рабочие директории: диск заканчивается внезапно

Runner cache хранится локально на машине с GitLab Runner (shell executor) или в директориях/томах Docker executor — зависит от настроек. Это частая причина «no space left on device»: кэш копится быстро, а автоматическая чистка обычно не настроена.

Отдельный «пожиратель диска» у Docker Runner — слои образов и build cache (overlay2). Формально это не GitLab cache, но в реальности заканчивается тот же диск.

Если вы держите Runner на отдельной машине, удобный и предсказуемый вариант — выделить под CI отдельный сервер или VDS с запасом по диску и возможностью быстро расширить хранилище.

Схема: где растут artifacts в GitLab и где копится runner cache

expire_in: главный рычаг против вечного хранения artifacts

Параметр expire_in относится к artifacts и задаёт срок, после которого GitLab пометит артефакты как просроченные и сможет их удалить (в рамках фоновых задач и настроек очистки).

Рабочая практика по срокам:

  • для отчётов тестов, линтеров и временных логов — 1–7 дней;
  • для сборок, которые иногда нужно скачать/откатить — 7–30 дней (по процессу);
  • «длинные» сроки оставляйте только для релизов по тегам или отдельных каноничных пайплайнов.

Пример: отчёты на 3 дня

test:
  stage: test
  script:
    - ./run-tests.sh
  artifacts:
    when: always
    expire_in: 3 days
    paths:
      - reports/
    reports:
      junit: reports/junit.xml

Так сохраняется диагностическая ценность для MR и разборов падений, но отчёты не копятся месяцами.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Cache key: как не превратить кэш в свалку (и не убить скорость)

cache:key определяет, какие job’ы разделяют один и тот же кэш. Частая ошибка — ключ слишком уникальный (например, включает SHA коммита): кэш никогда не переиспользуется и только растёт. Обратная ошибка — ключ слишком общий: кэш конфликтует между ветками/версиями зависимостей и сборка начинает падать «странно».

Три рабочие стратегии ключей

  • По lockfile: лучший баланс. Меняется lockfile — меняется кэш, иначе переиспользуется.
  • По ветке: удобно для долгоживущих веток, но мусорит на feature-ветках.
  • По версии runtime/инструмента: добавляйте версию Node/Go/Java, чтобы избежать несовместимостей.

Пример: кэш по lockfile

build:
  image: node:20
  script:
    - npm ci
    - npm run build
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - .npm/
      - node_modules/

Ключ на основе lockfile автоматически «инвалидирует» кэш при изменении зависимостей. Для репозиториев с большим количеством веток это почти всегда лучше, чем ключ по имени ветки или коммиту.

policy: pull-push и контроль разрастания

Политика кэша определяет, будет ли job только скачивать кэш или ещё и обновлять его. Часто разумно разделить роли:

  • в «установочных» job’ах — policy: pull-push;
  • в остальных — policy: pull, чтобы не плодить вариации кэша.

Artifacts по делу: меньше файлов, правильные paths и когда не сохранять

Storage нередко «съедают» не большие архивы, а тысячи мелких файлов (например, зависимости). Их не стоит складывать в artifacts «чтобы передать на следующую стадию»: для этого есть cache или правильная сборочная стратегия (пересборка в чистом окружении, либо один job, который делает и build, и упаковку).

Как уменьшить artifacts без потери смысла

  • Сохраняйте только то, что реально нужно: итоговый архив/пакет, отчёты, несколько ключевых логов.
  • Избегайте временных каталогов сборщика и полного workspace.
  • Если артефакт нужен только для деплоя — делайте минимальный пакет, а не «всё собранное дерево».

Пример: артефакт только с билдом

build:
  stage: build
  script:
    - make build
  artifacts:
    expire_in: 7 days
    paths:
      - dist/app.tar.gz

Если параллельно в инфраструктуре поднимаете внутренние сервисы и панели для артефактов/логов, не забывайте про базовую гигиену: для веб-интерфейсов и GitLab корректно настроенный TLS обычно обязателен. При необходимости можно выпустить и обновлять SSL-сертификаты централизованно.

Cleanup storage: что чистить, где и как не сломать пайплайны

Уборка — это два параллельных процесса: на стороне GitLab (просроченные artifacts) и на стороне Runner (runner cache, рабочие директории, docker data). Ошибка — пытаться «одной командой» вылечить всё.

Проверка: что именно занимает место

Начните с банального, но обязательного: посмотрите топ потребителей диска. На Runner и на GitLab это будут разные директории.

df -h
sudo du -xhd1 /var | sort -h
sudo du -xhd1 /home | sort -h

Если Runner в Docker — проверьте docker storage:

docker system df

Runner cache: безопасная чистка и «скользящее окно»

С точки зрения надёжности кэши можно удалять смело: вы не теряете «истину», вы теряете только ускорение. Чтобы скорость не просела резко, чистите по принципу «скользящего окна»: удаляйте старое, а не всё подряд.

  • Держите кэши на отдельном разделе или диске (проще контролировать и расширять).
  • Ограничьте срок жизни файлов в кэше через cron или systemd-tmpfiles.
  • Не запускайте агрессивную чистку в пиковое время на «горячих» runner’ах, иначе получите шторм скачиваний.

Docker на Runner: мусор слоёв и неиспользуемых образов

Если executor — Docker, внезапное заполнение диска чаще происходит из‑за образов, контейнеров и build cache. В этом случае cleanup — это регулярная уборка Docker.

docker image prune -af
docker builder prune -af
docker container prune -f
docker volume prune -f

Перед агрессивной чисткой убедитесь, что volumes не используются постоянно какими-то сервисами. Удаление build cache и образов увеличит время сборок до тех пор, пока кэш заново не прогреется.

Проверка места Docker на Runner и команды очистки

Как ускорять pipeline без бесконтрольного роста диска

Цель — не «закэшировать всё», а закэшировать самое дорогое и стабильное, а остальное собирать заново за предсказуемое время. По смыслу это очень похоже на подходы к кешированию в вебе: важна стратегия и срок жизни, а не максимальный объём. Если хотите глубже разобраться в подходах к TTL и контролю кеша на уровне инфраструктуры, пригодится материал про TTL и контроль доступа в кеше Nginx.

Чек-лист практик

  • Cache — для зависимостей и стабильных кэшей инструментов (npm/pip/composer/go build cache).
  • Ключи — lockfile плюс версия runtime/образа.
  • Не кэшируйте целиком workspace и нестабильный build output без необходимости.
  • Artifacts — только то, что реально нужно скачать/передать/показать.
  • Всегда задавайте expire_in, кроме осознанно «долгих» релизных случаев.
  • Регулярный cleanup на Runner: docker prune и/или чистка каталога runner cache.

Пример .gitlab-ci.yml: сбалансированный cache + artifacts

Шаблон для Node.js: кэшируем зависимости, артефакты держим коротко и только нужные.

stages:
  - test
  - build

default:
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - .npm/
      - node_modules/
    policy: pull-push

test:
  stage: test
  image: node:20
  script:
    - npm ci --cache .npm --prefer-offline
    - npm test
  artifacts:
    when: always
    expire_in: 3 days
    paths:
      - reports/

build:
  stage: build
  image: node:20
  script:
    - npm ci --cache .npm --prefer-offline
    - npm run build
    - tar -czf dist/app.tar.gz dist/
  artifacts:
    expire_in: 7 days
    paths:
      - dist/app.tar.gz
  • cache привязан к lockfile, поэтому не раздувается «по коммитам»;
  • artifacts с отчётами живут 3 дня, а билд — 7 дней;
  • в artifacts не попадают зависимости и мусор сборки.

Типовые ошибки и быстрые симптомы

Кэш «не работает»: каждый раз качает заново

Частая причина — слишком уникальный cache:key. Симптом: в логах job’ы постоянный cache miss и создание нового архива кэша.

Кэш «ломает сборку»: странные падения после обновлений

Причина — слишком общий ключ и отсутствие «инвалидации» при смене зависимостей или версии runtime. Решение: ключ по lockfile и добавление версии инструмента/образа в стратегию ключа.

GitLab раздувается: артефакты висят месяцами

Причина — нет expire_in или он одинаково длинный для всех job’ов. Введите классы артефактов: короткие для тестов, средние для сборок, длинные — только для релизных тегов.

Итог

Управление GitLab CI storage — это баланс между скоростью и дисциплиной хранения. Cache отвечает за ускорение и может быть удалён почти без риска, но требует разумного cache:key и регулярной чистки runner cache. Artifacts — это результаты и доказательства: задавайте expire_in, оставляйте только нужные файлы и не превращайте artifacts в перенос «всего проекта» между стадиями.

Если один раз разложить по полочкам «что кэшируем», «что сохраняем как артефакты» и «как чистим», то пайплайны будут быстрыми и предсказуемыми, а диски перестанут заканчиваться в самый неподходящий момент.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: как исправить Device is busy у Docker network, volume и namespace OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Device is busy у Docker network, volume и namespace

Если Docker на Debian или Ubuntu отвечает Device is busy при удалении сети, тома или namespace, причина обычно в живом процессе, о ...
Debian/Ubuntu: как исправить Host key verification failed в Ansible при смене IP OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Host key verification failed в Ansible при смене IP

Ошибка Host key verification failed в Ansible на Debian и Ubuntu обычно возникает после переустановки сервера, смены IP или повтор ...
Debian/Ubuntu: duplicate address detected, DAD failed IPv6 — причины и исправление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: duplicate address detected, DAD failed IPv6 — причины и исправление

Сообщения duplicate address detected и DAD failed в Debian/Ubuntu означают, что IPv6-адрес не прошёл проверку уникальности в локал ...