fix(renderer): Recreate strategy for Deployments mounting RWO PVCs
Any single-replica app with a `storage:` entry mounts a ReadWriteOnce Longhorn PVC. The default RollingUpdate strategy starts the new pod before terminating the old one, but the old pod still holds the RWO volume -> the new pod hangs in ContainerCreating and the rollout deadlocks (observed on gitea's update). Emit spec.strategy.type=Recreate whenever storage is present so the old pod releases the PVC first. Non-storage apps (and VMs) keep the default (no strategy emitted). Re-rendered apps: gitea, pihole, sonarr, radarr, plex, qbittorrent, jackett now carry strategy: Recreate. Updated the egress-class L1 render goldens (storage fixture) accordingly; all L1 render checks pass. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
73c4475d34
commit
d32b5c4ec0
10 files changed
+36
-11
No files matched your search
@@ -61,6 +61,8 @@ metadata:
|
|||||||
app.kubernetes.io/name: gitea
|
app.kubernetes.io/name: gitea
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: gitea
|
app.kubernetes.io/name: gitea
|
||||||
|
|||||||
+3
-1
@@ -61,6 +61,8 @@ metadata:
|
|||||||
app.kubernetes.io/name: jackett
|
app.kubernetes.io/name: jackett
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: jackett
|
app.kubernetes.io/name: jackett
|
||||||
@@ -123,7 +125,7 @@ spec:
|
|||||||
- port: '9117'
|
- port: '9117'
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
---
|
---
|
||||||
# CiliumNetworkPolicy internet/world egress (vpn class, R-NET-05): jackett-allow-internet-egress
|
# CiliumNetworkPolicy world egress allow (egress class governs the path; R-NET-05): jackett-allow-internet-egress
|
||||||
apiVersion: cilium.io/v2
|
apiVersion: cilium.io/v2
|
||||||
kind: CiliumNetworkPolicy
|
kind: CiliumNetworkPolicy
|
||||||
metadata:
|
metadata:
|
||||||
|
|||||||
+3
-1
@@ -61,6 +61,8 @@ metadata:
|
|||||||
app.kubernetes.io/name: pihole
|
app.kubernetes.io/name: pihole
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: pihole
|
app.kubernetes.io/name: pihole
|
||||||
@@ -123,7 +125,7 @@ spec:
|
|||||||
- port: '80'
|
- port: '80'
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
---
|
---
|
||||||
# CiliumNetworkPolicy internet/world egress (internet class, R-NET-05): pihole-allow-internet-egress
|
# CiliumNetworkPolicy world egress allow (egress class governs the path; R-NET-05): pihole-allow-internet-egress
|
||||||
apiVersion: cilium.io/v2
|
apiVersion: cilium.io/v2
|
||||||
kind: CiliumNetworkPolicy
|
kind: CiliumNetworkPolicy
|
||||||
metadata:
|
metadata:
|
||||||
|
|||||||
+3
-1
@@ -61,6 +61,8 @@ metadata:
|
|||||||
app.kubernetes.io/name: plex
|
app.kubernetes.io/name: plex
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: plex
|
app.kubernetes.io/name: plex
|
||||||
@@ -120,7 +122,7 @@ spec:
|
|||||||
- port: '32400'
|
- port: '32400'
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
---
|
---
|
||||||
# CiliumNetworkPolicy internet/world egress (internet class, R-NET-05): plex-allow-internet-egress
|
# CiliumNetworkPolicy world egress allow (egress class governs the path; R-NET-05): plex-allow-internet-egress
|
||||||
apiVersion: cilium.io/v2
|
apiVersion: cilium.io/v2
|
||||||
kind: CiliumNetworkPolicy
|
kind: CiliumNetworkPolicy
|
||||||
metadata:
|
metadata:
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ metadata:
|
|||||||
app.kubernetes.io/name: qbittorrent
|
app.kubernetes.io/name: qbittorrent
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: qbittorrent
|
app.kubernetes.io/name: qbittorrent
|
||||||
@@ -123,7 +125,7 @@ spec:
|
|||||||
- port: '8080'
|
- port: '8080'
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
---
|
---
|
||||||
# CiliumNetworkPolicy internet/world egress (vpn class, R-NET-05): qbittorrent-allow-internet-egress
|
# CiliumNetworkPolicy world egress allow (egress class governs the path; R-NET-05): qbittorrent-allow-internet-egress
|
||||||
apiVersion: cilium.io/v2
|
apiVersion: cilium.io/v2
|
||||||
kind: CiliumNetworkPolicy
|
kind: CiliumNetworkPolicy
|
||||||
metadata:
|
metadata:
|
||||||
|
|||||||
+3
-1
@@ -61,6 +61,8 @@ metadata:
|
|||||||
app.kubernetes.io/name: radarr
|
app.kubernetes.io/name: radarr
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: radarr
|
app.kubernetes.io/name: radarr
|
||||||
@@ -165,7 +167,7 @@ spec:
|
|||||||
- port: '7878'
|
- port: '7878'
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
---
|
---
|
||||||
# CiliumNetworkPolicy internet/world egress (vpn class, R-NET-05): radarr-allow-internet-egress
|
# CiliumNetworkPolicy world egress allow (egress class governs the path; R-NET-05): radarr-allow-internet-egress
|
||||||
apiVersion: cilium.io/v2
|
apiVersion: cilium.io/v2
|
||||||
kind: CiliumNetworkPolicy
|
kind: CiliumNetworkPolicy
|
||||||
metadata:
|
metadata:
|
||||||
|
|||||||
+3
-1
@@ -61,6 +61,8 @@ metadata:
|
|||||||
app.kubernetes.io/name: sonarr
|
app.kubernetes.io/name: sonarr
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: sonarr
|
app.kubernetes.io/name: sonarr
|
||||||
@@ -165,7 +167,7 @@ spec:
|
|||||||
- port: '8989'
|
- port: '8989'
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
---
|
---
|
||||||
# CiliumNetworkPolicy internet/world egress (vpn class, R-NET-05): sonarr-allow-internet-egress
|
# CiliumNetworkPolicy world egress allow (egress class governs the path; R-NET-05): sonarr-allow-internet-egress
|
||||||
apiVersion: cilium.io/v2
|
apiVersion: cilium.io/v2
|
||||||
kind: CiliumNetworkPolicy
|
kind: CiliumNetworkPolicy
|
||||||
metadata:
|
metadata:
|
||||||
|
|||||||
+12
-5
@@ -243,6 +243,17 @@ def r_deployment(s: dict) -> dict:
|
|||||||
if ann:
|
if ann:
|
||||||
tmpl_meta["annotations"] = ann
|
tmpl_meta["annotations"] = ann
|
||||||
|
|
||||||
|
spec = {"replicas": 1}
|
||||||
|
# An app with persistent storage mounts a ReadWriteOnce Longhorn PVC. The default
|
||||||
|
# RollingUpdate strategy tries to start the NEW pod before terminating the old one, but
|
||||||
|
# the old pod still holds the RWO volume -> the new pod is stuck ContainerCreating and
|
||||||
|
# the rollout deadlocks (single-replica app can never hand the volume over). Recreate
|
||||||
|
# terminates the old pod first, freeing the PVC for the new one (R-STATE-01/02).
|
||||||
|
if s["storage"]:
|
||||||
|
spec["strategy"] = {"type": "Recreate"}
|
||||||
|
spec["selector"] = {"matchLabels": {"app.kubernetes.io/name": s["name"]}}
|
||||||
|
spec["template"] = {"metadata": tmpl_meta, "spec": pod_spec}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"apiVersion": "apps/v1",
|
"apiVersion": "apps/v1",
|
||||||
"kind": "Deployment",
|
"kind": "Deployment",
|
||||||
@@ -251,11 +262,7 @@ def r_deployment(s: dict) -> dict:
|
|||||||
"namespace": s["name"],
|
"namespace": s["name"],
|
||||||
"labels": {"app.kubernetes.io/name": s["name"]},
|
"labels": {"app.kubernetes.io/name": s["name"]},
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": spec,
|
||||||
"replicas": 1,
|
|
||||||
"selector": {"matchLabels": {"app.kubernetes.io/name": s["name"]}},
|
|
||||||
"template": {"metadata": tmpl_meta, "spec": pod_spec},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
@@ -61,6 +61,8 @@ metadata:
|
|||||||
app.kubernetes.io/name: sonarr
|
app.kubernetes.io/name: sonarr
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: sonarr
|
app.kubernetes.io/name: sonarr
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ metadata:
|
|||||||
app.kubernetes.io/name: sonarr
|
app.kubernetes.io/name: sonarr
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: sonarr
|
app.kubernetes.io/name: sonarr
|
||||||
|
|||||||
Reference in new issue
Block a user