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:

KarakteristikStatic Module (Cara Lama)Dynamic Module (Cara Baru)
Pemuatan MemoriDikompilasi mati ke dalam satu binary utama.Dimuat dari file .so eksternal saat startup.
Ukuran BinaryBesar, karena memuat seluruh modul bawaan/eksternal.Kecil dan minimalis (hanya core engine).
Kemudahan PembaruanHarus kompilasi ulang seluruh Nginx jika ingin tambah modul.Tinggal ganti atau tambah berkas .so baru di disk.
Downtime UpdateTinggi, karena menimpa binary utama yang berjalan.Rendah/Zero, cukup reload konfigurasi Nginx.
Overhead PerformaSangat Rendah (panggilan fungsi lokal langsung).Rendah (overhead kecil akibat dynamic linking).
Kompatibilitas Versi100% 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/ (misalnya 50-mod-http-brotli.conf) yang kemudian diaktifkan menggunakan tautan simbolis (symlink) ke /etc/nginx/modules-enabled/.
  • CentOS / RHEL / Rocky Linux (YUM/DNF):
    • Modul dinamis disimpan di /usr/lib64/nginx/modules/ atau /etc/nginx/modules/.
  • Kompilasi Manual dari Source:
    • Secara default tersimpan di /usr/local/nginx/modules/.

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:

  1. Versi Nginx yang persis sama dengan versi Nginx berjalan di server produksi. Kita tidak bisa menggunakan modul yang dikompilasi untuk Nginx 1.24.0 pada Nginx 1.24.1 (meskipun hanya berbeda patch version).
  2. 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 data ngx_str_t yang 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 pointer data dan memperkecil len, 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 seperti malloc() atau free(). 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 menggunakan free(). 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 fungsi ngx_http_developer_header_init, modul kita mendaftarkan diri ke dalam fase NGX_HTTP_HEADERS_FILTER_PHASE. Ini adalah fase penyaringan tepat sebelum header respons dikirim ke soket jaringan. Di sini, kita memanggil ngx_list_push untuk menambahkan elemen baru ke dalam daftar rantai linked-list r->headers_out.headers yang 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 bernilai DYNAMIC dan 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:

  1. Gunakan DPKG Hooks (Ubuntu/Debian): Kita dapat membuat skrip di /etc/apt/apt.conf.d/99compile-modules yang otomatis memicu git pull dan kompilasi ulang modul shared object setiap kali ada deteksi instalasi versi baru paket nginx-core atau nginx-common.
  2. Pipeline CI/CD Pusat: Gunakan Docker container atau Gitlab Runner untuk secara periodik memeriksa pembaruan Nginx. Pipeline otomatis mengompilasi file .so untuk setiap versi Nginx baru, mengujinya menggunakan nginx -t di 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_module tidak pernah ditulis di dalam blok http {} atau events {}. 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 (apt atau yum) karena pembaruan kompatibilitas biner akan dikelola secara otomatis oleh OS.
  • Reload, Bukan Restart: Gunakan systemctl reload nginx (atau nginx -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.

← Sebelumnya: Modul Pihak Ketiga   Berikutnya: Node.js App →

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