Files
sless/doc/decisions/build-deploy-pipeline.md
Naeel 7f7aa44e59 chore: удаление устаревших examples, новый doc, правки funcs-service
- examples/: удалены старые директории (TNAR, demo-event-log, demo-managed-functions,
  hello-go, hello-node, notes-python, pg-list-python, simple-node, simple-python)
- examples/README.md: обновлён (только POSTGRES остался)
- doc/decisions/build-deploy-pipeline.md: новый документ по пайплайну сборки/деплоя
- services/funcs/main.go: правки из текущей сессии
2026-03-21 08:46:30 +03:00

360 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Build & Deploy Pipeline — Terraform Provider + Operator
Дата: 2026-03-20
---
## Контекст
Этот документ описывает **полный цикл** внесения изменений в систему:
от правки Go-кода до работающего `terraform apply` в продакшен-примере.
Охватывает:
- как изменять/добавлять ресурсы в terraform-провайдере
- как собирать и публиковать провайдер
- как собирать Docker-образ оператора
- как деплоить оператор в кластер
- как всё связано
---
## 1. Архитектура компонентов
```
┌──────────────────────────────────────────────────────┐
│ Пользователь пишет Terraform код (functions.tf) │
│ resource "sless_job" {...} / "sless_service" {...} │
└───────────────────┬──────────────────────────────────┘
│ terraform apply
┌──────────────────────────────────────────────────────┐
│ Terraform Provider (terraform-provider-sless) │
│ Go бинарник в /tmp/sless-provider-dev/ │
│ Исходники: sless/terraform/provider/ │
│ Публикуется на: terra.k8c.ru/naeel/sless │
└───────────────────┬──────────────────────────────────┘
│ HTTP REST API
┌──────────────────────────────────────────────────────┐
│ sless-operator (Kubernetes Deployment) │
│ Namespace: sless │
│ Image: pearlharbor.registryk8s.services.ngcloud.ru/ │
│ naeel/sless-operator:v0.1.xx │
│ Исходники: sless/ (корень репо) │
│ Управляет CRD: sless_function, sless_job, │
│ sless_service, sless_trigger │
└──────────────────────────────────────────────────────┘
```
---
## 2. Где что хранится
| Компонент | Путь на удалённой машине | Путь на редактирование (sshfs) |
|-----------|--------------------------|-------------------------------|
| Оператор (Go) | `/home/naeel/terra/sless/` | `/home/naeel/remote_dev/sless/` |
| Terraform provider | `/home/naeel/terra/sless/terraform/provider/` | `/home/naeel/remote_dev/sless/terraform/provider/` |
| Пример POSTGRES | `/home/naeel/terra/sless/examples/POSTGRES/` | `/home/naeel/remote_dev/sless/examples/POSTGRES/` |
| Dev override provider | `/tmp/sless-provider-dev/` | только на удалённой машине |
**Правило:** файлы редактируются через sshfs (`/home/naeel/remote_dev/`),
команды выполняются только на удалённой машине `5.172.178.213` через SSH.
---
## 3. Как изменить ресурс в terraform-провайдере
### 3.1 Структура провайдера
```
terraform/provider/
├── main.go # точка входа, регистрация провайдера
├── go.mod
├── internal/
│ ├── client/
│ │ └── client.go # REST-клиент к sless API (типы запросов/ответов)
│ ├── resources/
│ │ ├── job_resource.go # ресурс sless_job
│ │ ├── service_resource.go # ресурс sless_service
│ │ ├── function_resource.go # ресурс sless_function
│ │ └── trigger_resource.go # ресурс sless_trigger
│ └── provider/
│ └── provider.go # конфигурация провайдера (endpoint, token)
└── hack/
└── build-and-publish.sh # скрипт сборки + публикации в S3
```
### 3.2 Добавить новый атрибут к существующему ресурсу
Пример: добавить `timeout_sec` к `sless_job`.
**Шаг 1 — client.go**: добавить поле в `JobRequest` и `JobResponse`:
```go
type JobRequest struct {
// ... существующие поля
TimeoutSec int32 `json:"timeout_sec,omitempty"`
}
```
**Шаг 2 — job_resource.go**: добавить поле в `JobModel`:
```go
type JobModel struct {
// ... существующие поля
TimeoutSec types.Int64 `tfsdk:"timeout_sec"`
}
```
**Шаг 3 — job_resource.go**: добавить в `Schema()`:
```go
"timeout_sec": schema.Int64Attribute{
Optional: true,
Computed: true,
Default: int64default.StaticInt64(30),
MarkdownDescription: "Таймаут выполнения в секундах.",
},
```
**Шаг 4 — job_resource.go**: использовать в `Create()` при формировании запроса:
```go
req := client.JobRequest{
// ...
TimeoutSec: int32(plan.TimeoutSec.ValueInt64()),
}
```
### 3.3 Создать новый ресурс
1. Создать файл `terraform/provider/internal/resources/myresource_resource.go`
2. Реализовать интерфейс `resource.Resource` (минимум: `Metadata`, `Schema`, `Create`, `Read`, `Update`, `Delete`)
3. Зарегистрировать в `provider/provider.go`:
```go
func (p *SlessProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
resources.NewJobResource,
resources.NewMyResource, // добавить сюда
}
}
```
### 3.4 Что НЕ делать
- Не добавлять поле в `Schema()` без добавления его в `Model` struct — паника при `terraform plan`
- Не забыть `tfsdk:"..."` тег — без него поле невидимо
- При `RequiresReplace`: если ресурс immutable по этому полю, добавить `planmodifier.RequiresReplace()`
---
## 4. Как изменить CRD (API типы) в операторе
### 4.1 Файлы
```
api/v1alpha1/
├── job_types.go # FunctionJobSpec / FunctionJobStatus / FunctionJobPhase
├── function_types.go # FunctionSpec / FunctionStatus
├── service_types.go # ServiceSpec / ServiceStatus
└── zz_generated.deepcopy.go # ГЕНЕРИРУЕТСЯ АВТОМАТИЧЕСКИ — не трогать вручную
```
### 4.2 Добавить поле в Spec
1. Добавить в `job_types.go`:
```go
type FunctionJobSpec struct {
// +kubebuilder:validation:Required
NewField string `json:"newField"`
}
```
2. Регенерировать deepcopy:
```bash
ssh -i /home/naeel/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213 \
'cd /home/naeel/terra/sless && bin/controller-gen object paths="./api/..."'
```
3. Регенерировать CRD YAML:
```bash
ssh -i /home/naeel/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213 \
'cd /home/naeel/terra/sless && bin/controller-gen crd paths="./api/..." \
output:crd:artifacts:config=config/crd/bases'
```
4. Применить CRD в кластер:
```bash
ssh -i /home/naeel/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213 \
'kubectl apply -f /home/naeel/terra/sless/config/crd/bases/'
```
**Важно:** CRD нужно обновлять в кластере **до** деплоя оператора, иначе контроллер
не сможет читать/записывать новые поля из etcd.
---
## 5. Как собрать и задеплоить оператор
### 5.1 Полная последовательность
```bash
# Переменные
REGISTRY="pearlharbor.registryk8s.services.ngcloud.ru"
IMAGE="$REGISTRY/naeel/sless-operator"
VERSION="v0.1.42" # следующий тег
# 1. Логин в registry (один раз, credentials сохраняются)
echo "ieNocheiphaipheep1lie5johl7aqu" | docker login $REGISTRY -u admin --password-stdin
# 2. Сборка образа
docker build -t $IMAGE:$VERSION /home/naeel/terra/sless
# 3. Push
docker push $IMAGE:$VERSION
# 4. Обновить тег в operator.yaml (в файле sless/deployments/k8s/operator.yaml)
# Поле: image: pearlharbor.../naeel/sless-operator:v0.1.41 → v0.1.42
# 5. Apply в кластер
kubectl apply -f /home/naeel/terra/sless/deployments/k8s/operator.yaml
# 6. Дождаться rollout
kubectl rollout status deployment/sless-operator -n sless --timeout=180s
```
### 5.2 Что обязательно в operator.yaml
```yaml
spec:
template:
spec:
imagePullSecrets:
- name: sless-registry-auth # Secret должен существовать в namespace sless
containers:
- name: operator
image: pearlharbor.registryk8s.services.ngcloud.ru/naeel/sless-operator:v0.1.42
imagePullPolicy: Always # Always — иначе k8s возьмёт старый кеш
```
**Секрет `sless-registry-auth`** — содержит docker credentials для pearlharbor.
Если его нет: `kubectl create secret docker-registry sless-registry-auth -n sless ...`
### 5.3 Создать Harbor-проект (если нет)
Harbor требует чтобы проект существовал **до** первого push:
```bash
curl -X POST "https://pearlharbor.registryk8s.services.ngcloud.ru/api/v2.0/projects" \
-u admin:ieNocheiphaipheep1lie5johl7aqu \
-H "Content-Type: application/json" \
-d '{"project_name":"naeel","public":false}'
# Ожидаем: 201 Created
```
---
## 6. Как собрать и опубликовать terraform-провайдер
### 6.1 Быстрая версия (dev override — для локального теста)
Собирает бинарник прямо в `/tmp/sless-provider-dev/` — terraform подберёт его автоматически:
```bash
ssh -i /home/naeel/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213 \
'cd /home/naeel/terra/sless/terraform/provider && \
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags "-X main.version=0.1.19" \
-o /tmp/sless-provider-dev/terraform-provider-sless_v0.1.19 . && echo OK'
```
**Важно:** в `/tmp/sless-provider-dev/` должен быть только ОДИН бинарник — удалить старый!
```bash
rm /tmp/sless-provider-dev/terraform-provider-sless_v0.1.18
```
Dev override настроен в `~/.terraformrc`:
```hcl
provider_installation {
dev_overrides {
"terra.k8c.ru/naeel/sless" = "/tmp/sless-provider-dev"
}
direct {}
}
```
### 6.2 Полная публикация в S3-registry (для продакшена)
Скрипт `hack/build-and-publish.sh` собирает бинарники для всех платформ,
вычисляет SHA256, подписывает GPG и загружает в S3:
```bash
ssh -i /home/naeel/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213 \
'cd /home/naeel/terra/sless/terraform/provider && \
S3CFG=/home/naeel/terra/terraform/secrets/.s3cfg_registry \
GPG_KEY_FILE=/home/naeel/terra/sless/secrets/private_key.asc \
bash hack/build-and-publish.sh 0.1.19'
```
После публикации проверить:
```bash
curl -sk https://terra.k8c.ru/v1/providers/naeel/sless/versions
```
### 6.3 Обновить версию в примерах
В `examples/POSTGRES/main.tf` (и других примерах):
```hcl
sless = {
source = "terra.k8c.ru/naeel/sless"
version = "~> 0.1.19" # обновить тут
}
```
Затем `terraform init -upgrade` — скачает новую версию.
---
## 7. Полный цикл: от изменения кода до terraform apply
```
1. Правка кода (локально через sshfs /home/naeel/remote_dev/sless/)
2. git add + git commit + git push
(локально или через SSH)
3. [если изменились api/v1alpha1/*.go]
controller-gen object → zz_generated.deepcopy.go
controller-gen crd → config/crd/bases/*.yaml
kubectl apply -f config/crd/bases/
4. [если изменился operator Go-код]
docker build → docker push → kubectl apply operator.yaml
kubectl rollout status deployment/sless-operator -n sless
5. [если изменился terraform/provider/]
go build → /tmp/sless-provider-dev/terraform-provider-sless_vX.X.X
(удалить старый бинарник из /tmp/sless-provider-dev/)
6. terraform apply в examples/POSTGRES/
cd /home/naeel/terra/sless/examples/POSTGRES && terraform apply -auto-approve
```
---
## 8. Типичные ошибки и решения
| Ошибка | Причина | Решение |
|--------|---------|---------|
| `unknown field FunctionRef` при `docker build` | Остарелая ссылка на удалённое поле CRD | Найти и заменить все вхождения `FunctionRef` в Go-коде |
| `Unsupported argument "timeout_sec"` при `terraform apply` | Провайдер не пересобран / старый бинарник в dev override | Пересобрать и удалить старый файл из `/tmp/sless-provider-dev/` |
| `ImagePullBackOff` при деплое оператора | Нет `imagePullSecrets` или secret не содержит credentials для registry | Добавить `imagePullSecrets: [{name: sless-registry-auth}]` в operator.yaml |
| `project naeel not found` при `docker push` | Проект в Harbor не создан | POST к Harbor API (см. раздел 5.3) |
| `no route to host` при SSH | Машина недоступна напрямую | Подключаться через 5.172.178.213 с SSH-ключом |
| CRD имеет старые поля (`functionRef` вместо `runtime`) | CRD не обновлён в кластере после правки types.go | `kubectl apply -f config/crd/bases/` после регенерации |
---
## 9. Связанные файлы документации
- [doc/run_and_logs.md](../run_and_logs.md) — шаблоны SSH-команд, реквизиты машин
- [doc/decisions/log.md](log.md) — обоснование архитектурных решений
- [doc/infrastructure/overview.md](../infrastructure/overview.md) — инфраструктура кластера
- [doc/progress.md](../progress.md) — трекер задач