apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-mcp
namespace: secure-mcp
labels:
app.kubernetes.io/name: secure-mcp
app.kubernetes.io/component: backend
app.kubernetes.io/version: "1.0.0"
app.kubernetes.io/managed-by: kubectl
annotations:
deployment.kubernetes.io/revision: "1"
spec:
replicas: 3
revisionHistoryLimit: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app.kubernetes.io/name: secure-mcp
app.kubernetes.io/component: backend
template:
metadata:
labels:
app.kubernetes.io/name: secure-mcp
app.kubernetes.io/component: backend
app.kubernetes.io/version: "1.0.0"
version: "v1"
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
# Force pod restart on config/secret changes
checksum/config: "{{ include (print $.Template.BasePath \"/configmap.yaml\") . | sha256sum }}"
checksum/secret: "{{ include (print $.Template.BasePath \"/secret.yaml\") . | sha256sum }}"
spec:
# Service account for pod
serviceAccountName: secure-mcp-sa
# Security context for the pod
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
# Node affinity for production workloads
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- secure-mcp
topologyKey: kubernetes.io/hostname
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: node-type
operator: In
values:
- application
- general
# Topology spread constraints for even distribution
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app.kubernetes.io/name: secure-mcp
# Priority class for critical applications
priorityClassName: high-priority
# DNS configuration
dnsPolicy: ClusterFirst
dnsConfig:
options:
- name: ndots
value: "2"
- name: edns0
# Termination grace period
terminationGracePeriodSeconds: 60
# Init containers
initContainers:
- name: wait-for-db
image: busybox:1.36.1
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'until nc -z ${DB_HOST} ${DB_PORT}; do echo "Waiting for database..."; sleep 2; done']
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: secure-mcp-config
key: DB_HOST
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: secure-mcp-config
key: DB_PORT
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
limits:
cpu: 100m
memory: 64Mi
requests:
cpu: 50m
memory: 32Mi
- name: db-migrate
image: secure-mcp-server:latest
imagePullPolicy: Always
command: ["npm", "run", "db:migrate"]
envFrom:
- configMapRef:
name: secure-mcp-config
- secretRef:
name: secure-mcp-secrets
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
# Main container
containers:
- name: secure-mcp
image: secure-mcp-server:latest
imagePullPolicy: Always
# Container ports
ports:
- name: http
containerPort: 3000
protocol: TCP
- name: metrics
containerPort: 9090
protocol: TCP
# Environment variables
envFrom:
- configMapRef:
name: secure-mcp-config
- secretRef:
name: secure-mcp-secrets
# Additional environment variables
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: DATABASE_URL
value: "postgresql://$(DB_USERNAME):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?schema=public&sslmode=$(DB_SSL_MODE)"
- name: REDIS_URL
value: "redis://:$(REDIS_PASSWORD)@$(REDIS_HOST):$(REDIS_PORT)/$(REDIS_DB)"
# Security context for container
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
# Resource limits and requests
resources:
limits:
cpu: 2000m
memory: 2Gi
ephemeral-storage: 1Gi
requests:
cpu: 500m
memory: 512Mi
ephemeral-storage: 100Mi
# Volume mounts
volumeMounts:
- name: tmp
mountPath: /tmp
- name: app-tmp
mountPath: /app/tmp
- name: app-logs
mountPath: /app/logs
- name: scripts
mountPath: /scripts
readOnly: true
- name: tls-certs
mountPath: /app/certs
readOnly: true
# Liveness probe
livenessProbe:
httpGet:
path: /health/live
port: http
scheme: HTTP
httpHeaders:
- name: X-Health-Check
value: liveness
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
# Readiness probe
readinessProbe:
httpGet:
path: /health/ready
port: http
scheme: HTTP
httpHeaders:
- name: X-Health-Check
value: readiness
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
# Startup probe for slow starting containers
startupProbe:
httpGet:
path: /health/startup
port: http
scheme: HTTP
httpHeaders:
- name: X-Health-Check
value: startup
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 30
# Lifecycle hooks
lifecycle:
preStop:
exec:
command: ["/bin/sh", "/scripts/prestop.sh"]
# Volumes
volumes:
- name: tmp
emptyDir:
sizeLimit: 100Mi
- name: app-tmp
emptyDir:
sizeLimit: 500Mi
- name: app-logs
emptyDir:
sizeLimit: 1Gi
- name: scripts
configMap:
name: secure-mcp-scripts
defaultMode: 0755
- name: tls-certs
secret:
secretName: secure-mcp-tls
defaultMode: 0400