Find the Best Cosmetic Hospitals

Explore trusted cosmetic hospitals and make a confident choice for your transformation.

ā€œInvest in yourself — your confidence is always worth it.ā€

Explore Cosmetic Hospitals

Start your journey today — compare options in one place.

AWS EKS – Cluster-Level Multi-AZ Enforcement Solutions

āŒ What’s NOT Available in EKS

  • Kubernetes Scheduler Configuration: EKS doesn’t allow modification ofĀ KubeSchedulerConfigurationĀ to set custom default topology spread constraints
  • Built-in Cluster Defaults: No native EKS setting to enforce multi-AZ distribution automatically

āœ… Available Solutions (Ranked by Effectiveness)

Solution 1: Mutating Admission Webhook (Recommended)

This automatically injects topology spread constraints into all deployments at creation time.

A. Deploy the Mutating Webhook

# mutating-webhook-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: multiaz-webhook
  namespace: kube-system
  labels:
    app: multiaz-webhook
spec:
  replicas: 2
  selector:
    matchLabels:
      app: multiaz-webhook
  template:
    metadata:
      labels:
        app: multiaz-webhook
    spec:
      serviceAccountName: multiaz-webhook
      containers:
      - name: webhook
        image: your-registry/multiaz-webhook:latest
        ports:
        - containerPort: 8443
        env:
        - name: TLS_CERT_FILE
          value: /etc/certs/tls.crt
        - name: TLS_PRIVATE_KEY_FILE
          value: /etc/certs/tls.key
        volumeMounts:
        - name: certs
          mountPath: /etc/certs
          readOnly: true
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 200m
            memory: 256Mi
      volumes:
      - name: certs
        secret:
          secretName: multiaz-webhook-certs
---
apiVersion: v1
kind: Service
metadata:
  name: multiaz-webhook-service
  namespace: kube-system
spec:
  selector:
    app: multiaz-webhook
  ports:
  - port: 443
    targetPort: 8443
    protocol: TCP
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: multiaz-webhook
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: multiaz-webhook
rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "list"]
- apiGroups: ["apps"]
  resources: ["deployments", "replicasets"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: multiaz-webhook
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: multiaz-webhook
subjects:
- kind: ServiceAccount
  name: multiaz-webhook
  namespace: kube-system

B. Webhook Configuration

# mutating-webhook-config.yaml
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionWebhook
metadata:
  name: multiaz-enforcer
webhooks:
- name: multiaz.enforcer.eks.aws
  clientConfig:
    service:
      name: multiaz-webhook-service
      namespace: kube-system
      path: "/mutate"
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: ["apps"]
    apiVersions: ["v1"]
    resources: ["deployments"]
  - operations: ["CREATE", "UPDATE"]
    apiGroups: ["apps"]
    apiVersions: ["v1"]
    resources: ["replicasets"]
  namespaceSelector:
    matchExpressions:
    - key: name
      operator: NotIn
      values: ["kube-system", "kube-public", "kube-node-lease"]
  objectSelector:
    matchExpressions:
    - key: multiaz.enforcer.eks.aws/skip
      operator: DoesNotExist
  admissionReviewVersions: ["v1", "v1beta1"]
  sideEffects: None
  failurePolicy: Fail

C. Sample Webhook Code (Go)

// webhook-server.go
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "net/http"
    
    admissionv1 "k8s.io/api/admission/v1"
    appsv1 "k8s.io/api/apps/v1"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/runtime"
)

type WebhookServer struct {
    server *http.Server
}

func (ws *WebhookServer) mutate(w http.ResponseWriter, r *http.Request) {
    var body []byte
    if r.Body != nil {
        if data, err := ioutil.ReadAll(r.Body); err == nil {
            body = data
        }
    }

    var admissionResponse *admissionv1.AdmissionResponse
    ar := admissionv1.AdmissionReview{}
    
    if err := json.Unmarshal(body, &ar); err != nil {
        admissionResponse = &admissionv1.AdmissionResponse{
            Result: &metav1.Status{
                Message: err.Error(),
            },
        }
    } else {
        admissionResponse = ws.mutateDeployment(&ar)
    }

    admissionReview := admissionv1.AdmissionReview{}
    if admissionResponse != nil {
        admissionReview.Response = admissionResponse
        if ar.Request != nil {
            admissionReview.Response.UID = ar.Request.UID
        }
    }

    respBytes, _ := json.Marshal(admissionReview)
    w.Header().Set("Content-Type", "application/json")
    w.Write(respBytes)
}

