From 79719e078d48a1ca0a6055554b58af210b4ed39f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sun, 8 Mar 2026 20:55:47 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20rollout=20restart=20=D0=BF=D0=BE=D1=81?= =?UTF-8?q?=D0=BB=D0=B5=20kaniko=20rebuild=20(operator=20v0.1.11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - controllers/function_controller.go: в ensureDeployment добавить аннотацию kubectl.kubernetes.io/restartedAt = fn.Status.LastBuiltAt при обновлении существующего Deployment — принудительный rolling restart после каждой сборки - deployments/k8s/operator.yaml: образ обновлён до v0.1.11 - doc/errors/log.md: задокументирована причина и решение - doc/decisions/log.md: добавлено правило restartedAt + :latest - doc/progress.md: версия обновлена до v0.1.11 Причина бага: imagePullPolicy=IfNotPresent + :latest — kubelet кешировал старый образ, pod не рестартовал после успешного kaniko build. --- controllers/function_controller.go | 9 +++++++++ deployments/k8s/operator.yaml | 4 ++-- doc/decisions/log.md | 17 +++++++++++++++++ doc/errors/log.md | 15 +++++++++++++++ doc/progress.md | 2 +- examples/hello-node/code/handler-http.js | 2 +- examples/hello-node/handler-http.zip | Bin 413 -> 419 bytes examples/hello-node/job.tf | 2 +- 8 files changed, 46 insertions(+), 5 deletions(-) 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 8ce3f7ddbf43a1d10027b2581382d93a2964c7a7..eb7a08c39dc9e992694803eb92910f08ba303b8a 100644 GIT binary patch delta 333 zcmV-T0kZy`1ET|wFn?;|P#DGcd5RMlP+^jY1H+&=YnNRJUDlu%ZHbB8ds|vm@J}g} zwz%n{k0GIA+7f+*^Swf!q$KU~@cqt@j}ruF)Ej@j`X8_U&j|vI!gvrV?TxIR_!kDB zi)VDY-4jo4WzC%La>q+{&P)h*<6ay3h f2mlBG001G1d1L?I69E7KL6c(vGzL%s00000mnW75 delta 327 zcmV-N0l5C71DykqFn>w{Q5ePd^AsltRA^?zAVRUV%N9b*9Q2~aI3xGoqENv<5k!TX z7CnXxiBmE93g>%;o+M^!dH8HCVx!1@p3WWWzIc!{FWDP zW%-Y+Aaj^d8FOj%Rllz^$~jiFcd}x}OrH4(d1c0r!jLkPMa6T}-6+=9cz-{miNM@P zU6j?WifjeVGmISo=*E$;h=O6x6i2TJlL<-}u?~BsM!{)iZ*+t)hRT?ruj=TuR2arb zI2`AN=|M3CIyIO!oN2>xeggmi|Nl@+2M9mUpu-IT002G#002-+0Rj{Q6aWYS2mk;8 ZA&NiGpu-IT002IdX#q3_N&x@>0058?k4^vp 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" {