nelm CI: checkout from Bitbucket + docs
This commit is contained in:
commit
4e190d957d
196
CLIENT-SETUP.ru.md
Normal file
196
CLIENT-SETUP.ru.md
Normal 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
73
DEPLOY.md
Normal file
@ -0,0 +1,73 @@
|
||||
# Deploy guide (nelm + Jenkins, no werf)
|
||||
|
||||
Charts live under each Bitbucket repo’s `.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
83
Jenkinsfile
vendored
Normal 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
198
NELM-CLOSED-LOOP.ru.md
Normal 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
52
jenkins/build.groovy
Normal 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
47
jenkins/checkout-repos.sh
Executable 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
36
jenkins/deploy.groovy
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user