func (ws *WebhookServer) mutateDeployment(ar *admissionv1.AdmissionReview) *admissionv1.AdmissionResponse {
    req := ar.Request
    var deployment appsv1.Deployment
    
    if err := json.Unmarshal(req.Object.Raw, &deployment); err != nil {
        return &admissionv1.AdmissionResponse{
            Result: &metav1.Status{
                Message: err.Error(),
            },
        }
    }

    // Check if topology spread constraints already exist
    if hasTopologySpreadConstraints(&deployment) {
        return &admissionv1.AdmissionResponse{Allowed: true}
    }

    // Add multi-AZ topology spread constraints
    patches := createMultiAZPatches(&deployment)
    
    patchBytes, _ := json.Marshal(patches)
    
    return &admissionv1.AdmissionResponse{
        Allowed: true,
        Patch:   patchBytes,
        PatchType: func() *admissionv1.PatchType {
            pt := admissionv1.PatchTypeJSONPatch
            return &pt
        }(),
    }
}

func hasTopologySpreadConstraints(deployment *appsv1.Deployment) bool {
    return len(deployment.Spec.Template.Spec.TopologySpreadConstraints) > 0
}

func createMultiAZPatches(deployment *appsv1.Deployment) []map[string]interface{} {
    patches := []map[string]interface{}{}
    
    // Add topology spread constraints for AZ distribution
    topologyConstraints := []corev1.TopologySpreadConstraint{
        {
            MaxSkew:           1,
            TopologyKey:       "topology.kubernetes.io/zone",
            WhenUnsatisfiable: corev1.DoNotSchedule,
            MinDomains:        func() *int32 { i := int32(3); return &i }(),
            LabelSelector: &metav1.LabelSelector{
                MatchLabels: deployment.Spec.Selector.MatchLabels,
            },
        },
        {
            MaxSkew:           2,
            TopologyKey:       "kubernetes.io/hostname",
            WhenUnsatisfiable: corev1.ScheduleAnyway,
            LabelSelector: &metav1.LabelSelector{
                MatchLabels: deployment.Spec.Selector.MatchLabels,
            },
        },
    }
    
    patches = append(patches, map[string]interface{}{
        "op":    "add",
        "path":  "/spec/template/spec/topologySpreadConstraints",
        "value": topologyConstraints,
    })
    
    return patches
}

Solution 2: OPA Gatekeeper (Policy-Based Approach)

This validates and can mutate deployments to enforce multi-AZ distribution.

A. Install Gatekeeper

kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.14/deploy/gatekeeper.yaml
Code language: JavaScript (javascript)

B. Create Constraint Template

# gatekeeper-multiaz-template.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8smultiazrequired
spec:
  crd:
    spec:
      names:
        kind: K8sMultiAZRequired
      validation:
        openAPIV3Schema:
          type: object
          properties:
            message:
              type: string
            exemptNamespaces:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8smultiazrequired

        violation[{"msg": msg}] {
          input.review.kind.kind == "Deployment"
          input.review.object.spec.replicas > 1
          not input.review.object.spec.template.spec.topologySpreadConstraints
          not is_exempt_namespace
          msg := sprintf("Deployment %s must have topology spread constraints for multi-AZ distribution", [input.review.object.metadata.name])
        }

        violation[{"msg": msg}] {
          input.review.kind.kind == "Deployment"
          input.review.object.spec.replicas > 1
          input.review.object.spec.template.spec.topologySpreadConstraints
          not has_zone_topology_constraint
          not is_exempt_namespace
          msg := sprintf("Deployment %s must have zone topology spread constraint", [input.review.object.metadata.name])
        }

        has_zone_topology_constraint {
          constraint := input.review.object.spec.template.spec.topologySpreadConstraints[_]
          constraint.topologyKey == "topology.kubernetes.io/zone"
        }

        is_exempt_namespace {
          exempt_namespaces := input.parameters.exemptNamespaces
          input.review.object.metadata.namespace == exempt_namespaces[_]
        }

