nelm CI: checkout from Bitbucket + docs

This commit is contained in:
lab 2026-09-11 13:16:31 +00:00
commit 4e190d957d
7 changed files with 685 additions and 0 deletions

196
CLIENT-SETUP.ru.md Normal file
View File

@ -0,0 +1,196 @@
# Настройка билда и деплоя (nelm + Jenkins + Bitbucket) в клиентской инфраструктуре
Исходные условия у клиента: **уже есть** чистый Kubernetes/Deckhouse-кластер, **Jenkins** и **Bitbucket**. Ниже — только то, что нужно донастроить, чтобы нажатием кнопок в Jenkins собирать образы и выкатывать сервисы через `nelm` из актуальных репозиториев Bitbucket.
## 1. Идея схемы
```
Bitbucket (источник правды: код + .helm/)
│ git clone на каждый job
Jenkins agent (одна ВМ: docker build/push + nelm release install)
│ kubeconfig сервисного аккаунта
Кластер(ы) dev / preprod / prod
```
- **Не** хранить копию репозиториев вручную на агенте (`~/nelm-work/bitbucket` и т.п.).
- На каждый build/deploy Jenkins **клонирует** нужные репозитории с ветки (обычно `main`).
- Образы уходят во **внутренний registry**.
- Манифесты применяет **`nelm release install`** (не werf, не helm из GitLab CI).
- Секреты в чартах — в `secret-values.yaml`, ключ только на агенте / в credentials Jenkins.
## 2. Что подготовить один раз
### 2.1. Инструменты на Jenkins agent
На той же машине, где крутится agent (или в контейнере agent с доступом к Docker socket):
| Компонент | Зачем |
|-----------|--------|
| `git` | checkout репозиториев |
| `docker` (+ доступ к socket / DinD) | build & push |
| `nelm` | render/install релизов |
| `kubectl` | namespaces, проверка подов, kubeconfig |
| доступ к Bitbucket HTTP(S) | clone |
| доступ к registry | push/pull |
| доступ к API Kubernetes | deploy |
Пример установки `nelm` (если есть выход в интернет на этапе подготовки; в закрытом контуре — положить бинарь вручную):
```bash
# бинарь nelm → /usr/local/bin/nelm или ~/bin/nelm
export PATH="$HOME/bin:/usr/local/bin:$PATH"
nelm version
```
### 2.2. Сервисный аккаунт для деплоя
С админского kubeconfig (на master или с ноутбука с доступом):
```bash
export KUBECONFIG=/path/to/admin.kubeconfig
chmod +x create-deploy-sa.sh
./create-deploy-sa.sh
# получите kube-deploy.config
```
Скрипт создаёт SA + `ClusterAuthorizationRule` (Deckhouse) и пишет kubeconfig.
Дальше:
1. Скопируйте `kube-deploy.config` на Jenkins agent (например `/var/lib/jenkins/kube-deploy.config`, права только для пользователя agent).
2. Заведите **отдельный context на каждый контур**, если это разные кластеры:
```bash
kubectl --kubeconfig=kube-deploy.config config rename-context <old> lab-cluster-dev
# аналогично lab-cluster-preprod, lab-cluster-prod
# либо три разных kubeconfig + переключение в job
```
В job по умолчанию используется context `lab-cluster-${ENV}`.
### 2.3. Ключ секретов nelm
Один общий ключ на контур (или на все контуры, если один vault-подход):
```bash
nelm chart secret key create > /secure/nelm_secret_key
chmod 600 /secure/nelm_secret_key
```
Все `secret-values.yaml` в репозиториях должны быть зашифрованы **этим** ключом.
В Jenkins: credential типа **Secret text**, id = `nelm-secret-key`.
### 2.4. Доступ Jenkins → Bitbucket
1. В Bitbucket создайте technical user / HTTP access token с правом **read** на project со сервисами.
2. В Jenkins: credential **Username with password**, id = `bitbucket-git` (user + token).
3. Убедитесь, что agent резолвит URL Bitbucket (DNS / `/etc/hosts`) и доверяет TLS (корпоративный CA в trust store или внутренний HTTP).
### 2.5. Registry и pull secrets в кластере
В каждом namespace (`dev` / `preprod` / `prod`) или на уровне SA чарта нужен `imagePullSecrets` (часто `registrysecret`), совпадающий с `.Values.imagePullSecrets` в чартах.
Образы в `values.yaml` репозиториев должны указывать на **клиентский** registry, не на lab `192.168.10.68:5000` и не на docker.io.
### 2.6. StorageClass / ноды
В чартах по умолчанию часто `localpath` и `nodeSelector: node-role.kubernetes.io/worker: ""`.
Приведите values под клиентский StorageClass и лейблы нод **до** первого деплоя (или через `--set` в job, если так принято).
## 3. Репозитории в Bitbucket
Минимальный состав project (имена можно сохранить):
| Repo | Содержимое |
|------|------------|
| `postgres`, `seaweedfs`, `ollama`, `keycloak`, `vllm` | код + `Dockerfile` + каталог `.helm/` |
| опционально `nelm-ci` | `Jenkinsfile`, `jenkins/*.groovy`, `jenkins/checkout-repos.sh` |
В каждом сервисном репо обязательно:
```text
.helm/Chart.yaml
.helm/values.yaml # образы внутреннего registry
.helm/secret-values.yaml # зашифрованные секреты (если есть)
.helm/templates/...
Dockerfile # если нужен build
```
`values-public.yaml` — только для стендов с доступом в интернет; **в закрытом контуре не использовать**.
## 4. Job в Jenkins
### Вариант A (предпочтительно): Pipeline from SCM
1. Создайте repo `nelm-ci` с `Jenkinsfile` и каталогом `jenkins/`.
2. New Item → Pipeline → Pipeline script from SCM → Bitbucket Git → этот repo.
3. Credentials: `bitbucket-git`, `nelm-secret-key`.
4. Параметры job (уже в `Jenkinsfile`):
| Параметр | Смысл |
|----------|--------|
| `SERVICE` | один сервис или `all` |
| `ENV` | `dev` / `preprod` / `prod` (= namespace) |
| `ACTION` | `build` / `deploy` / `build-and-deploy` |
| `USE_PUBLIC_IMAGES` | **false** у клиента |
| `REGISTRY` | адрес внутреннего registry |
| `GIT_BASE_URL` | `https://bitbucket.example.com/scm/PROJ` или `.../bbadmin` |
| `GIT_BRANCH` | ветка |
| `KUBE_CONTEXT` | пусто = `lab-cluster-$ENV` |
Логика:
1. **Checkout from Bitbucket**`jenkins/checkout-repos.sh` клонирует актуальные сервисы в `$WORKSPACE/repos/<service>`.
2. **Build**`docker build` / mirror → `REGISTRY/...`, `docker push`.
3. **Deploy**`nelm release install -n $ENV -r ${service}-${ENV}` из `$WORKSPACE/repos/<service>/.helm`.
### Вариант B: Freestyle (как на lab)
Shell build step вызывает тот же `checkout-repos.sh`, затем `nelm`/`docker`.
Скрипты CI лежат на агенте или тоже подтягиваются из `nelm-ci` одним `git clone`.
## 5. Переменные окружения на agent / в job
```bash
export PATH="/usr/local/bin:$HOME/bin:$PATH"
export KUBECONFIG=/var/lib/jenkins/kube-deploy.config
export NELM_SECRET_KEY=... # из Jenkins credentials
export GIT_BASE_URL=https://bitbucket.client.local/bbadmin
export GIT_BRANCH=main
```
## 6. Первый прогон (чеклист)
1. В Bitbucket открывается repo, на `main` есть `.helm/`.
2. С agent: `git ls-remote $GIT_BASE_URL/postgres.git`.
3. `nelm chart render -n dev --set env=dev --set global.cluster=dev path/to/.helm` — без ошибок, секреты расшифровываются.
4. В нужном namespace есть `registrysecret` (если образы приватные).
5. Jenkins → Build with Parameters → `SERVICE=postgres`, `ENV=dev`, `ACTION=build-and-deploy`, `USE_PUBLIC_IMAGES=false`.
6. `kubectl -n dev get pods` — Ready; `nelm release list -n dev` — статус deployed.
## 7. Три контура
- Namespace = имя env (`dev` / `preprod` / `prod`), либо своя политика — тогда поправьте job.
- Разные кластеры → разные kube-context / kubeconfig.
- Разные значения (replicas, resources, storage) — через overlays в `values.yaml` (`dig .Values.env ...`), не через копипасту манифестов.
## 8. Чего не делать
- Не деплоить из «замороженной» папки на диске агента без `git fetch`.
- Не коммитить `NELM_SECRET_KEY` и сырые пароли в Bitbucket.
- Не оставлять в `values.yaml` адреса lab-registry / docker.io для боевого контура.
- Не смешивать werf converge и nelm на одном релизе без миграции ownership аннотаций Helm.
## 9. Файлы из поставки lab (ориентир)
| Файл | Назначение |
|------|------------|
| `create-deploy-sa.sh` | SA + kubeconfig для runner |
| `Jenkinsfile` | pipeline с checkout → build → deploy |
| `jenkins/checkout-repos.sh` | актуальный clone сервисов |
| `jenkins/build.groovy` / `deploy.groovy` | стадии |
| `DEPLOY.md` | краткий lab-ориентир |
| `NELM-CLOSED-LOOP.ru.md` | правки репозиториев под закрытый контур |

