MAATRIX / Блог / npm, PyPI и Maven через свой Nexus: зачем это бизнесу, а не только девопсу

npm, PyPI и Maven через свой Nexus: зачем это бизнесу, а не только девопсу

MAATRIX

Однажды сборка встаёт посреди рабочего дня — не потому что кто-то сломал код, а потому что npmjs.org отдаёт 503, PyPI прилёг на плановые работы, или пакет, от которого зависит половина проекта, просто исчез из реестра. Для разработчика это раздражающая пауза. Для бизнеса — это простой релиза, сорванный дедлайн и вопрос «а мы вообще понимаем, что тянем в прод из интернета». Свой прокси-репозиторий для npm, PyPI и Maven снимает эту зависимость от чужой инфраструктуры и заодно даёт то, чего нет ни у одного публичного реестра — контроль над тем, что попадает в ваши сборки.

Зачем это нужно бизнесу, а не только девопсу

Разговор про менеджер артефактов обычно начинают инженеры, а решение о бюджете принимает кто-то, для кого «Nexus» и «Artifactory» — просто незнакомые слова. Но у этой темы три экономических аргумента, которые не про удобство разработки, а про деньги и риски компании.

Устойчивость сборок. Публичные реестры — npmjs.org, PyPI, Maven Central — не дают SLA лично вам. Они падают, режут скорость под нагрузкой, банят IP за агрессивные запросы CI, а иногда становятся недоступны из конкретной страны или дата-центра без объяснений. Если у вас 20 сборок в день и каждая тянет пакеты напрямую из трёх внешних реестров, вы каждый раз ставите релиз в зависимость от чужой аптайм-статистики. Собственный прокси кэширует то, что уже качали — сборка идёт даже если апстрим недоступен, до тех пор, пока нужная версия уже лежит в кэше.

Безопасность цепочки поставок (supply chain security). Это не абстрактная угроза из отчётов — за последние годы были десятки реальных инцидентов: вредоносный код в популярном npm-пакете, подмена зависимости через тайпсквоттинг (пакет с похожим именем), скомпрометированный аккаунт мейнтейнера, из-под которого выкатили заражённую версию. Когда сборка тянет пакеты напрямую из публичного реестра, у компании физически нет точки, где это можно перехватить: npm install на CI-раннере доверяет всему, что нашёл. Свой Nexus — это точка контроля: можно блокировать определённые пакеты и версии, требовать сканирование на уязвимости перед тем, как пакет станет доступен сборкам, вести аудит-лог, кто и когда что подтянул.

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

Отдельно для российских компаний: доступ к npmjs.org, PyPI и Maven Central из России физически работает, но нестабильно — периодические замедления, сброс соединений, зависимость от того, какой маршрут в этот момент выбрал провайдер. Свой прокси на сервере с нормальным исходящим каналом (например, в Великобритании или США) убирает эту случайность из процесса сборки.

Proxy, hosted и group — как устроен репозиторий артефактов

Прежде чем разворачивать сервер, стоит понимать три типа репозиториев внутри Nexus (та же логика в Artifactory и других менеджерах артефактов) — путаница между ними — частая причина, почему первое подключение идёт не так, как ожидалось.

  • Proxy-репозиторий — кэширующий прокси перед публичным реестром. Первый запрос пакета уходит наружу и кэшируется, все последующие обращения к этой версии отдаются из локального кэша. Апстрим недоступен — Nexus продолжает отдавать то, что уже закэшировано.
  • Hosted-репозиторий — место, куда ваша команда публикует собственные пакеты: внутренние npm-модули, приватные Python-библиотеки, релизные и снапшот-артефакты Maven. Наружу не ходит вообще, живёт только у вас.
  • Group-репозиторий — объединяет несколько proxy и hosted под одним URL. Клиент (npm, pip, Maven) настраивается один раз на group-репозиторий и не знает, физически ли пакет пришёл из кэша npmjs.org или это ваш внутренний модуль — Nexus сам решает, откуда его отдать.

На практике для каждой экосистемы (npm, PyPI, Maven) вы заводите свою пару proxy + hosted и объединяете их в group — так CI и разработчики работают с одним URL на язык, а не с отдельными адресами для внешних и внутренних пакетов.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Разворачиваем Nexus Repository OSS на сервере

Дальше — практическая часть на примере Sonatype Nexus Repository OSS, самого распространённого бесплатного менеджера артефактов с поддержкой всех трёх экосистем из коробки. Если нужен подробный пошаговый разбор установки с нуля, включая настройку systemd-сервиса и firewall, — он есть отдельно: пошаговая установка Nexus Repository на Ubuntu 24.04. Здесь — сжатый практический минимум, чтобы понимать порядок действий.

Минимальные требования для рабочего стенда:

ПараметрТест/малая командаПрод, активный CI
CPU2 vCPU4+ vCPU
RAM4 ГБ8+ ГБ
Диск30 ГБ SSD100+ ГБ SSD, отдельно под blob store
JavaOpenJDK 17OpenJDK 17

