Latar belakang
Bagi organisasi yang mengimplementasikan konsep microservices, akan mengalami kesulitan dalam mengatur penempatan atau deployment aplikasi-aplikasinya ketika aplikasinya semakin banyak, apalagi load-nya yang semakin tinggi. Bukan hanya deployment yang menjadi tantangannya, tapi juga bagaimana memantau aplikasi kita agar selalu berjalan sesuai kebutuhan.
Atas dasar itu diperlukanlah tool untuk membantu mengatasi kesulitan itu, tentu dengan cara otomatis dan seminimal mungkin campur tangan manusia. Sebetulnya ada beberapa tools untuk mengatasi masalah ini, namun dalam tulisan kali ini saya akan fokus memperkenalkan nomad dari Hashicorp.
Apakah nomad ini?
Nomad adalah tool untuk deploy, manage, & scale suatu service atau aplikasi. Tool ini bisa menangani ketiga fitur utama tersebut secara otomatis, itulah mengapa tool semacam ini disebut orchestrator karena seperti seorang konduktor orkestra yang mengatur ritme suatu proses yang ingin dijalankan.
Deploy and Manage Any Application on Any Infrastructure with Ease
Slogan dari nomad ini menyebutkan bahwa kita bisa mengatur berbagai jenis aplikasi. Ya, anda tidak salah baca, “berbagai jenis aplikasi” yang mana bukan hanya berupa container, bisa juga legacy binary app, maupun batch jobs. Lalu dapat dijalankan pada berbagai macam platform infrastructure.
Kenapa & Kapan memerlukan nomad?
Seperti yang sudah saya bocorkan di awal, ada beberapa hal yang akan membuat kita membutuhkan tool semacam ini, di antaranya:
- Services yang semakin banyak
- Menggunakan banyak host server
- Jika kita menginginkan otomatisasi, tentu environment-nya akan bertransformasi dari yang statis menjadi dinamis.
- Kita akan terbantu oleh strategi deployment-nya, yang mana akan menghindarkan aplikasi kita dari downtime, termasuk ketika akan rilis versi baru dari aplikasi kita.
Kenapa tidak Kubernetes?
Ya, tidak dapat dipungkiri, kubernetes sangat populer sebagai orkestrasi service, dan sangat mungkin Anda lebih sering mendengar kubernetes dibanding nomad. Tapi kubernetes mungkin saja tidak cocok atau menjadi pilihan bagi beberapa organisasi. Dan nomad sudah dapat memenuhi kebutuhan minimum dari apa yang kubernetes tawarkan. Beberapa alasan kenapa tidak memilih kubernetes adalah:
- Kurva pembelajaran kubernetes cukup curam. Fiturnya cukup kompleks karena merupakan end to end container platform.
- Bagi sebagian orang kubernetes itu overkill¹. Ketika kita hanya perlu a, b, c, kenapa kita harus ambil sampai j? 😏
- Nomad lebih sederhana, dan kita dapat memutuskan integrasi dengan tool lain untuk memenuhi kebutuhan tambahannya.
- Sebagai tambahan, di efishery, kami mencoba menyelesaikan kebutuhan kami dengan alternatif yang lebih sederhana & cocok, dan yang terpenting, berusaha untuk tidak terbawa hype akan tool yang populer 🤓
Bagaimana cara mereka bekerja?
Istilah dasar yang digunakan pada nomad adalah server dan agent (atau client). Server berperan sebagai controller, sedangkan agent sebagai eksekutor pada masing-masing host. Pada dasarnya nomad harus terpasang pada masing-masing host. Server dan agent akan membaca file konfigurasinya masing-masing, yang mana konfigurasi ini mengatur cara kerja nomad terhadap host dan cluster server kita. Selain file konfigurasi, server memiliki tugas tambahan, yaitu membaca job file. Job file ini yang mengatur cara kerja nomad terhadap aplikasi kita.
Nomad dapat berperan sebagai server maupun client/agent sekaligus, ini bisa dilakukan sebagai latihan, tapi untuk lingkungan production, ini tidak disarankan, dan juga, 3 atau 5 nomad server sangat direkomendasikan demi ketersediaan yang tinggi (high availability). Tujuan akhir latihan ini adalah menghasilkan arsitektur seperti pada gambar di bawah ini:
Saya rasa cukup perkenalannya, mari kita mulai mempelajari cara menggunakannya dan juga cara mereka bekerja. (Di sini saya contohkan dengan menggunakan linux ubuntu).
Install
$ curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
$ sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
$ sudo apt-get update && sudo apt-get install nomad
//verifikasi instalasi nomad
$ nomad
Setelah berhasil install, sekarang coba jalankan sudo nomad agent -dev
, tanpa file konfigurasi, nomad akan berjalan sebagai server dan juga client.
==> No configuration files loaded
==> Starting Nomad agent...
==> Nomad agent configuration:
Advertise Addrs: HTTP: 127.0.0.1:4646; RPC: 127.0.0.1:4647; Serf: 127.0.0.1:4648
Bind Addrs: HTTP: 127.0.0.1:4646; RPC: 127.0.0.1:4647; Serf: 127.0.0.1:4648
Client: true
Log Level: DEBUG
Region: global (DC: dc1)
Server: true
Version: 0.12.1
Untuk melihat host/node mana saja yang sudah terpasang agent, buka terminal baru, dan jalankan nomad node status
pada kondisi nomad server masih berjalan. Karena kita baru menjalankan agent pada 1 host (server yang berperan sebagai client/agent sekaligus), maka akan muncul 1 row node.
ID DC Name Class Drain Eligibility Status
f98051bd dc1 hostname-server <none> false eligible ready
Jika kita menjalankan 3 atau 5 nomad server, maka mereka berkomunikasi dengan menggunakan gossip protocol² agar satu sama lain selalu *up-to-date *dan siap untuk saling menggantikan jika server leader mati. Coba jalankan nomad server members
, maka akan muncul:
Name Address Port Status Leader Protocol Build Datacenter Region
hostname-server.global 127.0.0.1 4648 alive true 2 0.12.1 dc1 global
Job File / JobSpec
Kita bisa memiliki banyak job file atau jobspec, berarti nama jobspec harus unik secara global pada project kita agar tidak bentrok. Selengkapnya silahkan dibaca di dokumen resminya³. Satu jobspec dapat berisi satu atau banyak group. Satu group dapat memiliki satu atau banyak task. Nah*, task *inilah yang menjadi acuan langsung bagi nomad untuk menjalankan aplikasi kita. Mari kita mulai dengan generate kerangka jobspec, sebagai permulaan nomad job init
akan menghasilkan jobspec bernama example.nomad
. Coba perhatikan isinya, setiap property konfigurasi (atau nomad menyebutnya dengan stanza) terdapat penjelasannya. Job generator secara default akan menggunakan task docker. Saat ini nomad mendukung 4 driver, docker
, qemu
, java
dan exec
.
Untuk latihan kali ini, mari fokus pada stanza task. Jobspec generator secara default membuat task cache redis dengan driver docker, dan pada latihan ini akan saya ubah task nya menjadi aplikasi web nginx, berikut modifikasinya:
task "nginx" {
driver = "docker"
config {
image = "nginx:alpine"
port_map {
web = 80
}
}
...
lalu di bawahnya, resources.network.port “web” {}
, seperti ini:
resources {
cpu = 500 # 500 MHz
memory = 256 # 256MB
network {
mbits = 10
port "web" {}
}
}
...
Untuk stanza service
ini menginstruksikan agar nomad mendaftarkan task tersebut kepada service discovery (dalam hal ini consul yang mana tidak dibahas di sini), karena belum akan kita bahas, maka kita beri comment dulu saja.
#service {
# name = "web-nginx"
# tags = ["global", "web"]
# port = "web"
# check {
# name = "alive"
# type = "tcp"
# interval = "10s"
# timeout = "2s"
# }
#}
Jalankan
Mari kita jalankan job pertama kita dengan nomad job run example.nomad
. Jika kita ingin melihat status job, eksekusi command nomad job status <job name>
(nama file akan menjadi job name dan ID) :
$ nomad job status example
ID = example
Name = example
Submit Date = 2020-08-02T18:52:28Z
Type = service
Priority = 50
Datacenters = dc1
Namespace = default
Status = running
Periodic = false
Parameterized = false
Summary
Task Group Queued Starting Running Failed Complete Lost
web 0 0 1 0 0 0
Latest Deployment
ID = c6b89419
Status = successful
Description = Deployment completed successfully
Deployed
Task Group Desired Placed Healthy Unhealthy Progress Deadline
web 1 1 1 0 2020-08-02T19:02:39Z
**Allocations**
ID Node ID Task Group Version Desired Status Created Modified
**e32c1bdf** f98051bd web 5 run running 2m22s ago 2m12s ago
Nomad akan membuat allocation yang merepresentasikan instance dari task group yang ditempatkan pada node (client). Mari kita inspect allocation berdasarkan ID-nya:nomad alloc status **e32c1bdf**
ID = e32c1bdf-2f50-22b9-9e33-fcc86e20461c
Eval ID = fb5f21f1
Name = example.web[0]
Node ID = f98051bd
Node Name = hostname-server
Job ID = example
Job Version = 5
Client Status = running
Client Description = Tasks are running
Desired Status = run
Desired Description = <none>
Created = 5m40s ago
Modified = 5m30s ago
Deployment ID = c6b89419
Deployment Health = healthy
Task "nginx" is "running"
**Task Resources**
CPU Memory Disk Addresses
0/500 MHz 1.8 MiB/256 MiB 300 MiB web: 127.0.0.1:26926
Task Events:
Started At = 2020-08-02T18:52:29Z
Finished At = N/A
Total Restarts = 0
Last Restart = N/A
Recent Events:
Time Type Description
2020-08-02T18:52:29Z Started Task started by client
2020-08-02T18:52:28Z Task Setup Building Task Directory
2020-08-02T18:52:28Z Received Task received by client
Secara default, nomad akan menjalankan aplikasi kita dengan port yang dinamis, dapat dilihat pada “Task Resources”, aplikasi saya berjalan pada port 26926 (port akan berbeda-beda setiap allocation, jadi port di mesin anda pun pasti berbeda dengan contoh latihan kita ini). Sebelum kita coba aplikasi kita, mari cek log di dalamnya dengan nomad alloc logs e32c1bdf
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
Persis seperti ketika kita menjalankan docker nginx secara manual. Sekarang kita coba hit aplikasi web-nya:
$ curl 127.0.0.1:26926
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="[http://nginx.org/](http://nginx.org/)">nginx.org</a>.<br/>
Commercial support is available at
<a href="[http://nginx.com/](http://nginx.com/)">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Sesuai ekspektasi, aplikasi web berjalan di atas docker nginx, mudah bukan? Untuk saat ini, arsitektur nya baru seperti gambar di bawah.
Clustering
Server
Untuk lingkungan production, nomad server dan agent tidak boleh berjalan dalam 1 mesin, maka dari itu mari kita gunakan host tambahan sebagai agent client untuk latihan selanjutnya. Hashicorp membuat jenis konfigurasi baru, bernama HCL (HashiCorp Configuration Language), jika masih kurang familiar, bisa juga menggunakan json. Kita buat konfigurasi pertama kita untuk nomad server:
# Increase log verbosity
log_level = "DEBUG"
# Setup data dir
data_dir = "/tmp/server1"
# Enable the server
server {
enabled = true
# Self-elect, should be 3 or 5 for production
bootstrap_expect = 1
}
Sebenarnya ada banyak variabel stanza yang bisa diatur⁴, tapi untuk kemudahan latihan ini, cukup dengan konfigurasi minimal seperti di atas. Kemudian jalankan server menggunakan konfigurasi yang baru saja kita buat, Anda akan melihat bahwa mode client/agent dimatikan:
$ nomad agent -config server.hcl
==> WARNING: Bootstrap mode enabled! Potentially unsafe operation.
==> Loaded configuration from server.hcl
==> Starting Nomad agent...
==> Nomad agent configuration:
Advertise Addrs: HTTP: server_ip:4646; RPC: server_ip:4647; Serf: server_ip:4648
Bind Addrs: HTTP: 0.0.0.0:4646; RPC: 0.0.0.0:4647; Serf: 0.0.0.0:4648
Client: false
Log Level: DEBUG
Region: global (DC: dc1)
Server: true
Version: 0.12.1
Client
Sama seperti server, untuk konfigurasi client pada latihan kali ini, cukup dengan minimal seperti di bawah ini, dan simpan pada host client pertama:
# Increase log verbosity
log_level = "DEBUG"
# Setup data dir
data_dir = "/tmp/client1"
# Give the agent a unique name. Defaults to hostname
name = "client1"
# Enable the client
client {
enabled = true
# For demo assume we are talking to server1. For production,
# this should be like "nomad.service.consul:4647" and a system
# like Consul used for service discovery.
servers = ["server_ip:4647"]
}
# Modify our port to avoid a collision with server1
ports {
http = 5656
}
Lalu ulangi dan sesuaikan untuk “client2” di host client kedua. Stanza client.servers
menginstruksikan untuk setiap client agar berkomunikasi pada nomad server yang telah kita jalankan di atas tadi. Jalankan dengan perintah sudo nomad agent -config client1.hcl
pada host client 1, dan sudo nomad agent -config client2.hcl
pada host client 2, maka node status akan menampilkan:
$ nomad node status
ID DC Name Class Drain Eligibility Status
7ad4b137 dc1 client1 <none> false eligible ready
8c95c895 dc1 client2 <none> false eligible ready
Update Jobspec
Sekarang mari kita coba ubah jobspec-nya, misalnya ternyata aplikasi kita sudah overload, dan kita membutuhkan horizontal scaling. Ubah group.count
menjadi 2.
group "web" {
# The "count" parameter specifies the number of the task groups that should
# be running under this group. This value must be non-negative and defaults
# to 1.
count = 2
...
Setelah itu, cek jobspec dengan nomad job plan example.nomad
:
+/- Job: "example"
+/- Task Group: "web" (1 create, 1 in-place update)
+/- Count: "1" => "2" (forces create)
Task: "nginx"
Scheduler dry-run:
- All tasks successfully allocated.
Job Modify Index: 169
To submit the job with version verification run:
**nomad job run -check-index 169 example.nomad**
When running the job with the check-index flag, the job will only be run if the
job modify index given matches the server-side version. If the index has
changed, another user has modified the job and the plan's results are
potentially invalid.
Nomad akan konfirmasi perubahan jobspec-nya, dan memberikan index terhadap jobspec update. Dengan nomad job run -check-index 169 example.nomad
, job akan dieksekusi sesuai index yang valid, ini menjaga dari perubahan paralel yang mungkin dilakukan oleh engineer lain. Perlu diingat, setiap host client yang akan kita gunakan harus terinstall docker karena pada latihan ini kita menggunakan nomad driver docker.
Silahkan cek job status-nya, anda akan lihat allocations menjadi 3, dengan status 1 complete, dan 2 running.
Allocations
ID Node ID Task Group Version Desired Status Created Modified
341a1758 8c95c895 web 6 run running 1m8s ago 58s ago
9f5173fc 7ad4b137 web 6 run running 23m3s ago 58s ago
6c14a960 f98051bd web 3 stop complete 54m6s ago 23m31s ago
1 allocation yang complete itu adalah bagian dari job versi sebelumnya, 2 yang baru adalah 2 task yang berjalan sesuai jobspec update yang baru saja kita lakukan. Sekarang kita coba check masing-masing allocation yang running:
Task "nginx" is "running"
Task Resources
CPU Memory Disk Addresses
0/500 MHz 1.9 MiB/256 MiB 300 MiB web: 127.0.0.1:**27458**
====================================================================Task "nginx" is "running"
Task Resources
CPU Memory Disk Addresses
0/500 MHz 1.9 MiB/256 MiB 300 MiB web: 127.0.0.1:**26926**
Dapat kita lihat, sekarang 2 instance aplikasi kita berjalan pada host yang berbeda, agar lebih meyakinkan, coba kita check docker container nya, pada masing-masing host, berjalan container nginx, dengan format nama {taskName}-{allocId}
.
$ docker ps -a
55265f5a0846 nginx:alpine "/docker-entrypoint.…" About a minute ago Up About a minute client1_ip:**27458**->80/tcp, client1_ip:**27458**->80/udp **nginx-9f5173fc-b83b-d532-7e03-c35d3659f816**
====================================================================
$ docker ps -a
1456b1e9f98a nginx:alpine "/docker-entrypoint.…" 2 minutes ago Up 2 minutes client2_ip:**26926**->80/tcp, client2_ip:**26926**->80/udp **nginx-341a1758-1934-c25a-0722-2f3c9762cbee**
Selamat! sekarang anda sudah cukup mengerti dasar dari bagaimana nomad bekerja. Nah sekarang pertanyaanya, jika nomad memberikan port secara dinamis, apalagi aplikasi berjalan lebih dari satu instance, bagaimana aplikasi frontend dapat mengetahui port tersebut? dan bagaimana cara mengakses kedua aplikasi tersebut?
Consul to the rescue!
Ya, dengan consul, setiap aplikasi yang dijalankan nomad akan terdaftar, dan dapat dipantau kesehatannya, juga dapat diintegrasikan dengan berbagai load balancer. Ingin tau cara integrasinya? Tunggu kelanjutan dari tulisan ini 😎
Saya cukupkan sesi tulisan ini. Bila ada kritik dan saran boleh tuangkan saja di komentar. Bila tulisan ini bermanfaat bagi anda, bantu share ya. Terima kasih sudah berkunjung.
staypositive. Ciao!
disclaimer: tulisan ini tidak terafiliasi dengan hashicorp, murni sebatas sharing saja. Bukan juga untuk membandingkan nomad dengan kubernetes, kedua tool tersebut memiliki kekurangan dan kelebihan. Pilihan kembali tergantung masing-masing kebutuhan dan kemampuan.
[1] Matthias Endler | Maybe You Don’t Need Kubernetes https://endler.dev/2019/maybe-you-dont-need-kubernetes/
[2] Gossip Protocol https://www.nomadproject.io/docs/internals/gossip
[3] Nomad Job Specification https://www.nomadproject.io/docs/job-specification
[4] Nomad Configuration https://www.nomadproject.io/docs/configuration