73
DEPLOY.md Normal file
View File

@ -0,0 +1,73 @@
# Deploy guide (nelm + Jenkins, no werf)
Charts live under each Bitbucket repos `.helm/`. Jenkins **clones those repos on every job** — it does not deploy from a hand-copied folder on disk.
## Lab URLs
| What | URL / host | Creds |
|------|------------|-------|
| Bitbucket UI (Gitea) | https://bitbucket.valdemorte.online | `bbadmin` / `admin123` |
| Jenkins | https://jenkins.valdemorte.online | `admin` / `admin123` |
| Compose | `ubuntu@192.168.10.68:~/ci-stack` | — |
Repos: `bbadmin/{postgres,seaweedfs,ollama,keycloak,vllm}`. Job: **`nelm-deploy`**. Contexts: `lab-cluster-{dev,preprod,prod}`.
## How Jenkins gets the code
1. Parameters include `GIT_BASE_URL` (default `http://192.168.10.68:7990/bbadmin`) and `GIT_BRANCH` (`main`).
2. Job runs `jenkins/checkout-repos.sh` → fresh clone into `$WORKSPACE/repos/<service>`.
3. Build/deploy use **only** that checkout (`.helm/`, `Dockerfile`).
Agent still holds non-git secrets/tools only:
- `~/nelm-work/.nelm_secret_key`
- `~/nelm-work/.git_user` + `.git_token`
- `~/kube-deploy.config`
- `~/nelm-work/jenkins/checkout-repos.sh` (CI helper; prefer keeping the same scripts in a `nelm-ci` Bitbucket repo in production)
See **`CLIENT-SETUP.ru.md`** for client rollout and **`NELM-CLOSED-LOOP.ru.md`** for closed-loop chart/Dockerfile changes.
## Tools on the agent
```bash
export NELM_SECRET_KEY=$(cat ~/nelm-work/.nelm_secret_key)
export PATH="$HOME/bin:$PATH"
export KUBECONFIG=$HOME/kube-deploy.config
```
## Images
- Default `values.yaml` → internal registry (`192.168.10.68:5000/...` in lab).
- Lab smoke test: job flag `USE_PUBLIC_IMAGES=true``values-public.yaml`.
- Client closed loop: `USE_PUBLIC_IMAGES=false`.
## Deploy SA
```bash
./create-deploy-sa.sh
```
## Manual deploy (after git clone)
```bash
git clone http://192.168.10.68:7990/bbadmin/postgres.git
export NELM_SECRET_KEY=$(cat .nelm_secret_key)
nelm release install -n dev -r postgres-dev \
--set env=dev --set global.cluster=dev \
--values postgres/.helm/values-public.yaml \
postgres/.helm
```
## Jenkins parameters
| Parameter | Meaning |
|-----------|---------|
| `SERVICE` | postgres … vllm / all |
| `ENV` | dev / preprod / prod |
| `ACTION` | build / deploy / build-and-deploy |
| `USE_PUBLIC_IMAGES` | lab overlay |
| `GIT_BASE_URL` | Bitbucket project base |
| `GIT_BRANCH` | branch to deploy |
| `REGISTRY` | push target for build |
Pipeline sources: root `Jenkinsfile` + `jenkins/*.groovy` + `jenkins/checkout-repos.sh`.

