Mengelola container Docker dan cluster Kubernetes dengan Ansible: collection kubernetes.core dan community.docker, modul k8s untuk Deployment dan Service, Helm chart deployment, hingga otomasi provisioning cluster kubeadm dan k3s.

Setelah di episode 22 sebelumnya kita membahas network automation dengan Ansible — mengelola Cisco, Arista, dan Juniper lewat network_cli, httpapi, dan netconf — pada episode kali ini kita akan melompat ke dunia yang menjadi standar deployment aplikasi modern: container dan Kubernetes.
Kalau kita analogikan, server adalah rumah, jaringan adalah jalan raya, dan container adalah kotak-kotak pengiriman standar yang bisa diangkut ke mana saja. Kotak itu berisi aplikasi beserta seluruh ketergantungannya — runtime, library, konfigurasi — sehingga apa yang berjalan di laptop developer pasti berjalan identik di production. Kubernetes adalah pelabuhan raksasa yang mengatur bagaimana ribuan kotak itu dibongkar, dimuat, dipindahkan, dan dijaga tetap berjalan. Masalahnya, mengelola pelabuhan itu secara manual sama beratnya dengan mengelola pelabuhan sungguhan: ada jadwal, prioritas, kapasitas, dan pemeliharaan yang harus diotomasi.
Di sinilah Ansible berperan. Meskipun Kubernetes sudah punya kubectl dan Helm yang sangat mumpuni, Ansible menambahkan lapisan orchestration yang deklaratif dan idempotent: menginstall cluster dari nol, menyimpan konfigurasi, menerapkan Deployment, hingga memperbarui secret — semua dalam bahasa YAML yang sama yang sudah kalian kenal sejak episode 1. Pada episode ini kita akan membahas collection kubernetes.core dan community.docker, modul k8s beserta perbedaan definition dan resource_definition, deployment Helm chart, manajemen Docker container dan image, serta use case nyata seperti provisioning cluster k3s dan pola kerja GitOps.
kubernetes.core dan community.dockerDua collection berikut adalah tulang punggung automasi container di Ansible:
| Collection | Fokus | Modul Utama |
|---|---|---|
kubernetes.core | Manajemen cluster Kubernetes | k8s, kubectl, helm, k8s_info, k8s_scale |
community.docker | Manajemen Docker engine & image | docker_container, docker_image, docker_compose_v2, docker_network, docker_volume |
Mereka menjawab dua kebutuhan berbeda. kubernetes.core berbicara kepada API server Kubernetes menggunakan kredensial kubeconfig atau token. community.docker berbicara kepada Docker daemon pada satu host — cocok untuk environment yang belum pakai Kubernetes, atau untuk mengelola Docker runtime itu sendiri.
Install kedua collection tersebut:
ansible-galaxy collection install kubernetes.core community.dockerSebelum bermain dengan modul k8s, Ansible harus bisa membuktikan diri ke API server. Ada beberapa cara:
kubeconfig di playbook atau gunakan ~/.kube/config default.api_key.host, validate_certs, ca_cert, client_key, client_cert.Untuk kubeconfig lokal, kalian cukup menjalankan playbook dari mesin yang sudah punya akses cluster:
kubernetes_auth:
kubeconfig: ~/.kube/configTip
Jika cluster diakses dari remote host (misalnya via SSH), bundle kubeconfig ke host tersebut atau gunakan api_key dari service account yang disimpan sebagai secret Ansible Vault (episode 14). Jangan pernah menyimpan token cluster ke repository Git secara plaintext.
k8s: Menerapkan Manifes KubernetesModul kubernetes.core.k8s adalah modul serbaguna untuk membuat, memperbarui, dan menghapus resource Kubernetes. Dua parameter penting yang sering membingungkan pemula adalah definition dan resource_definition. Keduanya sebenarnya identik fungsinya — resource_definition adalah nama parameter kanoniknya, sedangkan definition adalah alias legacy yang tetap didukung. Intinya: keduanya menerima dictionary yang berisi resource manifest (mirip file kubectl apply -f), bukan nama file.
Playbook berikut menerapkan sebuah Deployment NGINX dengan 3 replika:
- name: Deploy aplikasi ke Kubernetes
hosts: localhost
gather_facts: false
vars:
k8s_auth: ~/.kube/config
tasks:
- name: Terapkan Deployment NGINX
kubernetes.core.k8s:
kubeconfig: "{{ k8s_auth }}"
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: production
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80Karena modul ini declarative, menjalankan playbook yang sama untuk kedua kalinya tidak akan membuat perubahan apa pun (status ok) — persis prinsip idempotency Ansible yang kalian pelajari di episode 1. Ini perbedaan mendasar dengan sekadar menjalankan kubectl apply secara membabi buta: Ansible mengetahui diff dan hanya bertindak saat diperlukan.
Selain definition, resource bisa dibuat dengan template (path ke file template Jinja2) atau src (path ke file manifest):
- name: Terapkan semua resource dari file manifest
kubernetes.core.k8s:
kubeconfig: ~/.kube/config
state: present
src: manifests/web-deployment.ymlImportant
Parameter resource_definition (atau aliasnya definition) menerima objek dict manifest, sedangkan src menerima path file. Jangan mencampur keduanya. Untuk manifest yang dibuat dinamis dengan variabel, gunakan template + file Jinja2, atau gunakan lookup('template', ...) lalu lewatkan hasilnya ke definition.
Menjaga konfigurasi dan data rahasia tetap terpisah dari image adalah best practice Kubernetes. Dengan Ansible, ConfigMap bisa dibuat idempotent dari variabel playbook:
- name: Kelola ConfigMap dan Secret
hosts: localhost
gather_facts: false
vars:
k8s_auth: ~/.kube/config
tasks:
- name: Buat ConfigMap konfigurasi aplikasi
kubernetes.core.k8s:
kubeconfig: "{{ k8s_auth }}"
state: present
definition:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
LOG_LEVEL: info
PORT: "8080"
FEATURE_FLAG_NEW_UI: "true"
- name: Buat Secret dari variabel Vault
kubernetes.core.k8s:
kubeconfig: "{{ k8s_auth }}"
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
stringData:
DATABASE_URL: "{{ vault_db_url }}"
API_KEY: "{{ vault_api_key }}"Warning
Nilai Secret di atas diambil dari variabel yang dienkripsi dengan Ansible Vault. Meskipun Secret Kubernetes disimpan ter-obfuscate (base64), itu bukanlah enkripsi yang aman untuk data yang sangat sensitif. Untuk production, pertimbangkan eksternal secret seperti HashiCorp Vault atau integrasi cloud KMS — dan pastikan nilai secret tidak pernah tercetak ke log playbook (no_log: true).
kubernetes.core.helmJika tim kalian sudah mengadopsi Helm, modul helm di collection kubernetes.core memungkinkan instalasi dan upgrade chart secara idempotent:
- name: Install chart aplikasi via Helm
hosts: localhost
gather_facts: false
vars:
k8s_auth: ~/.kube/config
tasks:
- name: Install atau upgrade chart wordpress
kubernetes.core.helm:
kubeconfig: "{{ k8s_auth }}"
name: wordpress
namespace: production
chart_ref: bitnami/wordpress
chart_version: 18.1.6
create_namespace: true
release_values:
wordpressUsername: admin
wordpressPassword: "{{ vault_wordpress_password }}"
service:
type: ClusterIPPerhatikan parameter release_values yang menerima dictionary — ini setara dengan --set atau values.yaml pada Helm CLI, tapi dalam bentuk data yang bisa dibawa variabel Ansible. Untuk file values yang kompleks, gunakan values_files dengan daftar path file YAML.
community.dockerUntuk environment tanpa Kubernetes, collection community.docker menawarkan modul-manajemen yang sangat dekat dengan cara kerja Docker CLI. Modul docker_container mengelola siklus hidup container:
- name: Kelola container dengan Docker
hosts: docker_hosts
become: true
tasks:
- name: Jalankan container web-app
community.docker.docker_container:
name: web-app
image: registry.internal/web-app:latest
state: started
restart_policy: always
ports:
- "8080:8080"
env:
LOG_LEVEL: info
DATABASE_URL: "{{ vault_db_url }}"
networks:
- name: app-networkSedangkan modul docker_image menangani sisi image — build, pull, tag, hingga push ke registry:
- name: Bangun dan push image
hosts: docker_hosts
become: true
vars:
image_full: "registry.internal/web-app:{{ git_short_sha }}"
tasks:
- name: Build image dari Dockerfile
community.docker.docker_image:
name: "{{ image_full }}"
build:
path: /opt/app
pull: true
source: build
- name: Push image ke registry
community.docker.docker_image:
name: "{{ image_full }}"
push: true
source: localkubernetes.core vs community.dockerBerikut tabel referensi cepat untuk memilih modul yang tepat:
| Kebutuhan | kubernetes.core | community.docker |
|---|---|---|
| Resource di cluster (Deployment, Service, Pod) | k8s, k8s_info | — |
| Menjalankan container | — | docker_container |
| Build / push image | — | docker_image |
| Orchestrasi multi-container | — | docker_compose_v2 |
| Install / upgrade Helm chart | helm | — |
| Menjalankan command kubectl | kubectl | — |
| Jaringan & volume | — | docker_network, docker_volume |
| Scale deployment | k8s_scale | — |
Perhatikan pembagian yang tegas: urusan cluster → kubernetes.core, urusan Docker engine → community.docker. Memakai modul yang salah sasaran adalah kesalahan yang paling sering dijumpai.
Contoh code group berikut menunjukkan persamaan "menjalankan container" di dua dunia yang berbeda — satu Deployment di Kubernetes, satu container di Docker:
- name: Jalankan aplikasi di Kubernetes
kubernetes.core.k8s:
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: production
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: registry.internal/web:latest
ports:
- containerPort: 8080Perhatikan perbedaannya: Kubernetes butuh mendeskripsikan selector dan template karena itu adalah declarative orchestration dengan scheduling dan self-healing, sedangkan Docker cukup menyebutkan container tunggal. Struktur data inilah yang membuat definition di k8s terasa seperti manifest lengkap — karena memang begitu.
Ansible unggul di satu hal yang sulit dilakukan kubectl: membuat cluster itu sendiri dari nol. Mulai dari node kosong, playbook bisa menginstall container runtime, komponen Kubernetes, lalu menggabungkan worker ke control plane.
Contoh berikut mengotomasi provisioning cluster k3s (yang juga kita kenal dari series Kubernetes) — satu node sebagai server, lainnya sebagai agent:
- name: Provisioning cluster k3s
hosts: all
become: true
vars:
k3s_version: v1.32.4+k3s1
tasks:
- name: Install k3s server di control node
ansible.builtin.shell:
cmd: |
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION={{ k3s_version }} sh -
when: inventory_hostname == groups['k3s_server'][0]
- name: Ambil token cluster dari control node
ansible.builtin.command: cat /var/lib/rancher/k3s/server/node-token
register: k3s_token
when: inventory_hostname == groups['k3s_server'][0]
- name: Install k3s agent di worker nodes
ansible.builtin.shell:
cmd: |
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION={{ k3s_version }} \
K3S_URL=https://{{ hostvars[groups['k3s_server'][0]].ansible_host }}:6443 \
K3S_TOKEN={{ hostvars[groups['k3s_server'][0]].k3s_token.stdout }} sh -
when: inventory_hostname in groups['k3s_agents']Note
Untuk production-grade, lebih aman menggunakan peran komunitas yang sudah teruji seperti ansible-community.k3s atau kubernetes.core untuk kubeadm, daripada menulis semua langkah dari nol. Namun memahami urutan dasarnya penting agar kalian tahu apa yang terjadi di balik layar — prinsip yang sama berlaku untuk semua tooling yang kita bahas di series ini.
Kombinasi Ansible dan Kubernetes membuka pola deployment yang sangat produktif:
ansible-playbook untuk menerapkan manifest ke cluster setelah build image selesai. Ansible menjadi lapisan eksekusi yang bisa di-audit dan terintegrasi dengan credential store (ingat episode 19 dan 20).Alur ringkasnya:
1. Push kode aplikasi ke Git
2. CI build image + push ke registry
3. Update tag image di repo GitOps (manifest)
4. Argo CD mendeteksi drift → sinkronkan ke cluster
5. Ansible (via AWX/AAP) hanya mem-bootstrap cluster & credential1. Mencampur definition dan resource_definition
Keduanya adalah parameter yang sama, tapi banyak playbook lama menulis keduanya sekaligus atau mengira definition menerima path file. Ingat: keduanya menerima dict manifest, src menerima path file.
2. Salah menempatkan kubeconfig
Modul k8s yang dijalankan dari remote host (misal via delegate_to atau connection SSH) akan mencari kubeconfig di host tujuan, bukan di control node. Selalu tentukan kubeconfig secara eksplisit atau jalankan task dengan delegate_to: localhost.
3. Memakai modul Kubernetes untuk urusan Docker
Menggunakan kubernetes.core.k8s untuk container di Docker engine, atau community.docker.docker_container untuk Pod. Keduanya menangani API yang berbeda total. Pastikan kalian memilih collection sesuai target.
4. Lupa state: present / state: absent
Tanpa state, modul k8s menggunakan default present. Ini sering tidak disadari sampai seseorang tidak sengaja meng-set state: absent untuk me-remove resource dan menghapus sesuatu yang tidak dimaksudkan. Selalu eksplisit.
5. Secret ter-ekspos di log
Nilai stringData atau release_values yang berisi password bisa tercetak di output playbook saat mode verbose. Tambahkan no_log: true pada task yang menangani data sensitif, dan simpan sumber nilainya di Ansible Vault.
6. Membiarkan image tag latest di production
Image latest tidak bisa di-reproduce dan membuat idempotency playbook tidak berarti — Ansible tidak bisa tahu image "yang benar". Selalu gunakan tag immutabel seperti git_short_sha atau versi semver.
Pada episode ini kita telah membahas manajemen Kubernetes dan container dengan Ansible: collection kubernetes.core untuk mengelola resource cluster via modul k8s (dengan pemahaman definition vs resource_definition), ConfigMap dan Secret, deployment Helm chart, serta collection community.docker untuk Docker container, image, dan orchestration multi-container. Kita juga melihat use case nyata: provisioning cluster k3s, deployment automation, dan peran Ansible dalam pola GitOps — lengkap dengan kesalahan umum yang harus dihindari.
Dengan ini, kalian kini mampu mengotomasi hampir seluruh lapisan infrastruktur modern: server Linux, jaringan, cloud, hingga platform container dan Kubernetes. Ini adalah kemampuan komprehensif yang membuat kalian siap menghadapi environment kerja nyata yang multi-dimensi.
Di episode 24 selanjutnya, kita akan membahas Database Management & Automation — mengelola PostgreSQL, MySQL/MariaDB, dan MongoDB menggunakan collection community.postgresql, community.mysql, dan community.mongodb, mulai dari pembuatan database dan user, manajemen privilege, hingga backup otomatis dan konfigurasi high availability. Pastikan tetap semangat!