C. Create Constraint

# gatekeeper-multiaz-constraint.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sMultiAZRequired
metadata:
  name: must-have-multiaz-topology
spec:
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment"]
    excludedNamespaces: ["kube-system", "kube-public", "gatekeeper-system"]
  parameters:
    message: "All deployments with more than 1 replica must have multi-AZ topology spread constraints"
    exemptNamespaces: ["kube-system", "kube-public", "gatekeeper-system"]

Solution 3: Kyverno (Alternative Policy Engine)

A. Install Kyverno

kubectl create -f https://github.com/kyverno/kyverno/releases/latest/download/install.yaml
Code language: JavaScript (javascript)

B. Create Multi-AZ Policy

# kyverno-multiaz-policy.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-multiaz-topology
spec:
  validationFailureAction: enforce
  background: true
  rules:
  - name: check-multiaz-topology
    match:
      any:
      - resources:
          kinds:
          - Deployment
          namespaces:
          - "!kube-system"
          - "!kube-public"
          - "!kyverno"
    validate:
      message: "Deployments with replicas > 1 must have topology spread constraints for multi-AZ distribution"
      pattern:
        spec:
          =(replicas): "1"
  - name: require-zone-topology
    match:
      any:
      - resources:
          kinds:
          - Deployment
          namespaces:
          - "!kube-system"
          - "!kube-public"
          - "!kyverno"
    validate:
      message: "Deployments with replicas > 1 must have zone topology spread constraints"
      anyPattern:
      - spec:
          replicas: 1
      - spec:
          template:
            spec:
              topologySpreadConstraints:
              - topologyKey: "topology.kubernetes.io/zone"
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-multiaz-topology
spec:
  validationFailureAction: enforce
  background: false
  rules:
  - name: add-topology-constraints
    match:
      any:
      - resources:
          kinds:
          - Deployment
          namespaces:
          - "!kube-system"
          - "!kube-public"
          - "!kyverno"
    preconditions:
      all:
      - key: "{{ request.object.spec.replicas }}"
        operator: GreaterThan
        value: 1
      - key: "{{ request.object.spec.template.spec.topologySpreadConstraints || `[]` | length(@) }}"
        operator: Equals
        value: 0
    mutate:
      patchStrategicMerge:
        spec:
          template:
            spec:
              topologySpreadConstraints:
              - maxSkew: 1
                topologyKey: "topology.kubernetes.io/zone"
                whenUnsatisfiable: DoNotSchedule
                minDomains: 3
                labelSelector:
                  matchLabels:
                    "{{ request.object.spec.selector.matchLabels }}"
              - maxSkew: 2
                topologyKey: "kubernetes.io/hostname"
                whenUnsatisfiable: ScheduleAnyway
                labelSelector:
                  matchLabels:
                    "{{ request.object.spec.selector.matchLabels }}"

Solution 4: Namespace-Level Defaults with LimitRanges

While not directly enforcing topology constraints, you can use this approach combined with other solutions:

# namespace-defaults.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: multiaz-defaults
  namespace: production
spec:
  limits:
  - default:
      cpu: "500m"
      memory: "512Mi"
    defaultRequest:
      cpu: "100m"
      memory: "128Mi"
    type: Container
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: multiaz-quota
  namespace: production
spec:
  hard:
    requests.cpu: "10"
    requests.memory: 20Gi
    limits.cpu: "20"
    limits.memory: 40Gi
    pods: "50"

Implementation Guide

Step 1: Choose Your Approach

For Maximum Control: Use Mutating Admission Webhook For Policy Management: Use Kyverno (easier) or OPA Gatekeeper (more powerful) For Simple Validation: Use Gatekeeper validation only

Step 2: Deploy the Solution

# For Kyverno (Recommended for simplicity)
kubectl create -f https://github.com/kyverno/kyverno/releases/latest/download/install.yaml
kubectl apply -f kyverno-multiaz-policy.yaml