83
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,83 @@
pipeline {
agent any
parameters {
choice(name: 'SERVICE', choices: ['postgres', 'seaweedfs', 'ollama', 'keycloak', 'vllm', 'all'], description: 'Service to build/deploy')
choice(name: 'ENV', choices: ['dev', 'preprod', 'prod'], description: 'Target environment / namespace')
choice(name: 'ACTION', choices: ['build', 'deploy', 'build-and-deploy'], description: 'Pipeline action')
booleanParam(name: 'USE_PUBLIC_IMAGES', defaultValue: false, description: 'Use values-public.yaml (only for lab smoke tests; false in closed loop)')
string(name: 'REGISTRY', defaultValue: '192.168.10.68:5000', description: 'Internal registry for push/tag')
string(name: 'KUBE_CONTEXT', defaultValue: '', description: 'Optional override; default lab-cluster-<ENV>')
string(name: 'GIT_BASE_URL', defaultValue: 'http://192.168.10.68:7990/bbadmin', description: 'Bitbucket project base URL without trailing slash')
string(name: 'GIT_BRANCH', defaultValue: 'main', description: 'Branch to build/deploy')
}
environment {
PATH = "/home/ubuntu/bin:${env.HOME}/bin:/usr/local/bin:${env.PATH}"
KUBECONFIG = "/home/ubuntu/kube-deploy.config"
NELM_SECRET_KEY = credentials('nelm-secret-key')
BITBUCKET_GIT = credentials('bitbucket-git')
}
options {
timestamps()
disableConcurrentBuilds()
}
stages {
stage('Checkout from Bitbucket') {
steps {
script {
def services = params.SERVICE == 'all' ? ['postgres', 'seaweedfs', 'ollama', 'keycloak', 'vllm'] : [params.SERVICE]
env.SERVICES = services.join(' ')
env.REPOS_DIR = "${env.WORKSPACE}/repos"
withEnv([
"GIT_BASE_URL=${params.GIT_BASE_URL}",
"GIT_BRANCH=${params.GIT_BRANCH}",
"SERVICES=${env.SERVICES}",
"DEST=${env.REPOS_DIR}",
"GIT_USER=${env.BITBUCKET_GIT_USR}",
"GIT_TOKEN=${env.BITBUCKET_GIT_PSW}",
"GIT_SSL_NO_VERIFY=1"
]) {
sh 'bash jenkins/checkout-repos.sh'
}
}
}
}
stage('Build') {
when {
expression { params.ACTION == 'build' || params.ACTION == 'build-and-deploy' }
}
steps {
script {
load "${env.WORKSPACE}/jenkins/build.groovy"
env.SERVICES.split(' ').each { svc ->
buildService(svc, params.REGISTRY, params.ENV, env.REPOS_DIR)
}
}
}
}
stage('Deploy') {
when {
expression { params.ACTION == 'deploy' || params.ACTION == 'build-and-deploy' }
}
steps {
script {
load "${env.WORKSPACE}/jenkins/deploy.groovy"
env.SERVICES.split(' ').each { svc ->
deployService(svc, params.ENV, params.USE_PUBLIC_IMAGES, params.KUBE_CONTEXT, env.REPOS_DIR)
}
}
}
}
}
post {
always {
echo "Done: SERVICE=${params.SERVICE} ENV=${params.ENV} ACTION=${params.ACTION} BRANCH=${params.GIT_BRANCH}"
}
}
}

