Introduction
Kubernetes handles container orchestration. Ansible handles everything around it: provisioning clusters, bootstrapping namespaces, deploying Helm charts, managing RBAC, rotating secrets, and coordinating multi-cluster operations. While kubectl and Helm work for single-cluster manual operations, Ansible provides the automation layer for managing Kubernetes at enterprise scale — across dev, staging, and production clusters with consistent, auditable, repeatable deployments.
Collections and Prerequisites
# Install required collections
ansible-galaxy collection install kubernetes.core
ansible-galaxy collection install cloud.common
# Python dependencies on Ansible control node
pip install kubernetes openshift PyYAML
| Collection | Key Modules | Purpose |
|---|---|---|
kubernetes.core | k8s, k8s_info, helm, k8s_exec | Core K8s resource management |
amazon.aws | eks_cluster, eks_nodegroup | AWS EKS provisioning |
azure.azcollection | azure_rm_aks | Azure AKS provisioning |
google.cloud | gcp_container_cluster | GCP GKE provisioning |
Kubeconfig Management
# group_vars/k8s_clusters.yml
k8s_clusters:
dev:
kubeconfig: "~/.kube/configs/dev-cluster"
context: "dev-context"
staging:
kubeconfig: "~/.kube/configs/staging-cluster"
context: "staging-context"
production:
kubeconfig: "~/.kube/configs/prod-cluster"
context: "prod-context"
Pattern 1: Namespace and RBAC Bootstrap
---
- name: Bootstrap Kubernetes namespaces and RBAC
hosts: localhost
connection: local
vars:
target_cluster: production
namespaces:
- name: app-frontend
team: frontend
resource_quota:
cpu: "8"
memory: "16Gi"
- name: app-backend
team: backend
resource_quota:
cpu: "16"
memory: "32Gi"
- name: app-data
team: data-engineering
resource_quota:
cpu: "32"
memory: "64Gi"
tasks:
- name: Create namespaces
kubernetes.core.k8s:
kubeconfig: "{{ k8s_clusters[target_cluster].kubeconfig }}"
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "{{ item.name }}"
labels:
team: "{{ item.team }}"
managed-by: ansible
loop: "{{ namespaces }}"
- name: Apply resource quotas
kubernetes.core.k8s:
kubeconfig: "{{ k8s_clusters[target_cluster].kubeconfig }}"
state: present
definition:
apiVersion: v1
kind: ResourceQuota
metadata:
name: "{{ item.name }}-quota"
namespace: "{{ item.name }}"
spec:
hard:
requests.cpu: "{{ item.resource_quota.cpu }}"
requests.memory: "{{ item.resource_quota.memory }}"
limits.cpu: "{{ item.resource_quota.cpu }}"
limits.memory: "{{ item.resource_quota.memory }}"
loop: "{{ namespaces }}"
- name: Create team RBAC roles
kubernetes.core.k8s:
kubeconfig: "{{ k8s_clusters[target_cluster].kubeconfig }}"
state: present
definition:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: "{{ item.team }}-edit"
namespace: "{{ item.name }}"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: edit
subjects:
- kind: Group
name: "{{ item.team }}"
apiGroup: rbac.authorization.k8s.io
loop: "{{ namespaces }}"
- name: Apply network policies (namespace isolation)
kubernetes.core.k8s:
kubeconfig: "{{ k8s_clusters[target_cluster].kubeconfig }}"
state: present
definition:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: "{{ item.name }}"
spec:
podSelector: {}
policyTypes:
- Ingress
loop: "{{ namespaces }}"
Pattern 2: Helm Chart Deployment
---
- name: Deploy application stack via Helm
hosts: localhost
connection: local
vars:
cluster: production
app_version: "2.5.0"
tasks:
- name: Add Helm repositories
kubernetes.core.helm_repository:
name: "{{ item.name }}"
repo_url: "{{ item.url }}"
loop:
- { name: bitnami, url: "https://charts.bitnami.com/bitnami" }
- { name: ingress-nginx, url: "https://kubernetes.github.io/ingress-nginx" }
- { name: prometheus-community, url: "https://prometheus-community.github.io/helm-charts" }
- name: Deploy NGINX Ingress Controller
kubernetes.core.helm:
kubeconfig: "{{ k8s_clusters[cluster].kubeconfig }}"
name: ingress-nginx
chart_ref: ingress-nginx/ingress-nginx
release_namespace: ingress-system
create_namespace: true
values:
controller:
replicaCount: 3
resources:
requests:
cpu: 100m
memory: 128Mi
service:
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb
wait: true
timeout: 10m0s
- name: Deploy application
kubernetes.core.helm:
kubeconfig: "{{ k8s_clusters[cluster].kubeconfig }}"
name: myapp
chart_ref: ./charts/myapp
release_namespace: app-backend
values:
image:
repository: "registry.internal/myapp"
tag: "{{ app_version }}"
replicas: 3
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
ingress:
enabled: true
hostname: app.example.com
database:
host: "{{ db_endpoint }}"
name: appdb
existingSecret: app-db-credentials
wait: true
timeout: 5m0s
- name: Deploy monitoring stack
kubernetes.core.helm:
kubeconfig: "{{ k8s_clusters[cluster].kubeconfig }}"
name: kube-prometheus
chart_ref: prometheus-community/kube-prometheus-stack
release_namespace: monitoring
create_namespace: true
values:
grafana:
enabled: true
adminPassword: "{{ grafana_admin_password }}"
prometheus:
prometheusSpec:
retention: 30d
storageSpec:
volumeClaimTemplate:
spec:
resources:
requests:
storage: 100Gi
Pattern 3: Rolling Deployment with Validation
---
- name: Rolling application deployment with health checks
hosts: localhost
connection: local
vars:
cluster: production
namespace: app-backend
app_name: myapp
new_version: "{{ deploy_version }}"
tasks:
- name: Get current deployment info
kubernetes.core.k8s_info:
kubeconfig: "{{ k8s_clusters[cluster].kubeconfig }}"
kind: Deployment
name: "{{ app_name }}"
namespace: "{{ namespace }}"
register: current_deployment
- name: Store current version for rollback
ansible.builtin.set_fact:
previous_version: "{{ current_deployment.resources[0].spec.template.spec.containers[0].image.split(':')[1] }}"
- name: Update deployment image
kubernetes.core.k8s:
kubeconfig: "{{ k8s_clusters[cluster].kubeconfig }}"
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ app_name }}"
namespace: "{{ namespace }}"
spec:
template:
spec:
containers:
- name: "{{ app_name }}"
image: "registry.internal/{{ app_name }}:{{ new_version }}"
wait: true
wait_timeout: 300
wait_condition:
type: Available
status: "True"
register: deploy_result
- name: Wait for rollout to complete
kubernetes.core.k8s_info:
kubeconfig: "{{ k8s_clusters[cluster].kubeconfig }}"
kind: Deployment
name: "{{ app_name }}"
namespace: "{{ namespace }}"
register: rollout_status
until: >
rollout_status.resources[0].status.readyReplicas | default(0)
== rollout_status.resources[0].spec.replicas
retries: 30
delay: 10
- name: Run smoke tests
ansible.builtin.uri:
url: "https://app.example.com/healthz"
status_code: 200
timeout: 10
retries: 5
delay: 5
register: health_check
ignore_errors: true
- name: Rollback on failure
kubernetes.core.k8s:
kubeconfig: "{{ k8s_clusters[cluster].kubeconfig }}"
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ app_name }}"
namespace: "{{ namespace }}"
spec:
template:
spec:
containers:
- name: "{{ app_name }}"
image: "registry.internal/{{ app_name }}:{{ previous_version }}"
when: health_check is failed
Pattern 4: Secret Management
---
- name: Manage Kubernetes secrets from Vault
hosts: localhost
connection: local
tasks:
- name: Create database credentials secret
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: app-db-credentials
namespace: app-backend
labels:
managed-by: ansible
type: Opaque
stringData:
DB_HOST: "{{ vault_db_host }}"
DB_USER: "{{ vault_db_user }}"
DB_PASSWORD: "{{ vault_db_password }}"
DB_NAME: "appdb"
- name: Create TLS secret from certificates
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: app-tls
namespace: app-backend
type: kubernetes.io/tls
data:
tls.crt: "{{ lookup('file', 'certs/app.crt') | b64encode }}"
tls.key: "{{ lookup('file', 'certs/app.key') | b64encode }}"
Pattern 5: Multi-Cluster Operations
---
- name: Deploy across all clusters
hosts: localhost
connection: local
vars:
clusters:
- { name: us-east, kubeconfig: "~/.kube/us-east" }
- { name: eu-west, kubeconfig: "~/.kube/eu-west" }
- { name: ap-south, kubeconfig: "~/.kube/ap-south" }
tasks:
- name: Deploy to each cluster
kubernetes.core.helm:
kubeconfig: "{{ item.kubeconfig }}"
name: myapp
chart_ref: ./charts/myapp
release_namespace: app-backend
values_files:
- "values/{{ item.name }}.yml"
wait: true
loop: "{{ clusters }}"
loop_control:
label: "{{ item.name }}"
- name: Verify deployment in all clusters
kubernetes.core.k8s_info:
kubeconfig: "{{ item.kubeconfig }}"
kind: Deployment
name: myapp
namespace: app-backend
loop: "{{ clusters }}"
loop_control:
label: "{{ item.name }}"
register: cluster_status
- name: Report status
ansible.builtin.debug:
msg: "{{ item.item.name }}: {{ item.resources[0].status.readyReplicas | default(0) }}/{{ item.resources[0].spec.replicas }} ready"
loop: "{{ cluster_status.results }}"
loop_control:
label: "{{ item.item.name }}"
Troubleshooting
Module Can't Find kubeconfig
# Explicitly set in environment
- name: Deploy with explicit kubeconfig
kubernetes.core.k8s:
state: present
src: deployment.yml
environment:
K8SCONFIG: "{{ kubeconfig_path }}"
KUBECONFIG: "{{ kubeconfig_path }}"
Timeout During Deployment
# Increase wait timeout for large deployments
- name: Deploy with extended timeout
kubernetes.core.helm:
name: large-app
chart_ref: ./charts/large-app
wait: true
timeout: 15m0s # Default is 5m
RBAC Permission Denied
# Check service account permissions
kubectl auth can-i create deployments --namespace app-backend --as system:serviceaccount:ansible:ansible-sa
Related Articles
- Kubernetes Namespace with Ansible
- Apply YAML to Kubernetes
- Kubernetes Pod Resources
- Ansible vs Terraform
- Managing Credentials with Vault
Conclusion
Use Ansible to automate the Kubernetes lifecycle beyond kubectl apply: namespace bootstrapping with RBAC and network policies, Helm chart deployments with kubernetes.core.helm, rolling updates with automated health checks and rollback, secret management integrated with external vaults, and multi-cluster operations from a single playbook. The kubernetes.core collection provides all the modules needed — k8s for raw manifests, helm for chart deployments, and k8s_info for status queries. For enterprise environments, combine with AAP workflows for approval gates and audit trails.
Related
For a production-focused walkthrough, see Luca Berton's guide on Ansible Kubernetes day-2 operations.