Setelah berhasil menjalankan job di Nomad dan mendaftarkan service di Consul, sekarang kita akan menambahkan layer reverse proxy menggunakan Caddy. Dengan setup ini, kita tidak perlu lagi mengakses aplikasi melalui IP dan port acak, melainkan melalui domain name yang mudah diingat.
Seperti yang sudah dijelaskan di artikel pertama, Caddy akan diinstall di seluruh node dalam cluster. Setiap node menjalankan Caddy dengan public IP masing-masing, sehingga traffic dapat masuk melalui node mana pun.
Alur Kerja
Sebelum masuk ke tahap instalasi, mari kita pahami dulu bagaimana semua komponen bekerja bersama:
- Nomad menjalankan workload (container) di node mana saja dalam cluster
- Consul mencatat semua service yang berjalan beserta alamat dan port-nya
- consul-template memantau perubahan di Consul dan menggenerate konfigurasi Caddy secara otomatis
- Caddy membaca konfigurasi tersebut dan melakukan routing ke service yang tepat
Dengan alur ini, setiap kali ada service baru di-deploy, di-scale, atau dihentikan, routing akan ter-update secara otomatis tanpa intervensi manual.
Build Caddy dengan Custom Plugin
Kita akan menggunakan custom build Caddy yang menyertakan plugin caddy-tlsconsul dan cloudflare. Plugin caddy-tlsconsul memungkinkan Caddy menyimpan sertifikat TLS di Consul KV store, sehingga semua node dapat berbagi sertifikat yang sama.
Plugin Cloudflare di-build juga tapi tidak kita gunakan di artikel ini. Kita akan menggunakan HTTP challenge untuk mendapatkan sertifikat TLS dari Let’s Encrypt.
Install Go dan xcaddy
Proses build akan dilakukan di workstation lokal kita, kemudian binary-nya
akan didistribusikan ke semua node menggunakan scp.
Pastikan Go sudah terinstall di workstation kita. Jika belum, ikuti cara install di https://go.dev/doc/install.
Setelah Go terinstall, install xcaddy untuk membangun Caddy dengan plugin:
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
# Tambahkan go bin ke PATH jika belum
echo 'export PATH=$PATH:~/go/bin' >> ~/.bashrc
source ~/.bashrc
Build Caddy
Sekarang kita bisa build Caddy dengan plugin yang dibutuhkan:
xcaddy build \
--with github.com/pteich/caddy-tlsconsul \
--with github.com/caddy-dns/cloudflare
Proses build akan memakan waktu beberapa menit. Setelah selesai, akan ada file
binary caddy di direktori saat ini.
Verifikasi build berhasil:
./caddy version
# Output:
v2.x.x h1:...
Install Caddy dari Repository
Sebelum mendistribusikan binary custom, kita install Caddy default dari repository resmi terlebih dahulu di setiap node untuk mendapatkan systemd service dan struktur direktori yang sudah dikonfigurasi dengan baik:
# Tambahkan repository Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
Instalasi ini akan otomatis membuat:
- User dan group
caddy - Systemd service yang sudah dikonfigurasi dengan security hardening
- Direktori
/etc/caddyuntuk konfigurasi - Binary default di
/usr/bin/caddy
Kita akan mengganti binary default dengan versi custom yang sudah kita build dengan plugin caddy-tlsconsul dan cloudflare.
Mengganti Binary dengan Versi Custom
Setelah Caddy default terinstall di semua node, sekarang kita ganti binary-nya
dengan versi custom yang sudah kita build di workstation. Salin binary ke semua
node menggunakan scp atau alternatif lain seperti croc
atau rsync seperti yang disebutkan di artikel sebelumnya.
# Copy ke node-1
scp caddy [email protected]:/tmp/caddy
# Copy ke node-2
scp caddy [email protected]:/tmp/caddy
# Copy ke node-3
scp caddy [email protected]:/tmp/caddy
Setelah binary tersalin, login ke setiap node dan jalankan perintah berikut:
# Stop service dulu sebelum replace binary
sudo systemctl stop caddy
# Pindahkan binary ke /usr/bin (replace yang lama)
sudo mv /tmp/caddy /usr/bin/caddy
# Set permission
sudo chmod +x /usr/bin/caddy
# Verifikasi - seharusnya menampilkan custom build dengan plugin
caddy version
# Pastikan plugin consul ada
caddy list-modules | grep consul
Verifikasi Systemd Service
Systemd service untuk Caddy sudah otomatis dibuat oleh paket apt. Kita bisa verifikasi konfigurasinya:
# Lihat status service
systemctl status caddy
# Lihat konfigurasi lengkap service
systemctl cat caddy
Service default dari apt sudah memiliki konfigurasi yang baik dengan security hardening yang tepat. Kita tidak perlu membuat atau memodifikasi service file.
💡 Catatan
Service berjalan sebagai usercaddyyang dibuat otomatis saat instalasi. File konfigurasi di/etc/caddysudah memiliki permission yang tepat untuk user tersebut.
Modifikasi Service untuk Auto-Restart
Service Caddy dari paket apt sudah bagus, namun kita perlu menambahkan konfigurasi restart otomatis. Ini penting karena saat sistem startup, Caddy mungkin gagal start karena mencoba menghubungi Consul yang belum siap.
Buat file override untuk systemd service di setiap node:
sudo systemctl edit caddy
Tambahkan konfigurasi berikut:
[Service]
Restart=always
RestartSec=5
Simpan dan keluar dari editor. Systemd akan membuat file override di
/etc/systemd/system/caddy.service.d/override.conf.
Penjelasan singkat:
Restart=alwaysmembuat Caddy otomatis restart jika gagal start atau crash.RestartSec=5memberi jeda 5 detik sebelum restart agar Consul sempat siap.
Verifikasi konfigurasi sudah diterapkan:
systemctl cat caddy
Reload systemd untuk memastikan override dibaca:
sudo systemctl daemon-reload
Konfigurasi Caddy
Direktori /etc/caddy sudah dibuat otomatis oleh paket apt. Berbeda dengan
setup tradisional, kita tidak akan membuat Caddyfile secara manual.
Seluruh konfigurasi Caddy, termasuk global options dan routing, akan di-generate secara otomatis oleh consul-template berdasarkan service yang terdaftar di Consul. Pendekatan ini membuat setup menjadi sepenuhnya dinamis - tidak ada konfigurasi hardcoded yang perlu diubah saat menambah atau menghapus service.
Caddy akan membaca /etc/caddy/Caddyfile yang akan dibuat oleh consul-template
pada langkah berikutnya.
Setup Consul Template
Consul template akan memantau service catalog di Consul dan menghasilkan seluruh konfigurasi Caddy secara dinamis, termasuk global options dan routing. Setiap kali ada perubahan (service baru, scale up/down, service dihapus), konfigurasi akan di-update dan Caddy akan di-reload secara otomatis.
Membuat Direktori
Pertama, buat direktori untuk template dan konfigurasi consul-template di setiap node:
sudo mkdir -p /consul/config/templates
sudo mkdir -p /etc/consul-template.d
Direktori /consul/config/templates digunakan untuk menyimpan template file
(.ctmpl), sedangkan /etc/consul-template.d untuk menyimpan file konfigurasi
consul-template (.hcl).
💡 Mengapa Direktori
/consul/config?
Direktori ini dipilih karena satu namespace dengan konfigurasi Consul yang sudah kita setup sebelumnya. Ini memudahkan manajemen file yang berhubungan dengan Consul dan ekosistemnya dalam satu lokasi.
Membuat Systemd Service
Buat file /etc/systemd/system/consul-template.service di setiap node:
[Unit]
Description=Consul Template
Requires=network-online.target
After=network-online.target consul.service
[Service]
Restart=always
ExecStart=/usr/bin/consul-template -config=/etc/consul-template.d
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
PIDFile=/run/consul-template.pid
TimeoutSec=10s
[Install]
WantedBy=multi-user.target
Dengan -config=/etc/consul-template.d, consul-template akan membaca semua file
.hcl di direktori tersebut. Ini lebih fleksibel jika nanti kita menambah
template lain.
Setelah service dibuat, reload systemd:
sudo systemctl daemon-reload
Membuat File Konfigurasi
Buat file /etc/consul-template.d/caddy.hcl di setiap node:
consul {
token = "<bootstrap-token-from-consul>"
}
template {
source = "/consul/config/templates/caddy.ctmpl"
destination = "/etc/caddy/Caddyfile"
command = "systemctl reload caddy"
wait {
min = "5s"
max = "15s"
}
}
Penjelasan singkat:
sourcemengarah ke template file yang akan kita buat berikutnya.destinationlangsung ke/etc/caddy/Caddyfile, jadi seluruh Caddyfile di-generate otomatis.commandakan me-reload Caddy setelah file berubah.waitmencegah reload berlebihan jika ada banyak perubahan berdekatan.
Membuat Template File
Sekarang buat template file di /consul/config/templates/caddy.ctmpl:
{{- $SERVER_NAME := "fe1" -}}
{
storage consul {
address "localhost:8500"
token "<bootstrap-token-from-consul>"
timeout 300
}
}
{{ range services }}
{{- $domains := sprig_list }}
{{- range service .Name }}
{{- range $k, $v := .ServiceMeta }}
{{- if eq $k "domain" }}
{{- $domains = sprig_append $domains $v }}
{{- $domains = $domains | sprig_uniq }}
{{- end }}
{{- end }}
{{- end }}
{{- if gt (len $domains) 0 }}
{{- $domains | sprig_join "," }} {
handle /* {
reverse_proxy {{ range service .Name }}{{ .Address }}:{{ .Port }} {{ end }} {
lb_policy round_robin
lb_retries 5
{{- $sets := sprig_list }}
{{- range service .Name }}
{{- range $k, $v := .ServiceMeta }}
{{- if not (in ($sets | sprig_join ",") $k) }}
{{- if in $k "health" }}
{{ $k }} {{ $v }}
{{- $sets = sprig_append $sets $k -}}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
}
header >Server {{ $SERVER_NAME }}
header >Via {{ $SERVER_NAME }}
}
}
{{ end }}
{{- end }}
Template ini menggunakan Go template dengan Sprig functions untuk logic tambahan. Dokumentasi Sprig ada di http://masterminds.github.io/sprig/.
Penjelasan Singkat
- Template membaca ServiceMeta dari setiap service dan mencari key
domain. - Jika ada domain, Caddy akan membuat site block untuk domain tersebut.
- Semua instance service akan dimasukkan ke
reverse_proxydengan load balancing. - Health check diambil dari ServiceMeta yang memiliki prefix
health. - Header
ServerdanViaditambahkan untuk identifikasi node.
Enable dan Start Consul Template
sudo systemctl enable --now consul-template
# Cek status
sudo systemctl status consul-template
Consul-template akan mulai memantau Consul dan menghasilkan /etc/caddy/Caddyfile.
Kita bisa cek hasilnya:
cat /etc/caddy/Caddyfile
Update Job Nomad dengan ServiceMeta
Agar service kita bisa di-detect oleh consul-template dan mendapatkan routing
otomatis, kita perlu menambahkan ServiceMeta dengan key domain pada
service definition. Edit file snake.hcl di workstation:
job "snake-web" {
datacenters = ["dc1"]
group "snake" {
count = 1
service {
name = "snake-web"
port = "http"
# ServiceMeta untuk konfigurasi domain dan health check
meta {
domain = "snake-web.example.com"
health_uri = "/"
health_interval = "10s"
health_timeout = "5s"
}
check {
type = "http"
path = "/"
interval = "3s"
timeout = "2s"
}
}
network {
port "http" {
to = 8080
}
}
task "snake" {
driver = "docker"
config {
image = "aschil/snake:latest"
ports = ["http"]
}
resources {
cpu = 50
memory = 100
}
}
}
}
Update job di cluster:
nomad job run snake.hcl
Setelah job ter-update, consul-template akan mendeteksi ServiceMeta domain
dan menghasilkan konfigurasi Caddy baru. Kita bisa verifikasi dengan melihat
log consul-template:
sudo journalctl -u consul-template -f
Start Caddy dan Testing
Sekarang kita bisa start Caddy di semua node:
sudo systemctl start caddy
# Cek status
sudo systemctl status caddy
# Lihat log jika ada error
sudo journalctl -u caddy -f
Verifikasi Konfigurasi yang Di-generate
Setelah consul-template mendeteksi service dengan ServiceMeta domain,
konfigurasi Caddy akan di-generate. Mari kita periksa hasilnya:
cat /etc/caddy/Caddyfile
Setup DNS dan TLS Certificate
Untuk mendapatkan sertifikat TLS dari Let’s Encrypt menggunakan HTTP challenge pada setup multi-node seperti ini, kita perlu strategi khusus:
Langkah 1: Gunakan Satu IP Dulu
Tambahkan hanya satu IP (misalnya node-1) ke DNS A record di Cloudflare:
A snake-web.example.com 203.0.113.10
Tunggu propagasi DNS (beberapa menit), kemudian akses https://snake-web.example.com
dari browser. Caddy akan otomatis meminta sertifikat dari Let’s Encrypt menggunakan
HTTP challenge.
Langkah 2: Tambahkan IP Lainnya
Setelah sertifikat berhasil didapatkan (cek dengan membuka domain di browser), tambahkan IP node lainnya ke DNS:
A snake-web.example.com 203.0.113.10
A snake-web.example.com 203.0.113.11
A snake-web.example.com 203.0.113.12
Dengan setup storage Consul, sertifikat yang sudah didapatkan akan ter-sync ke semua node. Jadi semua node dapat melayani HTTPS dengan sertifikat yang sama.
💡 Mengapa Perlu Strategi Ini?
Pada HTTP challenge, Let’s Encrypt akan mengirim request ke domain untuk memverifikasi kepemilikan. Jika DNS memiliki multiple A record, request bisa masuk ke node mana saja. Node yang menerima challenge belum tentu node yang mengajukan permintaan sertifikat, sehingga validasi bisa gagal.Dengan menggunakan satu IP dulu, kita pastikan request challenge masuk ke node yang tepat. Setelah sertifikat didapat dan tersimpan di Consul KV, semua node dapat mengaksesnya.
Verifikasi Routing
Buka browser dan akses https://snake-web.example.com. Kamu seharusnya bisa
melihat game snake yang berjalan.
Sekarang kita sudah memiliki setup yang sepenuhnya otomatis:
- Deploy job baru di Nomad dengan ServiceMeta
domain - Nomad mendaftarkan service ke Consul
- Consul-template mendeteksi service baru
- Konfigurasi Caddy di-generate otomatis
- Caddy di-reload dan langsung bisa route traffic ke service baru
Tidak ada konfigurasi manual yang perlu dilakukan. Semua berjalan otomatis.
Untuk melihat bagaimana sistem merespons perubahan, mari kita scale up aplikasi
snake-web dari 1 instance menjadi 3 instance. Edit file snake.hcl di
workstation, ubah count menjadi 3:
job "snake-web" {
datacenters = ["dc1"]
group "snake" {
count = 3
service {
name = "snake-web"
port = "http"
meta {
domain = "snake-web.example.com"
health_uri = "/"
health_interval = "10s"
health_timeout = "5s"
}
check {
type = "http"
path = "/"
interval = "3s"
timeout = "2s"
}
}
network {
port "http" {
to = 8080
}
}
task "snake" {
driver = "docker"
config {
image = "aschil/snake:latest"
ports = ["http"]
}
resources {
cpu = 50
memory = 100
}
}
}
}
Deploy perubahan:
nomad job run snake.hcl
Nomad akan men-deploy 2 instance tambahan. Lihat status deployment:
nomad job status snake-web
Setelah deployment selesai, consul-template akan otomatis mendeteksi ada 3 instance service dan meng-update Caddyfile. Tunggu beberapa detik (sesuai wait time 5-15 detik), kemudian cek Caddyfile:
cat /etc/caddy/Caddyfile
Seharusnya sekarang ada 3 upstream di dalam reverse_proxy.
Penutup
Dengan setup ini, kita sudah memiliki cluster Nomad yang lengkap dengan:
- Service discovery otomatis menggunakan Consul
- Dynamic reverse proxy dengan Caddy dan consul-template
- TLS certificate management otomatis dari Let’s Encrypt
- Multi-node setup untuk high availability
- Load balancing otomatis untuk service yang di-scale
Alur kerja deployment menjadi sangat sederhana:
# 1. Buat job file dengan service tag "web"
# 2. Deploy ke Nomad
nomad job run myapp.hcl
# 3. Tambahkan DNS record
# 4. Done! Service otomatis accessible via HTTPS
Semua proses routing, TLS, dan load balancing ditangani secara otomatis oleh stack Nomad → Consul → consul-template → Caddy.
⚠️ Production Considerations
Setup ini cocok untuk belajar dan deployment skala kecil. Untuk production, pertimbangkan:
- Pisahkan node server dan client Nomad
- Gunakan private network untuk komunikasi internal
- Implementasi ACL yang lebih granular
- Monitoring dan alerting
- Backup regular untuk Consul state
- Rate limiting dan security headers di Caddy
- Dedicated load balancer di depan node Caddy
Selamat! Kamu sudah berhasil membuat cluster orchestration dengan HashiCorp Nomad. 🎉