21 KiB
План миграции в Nubes Managed Kubernetes — Инструкции для агента
Создан: 2026-03-13 (Opus 4.6)
Исполнитель: Sonnet 4.6
Статус: Ожидает исполнения
ВАЖНО: Этот документ — пошаговый план. Каждый пункт содержит:
- ЧТО делать (цель)
- ГДЕ делать (файлы)
- КАК делать (конкретные инструкции)
- ОГРАНИЧЕНИЯ (что нельзя трогать)
Контекст
Terraform-провайдер Nubes Cloud сейчас работает на self-managed K8s (registry.kube5s.ru). Цель: подготовить инфраструктуру к передаче в Nubes Managed Kubernetes под управление их DevOps.
Nubes (nubes.ru) — российский cloud-провайдер, собственный DC Tier III (Москва), имеет Managed K8s, Harbor, S3.
Репозиторий: /home/naeel/remote_dev/terraform
Обязательно прочитать перед работой:
REPO_CONTENTS.md— карта репозитория.github/copilot-instructions.md— правила работы (IMMUTABILITY POLICY)docs/CODEBASE_ANALYSIS_AND_ROADMAP.md— анализ кодовой базы
Группа A: Подготовительные задачи (делать ПЕРВЫМИ)
A1. Helm Chart для всех K8s-манифестов
Цель: Конвертировать raw YAML манифесты в Helm chart.
Исходные файлы (ТОЛЬКО ЧИТАТЬ, НЕ МЕНЯТЬ):
k8s/registry-deployment-new.yamlk8s/registry-ingress-new.yamloperator/manifests/00-namespace.yamloperator/manifests/00-rbac.yamloperator/manifests/02-crd.yamloperator/manifests/03-build-script.yamloperator/manifests/04-operator-deployment.yamloperator/manifests/05-registry-server.yamloperator/manifests/06-registry-server-docs.yaml
Создать:
charts/
terraform-registry/
Chart.yaml
values.yaml
values-dev.yaml
values-prod.yaml
templates/
_helpers.tpl
namespace.yaml
rbac.yaml
crd.yaml
build-script-configmap.yaml
operator-deployment.yaml
registry-server-deployment.yaml
registry-server-service.yaml
docs-server-deployment.yaml (optional)
ingress.yaml
pdb.yaml
networkpolicy.yaml
Требования к values.yaml:
global:
registryHostname: "registry.kube5s.ru" # Переопределяется при миграции
namespace: "terraform-registry"
registry:
image:
repository: "naeel/terraform-registry-server" # → Harbor при миграции
tag: "latest"
pullPolicy: IfNotPresent
replicas: 1 # → 2 в prod
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
service:
port: 80
targetPort: 8080
healthcheck:
enabled: true
path: /healthz
port: 8080
operator:
image:
repository: "naeel/terraform-registry-operator"
tag: "latest"
replicas: 1
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi
ingress:
enabled: true
className: "nginx"
host: "registry.kube5s.ru" # Переопределяется
tls:
enabled: true
issuer: "letsencrypt-prod" # НЕ ТРОГАТЬ LetsEncrypt issuer!
secretName: "registry-tls"
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
s3:
endpoint: "s3.msk-1.ngcloud.ru"
bucket: "terraform-registry"
useSSL: true
# credentials через existingSecret
existingSecret: "s3-credentials"
accessKeyField: "access-key"
secretKeyField: "secret-key"
pdb:
enabled: false # → true в prod
minAvailable: 1
networkPolicy:
enabled: false # → true при миграции
Требования к values-dev.yaml:
global:
registryHostname: "registry.kube5s.ru"
registry:
replicas: 1
pdb:
enabled: false
Требования к values-prod.yaml:
global:
registryHostname: "registry.nubes.ru" # Целевой домен
registry:
image:
repository: "pearlharbor.registryk8s.services.ngcloud.ru/terraform/registry-server"
replicas: 2
operator:
image:
repository: "pearlharbor.registryk8s.services.ngcloud.ru/terraform/registry-operator"
pdb:
enabled: true
minAvailable: 1
networkPolicy:
enabled: true
Ограничения:
- НЕ менять исходные YAML в
k8s/иoperator/manifests/(они могут ещё использоваться) - НЕ трогать CRD-ресурсы с LetsEncrypt issuerRef
- НЕ запускать
helm install/upgrade— только создать файлы - Helm chart — НОВЫЕ файлы в
charts/(APPEND ONLY)
A2. Externalize hardcoded values
Цель: Убрать все hardcoded пути и домены, заменить на env vars.
Файл 1: internal/core/client.go строка ~18
// СЕЙЧАС:
f, err := os.OpenFile("/home/naeel/terra/debug_nubes.log", ...)
// НОВЫЙ КОД (добавить новую функцию в КОНЕЦ файла):
func debugLogPath() string {
if p := os.Getenv("NUBES_DEBUG_LOG"); p != "" {
return p
}
return filepath.Join(os.TempDir(), "nubes_debug.log")
}
Ограничение: НЕ менять строку 18 напрямую. Добавить функцию debugLogPath() в КОНЕЦ файла. Спросить оператора перед заменой вызова.
Файл 2: internal/provider/provider.go строка ~103
// СЕЙЧАС:
InsecureSkipVerify: true,
// НУЖНО: Сделать конфигурируемым через provider schema + env var
Инструкция:
- Добавить атрибут
insecureв schema провайдера (Optional, bool, default false) - Добавить чтение env var
NUBES_INSECURE - InsecureSkipVerify = config_value || env_value || false
- Код добавлять В КОНЕЦ секции Configure(), не рефакторить существующий
Файл 3: universal_rebuild/internal/provider/provider.go
- Проверить аналогичную проблему с InsecureSkipVerify
- Применить тот же паттерн
Ограничения:
- НЕ менять сигнатуры существующих функций
- Новый код — APPEND ONLY
- InsecureSkipVerify=false по умолчанию (breaking change для текущих юзеров — СПРОСИТЬ оператора)
A3. Health endpoints для registry-server
Цель: Добавить /healthz, /readyz, /metrics endpoints.
Файл: registry-server-build/main.go
Инструкция:
- Прочитать текущий
main.goполностью - Добавить в КОНЕЦ файла (новые handler-функции):
func healthzHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
}
func readyzHandler(w http.ResponseWriter, r *http.Request) {
// Проверить доступность S3
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
}
- Зарегистрировать handlers в main() — СПРОСИТЬ оператора перед добавлением в mux
Ограничения:
- НЕ менять существующие handlers
- НЕ запускать docker build
- Новые функции — APPEND ONLY
A4. Operaционная документация
Цель: Создать набор операционных документов для DevOps Nubes.
Создать файлы:
docs/ops/RUNBOOK.md:
# Runbook: Terraform Provider Registry
## Предпосылки
- Kubernetes cluster ≥ 1.27
- Helm ≥ 3.12
- Доступ к S3 (s3.msk-1.ngcloud.ru)
- Harbor registry (для образов)
## Установка
helm install terraform-registry ./charts/terraform-registry \
-f charts/terraform-registry/values-prod.yaml \
-n terraform-registry --create-namespace
## Обновление версии
1. Собрать новый образ (CI pipeline)
2. Обновить tag в values
3. helm upgrade terraform-registry ./charts/terraform-registry -f values-prod.yaml
## Проверка здоровья
kubectl -n terraform-registry get pods
curl https://<REGISTRY_HOST>/healthz
curl https://<REGISTRY_HOST>/.well-known/terraform.json
## Компоненты
- Registry Server — HTTP-сервер протокола Terraform Registry
- Operator — K8s controller для сборки provider binaries
- S3 — хранилище артефактов (бинарники + документация)
docs/ops/TROUBLESHOOTING.md:
# Troubleshooting
## Registry Server не отвечает
1. kubectl -n terraform-registry get pods -l app=registry-server
2. kubectl -n terraform-registry logs -l app=registry-server --tail=100
3. Проверить ingress: kubectl get ingress -n terraform-registry
4. Проверить S3: curl -s https://s3.msk-1.ngcloud.ru (bucket access)
## Provider binary не скачивается
1. Проверить наличие в S3: s3cmd ls s3://terraform-registry/terraform-providers/...
2. Проверить SHA256SUMS сигнатуру
3. Проверить GPG ключ
## Operator не создаёт build job
1. kubectl -n terraform-registry get terraformproviderrelease
2. kubectl -n terraform-registry describe terraformproviderrelease <name>
3. kubectl -n terraform-registry get jobs
4. Проверить RBAC: operator ServiceAccount должен иметь права на jobs и secrets
## TLS / Certificate проблемы
- Проверить cert-manager: kubectl get certificates -n terraform-registry
- ⚠️ НЕ пересоздавать certificates с LetsEncrypt issuer (rate limits!)
- Для отладки использовать self-signed issuer
docs/ops/MONITORING.md:
# Мониторинг
## Ключевые метрики
- registry_http_requests_total — кол-во запросов к registry
- registry_http_request_duration_seconds — latency
- registry_s3_operations_total — операции с S3
- registry_s3_errors_total — ошибки S3
## Алерты (Prometheus)
- RegistryDown: up == 0 (>2 min)
- RegistryHighLatency: p99 > 5s (>5 min)
- RegistryS3Errors: rate > 0.1/s (>5 min)
- RegistryPodRestart: увеличение restart count
## Grafana Dashboard
- Import dashboard ID: (создать при установке мониторинга)
docs/ops/UPGRADE.md:
# Процедура обновления
## Provider version update (без downtime)
1. CI собирает новый provider binary
2. Создать TerraformProviderRelease CR с новой версией
3. Operator создаёт build job → артефакты в S3
4. Старые версии остаются доступны (immutable artifacts)
## Registry Server update (rolling)
1. Обновить image tag в Helm values
2. helm upgrade --set registry.image.tag=<new> terraform-registry ./charts/...
3. Проверить: kubectl rollout status deployment/registry-server -n terraform-registry
4. Rollback: helm rollback terraform-registry 1
## Operator update
1. Обновить operator image tag
2. helm upgrade ...
3. Проверить CRD compatibility: kubectl get crd terraformproviderreleases.terra.core.nubes.ru
docs/ops/ROLLBACK.md:
# Процедура отката
## Helm rollback
helm rollback terraform-registry <revision>
helm history terraform-registry -n terraform-registry
## Emergency: Direct image rollback
kubectl -n terraform-registry set image deployment/registry-server \
registry-server=<HARBOR>/terraform/registry-server:<PREV_TAG>
## S3 artifacts (immutable — откат не нужен)
Все версии provider binary хранятся бессрочно.
Удаление только вручную через s3cmd.
Группа B: Инфраструктурная подготовка
B1. Dockerfile оптимизация
Цель: Убедиться что Dockerfile для registry-server и operator готовы к Harbor.
Инструкция:
- Прочитать
registry-server-build/иoperator/build/ - Проверить что Dockerfile использует multi-stage build
- Проверить что нет hardcoded путей
- Убедиться что base image — official (golang:1.24 + alpine/scratch)
- НЕ запускать docker build — только проверить файлы
Создать (если отсутствует): registry-server-build/.dockerignore, operator/.dockerignore
B2. CI Pipeline definition
Цель: Создать файл CI pipeline (GitLab CI / Tekton) для автосборки.
Создать: devops/ci/pipeline.yaml
# GitLab CI - пример (адаптировать под конкретный CI Nubes)
stages:
- test
- build
- sign
- publish
variables:
HARBOR_HOST: "pearlharbor.registryk8s.services.ngcloud.ru"
S3_BUCKET: "terraform-registry"
PROVIDER_NAME: "nubes"
PROVIDER_NAMESPACE: "nubes"
test:
stage: test
image: golang:1.24
script:
- cd universal_rebuild
- go test ./...
- go vet ./...
build-provider:
stage: build
image: golang:1.24
script:
- cd universal_rebuild
- GOOS=linux GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_linux_amd64
- GOOS=darwin GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_darwin_amd64
- GOOS=windows GOARCH=amd64 go build -o bin/terraform-provider-${PROVIDER_NAME}_windows_amd64.exe
artifacts:
paths: [universal_rebuild/bin/]
build-images:
stage: build
script:
- docker build -t ${HARBOR_HOST}/terraform/registry-server:${CI_COMMIT_TAG} registry-server-build/
- docker build -t ${HARBOR_HOST}/terraform/registry-operator:${CI_COMMIT_TAG} operator/
- docker push ${HARBOR_HOST}/terraform/registry-server:${CI_COMMIT_TAG}
- docker push ${HARBOR_HOST}/terraform/registry-operator:${CI_COMMIT_TAG}
sign:
stage: sign
script:
- cd universal_rebuild/bin
- sha256sum terraform-provider-* > SHA256SUMS
- gpg --import $GPG_PRIVATE_KEY
- gpg --detach-sign SHA256SUMS
publish-to-s3:
stage: publish
script:
- s3cmd put bin/* s3://${S3_BUCKET}/terraform-providers/...
B3. NetworkPolicy template
Цель: Подготовить NetworkPolicy для изоляции namespace.
Включить в Helm chart: charts/terraform-registry/templates/networkpolicy.yaml
{{- if .Values.networkPolicy.enabled }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {{ include "terraform-registry.fullname" . }}-netpol
namespace: {{ .Values.global.namespace }}
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- port: {{ .Values.registry.service.targetPort }}
protocol: TCP
egress:
- to: []
ports:
- port: 443 # S3, Vault
protocol: TCP
- port: 53 # DNS
protocol: UDP
- port: 53
protocol: TCP
{{- end }}
Группа C: Domain Migration Plan
C1. Domain migration (порядок действий)
Это НЕ код — это инструкция для DevOps. Записать в docs/ops/DOMAIN_MIGRATION.md:
# Миграция домена registry.kube5s.ru → registry.nubes.ru
## Фаза 1: Dual-domain (параллельная работа)
1. Настроить Ingress с двумя hosts: registry.kube5s.ru + registry.nubes.ru
2. Оба домена указывают на один Registry Server
3. Обновить provider main.go: Address → registry.nubes.ru
4. Старый адрес registry.kube5s.ru продолжает работать
## Фаза 2: Миграция клиентов
1. Документировать новый registry address для пользователей
2. .terraformrc mirror config для переходного периода:
provider_installation {
direct {
exclude = ["registry.kube5s.ru/*/*"]
}
network_mirror {
url = "https://registry.nubes.ru/v1/providers/"
}
}
## Фаза 3: Редирект
1. registry.kube5s.ru Ingress → 301 redirect на registry.nubes.ru
2. Мониторинг: отслеживать запросы на старый домен
## Фаза 4: Деком (через 6+ месяцев)
1. Убрать registry.kube5s.ru из Ingress
2. DNS → удалить A/CNAME запись
Группа D: Code Quality (из CODEBASE_ANALYSIS_AND_ROADMAP.md)
D1. Deprecated-маркеры для legacy CreateGenericInstance
Файл: internal/core/client.go
Инструкция: Добавить КОММЕНТАРИИ (не код) перед каждым методом V1-V5:
// Deprecated: Use CreateGenericInstanceUniversalV5 instead.
// This method is kept for backward compatibility and will be removed in v3.0.
func (c *UniversalClient) CreateGenericInstance(...) ...
Методы для пометки:
CreateGenericInstance(V1)CreateGenericInstanceUniversal(V2)CreateGenericInstanceUniversalV2(V3)CreateGenericInstanceUniversalV3(V4)CreateGenericInstanceUniversalV4(V5)
Ограничения: ТОЛЬКО комментарии. НЕ менять код методов. НЕ удалять.
D2. .gitignore для secrets
Файл: .gitignore (корень репозитория)
Добавить в КОНЕЦ файла:
# Secrets (should be in Vault, not in git)
secrets/*.asc
secrets/*.token
secrets/*.key
!secrets/.gitkeep
Создать: secrets/.gitkeep (пустой файл, чтобы директория осталась в git)
D3. Unit tests для core layer
Цель: Создать минимальный набор тестов.
Создать файлы:
universal_rebuild/internal/core/client_test.gouniversal_rebuild/internal/resources_core/crud_test.go
Минимальные тесты для client_test.go:
TestNormalizeValue_EmptyStringTestNormalizeValue_NullStringTestNormalizeValue_MapTypeTestNormalizeValue_ArrayTypeTestNormalizeValue_TrimSpace
Минимальные тесты для crud_test.go:
TestIsStatusSuspendedTestIsStatusNonAdoptableTestDeleteBehaviorDefault
Ограничения:
- Тесты — НОВЫЕ файлы (не менять существующие)
- Использовать стандартный
testingпакет Go - НЕ запускать тесты (
go test) без разрешения оператора
Порядок выполнения
ФАЗА 0 (быстрые wins):
D1 → Deprecated комментарии [5 мин]
D2 → .gitignore для secrets [2 мин]
A2 → debugLogPath() function [10 мин]
ФАЗА 1 (Helm chart):
A1 → Полный Helm chart [30-60 мин]
ФАЗА 2 (Ops docs):
A4 → RUNBOOK, TROUBLESHOOTING и др. [20 мин]
C1 → DOMAIN_MIGRATION.md [10 мин]
ФАЗА 3 (Code quality):
D3 → Unit tests [30 мин]
A3 → Health endpoints [15 мин]
ФАЗА 4 (CI/CD):
B2 → CI pipeline definition [15 мин]
B1 → Dockerfile audit [10 мин]
Правила для агента (напоминание)
- IMMUTABILITY POLICY — НЕ менять существующий рабочий код
- APPEND ONLY — новый код только в конец файла
- Комментарии — ЭТО НЕ ПРАВКА КОДА, их можно и нужно добавлять
- НЕ запускать docker build, kubectl apply, helm install
- НЕ трогать ресурсы с LetsEncrypt issuerRef
- Спрашивать разрешения перед изменением сигнатур функций
- Secrets в коде — КАТЕГОРИЧЕСКИ нет
- Перед любой работой — прочитать
REPO_CONTENTS.md - Файлы в
internal/resources_gen/— НЕ МЕНЯТЬ ВРУЧНУЮ (только через генератор) - Минимальные ресурсы — при создании K8s ресурсов ставить минимальный CPU/memory