Kustomize

Penyesuaian manifest dengan Kustomize: struktur base, overlay, dan patch untuk tiap lingkungan.

Daftar isi

Topik ini membahas Kustomize, yaitu alat penyesuaian manifest Kubernetes yang bekerja tanpa template, sehingga berkas yang dikelola tetap YAML biasa dari awal sampai akhir. Pembahasan dimulai dari struktur base yang memuat manifest asli, dilanjutkan overlay yang menampung perbedaan tiap lingkungan, kemudian patch strategis dan penyesuaian image, dan ditutup perbandingannya dengan Helm. Contoh di sepanjang topik menggunakan aplikasi catatan yang terdiri atas API dan web, dengan lingkungan staging dan produksi. Setelah topik ini dibaca, satu set manifest yang sama dapat dibangun menjadi varian per lingkungan melalui proses build yang deterministik.

Penyesuaian manifest tanpa template#

Setiap lingkungan membutuhkan manifest yang hampir sama dengan perbedaan kecil, misalnya jumlah replika, tag image, dan namespace. Salah satu cara yang lazim adalah menyalin manifest utuh untuk setiap lingkungan, tetapi cara itu menimbulkan duplikasi. Ketika satu perbaikan dilakukan pada berkas staging, berkas produksi mudah terlewat, sehingga kedua lingkungan perlahan menyimpang satu sama lain. Semakin banyak jenis objek yang dikelola, semakin besar biaya penjagaan salinan tersebut.

Kustomize menjawab masalah tersebut dengan memisahkan manifest asli dari perubahannya. Manifest asli diletakkan pada direktori yang disebut base, sedangkan perbedaan tiap lingkungan diletakkan pada overlay yang merujuk base tersebut. Pusat kendalinya adalah berkas kustomization.yaml, yaitu berkas yang mendeklarasikan daftar sumber daya dan daftar perubahan yang ingin diterapkan. Perintah kustomize build membaca berkas tersebut, menggabungkan seluruh sumber daya dengan perubahannya, lalu mencetak manifest hasilnya ke keluaran standar.

Proses gabungan tersebut adalah fungsi murni dari berkasnya. Masukan yang sama selalu menghasilkan keluaran yang sama, dan tidak ada keadaan yang disimpan di mana pun. Karena itu, hasil build dapat disimpan, dibandingkan antara revisi di git, dan ditinjau sebelum dikirim ke kluster. Penerapan ke kluster dilakukan dengan kubectl apply -k, yang menjalankan build yang sama sebelum mengirim manifestnya ke API server.

Struktur base dan overlay#

Struktur direktori berikut memuat satu base dan dua overlay untuk aplikasi catatan. Base menyimpan manifest yang berlaku untuk semua lingkungan, sedangkan setiap overlay menyimpan kustomization.yaml beserta berkas patch miliknya. Pada bundle topik ini, berkas-berkas tersebut ditampilkan datar di samping artikel dengan nama yang disesuaikan, misalnya kustomization-staging.yaml yang dalam pohon sesungguhnya tersimpan sebagai overlays/staging/kustomization.yaml.

text Struktur direktori
catatan/
├── base/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
└── overlays/
    ├── staging/
    │   ├── kustomization.yaml
    │   └── patch-replika.yaml
    └── produksi/
        ├── kustomization.yaml
        └── patch-replika.yaml

Base harus dapat dibangun sendiri tanpa overlay, karena base adalah titik awal yang berlaku apabila tidak ada penyesuaian apa pun. Berkas pertama pada base adalah manifest Deployment untuk API aplikasi catatan dengan satu replika dan image versi 1.0.0. Berkas ini tersimpan sebagai base/deployment.yaml pada pohon di atas.

yaml deployment-base.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-catatan
  labels:
    app: api-catatan
spec:
  replicas: 1
  selector:
    matchLabels:
      app: api-catatan
  template:
    metadata:
      labels:
        app: api-catatan
    spec:
      containers:
        - name: api
          image: registry.local/api-catatan:1.0.0
          ports:
            - containerPort: 8000

