DNS & Service Discovery #

Di dalam kluster Kubernetes yang padat dan dinamis, ratusan kontainer dapat dihidupkan, dihentikan, dan dijadwalkan ulang dalam hitungan detik. Kita sudah mengetahui bahwa objek Service bertindak sebagai penyeimbang beban (load balancer) virtual yang menyediakan alamat IP stabil (ClusterIP) untuk sekumpulan Pod yang dinamis. Namun, pertanyaannya tetap ada: bagaimana aplikasi kita dapat menemukan alamat IP virtual milik Service tersebut sejak awal?

Menghubungi alamat IP virtual secara langsung bukanlah cara kerja sistem modern. Kita membutuhkan mekanisme Service Discovery (Penemuan Layanan) yang otomatis, terpusat, dan dinamis. Di Kubernetes, mekanisme ini sepenuhnya diselesaikan menggunakan sistem DNS (Domain Name System) internal kluster.

DNS kluster secara dinamis memetakan nama layanan ramah manusia (seperti auth-service) ke alamat IP virtualnya yang aktif. Ketika kita membuat, menghapus, atau memindahkan Service, DNS server kluster secara otomatis memperbarui catatannya secara real-time. Artikel ini akan membahas arsitektur DNS kluster, cara kerja kueri DNS internal, konfigurasi resolver kontainer, teknik optimasi performa DNS, serta panduan praktis mendeteksi gangguannya.


Arsitektur CoreDNS: Otak Service Discovery Kluster #

Sejak Kubernetes versi 1.13, CoreDNS telah menjadi standar default server DNS internal untuk kluster Kubernetes menggantikan Kube-DNS yang lama. CoreDNS adalah server DNS modular berkecepatan tinggi yang ditulis dalam bahasa Go dan merupakan proyek berstatus graduated di bawah naungan CNCF.

CoreDNS dideploy sebagai Deployment standar (biasanya terdiri dari 2 atau lebih replika demi ketersediaan tinggi) di dalam namespace kube-system. Untuk menghubungkan Pod aplikasi ke CoreDNS, Kubernetes mengekspos Deployment CoreDNS tersebut melalui Service khusus bernama kube-dns.

# Memeriksa Service kube-dns di namespace kube-system
kubectl get service -n kube-system kube-dns

Output dari perintah di atas:

NAME       TYPE        CLUSTER-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   53/UDP,53/TCP,9153/TCP   365d

IP ClusterIP stabil milik Service kube-dns ini (misalnya 10.96.0.10) bertindak sebagai jangkar resolusi DNS untuk seluruh Pod di kluster.

Bagaimana Pod Menghubungi CoreDNS? #

Setiap kali Kubelet membuat kontainer Pod baru di node worker, Kubelet secara otomatis menyuntikkan konfigurasi resolver DNS kluster ke dalam berkas internal /etc/resolv.conf kontainer tersebut.

Mari kita intip isi file /etc/resolv.conf standar di dalam Pod:

nameserver 10.96.0.10
search production.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
  • nameserver 10.96.0.10: Mengarahkan seluruh kueri DNS dari dalam kontainer ke alamat IP virtual milik CoreDNS.
  • search ...: Mendefinisikan daftar pencarian sufiks domain lokal. Jika kita melakukan kueri nama pendek (misalnya database), resolver akan mencoba melengkapinya secara berurutan dengan sufiks-sufiks ini.
  • options ndots:5: Aturan kritis yang memberitahu resolver bahwa jika sebuah nama domain yang dicari mengandung titik kurang dari 5 buah, nama tersebut harus diperlakukan sebagai domain lokal kluster terlebih dahulu, baru kemudian dicari ke DNS luar jika gagal.

Format Standar DNS Records di Kubernetes #

CoreDNS secara otomatis memantau Kubernetes API untuk setiap objek Service yang dibuat, diubah, atau dihapus, kemudian membuat record DNS yang sesuai. Berikut adalah format catatan DNS yang terstandarisasi di Kubernetes:

1. A/AAAA Records untuk Service (ClusterIP) #

Setiap Service ClusterIP akan mendapatkan catatan A (untuk IPv4) atau AAAA (untuk IPv6) dengan format Fully Qualified Domain Name (FQDN) sebagai berikut:

Format FQDN:
  <nama-service>.<namespace-service>.svc.<domain-kluster>

Contoh riil:
  payment-service.finance.svc.cluster.local

Jika aplikasi kita melakukan kueri terhadap payment-service.finance.svc.cluster.local, CoreDNS akan langsung mengembalikan alamat IP virtual 10.96.120.45 milik Service tersebut.

2. A Records untuk Headless Service #

