# Особенности билда/деплоя через 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 = `-`, 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 = `-` (например `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.