Ketika Image Container Anda Siap, Di Mana Sebenarnya Ia Berjalan?

Anda sudah membangun image. Anda sudah memindai kerentanannya. Anda sudah mendorongnya ke registry. Sekarang tibalah momen yang membedakan pipeline yang berfungsi dari deployment yang sesungguhnya: benar-benar menjalankan container itu di suatu tempat yang bisa dijangkau pengguna.

Cara Anda menjalankan container itu sepenuhnya tergantung pada tujuannya. Server tunggal dan klaster Kubernetes terlihat mirip di atas kertas — keduanya menjalankan container — tetapi pengalaman operasionalnya benar-benar berbeda. Pilihan ini memengaruhi cara Anda memperbarui, cara Anda pulih dari kegagalan, dan seberapa banyak koordinasi manual yang perlu dilakukan tim Anda setiap kali versi baru dirilis.

Menjalankan Container di Server Tunggal

Deployment server tunggal terlihat sederhana. Anda SSH ke mesin, jalankan docker run dengan tag image yang baru saja Anda promosikan, dan aplikasi pun berjalan. Di lingkungan demo, ceritanya selesai sampai di situ.

Dalam praktiknya, server tunggal jarang hanya menjalankan satu container. Biasanya ada container aplikasi, container database, cache, mungkin juga worker antrean. Container-container ini perlu dimulai dalam urutan yang benar, saling berkomunikasi melalui jaringan yang tepat, dan menangani situasi ketika salah satunya crash. Di sinilah docker-compose menjadi berguna. Anda mendefinisikan semua service, dependensinya, port-nya, dan kebijakan restart-nya dalam satu file. Satu perintah akan menjalankan semuanya dalam urutan yang benar.

Tantangan sesungguhnya muncul ketika Anda perlu memperbarui versi aplikasi. Di server tunggal, Anda menghentikan container lama dan memulai yang baru. Selama jendela waktu itu, aplikasi tidak bisa melayani permintaan. Jika aplikasi digunakan oleh orang sungguhan, downtime itu berarti.

Cara paling sederhana untuk mengurangi downtime adalah menjalankan dua container secara berdampingan. Biarkan versi lama tetap berjalan sementara versi baru dimulai. Setelah container baru siap menerima koneksi, alihkan traffic ke container tersebut, lalu hentikan container lama. Ini adalah rolling update dalam bentuknya yang paling dasar. Anda bisa melakukannya secara manual dengan skrip, atau menggunakan reverse proxy seperti Nginx atau Traefik untuk menangani pengalihan traffic.

Tetapi meskipun dengan pola rolling update, server tunggal memiliki batas yang keras. Jika server itu sendiri mati, aplikasi ikut mati. Jika Anda perlu menerapkan patch keamanan pada sistem operasi host, Anda harus menjadwalkan downtime. Untuk alat internal yang digunakan oleh tim kecil, trade-off ini seringkali dapat diterima. Untuk aplikasi yang berhadapan dengan pelanggan, biasanya tidak.

Menjalankan Container di Kubernetes

Kubernetes memperlakukan masalah deployment server tunggal sebagai masalah yang sudah terpecahkan dan membangun di atasnya. Anda tidak mengelola container secara langsung. Anda mendefinisikan objek Deployment yang mendeskripsikan keadaan yang diinginkan: image mana yang akan dijalankan, berapa banyak replika, health check apa yang digunakan, dan bagaimana cara melakukan pembaruan.

Ketika Anda memperbarui tag image di sebuah Deployment, Kubernetes tidak menghentikan semuanya lalu memulai ulang. Ia membuat pod baru dengan image baru, menunggu pod tersebut lulus health check, lalu secara bertahap menghentikan pod lama. Selama seluruh proses, setidaknya selalu ada satu pod yang melayani traffic. Pengguna tidak melihat gangguan layanan.

Pod adalah unit terkecil di Kubernetes. Ia bisa menjalankan satu atau lebih container, tetapi ide utamanya adalah bahwa pod bersifat sementara (ephemeral). Kubernetes membuat pod, menghancurkannya, dan memindahkannya ke node yang berbeda sesuai kebutuhan. Anda tidak perlu memikirkan server spesifik mana yang menjalankan pod. Klaster yang mengurusnya.

Perbedaan antara server tunggal dan Kubernetes bukan hanya tentang skalabilitas ke traffic yang lebih besar. Ini tentang siapa yang memiliki koordinasi. Di server tunggal, Anda yang menentukan urutan startup, kebijakan restart, dan penanganan kegagalan. Anda menulis skrip atau menggunakan docker-compose untuk menegakkan keputusan-keputusan itu. Di Kubernetes, orchestrator-lah yang memiliki koordinasi itu. Ia memeriksa kesehatan pod secara berkala, memulai ulang pod yang gagal, dan mendistribusikan ulang pod ke node yang sehat ketika sebuah node mati.

