На devops.local в стеке devops:
| Что | ПО | URL | Данные на диске |
|---|---|---|---|
| Maven / Gradle | Reposilite 3.x | https://devops.local/maven/ | /opt/compose/devops/data/maven |
| Debian APT | reprepro + nginx | https://devops.local/debian/ | /opt/compose/devops/data/debian |
| GPG ключ APT | gnupg в контейнере | https://devops.local/debian/gpg-pubkey.asc | /opt/compose/devops/data/debian-gpg |
Веб-морды только через nginx HTTPS. Reposilite и debian-www в Docker-сети portal (nginx резолвит devops-maven / devops-debian-www); на хосте ещё 127.0.0.1:8083 / :8084 для отладки. CA: docs/registry-tls.md.
Портал: https://devops.local/ — секции Middleware и DevOps.
make devops # runner, mcp, maven, debian, registry, portainer, TLS
make middleware # nginx: /maven/, /debian/ + портал
Порядок важен: сначала devops (TLS), потом middleware. make deploy делает так сам.
Перед первым деплоем: make setup (Maven username/password в .env).
Лёгкий Maven 2-репозиторий с UI. По умолчанию три хранилища:
releases— релизные версии (1.2.3, без-SNAPSHOT)snapshots—-SNAPSHOTcentral— прокси Maven Central (read-through)
UI: https://devops.local/maven/
Логин: maven_username / maven_password из secrets (--token при старте контейнера).
После первого входа в UI → Console:
token-generate ci m
получите отдельный токен для CI (можно с --secret=...). Management-токен m умеет всё; для publish хватает прав на репозиторий.
<settings>
<servers>
<server>
<id>devops-releases</id>
<username>darvik</username>
<password>ВАШ_ПАРОЛЬ</password>
</server>
<server>
<id>devops-snapshots</id>
<username>darvik</username>
<password>ВАШ_ПАРОЛЬ</password>
</server>
</servers>
<mirrors>
<mirror>
<id>devops-central</id>
<name>Reposilite central proxy</name>
<url>https://devops.local/maven/central</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>id сервера должен совпасть с id в pom.xml / distributionManagement.
Репозиторий отдаётся по HTTPS с приватным CA. Сначала доверьтесь CA (registry-tls.md): Windows — в «Доверенные корневые», Linux — update-ca-certificates. JVM после этого обычно подхватывает системный store.
Если mvn всё ещё пишет PKIX / unable to find valid certification path:
sudo curl -fsSL http://devops.local/ca.crt -o /tmp/devops.local.crt
sudo keytool -importcert -noprompt -alias devops.local \
-keystore "$JAVA_HOME/lib/security/cacerts" -storepass changeit \
-file /tmp/devops.local.crtПример <repository>:
<repository>
<id>devops-releases</id>
<url>https://devops.local/maven/releases</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>false</enabled></snapshots>
</repository><distributionManagement>
<repository>
<id>devops-releases</id>
<url>https://devops.local/maven/releases</url>
</repository>
<snapshotRepository>
<id>devops-snapshots</id>
<url>https://devops.local/maven/snapshots</url>
</snapshotRepository>
</distributionManagement>mvn -DskipTests deployВерсия 1.0.0-SNAPSHOT → snapshots, 1.0.0 → releases.
settings.gradle.kts / build.gradle.kts:
repositories {
maven { url = uri("https://devops.local/maven/releases") }
maven { url = uri("https://devops.local/maven/snapshots") }
mavenCentral()
}
publishing {
repositories {
maven {
name = "devops"
url = uri(
if (version.toString().endsWith("-SNAPSHOT"))
"https://devops.local/maven/snapshots"
else
"https://devops.local/maven/releases"
)
credentials {
username = System.getenv("MAVEN_USERNAME")
password = System.getenv("MAVEN_PASSWORD")
}
isAllowInsecureProtocol = true
}
}
}export MAVEN_USERNAME=darvik MAVEN_PASSWORD='...'
./gradlew publishcurl -u darvik:'пароль' --upload-file target/my-lib-1.0.0.jar \
"https://devops.local/maven/releases/com/example/my-lib/1.0.0/my-lib-1.0.0.jar"Нужны ещё POM и checksums; проще mvn deploy.
# .gitea/workflows/maven-publish.yml
name: Publish Maven
on:
push:
tags: ["v*"]
jobs:
publish:
runs-on: devops.local
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
- name: Trust devops.local CA
run: |
sudo curl -fsSL http://devops.local/ca.crt -o /usr/local/share/ca-certificates/devops.local.crt
sudo update-ca-certificates
sudo keytool -importcert -noprompt -alias devops.local \
-keystore "$JAVA_HOME/lib/security/cacerts" -storepass changeit \
-file /usr/local/share/ca-certificates/devops.local.crt || true
- name: settings.xml
run: |
mkdir -p ~/.m2
cat > ~/.m2/settings.xml <<EOF
<settings>
<servers>
<server>
<id>devops-releases</id>
<username>${{ secrets.MAVEN_USERNAME }}</username>
<password>${{ secrets.MAVEN_PASSWORD }}</password>
</server>
</servers>
</settings>
EOF
- run: mvn -B -DskipTests deployСекреты в Gitea: Settings → Secrets (MAVEN_USERNAME, MAVEN_PASSWORD). Runner label: devops.local (как в gitea_runner_labels).
Runner крутится на том же хосте: в job используйте https://devops.local/maven/... и шаг Trust devops.local CA выше (job-контейнер не видит host trust store).
<repositories>
<repository>
<id>devops-releases</id>
<url>https://devops.local/maven/releases</url>
</repository>
</repositories>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-lib</artifactId>
<version>1.0.0</version>
</dependency>Если артефакт публичный в Reposilite (visibility PUBLIC) — пароль для download не нужен. Для PRIVATE — тот же <server> в settings.xml.
| Действие | Как |
|---|---|
| Логи | docker logs -f devops-maven |
| Сброс UI-конфига | удалить /opt/compose/devops/data/maven/configuration.cdn и make devops (seed вернёт basePath: /maven/) |
| Память | JAVA_OPTS=-Xmx256M в compose |
| Бэкап | скопировать data/maven |
Подписанный APT-репозиторий одной серии:
- Origin / Label:
devops.local - Codename:
bookworm(переменные вinventory/group_vars/devops.yml) - Component:
main - Arch:
amd64 source
Контейнер devops-debian-repo раз в 15 с забирает *.deb из incoming/ и делает reprepro includedeb.
devops-debian-www отдаёт дерево наружу. Nginx портала проксирует /debian/.
GPG-ключ создаётся один раз (без passphrase) и лежит в /opt/compose/devops/data/debian-gpg. Публичная часть: /debian/gpg-pubkey.asc.
sudo mkdir -p /etc/apt/keyrings /usr/local/share/ca-certificates
sudo curl -fsSL http://devops.local/ca.crt \
-o /usr/local/share/ca-certificates/devops.local.crt
sudo update-ca-certificates
curl -fsSL https://devops.local/debian/gpg-pubkey.asc \
| sudo gpg --dearmor -o /etc/apt/keyrings/devops.local.gpg
echo "deb [signed-by=/etc/apt/keyrings/devops.local.gpg] https://devops.local/debian bookworm main" \
| sudo tee /etc/apt/sources.list.d/devops.local.list
sudo apt update
apt-cache policyСтарый apt-key add не используйте. signed-by= обязателен на bookworm/noble.
Установка пакета:
sudo apt install имя-пакетаВариант A — incoming (проще)
scp myapp_1.0.0_amd64.deb darvik@devops.local:/opt/compose/devops/data/debian/incoming/
# через ~15 с пакет появится в dists/bookworm/mainВариант B — сразу reprepro
ssh darvik@devops.local
sudo docker exec -i devops-debian-repo \
reprepro -b /repo includedeb bookworm /repo/incoming/myapp_1.0.0_amd64.debПоложить файл в incoming и exec — если не хотите ждать цикл.
Вариант C — с Windows
scp .\myapp_1.0.0_amd64.deb darvik@devops.local:/opt/compose/devops/data/debian/incoming/Проверка:
curl -s https://devops.local/debian/dists/bookworm/main/binary-amd64/PackagesМинимум: debian/ в исходниках + dpkg-buildpackage, или nfpm, или fpm.
nfpm example nfpm.yaml:
name: myapp
arch: amd64
platform: linux
version: 1.0.0
maintainer: darvik
description: example
contents:
- src: ./myapp
dst: /usr/bin/myappnfpm package -p deb -f nfpm.yamlИмя файла лучше в формате Debian: имя_версия_архитектура.deb.
name: Publish deb
on:
push:
tags: ["v*"]
jobs:
deb:
runs-on: devops.local
steps:
- uses: actions/checkout@v4
- name: Build deb
run: |
# ваш nfpm / dpkg-buildpackage
nfpm package -p deb -f nfpm.yaml
- name: Ingest
run: |
cp ./*.deb /opt/compose/devops/data/debian/incoming/
sleep 20
curl -fsSL "http://127.0.0.1/debian/dists/bookworm/main/binary-amd64/Packages" | grep -E '^Package:'Job идёт на runner на devops.local, путь /opt/compose/... доступен, если runner не в изоляции. Если job в контейнере catthehacker/ubuntu:act-latest, bind-mount хоста нет — тогда:
- name: Ingest via docker
run: |
docker cp ./*.deb devops-debian-repo:/repo/incoming/(docker.sock у runner уже примонтирован.)
В devops.yml:
debian_codename: bookworm # или jammy, noble, ...Для нескольких codename сразу — добавьте ещё один блок в stacks/devops/debian/conf/distributions.j2 (пустая строка между блоками) и кладите пакеты так:
docker exec -i devops-debian-repo reprepro -b /repo includedeb jammy /repo/incoming/foo.debЦикл incoming/*.deb всегда кладёт в DEBIAN_CODENAME (один codename). Для второго — только ручной includedeb.
| Тема | Детали |
|---|---|
| Логи ingest | docker logs -f devops-debian-repo |
| Список пакетов | docker exec devops-debian-repo reprepro -b /repo list bookworm |
| Удалить пакет | docker exec devops-debian-repo reprepro -b /repo remove bookworm имя |
| Ключ | бэкап data/debian-gpg + data/debian; потеря ключа = клиенты перестанут доверять Release |
conf/ и db/ |
закрыты в nginx, снаружи не отдаются |
| HTTP без TLS | для LAN ок; подпись Release защищает метаданные, но ключ лучше раздавать по доверенному каналу (вы уже в LAN) |
| Passphrase ключа | сейчас пустая, чтобы reprepro подписывал без tty. Не выносите data/debian-gpg за пределы хоста |
inventory/group_vars/devops.yml и all.yml:
| Variable | Default | Назначение |
|---|---|---|
maven_http_port |
8083 |
Reposilite на localhost (nginx → /maven/) |
debian_http_port |
8084 |
debian-www на localhost (nginx → /debian/) |
debian_codename |
bookworm |
APT suite |
debian_components |
main |
компонент |
debian_architectures |
amd64 source |
архитектуры |
debian_gpg_name / debian_gpg_email |
devops.local / packages@devops.local |
uid ключа |
Секреты: maven_username, maven_password, плюс токены runner/MCP.
Maven 401 / 403 — неверный token или snapshot в releases (Reposilite не пускает SNAPSHOT в releases).
Maven «Blocked mirror» / HTTP disabled — Maven 3.8.1+; см. mirror blocked: false выше.
/maven/ отдаёт портал — nginx не пересоздан после смены конфига: make middleware (роль force-recreate nginx) или docker restart middleware-nginx.
APT NO_PUBKEY / EXPKEYSIG — не импортирован gpg-pubkey.asc или ключ перевыпустили (тогда заново dearmor + apt update).
APT пустой Packages — .deb ещё в incoming/ (смотрите логи devops-debian-repo) или архитектура не amd64.
Portal debian/maven offline — nginx должен быть в сети portal вместе с devops-maven / devops-debian-www. Сначала make devops, потом make middleware (nginx force-recreate). Не используйте host.docker.internal:8083 — порты только на 127.0.0.1, из контейнера туда не попасть.
gitea-mcp -bind — в текущем образе флага нет; command: ["/app/gitea-mcp", "-t", "http", "-p", "8080"].