apiVersion: v1 kind: Service metadata: name: mongodb-tenant-headless namespace: ${NAMESPACE} spec: clusterIP: None selector: app: mongodb-tenant ports: - port: 27017 targetPort: 27017 --- apiVersion: apps/v1 kind: StatefulSet metadata: name: mongodb-tenant namespace: ${NAMESPACE} spec: serviceName: mongodb-tenant-headless replicas: 3 podManagementPolicy: Parallel selector: matchLabels: app: mongodb-tenant template: metadata: labels: app: mongodb-tenant spec: imagePullSecrets: - name: registry-codebaker securityContext: # fsGroup garantisce ownership corretta sul PVC (mongo data) per uid 999. # runAsUser/Group: il container principale gira come mongodb (uid 999), # non root. L'init invece deve essere root per poter chmod 400 + chown. runAsUser: 999 runAsGroup: 999 fsGroup: 999 initContainers: # Prepara keyfile e PEM nel tmpfs /run/mongo con owner=mongodb mode=0400. # Mongod richiede esattamente 0400/0600 owned-by-self e quel mode non si # può ottenere via secret defaultMode senza far girare il container come # root. Quindi: init come root, main come 999. - name: init-tls image: mongo:6.0 securityContext: runAsUser: 0 runAsGroup: 0 command: - /bin/sh - -c - >- cp /etc/secrets/keyfile /run/mongo/mongodb-keyfile && cat /etc/mongo-tls/tls.crt /etc/mongo-tls/tls.key > /run/mongo/mongo-server.pem && chown 999:999 /run/mongo/mongodb-keyfile /run/mongo/mongo-server.pem && chmod 400 /run/mongo/mongodb-keyfile /run/mongo/mongo-server.pem volumeMounts: - name: mongo-runtime mountPath: /run/mongo - name: mongo-keyfile mountPath: /etc/secrets readOnly: true - name: mongo-tls mountPath: /etc/mongo-tls readOnly: true affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: mongodb-tenant topologyKey: kubernetes.io/hostname containers: - name: mongodb image: mongo:6.0 # I file runtime (keyfile + PEM cert+key) sono già stati preparati dall' # initContainer in /run/mongo con owner mongodb:mongodb mode 0400. command: - mongod - --bind_ip_all - --replSet - rs0 - --auth - --keyFile - /run/mongo/mongodb-keyfile - --tlsMode - requireTLS - --tlsCertificateKeyFile - /run/mongo/mongo-server.pem - --tlsCAFile - /etc/mongo-tls/ca.crt # Senza questo flag, --tlsCAFile mette mongod in mTLS e rifiuta ogni # client che non presenta un cert. I membri del replica set si autenticano # via keyFile (clusterAuthMode keyFile, default), non x.509: quindi non # serve mTLS. I client (API, probe, mongosh) presentano solo password. - --tlsAllowConnectionsWithoutCertificates ports: - containerPort: 27017 volumeMounts: - name: mongodb-data mountPath: /data/db - name: mongo-runtime mountPath: /run/mongo readOnly: true - name: mongo-tls mountPath: /etc/mongo-tls readOnly: true resources: requests: cpu: "${MONGO_CPU_REQUEST}" memory: "${MONGO_MEM_REQUEST}" limits: cpu: "${MONGO_CPU_LIMIT}" memory: "${MONGO_MEM_LIMIT}" # quit(N) propaga N come exit code: probe fallisce solo se ping.ok != 1. # Connessione su 127.0.0.1 con --tlsAllowInvalidHostnames perché il cert # non include localhost nei SAN (la CA però viene validata). livenessProbe: exec: command: - mongosh - --quiet - --tls - --tlsCAFile - /etc/mongo-tls/ca.crt - --tlsAllowInvalidHostnames - --host - "127.0.0.1" - --eval - "quit(db.adminCommand('ping').ok===1?0:1)" initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 readinessProbe: exec: command: - mongosh - --quiet - --tls - --tlsCAFile - /etc/mongo-tls/ca.crt - --tlsAllowInvalidHostnames - --host - "127.0.0.1" - --eval - "quit(db.adminCommand('ping').ok===1?0:1)" initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 5 volumes: - name: mongo-runtime emptyDir: medium: Memory sizeLimit: 16Mi - name: mongo-keyfile secret: secretName: mongodb-tenant-auth items: - key: keyfile path: keyfile # Con fsGroup=999 il file è root:mongodb 0440 — leggibile solo dal gruppo # mongodb (uid/gid 999), non world-readable. Il container poi lo copia in # /data/db/ e fa chmod 400 perché mongod richiede mode 0400/0600 owner-only. defaultMode: 0440 - name: mongo-tls secret: # Secret popolato da cert-manager (vedi templates/01-mongodb-tls.yaml). # Contiene tls.crt, tls.key, ca.crt — leggibili dal gruppo 999 via fsGroup. secretName: mongodb-tenant-tls defaultMode: 0440 volumeClaimTemplates: - metadata: name: mongodb-data spec: accessModes: ["ReadWriteOnce"] storageClassName: "${STORAGE_CLASS}" resources: requests: storage: ${STORAGE_SIZE} --- apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: mongodb-tenant-pdb namespace: ${NAMESPACE} spec: # Il replica set tollera la perdita di 1 membro (quorum = 2/3). # minAvailable: 2 evita che drain/voluntary disruption tolga il quorum. minAvailable: 2 selector: matchLabels: app: mongodb-tenant # NB: rs.initiate e la creazione degli utenti root/app sono eseguite da # `deploy.sh` via `kubectl exec` nel pod mongodb-tenant-0 (sfrutta la # "localhost exception" di Mongo per creare il primo utente senza auth).