Tren aplikasi berbasis Large Language Model (LLM) membuat makin banyak Developer ingin menjalankan model AI sendiri di infrastruktur yang dikuasai penuh, entah karena alasan biaya, privasi data, atau sekadar ingin lepas dari rate limit API pihak ketiga. Tantangannya, model AI punya kebutuhan resource yang jauh berbeda dari aplikasi web biasa: ukuran image gampang membengkak sampai puluhan gigabyte, prosesnya rakus memori, dan performanya sering bergantung penuh pada akses GPU yang oleh Docker tidak diekspos secara otomatis ke dalam container. Bab ini membahas cara containerize aplikasi berbasis LLM memakai Ollama, mulai dari setup dasar, mengaktifkan akses GPU NVIDIA lewat nvidia-container-toolkit, mengatur batas resource yang wajar, sampai memasang API gateway di depan model serving supaya siap dipakai aplikasi lain.
35.1 Containerizing Aplikasi Berbasis LLM dengan Ollama
Ollama adalah runtime open-source untuk menjalankan model bahasa besar secara lokal, membungkus proses download, manajemen versi model, dan inference server jadi satu command line interface yang sederhana. Ollama sudah menyediakan official image di Docker Hub bernama ollama/ollama, sehingga tidak perlu menulis Dockerfile sendiri dari nol untuk kebutuhan menjalankan model siap pakai seperti Llama atau Mistral.
35.1.1 Menjalankan Ollama Server dengan Docker Compose
Developer yang baru mengenal Ollama biasanya mulai dengan docker run satu baris, tapi begitu kebutuhan berkembang ke persistensi model dan integrasi dengan service lain, compose.yaml jadi cara yang jauh lebih rapi untuk mengelola konfigurasinya. Buat compose.yaml berikut untuk menjalankan Ollama server.
services:
ollama:
image: ollama/ollama:0.34.0
container_name: ollama
ports:
- "11434:11434"
volumes:
- ollama-data:/root/.ollama
restart: unless-stopped
volumes:
ollama-data:Volume ollama-data dipetakan ke /root/.ollama, direktori tempat Ollama menyimpan model yang sudah di-pull. Tanpa volume ini, setiap kali container dibuat ulang, seluruh model berukuran gigabyte harus di-download lagi dari awal, sesuatu yang jelas buang-buang bandwidth dan waktu di lingkungan production. Tag versi 0.34.0 sengaja dipakai secara eksplisit alih-alih latest, mengikuti kebiasaan image tagging yang sudah dibahas pada bab-bab sebelumnya, supaya perilaku inference tidak berubah tiba-tiba akibat update otomatis yang tidak terduga.
Jalankan stack, lalu pull model lewat docker exec ke dalam container yang sudah berjalan.
docker compose up -d
docker exec -it ollama ollama pull llama3.2Setelah model selesai di-download, uji endpoint inference-nya lewat curl untuk memastikan API Ollama merespons dengan benar.
curl http://localhost:11434/api/generate -d '{
"model": "llama3.2",
"prompt": "Jelaskan apa itu container dalam satu kalimat",
"stream": false
}'Respons JSON berisi field response menandakan model sudah aktif dan siap menerima request inference. Kalau respons berupa error model not found, biasanya nama model salah ketik atau proses pull di langkah sebelumnya belum selesai sepenuhnya.
35.1.2 Menghubungkan Aplikasi Backend ke Ollama
Aplikasi backend yang perlu memanggil LLM cukup terhubung ke service ollama lewat network Compose yang sama, tanpa perlu mengekspos port 11434 ke host kalau memang tidak ada kebutuhan akses langsung dari luar. Tambahkan service app berikut ke dalam compose.yaml yang sama.
services:
app:
build: ./app
environment:
OLLAMA_HOST: http://ollama:11434
depends_on:
- ollama
networks:
- default
ollama:
image: ollama/ollama:0.34.0
volumes:
- ollama-data:/root/.ollama
networks:
- default
networks:
default:
volumes:
ollama-data:Environment variable OLLAMA_HOST di sini cuma konvensi penamaan bebas di sisi kode aplikasi, bukan variabel bawaan Ollama, jadi sesuaikan namanya dengan variabel yang benar-benar dibaca kode backend. Nama service ollama bisa langsung dipakai sebagai hostname karena Compose otomatis meletakkan kedua service dalam network internal yang sama, pola DNS antar-container yang sama seperti pada studi kasus aplikasi lain. depends_on memastikan container ollama mulai lebih dulu, meski perlu dicatat bahwa opsi ini cuma menunggu container berstatus running, bukan menunggu model API-nya benar-benar siap menerima request; tambahkan retry logic sederhana di sisi kode aplikasi kalau race condition semacam ini jadi masalah nyata di lapangan.
35.2 GPU Access dalam Container dengan nvidia-container-toolkit
Menjalankan model LLM di atas CPU saja masih mungkin untuk model kecil, tapi latency-nya jauh lebih lambat dibanding memakai GPU, apalagi untuk model dengan miliaran parameter. Docker secara default mengisolasi container dari hardware host, termasuk GPU, sehingga dibutuhkan komponen tambahan bernama NVIDIA Container Toolkit supaya container bisa mengakses GPU NVIDIA yang terpasang di host.
35.2.1 Instalasi NVIDIA Container Toolkit
Instalasi ini dilakukan di level host, bukan di dalam Dockerfile, karena toolkit ini bekerja dengan memodifikasi runtime Docker di host supaya bisa memetakan device GPU ke dalam container. Driver NVIDIA untuk kartu grafis yang dipakai wajib sudah terpasang lebih dulu di host sebelum toolkit ini diinstal, sesuai prasyarat resmi dari dokumentasi NVIDIA Container Toolkit. Di distribusi Linux berbasis Debian/Ubuntu, tambahkan repository resmi NVIDIA terlebih dahulu.
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
| sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
| sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.listUpdate daftar package dan instal toolkit-nya.
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkitUntuk distribusi berbasis RHEL/Fedora seperti CentOS atau Rocky Linux, gunakan dnf setelah menambahkan repository yang sesuai lewat dnf config-manager, mengikuti langkah setara di dokumentasi resmi NVIDIA untuk distribusi berbasis yum/dnf. Setelah toolkit terpasang, konfigurasikan Docker daemon supaya mengenali runtime NVIDIA, lalu restart service Docker agar konfigurasi baru diterapkan.
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart dockerPerintah nvidia-ctk runtime configure menulis konfigurasi runtime NVIDIA ke /etc/docker/daemon.json secara otomatis, jadi tidak perlu mengedit file tersebut secara manual. Windows lewat WSL2 dan Docker Desktop punya jalur instalasi berbeda, memanfaatkan driver NVIDIA yang sudah terintegrasi dengan WSL2 tanpa perlu instalasi toolkit terpisah di dalam distribusi Linux WSL, sesuai dokumentasi resmi NVIDIA untuk CUDA di WSL2, jadi sebaiknya sysadmin/DevOps Engineer memverifikasi ulang jalur instalasi yang sesuai dengan versi Docker Desktop yang dipakai.
35.2.2 Verifikasi Akses GPU dari Container
Sebelum menghubungkan GPU ke Ollama, verifikasi dulu instalasi toolkit dengan menjalankan image uji nvidia-smi resmi dari NVIDIA.
docker run --rm --gpus all nvidia/cuda:12.6.0-base-ubuntu24.04 nvidia-smiFlag --gpus all ini tersedia sejak Docker Engine 19.03 dan menjadi cara standar mengekspos seluruh GPU yang terdeteksi ke dalam container, sesuai dokumentasi resmi NVIDIA Container Toolkit. Output yang menampilkan tabel informasi GPU (nama device, penggunaan memori, versi driver) menandakan instalasi berhasil. Kalau muncul error could not select device driver "" with capabilities: [[gpu]], itu tanda konfigurasi runtime Docker belum menerapkan perubahan dari nvidia-ctk runtime configure, dan solusinya biasanya cukup restart ulang service Docker.
Setelah verifikasi berhasil, tambahkan akses GPU ke service ollama lewat docker run.
docker run -d --gpus=all -v ollama-data:/root/.ollama -p 11434:11434 --name ollama ollama/ollama:0.34.0Kalau memakai Docker Compose, akses GPU dikonfigurasi lewat bagian deploy.resources.reservations.devices, mengikuti Compose Specification untuk device request.
services:
ollama:
image: ollama/ollama:0.34.0
volumes:
- ollama-data:/root/.ollama
ports:
- "11434:11434"
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: ["gpu"]
volumes:
ollama-data:Field capabilities: ["gpu"] wajib diisi, karena Compose akan menolak konfigurasi ini dengan error kalau field tersebut kosong. Field count: all mengalokasikan seluruh GPU yang terdeteksi di host; ganti dengan angka tertentu (misalnya count: 1) kalau host punya lebih dari satu GPU dan ingin membatasi berapa banyak yang dipakai satu service, atau pakai device_ids untuk menunjuk GPU spesifik berdasarkan indeksnya. Field count dan device_ids saling eksklusif, jadi cukup pilih salah satu sesuai kebutuhan.
Jalankan ulang stack, lalu masuk ke dalam container untuk memverifikasi GPU benar-benar terdeteksi dari sisi Ollama.
docker compose up -d
docker exec -it ollama nvidia-smiKalau perintah ini gagal dijalankan atau tidak menampilkan GPU sama sekali, Ollama tetap akan berjalan tapi diam-diam jatuh kembali ke mode CPU tanpa pesan error yang mencolok, sehingga inference terasa lambat tanpa alasan yang jelas dari luar. Pengalaman di lapangan menunjukkan penyebab paling sering untuk kasus ini adalah driver NVIDIA di host belum kompatibel dengan versi toolkit yang terpasang, atau Docker daemon belum benar-benar direstart setelah proses konfigurasi runtime.
35.3 Resource Management untuk Workload AI
Model LLM punya karakteristik pemakaian resource yang jauh berbeda dari aplikasi web pada umumnya: kebutuhan memorinya besar dan cenderung konstan selama model dimuat, sementara load CPU/GPU-nya melonjak tajam hanya saat sedang memproses request inference. Tanpa batas resource yang jelas, satu container model AI yang kehabisan memori bisa memicu Out Of Memory (OOM) killer di level kernel dan mengganggu container lain yang berjalan di host yang sama.
35.3.1 Membatasi Memori dan CPU Container
Terapkan batas resource lewat deploy.resources.limits pada compose.yaml, mengikuti Compose Specification untuk pembatasan resource container.
services:
ollama:
image: ollama/ollama:0.34.0
volumes:
- ollama-data:/root/.ollama
deploy:
resources:
limits:
cpus: "4"
memory: 16G
reservations:
cpus: "2"
memory: 8G
devices:
- driver: nvidia
count: all
capabilities: ["gpu"]
volumes:
ollama-data:limits menetapkan batas atas resource yang boleh dipakai container, sementara reservations menjamin resource minimum yang dialokasikan untuknya. Perlu dicatat, field deploy.resources ini murni ditujukan untuk orkestrator seperti Docker Swarm; saat dijalankan lewat docker compose up biasa tanpa Swarm mode, hanya limits yang benar-benar diterapkan ke container, sedangkan reservations selain untuk device GPU cenderung diabaikan, sesuai catatan resmi Compose Specification. Sysadmin/DevOps Engineer yang menjalankan Ollama lewat docker compose tanpa Swarm sebaiknya fokus mengandalkan limits sebagai kontrol batas resource yang efektif.
Perhatikan juga bahwa batas memori di atas cuma membatasi resource container secara keseluruhan, bukan mengatur berapa banyak model yang boleh dimuat Ollama secara bersamaan ke memori. Atur variabel environment OLLAMA_MAX_LOADED_MODELS untuk membatasi jumlah model yang aktif dimuat sekaligus, dan OLLAMA_NUM_PARALLEL untuk membatasi jumlah request paralel yang diproses tiap model, keduanya sesuai dokumentasi resmi Ollama tentang variabel environment server.
services:
ollama:
image: ollama/ollama:0.34.0
environment:
OLLAMA_MAX_LOADED_MODELS: "1"
OLLAMA_NUM_PARALLEL: "2"
deploy:
resources:
limits:
memory: 16GKombinasi ini penting di lapangan karena kegagalan paling umum bukan container yang crash langsung, melainkan request yang antre lama atau bahkan timeout ketika terlalu banyak model besar dimuat bersamaan padahal memori host terbatas.
35.3.2 Memantau Pemakaian Resource
Pantau pemakaian resource container Ollama secara langsung lewat docker stats untuk melihat apakah batas yang ditetapkan sudah realistis dibanding beban kerja sebenarnya.
docker stats ollamaKalau kolom MEM USAGE / LIMIT terus-menerus mendekati batas atas yang ditetapkan, itu tanda batas memori perlu dinaikkan atau jumlah model yang dimuat perlu dikurangi. Untuk pemantauan berkelanjutan yang lebih menyeluruh, termasuk korelasi antara pemakaian GPU dan latency inference dalam bentuk dashboard, integrasikan container ini ke stack observabilitas berbasis Prometheus dan Grafana yang sudah dibahas pada studi kasus lain, memakai cadvisor sebagai sumber metrik resource container.
35.4 API Gateway untuk Model Serving
Ollama secara default mengekspos API dengan format sendiri, berbeda dari format API OpenAI yang sudah jadi standar de facto banyak library dan aplikasi klien LLM. Menghubungkan banyak aplikasi langsung ke Ollama juga berarti tidak ada lapisan terpusat untuk autentikasi, pencatatan pemakaian, atau load balancing kalau suatu saat ada lebih dari satu backend model. Menambahkan API gateway di depan Ollama menyelesaikan kedua masalah ini sekaligus.
35.4.1 Ollama sebagai Endpoint OpenAI-Compatible
Sebelum menambahkan gateway terpisah, perlu diketahui bahwa Ollama sejak versi 0.1.24 sudah menyediakan endpoint bawaan yang kompatibel dengan format API Chat Completions milik OpenAI, sesuai dokumentasi resmi Ollama, lewat path /v1/chat/completions. Endpoint ini memudahkan integrasi dengan library client yang sudah terbiasa dipakai untuk memanggil OpenAI, cukup dengan mengganti base_url-nya.
curl http://localhost:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llama3.2",
"messages": [{"role": "user", "content": "Halo, siapa kamu?"}]
}'Pendekatan ini cukup memadai kalau aplikasi cuma memanggil satu backend Ollama. Tapi begitu kebutuhan berkembang ke banyak backend model, virtual key per klien, atau pencatatan biaya pemakaian, dibutuhkan komponen gateway khusus yang berdiri di antara aplikasi dan model.
35.4.2 Memasang LiteLLM Proxy sebagai Gateway
LiteLLM Proxy adalah API gateway open-source yang menyatukan pemanggilan ke ratusan provider LLM, termasuk Ollama, lewat satu format API bergaya OpenAI, sekaligus menambahkan fitur manajemen virtual key dan pencatatan pemakaian. Buat file konfigurasi litellm-config.yaml yang mengarahkan satu model ke backend Ollama yang sudah berjalan.
model_list:
- model_name: llama3.2
litellm_params:
model: ollama_chat/llama3.2
api_base: http://ollama:11434
general_settings:
master_key: os.environ/LITELLM_MASTER_KEYPrefiks ollama_chat/ pada field model memberitahu LiteLLM untuk memanggil endpoint /api/chat milik Ollama, bukan /api/generate, sesuai dokumentasi resmi LiteLLM untuk provider Ollama, dan disebutkan sebagai pilihan yang menghasilkan respons percakapan lebih baik dibanding prefiks ollama/ biasa. Tambahkan service litellm ke compose.yaml yang sudah berisi service ollama.
services:
ollama:
image: ollama/ollama:0.34.0
volumes:
- ollama-data:/root/.ollama
litellm:
image: ghcr.io/berriai/litellm:v1.99.1
ports:
- "4000:4000"
volumes:
- ./litellm-config.yaml:/app/config.yaml:ro
environment:
LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
command: ["--config", "/app/config.yaml", "--port", "4000"]
depends_on:
- ollama
volumes:
ollama-data:Variabel LITELLM_MASTER_KEY berfungsi sebagai API key utama untuk mengakses gateway ini, sebaiknya diisi lewat file .env yang tidak ikut masuk version control, konsisten dengan pola pengelolaan kredensial pada studi kasus lain. Jalankan stack dan uji gateway lewat format panggilan bergaya OpenAI.
docker compose up -d
curl http://localhost:4000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-d '{
"model": "llama3.2",
"messages": [{"role": "user", "content": "Halo, siapa kamu?"}]
}'Aplikasi backend yang memanggil gateway ini sekarang cukup mengganti base_url ke alamat LiteLLM dan menyertakan API key yang valid, tanpa perlu tahu detail backend model apa yang sebenarnya melayani request di baliknya, termasuk kalau suatu saat backend-nya berpindah dari Ollama ke provider lain.
35.4.3 Verifikasi dan Troubleshooting Gateway
Kalau gateway mengembalikan error 401 Unauthorized, periksa dulu apakah header Authorization sudah memakai format Bearer <key> yang sesuai dengan nilai LITELLM_MASTER_KEY yang aktif pada container. Error model not found dari sisi gateway biasanya berarti nama model_name pada litellm-config.yaml tidak cocok persis dengan nama model yang dikirim di body request, karena LiteLLM mencocokkan nama model ini secara case-sensitive.
Kalau gateway berhasil merespons tapi isinya berupa error dari backend, periksa log container litellm untuk melihat pesan asli dari Ollama yang diteruskan gateway.
docker compose logs -f litellmPenyebab paling umum di lapangan untuk kasus ini adalah api_base pada konfigurasi LiteLLM belum memakai nama service Compose yang benar, atau model yang diminta belum sempat di-pull ke Ollama lewat docker exec ollama ollama pull. Pastikan juga urutan startup: kalau LiteLLM langsung mencoba memuat daftar model saat baru menyala sementara Ollama masih dalam proses inisialisasi, tambahkan mekanisme retry atau healthcheck pada service ollama supaya depends_on menunggu Ollama benar-benar siap, bukan cuma menunggu container-nya berstatus running.