# Verify installation
kubectl get clusterpolicy
Run in CloudShell

Step 3: Test the Enforcement

# test-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-app
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: test-app
  template:
    metadata:
      labels:
        app: test-app
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
# This should either be automatically modified (mutating webhook/Kyverno)
# or rejected (validation-only policies)
kubectl apply -f test-deployment.yaml

# Check if topology constraints were added
kubectl get deployment test-app -o yaml | grep -A 20 topologySpreadConstraints
Run in CloudShell

Step 4: Create Exemption Mechanism

# For deployments that should skip multi-AZ enforcement
apiVersion: apps/v1
kind: Deployment
metadata:
  name: single-az-app
  namespace: default
  annotations:
    multiaz.enforcer.eks.aws/skip: "true"  # For webhook
    policies.kyverno.io/skip: "true"       # For Kyverno
spec:
  replicas: 1
  # ... rest of deployment

Monitoring and Validation

Check Policy Compliance

# For Kyverno
kubectl get cpol
kubectl describe cpol require-multiaz-topology

# For Gatekeeper
kubectl get constraints
kubectl describe k8smultiazrequired must-have-multiaz-topology

# Check violations
kubectl get events --field-selector reason=PolicyViolation
Run in CloudShell

Validate Existing Deployments

#!/bin/bash
# check-multiaz-compliance.sh

echo "Checking Multi-AZ compliance for all deployments..."

kubectl get deployments --all-namespaces -o json | jq -r '
.items[] | 
select(.spec.replicas > 1) |
select(.spec.template.spec.topologySpreadConstraints == null or 
       (.spec.template.spec.topologySpreadConstraints | 
        map(select(.topologyKey == "topology.kubernetes.io/zone")) | 
        length == 0)) |
"\(.metadata.namespace)/\(.metadata.name) - Missing multi-AZ topology constraints"
'
Run in CloudShell

Recommended Implementation

For your EKS Auto Mode cluster, I recommend:

  1. Start with KyvernoĀ – easier to implement and maintain
  2. Use the mutating policyĀ to automatically add topology constraints
  3. Set up monitoringĀ to track compliance
  4. Create exemption mechanismsĀ for special cases
  5. Test thoroughlyĀ in a non-production environment first

This approach ensures that every deployment with more than 1 replica automatically gets multi-AZ distribution without requiring developers to remember to add topology spread constraints manually.

Sources

Spread workloads across nodes and Availability Zones – AWS Prescriptive Guidance 

Use admission controllers to enforce security policies – Security Practices for Multi-Tenant SaaS Applications using Amazon EKS 

Running highly-available applications – Amazon EKS 

EKS Control Plane – Amazon EKS 

Tenant Isolation – Amazon EKS 

Managing webhook failures on Amazon EKS | AWS re:Post 

Pod Security – Amazon EKS 

TopologySpreadConstraints in EKS auto mode does not seem to work | AWS re:Post 

Troubleshoot cluster scaling in Amazon EKS with Karpenter autoscaler | AWS re:Post 

Image security – Amazon EKS 

Scheduling – Containers on AWS 

Kubernetes Data Plane – Amazon EKS 

Customizing scheduling on Amazon EKS | Containers 

EKS Data Plane – Amazon EKS 

VPC and Subnet Considerations – Amazon EKS 

Manage CoreDNS for DNS in Amazon EKS clusters – Amazon EKS 

Find Trusted Cardiac Hospitals

Compare heart hospitals by city and services — all in one place.

Explore Hospitals
Subscribe
Notify of
guest
0 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments

Certification Courses

DevOpsSchool has introduced a series of professional certification courses designed to enhance your skills and expertise in cutting-edge technologies and methodologies. Whether you are aiming to excel in development, security, or operations, these certifications provide a comprehensive learning experience. Explore the following programs:

DevOps Certification, SRE Certification, and DevSecOps Certification by DevOpsSchool

Explore our DevOps Certification, SRE Certification, and DevSecOps Certification programs at DevOpsSchool. Gain the expertise needed to excel in your career with hands-on training and globally recognized certifications.

0
Would love your thoughts, please comment.x
()
x