Быстрый запуск через Docker Compose — самый простой путь для теста и небольшой продакшен-нагрузки:

services:
  nexus:
    image: sonatype/nexus3:latest
    container_name: nexus
    restart: unless-stopped
    ports:
      - "8081:8081"
    volumes:
      - nexus-data:/nexus-data
    environment:
      - INSTALL4J_ADD_VM_PARAMS=-Xms2703m -Xmx2703m -XX:MaxDirectMemorySize=2703m
volumes:
  nexus-data:
docker compose up -d
docker exec nexus cat /nexus-data/admin.password

Пароль из последней команды нужен для первого входа на http://<ip-сервера>:8081 под пользователем admin — мастер настройки сразу предложит сменить его на постоянный.

Сразу после установки — два действия, которые часто откладывают и потом жалеют: настройка cleanup policies (иначе кэш и снапшоты Maven съедят диск за пару месяцев) и настройка регулярного бэкапа каталога nexus-data — репозиторий с двумя годами кэша и внутренних пакетов восстанавливать без бэкапа болезненно.

Подключаем npm

В веб-интерфейсе Nexus (Server administration → Repositories → Create repository) заводим три репозитория:

  1. npm (proxy) — тип npm (proxy), Remote storage URL: https://registry.npmjs.org. Nexus будет кэшировать всё, что запрашивают клиенты.
  2. npm-internal (hosted) — тип npm (hosted), Deployment policy: Allow redeploy на время разработки или Disable redeploy для строгого контроля версий в проде.
  3. npm-group (group) — объединяет npm-internal и npm-proxy, порядок членов важен: hosted обычно ставят выше proxy, чтобы внутренние пакеты имели приоритет при совпадении имени.

На стороне разработчика или CI — один файл .npmrc (в проекте или глобально в ~/.npmrc):

registry=https://nexus.example.com/repository/npm-group/
always-auth=true
//nexus.example.com/repository/npm-group/:_authToken=${NPM_TOKEN}

Публикация внутреннего пакета идёт как обычно, но с указанием hosted-репозитория:

npm publish --registry https://nexus.example.com/repository/npm-internal/

Частый вопрос — как быть со scoped-пакетами (@company/package). Их удобно направить в hosted через .npmrc:

@company:registry=https://nexus.example.com/repository/npm-internal/

Тогда обычные пакеты идут через group (с кэшированием из npmjs.org), а всё под своим scope — сразу во внутренний hosted, минуя лишний проход через group-роутинг.

Подключаем PyPI

Логика та же, но конфигурация pip устроена немного иначе — единого group-эндпоинта для чтения и записи у PyPI-подобных репозиториев в Nexus два: один под установку пакетов (pypi-group), другой под их публикацию (pypi-internal, hosted).

Создаём репозитории:

  1. pypi-proxy — тип pypi (proxy), Remote storage: https://pypi.org.
  2. pypi-internal — тип pypi (hosted).
  3. pypi-group — group из первых двух.

Настройка pip на клиенте — файл ~/.pip/pip.conf (или %APPDATA%\pip\pip.ini на Windows):

[global]
index-url = https://nexus.example.com/repository/pypi-group/simple/

Обратите внимание на /simple/ в конце — это обязательная часть URL для PyPI-совместимого API, без неё pip не найдёт пакеты.

Публикация своего пакета — через twine, с указанием hosted-репозитория как upload URL:

pip install twine
python -m build
twine upload --repository-url https://nexus.example.com/repository/pypi-internal/ dist/*

Учётные данные для twine удобнее держать не в открытом виде в команде, а в ~/.pypirc:

[distutils]
index-servers = nexus

[nexus]
repository = https://nexus.example.com/repository/pypi-internal/
username = ci-publisher
password = <токен-пользователя-nexus>

Для CI-раннеров логин лучше заводить отдельным техническим пользователем с правами только на нужный репозиторий, а не использовать личные учётки разработчиков — это упрощает и аудит, и последующий отзыв доступа.

Подключаем Maven и Gradle

