Orkestrasi Kontainer
Podman adalah mesin kontainer modern yang mengadopsi standar Open Container Initiative (OCI) dan dirancang sebagai alternatif langsung bagi Docker. Keunggulan utamanya terletak pada arsitektur tanpa daemon (daemonless), yang berarti setiap kontainer dijalankan sebagai proses anak dari pengguna yang memulainya, tanpa memerlukan proses latar belakang istimewa. Pendekatan ini tidak hanya meningkatkan keamanan, tetapi juga mendukung mode rootless, sehingga pengguna biasa dapat menjalankan kontainer tanpa hak akses administrator.
1. Instalasi dan Pengenalan Podman
Podman tersedia di repositori resmi distribusi berbasis Debian/Ubuntu. Untuk memasangnya, cukup jalankan perintah berikut:
sudo apt update
sudo apt install -y podman
podman version
Setelah terinstal, mari kita kenali alur kerja dasar Podman. Saya akan menggunakan citra BusyBox sebagai contoh untuk demonstrasi pencarian, pengunduhan, dan penjalanan kontainer interaktif.
Pertama, cari citra BusyBox di registri publik Docker Hub:
podman search docker.io/busybox
Kemudian unduh citra tersebut ke penyimpanan lokal:
podman pull docker.io/library/busybox
Untuk menjalankan kontainer interaktif, gunakan perintah run dengan flag -it:
podman run -it docker.io/library/busybox
Di dalam shell BusyBox, Anda dapat menjalankan perintah sederhana, misalnya ls atau echo. Untuk keluar, ketik exit. Setelah keluar, kontainer akan berhenti. Anda dapat melihat daftar kontainer yang sedang berjalan atau yang sudah berhenti dengan:
podman ps # hanya yang berjalan
podman ps -a # semua kontainer
Untuk membersihkan kontainer dan citra yang tidak terpakai:
podman stop <id_container> && podman rm <id_container>
podman rmi <id_image>
Dengan pemahaman dasar ini, kita siap membangun citra kustom untuk aplikasi kita sendiri.
2. Membangun Citra Kustom dengan Multi-Stage Build
Salah satu fitur andalan Podman adalah kemampuannya membangun citra dari Containerfile (atau Dockerfile). Pendekatan multi-stage build sangat dianjurkan untuk menghasilkan citra yang ramping dan aman, karena hanya menyalin biner kompilasi akhir tanpa menyertakan perangkat pengembangan atau dependensi yang tidak diperlukan.
Sebagai studi kasus, kita akan membuat sebuah REST API sederhana dalam bahasa Go. Buat direktori proyek dan inisialisasi modul Go:
mkdir tasks-api && cd tasks-api
go mod init tasks-api
Kemudian buat berkas main.go dengan logika dasar untuk mengelola daftar tugas (tasks) melalui endpoint /tasks:
package main
import (
"encoding/json"
"log"
"net/http"
)
type Task struct {
ID int `json:"id"`
Name string `json:"name"`
}
var (
tasks []Task
nextID = 1
)
func getTasks(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(tasks)
}
func createTask(w http.ResponseWriter, r *http.Request) {
var task Task
if err := json.NewDecoder(r.Body).Decode(&task); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
task.ID = nextID
nextID++
tasks = append(tasks, task)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(task)
}
func taskHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
getTasks(w, r)
case http.MethodPost:
createTask(w, r)
default:
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}
}
func main() {
http.HandleFunc("/tasks", taskHandler)
log.Println("Server running on http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Selanjutnya, buat Containerfile di direktori yang sama. Tahap pertama (builder) akan menggunakan citra golang:1.26-alpine untuk mengunduh modul dan mengompilasi kode menjadi biner statis. Tahap kedua akan menyalin biner tersebut ke dalam citra kosong scratch yang tidak memiliki shell atau pustaka tambahan, sehingga ukuran akhir sangat kecil dan lebih aman.
FROM golang:1.26-alpine AS builder
WORKDIR /app
COPY go.mod ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o tasks .
FROM scratch
WORKDIR /app
COPY --from=builder /app/tasks .
EXPOSE 8080
CMD ["./tasks"]
Bangun citra tersebut dengan perintah podman build:
podman build -t tasks .
Setelah selesai, jalankan kontainer di latar belakang dengan pemetaan port 8080 dari host ke kontainer:
podman run -d -p 8080:8080 localhost/tasks
Untuk menguji API, gunakan curl:
curl http://localhost:8080/tasks
Anda akan melihat hasil berupa array kosong. Anda juga dapat menambahkan tugas baru dengan metode POST:
curl -X POST http://localhost:8080/tasks -H "Content-Type: application/json" -d '{"name":"Belajar Podman"}'
Jika citra ini ingin dibagikan ke registri publik seperti Docker Hub, lakukan autentikasi, beri tag, dan unggah:
podman login docker.io
podman tag localhost/tasks docker.io/username/tasks
podman push docker.io/username/tasks
3. Orkestrasi Multi-Kontainer dengan Podman Compose
Untuk aplikasi yang lebih kompleks, seperti yang membutuhkan beberapa layanan yang saling berkomunikasi, kita dapat menggunakan podman-compose, yang mengikuti spesifikasi Compose standar. Pastikan paket podman-compose telah terpasang:
sudo apt install -y podman-compose
Sebagai contoh, kita akan menggabungkan API Go yang telah dibuat dengan Caddy sebagai reverse proxy. Caddy akan bertindak sebagai pintu masuk (entrypoint) dan meneruskan permintaan ke layanan backend secara internal, tanpa perlu mengekspos port API langsung ke host.
Pertama, buat berkas Caddyfile di direktori proyek:
:80 {
reverse_proxy backend:8080
}
Kemudian buat berkas docker-compose.yml (atau compose.yaml) untuk mendefinisikan kedua layanan:
services:
backend:
build: .
expose:
- "8080"
caddy:
image: caddy:alpine
ports:
- "8081:80"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
depends_on:
- backend
Perhatikan bahwa layanan backend tidak memetakan port ke host, tetapi hanya mengekspos port 8080 agar dapat diakses oleh layanan lain di dalam jaringan internal. Sementara itu, layanan caddy memetakan port 8081 dari host ke port 80 di dalam kontainer, sehingga kita dapat mengakses aplikasi melalui http://localhost:8081.
Jalankan seluruh tumpukan layanan dengan perintah:
podman-compose up -d
Perintah -d menjalankan semua kontainer di latar belakang. Untuk memastikan semuanya berjalan, Anda dapat memeriksa daftar kontainer yang aktif:
podman ps
Uji coba akses melalui Caddy:
curl http://localhost:8081/tasks
Hasilnya akan sama seperti sebelumnya, tetapi kali ini lalu lintas melewati proxy Caddy terlebih dahulu.
Jika terjadi masalah, gunakan podman logs untuk melihat log masing-masing kontainer:
podman logs <nama-kontainer>
Untuk melihat detail konfigurasi atau variabel lingkungan, gunakan podman inspect:
podman inspect <nama-kontainer>
Setelah selesai, hentikan dan hapus semua kontainer yang terkait dengan tumpukan tersebut:
podman-compose down
Terakhir, untuk membersihkan sumber daya yang tidak terpakai (citra, kontainer berhenti, volume, dan cache build), jalankan:
podman system prune -a
Perintah ini akan menghapus semua citra dan kontainer yang tidak sedang digunakan, sehingga menghemat ruang penyimpanan.