198
NELM-CLOSED-LOOP.ru.md Normal file
View File

@ -0,0 +1,198 @@
# Особенности билда/деплоя через nelm и правки репозиториев `bitbucket/` под закрытый контур
## 1. Чем nelm отличается от привычного werf/helm в CI
| Тема | Как было (werf / common-ci) | Как делаем (nelm + Jenkins) |
|------|-----------------------------|-----------------------------|
| Источник манифестов | `werf.yaml` + `.helm`, converge | только chart в `.helm/`, `nelm release install` |
| Секреты | werf secret / тот же формат ключа | `secret-values.yaml` + `NELM_SECRET_KEY` (`nelm chart secret …`) |
| Сборка образа | часто встроенный werf build | отдельный этап: `docker build` + `docker push` в Jenkins |
| Откуда код | GitLab CI clone | Jenkins сам клонирует Bitbucket на каждый job |
| Релиз | имя/лейблы werf | release name = `<service>-<env>`, namespace = env |
| Три контура | разные values / stages | `--set env=… --set global.cluster=…`, overlays через `dig` |
Практические следствия:
1. **`werf.yaml` в закрытом контуре не нужен для деплоя.** Его можно оставить для совместимости, но pipeline на него не опирается. Деплой идёт из `.helm/`.
2. Ресурсы в кластере получают Helm-аннотации `meta.helm.sh/release-name` / `release-namespace`. Если объект уже создан руками или другим релизом — `nelm` откажется без `--force-adoption` (лучше удалить чужой объект или один раз осознанно adopt).
3. Расшифровка секретов происходит **на agent в момент install**. Без `NELM_SECRET_KEY` job упадёт или зальёт ciphertext в Secret (если включить `--no-decrypt-secrets` — так делать нельзя).
4. `nelm` ждёт Ready у workloads в пределах `--timeout`. В закрытом контуре таймауты чаще связаны с **ImagePullBackOff** (нет образа / нет pull secret), а не с самим nelm.
Типовые команды:
```bash
export NELM_SECRET_KEY=$(cat /secure/nelm_secret_key)
# проверка шаблонов без кластера
nelm chart render -n dev --set env=dev --set global.cluster=dev .helm
# деплой
nelm release install -n dev -r postgres-dev \
--set env=dev --set global.cluster=dev \
--timeout 5m \
.helm
nelm release list -n dev
nelm release history -n dev -r postgres-dev
```
Шифрование значений:
```bash
# показать plaintext (только на защищённой машине)
nelm chart secret values-file decrypt .helm/secret-values.yaml
# зашифровать новый файл тем же ключом
nelm chart secret values-file encrypt plaintext-values.yaml > .helm/secret-values.yaml
```
## 2. Что поправить в репозиториях из папки `bitbucket/`
Ниже — конкретный чеклист по текущим сервисам (`postgres`, `seaweedfs`, `ollama`, `keycloak`, `vllm`).
### 2.1. Образы: только внутренний registry
Сейчас в `values.yaml` часто:
```yaml
image:
postgresql: 192.168.10.68:5000/library/postgres:16-alpine
```
У клиента заменить host на **их** registry, например:
```yaml
image:
postgresql: registry.client.local/library/postgres:16-alpine
```
То же для всех ключей `image.*` во всех пяти чартах.
`values-public.yaml` (ghcr/docker.io/quay) — **не подключать** в job (`USE_PUBLIC_IMAGES=false`). Файл можно оставить в repo как reference для открытых стендов или удалить, чтобы никто случайно не выкатил публичные теги.
### 2.2. Dockerfile `FROM` — без интернета
Сейчас:
```dockerfile
FROM docker.io/library/postgres:16-alpine
FROM quay.io/keycloak/keycloak:26.4.0
```
В закрытом контуре:
```dockerfile
FROM registry.client.local/library/postgres:16-alpine
FROM registry.client.local/keycloak/keycloak:26.4.0
```
Либо на этапе build в Jenkins делать `docker pull` уже с зеркала (если base заранее завезли), а в Dockerfile всё равно писать внутренний путь — иначе `docker build` на agent без интернета упадёт.
Заранее завезти в registry: postgres, postgres-exporter, seaweedfs, ollama, keycloak, vllm (и зависимости keycloak plugins, если тянутся с сети на build).
### 2.3. `imagePullSecrets`
В values:
```yaml
imagePullSecrets:
- name: registrysecret
```
В каждом namespace деплоя должен существовать Secret этого имени (тип `kubernetes.io/dockerconfigjson`) **до** первого install, либо чарт должен его создавать из зашифрованных данных (сейчас обычно ожидается снаружи).
Иначе: `ErrImagePull` / `ImagePullBackOff`.
### 2.4. Секреты приложения
Файлы:
- `postgres/.helm/secret-values.yaml` — пароль БД
- `keycloak/.helm/secret-values.yaml` — admin / DB
- `seaweedfs/.helm/secret-values.yaml` — S3 keys
Действия у клиента:
1. Сгенерировать **свой** `NELM_SECRET_KEY` (не lab-ключ).
2. Перешифровать все `secret-values.yaml` новым ключом (`nelm chart secret key rotate` или encrypt заново).
3. Положить ключ только в Jenkins credentials / vault агента.
4. Заменить lab-пароли (`admin123`) на боевые **до** шифрования.
Репозитории без secret-values (`ollama`, частично `vllm`) — проверить, нет ли plaintext секретов в обычном `values.yaml` / TLS файлах.
### 2.5. TLS и вложенные секреты в git (`vllm`)
В `vllm/.helm/secret/{dev,preprod,prod}/` лежат `tls.crt` / `tls.key`.
Для клиента:
- либо заменить на клиентские сертификаты;
- либо убрать из git и монтировать из внешнего Secret / cert-manager;
- не коммитить боевые private key в публичные зеркала.
### 2.6. StorageClass и scheduling
В чартах lab:
- `storageClassName: localpath` (или аналог)
- `nodeSelector: node-role.kubernetes.io/worker: ""`
У клиента:
- имя StorageClass из их кластера (`ceph-rbd`, `nfs-client`, …);
- nodeSelector/tolerations под GPU-ноды для `ollama` / `vllm` (`gpu.enabled` уже есть флагом — выставить true и нужные taints только если ноды готовы);
- ресурсы (CPU/memory) под квоты namespace.
### 2.7. Ingress / DNS
У `keycloak`, `ollama`, `vllm` есть Ingress. Поправить:
- hosts под клиентскую зону;
- TLS (cert-manager annotations или уже существующие секреты);
- класс Ingress (`ingressClassName`), если не default.
В закрытом контуре без внешнего DNS — либо внутренние имена, либо временно ClusterIP + port-forward для приёмки.
### 2.8. Убрать/игнорировать зависимости от GitLab CI и werf
В репозиториях могут остаться:
- `werf.yaml`
- `.gitlab-ci.yml` / include на `common-ci`
Для клиентского Bitbucket+Jenkins:
- pipeline из Bitbucket **не** должен вызывать `werf converge`;
- достаточно `.helm/` + Dockerfile;
- CI logic — в Jenkins (`Jenkinsfile` / freestyle), см. `CLIENT-SETUP.ru.md`.
### 2.9. Имена release и namespace
Соглашение lab/клиента:
- namespace = `dev` | `preprod` | `prod`
- release = `<chartName>-<env>` (например `postgres-dev`)
Шаблоны используют `.Values.env` для overlays. Не хардкодить namespace в templates.
### 2.10. Экспортёры и sidecar-образы
У postgres отдельный образ `postgresqlExporter`. Его тоже нужно завезти во внутренний registry и прописать в `values.yaml`.
Нельзя подставлять образ postgres как exporter (так ломается readiness).
## 3. Рекомендуемый порядок миграции репо под клиента
1. Завести project в Bitbucket, запушить пять сервисов.
2. Заменить registry host + Dockerfile `FROM`.
3. Создать `registrysecret` в `dev`.
4. Сгенерировать клиентский `NELM_SECRET_KEY`, перешифровать `secret-values.yaml`, обновить пароли.
5. Поправить StorageClass / Ingress / nodeSelector.
6. Прогнать `nelm chart render` локально.
7. Подключить Jenkins job с **checkout из Bitbucket**, `USE_PUBLIC_IMAGES=false`.
8. `build-and-deploy` для `postgres` на `dev`, затем остальные сервисы.
## 4. Критерий «готово для закрытого контура»
- Job клонирует **актуальный** commit из Bitbucket (в логе виден `HEAD=…` после checkout).
- `docker build` не ходит в интернет (все `FROM` и base — внутренние).
- `nelm release install` без `values-public.yaml`.
- Секреты в кластере — расшифрованный plaintext, ключ не в git.
- Поды Running/Ready, образы с клиентского registry.