Maven-экосистема требует чуть больше конфигурации на стороне клиента, зато сама схема repository/proxy давно встроена в инструмент — именно так изначально и задумывался Nexus. Заводим три репозитория: maven-proxy (тип maven2 (proxy), Remote storage https://repo1.maven.org/maven2/), maven-releases и maven-snapshots (оба maven2 (hosted), с Version policy Release и Snapshot соответственно — Maven чувствителен к этому разделению), и maven-group, объединяющий все три.

Настройка в ~/.m2/settings.xml:

<settings>
  <mirrors>
    <mirror>
      <id>nexus</id>
      <mirrorOf>*</mirrorOf>
      <url>https://nexus.example.com/repository/maven-group/</url>
    </mirror>
  </mirrors>
  <servers>
    <server>
      <id>maven-releases</id>
      <username>ci-publisher</username>
      <password>${env.NEXUS_PASSWORD}</password>
    </server>
    <server>
      <id>maven-snapshots</id>
      <username>ci-publisher</username>
      <password>${env.NEXUS_PASSWORD}</password>
    </server>
  </servers>
</settings>

В pom.xml проекта — секция distributionManagement для публикации своих артефактов:

<distributionManagement>
  <repository>
    <id>maven-releases</id>
    <url>https://nexus.example.com/repository/maven-releases/</url>
  </repository>
  <snapshotRepository>
    <id>maven-snapshots</id>
    <url>https://nexus.example.com/repository/maven-snapshots/</url>
  </snapshotRepository>
</distributionManagement>

Публикация — стандартной командой mvn deploy, Maven сам определит по версии (оканчивается на -SNAPSHOT или нет), в какой из двух hosted-репозиториев отправить артефакт.

Для Gradle аналогично — в build.gradle:

repositories {
    maven { url 'https://nexus.example.com/repository/maven-group/' }
}
publishing {
    repositories {
        maven {
            url = version.endsWith('SNAPSHOT') ? 'https://nexus.example.com/repository/maven-snapshots/' : 'https://nexus.example.com/repository/maven-releases/'
            credentials { username = ciUser; password = ciPassword }
        }
    }
}

Политики безопасности и контроль цепочки поставок

Развёртывание — только половина задачи. Ценность для бизнеса появляется, когда прокси-репозиторий становится реальной точкой контроля, а не просто быстрым кэшем.

Блокировка компонентов. В Nexus можно вручную заблокировать конкретную версию пакета в proxy-репозитории (Blocking), если стало известно, что она скомпрометирована — новые сборки её не получат, даже если она уже была скачана кем-то раньше и висит в кэше. Это ручной, но рабочий механизм на случай инцидента типа «в пакете X обнаружили бэкдор» — подобные истории с реальными последствиями для прод-сборок разобраны в статье пакет удалили из реестра, и прод перестал собираться.

Разграничение доступа. Заведите отдельные роли: разработчики — только на чтение из group-репозиториев, CI — на чтение plus публикацию в свой hosted, администраторы — полный доступ. Nexus поддерживает ролевую модель на уровне репозиториев и путей внутри них (privileges с масками), этим стоит пользоваться с первого дня, а не «потом настроим».

Аудит и мониторинг исходящего трафика сборки. Даже с прокси-репозиторием стоит помнить: вредоносный пакет может не только украсть секреты во время установки, но и попытаться обратиться наружу в момент postinstall-скрипта — сам факт прокси это не остановит, если политика не блокирует конкретный пакет заранее. О том, как вообще заметить подобное поведение на уровне сети, — в статье пакет из npm полез в сеть при сборке: как заметить.

Explicitly не use latest в проде. Это не функция Nexus, а дисциплина команды, которую прокси-репозиторий делает практичной: фиксируйте версии в lock-файлах (package-lock.json, poetry.lock, pom.xml без диапазонов версий) — тогда даже если апстрим обновит пакет с уязвимостью, ваша сборка не подхватит её автоматически, пока вы сами не обновите lock-файл осознанно.

Здесь стоит быть честным: OSS-редакция Nexus Repository не включает автоматическое сканирование на уязвимости (CVE) при загрузке пакета — эта функция (Nexus Firewall / IQ Server) идёт в платной линейке Sonatype. На бесплатной версии контроль остаётся ручным и процессным — блокировки, ревью зависимостей, дисциплина lock-файлов — а не автоматическим. Для многих небольших и средних команд этого достаточно, но если требуется автоматическое сканирование каждого входящего артефакта, стоит закладывать это как отдельную статью бюджета, а не ожидать «из коробки».

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Nexus заменяет Docker registry, npm registry и PyPI одновременно?

Да, один инстанс Nexus Repository OSS умеет обслуживать все эти форматы параллельно — заводите отдельные репозитории под каждый тип, они не мешают друг другу и живут в одном blob store или в разных, по вашему выбору.

Что будет со сборкой, если сервер с Nexus упадёт?

Сборки, которым нужны новые пакеты, встанут — group-репозиторий больше не отвечает. Поэтому под Nexus стоит закладывать регулярный бэкап и, при серьёзной нагрузке, план восстановления на резервном сервере, а не рассчитывать на вечный аптайм одного инстанса.

Сколько ресурсов реально нужно на VPS под Nexus и CI?

Зависит от числа сборок и размера кэша — ориентировочные цифры и логика расчёта разобраны отдельно в статье сколько ресурсов нужно VPS для разработчика и CI/CD; для старта достаточно 4 ГБ RAM и 30-50 ГБ диска с последующим ростом по факту нагрузки.

Можно ли подключить приватные пакеты из GitHub Packages или другого стороннего реестра через Nexus?

Да, через дополнительный proxy-репозиторий с нужным remote URL и, если требуется, токеном авторизации в настройках remote storage — принцип тот же, что для npmjs.org или PyPI.

Nexus обязательно ставить на отдельный сервер?

Нет, для небольшой команды его можно держать на том же VPS, где крутится CI-раннер, но тогда стоит внимательнее следить за памятью — JVM-процесс Nexus и раннер конкурируют за одни и те же ресурсы под пиковой нагрузкой.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →