feat: podpora high-availability a multi-replica nasazení
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 20s
CI / Build server (push) Successful in 26s
CI / Build client (push) Successful in 35s
CI / Playwright E2E tests (push) Failing after 1m56s
CI / Build and push Docker image (push) Has been skipped
CI / Notify (push) Successful in 1s
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 20s
CI / Build server (push) Successful in 26s
CI / Build client (push) Successful in 35s
CI / Playwright E2E tests (push) Failing after 1m56s
CI / Build and push Docker image (push) Has been skipped
CI / Notify (push) Successful in 1s
- Socket.io Redis adapter pro sdílený stav přes repliky - graceful shutdown serveru - WATCH/MULTI v updateData pro race-condition-safe aktualizace - lease mechanismus pro push reminder (zabrání duplicitnímu odesílání) - k8s/ manifesty pro testovací kind cluster - Dockerfile: opraven EXPOSE port na 3001 - .gitignore: ignorovány Claude pracovní soubory
This commit is contained in:
+186
@@ -0,0 +1,186 @@
|
||||
# Kubernetes — Luncher HA
|
||||
|
||||
Manifesty pro nasazení Luncheru na Kubernetes s vysokou dostupností (3 repliky, Redis adapter pro Socket.io, WATCH/MULTI atomické zápisy, graceful shutdown).
|
||||
|
||||
## Prerekvizity
|
||||
|
||||
- kubectl nakonfigurovaný na cílový cluster
|
||||
- `helm` nainstalovaný
|
||||
- Redis Stack image přístupný z clusteru (`redis/redis-stack-server:7.2.0-v14`)
|
||||
- Obraz `luncher:ha-test` načtený do clusteru (viz níže)
|
||||
|
||||
## Lokální kind cluster (testik) — setup
|
||||
|
||||
### 1. Smazat a znovu vytvořit cluster s port mappings
|
||||
|
||||
```powershell
|
||||
$env:KIND_EXPERIMENTAL_PROVIDER = "nerdctl"
|
||||
# Přidat nerdctl do PATH (Rancher Desktop)
|
||||
$env:PATH += ";$env:LOCALAPPDATA\Programs\Rancher Desktop\resources\resources\win32\bin"
|
||||
|
||||
kind delete cluster --name testik
|
||||
kind create cluster --name testik --config k8s/kind/testik.yaml
|
||||
```
|
||||
|
||||
### 2. Sestavit a načíst obraz
|
||||
|
||||
```powershell
|
||||
docker build -t luncher:ha-test .
|
||||
|
||||
# Uložit a načíst přes nerdctl (kind + nerdctl provider)
|
||||
nerdctl save luncher:ha-test -o luncher.tar
|
||||
kind load image-archive luncher.tar --name testik
|
||||
Remove-Item luncher.tar
|
||||
```
|
||||
|
||||
### 3. Nainstalovat Traefik (rke2-traefik)
|
||||
|
||||
> **Prerekvizita (Rancher Desktop):** Pokud Rancher Desktop běží s `kubernetes.options.traefik=true`,
|
||||
> host-switch.exe obsadí port 80 dříve než kind. Vypni traefik v k3s:
|
||||
> ```powershell
|
||||
> rdctl set --kubernetes.options.traefik=false
|
||||
> ```
|
||||
>
|
||||
> **Prerekvizita — inotify limity:** Čtyř-uzlový kind cluster vyčerpá výchozí
|
||||
> `fs.inotify.max_user_instances=128`. kube-proxy pak padá s „too many open files".
|
||||
> Zvyš limit v rancher-desktop WSL2 (přežije restart WSL2, ale ne reboot — přidej do
|
||||
> `/etc/sysctl.d/99-kind.conf` pro trvalost):
|
||||
> ```powershell
|
||||
> wsl -d rancher-desktop -- sysctl -w fs.inotify.max_user_instances=1280
|
||||
> ```
|
||||
|
||||
```powershell
|
||||
# rke2-traefik je v rke2-charts, ne rancher-charts
|
||||
helm repo add rke2-charts https://rke2-charts.rancher.io
|
||||
helm repo update
|
||||
|
||||
# Nejdřív CRD chart, pak samotný chart
|
||||
helm install traefik-crd rke2-charts/rke2-traefik-crd -n kube-system --create-namespace
|
||||
helm install traefik rke2-charts/rke2-traefik -n kube-system `
|
||||
--set "tolerations[0].key=node-role.kubernetes.io/control-plane" `
|
||||
--set "tolerations[0].operator=Exists" `
|
||||
--set "tolerations[0].effect=NoSchedule"
|
||||
```
|
||||
|
||||
Ověř že Traefik DaemonSet běží na control-plane (má hostPort 80):
|
||||
```powershell
|
||||
kubectl get ds -n kube-system traefik-rke2-traefik
|
||||
kubectl get pods -n kube-system -o wide | Select-String traefik
|
||||
```
|
||||
|
||||
### 4. Nainstalovat Reloader
|
||||
|
||||
[stakater/Reloader](https://github.com/stakater/Reloader) sleduje změny Secret a ConfigMap a automaticky spustí rolling restart Deploymentu — odpadá nutnost ručního `kubectl rollout restart` po rotaci `JWT_SECRET` nebo `ADMIN_PASSWORD`.
|
||||
|
||||
Manifest je vendorovaný ve verzi v1.4.16 (`k8s/base/reloader.yaml`). Nasadit do `default` namespace:
|
||||
|
||||
```powershell
|
||||
kubectl apply -f k8s/base/reloader.yaml
|
||||
kubectl rollout status deploy/reloader-reloader
|
||||
```
|
||||
|
||||
Reloader běží cluster-wide díky `ClusterRoleBinding` — nepotřebuje žádnou konfiguraci per-namespace. Deployment Luncheru má anotaci `reloader.stakater.com/auto: "true"`, která říká Reloaderu, ať sleduje všechny Secrety a ConfigMapy odkazované přes `envFrom`.
|
||||
|
||||
### 5. Nasadit Luncher
|
||||
|
||||
```powershell
|
||||
# Namespace + Redis
|
||||
kubectl apply -f k8s/base/namespace.yaml
|
||||
kubectl apply -f k8s/base/redis-statefulset.yaml
|
||||
kubectl apply -f k8s/base/redis-service.yaml
|
||||
|
||||
# Počkat na Redis
|
||||
kubectl rollout status statefulset/redis -n luncher
|
||||
|
||||
# Server secret (nebo použít šablonu server-secret.yaml)
|
||||
kubectl create secret generic luncher-secrets -n luncher `
|
||||
--from-literal=JWT_SECRET=dev-secret-change-me `
|
||||
--from-literal=ADMIN_PASSWORD=admin
|
||||
|
||||
# Server
|
||||
kubectl apply -f k8s/base/server-configmap.yaml
|
||||
kubectl apply -f k8s/base/server-deployment.yaml
|
||||
kubectl apply -f k8s/base/server-service.yaml
|
||||
kubectl apply -f k8s/base/server-pdb.yaml
|
||||
kubectl apply -f k8s/base/ingressroute.yaml
|
||||
|
||||
# Počkat na server
|
||||
kubectl rollout status deploy/luncher -n luncher
|
||||
```
|
||||
|
||||
## Testovací scénáře
|
||||
|
||||
### Baseline
|
||||
|
||||
```powershell
|
||||
kubectl get pods -n luncher -o wide
|
||||
# Ověř: 3 pody na 3 různých worker uzlech, status Running
|
||||
```
|
||||
|
||||
### Rolling update bez výpadku
|
||||
|
||||
V jednom terminálu posílej provoz:
|
||||
```powershell
|
||||
# Nainstaluj hey: go install github.com/rakyll/hey@latest
|
||||
hey -z 60s -c 20 http://luncher.localhost/api/health
|
||||
```
|
||||
|
||||
Ve druhém terminálu spusť rollout:
|
||||
```powershell
|
||||
kubectl rollout restart deploy/luncher -n luncher
|
||||
```
|
||||
|
||||
**Kritérium: 0 non-2xx odpovědí, 0 connection errors.**
|
||||
|
||||
### Node drain
|
||||
|
||||
```powershell
|
||||
kubectl cordon testik-worker2
|
||||
kubectl drain testik-worker2 --ignore-daemonsets --delete-emptydir-data
|
||||
# PDB zabrání souběžnému drainu druhého nodu
|
||||
kubectl get pods -n luncher -o wide # pody se přeplánují
|
||||
kubectl uncordon testik-worker2
|
||||
```
|
||||
|
||||
### Ověření Socket.io cross-pod
|
||||
|
||||
1. Otevři dvě záložky prohlížeče na `http://luncher.localhost`
|
||||
2. Z jednoho podu vyvolej změnu:
|
||||
```powershell
|
||||
kubectl exec -it deploy/luncher -n luncher -- curl -s -X POST localhost:3001/api/...
|
||||
```
|
||||
3. Ověř, že druhá záložka (pravděpodobně jiný pod) obdrží WebSocket event
|
||||
|
||||
### Concurrent write test
|
||||
|
||||
1. Otevři stejnou Pizza day objednávku ve dvou záložkách
|
||||
2. Simuluj souběžné odeslání (otevřít DevTools → síť → odeslat obě požadavky současně)
|
||||
3. Ověř Redis: `kubectl exec -it redis-0 -n luncher -- redis-cli JSON.GET luncher:<datum>`
|
||||
— oba zápisy musí být zachovány (WATCH/MULTI retry)
|
||||
|
||||
### Auto-rollout při změně Secret / ConfigMap
|
||||
|
||||
Reloader automaticky spustí rolling restart, kdykoli se změní `luncher-secrets` nebo `luncher-config`:
|
||||
|
||||
```powershell
|
||||
# Příklad: rotace admin hesla
|
||||
kubectl -n luncher patch secret luncher-secrets --type=merge `
|
||||
-p '{"stringData":{"ADMIN_PASSWORD":"nove-heslo"}}'
|
||||
|
||||
# Reloader detekuje změnu resourceVersion a patchne pod template
|
||||
kubectl rollout status deploy/luncher -n luncher
|
||||
|
||||
# Ověř anotaci přidanou Reloaderem na pod template
|
||||
kubectl get deploy luncher -n luncher -o yaml | Select-String "STAKATER"
|
||||
```
|
||||
|
||||
**Kritérium: pody se automaticky vyrolují bez ručního restartu. PDB zajistí, že alespoň jeden pod zůstane dostupný.**
|
||||
|
||||
## Pořadí aplikace manifestů
|
||||
|
||||
1. `reloader.yaml` (do `default` namespace — musí být před Deployment)
|
||||
2. `namespace.yaml`
|
||||
3. `redis-statefulset.yaml` + `redis-service.yaml`
|
||||
4. `server-configmap.yaml` + `server-secret.yaml`
|
||||
5. `server-deployment.yaml` + `server-service.yaml` + `server-pdb.yaml`
|
||||
6. `ingressroute.yaml`
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: luncher
|
||||
namespace: luncher
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: traefik
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`luncher.localhost`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: luncher
|
||||
port: 3001
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: luncher
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: luncher
|
||||
spec:
|
||||
clusterIP: None # headless — StatefulSet pod discovery
|
||||
selector:
|
||||
app: redis
|
||||
ports:
|
||||
- port: 6379
|
||||
targetPort: 6379
|
||||
@@ -0,0 +1,50 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: luncher
|
||||
spec:
|
||||
serviceName: redis
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
# Redis Stack je nutný — aplikace používá JSON.GET / JSON.SET (modul RedisJSON)
|
||||
image: redis/redis-stack-server:7.2.0-v14
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["redis-cli", "ping"]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
exec:
|
||||
command: ["redis-cli", "ping"]
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
@@ -0,0 +1,184 @@
|
||||
# stakater/Reloader v1.4.16
|
||||
# Zdroj: https://raw.githubusercontent.com/stakater/Reloader/v1.4.16/deployments/kubernetes/reloader.yaml
|
||||
# Aktualizace: stáhnout novou verzi ze stejné URL a nahradit tento soubor.
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: reloader-reloader
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: reloader-reloader-metadata-role
|
||||
namespace: default
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: reloader-reloader-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
- configmaps
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- watch
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
- daemonsets
|
||||
- statefulsets
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- update
|
||||
- patch
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- deployments
|
||||
- daemonsets
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- update
|
||||
- patch
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- cronjobs
|
||||
verbs:
|
||||
- list
|
||||
- get
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- jobs
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- list
|
||||
- get
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: reloader-reloader-metadata-rolebinding
|
||||
namespace: default
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: reloader-reloader-metadata-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: reloader-reloader
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: reloader-reloader-role-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: reloader-reloader-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: reloader-reloader
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: reloader-reloader
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: reloader-reloader
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: reloader-reloader
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: GOMAXPROCS
|
||||
valueFrom:
|
||||
resourceFieldRef:
|
||||
divisor: "1"
|
||||
resource: limits.cpu
|
||||
- name: GOMEMLIMIT
|
||||
valueFrom:
|
||||
resourceFieldRef:
|
||||
divisor: "1"
|
||||
resource: limits.memory
|
||||
- name: RELOADER_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: RELOADER_DEPLOYMENT_NAME
|
||||
value: reloader-reloader
|
||||
image: ghcr.io/stakater/reloader:v1.4.16
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
failureThreshold: 5
|
||||
httpGet:
|
||||
path: /live
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 5
|
||||
name: reloader-reloader
|
||||
ports:
|
||||
- containerPort: 9090
|
||||
name: http
|
||||
readinessProbe:
|
||||
failureThreshold: 5
|
||||
httpGet:
|
||||
path: /metrics
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 5
|
||||
resources:
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 512Mi
|
||||
securityContext: {}
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65534
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
serviceAccountName: reloader-reloader
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: luncher-config
|
||||
namespace: luncher
|
||||
data:
|
||||
NODE_ENV: production
|
||||
STORAGE: redis
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: "6379"
|
||||
PORT: "3001"
|
||||
HOST: "0.0.0.0"
|
||||
@@ -0,0 +1,85 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: luncher
|
||||
namespace: luncher
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: luncher
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 0 # nelze přidat extra pod — každý worker je obsazen
|
||||
maxUnavailable: 1 # nejdřív smaž starý pod, pak naplánuj nový
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: luncher
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 30
|
||||
|
||||
# Rozmístit každý pod na jiný worker uzel
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
- labelSelector:
|
||||
matchLabels:
|
||||
app: luncher
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
containers:
|
||||
- name: luncher
|
||||
image: luncher:ha-test
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 3001
|
||||
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: luncher-config
|
||||
- secretRef:
|
||||
name: luncher-secrets
|
||||
|
||||
env:
|
||||
# POD_ID pro leader election scheduleru připomínek
|
||||
- name: POD_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
|
||||
# Liveness — levná kontrola bez externích závislostí
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 3001
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
failureThreshold: 3
|
||||
|
||||
# Readiness — kontroluje Redis; při shutdown vrací 503
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/health/ready
|
||||
port: 3001
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
failureThreshold: 2
|
||||
|
||||
# preStop sleep: dá čas kube-proxy a Traefiku odebrat endpoint
|
||||
# dřív než kontejner začne odmítat nová spojení
|
||||
lifecycle:
|
||||
preStop:
|
||||
exec:
|
||||
command: ["sleep", "5"]
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: luncher-pdb
|
||||
namespace: luncher
|
||||
spec:
|
||||
minAvailable: 2 # ze 3 replik, max 1 voluntary disruption najednou
|
||||
selector:
|
||||
matchLabels:
|
||||
app: luncher
|
||||
@@ -0,0 +1,14 @@
|
||||
# Šablona — hodnoty jsou zástupné symboly.
|
||||
# Pro kind test vytvoř secret příkazem:
|
||||
# kubectl create secret generic luncher-secrets -n luncher \
|
||||
# --from-literal=JWT_SECRET=<your-secret> \
|
||||
# --from-literal=ADMIN_PASSWORD=<your-password>
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: luncher-secrets
|
||||
namespace: luncher
|
||||
type: Opaque
|
||||
stringData:
|
||||
JWT_SECRET: CHANGE_ME
|
||||
ADMIN_PASSWORD: CHANGE_ME
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: luncher
|
||||
namespace: luncher
|
||||
spec:
|
||||
selector:
|
||||
app: luncher
|
||||
ports:
|
||||
- port: 3001
|
||||
targetPort: 3001
|
||||
@@ -0,0 +1,16 @@
|
||||
kind: Cluster
|
||||
apiVersion: kind.x-k8s.io/v1alpha4
|
||||
nodes:
|
||||
- role: control-plane
|
||||
# Mapuje porty na Windows localhost — luncher.localhost resolves to 127.0.0.1
|
||||
# Traefik na control-plane podu poslouchá na těchto portech přes hostPort
|
||||
extraPortMappings:
|
||||
- containerPort: 80
|
||||
hostPort: 80
|
||||
protocol: TCP
|
||||
- containerPort: 443
|
||||
hostPort: 443
|
||||
protocol: TCP
|
||||
- role: worker
|
||||
- role: worker
|
||||
- role: worker
|
||||
Reference in New Issue
Block a user