Deploy Manifest #
Cluster Kubernetes sudah berjalan — sekarang saatnya men-deploy aplikasi ke dalamnya. Kamu bisa menggunakan kubectl apply secara langsung, tapi Ansible memberikan sesuatu yang kubectl tidak punya: idempotency yang terjamin, templating Jinja2 untuk manifest yang dinamis, integrasi dengan Vault untuk secret management, dan kemampuan mengkoordinasikan deployment dengan perubahan infrastruktur lainnya. Artikel ini membahas cara men-deploy Kubernetes manifest menggunakan Ansible.
Setup: Koleksi yang Diperlukan #
ansible-galaxy collection install kubernetes.core
pip install kubernetes # Library Python untuk Kubernetes API
Pastikan kubeconfig tersedia — Ansible butuh akses ke Kubernetes API:
# group_vars/k8s.yml
k8s_kubeconfig: "{{ playbook_dir }}/kubeconfig/admin.conf"
Apply Manifest dari File #
Module kubernetes.core.k8s adalah cara utama untuk mendeploy resource Kubernetes:
- name: Apply Deployment manifest
kubernetes.core.k8s:
kubeconfig: "{{ k8s_kubeconfig }}"
state: present
src: manifests/deployment.yml # Path ke file manifest
- name: Apply manifest dari direktori
kubernetes.core.k8s:
kubeconfig: "{{ k8s_kubeconfig }}"
state: present
src: "{{ item }}"
with_fileglob:
- "manifests/*.yml"
Apply Manifest Inline (dengan Templating) #
Keunggulan terbesar Ansible dibanding kubectl murni adalah kemampuan templating Jinja2 langsung di dalam definisi manifest:
- name: Deploy Namespace
kubernetes.core.k8s:
kubeconfig: "{{ k8s_kubeconfig }}"
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "{{ app_namespace }}"
labels:
environment: "{{ env }}"
managed-by: ansible
- name: Deploy ConfigMap dari variabel Ansible
kubernetes.core.k8s:
kubeconfig: "{{ k8s_kubeconfig }}"
state: present
definition:
apiVersion: v1
kind: ConfigMap
metadata:
name: "{{ app_name }}-config"
namespace: "{{ app_namespace }}"
data:
APP_PORT: "{{ app_port | string }}"
LOG_LEVEL: "{{ app_log_level }}"
DATABASE_HOST: "{{ db_host }}"
REDIS_HOST: "{{ redis_host }}"
Deploy Secret dari Ansible Vault #
Kubernetes Secret harus dibuat dari nilai yang tersimpan di Ansible Vault — jangan hardcode di manifest:
- name: Deploy database Secret dari Ansible Vault
kubernetes.core.k8s:
kubeconfig: "{{ k8s_kubeconfig }}"
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: "{{ app_name }}-db-secret"
namespace: "{{ app_namespace }}"
type: Opaque
stringData: # stringData di-encode otomatis oleh Kubernetes
DATABASE_URL: "postgresql://{{ db_user }}:{{ vault_db_password }}@{{ db_host }}:5432/{{ db_name }}"
SECRET_KEY: "{{ vault_app_secret_key }}"
no_log: true
Deploy Deployment dan Service #
- name: Deploy aplikasi ke Kubernetes
kubernetes.core.k8s:
kubeconfig: "{{ k8s_kubeconfig }}"
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
labels:
app: "{{ app_name }}"
version: "{{ app_version }}"
spec:
replicas: "{{ app_replicas | default(2) }}"
selector:
matchLabels:
app: "{{ app_name }}"
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # Zero-downtime rolling update
template:
metadata:
labels:
app: "{{ app_name }}"
version: "{{ app_version }}"
spec:
containers:
- name: "{{ app_name }}"
image: "{{ app_image }}:{{ app_version }}"
ports:
- containerPort: "{{ app_port }}"
envFrom:
- configMapRef:
name: "{{ app_name }}-config"
- secretRef:
name: "{{ app_name }}-db-secret"
resources:
requests:
cpu: "{{ app_cpu_request | default('100m') }}"
memory: "{{ app_memory_request | default('128Mi') }}"
limits:
cpu: "{{ app_cpu_limit | default('500m') }}"
memory: "{{ app_memory_limit | default('512Mi') }}"
livenessProbe:
httpGet:
path: /health
port: "{{ app_port }}"
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: "{{ app_port }}"
initialDelaySeconds: 5
periodSeconds: 5
- name: Deploy Service
kubernetes.core.k8s:
kubeconfig: "{{ k8s_kubeconfig }}"
state: present
definition:
apiVersion: v1
kind: Service
metadata:
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
spec:
selector:
app: "{{ app_name }}"
ports:
- port: 80
targetPort: "{{ app_port }}"
type: ClusterIP
Menunggu Deployment Selesai #
Setelah apply Deployment, tunggu sampai rollout selesai sebelum melanjutkan:
- name: Tunggu Deployment rollout selesai
kubernetes.core.k8s_rollout_status:
kubeconfig: "{{ k8s_kubeconfig }}"
name: "{{ app_name }}"
namespace: "{{ app_namespace }}"
kind: Deployment
timeout: 300 # Timeout 5 menit
- name: Verifikasi jumlah Pod yang berjalan
kubernetes.core.k8s_info:
kubeconfig: "{{ k8s_kubeconfig }}"
kind: Pod
namespace: "{{ app_namespace }}"
label_selectors:
- "app={{ app_name }}"
- "version={{ app_version }}"
register: pod_info
- name: Assert semua Pod dalam kondisi Running
assert:
that:
- pod_info.resources | length == app_replicas
- pod_info.resources | selectattr('status.phase', 'equalto', 'Running') | list | length == app_replicas
fail_msg: "Tidak semua Pod dalam kondisi Running setelah deployment!"
Menghapus Resource #
# Hapus resource tertentu
- name: Hapus Deployment lama
kubernetes.core.k8s:
kubeconfig: "{{ k8s_kubeconfig }}"
state: absent
kind: Deployment
name: "{{ app_name }}-old"
namespace: "{{ app_namespace }}"
# Hapus semua resource dengan label tertentu
- name: Hapus semua resource dengan label version lama
kubernetes.core.k8s:
kubeconfig: "{{ k8s_kubeconfig }}"
state: absent
kind: "{{ item }}"
namespace: "{{ app_namespace }}"
label_selectors:
- "app={{ app_name }}"
- "version={{ old_app_version }}"
loop:
- Deployment
- Service
- ConfigMap
Ringkasan #
- Instal
kubernetes.corecollection dan library Pythonkubernetessebelum menggunakan module k8s.kubernetes.core.k8sdengandefinition:mengizinkan templating Jinja2 penuh dalam manifest — nilai variabel Ansible langsung masuk ke manifest.- Buat Kubernetes Secret dari Ansible Vault menggunakan
stringData— Kubernetes otomatis meng-encode ke base64, Ansible otomatis mendekripsi vault.- Selalu gunakan
no_log: trueuntuk task yang mendeploy Secret agar nilai sensitif tidak muncul di log.maxUnavailable: 0di rolling update strategy memastikan zero-downtime deployment.- Gunakan
k8s_rollout_statusuntuk menunggu deployment selesai sebelum melanjutkan ke task berikutnya.- Selalu definisikan resource requests dan limits untuk setiap container — tanpa ini, satu container bisa menghabiskan seluruh resource node.