GitLab 13.10: Dependency Proxy как ответ на Docker Hub rate limits
GitLab 13.10 привёз улучшенный Container Scanning, Review Apps в MR и Dependency Proxy для кеширования образов Docker Hub - последнее оказалось самым востребованным.
GitLab 13.10: Dependency Proxy для кеширования Docker Hub образов, улучшенный Container Scanning, автоматические Review Apps в merge request
GitLab 13.10 вышел в конце марта, и на фоне крупных security-анонсов этих недель - ProxyLogon, Pulse Secure - его как-то прошли стороной. А зря: там есть вещи, которые решают конкретные рабочие проблемы прямо сейчас.
Dependency Proxy - вот за чем стоило следить
Docker Hub в ноябре 2020 ввёл rate limits: анонимным запросам - 100 pull за 6 часов с одного IP, авторизованным на free-плане - 200. Для разработчика, который тянет образ руками раз в день, это ничто. Для CI/CD с несколькими десятками параллельных job'ов, каждый из которых делает FROM node:14-alpine или FROM python:3.9-slim, - проблема в полный рост. Ошибки toomanyrequests начали всплывать у нас на нескольких проектах именно тогда.
Классические решения: платный Docker Hub аккаунт с более высокими лимитами, собственный pull-through cache (Nexus, Harbor в режиме proxy, nginx с кешированием). Всё это работает, но требует отдельной инфраструктуры и настройки.
GitLab Dependency Proxy - это встроенный pull-through кеш для Docker Hub, встроенный прямо в инстанс GitLab. Схема работы простая: вместо docker.io/node:14-alpine в пайплайне пишешь ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/node:14-alpine. GitLab при первом обращении тянет образ с Docker Hub от имени своего сервисного аккаунта (с авторизацией), кеширует его локально, при последующих запросах отдаёт из кеша. Rate limits попадают на один аккаунт GitLab, а не на сотни параллельных job'ов.
Dependency Proxy появился ещё в GitLab 11, но именно в 13.10 он получил поддержку авторизованного pull от Docker Hub через учётные данные группы. Это важно: без этого GitLab тянул образы анонимно и сам попадал под те же лимиты.
Для включения достаточно нескольких шагов. В настройках группы включаем Dependency Proxy. В настройках CI группы (Settings - CI/CD - Variables) добавляем переменные DOCKER_HUB_USER и DOCKER_HUB_PASSWORD от авторизованного Docker Hub аккаунта. В .gitlab-ci.yml меняем ссылки на образы:
variables:
IMAGE_PREFIX: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX
build:
image: ${IMAGE_PREFIX}/node:14-alpine
script:
- npm ci
- npm run build
Для services - так же:
integration-test:
image: ${IMAGE_PREFIX}/python:3.9-slim
services:
- name: ${IMAGE_PREFIX}/postgres:13
alias: db
script:
- pytest tests/integration/
В пайплайне CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX разворачивается в gitlab.example.com/groupname/dependency_proxy/containers. Первый pull каждого образа идёт на Docker Hub, дальше - из кеша GitLab.
Что поменялось на практике
У нас один из клиентов - e-commerce, Kubernetes-кластер, GitLab с несколькими десятками разработчиков. Пайплайны запускаются часто, параллельно, на разных ветках. После ноября 2020 ошибки toomanyrequests стали появляться нерегулярно, но достаточно часто, чтобы это раздражало. Временным решением была авторизация в Docker Hub прямо в пайплайне через docker login - помогало, но добавляло шаг к каждому job'у и требовало хранить credentials в CI-переменных каждого проекта по отдельности.
После перехода на Dependency Proxy ошибки rate limit исчезли. Credentials храним в одном месте - переменные группы. Первый прогон после переключения чуть медленнее (образы тянутся впервые и кешируются), дальше - быстро, потому что node:14-alpine в следующий раз приходит уже с локального инстанса.
Кеш не вечный: по умолчанию GitLab хранит образы 90 дней без обращений, потом удаляет. На практике популярные образы обращаются к себе каждый день из пайплайнов, так что старение не проблема.
Container Scanning в 13.10
Параллельно GitLab улучшил встроенный Container Scanning - поиск уязвимостей в Docker-образах. Основное изменение: переход на Trivy (от Aqua Security) как основной сканер рядом с Clair. Trivy заметно быстрее, лучше работает с alpine-based образами и даёт более детальные отчёты по пакетам.
Мы начали переводить проекты на встроенный Container Scanning в рамках той же работы по GitLab security features. Настройка стандартная:
include:
- template: Security/Container-Scanning.gitlab-ci.yml
container_scanning:
variables:
CS_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
CS_SEVERITY_THRESHOLD: HIGH
CS_SEVERITY_THRESHOLD: HIGH - это ключевая переменная: фильтрует только высокие и критические уязвимости, иначе первый прогон выдаст сотни medium-предупреждений по базовым пакетам alpine и парализует команду. Сначала закрываем HIGH/CRITICAL, потом разбираемся с остальными.
Review Apps в merge request
Ещё одно из 13.10, которое интересно, но мы пока не внедрили: автоматическое создание Review Apps при открытии MR с автоматической записью URL окружения в комментарий к MR.
Технически это не новая концепция - Review Apps в GitLab были давно. Новое в 13.10 - упрощение конфигурации: GitLab может сам формировать ссылку на временное окружение и добавлять её в UI merge request без дополнительных скриптов. Для команд, которые делают review на pull request'ах с живым окружением, это уменьшает количество шагов.
У нас это на очереди для проектов с фронтендом - сейчас review делается по скриншотам и описаниям, живые окружения под каждую ветку не поднимаем. Посмотрим как будет работать с Kubernetes namespace per branch, там своя специфика.
Итого
Из 13.10 реально внедрили прямо сейчас: Dependency Proxy - решение готовое, работает, закрывает Docker Hub rate limit без отдельного сервиса. Остальное - в очереди.
В рамках managed-сопровождения переключение на Dependency Proxy делается в течение пары часов: включить в GitLab, добавить групповые переменные, поправить .gitlab-ci.yml в проектах. Особой магии нет, но делать это нужно один раз централизованно, а не в каждом проекте по отдельности.