Dynamic Module #
Sebelum Nginx versi 1.9.11 dirilis, setiap kali kita ingin menambahkan modul baru ke dalam web server Nginx kita, kita terpaksa harus mengompilasi ulang seluruh binary Nginx dari source code. Proses ini sangat memakan waktu, memicu risiko kesalahan konfigurasi sistem, dan membutuhkan downtime yang lama karena kita harus menghentikan dan mengganti binary server utama.
Pengenalan Dynamic Module (Modul Dinamis) merevolusi arsitektur modular Nginx. Modul dinamis dikompilasi secara terpisah menjadi berkas pustaka bersama (shared object dengan ekstensi .so) dan dimuat secara dinamis ke dalam memori RAM oleh binary Nginx inti saat runtime menggunakan direktif load_module. Di artikel ini, kita akan membahas perbedaan mendalam arsitektur statis vs dinamis, mengelola lokasi penyimpanan modul, membedah masalah kompatibilitas biner, serta menyusun panduan penulisan modul C dinamis kustom kita sendiri.
Perbandingan Arsitektur: Static vs Dynamic Module #
Untuk memahami perbedaan cara kerja pengompilan dan pemuatan modul statis vs dinamis di memori, perhatikan diagram perbandingan arsitektur berikut:
flowchart TD
subgraph Static["Arsitektur Modul Statis (Cara Lama)"]
SrcCode["Nginx Source + Module Source"] --> Compiler["C Compiler (make)"]
Compiler --> Binary["Single Large Binary (nginx)"]
Binary --> RAMSt["Dimuat Utuh di RAM"]
end
subgraph Dynamic["Arsitektur Modul Dinamis (Cara Baru)"]
SrcNginx["Nginx Core Source"] --> CompilerN["C Compiler"]
CompilerN --> CoreBinary["Small Core Binary (nginx)"]
SrcModule["Module Source (.c)"] --> CompilerM["C Compiler"]
CompilerM --> SharedObject["Shared Object (.so)"]
CoreBinary -.-> LoadModule["load_module di nginx.conf"]
SharedObject -.-> LoadModule
LoadModule --> RAMDy["Dimuat Dinamis di RAM saat Runtime"]
end
classDef default fill:#f9f9f9,stroke:#d1d5db,stroke-width:1px,color:#111827;
classDef stStyle fill:#fee2e2,stroke:#ef4444,stroke-width:2px,color:#991b1b;
classDef dyStyle fill:#d1fae5,stroke:#10b981,stroke-width:2px,color:#065f46;
class Binary,RAMSt stStyle;
class CoreBinary,SharedObject,RAMDy dyStyle;Berikut adalah tabel analisis komparatif fungsional antara kedua jenis modul:
| Karakteristik | Static Module (Cara Lama) | Dynamic Module (Cara Baru) |
|---|---|---|
| Pemuatan Memori | Dikompilasi mati ke dalam satu binary utama. | Dimuat dari file .so eksternal saat startup. |
| Ukuran Binary | Besar, karena memuat seluruh modul bawaan/eksternal. | Kecil dan minimalis (hanya core engine). |
| Kemudahan Pembaruan | Harus kompilasi ulang seluruh Nginx jika ingin tambah modul. | Tinggal ganti atau tambah berkas .so baru di disk. |
| Downtime Update | Tinggi, karena menimpa binary utama yang berjalan. | Rendah/Zero, cukup reload konfigurasi Nginx. |
| Overhead Performa | Sangat Rendah (panggilan fungsi lokal langsung). | Rendah (overhead kecil akibat dynamic linking). |
| Kompatibilitas Versi | 100% Aman (terkunci di level kompilasi binary). | Sangat Ketat (wajib kompilasi dengan versi yang sama). |
Cara Memuat Dynamic Module di nginx.conf #
Untuk mengaktifkan modul dinamis yang telah kita miliki, kita menggunakan direktif load_module. Letak pemanggilan direktif ini wajib berada di bagian paling atas berkas nginx.conf kita (main context), tepat sebelum blok konfigurasi events {} atau http {}.
Contoh Penulisan Sintaksis yang Benar #
# 1. Bagian Teratas Berkas nginx.conf (Main Context)
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
load_module modules/ngx_http_headers_more_filter_module.so;
# 2. Blok Konfigurasi Utama Lainnya
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
# ... konfigurasi web server lainnya ...
}
Path berkas .so yang kita berikan ke direktif load_module dapat ditulis secara relatif terhadap direktori konfigurasi prefix Nginx kita (biasanya /etc/nginx/) atau ditulis secara absolut seperti /usr/lib/nginx/modules/ngx_http_geoip2_module.so.
Manajemen Direktori Modul di Berbagai OS #
Setiap distribusi Linux menempatkan file shared object .so Nginx di lokasi direktori standar yang berbeda:
- Ubuntu / Debian (APT):
- Modul aktif disimpan di
/usr/lib/nginx/modules/. - Sistem Debian menggunakan struktur pemuatan modular: konfigurasi pemuatan disimpan di
/etc/nginx/modules-available/(misalnya50-mod-http-brotli.conf) yang kemudian diaktifkan menggunakan tautan simbolis (symlink) ke/etc/nginx/modules-enabled/.
- Modul aktif disimpan di
- CentOS / RHEL / Rocky Linux (YUM/DNF):
- Modul dinamis disimpan di
/usr/lib64/nginx/modules/atau/etc/nginx/modules/.
- Modul dinamis disimpan di
- Kompilasi Manual dari Source:
- Secara default tersimpan di
/usr/local/nginx/modules/.
- Secara default tersimpan di
Struktur modules-available & modules-enabled (Standard Debian) #
Di sistem Ubuntu/Debian, file nginx.conf bawaan secara otomatis memuat seluruh modul aktif dengan direktif berikut di baris teratasnya:
include /etc/nginx/modules-enabled/*.conf;
Jika kita mengompilasi modul baru, kita sebaiknya membuat file konfigurasinya secara rapi:
# 1. Buat file konfigurasinya
echo "load_module modules/ngx_http_my_module.so;" | sudo tee /etc/nginx/modules-available/60-mod-my-module.conf
# 2. Aktifkan dengan symlink
sudo ln -s /etc/nginx/modules-available/60-mod-my-module.conf /etc/nginx/modules-enabled/
# 3. Uji & reload Nginx
sudo nginx -t && sudo systemctl reload nginx
Hambatan Utama: Kompatibilitas Biner Nginx #
Keterbatasan terpenting yang wajib dipahami saat mengelola dynamic module adalah kompatibilitas biner tingkat ketat (strict binary compatibility).
File modul .so harus dikompilasi menggunakan:
- Versi Nginx yang persis sama dengan versi Nginx berjalan di server produksi. Kita tidak bisa menggunakan modul yang dikompilasi untuk Nginx
1.24.0pada Nginx1.24.1(meskipun hanya berbeda patch version). - Opsi konfigurasi kompilasi (
./configure) yang kompatibel. Jika Nginx utama kita dikompilasi dengan dukungan SSL, modul dinamis kita juga harus dikompilasi dengan menyertakan dukungan yang sama.
Mendeteksi Kegagalan Kompatibilitas Biner #
Jika kita memuat modul yang tidak kompatibel secara biner, Nginx akan menolak menyala dan mengeluarkan pesan galat (error) kritis berikut saat kita menjalankan pengujian konfigurasi (nginx -t):
nginx: [emerg] module "/usr/lib/nginx/modules/ngx_http_brotli_filter_module.so" is not binary compatible in /etc/nginx/nginx.conf:2
Solusi Mengatasi Galat Kompatibilitas #
Untuk menyelesaikan masalah ini, kita harus mengunduh source code Nginx dengan versi yang sesuai, lalu mengompilasi ulang modul .so tersebut dengan menyertakan opsi --with-compat seperti yang telah dibahas pada bagian modul pihak ketiga.
[NEW] Menulis Dynamic Module C Kustom Sederhana #
Untuk memahami bagaimana Nginx berinteraksi dengan modul dinamis di bawah kap, mari kita rancang sebuah modul C dinamis sederhana bernama ngx_http_developer_header_module. Modul ini bertugas menambahkan header HTTP kustom "X-Developer: Antigravity" ke setiap respons yang disajikan oleh server.
Proses pembuatan modul C kustom ini terbagi menjadi 3 file utama:
1. File Source Code C: ngx_http_developer_header_module.c
#
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
// Deklarasi handler request
static ngx_int_t ngx_http_developer_header_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_developer_header_init(ngx_conf_t *cf);
// Konteks modul HTTP
static ngx_http_module_t ngx_http_developer_header_module_ctx = {
NULL, /* preconfiguration */
ngx_http_developer_header_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
// Definisi modul utama Nginx
ngx_module_t ngx_http_developer_header_module = {
NGX_MODULE_V1,
&ngx_http_developer_header_module_ctx, /* module context */
NULL, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
// Fungsi inisialisasi modul ke dalam siklus Nginx
static ngx_int_t
ngx_http_developer_header_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
// Sisipkan handler kita ke dalam fase HTTP HEADERS_FILTER
h = ngx_array_push(&cmcf->phases[NGX_HTTP_HEADERS_FILTER_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_developer_header_handler;
return NGX_OK;
}
// Logika pemrosesan header respons
static ngx_int_t
ngx_http_developer_header_handler(ngx_http_request_t *r)
{
ngx_table_elt_t *h;
// Alokasikan memori untuk header baru
h = ngx_list_push(&r->headers_out.headers);
if (h == NULL) {
return NGX_ERROR;
}
// Setel key header: X-Developer
h->hash = 1;
ngx_str_set(&h->key, "X-Developer");
ngx_str_set(&h->value, "Antigravity");
return NGX_DECLINED; // Lanjutkan proses ke filter berikutnya
}
Bedah Komponen dan Anatomi Kode C Modul Nginx #
Menulis modul dalam bahasa C untuk Nginx membutuhkan pemahaman mendalam tentang prinsip desain core engine Nginx:
Tipe Data
ngx_str_t: Nginx tidak menggunakan string standar C (char*) yang diakhiri dengan karakter null\0. Sebagai gantinya, Nginx mendefinisikan tipe datangx_str_tyang memiliki struktur:typedef struct { size_t len; // Panjang string u_char *data; // Pointer ke array karakter } ngx_str_t;Desain ini memiliki dua keuntungan besar. Pertama, Nginx tidak perlu memanggil fungsi
strlen()yang memakan resource CPU untuk menghitung panjang teks. Kedua, Nginx dapat melakukan pemotongan string (substring slicing) secara aman hanya dengan menggeser pointerdatadan memperkecillen, tanpa perlu mengalokasikan memori baru di RAM (zero-copy operation).Manajemen Memori Pool (
r->pool): Di dalam modul C Nginx, kita tidak menggunakan fungsi alokasi memori standar C sepertimalloc()ataufree(). Nginx mengimplementasikan sistem Memory Pool. Setiap permintaan HTTP yang masuk memiliki pool memori masing-masing (r->pool).Ketika kita ingin mengalokasikan data (seperti menambahkan header), kita memanggil fungsi
ngx_palloc(r->pool, size). Nginx akan mengambil ruang memori dari pool yang sudah dialokasikan di awal. Keuntungan utamanya adalah kita tidak perlu membebaskan memori secara manual menggunakanfree(). Saat transaksi HTTP selesai dan koneksi ditutup, Nginx akan otomatis menghancurkan seluruh pool memori tersebut secara massal. Sistem ini secara total meniadakan risiko kebocoran memori (memory leaks) akibat kelalaian pengembang.Fase Penyaringan Header (
HEADERS_FILTER_PHASE): Pada fungsingx_http_developer_header_init, modul kita mendaftarkan diri ke dalam faseNGX_HTTP_HEADERS_FILTER_PHASE. Ini adalah fase penyaringan tepat sebelum header respons dikirim ke soket jaringan. Di sini, kita memanggilngx_list_pushuntuk menambahkan elemen baru ke dalam daftar rantai linked-listr->headers_out.headersyang menampung seluruh header HTTP keluar.
2. Berkas Konfigurasi Compiler: config
#
Nginx membutuhkan file bernama config (tanpa ekstensi) di dalam direktori modul kita untuk memberi tahu compiler cara membangun berkas .so:
ngx_addon_name=ngx_http_developer_header_module
if test -n "$ngx_module_link"; then
ngx_module_type=HTTP
ngx_module_name=ngx_http_developer_header_module
ngx_module_srcs="$ngx_addon_dir/ngx_http_developer_header_module.c"
. auto/module
else
HTTP_MODULES="$HTTP_MODULES ngx_http_developer_header_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_developer_header_module.c"
fi
ngx_module_link: Variabel ini diperiksa oleh skrip configure Nginx. Jika kita mengompilasi sebagai dynamic module (menggunakan--add-dynamic-module), variabel ini akan bernilaiDYNAMICdan Nginx akan membangun file shared object.so. Jika bernilai kosong, modul akan dibangun secara statis ke dalam binary utama Nginx.$ngx_addon_dir: Variabel otomatis yang merujuk pada path absolut folder tempat modul kustom kita disimpan saat kita memanggil skrip./configure.
3. Langkah Kompilasi Menjadi berkas .so
#
Letakkan kedua file di atas di dalam folder kustom (misal /home/user/my_module/), lalu jalankan kompilasi dari dalam direktori source code Nginx kita:
cd /home/user/nginx-1.24.0
./configure --with-compat --add-dynamic-module=/home/user/my_module/
make modules
Setelah kompilasi selesai, file binary dinamis ngx_http_developer_header_module.so akan tercipta di dalam direktori objs/. Salin file ini ke /etc/nginx/modules/ dan panggil menggunakan load_module di nginx.conf kita.
Alur Kerja Otomatisasi Upgrade Server (CI/CD Best Practices) #
Karena keterbatasan kompatibilitas biner, pembaruan paket Nginx otomatis (seperti apt upgrade) dapat memicu kegagalan startup server jika dynamic module kompilasi mandiri kita tidak diperbarui bersamaan.
Untuk mengotomatisasi ini di lingkungan produksi tingkat lanjut, kita dapat menerapkan praktik berikut:
- Gunakan DPKG Hooks (Ubuntu/Debian):
Kita dapat membuat skrip di
/etc/apt/apt.conf.d/99compile-modulesyang otomatis memicu git pull dan kompilasi ulang modul shared object setiap kali ada deteksi instalasi versi baru paketnginx-coreataunginx-common. - Pipeline CI/CD Pusat:
Gunakan Docker container atau Gitlab Runner untuk secara periodik memeriksa pembaruan Nginx. Pipeline otomatis mengompilasi file
.sountuk setiap versi Nginx baru, mengujinya menggunakannginx -tdi container isolasi, lalu mendistribusikannya ke seluruh armada server produksi kita jika tes sukses.
Ringkasan dan Praktik Terbaik #
- Wajib Gunakan load_module di Main Context: Pastikan baris direktif
load_moduletidak pernah ditulis di dalam blokhttp {}atauevents {}. Menulis di dalam blok konfigurasi tersebut akan memicu kegagalan sintaksis kritis saat startup Nginx.- Gunakan Paket Distribusi Jika Memungkinkan: Jika modul dinamis yang kita butuhkan sudah disediakan oleh paket repositori resmi Linux kita (misalnya
libnginx-mod-http-brotli), prioritaskan instalasi via paket manager (aptatauyum) karena pembaruan kompatibilitas biner akan dikelola secara otomatis oleh OS.- Reload, Bukan Restart: Gunakan
systemctl reload nginx(ataunginx -s reload) saat mengaktifkan modul dinamis baru. Langkah ini menjamin proses pemuatan modul ke memori worker baru terjadi tanpa pemutusan koneksi klien aktif yang sedang berjalan.