diff --git a/controllers/function_controller.go b/controllers/function_controller.go index 8e990cc..0307ea9 100644 --- a/controllers/function_controller.go +++ b/controllers/function_controller.go @@ -211,6 +211,15 @@ func (r *FunctionReconciler) ensureDeployment(ctx context.Context, fn *slessv1al // Обновляем образ и imagePullSecrets если изменились (новая сборка или смена конфига) existing.Spec.Template.Spec.Containers[0].Image = fn.Status.ImageRef existing.Spec.Template.Spec.ImagePullSecrets = desired.Spec.Template.Spec.ImagePullSecrets + // Принудительный rollout restart при каждой новой сборке. + // Необходимо т.к. image tag :latest не меняется — без этой аннотации + // kubelet берёт кешированный образ и pod не видит новый код. + if fn.Status.LastBuiltAt != nil { + if existing.Spec.Template.Annotations == nil { + existing.Spec.Template.Annotations = map[string]string{} + } + existing.Spec.Template.Annotations["kubectl.kubernetes.io/restartedAt"] = fn.Status.LastBuiltAt.Time.Format(time.RFC3339) + } if err := r.Update(ctx, existing); err != nil { return ctrl.Result{}, fmt.Errorf("update deployment: %w", err) } diff --git a/deployments/k8s/operator.yaml b/deployments/k8s/operator.yaml index 3f79a4a..4d4d2db 100644 --- a/deployments/k8s/operator.yaml +++ b/deployments/k8s/operator.yaml @@ -3,7 +3,7 @@ # Состав: # - ConfigMap: не-секретные env vars (S3_ENDPOINT, REGISTRY_HOST и т.д.) # - Secret: секретные данные (S3 keys, postgres DSN, API token, docker auth) -# - Deployment: оператор naeel/sless-operator:v0.1.4 в namespace sless +# - Deployment: оператор naeel/sless-operator:v0.1.11 в namespace sless # - Service: ClusterIP :9090 (REST API) # - Ingress: sless-api.kube5s.ru → :9090 (внешний доступ с TLS) # @@ -70,7 +70,7 @@ spec: containers: - name: operator # При обновлении версии оператора — менять тег здесь (не latest!) - image: naeel/sless-operator:v0.1.10 + image: naeel/sless-operator:v0.1.11 # Always — чтобы всегда тянуть по точному тегу (не кешировать старый) imagePullPolicy: Always ports: diff --git a/doc/decisions/log.md b/doc/decisions/log.md index 534dd65..bf89e2a 100644 --- a/doc/decisions/log.md +++ b/doc/decisions/log.md @@ -245,3 +245,20 @@ **Правило проекта:** В `sless_function.code_hash` всегда использовать `filesha256(source_file)`, не `archive_file.output_md5`. + + +--- + +## 2026-03-08 — Rollout restart после kaniko build (imagePullPolicy + :latest) + +**Проблема:** После успешной kaniko сборки pod не перезапускался — kubelet брал кешированный образ `:latest` (imagePullPolicy: IfNotPresent). Функция возвращала старый код. + +**Решение:** В `ensureDeployment` при обновлении существующего Deployment проставляем аннотацию: +```go +existing.Spec.Template.Annotations["kubectl.kubernetes.io/restartedAt"] = fn.Status.LastBuiltAt.Time.Format(time.RFC3339) +``` +Значение привязано к `fn.Status.LastBuiltAt` → меняется при каждой сборке → Kubernetes делает rolling restart → свежий образ гарантированно пул-ится. + +**Правило проекта:** При использовании `:latest` tag всегда явно проставлять `restartedAt` annotation при обновлении кода. + +**Версия:** operator `naeel/sless-operator:v0.1.11` diff --git a/doc/errors/log.md b/doc/errors/log.md index d11ec5b..3f084f9 100644 --- a/doc/errors/log.md +++ b/doc/errors/log.md @@ -301,3 +301,18 @@ k8s.io/api should be direct **Решение:** `go mod tidy` автоматически расставил правильные аннотации. --- + +## 2026-03-08 — После kaniko rebuild функция возвращает старый код + +**Проблема:** `terraform apply` с изменённым кодом успешно завершал kaniko build, но функция продолжала возвращать старую версию кода. + +**Причина:** `imagePullPolicy: IfNotPresent` (default в k8s) + `:latest` тег — после успешной сборки оператор обновлял `.spec.template.spec.containers[0].image`, но image tag не менялся (всегда `:latest`). Kubernetes видел что образ уже есть на ноде и не пул-ил новый. Pod оставался запущенным со старым образом. + +**Решение:** В `controllers/function_controller.go`, функция `ensureDeployment`, после обновления image добавлена аннотация: +```go +existing.Spec.Template.Annotations["kubectl.kubernetes.io/restartedAt"] = fn.Status.LastBuiltAt.Time.Format(time.RFC3339) +``` +Аннотация меняется при каждой новой сборке (значение = `lastBuiltAt`) → Kubernetes делает rolling restart → kubelet пул-ит свежий образ. +Оператор: **v0.1.11**. + +--- diff --git a/doc/progress.md b/doc/progress.md index 716b58c..2da6664 100644 --- a/doc/progress.md +++ b/doc/progress.md @@ -1,6 +1,6 @@ # Прогресс разработки -Последнее обновление: 2026-03-08 (operator v0.1.10, provider v0.1.5) +Последнее обновление: 2026-03-08 (operator v0.1.11, provider v0.1.5) ## Статусы: ✅ готово | 🔄 в процессе | ⏳ не начато diff --git a/examples/hello-node/code/handler-http.js b/examples/hello-node/code/handler-http.js index 9766375..16bc1a1 100644 --- a/examples/hello-node/code/handler-http.js +++ b/examples/hello-node/code/handler-http.js @@ -3,6 +3,6 @@ // Используется с sless_trigger (постоянный эндпоинт). exports.handle = async (event) => { const name = event.name || 'World'; - return { message: `Hello, ${name}! HTTP ` }; + return { message: `Hello, ${name}! HTTP !!!` }; }; diff --git a/examples/hello-node/handler-http.zip b/examples/hello-node/handler-http.zip index 8ce3f7d..eb7a08c 100644 Binary files a/examples/hello-node/handler-http.zip and b/examples/hello-node/handler-http.zip differ diff --git a/examples/hello-node/job.tf b/examples/hello-node/job.tf index 59b2d27..cd168bc 100644 --- a/examples/hello-node/job.tf +++ b/examples/hello-node/job.tf @@ -36,7 +36,7 @@ resource "sless_job" "hello_run" { function = sless_function.hello_job.name event_json = jsonencode({ numbers = [1, 2, 3, 4, 5] }) wait_timeout_sec = 600 - run_id = 1 + run_id = 5 } output "job_phase" {