indicat est une application web conteneurisée : une image, , qui écoute sur , a besoin d'un DATABASE_URL et d'une SECRET_KEY dans son environnement, et répond sur /healthz quand elle va bien. Cette page lui donne un namespace, ses secrets, un PostgreSQL, réplicas avec sondes et limites, des migrations qui tournent avant chaque rollout, un Ingress avec TLS sur , et un autoscaler.
Avant de commencer
$openssl rand -hex 32 # APP_SECRET$openssl rand -hex 16 # DB_PASSWORDNamespace et secrets
$kubectl create namespace --dry-run=client -o yaml | kubectl apply -f -$kubectl -n create secret generic indicat-db-app --type=kubernetes.io/basic-auth \$ --from-literal=username=indicat --from-literal=password="" \$ --dry-run=client -o yaml | kubectl apply -f -$kubectl -n create secret generic indicat-secrets \$ --from-literal=DATABASE_URL="postgresql://indicat:@indicat-db-rw:5432/indicat" \$ --from-literal=SECRET_KEY="" \$ --dry-run=client -o yaml | kubectl apply -f -PostgreSQL
$helm repo add cnpg https://cloudnative-pg.github.io/charts --force-update$helm upgrade --install cnpg cnpg/cloudnative-pg --namespace cnpg-system --create-namespace --waitapiVersion: postgresql.cnpg.io/v1kind: Clustermetadata: name: indicat-dbspec: instances: 1 imageName: ghcr.io/cloudnative-pg/postgresql:16 storage: size: 10Gi storageClass: local-path bootstrap: initdb: database: indicat owner: indicat secret: name: indicat-db-app$kubectl -n apply -f deploy/postgres.yaml$kubectl -n get cluster indicat-db -w$kubectl -n get cluster indicat-db -o jsonpath='{.status.phase}'Cluster in healthy state
Pull secret
$kubectl -n create secret docker-registry regcred \$ --docker-server=$(echo | cut -d/ -f1) \$ --docker-username= --docker-password="" \$ --dry-run=client -o yaml | kubectl apply -f -$kubectl -n patch serviceaccount default -p '{"imagePullSecrets":[{"name":"regcred"}]}'$kubectl -n get serviceaccount default -o jsonpath='{.imagePullSecrets[0].name}'regcred
Migrations
apiVersion: batch/v1kind: Jobmetadata: name: indicat-migrate labels: { app: indicat-migrate }spec: backoffLimit: 2 ttlSecondsAfterFinished: 600 template: spec: restartPolicy: Never containers: - name: migrate image: command: ["npm", "run", "migrate"] envFrom: - secretRef: { name: indicat-secrets }$kubectl -n delete job indicat-migrate --ignore-not-found$kubectl -n apply -f deploy/migrate.yaml$kubectl -n wait --for=condition=complete job/indicat-migrate --timeout=300s$kubectl -n wait --for=condition=complete job/indicat-migrate --timeout=10sjob.batch/indicat-migrate condition met
L'application
apiVersion: apps/v1kind: Deploymentmetadata: name: indicatspec: replicas: selector: matchLabels: { app: indicat } strategy: type: RollingUpdate rollingUpdate: { maxUnavailable: 0, maxSurge: 1 } template: metadata: labels: { app: indicat } spec: containers: - name: indicat image: ports: - containerPort: envFrom: - secretRef: { name: indicat-secrets } readinessProbe: httpGet: { path: /healthz, port: } periodSeconds: 5 livenessProbe: httpGet: { path: /healthz, port: } initialDelaySeconds: 15 periodSeconds: 10 resources: requests: { cpu: 100m, memory: 128Mi } limits: { cpu: 500m, memory: 256Mi }$kubectl -n apply -f deploy/deployment.yaml -f deploy/service.yaml -f deploy/ingress.yaml$kubectl -n rollout status deploy/indicat$kubectl -n get pods -l app=indicat --field-selector=status.phase=Running --no-headers | wc -l$curl -s -o /dev/null -w '%{http_code}' https:///healthz200
Si les pods ne passent jamais Ready
Commence par les événements et les logs :
$kubectl -n describe pod -l app=indicat | tail -20$kubectl -n logs -l app=indicat --tail=50Par ordre de probabilité :
ImagePullBackOff: mauvais nom ou tag d'image, ou pull secret manquant ou expiré.Readiness probe failed: connection refused: le processus n'écoute pas sur, ou pas sur toutes les interfaces (0.0.0.0).- Les logs montrent une erreur de base :
DATABASE_URLa le mauvais hôte ou mot de passe. Décode le Secret pour voir ce que voit le pod :kubectl get secret indicat-secrets -o jsonpath='{.data.DATABASE_URL}' | base64 -d. OOMKilleddans le statut du pod : montelimits.memory.
Autoscaling
apiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata: name: indicatspec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: indicat minReplicas: maxReplicas: 6 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70$kubectl -n apply -f deploy/hpa.yaml$kubectl -n top pods -l app=indicat --no-headers | wc -lAu quotidien : rollout, rollback, logs, exec
$kubectl -n rollout status deploy/indicat$kubectl -n rollout history deploy/indicat$kubectl -n rollout undo deploy/indicat$kubectl -n logs -l app=indicat --tail=100 -f$kubectl -n exec -it deploy/indicat -- shPlus d'un réplica
Avec pods, trois choses qui étaient gratuites sur un serveur demandent une décision : les sessions, les migrations, et ce qui se passe pendant la maintenance d'un nœud.
Terminé
indicat répond sur depuis pods, avec sa base, ses secrets, des migrations qui tournent avant chaque rollout, et un autoscaler. Chaque objet est un fichier dans deploy/, exactement ce dont la page suivante a besoin : un pipeline qui construit l'image à chaque push, la tague avec le commit, et applique ces fichiers avec un ServiceAccount qui ne peut toucher qu'à .