Service berikut menghubungkan Pod API melalui port 80 yang diteruskan ke port 8000 pada container. Berkas ini tidak tersentuh oleh patch mana pun pada contoh topik ini, sehingga hasil build staging dan produksi hanya berbeda pada Deployment.

yaml service-base.yaml
apiVersion: v1
kind: Service
metadata:
  name: api-catatan
spec:
  selector:
    app: api-catatan
  ports:
    - port: 80
      targetPort: 8000

kustomization.yaml pada base mendaftarkan kedua berkas di atas melalui bidang resources, dan urutan penulisan menentukan urutan keluaran build.

yaml kustomization-base.yaml
resources:
  - deployment.yaml
  - service.yaml

Diagram berikut merangkum alur gabungan dari base dan overlay menjadi satu set manifest hasil build.

basedeployment.yamlservice.yamlkustomization.yamlmanifest asli semua lingkunganoverlay stagingkustomization.yamlpatch-replika.yamlreplika: 2image: 2.0.0-rc.1hasil buildDeployment (replika 2)Service (port 80)siap dikirim dengankubectl apply -kresources: ../../basekustomize build
Base dan overlay staging digabung menjadi satu set manifest siap terap

Overlay staging mendeklarasikan base sebagai sumber dayanya, kemudian menambahkan patch jumlah replika dan penggantian tag image melalui bidang patches dan images.

yaml kustomization-staging.yaml
resources:
  - ../../base
patches:
  - path: patch-replika.yaml
images:
  - name: registry.local/api-catatan
    newTag: "2.0.0-rc.1"

Patch pada overlay staging hanya mengubah bidang spec.replicas pada Deployment bernama api-catatan.

yaml patch-replika-staging.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-catatan
spec:
  replicas: 2

Overlay produksi mengikuti pola yang sama dengan dua perbedaan. Bidang namespace menambahkan metadata.namespace pada setiap objek hasil build, sehingga seluruh objek produksi berada pada namespace catatan-produksi. Tag image dituliskan secara eksplisit agar hasil build produksi tidak bergantung pada nilai default base.

yaml kustomization-produksi.yaml
resources:
  - ../../base
namespace: catatan-produksi
patches:
  - path: patch-replika.yaml
images:
  - name: registry.local/api-catatan
    newTag: "1.0.0"
yaml patch-replika-produksi.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-catatan
spec:
  replicas: 3

Patch strategis dan penyesuaian image#

Patch strategis (strategic merge patch) adalah berkas YAML berbentuk manifest yang hanya menuliskan bidang yang ingin diubah. Saat build berjalan, bidang tersebut ditimpa pada manifest asal, sedangkan bidang yang tidak disebut tetap dipertahankan. Penggabungan berlaku bertingkat, termasuk pada daftar objek seperti spec.template.spec.containers yang disatukan berdasarkan nama container-nya. Karena bentuknya sama dengan manifest, patch strategis mudah dibaca dan mudah diperiksa di git.

Beberapa bidang lama pada kustomization.yaml masih dikenali banyak alat, tetapi padanannya pada sintaks saat ini menyatu pada bidang patches yang sama.

Bidang lamaPadanan saat iniPerilaku
basesresourcesmenyatakan kustomization lain sebagai sumber daya
patchesStrategicMergepatches dengan pathpenggabungan berdasarkan kunci bidang
patchesJson6902patches dengan targetoperasi JSON Patch RFC 6902, termasuk penghapusan bidang

Bidang images menangani penyesuaian image tanpa menyentuh berkas patch. Setiap aturan menyebut nama image pada base, lalu mengganti bagian tag melalui newTag, mengganti seluruh nama melalui newName, atau menyematkan digest tertentu. Pada contoh topik ini, overlay staging mengganti tag menjadi 2.0.0-rc.1, sedangkan overlay produksi menuliskan 1.0.0 secara eksplisit.

