feat(cli): 헬스체크 경로 동기화(경로+/healthz) — 콘솔 정합 · v0.31.0
deploy.py: health 미지정 시 healthPath=경로+/healthz 기본(명시=커스텀) ctl.py: set --path 시 커스텀 아니면 healthPath 동반 갱신 dev.py: 로컬 kind도 동일(readiness+liveness, 백엔드 정합) bin/yakcloud: VERSION 0.30.0 → 0.31.0
This commit is contained in:
@ -180,6 +180,12 @@ def cmd_scale(a) -> None:
|
|||||||
_apply_workload_patch(m, _cid(m), w, {"replicasDesired": int(a.replicas)}, "replicas=%s" % a.replicas)
|
_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:
|
def cmd_set(a) -> None:
|
||||||
m = load_manifest(); w = find_workload(m, a.workload)
|
m = load_manifest(); w = find_workload(m, a.workload)
|
||||||
patch, changed = {}, []
|
patch, changed = {}, []
|
||||||
@ -199,6 +205,10 @@ def cmd_set(a) -> None:
|
|||||||
ex = w.get("expose", {}) or {}; w["expose"] = ex
|
ex = w.get("expose", {}) or {}; w["expose"] = ex
|
||||||
if a.path is not None:
|
if a.path is not None:
|
||||||
ex["path"] = a.path; patch["path"] = a.path; changed.append("path")
|
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:
|
if a.rewrite is not None:
|
||||||
rw = a.rewrite.lower() in ("1", "true", "yes", "on")
|
rw = a.rewrite.lower() in ("1", "true", "yes", "on")
|
||||||
ex["rewrite"] = rw; patch["rewritePrefix"] = rw; changed.append("rewrite")
|
ex["rewrite"] = rw; patch["rewritePrefix"] = rw; changed.append("rewrite")
|
||||||
|
|||||||
@ -174,6 +174,13 @@ def _repo(img: str) -> str:
|
|||||||
return img.rsplit(":", 1)[0] if ":" in seg else img
|
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]:
|
def deploy_workload(w: dict) -> tuple[str | None, bool]:
|
||||||
image = w["image"].replace("${TAG}", TAG)
|
image = w["image"].replace("${TAG}", TAG)
|
||||||
ex = w.get("expose", {}) or {}
|
ex = w.get("expose", {}) or {}
|
||||||
@ -183,7 +190,8 @@ def deploy_workload(w: dict) -> tuple[str | None, bool]:
|
|||||||
"port": w.get("port"),
|
"port": w.get("port"),
|
||||||
"replicasDesired": w.get("replicas", 1),
|
"replicasDesired": w.get("replicas", 1),
|
||||||
"cpuRequest": res.get("cpu", "25m"), "memRequest": res.get("mem", "96Mi"),
|
"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)),
|
"path": ex.get("path", "/"), "pathType": "Prefix", "rewritePrefix": bool(ex.get("rewrite", False)),
|
||||||
}
|
}
|
||||||
# 노출 도메인 = 워크로드 지정 host(s) + 환경 도메인(운영). 커스텀이 있으면 기본 도메인도 함께 노출.
|
# 노출 도메인 = 워크로드 지정 host(s) + 환경 도메인(운영). 커스텀이 있으면 기본 도메인도 함께 노출.
|
||||||
|
|||||||
@ -67,6 +67,12 @@ SUPPORTED = set(_SVC_PORT)
|
|||||||
MANAGED = "app.kubernetes.io/managed-by=yakcloud-dev"
|
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:
|
def log(m: str) -> None:
|
||||||
print("\033[36m▸\033[0m " + m, flush=True)
|
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",
|
container = {"name": name, "image": image, "imagePullPolicy": "IfNotPresent",
|
||||||
"ports": [{"containerPort": port}], "env": env,
|
"ports": [{"containerPort": port}], "env": env,
|
||||||
"resources": {"requests": {"cpu": res.get("cpu", "25m"), "memory": res.get("mem", "96Mi")}}}
|
"resources": {"requests": {"cpu": res.get("cpu", "25m"), "memory": res.get("mem", "96Mi")}}}
|
||||||
if w.get("health"):
|
# health 미지정 시 경로와 동기화(경로+/healthz) — 콘솔 '경로 동기화' 기본·백엔드와 정합.
|
||||||
container["readinessProbe"] = {"httpGet": {"path": w["health"], "port": port},
|
# 백엔드처럼 readiness + liveness 둘 다(로컬에서 prod 동일 동작을 조기 검증).
|
||||||
"initialDelaySeconds": 3, "periodSeconds": 5, "failureThreshold": 30}
|
_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",
|
dep = {"apiVersion": "apps/v1", "kind": "Deployment",
|
||||||
"metadata": {"name": name, "namespace": ns, "labels": labels},
|
"metadata": {"name": name, "namespace": ns, "labels": labels},
|
||||||
"spec": {"replicas": int(w.get("replicas", 1)),
|
"spec": {"replicas": int(w.get("replicas", 1)),
|
||||||
|
|||||||
@ -16,7 +16,7 @@ set -uo pipefail # -e 미사용: 'test && action' 관용구가 값 없을 때
|
|||||||
|
|
||||||
REPO="${YAKCLOUD_STARTER_REPO:-https://gitea.yakenator.io/yakenator/yakcloud-starter}"
|
REPO="${YAKCLOUD_STARTER_REPO:-https://gitea.yakenator.io/yakenator/yakcloud-starter}"
|
||||||
BRANCH="${YAKCLOUD_STARTER_BRANCH:-main}"
|
BRANCH="${YAKCLOUD_STARTER_BRANCH:-main}"
|
||||||
VERSION="0.30.0"
|
VERSION="0.31.0"
|
||||||
# 로컬 개발 클러스터(kind) — 개발 후 운영까지 '직선 배포'의 dev 구간.
|
# 로컬 개발 클러스터(kind) — 개발 후 운영까지 '직선 배포'의 dev 구간.
|
||||||
DEV_CTX="${YAK_DEV_CONTEXT:-kind-yak-dev}"
|
DEV_CTX="${YAK_DEV_CONTEXT:-kind-yak-dev}"
|
||||||
DEV_CLUSTER="${YAK_DEV_CLUSTER:-yak-dev}"
|
DEV_CLUSTER="${YAK_DEV_CLUSTER:-yak-dev}"
|
||||||
|
|||||||
Reference in New Issue
Block a user