Jika kita menggunakan Headless Service (menggunakan parameter clusterIP: None), kueri DNS terhadap domain Service tersebut tidak akan mengembalikan satu IP virtual, melainkan mengembalikan seluruh daftar alamat IP asli dari Pod yang terikat pada Service tersebut.

database-service.production.svc.cluster.local  ──>  10.244.1.15
                                               ──>  10.244.2.22
                                               ──>  10.244.1.30

3. A Records untuk Pod Individual #

Selain Service, Kubernetes juga menyediakan catatan DNS khusus untuk Pod individual. Format penulisan alamat DNS-nya adalah mengganti seluruh karakter titik pada alamat IP Pod dengan tanda hubung (dash):

Format DNS Pod:
  <ip-pod-dengan-dash>.<namespace-pod>.pod.<domain-kluster>

Contoh riil (IP Pod 10.244.1.15 di namespace production):
  10-244-1-15.production.pod.cluster.local

4. SRV Records untuk Port Bernama #

Jika Service kita mendefinisikan nama port secara eksplisit di manifesnya, CoreDNS akan membuat catatan SRV untuk menemukan nomor port dan protokol yang digunakan:

Format SRV:
  _<nama-port>._<protokol>.<nama-service>.<namespace>.svc.cluster.local

Contoh riil (Port bernama "http-api" dengan protokol TCP):
  _http-api._tcp.payment-service.finance.svc.cluster.local

Pencarian Nama Pendek (Short Name) vs FQDN #

Berkat adanya parameter search di dalam /etc/resolv.conf, developer tidak wajib menuliskan FQDN lengkap seperti payment-service.finance.svc.cluster.local di dalam kode aplikasi mereka. Kita dapat menggunakan nama pendek (short name) sesuai dengan konteks lokasinya.

Mari kita perhatikan simulasi bagaimana resolver memproses nama pendek melalui diagram berikut:

flowchart TD
    Start["Pod di namespace 'production' query nama pendek: 'auth'"] --> Step1{"Coba auth.production.svc.cluster.local"}
    Step1 -- "Ditemukan (A Record)" --> Success["Kembalikan IP: 10.96.0.22 (Sukses)"]
    Step1 -- "Gagal (NXDOMAIN)" --> Step2{"Coba auth.svc.cluster.local"}
    Step2 -- "Ditemukan" --> Success
    Step2 -- "Gagal" --> Step3{"Coba auth.cluster.local"}
    Step3 -- "Ditemukan" --> Success
    Step3 -- "Gagal" --> Step4{"Kueri ke upstream external DNS"}
    Step4 -- "Sukses" --> SuccessExternal["Kembalikan IP Publik"]
    Step4 -- "Gagal" --> Failure["Kembalikan Error NXDOMAIN"]

Aturan Navigasi Lintas Namespace #

  1. Satu Namespace: Jika Pod backend dan Pod frontend berada di namespace yang sama (misalnya sama-sama di namespace production), frontend cukup memanggil backend dengan nama pendek:
    http://auth-service/api/login
    
  2. Lintas Namespace: Jika Pod frontend berada di namespace frontend, sedangkan backend berada di namespace backend-api, nama pendek auth-service tidak akan berhasil di-resolve karena DNS resolver hanya mencari di namespace lokal frontend terlebih dahulu. Kita wajib menyertakan nama namespace target minimal:
    http://auth-service.backend-api/api/login
    
    Atau lebih aman menggunakan FQDN lengkap untuk mencegah ambiguitas:
    http://auth-service.backend-api.svc.cluster.local/api/login
    

Kustomisasi Konfigurasi DNS di Level Pod #

Kubernetes memungkinkan kita mengesampingkan atau menambahkan konfigurasi DNS default untuk Pod tertentu menggunakan properti dnsPolicy dan dnsConfig pada spec Pod.

apiVersion: v1
kind: Pod
metadata:
  name: custom-dns-pod
  namespace: production
spec:
  dnsPolicy: ClusterFirst # Menggunakan DNS internal kluster CoreDNS (Default)
  dnsConfig:
    nameservers:
      - 1.1.1.1 # Menambahkan DNS fallback eksternal tambahan
    searches:
      - custom-company-domain.internal # Menambahkan search suffix khusus
    options:
      - name: ndots
        value: "2" # Menurunkan nilai ndots untuk mengoptimalkan performa kueri DNS luar
      - name: timeout
        value: "2"
  containers:
    - name: app-container
      image: nginx:alpine