Melihat hasil build sebelum diterapkan#

Perintah kustomize build mencetak hasil gabungan ke terminal tanpa mengirim apa pun ke kluster, sehingga cocok untuk peninjauan sebelum penerapan.

bash
# tidak-dijalankan
# mencetak hasil build overlay staging ke terminal
kustomize build overlays/staging
text Contoh keluaran
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: api-catatan
  name: api-catatan

Apabila keluarannya sudah sesuai, penerapan ke kluster menggunakan kubectl apply -k.

bash
# tidak-dijalankan
# menerapkan seluruh hasil build overlay staging ke kluster
kubectl apply -k overlays/staging
text Contoh keluaran
deployment.apps/api-catatan created
service/api-catatan created

Kedua perintah di atas tidak dapat dijalankan pada sandbox artikel ini karena biner kustomize dan kubectl tidak tersedia, sehingga mekanisme gabungannya disimulasikan dengan Python menggunakan PyYAML. Skrip berikut membaca deployment-base.yaml dan berkas patch dari bundle, menerapkan penggabungan strategis sederhana, kemudian mengganti tag image dan menambahkan namespace sesuai isi kustomization-staging.yaml dan kustomization-produksi.yaml. Simulasi ini hanya memproses Deployment dan menyederhanakan penggabungan daftar, sedangkan alat asli juga menyatukan daftar container berdasarkan nama container.

python simulasi-kustomize.py
# Simulasi kustomize build: manifest base digabung dengan patch overlay,
# lalu tag image dan namespace diperbarui mengikuti kustomization overlay.
import copy
import yaml

with open("deployment-base.yaml", encoding="utf-8") as berkas:
    base = yaml.safe_load(berkas)

def gabung_strategis(asal, patch):
    # bidang yang disebut patch menimpa nilai base, sisanya dipertahankan
    hasil = copy.deepcopy(asal)
    for kunci, nilai in patch.items():
        if isinstance(nilai, dict) and isinstance(hasil.get(kunci), dict):
            hasil[kunci] = gabung_strategis(hasil[kunci], nilai)
        else:
            hasil[kunci] = copy.deepcopy(nilai)
    return hasil

for lingkungan in ("staging", "produksi"):
    with open(f"kustomization-{lingkungan}.yaml", encoding="utf-8") as berkas:
        kustomisasi = yaml.safe_load(berkas)
    manifest = copy.deepcopy(base)
    # berkas patch pada pohon tersimpan sebagai overlays/<lingkungan>/patch-replika.yaml
    with open(f"patch-replika-{lingkungan}.yaml", encoding="utf-8") as berkas:
        patch = yaml.safe_load(berkas)
    manifest = gabung_strategis(manifest, patch)
    for aturan in kustomisasi.get("images", []):
        for container in manifest["spec"]["template"]["spec"]["containers"]:
            nama, _, _ = container["image"].rpartition(":")
            if nama == aturan["name"]:
                container["image"] = f"{nama}:{aturan['newTag']}"
    if kustomisasi.get("namespace"):
        manifest["metadata"]["namespace"] = kustomisasi["namespace"]
    print(f"--- hasil build overlay {lingkungan} ---")
    print(yaml.dump(manifest, sort_keys=True, default_flow_style=False).rstrip())
text Keluaran
--- hasil build overlay staging ---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: api-catatan
  name: api-catatan
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api-catatan
  template:
    metadata:
      labels:
        app: api-catatan
    spec:
      containers:
      - image: registry.local/api-catatan:2.0.0-rc.1
        name: api
        ports:
        - containerPort: 8000
--- hasil build overlay produksi ---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: api-catatan
  name: api-catatan
  namespace: catatan-produksi
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-catatan
  template:
    metadata:
      labels:
        app: api-catatan
    spec:
      containers:
      - image: registry.local/api-catatan:1.0.0
        name: api
        ports:
        - containerPort: 8000