Pergeseran kepemilikan ini mengubah cara tim Anda beroperasi. Anda berhenti menulis skrip yang menangani siklus hidup container. Anda mulai menulis manifes Deployment yang mendeskripsikan keadaan yang diinginkan, dan Anda membiarkan klaster mencari cara untuk mencapai keadaan itu.

Berikut adalah contoh manifes Deployment minimal dalam praktik:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: my-registry/my-app:v1.2.3
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10

Manifes ini memberi tahu Kubernetes untuk menjalankan tiga replika, memperbaruinya satu per satu, dan hanya merutekan traffic ke sebuah pod setelah endpoint /health-nya merespons dengan sukses.

Cara Memilih di Antara Keduanya

Pilihan antara server tunggal dan Kubernetes bukanlah uji kemurnian teknis. Ini adalah keputusan berdasarkan kebutuhan operasional.

Diagram alir berikut dapat membantu Anda memutuskan jalur mana yang sesuai dengan situasi Anda:

flowchart TD A[Mulai] --> B{Traffic tinggi atau butuh zero-downtime?} B -- Ya --> C{Banyak service yang dikelola?} B -- Tidak --> D{Tim kecil, aplikasi sederhana?} C -- Ya --> E[Gunakan Kubernetes] C -- Tidak --> F[Pertimbangkan K3s atau MicroK8s] D -- Ya --> G[Gunakan server tunggal dengan docker-compose] D -- Tidak --> H{Kematangan operasional untuk klaster?} H -- Ya --> E H -- Tidak --> G

Gunakan server tunggal dengan docker-compose ketika:

  • Aplikasi digunakan oleh tim internal kecil.
  • Downtime untuk pembaruan atau pemeliharaan dapat diterima.
  • Anda hanya mengelola satu atau dua service.
  • Anda tidak perlu melakukan scaling horizontal.
  • Ukuran tim Anda kecil dan Anda menginginkan kompleksitas infrastruktur yang minimal.

Gunakan Kubernetes ketika:

  • Aplikasi harus tetap tersedia bahkan selama pembaruan.
  • Anda perlu melakukan scaling service secara independen berdasarkan traffic.
  • Anda menjalankan banyak service yang perlu di-deploy dan diperbarui secara terpisah.
  • Anda menginginkan pemulihan otomatis dari kegagalan node.
  • Tim Anda memiliki kematangan operasional untuk mengelola klaster.

Ada jalan tengah. Beberapa tim menjalankan klaster Kubernetes kecil dengan satu node menggunakan alat seperti K3s atau MicroK8s. Ini memberi Anda fitur rolling update dan health check dari Kubernetes tanpa kompleksitas penuh dari klaster multi-node. Ini layak dipertimbangkan jika Anda menginginkan pola deployment tetapi belum membutuhkan skalanya.

Satu Aturan yang Tidak Pernah Berubah

Terlepas dari di mana Anda melakukan deployment, satu aturan tetap sama: image yang berjalan di produksi harus persis sama dengan image yang lulus semua pengujian dan pemindaian di pipeline.

Jangan pernah membangun ulang image di server. Jangan pernah menarik tag yang berbeda karena "seharusnya sama." Jangan pernah membiarkan siapa pun SSH ke server dan menjalankan container dengan image yang dimodifikasi secara lokal. Jika image di registry bukan image yang berjalan, Anda telah kehilangan kemampuan untuk mereproduksi, mengaudit, dan melakukan rollback.

Inilah mengapa pemberian tag dan promosi image itu penting. Ketika Anda mempromosikan image dari staging ke produksi, Anda tidak membangun ulang apa pun. Anda hanya mengubah lingkungan mana yang diizinkan untuk menarik tag tertentu tersebut. Byte-nya identik.

Daftar Periksa Praktis untuk Deployment Container

Sebelum Anda melakukan deployment container ke lingkungan mana pun, pastikan hal-hal berikut:

  • Tag image di deployment cocok dengan tag yang lulus pipeline.
  • Container memiliki endpoint health check yang memberi tahu orchestrator kapan ia siap.
  • Variabel lingkungan dan secret telah diatur dengan benar untuk lingkungan target.
  • Strategi pembaruan telah ditentukan: rolling update untuk zero-downtime, recreate untuk kasus sederhana.
  • Anda memiliki cara untuk melihat versi image mana yang sedang berjalan.
  • Anda memiliki rencana rollback: baik tag image sebelumnya atau manifes Deployment sebelumnya.

Apa Selanjutnya

Menjalankan container hanyalah setengah dari pekerjaan. Setelah berjalan, Anda perlu tahu versi mana yang benar-benar melayani traffic, apakah ia sehat, dan apa yang harus dilakukan ketika versi baru memiliki masalah. Di sinilah pelacakan versi image dan rollback berperan. Itu adalah topik untuk bagian selanjutnya dari diskusi ini.

Untuk saat ini, hal yang penting adalah memilih target deployment yang sesuai dengan realitas operasional Anda, dan memastikan image yang Anda jalankan adalah image yang Anda uji. Segala sesuatu yang lain mengikuti dari sana.