52
jenkins/build.groovy Normal file
View File

@ -0,0 +1,52 @@
def buildService(String service, String registry, String envName, String reposDir) {
def dirPath = "${reposDir}/${service}"
if (!fileExists(dirPath)) {
error("Service directory not found after checkout: ${dirPath}")
}
def images = [
postgres: [
[ref: 'library/postgres:16-alpine', build: true],
[ref: 'prometheuscommunity/postgres-exporter:v0.19.0', src: 'quay.io/prometheuscommunity/postgres-exporter:v0.19.0']
],
seaweedfs: [
[ref: 'chrislusf/seaweedfs:3.99', build: true]
],
ollama: [
[ref: 'ollama/ollama:0.30.11', build: true]
],
keycloak: [
[ref: 'keycloak/keycloak:26.4.0', build: true],
[ref: 'library/postgres:16-alpine', src: 'docker.io/library/postgres:16-alpine']
],
vllm: [
[ref: 'vllm/vllm-openai:v0.14.0', build: true]
]
]
if (!images.containsKey(service)) {
error("Unknown service: ${service}")
}
echo "Building/mirroring ${service} into ${registry} (env=${envName}) from ${dirPath}"
images[service].each { img ->
def localRef = "${registry}/${img.ref}"
if (img.build) {
sh """
set -euo pipefail
docker build -t '${localRef}' '${dirPath}'
docker push '${localRef}'
"""
} else {
def src = img.src ?: "docker.io/${img.ref}"
sh """
set -euo pipefail
docker pull '${src}'
docker tag '${src}' '${localRef}'
docker push '${localRef}'
"""
}
}
}
return this