Pada keluaran di atas, jumlah replika staging menjadi dua dan image berubah menjadi tag 2.0.0-rc.1, sedangkan bagian lain manifest dipertahankan dari base. Overlay produksi menambah namespace catatan-produksi dan menetapkan tiga replika. Penataan spasi pada keluaran simulasi dapat berbeda dari keluaran perintah kustomize asli, tetapi makna kedua keluaran tersebut sama.

Perbandingan Kustomize dan Helm#

Kustomize dan Helm menjawab persoalan yang sama, yaitu mengelola manifest dalam jumlah banyak tanpa duplikasi, tetapi keduanya memilih jalan yang berbeda. Kustomize menyesuaikan berkas YAML asli melalui overlay dan patch, sedangkan Helm merender template menggunakan values sebelum manifest dikirim ke kluster. Perbedaan lainnya terletak pada keadaan, karena Helm melacak setiap release di dalam kluster, sedangkan Kustomize tidak menyimpan keadaan apa pun.

AspekKustomizeHelm
Bentuk berkasYAML murni tanpa templatetemplate Go yang diisi values
Mekanisme penyesuaianoverlay dan patch deklaratifvalues dan fungsi templating
Pelacakan keadaantidak menyimpan keadaan di klusterrelease tersimpan dan memiliki riwayat revisi
Perintah utamakubectl apply -khelm install dan helm upgrade
Titik awalmanifest yang sudah adachart yang ditulis dari awal

Tabel di atas juga menjelaskan alasan kedua alat ini sering ditemukan sekaligus dalam satu tim. Kustomize menambah nilai pada manifest yang sudah teruji karena tidak ada perubahan sintaks pada berkasnya, sedangkan Helm menambah nilai ketika penyesuaian perlu dinyatakan sebagai nilai yang diteruskan ke chart. Keduanya sama-sama deklaratif, sehingga hasil akhirnya tetap dapat diperiksa di git sebelum dikirim ke kluster.

Kesalahan umum#

Kesalahan pada pekerjaan Kustomize biasanya bukan pada sintaksnya, melainkan pada penjagaan struktur. Beberapa kesalahan berikut muncul ketika overlay mulai bertambah, dan semuanya dapat dicegah dengan membaca keluaran build secara rutin.

  • Mengubah berkas base demi kebutuhan satu lingkungan. Perubahan tersebut ikut berlaku pada semua overlay, sehingga lingkungan lain diam-diam ikut berubah. Perbaikannya adalah memindahkan perbedaan tersebut menjadi patch pada overlay yang berkepentingan.
  • Lupa mendaftarkan berkas baru pada bidang resources. Berkas manifest yang berada di folder base tetapi tidak terdaftar tidak ikut dibangun, sehingga objeknya tidak pernah diterapkan. Tambahkan berkas tersebut pada daftar resources, lalu jalankan build untuk memeriksa keluarannya.
  • Menulis patch dengan nama atau kind yang tidak cocok dengan manifest asal. Penggabungan strategis mencari objek target berdasarkan apiVersion, kind, dan metadata.name, sehingga patch yang salah nama membatalkan build dengan error.

Ketiga kesalahan di atas berbagi satu gejala yang sama, yaitu keluaran build yang berbeda dari dugaan. Keluaran yang mencantumkan objek kurang atau lebih dari yang diharapkan adalah sinyal bahwa daftar resources atau isi patch perlu diperiksa ulang sebelum penerapan.

Ringkasan#

  • Kustomize menyesuaikan manifest tanpa template, yaitu base berisi manifest asli dan overlay menampung perbedaan tiap lingkungan melalui kustomization.yaml.
  • Patch strategis menimpa hanya bidang yang disebut, sedangkan bidang images mengganti tag, nama, atau digest image pada saat build.
  • Hasil build adalah fungsi murni dari berkasnya, sehingga dapat ditinjau dengan kustomize build dan diterapkan dengan kubectl apply -k.
  • Kustomize tidak menyimpan keadaan di kluster, sedangkan Helm menyimpan release beserta riwayat revisinya.

Referensi#