service:nexus
Различия
Показаны различия между двумя версиями страницы.
Предыдущая версия справа и слеваПредыдущая версияСледующая версия | Предыдущая версия | ||
service:nexus [13.02.2025 13:37] – [Получить версию самой последней версии артефакта] viacheslav | service:nexus [12.04.2025 06:57] (текущий) – [apt] viacheslav | ||
---|---|---|---|
Строка 1: | Строка 1: | ||
+ | ====== Nexus ====== | ||
+ | Система хранения артефактов, | ||
+ | |||
+ | Типы: | ||
+ | * hosted — располагающиеся локально на собственном хранилище | ||
+ | * proxy — ссылка на удалённый репозиторий, | ||
+ | * group — позволяет объединять несколько репозиториев в одну группу | ||
+ | |||
+ | В Nexus при создании репозитория типа maven2 спрашивается, | ||
+ | Когда продукт сырой и мы ещё не готовы опубликовать готовую версию, | ||
+ | |||
+ | ===== Заливка ===== | ||
+ | These are bare bones bash scripts to import a Nexus 2 Maven, NuGet or npm repository (and likely other file system based repos) into Nexus Repository 3. It imports artifacts into a Nexus Repository 3 Maven2, NuGet or npm hosted repo.\\ | ||
+ | https:// | ||
+ | |||
+ | Если заливать одну и ту же версию, | ||
+ | * Disable redeploy (по умолчанию) - если версия уже есть, то заливаться ничего не будет, даже если файл отличается. | ||
+ | * Allow redeploy - если файл той же версии, | ||
+ | ==== Maven ==== | ||
+ | '' | ||
+ | |||
+ | <file xml pom.xml> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <file xml settings.xml> | ||
+ | < | ||
+ | xmlns=" | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <file yaml .gitlab-ci.yml> | ||
+ | stages: | ||
+ | - release | ||
+ | |||
+ | # backend | ||
+ | upload-backend-release: | ||
+ | stage: release | ||
+ | only: | ||
+ | changes: | ||
+ | - backend/ | ||
+ | needs: | ||
+ | - build-backend-code-job | ||
+ | script: | ||
+ | - cd backend | ||
+ | # Вместо mvn package теперь mvn deploy | ||
+ | - > | ||
+ | mvn deploy -DskipTests | ||
+ | -Dmaven.repo.local=${CI_PROJECT_DIR}/ | ||
+ | -s settings.xml -Dversion.application=${VERSION} | ||
+ | |||
+ | # frontend заливается через curl | ||
+ | upload-frontend-release: | ||
+ | stage: release | ||
+ | only: | ||
+ | changes: | ||
+ | - frontend/ | ||
+ | needs: | ||
+ | - build-frontend-code-job | ||
+ | script: | ||
+ | - cd frontend/ | ||
+ | - tar czvf testapp-${VERSION}.tar.gz frontend | ||
+ | - > | ||
+ | curl -v -u " | ||
+ | --upload-file testapp-${VERSION}.tar.gz | ||
+ | ${NEXUS_REPO_URL}/ | ||
+ | </ | ||
+ | |||
+ | ==== curl ==== | ||
+ | Curl-ом можно заливать файлы в репозитории Maven2, YUM и RAW. Репозитории NuGet, NPM и Docker не поддерживают такую загрузку. | ||
+ | <code bash> | ||
+ | # В URL maven-releases - название репозитория, | ||
+ | curl -u admin:admin -T artifact.jar http:// | ||
+ | </ | ||
+ | {{: | ||
+ | |||
+ | https:// | ||
+ | |||
+ | Залить все файлы из каталога, | ||
+ | <code bash> | ||
+ | # Здесь: из каталога ~/cka в репозиторий http:// | ||
+ | # 2 замены - первая меняет путь на URL, вторая - пробелы на %20 | ||
+ | find ~/cka -type f -print0 |xargs -0 bash -c 'for f; do i=$(echo ${f/ | ||
+ | </ | ||
+ | По мотивам https:// | ||
+ | |||
+ | Powershell | ||
+ | <code powershell> | ||
+ | $folder = ' | ||
+ | $repo = ' | ||
+ | $cred = ' | ||
+ | |||
+ | $files = (dir $folder -Recurse -File).FullName | ||
+ | |||
+ | foreach ($f in $files) { | ||
+ | $url = $f.Replace(" | ||
+ | "$f -> | ||
+ | & curl.exe -ksu $cred -T $f $url | ||
+ | } | ||
+ | </ | ||
+ | ==== Docker ==== | ||
+ | Предполагается, | ||
+ | |||
+ | - Cоздать репозиторий '' | ||
+ | - Отредактировать docker-compose или Dockerfile, чтобы этот порт был доступен снаружи, | ||
+ | - Cоздать в Нексусе пользователя для этого репозитория и дать ему права. | ||
+ | - Чтобы Докер работал с незащищённым репозиторием, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | ], | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ] | ||
+ | } | ||
+ | </ | ||
+ | - Далее <code bash> | ||
+ | # Дать права Докеру на файл | ||
+ | sudo chown $USER: | ||
+ | # Перечитать настройки (может, | ||
+ | sudo systemctl reload docker | ||
+ | |||
+ | # Залогиниться (Докер-клиент не поддерживает путей URL, только хост: | ||
+ | docker login k3:8082 | ||
+ | # Поставить тэг образу, | ||
+ | docker tag alpine:3.19 k3: | ||
+ | # Залить образ в Нексус | ||
+ | docker push k3: | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | Заливка всех образов, | ||
+ | <code bash> | ||
+ | # Логин в репу Нексуса | ||
+ | docker login k3:8082 | ||
+ | # Адрес репы (без слэша в конце) | ||
+ | repo=' | ||
+ | # Список образов (кроме < | ||
+ | # egrep отфильтровывает образы с тэгами, | ||
+ | images=($(docker image ls | awk ' | ||
+ | # Тэгировать и залить | ||
+ | for i in " | ||
+ | </ | ||
+ | |||
+ | ==== Python ==== | ||
+ | Используется twine. В конце URL необходим trailing slash, иначе будет ошибка при загрузке. | ||
+ | <code powershell> | ||
+ | # Upload all files in the folder | ||
+ | $twine = " | ||
+ | |||
+ | & $twine upload --repository-url " | ||
+ | -u admin -p admin --non-interactive --skip-existing --disable-progress-bar ` | ||
+ | C: | ||
+ | </ | ||
+ | Есть вариант прописывать реквизиты входа в файл '' | ||
+ | <file ini ~/ | ||
+ | [distutils] | ||
+ | index-servers = pypi | ||
+ | [pypi] | ||
+ | repository: http:// | ||
+ | username: admin | ||
+ | password: admin | ||
+ | </ | ||
+ | Тогда команда загрузки будет | ||
+ | <code powershell> | ||
+ | # -r - [имя репы], прописанной в .pypirc | ||
+ | twine upload -r pypi C: | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | |||
+ | ===== Скачивание ===== | ||
+ | <file bash testapp-backend.service> | ||
+ | [Unit] | ||
+ | Description=testapp-backend | ||
+ | |||
+ | [Service] | ||
+ | User=jarservice | ||
+ | Environment=REPORT_PATH=/ | ||
+ | Environment=LOG_PATH=/ | ||
+ | Restart=always | ||
+ | ExecStart=/ | ||
+ | -Dmyserver.basePath='/ | ||
+ | -Dmyserver.bindAddr=' | ||
+ | -Dmyserver.bindPort=' | ||
+ | -Dmyserver.hostName=' | ||
+ | -jar '/ | ||
+ | SuccessExitStatus=143 | ||
+ | |||
+ | [Install] | ||
+ | WantedBy=multi-user.target | ||
+ | </ | ||
+ | |||
+ | <file bash deploy.sh> | ||
+ | #!/bin/bash | ||
+ | # Скрипт валится при любой ошибке | ||
+ | set -xe | ||
+ | # Перезалить дескриптор сервиса на ВМ для деплоя | ||
+ | sudo cp -rf testapp-backend.service / | ||
+ | sudo rm -f / | ||
+ | # Перенос артефакта в нужную папку | ||
+ | curl -u ${NEXUS_REPO_USER}: | ||
+ | sudo cp ./ | ||
+ | # Обновить конфиг systemd | ||
+ | sudo systemctl daemon-reload | ||
+ | # Перезапустить сервис | ||
+ | sudo systemctl restart testapp-backend | ||
+ | </ | ||
+ | |||
+ | <file yaml .gitlab-ci.yml> | ||
+ | deploy: | ||
+ | stage: deploy | ||
+ | script: | ||
+ | - scp ./ | ||
+ | - > | ||
+ | ssh ${DEV_USER}@${DEV_HOST} " | ||
+ | export " | ||
+ | export " | ||
+ | export " | ||
+ | export " | ||
+ | export " | ||
+ | export " | ||
+ | setsid /bin/bash -s " < ./ | ||
+ | </ | ||
+ | ==== Gradle ==== | ||
+ | Копия дистрибутива в локальном Nexus (raw-репозиторий) | ||
+ | <file bash gradle/ | ||
+ | distributionBase=GRADLE_USER_HOME | ||
+ | distributionPath=wrapper/ | ||
+ | # | ||
+ | distributionUrl=http\:// | ||
+ | zipStoreBase=GRADLE_USER_HOME | ||
+ | zipStorePath=wrapper/ | ||
+ | </ | ||
+ | |||
+ | === Аутентификация для gradlew === | ||
+ | |||
+ | <file yaml .gitlab-ci.yaml> | ||
+ | variables: | ||
+ | GRADLE_OPTS: | ||
+ | |||
+ | # Сборка на воркере | ||
+ | .build: &build | ||
+ | - cd $CI_PROJECT_DIR | ||
+ | # Если нет в вышестоящих переменных | ||
+ | # - export GRADLE_OPTS=" | ||
+ | - cd test-module | ||
+ | - chmod +x ./gradlew | ||
+ | - > | ||
+ | ./gradlew build | ||
+ | -x test-metamodel: | ||
+ | -P gitLabPrivateToken=$REGISTRY_TOKEN | ||
+ | |||
+ | # Сборка через ssh, нужно экранировать кавычки, | ||
+ | # bash: line 0: export: `-Dgradle.wrapperPassword=[MASKED]': | ||
+ | apiTests: | ||
+ | stage: test | ||
+ | script: | ||
+ | - cd $CI_PROJECT_DIR | ||
+ | - ssh dev-server | ||
+ | " | ||
+ | cd / | ||
+ | git init && | ||
+ | ./gradlew -x test clean build publishMavenPublicationToMavenLocal -P gitLabPrivateToken=$REGISTRY_TOKEN" | ||
+ | </ | ||
+ | |||
+ | В свойствах пишем URL без логина и пароля, | ||
+ | <file bash gradle/ | ||
+ | distributionUrl=http\:// | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | Для CI/CD. Дело в том, что при локальной сборке разработчиками у них нет данных для доступа в локальный репозиторий.\\ | ||
+ | Это скрипт замены ссылки в файле ''/ | ||
+ | если в переменной '' | ||
+ | <code bash> | ||
+ | [[ $GRADLE_OPTS =~ wrapperUser ]] && \ | ||
+ | sed -i ' | ||
+ | # Было | ||
+ | distributionUrl=https\:// | ||
+ | # Стало | ||
+ | distributionUrl=https\:// | ||
+ | </ | ||
+ | |||
+ | ==== Python ==== | ||
+ | Репозиторий pypi можно скачать curl-ом, а вот загружать им туда не получится, | ||
+ | С иерархией каталогов, | ||
+ | |||
+ | Powershell | ||
+ | <code powershell> | ||
+ | $server = ' | ||
+ | $repo = ' | ||
+ | $cred = ' | ||
+ | $urls = " | ||
+ | $dest = " | ||
+ | |||
+ | mkdir $dest -ErrorAction SilentlyContinue > $null | ||
+ | $repoUrl = " | ||
+ | |||
+ | # Make URLs list of packages | ||
+ | $packageList = curl.exe -ksu $cred " | ||
+ | |||
+ | $versionsList = ($packageList -match 'a href' -replace '< | ||
+ | (curl.exe -ksu $cred " | ||
+ | } | ||
+ | |||
+ | # Packages or not packages | ||
+ | # ($versionsList -replace ' | ||
+ | |||
+ | $packages - $versionsList -replace ' | ||
+ | # If list restriction is needed, e.g. low disk space | ||
+ | #$letters = ($([char[]](0..255) -match ' | ||
+ | if ($letters) { | ||
+ | $packagesSelected = $packages -match " | ||
+ | } | ||
+ | else { | ||
+ | $packagesSelected = $packages | ||
+ | } | ||
+ | |||
+ | $packagesSelected |% { | ||
+ | " | ||
+ | } > $urls | ||
+ | |||
+ | # Download | ||
+ | gc $urls |% { | ||
+ | echo $_ | ||
+ | curl.exe -ksu $cred -o " | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | ==== curl ==== | ||
+ | Скачивание всего репозитория. В зависимости от типа репозитория (здесь пример для maven-репы), | ||
+ | <code bash> | ||
+ | server=' | ||
+ | repo=' | ||
+ | cred=' | ||
+ | urls='/ | ||
+ | |||
+ | # Get assets list | ||
+ | assets=" | ||
+ | i=$(curl -ksu $cred -X GET $assets) | ||
+ | grep downloadUrl <<< | ||
+ | token=$(grep continuationToken <<< | ||
+ | while [[ -n $token ]]; do | ||
+ | i=$(curl -ksu $cred -X GET " | ||
+ | grep downloadUrl <<< | ||
+ | token=$(grep continuationToken <<< | ||
+ | done | ||
+ | |||
+ | # Create subdirs | ||
+ | mkdir -p $(sed -E " | ||
+ | |||
+ | # Download | ||
+ | for i in $(cat $urls); do | ||
+ | echo $i | ||
+ | curl -ksu $cred -o $(sed -E " | ||
+ | done | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Примеры ссылок в $urls для репозитория Docker | ||
+ | # http:// | ||
+ | # http:// | ||
+ | # Регулярки для sed | ||
+ | mkdir -p $(sed -E ' | ||
+ | curl -ksu $cred -o $(sed -E ' | ||
+ | </ | ||
+ | https:// | ||
+ | https:// | ||
+ | https:// | ||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | ==== Ansible ==== | ||
+ | <code bash> | ||
+ | ansible k3 -m maven_artifact -a " | ||
+ | repository_url=http:// | ||
+ | username=admin | ||
+ | password=admin | ||
+ | artifact_id=lunar-lang | ||
+ | group_id=org.foo | ||
+ | dest=/ | ||
+ | </ | ||
+ | |||
+ | ==== apt ==== | ||
+ | Прокси-репа. На Нексусе надо сделать репозиторий apt (proxy), например, | ||
+ | Удобно сделать анонимный доступ к серверу, | ||
+ | |||
+ | ^Параметр ^Значение ^Описание ^ | ||
+ | |Distribution |jammy |Ubuntu 22.04 | | ||
+ | |Remote storage |http:// | ||
+ | |||
+ | На клиентах прописать в ''/ | ||
+ | <code bash> | ||
+ | deb http:// | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | |||
+ | ===== Поиск ===== | ||
+ | <code bash> | ||
+ | # Артефакт lunar-lang, сортировать по версиям | ||
+ | curl -qu admin:admin ' | ||
+ | # Определённая версия | ||
+ | curl -qu admin:admin ' | ||
+ | # В определённом репозитории | ||
+ | curl -qu admin:admin ' | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | Извлечение ссылок, | ||
+ | <code powershell> | ||
+ | ($json |ConvertFrom-Json).items.downloadUrl | ||
+ | |||
+ | # Вывод информации о найденных пакетах, | ||
+ | ($json |ConvertFrom-Json).items.maven2 |sort {[version]$_.version} | ||
+ | |||
+ | extension groupId artifactId version | ||
+ | --------- ------- ---------- ------- | ||
+ | jar | ||
+ | jar | ||
+ | jar | ||
+ | jar | ||
+ | </ | ||
+ | Bash | ||
+ | <code bash> | ||
+ | # jq -r - без кавычек | ||
+ | $json |jq .items[].downloadUrl | ||
+ | </ | ||
+ | |||
+ | ==== Получить версию самой последней версии артефакта ==== | ||
+ | Нужно, чтобы проставить версию в описании компонента при деплое. Надо парсить maven-metadata.xml | ||
+ | <code bash> | ||
+ | version=$(curl -qu admin:admin http:// | ||
+ | # В версиях встречаются цифры, точки и дефисы | ||
+ | sed ' | ||
+ | 1.0.12 | ||
+ | </ | ||
+ | |||
+ | Это не всегда работает. Можно сначала запросить все версии (важно '' | ||
+ | <code bash> | ||
+ | # curl | ||
+ | json=$(curl -qu admin:admin ' | ||
+ | |||
+ | jq -r .version <<< | ||
+ | </ | ||
+ | |||
+ | Ansible | ||
+ | <code yaml> | ||
+ | - name: Get artifact versions | ||
+ | uri: | ||
+ | url: http:// | ||
+ | user: admin | ||
+ | password: admin | ||
+ | method: GET | ||
+ | force_basic_auth: | ||
+ | validate_certs: | ||
+ | body_format: | ||
+ | register: versions | ||
+ | |||
+ | - name: Set variables | ||
+ | set_fact: | ||
+ | artifactId: "{{ versions[' | ||
+ | extension: "{{ versions[' | ||
+ | groupId: "{{ versions[' | ||
+ | version: "{{ versions[' | ||
+ | |||
+ | - name: Display variables | ||
+ | debug: | ||
+ | msg: | ||
+ | - "{{ groupId }}" | ||
+ | - "{{ artifactId }}.{{ extension }}" | ||
+ | - "{{ version }}" | ||
+ | |||
+ | # Как вариант - запрос json | ||
+ | # - debug: | ||
+ | # msg: "{{ versions.json | json_query(' | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== Список репозиториев ==== | ||
+ | '' | ||
+ | <code bash> | ||
+ | (& curl.exe -ku admin:admin http:// | ||
+ | ConvertFrom-Json) |select name, | ||
+ | |||
+ | name format type | ||
+ | ---- ------ ---- | ||
+ | docker | ||
+ | maven-central | ||
+ | maven-public | ||
+ | maven-releases | ||
+ | maven-snapshots maven2 hosted http:// | ||
+ | nuget.org-proxy nuget proxy http:// | ||
+ | nuget-group | ||
+ | nuget-hosted | ||
+ | pypi pypi | ||
+ | pypi-lib | ||
+ | test raw hosted http:// | ||
+ | ubuntu2204 | ||
+ | ubuntu2204-sec | ||
+ | </ | ||
+ | ===== Удаление ===== | ||
+ | <code powershell> | ||
+ | $apiUrl = ' | ||
+ | $cred = ' | ||
+ | (curl.exe -ksu $cred " | ||
+ | curl.exe -ksu $cred -X DELETE " | ||
+ | } | ||
+ | </ | ||
+ | ==== Docker ==== | ||
+ | [[https:// | ||
+ | |||
+ | |||
+ | ===== Установка Nexus ===== | ||
+ | |||
+ | ==== Docker ==== | ||
+ | <file yaml docker-compose.yml> | ||
+ | services: | ||
+ | nexus: | ||
+ | image: sonatype/ | ||
+ | container_name: | ||
+ | restart: unless-stopped | ||
+ | ports: | ||
+ | - 8081:8081 | ||
+ | volumes: | ||
+ | - ./ | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Узнать пароль админа после первоначальной установки | ||
+ | docker exec nexus cat / | ||
+ | </ | ||
+ | |||
+ | Пользователь id 200 | ||
+ | <code bash> | ||
+ | docker run --rm -d --name nexus sonatype/ | ||
+ | docker exec nexus grep nexus /etc/passwd | ||
+ | nexus: | ||
+ | </ | ||
+ | |||
+ | ==== Gitlab Authentication Realm ==== | ||
+ | |||
+ | <code bash> | ||
+ | FROM sonatype/ | ||
+ | USER root | ||
+ | |||
+ | RUN mkdir -p / | ||
+ | curl -Lo / | ||
+ | echo " | ||
+ | |||
+ | USER nexus | ||
+ | |||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ==== Миграция OrientDB > H2 ==== | ||
+ | Nexus 3.70 - последняя версия, | ||
+ | <code bash> | ||
+ | # Забэкапить базы из GUI, скопировать в папку backup. | ||
+ | # Остановить Nexus. | ||
+ | # Скачать мигратор | ||
+ | curl -L https:// | ||
+ | # Запустить Nexus в командной строке | ||
+ | docker run -it --rm -v ./ | ||
+ | # В контейнере: | ||
+ | cd /nexus-data | ||
+ | java -Xmx16G -Xms1G -XX: | ||
+ | # На хосте | ||
+ | cp backup/ | ||
+ | echo " | ||
+ | </ | ||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | ==== SSL ==== | ||
+ | Исходный сертификат с ключом должен быть в формате PCKS12. | ||
+ | <code bash> | ||
+ | # Если исходник в формате PEM, его надо сконвертировать | ||
+ | openssl pkcs12 -export -in cert.pem -out cert.pkcs12 | ||
+ | |||
+ | # Если исходник - это crt + key, то | ||
+ | openssl pkcs12 -export -in cert.crt -inkey private.key -out cert.pkcs12 | ||
+ | </ | ||
+ | Если исходник в виде .pfx, то конвертировать ничего не надо. | ||
+ | |||
+ | <code bash> | ||
+ | # Импортировать сертификат в хранилище ключей jks | ||
+ | # Важно: пароль оставить тем же, что и в сертификате | ||
+ | # Возможно, | ||
+ | keytool -v -importkeystore -srckeystore cert.pkcs12 -srcstoretype PKCS12 \ | ||
+ | -destkeystore | ||
+ | |||
+ | nano < | ||
+ | # Добавить строку | ||
+ | application-port-ssl=443 | ||
+ | # Раскомментировать строку с nexus-args и добавить туда | ||
+ | ${jetty.etc}/ | ||
+ | |||
+ | nano < | ||
+ | # Заменить password на пароль сертификата (в 3 местах) | ||
+ | </ | ||
+ | Перезапустить Nexus. | ||
+ | |||
+ | https:// | ||
+ | ===== Дополнительно ===== | ||
+ | ==== Nexus как веб-сервер ==== | ||
+ | Создаётся репозиторий hosted raw, и в его настройках нужно переключить Content Disposition на Inline, чтобы html-файлы отображались непосредственно в браузере, | ||
+ | |||
+ | {{: | ||
+ | |||
+ | |||
+ | ===== Проблемы ===== | ||
+ | ==== An error occured saving data. Duplicate key ==== | ||
+ | Появляется при попытке загрузки после миграции БД с OrientDB на H2. | ||
+ | |||
+ | предполагаемое решение: | ||
+ | ===== Литература ===== | ||
+ | |||
+ | [[https:// | ||
+ | [[https:// | ||