Opsi dnsPolicy yang Tersedia #

  • ClusterFirst (Default): Seluruh kueri DNS yang tidak cocok dengan domain kluster lokal akan diteruskan ke DNS upstream eksternal yang dikonfigurasi di node worker host.
  • ClusterFirstWithHostNet: Harus diaktifkan secara eksplisit jika Pod kita menggunakan konfigurasi hostNetwork: true agar Pod tersebut tetap dapat menggunakan CoreDNS kluster untuk resolusi nama.
  • Default: Pod akan mewarisi konfigurasi DNS langsung dari sistem operasi node worker host tanpa melewati CoreDNS kluster (tidak direkomendasikan karena Pod tidak bisa mendeteksi Service kluster).
  • None: Mengabaikan seluruh konfigurasi DNS kluster bawaan. Kita wajib mendefinisikan properti dnsConfig secara lengkap secara manual.

Optimasi Performa Jaringan DNS di Produksi #

Pada kluster produksi berskala besar dengan aktivitas mikroservis yang sangat sibuk, CoreDNS sering kali menjadi bottleneck performa utama kluster. Masalah ini paling sering dipicu oleh parameter bawaan options ndots:5.

Bottleneck ndots:5 #

Ketika aplikasi kita mencoba menghubungi kueri domain eksternal (misalnya api.stripe.com), karena domain tersebut memiliki kurang dari 5 buah titik, resolver internal kontainer akan berasumsi bahwa domain tersebut adalah domain lokal kluster dan melakukan kueri beruntun:

  1. Query: api.stripe.com.production.svc.cluster.local -> Gagal (NXDOMAIN)
  2. Query: api.stripe.com.svc.cluster.local -> Gagal (NXDOMAIN)
  3. Query: api.stripe.com.cluster.local -> Gagal (NXDOMAIN)
  4. Query: api.stripe.com. -> Sukses (Kueri dikirim ke internet luar)

Ini berarti untuk setiap resolusi kueri domain internet eksternal tunggal, CoreDNS dipaksa memproses 3 kueri sampah tambahan yang tidak berguna terlebih dahulu. Hal ini memicu lonjakan latensi DNS dan beban CPU CoreDNS yang sangat tinggi.

Solusi Optimasi #

  1. Gunakan Titik di Akhir Domain Luar: Di dalam kode aplikasi, saat memanggil domain luar, akhiri domain tersebut dengan tanda titik (misalnya api.stripe.com.). Tanda titik di akhir memberitahu DNS resolver bahwa ini adalah domain ekstrnal mutlak (FQDN absolute) sehingga resolver akan langsung melakukan kueri ke internet tanpa melakukan lookup pencarian lokal kluster terlebih dahulu.
  2. Gunakan NodeLocal DNSCache: Kita disarankan mendeploy addon NodeLocal DNSCache (DaemonSet pendamping). Addon ini menjalankan agen DNS cache kecil di setiap node worker fisik menggunakan IP loopback lokal node. Kueri DNS dari Pod akan ditangkap oleh agen lokal node ini terlebih dahulu. Jika record DNS sudah ada di cache, respon dikembalikan dalam mikrodetik tanpa perlu melakukan pemanggilan ke CoreDNS pusat.

Anti-Pattern vs Solusi DNS & Service Discovery #

Mari kita pelajari beberapa kesalahan paling fatal terkait manajemen DNS di Kubernetes beserta perbandingan manifes kodenya.

Anti-Pattern 1: Menghubungi Layanan Lintas Namespace Menggunakan Nama Pendek #

Kita mendeploy Pod frontend di namespace frontend-zone dan mengonfigurasinya untuk menghubungi layanan pembayaran di namespace backend-zone hanya dengan menggunakan nama pendek payment-service.

Kode Manifest Salah (Mengandalkan Resolusi DNS Namespace Lokal) #

# JANGAN LAKUKAN INI: Layanan lintas namespace akan gagal di-resolve
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-deployment
  namespace: frontend-zone
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web-front
  template:
    metadata:
      labels:
        app: web-front
    spec:
      containers:
        - name: app
          image: my-front-app:v1.0.0
          env:
            - name: PAYMENT_API_URL
              value: "http://payment-service:8080/charge" # GAGAL: Mencari di frontend-zone.svc.cluster.local

Konsekuensi Buruk #

Saat dijalankan, aplikasi frontend kita akan memicu error Host not found atau NXDOMAIN. DNS resolver di namespace frontend-zone hanya akan mencoba mencari payment-service.frontend-zone.svc.cluster.local yang tentu saja tidak ada.

Kode Solusi (Menggunakan Target Namespace Eksplisit) #

# SOLUSI: Tentukan letak namespace tujuan secara eksplisit
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-deployment
  namespace: frontend-zone
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web-front
  template:
    metadata:
      labels:
        app: web-front
    spec:
      containers:
        - name: app
          image: my-front-app:v1.0.0
          env:
            - name: PAYMENT_API_URL
              value: "http://payment-service.backend-zone.svc.cluster.local:8080/charge" # SUKSES: FQDN lengkap dan aman