47
jenkins/checkout-repos.sh Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -euo pipefail
# Clone/update service repos from Bitbucket (or Gitea) into DEST.
# Required env:
# GIT_BASE_URL e.g. http://192.168.10.68:7990/bbadmin
# SERVICES space-separated list, e.g. "postgres seaweedfs"
# Optional:
# GIT_BRANCH default main
# DEST default $WORKSPACE/repos (or ./repos)
# GIT_USER / GIT_TOKEN if set, injected into clone URL
# GIT_SSL_NO_VERIFY=1 for lab self-signed HTTPS
GIT_BRANCH="${GIT_BRANCH:-main}"
DEST="${DEST:-${WORKSPACE:-.}/repos}"
GIT_BASE_URL="${GIT_BASE_URL:?GIT_BASE_URL is required}"
SERVICES="${SERVICES:?SERVICES is required}"
mkdir -p "$DEST"
auth_base="$GIT_BASE_URL"
if [ -n "${GIT_USER:-}" ] && [ -n "${GIT_TOKEN:-}" ]; then
# inject user:token after scheme://
auth_base="$(echo "$GIT_BASE_URL" | sed -E "s#^(https?://)#\1${GIT_USER}:${GIT_TOKEN}@#")"
fi
export GIT_TERMINAL_PROMPT=0
for svc in $SERVICES; do
url="${auth_base%/}/${svc}.git"
dir="$DEST/$svc"
echo "==> checkout $svc from $GIT_BASE_URL/$svc.git branch=$GIT_BRANCH"
if [ -d "$dir/.git" ]; then
git -C "$dir" remote set-url origin "$url"
git -C "$dir" fetch --depth 1 origin "$GIT_BRANCH"
git -C "$dir" checkout -B "$GIT_BRANCH" "FETCH_HEAD"
git -C "$dir" reset --hard "FETCH_HEAD"
git -C "$dir" clean -fdx
else
rm -rf "$dir"
git clone --depth 1 --branch "$GIT_BRANCH" "$url" "$dir"
fi
test -d "$dir/.helm" || { echo "ERROR: $svc has no .helm chart"; exit 1; }
echo " HEAD=$(git -C "$dir" rev-parse --short HEAD)"
done
echo "CHECKOUT_DIR=$DEST"

36
jenkins/deploy.groovy Normal file
View File

@ -0,0 +1,36 @@
def deployService(String service, String envName, boolean usePublic, String kubeContext, String reposDir) {
def chartDir = "${reposDir}/${service}/.helm"
def release = "${service}-${envName}"
def ns = envName
def ctx = kubeContext?.trim() ? kubeContext.trim() : "lab-cluster-${envName}"
def publicArgs = usePublic ? "--values ${chartDir}/values-public.yaml" : ""
def setArgs = "--set env=${envName} --set global.cluster=${envName}"
echo "Deploy ${service} release=${release} ns=${ns} context=${ctx} public=${usePublic} chart=${chartDir}"
sh """
set -euo pipefail
export NELM_SECRET_KEY="\${NELM_SECRET_KEY}"
export PATH="/home/ubuntu/bin:\${HOME}/bin:/usr/local/bin:\${PATH}"
export KUBECONFIG="\${KUBECONFIG:-/home/ubuntu/kube-deploy.config}"
command -v nelm >/dev/null || { echo 'nelm not found in PATH'; exit 1; }
test -d '${chartDir}' || { echo "chart missing: ${chartDir}"; exit 1; }
kubectl --context='${ctx}' get ns '${ns}' >/dev/null 2>&1 || kubectl --context='${ctx}' create ns '${ns}'
nelm release install \\
-n '${ns}' \\
-r '${release}' \\
--kube-context '${ctx}' \\
${publicArgs} \\
${setArgs} \\
--timeout 5m \\
'${chartDir}'
kubectl --context='${ctx}' -n '${ns}' get pods,svc -o wide
"""
}
return this