feat: add ingress-nginx, demo-app, Chaos Mesh apps and Grafana alert rules

WP-14(Terraform 설계서 7.3, 실험·앱 설계서 3.1·4.3).
- apps/ingress-nginx: baremetal 매니페스트(controller-v1.15.1)를 kustomize로 가져와
  NodePort 30080/30443 고정(ALB TG와 일치).
- apps/demo-app + workloads/demo-app: front(nginx-unprivileged 1.31.6, 정적 페이지 +
  /api/ 프록시) 2, api(demo-api:8f5378b) 3 + PDB minAvailable 2, Service, Ingress(/ front,
  /api api). DB_DSN은 Secret demo-db(make k8s-secrets로 생성, git 밖).
- apps/chaos-mesh: Helm chart 2.8.4(containerd 소켓, dashboard 없음).
- vm/observability/grafana/provisioning/alerting: contact point harness-webhook
  (10.20.0.11:8080/hooks/alert), 루트 정책, 규칙 8건(ProbeDown·ApiLatencyHigh·
  ApiErrorRateHigh·PodCrashLooping·LegacyUnhealthy·DbReplicationLag·DbPrimaryDown·
  GitOpsDrift, 라벨 layer/signature).
This commit is contained in:
seon committed 2026-09-24 01:35:45 +09:00
1 parent 907056f4f6
commit 1db68b643f
14 files changed
+511 -12

No files matched your search

-3
View File
@@ -1,3 +0,0 @@
# workloads/alloy
Placeholder. See the root README for the work package that fills this directory.
-3
View File
@@ -1,3 +0,0 @@
# workloads/chaos-mesh
Placeholder. See the root README for the work package that fills this directory.
-3
View File
@@ -1,3 +0,0 @@
# workloads/demo-app
Placeholder. See the root README for the work package that fills this directory.
+53
View File
@@ -0,0 +1,53 @@
# api: Go service (selfheal-lab-apps/demo-app). 3 replicas, PDB minAvailable 2, probes per experiment design 3.1.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: demo
labels: { app: api }
spec:
replicas: 3
selector:
matchLabels: { app: api }
template:
metadata:
labels: { app: api }
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: /metrics
spec:
containers:
- name: api
image: asia-northeast1-docker.pkg.dev/cms-d-414903/selfheal-public/demo-api:8f5378b
ports: [{ name: http, containerPort: 8080 }]
env:
- name: DB_DSN
valueFrom: { secretKeyRef: { name: demo-db, key: DB_DSN } }
- { name: LEGACY_URL, value: "http://10.20.0.100:8080" }
- { name: LEGACY_TIMEOUT, value: "2s" }
livenessProbe: { httpGet: { path: /healthz, port: http }, periodSeconds: 10, failureThreshold: 3 }
readinessProbe: { httpGet: { path: /readyz, port: http }, periodSeconds: 5, failureThreshold: 2 }
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { memory: 256Mi }
securityContext: { allowPrivilegeEscalation: false, runAsNonRoot: true }
---
apiVersion: v1
kind: Service
metadata:
name: api
namespace: demo
spec:
selector: { app: api }
ports: [{ name: http, port: 8080, targetPort: http }]
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api
namespace: demo
spec:
minAvailable: 2
selector:
matchLabels: { app: api }
+65
View File
@@ -0,0 +1,65 @@
# front: nginx (unprivileged) serving a static page and proxying /api/ to the api Service.
apiVersion: v1
kind: ConfigMap
metadata:
name: front-nginx
namespace: demo
data:
default.conf: |
server {
listen 8080;
server_name _;
location = /healthz { add_header Content-Type text/plain; return 200 'ok'; }
location /api/ {
proxy_pass http://api:8080/api/;
proxy_connect_timeout 2s;
proxy_read_timeout 5s;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / { root /usr/share/nginx/html; index index.html; }
}
index.html: |
<!doctype html><html lang="ja"><meta charset="utf-8"><title>selfheal demo</title>
<body style="font-family:system-ui;margin:2rem"><h1>selfheal demo front</h1>
<p>container layer (AWS k8s) -&gt; VPN -&gt; VM layer (GCP legacy-api, PostgreSQL)</p>
<ul><li><a href="/api/orders">/api/orders</a></li><li><a href="/api/price/apple">/api/price/apple</a></li><li><a href="/api/readyz">/api/readyz</a></li></ul>
</body></html>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: front
namespace: demo
labels: { app: front }
spec:
replicas: 2
selector:
matchLabels: { app: front }
template:
metadata:
labels: { app: front }
spec:
containers:
- name: nginx
image: nginxinc/nginx-unprivileged:1.31.6-alpine
ports: [{ name: http, containerPort: 8080 }]
volumeMounts:
- { name: conf, mountPath: /etc/nginx/conf.d/default.conf, subPath: default.conf, readOnly: true }
- { name: conf, mountPath: /usr/share/nginx/html/index.html, subPath: index.html, readOnly: true }
livenessProbe: { httpGet: { path: /healthz, port: http }, periodSeconds: 10, failureThreshold: 3 }
readinessProbe: { httpGet: { path: /healthz, port: http }, periodSeconds: 5, failureThreshold: 2 }
resources:
requests: { cpu: 50m, memory: 32Mi }
limits: { memory: 64Mi }
volumes:
- name: conf
configMap: { name: front-nginx }
---
apiVersion: v1
kind: Service
metadata:
name: front
namespace: demo
spec:
selector: { app: front }
ports: [{ name: http, port: 8080, targetPort: http }]
+17
View File
@@ -0,0 +1,17 @@
# Host-agnostic Ingress (reached through the ALB DNS name): / -> front, /api -> api.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo
namespace: demo
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /api
pathType: Prefix
backend: { service: { name: api, port: { name: http } } }
- path: /
pathType: Prefix
backend: { service: { name: front, port: { name: http } } }
-3
View File
@@ -1,3 +0,0 @@
# workloads/ingress-nginx
Placeholder. See the root README for the work package that fills this directory.
@@ -0,0 +1,18 @@
# ingress-nginx baremetal (NodePort) provider manifest, pinned; NodePorts fixed so the ALB target group (30080) matches.
# https://kubernetes.github.io/ingress-nginx/deploy/baremetal/
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/baremetal/deploy.yaml
patches:
- target:
kind: Service
name: ingress-nginx-controller
namespace: ingress-nginx
patch: |-
- op: add
path: /spec/ports/0/nodePort
value: 30080
- op: add
path: /spec/ports/1/nodePort
value: 30443