Anti-Pattern 2: Menyalahgunakan dnsPolicy: Default pada Pod Aplikasi #

Kita mengonfigurasi properti dnsPolicy: Default pada pod aplikasi mikroservis kita dengan asumsi properti “Default” berarti setelan terbaik bawaan Kubernetes.

Kode Manifest Salah (Memutus Koneksi ke CoreDNS) #

# JANGAN LAKUKAN INI: Pod tidak bisa mencari Service kluster internal
apiVersion: v1
kind: Pod
metadata:
  name: backend-app-isolated
  namespace: production
spec:
  dnsPolicy: Default # JANGAN: Ini mengabaikan CoreDNS kluster dan merujuk ke DNS VM host
  containers:
    - name: app
      image: node:alpine

Konsekuensi Buruk #

Dengan menggunakan dnsPolicy: Default, berkas /etc/resolv.conf di dalam kontainer akan menunjuk langsung ke nameserver eksternal milik VM host node (misalnya IP resolver kantor atau IP DNS cloud provider). Pod backend-app-isolated ini tidak akan pernah bisa melakukan resolusi DNS terhadap Service internal apa pun (seperti auth-service atau database-service) yang berjalan di dalam kluster.

Kode Solusi (Gunakan ClusterFirst atau Kosongkan) #

Biarkan properti dnsPolicy menggunakan nilai default ClusterFirst (cukup kosongkan saja di manifes agar Kubernetes memilihkan ClusterFirst secara otomatis).

# SOLUSI: Menggunakan resolusi DNS kluster standar
apiVersion: v1
kind: Pod
metadata:
  name: backend-app-connected
  namespace: production
spec:
  # dnsPolicy: ClusterFirst (Secara default bernilai ClusterFirst jika dikosongkan)
  containers:
    - name: app
      image: node:alpine

Panduan Praktis Mendiagnosis Masalah DNS Kluster #

Jika aplikasi kita gagal mengenali nama Service, kita dapat melakukan pengecekan kesehatan DNS secara interaktif.

1. Membuat Pod Uji Coba DNS Khusus #

Jalankan kontainer utilitas DNS di dalam namespace yang sama dengan aplikasi kita yang bermasalah:

kubectl run dns-tester --rm -i --tty --image=tutum/dnsutils --namespace=production -- /bin/sh

2. Menguji Resolusi DNS Lokal #

Dari dalam terminal kontainer dns-tester, lakukan kueri nslookup terhadap nama Service lokal kita:

# Uji resolusi service lokal
nslookup auth-service

# Uji resolusi domain Kubernetes default
nslookup kubernetes.default

Jika nslookup mengembalikan pesan server can't find auth-service: NXDOMAIN, periksa apakah Service tersebut benar-benar ada di namespace tersebut:

kubectl get svc -n production

3. Memeriksa Kesehatan Pod CoreDNS #

Jika seluruh kueri DNS gagal (termasuk kubernetes.default), periksa kesehatan pod CoreDNS pusat:

kubectl get pods -n kube-system -l k8s-app=kube-dns

Tinjau log pod CoreDNS untuk mendeteksi adanya error integrasi atau kendala jaringan upstream:

kubectl logs -n kube-system -l k8s-app=kube-dns

Ringkasan #

  • CoreDNS mengelola Service Discovery: Server DNS internal kluster berjalan di namespace kube-system dan secara otomatis memperbarui record DNS seiring perubahan objek Service di kluster.
  • Kubelet menginjeksi resolv.conf: Setiap kontainer mendapatkan file /etc/resolv.conf yang mengarahkan kueri nameserver ke IP kube-dns dan menyediakan search domain lokal.
  • Kueri lintas namespace wajib menuliskan nama namespace target: Gunakan format minimal <service-name>.<namespace> untuk komunikasi lintas namespace, atau gunakan FQDN lengkap demi keamanan.
  • Pahami dampak parameter ndots:5: Kueri domain internet luar dapat memicu overload CPU CoreDNS karena resolver mencoba mencocokkan sufiks lokal kluster terlebih dahulu.
  • Optimalkan performa DNS: Akhiri kueri alamat eksternal dengan tanda titik (misalnya api.stripe.com.) atau deploy addon NodeLocal DNSCache untuk memotong latensi kueri DNS.
  • Gunakan dnsutils untuk troubleshooting: Selalu manfaatkan kontainer utilitas tutum/dnsutils untuk menjalankan perintah nslookup atau dig secara langsung di dalam kluster.

← Sebelumnya: Service   Berikutnya: Ingress →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact