diff --git a/.yakcloud/ctl.py b/.yakcloud/ctl.py index da651ad..270e3ef 100644 --- a/.yakcloud/ctl.py +++ b/.yakcloud/ctl.py @@ -180,6 +180,12 @@ def cmd_scale(a) -> None: _apply_workload_patch(m, _cid(m), w, {"replicasDesired": int(a.replicas)}, "replicas=%s" % a.replicas) +def synced_health_path(path: str) -> str: + """경로 동기화 기본: healthPath = 경로 + '/healthz' (콘솔 '경로 동기화'와 정합).""" + base = (path or "/").strip().rstrip("/") + return base + "/healthz" + + def cmd_set(a) -> None: m = load_manifest(); w = find_workload(m, a.workload) patch, changed = {}, [] @@ -199,6 +205,10 @@ def cmd_set(a) -> None: ex = w.get("expose", {}) or {}; w["expose"] = ex if a.path is not None: ex["path"] = a.path; patch["path"] = a.path; changed.append("path") + # 헬스체크가 커스텀(매니페스트 health 명시)이 아니고 이번에 --health 도 안 주면 + # 경로와 동기화 — healthPath 도 경로+/healthz 로 갱신. + if a.health is None and not w.get("health"): + patch["healthPath"] = synced_health_path(a.path); changed.append("health(sync)") if a.rewrite is not None: rw = a.rewrite.lower() in ("1", "true", "yes", "on") ex["rewrite"] = rw; patch["rewritePrefix"] = rw; changed.append("rewrite") diff --git a/.yakcloud/deploy.py b/.yakcloud/deploy.py index b37cb5a..da9f038 100644 --- a/.yakcloud/deploy.py +++ b/.yakcloud/deploy.py @@ -174,6 +174,13 @@ def _repo(img: str) -> str: return img.rsplit(":", 1)[0] if ":" in seg else img +def synced_health_path(path: str) -> str: + """경로 동기화 기본: healthPath = 경로 + '/healthz' (예: '/b21'→'/b21/healthz', '/'→'/healthz'). + 콘솔 '경로 동기화'(기본 ON)와 정합 — 매니페스트에 health 를 명시하면 그 값을 그대로 쓴다.""" + base = (path or "/").strip().rstrip("/") + return base + "/healthz" + + def deploy_workload(w: dict) -> tuple[str | None, bool]: image = w["image"].replace("${TAG}", TAG) ex = w.get("expose", {}) or {} @@ -183,7 +190,8 @@ def deploy_workload(w: dict) -> tuple[str | None, bool]: "port": w.get("port"), "replicasDesired": w.get("replicas", 1), "cpuRequest": res.get("cpu", "25m"), "memRequest": res.get("mem", "96Mi"), - "healthPath": w.get("health"), + # health 미지정 시 경로와 동기화(경로+/healthz). 명시하면 그 값(커스텀). + "healthPath": w.get("health") or synced_health_path(ex.get("path", "/")), "path": ex.get("path", "/"), "pathType": "Prefix", "rewritePrefix": bool(ex.get("rewrite", False)), } # 노출 도메인 = 워크로드 지정 host(s) + 환경 도메인(운영). 커스텀이 있으면 기본 도메인도 함께 노출. diff --git a/.yakcloud/dev.py b/.yakcloud/dev.py index 571c526..b104278 100644 --- a/.yakcloud/dev.py +++ b/.yakcloud/dev.py @@ -67,6 +67,12 @@ SUPPORTED = set(_SVC_PORT) MANAGED = "app.kubernetes.io/managed-by=yakcloud-dev" +def synced_health_path(path: str) -> str: + """경로 동기화 기본: healthPath = 경로 + '/healthz' (콘솔 '경로 동기화'·백엔드와 정합). '/' 더블슬래시 방지.""" + base = (path or "/").strip().rstrip("/") + return base + "/healthz" + + def log(m: str) -> None: print("\033[36m▸\033[0m " + m, flush=True) @@ -463,9 +469,15 @@ def deploy_workload(project: str, ns: str, w: dict, src_conns: dict) -> tuple[st container = {"name": name, "image": image, "imagePullPolicy": "IfNotPresent", "ports": [{"containerPort": port}], "env": env, "resources": {"requests": {"cpu": res.get("cpu", "25m"), "memory": res.get("mem", "96Mi")}}} - if w.get("health"): - container["readinessProbe"] = {"httpGet": {"path": w["health"], "port": port}, - "initialDelaySeconds": 3, "periodSeconds": 5, "failureThreshold": 30} + # health 미지정 시 경로와 동기화(경로+/healthz) — 콘솔 '경로 동기화' 기본·백엔드와 정합. + # 백엔드처럼 readiness + liveness 둘 다(로컬에서 prod 동일 동작을 조기 검증). + _ex = w.get("expose", {}) or {} + _health = w.get("health") or synced_health_path(_ex.get("path", "/")) + if _health and port: + _probe = {"httpGet": {"path": _health, "port": port}, + "initialDelaySeconds": 10, "periodSeconds": 10} + container["readinessProbe"] = _probe + container["livenessProbe"] = {**_probe, "initialDelaySeconds": 20} dep = {"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": name, "namespace": ns, "labels": labels}, "spec": {"replicas": int(w.get("replicas", 1)), diff --git a/bin/yakcloud b/bin/yakcloud index f45000f..b2789de 100755 --- a/bin/yakcloud +++ b/bin/yakcloud @@ -16,7 +16,7 @@ set -uo pipefail # -e 미사용: 'test && action' 관용구가 값 없을 때 REPO="${YAKCLOUD_STARTER_REPO:-https://gitea.yakenator.io/yakenator/yakcloud-starter}" BRANCH="${YAKCLOUD_STARTER_BRANCH:-main}" -VERSION="0.30.0" +VERSION="0.31.0" # 로컬 개발 클러스터(kind) — 개발 후 운영까지 '직선 배포'의 dev 구간. DEV_CTX="${YAK_DEV_CONTEXT:-kind-yak-dev}" DEV_CLUSTER="${YAK_DEV_CLUSTER:-yak-dev}"