Symptôme observé : `kadans.arcodange.fr` et `gitea.arcodange.fr` en 403, corps vide, depuis n'importe quel client extérieur — pendant que `arcodange.fr` et `www` répondaient 200. Ça ressemble à un bannissement d'IP. Ça n'en était pas un. La chaîne, remontée de bout en bout : 1. Le sous-chart fige ses sondes à `timeoutSeconds: 1` sur `redis-cli ping` et n'expose aucune valeur pour les surcharger. 2. Sans réservation de ressources, le conteneur est en QoS `BestEffort` : sur un Raspberry Pi chargé, lancer `redis-cli` dépasse la seconde. 3. « Liveness probe failed: command timed out after 1s » 836 fois en 23 jours, 135 redémarrages, SIGTERM après ~90 s de vie à chaque tour. 4. Le plugin crowdsec de Traefik utilise ce Redis comme cache de décisions. Cache injoignable ⇒ `isCrowdsecStreamHealthy:false` ⇒ refus par défaut de tout client absent de `clientTrustedIPs` ⇒ le 403. D'où le motif trompeur : seuls les hôtes portant le middleware crowdsec tombaient, et depuis le LAN (IP de confiance) tout paraissait sain — l'origine répondait 401, son défi d'authentification normal. Trois fausses pistes en sont sorties : Cloudflare, un bannissement crowdsec, une règle de pare-feu. Aucune n'était la bonne, et `cscli decisions list` disait `isBanned:false` du début à la fin. La réservation fait passer le conteneur en `Burstable` et lui garantit sa part. Les limites restent larges : Redis n'est pas ce qui sature ce nœud. ⚠ Ce commit ne corrige PAS la sonde elle-même — elle reste à 1 s, et elle reste inatteignable depuis les valeurs. Si le clignotement revient malgré la réservation, il faudra un patch Kustomize au niveau de l'Application ArgoCD, ou abandonner ce sous-chart. Une vérification en production a été appliquée à chaud (`timeoutSeconds: 5`, `failureThreshold: 6`) pour rétablir l'accès immédiatement, mais `selfHeal: true` la ré-écrasera : elle n'est pas la correction, ce commit l'est. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GdUCA5Uz8QyMwa2P4Pg2hK
221 lines
7.8 KiB
YAML
221 lines
7.8 KiB
YAML
redis: &redis_config
|
|
image:
|
|
# -- The repository to pull the image from.
|
|
repository: redis
|
|
# -- The docker tag, if left empty chart's appVersion will be used.
|
|
# @default -- `.Chart.AppVersion`
|
|
tag: ''
|
|
# -- The pull policy for the controller.
|
|
pullPolicy: IfNotPresent
|
|
|
|
# -- Optionally supply image pull secrets.
|
|
imagePullSecrets: []
|
|
|
|
nameOverride: ''
|
|
fullnameOverride: ''
|
|
|
|
controller:
|
|
# -- Create a workload for this chart.
|
|
enabled: true
|
|
# -- Type of the workload object.
|
|
kind: StatefulSet
|
|
# -- The number of replicas.
|
|
replicas: 1
|
|
# -- Additional annotations for the controller object.
|
|
annotations: {}
|
|
# -- Additional labels for the controller object.
|
|
labels: {}
|
|
|
|
service:
|
|
# -- Create a service for exposing this chart.
|
|
enabled: true
|
|
# -- The service type used.
|
|
type: ClusterIP
|
|
# -- ClusterIP used if service type is `ClusterIP`.
|
|
clusterIP: ''
|
|
# -- LoadBalancerIP if service type is `LoadBalancer`.
|
|
loadBalancerIP: ''
|
|
# -- Allowed addresses when service type is `LoadBalancer`.
|
|
loadBalancerSourceRanges: []
|
|
# -- Additional annotations for the service object.
|
|
annotations: {}
|
|
# -- Additional labels for the service object.
|
|
labels: {}
|
|
|
|
serviceMonitor:
|
|
# -- Create a service monitor for prometheus operator.
|
|
enabled: false
|
|
# -- How frequently the exporter should be scraped.
|
|
interval: 30s
|
|
# -- Timeout value for individual scrapes.
|
|
timeout: 10s
|
|
# -- Additional annotations for the service monitor object.
|
|
annotations: {}
|
|
# -- Additional labels for the service monitor object.
|
|
labels: {}
|
|
|
|
redisExporter:
|
|
# -- Enable optional redis exporter instance as sidecar container.
|
|
enabled: false
|
|
# -- Image for the metric exporter
|
|
image:
|
|
# -- The repository to pull the image from.
|
|
repository: oliver006/redis_exporter
|
|
# -- The docker tag, if left empty latest will be used.
|
|
# @default -- `latest`
|
|
tag: 'latest'
|
|
# -- The pull policy for the exporter.
|
|
pullPolicy: IfNotPresent
|
|
# -- Pod-level security attributes. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context).
|
|
securityContext:
|
|
runAsUser: 59000
|
|
runAsGroup: 59000
|
|
allowPrivilegeEscalation: false
|
|
capabilities:
|
|
drop:
|
|
- ALL
|
|
# -- Compute resources used by the container. More info [here](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/).
|
|
resources:
|
|
requests:
|
|
cpu: 10m
|
|
memory: 50Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 100Mi
|
|
|
|
env:
|
|
# -- Timezone for the container.
|
|
- name: TZ
|
|
value: Europe/Paris
|
|
|
|
# -- List of extra arguments for the container.
|
|
extraArgs: []
|
|
# - --loglevel warning
|
|
|
|
ports:
|
|
redis:
|
|
# -- Enable the port inside the `Controller` and `Service` objects.
|
|
enabled: true
|
|
# -- The port used as internal port and cluster-wide port if `.service.type` == `ClusterIP`.
|
|
port: 6379
|
|
# -- The external port used if `.service.type` == `NodePort`.
|
|
nodePort: null
|
|
# -- The protocol used for the service.
|
|
protocol: TCP
|
|
# -- The application protocol for this port. Used as hint for implementations to offer richer behavior.
|
|
appProtocol: redis
|
|
|
|
persistentVolumeClaim:
|
|
# -- Create a new persistent volume claim object.
|
|
create: true
|
|
# -- Mount path of the persistent volume claim object.
|
|
mountPath: /data
|
|
# -- Access mode of the persistent volume claim object.
|
|
accessMode: ReadWriteOnce
|
|
# -- Volume mode of the persistent volume claim object.
|
|
volumeMode: Filesystem
|
|
# -- Storage request size for the persistent volume claim object.
|
|
size: 1Gi
|
|
# -- Storage class name for the persistent volume claim object.
|
|
storageClassName: ''
|
|
# -- Use an existing persistent volume claim object.
|
|
existingPersistentVolumeClaim: ''
|
|
# -- Additional annotations for the persistent volume claim object.
|
|
annotations: {}
|
|
# -- Additional labels for the persistent volume claim object.
|
|
labels: {}
|
|
|
|
serviceAccount:
|
|
# -- Specify the service account used for the controller.
|
|
name: ''
|
|
|
|
# -- Optional priority class name to be used for pods.
|
|
priorityClassName: ''
|
|
|
|
# -- Pod-level security attributes. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context).
|
|
securityContext:
|
|
fsGroup: 999
|
|
runAsNonRoot: true
|
|
runAsGroup: 999
|
|
runAsUser: 999
|
|
|
|
# -- Compute resources used by the container. More info [here](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/).
|
|
#
|
|
# ⚠ CE N'EST PAS DU CONFORT — c'est ce qui empêche Redis de mourir en boucle.
|
|
#
|
|
# Le sous-chart FIGE ses sondes à `timeoutSeconds: 1` sur `redis-cli ping`, et
|
|
# n'expose aucune valeur pour les surcharger. Sur ce matériel (Raspberry Pi),
|
|
# un conteneur SANS réservation tombe dans la classe QoS `BestEffort` : il est
|
|
# le premier affamé quand le nœud est chargé, et le simple lancement de
|
|
# `redis-cli` y dépasse la seconde.
|
|
#
|
|
# Mesuré le 2026-07-29 sur `redis-0` : « Liveness probe failed: command timed
|
|
# out: "redis-cli ping" timed out after 1s » **836 fois en 23 jours**, 135
|
|
# redémarrages, le conteneur tué par SIGTERM après ~90 s de vie à chaque tour.
|
|
#
|
|
# ⚠ CE QUE ÇA CASSAIT, ET QUI N'AVAIT RIEN À VOIR AVEC REDIS EN APPARENCE : le
|
|
# plugin crowdsec de Traefik utilise ce Redis comme cache de décisions. Cache
|
|
# injoignable ⇒ `isCrowdsecStreamHealthy:false` ⇒ le plugin REFUSE PAR DÉFAUT
|
|
# tout client non listé dans `clientTrustedIPs` ⇒ **403, corps vide, sur
|
|
# `kadans.arcodange.fr` et `gitea.arcodange.fr`**, pendant que les hôtes qui ne
|
|
# portent pas ce middleware répondaient normalement. Le symptôme ne nomme ni
|
|
# Redis, ni crowdsec, ni la sonde — il ressemble à un bannissement d'IP, et
|
|
# c'est par là qu'on cherche d'abord.
|
|
#
|
|
# Une réservation fait passer le conteneur en `Burstable` et lui garantit sa
|
|
# part. Les limites restent larges : Redis n'est pas ce qui sature ce nœud.
|
|
resources:
|
|
requests:
|
|
cpu: 50m
|
|
memory: 64Mi
|
|
limits:
|
|
memory: 256Mi
|
|
|
|
# -- Pod-level affinity. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling).
|
|
affinity: {}
|
|
# nodeAffinity:
|
|
# requiredDuringSchedulingIgnoredDuringExecution:
|
|
# nodeSelectorTerms:
|
|
# - matchExpressions:
|
|
# - key: kubernetes.io/hostname
|
|
# operator: In
|
|
# values:
|
|
# - my-node-xyz
|
|
|
|
# -- Pod-level tolerations. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling).
|
|
tolerations: []
|
|
# - key: node-role.kubernetes.io/control-plane
|
|
# operator: Exists
|
|
# effect: NoSchedule
|
|
|
|
# -- Pod-level node selector. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling).
|
|
nodeSelector: {}
|
|
# label: value
|
|
|
|
# -- Specify any extra containers here as dictionary items - each should have its own key.
|
|
extraContainers: {}
|
|
# container:
|
|
# name: my-container
|
|
# image: my-org/my-image
|
|
|
|
# -- Specify extra volume mounts for the default containers.
|
|
extraVolumeMounts: []
|
|
# - name: my-volume
|
|
# mountPath: /path/to/volume
|
|
# readOnly: false
|
|
|
|
# -- Specify extra volumes for the workload.
|
|
extraVolumes: []
|
|
# - name: my-volume
|
|
# secret:
|
|
# secretName: my-secret
|
|
|
|
|
|
tool:
|
|
# kind: 'SubChart' or 'HelmChart', if subchart then uncomment Chart.yaml dependency, else comment and use tool library with helm chart template
|
|
kind: 'SubChart'
|
|
repo: https://charts.pascaliske.dev
|
|
chart: redis
|
|
version: 2.1.0
|
|
values: *redis_config
|