Harbor на сервере: частые ошибки и решения
Harbor — не «докер-реестр с картинкой», а полноценный сервис из десятка контейнеров: core, portal, registry, jobservice, trivy, redis, postgres, nginx-прокси перед всем этим. Если что-то не так с DNS, сертификатом или диском — падает не один контейнер, а вся цепочка, и логи расползаются по разным сервисам. В этой статье — конкретные симптомы, которые встречаются на практике при развёртывании Harbor на VPS, и то, как их закрывать без переустановки с нуля.
Содержание
- Harbor не стартует после install.sh: где искать причину
- Сертификат и HTTPS: `x509: certificate signed by unknown authority`
- `denied: requested access to the resource is denied` при push
- Trivy не сканирует образы или зависает в статусе `Queued`
- Диск заканчивается: garbage collection и блоб-хранилище
- Репликация между инстансами Harbor обрывается
- Резервное копирование и восстановление Harbor
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Harbor не стартует после install.sh: где искать причину
Установщик install.sh генерирует docker-compose.yml из harbor.yml и поднимает стек через docker compose up -d. Если после этого docker ps показывает не все контейнеры или часть в статусе Restarting, первым делом смотрите логи конкретного сервиса, а не общий вывод инсталлятора:
cd /path/to/harbor
docker compose ps
docker compose logs --tail=100 core
docker compose logs --tail=100 registry
docker compose logs --tail=100 nginx
Самые частые причины на этом этапе:
- Порт 80/443 уже занят. Если на сервере уже стоит nginx или Caddy для других сайтов, контейнер
nginxиз Harbor не поднимется с ошибкойbind: address already in use. Решение — либо освободить порт, либо развести Harbor и внешний веб-сервер черезproxy_pass(об этом ниже). - Файл
harbor.ymlне пересобран. После правкиharbor.ymlнужно заново прогнать./prepare, иначеdocker-compose.ymlостанется со старыми значениями:
./prepare
docker compose down
docker compose up -d
- Недостаточно памяти. Полный стек Harbor с Trivy-сканером под нагрузкой спокойно съедает 4 ГБ RAM даже без активных пушей. На VPS с 2 ГБ контейнер
trivy-adapterпервым уходит в OOM — это видно вdmesg | grep -i killилиdocker compose logs trivy-adapter.
Если проект называется не harbor по умолчанию (переопределён через -p или переменную COMPOSE_PROJECT_NAME), убедитесь, что все команды docker compose выполняются из той же директории, где лежит .env и сгенерированный docker-compose.yml — иначе Compose создаст второй, пустой стек рядом с первым, и вы будете чинить не тот набор контейнеров.
Сертификат и HTTPS: `x509: certificate signed by unknown authority`
Это, пожалуй, самая частая ошибка Harbor вообще. Она вылезает при docker login или docker pull с клиентской машины и означает, что клиент не доверяет сертификату реестра.
Два рабочих пути:
Вариант 1 — нормальный TLS-сертификат (рекомендуется). Если у Harbor есть свой поддомен (например, registry.example.com), проще всего повесить перед ним отдельный reverse-proxy с Let's Encrypt и проксировать на порт Harbor изнутри сети докера. Это снимает необходимость городить самоподписанные сертификаты и рассылать их по всем клиентам. Про выбор между certbot и acme.sh для управления сертификатами есть отдельный разбор — certbot или acme.sh: что выбрать для сервера.
Пример проксирования через nginx перед Harbor (сам Harbor слушает локально на 8443):
server {
listen 443 ssl;
server_name registry.example.com;
ssl_certificate /etc/letsencrypt/live/registry.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/registry.example.com/privkey.pem;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
proxy_pass https://127.0.0.1:8443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 900;
}
}
В harbor.yml при этом hostname должен совпадать с внешним доменом, а https.port — с тем, что слушает Harbor локально.
Вариант 2 — самоподписанный сертификат для внутреннего стенда. Если реестр не смотрит наружу и используется только внутри VPN/локальной сети, добавьте CA-сертификат Harbor в доверенные на каждом клиенте:
sudo mkdir -p /etc/docker/certs.d/registry.example.com
sudo cp ca.crt /etc/docker/certs.d/registry.example.com/ca.crt
sudo systemctl restart docker
Путь важен побуквенно — Docker ищет сертификаты именно по имени хоста в /etc/docker/certs.d/, без порта (если он не нестандартный) и без протокола.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать сервер`denied: requested access to the resource is denied` при push
Эта ошибка возникает по трём независимым причинам, и их легко перепутать:
- Не выполнен
docker loginлибо токен истёк. Сессии Harbor по умолчанию живут ограниченное время — если пуш идёт из CI, где логин был час назад, стоит перелогиниться заново перед каждым деплоем, а не полагаться на кэш~/.docker/config.json. - Проект в Harbor приватный, а у пользователя нет роли
developer/maintainerв этом конкретном проекте. RBAC в Harbor завязан на проекты: системный админ автоматически не имеет прав пушить во все чужие проекты, если явно не добавлен участником. - Образ не соответствует пути проекта. Harbor требует, чтобы имя образа начиналось с имени проекта:
registry.example.com/myproject/app:1.0, а не простоregistry.example.com/app:1.0. Вторая форма трактуется как попытка пушнуть в проектlibrary, куда у большинства пользователей нет доступа.
Проверить права быстро:
docker login registry.example.com -u ivan
docker tag app:latest registry.example.com/myproject/app:1.0
docker push registry.example.com/myproject/app:1.0
Если ошибка сохраняется — зайдите в веб-интерфейс Harbor, Projects → myproject → Members и убедитесь, что пользователь или робот-аккаунт (Robot Account) там числится с нужной ролью. Для CI/CD лучше сразу заводить робот-аккаунты с правами только на нужный проект, а не использовать личные учётки — это и безопаснее, и не ломается при увольнении сотрудника.
Trivy не сканирует образы или зависает в статусе `Queued`
Встроенный сканер уязвимостей (Trivy-adapter) — частый источник проблем, потому что ему нужен доступ в интернет для обновления базы CVE.
Если задача сканирования висит в Queued бесконечно:
docker compose logs -f trivy-adapter
docker compose logs -f jobservice
Типичные находки в логах:
- Нет доступа к
raw.githubusercontent.comи базам NVD. Если сервер стоит за файрволом с ограниченным исходящим трафиком или в изолированном контуре, Trivy не может скачать актуальную базу уязвимостей. Проверьте исходящий доступ:
docker compose exec trivy-adapter wget -qO- https://raw.githubusercontent.com/aquasecurity/trivy-db/main/README.md
- Redis недоступен для jobservice. Все задачи (сканирование, репликация, GC) идут через очередь в Redis. Если контейнер
redisупал или перезапустился с потерей данных, очередь зависает молча — перезапуститеjobserviceиredisвместе:
docker compose restart redis jobservice
- Устаревшая версия Trivy-adapter несовместима с текущей базой данных. Такое бывает после долгого простоя без обновлений. Обновление Harbor целиком через официальный
install.shс новым релизом обычно решает это надёжнее, чем точечное обновление одного контейнера.
Имейте в виду: скорость и результаты сканирования зависят от версии базы CVE и от того, насколько давно она обновлялась на конкретном сервере — не берите абсолютные цифры "сканирует N образов в минуту" из чужих статей как гарантию, у вас будет иначе в зависимости от размера образов и сети.
Диск заканчивается: garbage collection и блоб-хранилище
Harbor хранит блобы образов в /data (или в S3-совместимом хранилище, если оно настроено). Проблема в том, что docker rmi или удаление тега через UI не освобождает место сразу — блобы, на которые больше нет ссылок, остаются на диске до запуска Garbage Collection.
Запустить GC вручную:
# Через API
curl -X POST -u admin:Harbor12345 \
https://registry.example.com/api/v2.0/system/gc/schedule \
-H "Content-Type: application/json" \
-d '{"schedule": {"type": "Manual"}}'
Либо через веб-интерфейс: Administration → Garbage Collection → GC Now. На время GC реестр по умолчанию уходит в read-only, если не включён режим "non-blocking GC" (доступен начиная с определённых версий Harbor) — учитывайте это при планировании, не запускайте GC в момент активных деплоев из CI.
Второй источник разрастания диска — отсутствие политики хранения тегов (Tag Retention). Без неё в проект годами копятся все версии образов из CI. В Projects → myproject → Tag Retention можно задать правило вида «хранить последние 10 тегов, совпадающих с release-*, удалять остальное». Это не заменяет GC, а дополняет: retention помечает лишние теги на удаление, GC физически освобождает место.
Если тема свободного места на диске под контейнеры в целом больна не только для Harbor, разбор общих причин есть здесь: Docker занимает всё место на диске: причины и решение.
Репликация между инстансами Harbor обрывается
Если вы настроили репликацию образов между двумя Harbor (например, между сервером в России для разработки и сервером в другой юрисдикции для продакшена), возможны обрывы на середине:
- Таймаут при передаче больших слоёв.
jobserviceне бесконечно терпелив к медленным каналам. Дробите большие образы (multi-stage build, меньше слоёв) и проверяйтеdocker compose logs jobserviceна предметcontext deadline exceeded. - Несовпадение версий Harbor. Репликация между сильно разными мажорными версиями иногда ломается на API-уровне — держите обе стороны в пределах одной-двух минорных версий.
- Правило репликации фильтрует не то, что ожидалось. Фильтры по имени образа и тегу работают по wildcard-паттернам (
release-*), а не по regex — частая ошибка ставить туда regex-синтаксис, который просто ничего не матчит, и репликация "молча" не переносит ничего.
Проверить статус конкретного задания репликации: Administration → Replications → [имя правила] → Executions. Там видно, на каком именно артефакте оборвалась передача.
Резервное копирование и восстановление Harbor
Harbor хранит состояние в трёх местах, и бэкапить нужно все три согласованно:
| Компонент | Что содержит | Как бэкапить |
|---|---|---|
| PostgreSQL | Проекты, пользователи, права, метаданные | pg_dump из контейнера postgresql |
Registry storage (/data) | Сами блобы образов | rsync/архив каталога или снапшот диска |
| Redis | Очереди задач (не критично для восстановления) | Обычно не бэкапится, переживаем потерю |
Пример дампа базы:
docker compose exec postgresql pg_dumpall -U postgres > harbor_db_backup.sql
Восстановление требует остановленного стека и совпадающей версии Harbor — дамп от старой версии на свежеустановленный Harbor напрямую не накатить, схема базы меняется между релизами. Общие принципы бэкапа контейнерных томов, применимые и к /data Harbor, разобраны в статье бэкап Docker volume на сервере: частые ошибки и решения. Перед серьёзным обновлением версии делайте снапшот диска целиком — это быстрее, чем восстанавливать по частям из дампа базы и архива блобов.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Harbor — это замена Docker Hub?
Нет, это приватный self-hosted реестр для своей инфраструктуры: образы не публикуются наружу, доступ регулируется через RBAC, плюс встроено сканирование уязвимостей. Для публичного распространения образов Docker Hub или GHCR остаются удобнее.
Сколько ресурсов сервера нужно под Harbor?
Для стабильной работы с включённым Trivy комфортно закладывать от 4 ГБ RAM и от 2 vCPU, плюс отдельное место под /data под рост образов — оно быстро становится узким местом без настроенной retention-политики и GC.
Можно ли обойтись без Trivy, если сканирование не нужно?
Да, при установке через install.sh --with-trivy этот компонент опционален — без него стек легче и не требует исходящего доступа к базам CVE, но тогда пропадает автоматическая проверка образов на уязвимости при пуше.
Чем Harbor отличается от простого registry:2 из Docker Hub?
Голый registry:2 — это только хранилище блобов без веб-интерфейса, RBAC, сканирования и репликации. Если нужен именно минимальный реестр без лишнего, посмотрите на приватный Docker registry на сервере: частые ошибки и решения — там разобран более лёгкий вариант.
После обновления Harbor всё сломалось — что делать?
Проверяйте CHANGELOG конкретной версии на предмет breaking changes в схеме БД, всегда обновляйтесь через официальный install.sh нужной версии, а не ручной правкой docker-compose.yml, и держите свежий бэкап перед стартом обновления.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →