Tips dan Trik Mengoptimalkan Penggunaan Docker

Contents

Share the article

Contents

Docker telah menjadi standar industri dalam pengembangan dan deployment aplikasi modern. Sebagai platform kontainerisasi yang powerful, Docker memungkinkan developer untuk mengemas, mendistribusikan, dan menjalankan aplikasi dengan konsisten di berbagai lingkungan. Namun, untuk memaksimalkan potensinya, penerapan best practices menjadi kunci utama dalam membangun aplikasi yang efisien, aman, dan mudah di-maintain.

Dalam dunia development yang semakin kompleks, implementasi Docker yang tidak tepat dapat menimbulkan berbagai masalah seperti kerentanan keamanan, performa yang buruk, dan kesulitan dalam maintenance. Artikel ini akan membahas secara mendalam tentang best practices dalam penggunaan Docker, mulai dari optimalisasi image hingga konfigurasi container yang efisien.

1. Menggunakan official image

Dalam pengembangan aplikasi menggunakan Docker, pemilihan base image menjadi salah satu keputusan paling kritis. Official image, yang dikelola langsung oleh Docker atau vendor teknologi terkait, menawarkan berbagai keuntungan yang signifikan dibandingkan dengan image tidak resmi atau custom-built dari awal.

Keamanan menjadi aspek utama yang membuat official image sangat direkomendasikan. Tim Docker dan vendor terkait secara rutin melakukan pemeriksaan keamanan dan memberikan update untuk mengatasi vulnerabilities yang ditemukan. Ini berarti ketika kita menggunakan official image, kita mendapatkan jaminan bahwa base system kita telah melalui proses security audit yang ketat.

Dari sisi dokumentasi dan standardisasi, official image menyediakan panduan yang sangat jelas dan terstruktur. Misalnya, ketika menggunakan official Node.js image, kita tidak perlu khawatir tentang konfigurasi dasar seperti environment variables, working directory, atau user permissions karena semua ini sudah diatur mengikuti best practices. Hal ini sangat membantu dalam mengurangi kesalahan konfigurasi yang bisa menyebabkan masalah di production.

Maintenance dan updates menjadi lebih terjamin dengan official image. Setiap kali ada security patches atau bug fixes, tim Docker atau vendor akan segera merilis update. Ini sangat penting untuk menjaga aplikasi kita tetap aman dan up-to-date. Dibandingkan dengan custom image yang harus kita maintain sendiri, ini menghemat banyak waktu dan resource.

Reliability dan konsistensi juga menjadi keunggulan utama official image. Ketika kita menggunakan image yang sama di development dan production environment, kita bisa yakin bahwa behavior aplikasi akan konsisten. Ini mengurangi kemungkinan terjadinya “it works on my machine” syndrome yang sering menjadi masalah dalam development.


     # Disarankan
     FROM python:3.9-slim  
 
     # Hindari  
     FROM ubuntu RUN apt-get update && apt-get install -y python3

2. Menggunakan versi image yang lebih spesifik

Dalam pengembangan aplikasi menggunakan Docker, penggunaan versi spesifik untuk base image merupakan praktik yang sangat penting. Ketika kita tidak menentukan versi spesifik dan hanya menggunakan tag ‘latest’, kita sebenarnya membuka potensi masalah yang bisa muncul secara tidak terduga di masa depan.

Bayangkan sebuah situasi di mana tim Anda mengembangkan aplikasi Node.js yang berjalan dengan baik di lingkungan development. Aplikasi tersebut menggunakan beberapa fitur spesifik dari Node.js versi 16, seperti fitur fs.promises. Dalam Dockerfile, tim menggunakan:


     FROM node:latest

Setelah beberapa bulan berjalan dengan baik, tiba-tiba aplikasi mengalami crash di production. Setelah dilakukan investigasi, ternyata masalahnya adalah ketika server melakukan rebuild image, Docker mengambil versi Node.js terbaru yang ternyata memiliki breaking changes dengan versi sebelumnya. Waktu berharga tim terbuang untuk debugging masalah yang sebenarnya bisa dihindari dengan menggunakan versi spesifik:


     FROM node:16.14.2

3. Implementasi Multi-stage Builds

Multi-stage builds adalah teknik dalam Docker yang memungkinkan kita untuk mengoptimalkan Dockerfile dengan cara memisahkan proses build dan runtime environment. Teknik ini sangat berguna untuk mengurangi ukuran final image dan meningkatkan keamanan aplikasi.

Bayangkan sebuah scenario aplikasi React.js. Untuk membangun aplikasi ini, kita membutuhkan Node.js, npm, berbagai development dependencies, dan build tools. Namun, untuk menjalankan hasil build (static files), kita hanya membutuhkan web server sederhana seperti Nginx. Tanpa multi-stage builds, image kita akan membawa semua development tools ke production, yang tidak diperlukan dan bahkan bisa menjadi risiko keamanan.


     # Build stage
     FROM node:16 AS builder
     WORKDIR /app
     COPY package*.json ./
     RUN npm install
     COPY . .
     RUN npm run build

     # Production stage
     FROM nginx:alpine
     COPY --from=builder /app/build /usr/share/nginx/html


Dalam contoh di atas, stage pertama (‘builder’) digunakan untuk membangun aplikasi dengan semua tools yang diperlukan. Stage kedua hanya mengambil hasil build dan menggunakan Nginx sebagai web server. Final image hanya berisi hasil build dan Nginx, tanpa Node.js, npm, atau development dependencies.

4. Penggunaan Layer yang Efisien dalam Docker

Docker menggunakan sistem layer dalam membangun sebuah image, dimana setiap instruksi dalam Dockerfile akan membentuk layer baru. Memahami cara kerja layer ini sangat penting karena akan mempengaruhi efisiensi proses build dan performa aplikasi secara keseluruhan.

Dalam pengembangan aplikasi, kita perlu memperhatikan urutan instruksi yang ditulis dalam Dockerfile. Docker menggunakan sistem cache untuk setiap layer, dan jika terjadi perubahan pada satu layer, semua layer setelahnya akan dibangun ulang. Oleh karena itu, sangat penting untuk menempatkan instruksi yang jarang berubah di bagian atas Dockerfile, dan instruksi yang sering berubah di bagian bawah.

Sebagai contoh, dalam pengembangan aplikasi Node.js, proses instalasi dependencies biasanya lebih jarang berubah dibandingkan dengan kode aplikasi. Berikut adalah contoh implementasi yang efisien:


     # Base Image
     FROM node:14

     # Set working directory
     WORKDIR /app

     # Layer 1: Copy package files
     COPY package*.json ./

     # Layer 2: Install dependencies
     RUN npm install

     # Layer 3: Copy application code
     COPY . .

     # Layer 4: Build application
     RUN npm run build

     # Command to run the application
     CMD ["npm", "start"]

Dalam contoh di atas, ketika kita mengubah kode aplikasi, Docker hanya perlu membangun ulang layer setelah perintah “RUN npm install”. Layer-layer sebelumnya akan menggunakan cache, sehingga tidak perlu menginstal ulang dependencies setiap kali ada perubahan kode.

Pengaturan layer yang efisien ini memberikan beberapa keuntungan signifikan. Pertama, waktu build menjadi lebih cepat karena Docker dapat menggunakan cache untuk layer-layer yang tidak berubah. Kedua, proses development menjadi lebih efisien karena developer tidak perlu menunggu instalasi dependencies setiap kali melakukan build. Ketiga, penggunaan resource menjadi lebih optimal, baik dari segi penyimpanan maupun bandwidth saat melakukan push atau pull image.

5. Menggabungkan Multiple Commands Menjadi Satu RUN Instruction

Dalam membuat Dockerfile, setiap instruksi RUN akan menciptakan layer baru pada image. Semakin banyak layer yang dibuat, semakin besar ukuran image dan semakin kompleks manajemen cache-nya. Oleh karena itu, sangat penting untuk menggabungkan perintah-perintah yang berkaitan menjadi satu instruksi RUN.

Praktik yang tidak efisien adalah menulis setiap perintah dalam RUN terpisah. Misalnya ketika menginstal beberapa package, sering kali developer menulis seperti ini:


     RUN apt-get update
     RUN apt-get install -y package1
     RUN apt-get install -y package2
     RUN apt-get install -y package3
     RUN rm -rf /var/lib/apt/lists/*

Pendekatan di atas akan menciptakan 5 layer berbeda, yang berarti membutuhkan lebih banyak ruang penyimpanan dan memperlambat proses build.

Sebagai gantinya, kita bisa menggabungkan semua perintah terkait menjadi satu instruksi RUN dengan menggunakan operator &&. Berikut contoh yang lebih efisien:


      RUN apt-get update && \
         apt-get install -y \
             package1 \
             package2 \
             package3 && \
         rm -rf /var/lib/apt/lists/*

6. Mengoptimalkan Build dengan dockerignore

File .dockerignore memiliki fungsi yang mirip dengan .gitignore. File ini membantu kita menentukan file dan folder mana yang tidak perlu dimasukkan ke dalam Docker image. Mari kita bahas dengan contoh yang sering ditemui dalam project sehari-hari. Bayangkan kita memiliki project Node.js dengan struktur seperti ini:


      project/
       ├── node_modules/
       ├── src/
       ├── tests/
       ├── logs/
       ├── .git/
       ├── .env
       ├── package.json
       └── package-lock.json


Tanpa .dockerignore, saat kita menjalankan perintah COPY . . dalam Dockerfile, semua file dan folder di atas akan disalin ke dalam image. Ini menimbulkan beberapa masalah:

  1. Folder node_modules yang besar ikut tersalin padahal kita akan menginstalnya lagi di dalam container
  2. File .env yang berisi informasi sensitif tidak sengaja masuk ke dalam image
  3. Folder .git yang besar dan tidak diperlukan dalam production ikut tersalin
  4. File logs yang hanya diperlukan untuk development ikut masuk

Untuk mengatasi ini, kita buat file .dockerignore:


      # Dependencies
     node_modules
     npm-debug.log
     yarn-debug.log
     yarn-error.log

     # Sistem Version Control
     .git
     .gitignore

     # Environment dan Konfigurasi
     .env
     .env.local
     .env.*.local
     .dockerignore
     Dockerfile

     # Testing
     coverage
     tests
     __tests__
     *.test.js

     # Development
     .vscode
     .idea
     *.swp
     *.swo

     # Logs
     logs
     *.log

     # Build output
     dist
     build

Dengan adanya file .dockerignore ini:

  1. Proses build menjadi lebih cepat karena tidak perlu menyalin file-file yang tidak diperlukan
  2. Image menjadi lebih kecil karena hanya berisi file yang diperlukan
  3. Informasi sensitif tidak akan masuk ke dalam image
  4. Menghindari konflik dengan node_modules yang akan diinstall di dalam container

7. Menggunakan Non-root User untuk Keamanan Container

Keamanan dalam Docker sangat penting, terutama ketika menjalankan aplikasi di production. Salah satu praktik keamanan yang paling penting adalah menghindari menjalankan aplikasi sebagai root user di dalam container. Mari kita bahas secara detail mengapa dan bagaimana implementasinya.

Mengapa Menghindari Root User?

Ketika container dijalankan sebagai root, jika terjadi peretasan atau vulnerability, penyerang bisa mendapatkan akses root tidak hanya ke container tetapi potensial juga ke host system. Ini sangat berbahaya karena:

  1. Root user memiliki akses penuh ke semua resources
  2. Bisa mengubah konfigurasi system
  3. Bisa menginstall software berbahaya
  4. Potensial merusak host system

Berikut contoh implementasi lengkap untuk aplikasi Node.js:


      # Base image
     FROM node:16-slim

     # Buat group dan user baru
     RUN groupadd -r appgroup && \
         useradd -r -g appgroup -s /sbin/nologin -M appuser

     # Buat direktori aplikasi dan set kepemilikan
     WORKDIR /app
     RUN chown appuser:appgroup /app

     # Install dependencies sebagai root
     COPY package*.json ./
     RUN npm install

     # Copy source code aplikasi
     COPY --chown=appuser:appgroup . .

     # Ganti ke non-root user
     USER appuser

     # Jalankan aplikasi
     CMD ["npm", "start"]

8. Penggunaan Environment Variables yang Tepat

Environment variables memainkan peran penting dalam konfigurasi aplikasi. Dengan menggunakan ENV dalam Dockerfile, Anda dapat mendefinisikan nilai default yang akan tersedia saat container berjalan. Untuk nilai yang berbeda di setiap build, gunakan ARG yang dapat diatur saat proses build. Pastikan untuk tidak menyimpan informasi sensitif seperti password atau API key dalam ENV, melainkan gunakan Docker secrets atau sistem manajemen konfigurasi eksternal.


     ENV NODE_ENV=production
     ENV PORT=3000
     ARG BUILD_VERSION
     ENV APP_VERSION=${BUILD_VERSION}

9. Dokumentasikan Port yang Digunakan


     EXPOSE 3000
     # Dokumentasi dalam bentuk komentar
     # API Server: 3000
     # Metrics: 9090

Instruksi EXPOSE digunakan untuk mendokumentasikan port mana yang akan digunakan oleh aplikasi di dalam container. Meskipun ini tidak secara otomatis membuka port tersebut (Anda masih perlu menggunakan flag -p saat menjalankan container), instruksi ini sangat penting sebagai dokumentasi untuk developer lain. Tambahkan juga komentar yang menjelaskan tujuan setiap port yang diexpose.

10. Penggunaan ENTRYPOINT dan CMD yang tepat

Penggunaan ENTRYPOINT dan CMD dalam Docker merupakan aspek penting yang perlu dipahami dengan baik oleh setiap pengembang. ENTRYPOINT berfungsi sebagai titik masuk utama yang menentukan executable yang akan dijalankan ketika container dimulai, sementara CMD menyediakan argumen default yang dapat dimodifikasi saat runtime. Kombinasi keduanya menciptakan keseimbangan antara konsistensi dan fleksibilitas dalam menjalankan container.

Dalam konteks aplikasi Node.js, penggunaan yang tepat dari ENTRYPOINT dan CMD dapat memastikan bahwa aplikasi kita berjalan dengan cara yang konsisten dan aman, sambil tetap mempertahankan fleksibilitas yang diperlukan untuk berbagai skenario penggunaan.

Mari kita lihat contoh implementasi sederhana untuk aplikasi Node.js:


     // server.js
     const express = require('express');
     const app = express();
     const port = process.env.PORT || 3000;

     app.get('/', (req, res) => {
       res.send('Hello from Docker!');
     });

     app.listen(port, () => {
       console.log(`Server running on port ${port}`);
     });


     #Dockerfile 
     FROM node:18

     WORKDIR /app

     COPY package*.json ./
     RUN npm install

     COPY . .

     # Node.js sebagai executor utama
     ENTRYPOINT ["node"]

     # File default yang akan dijalankan
     CMD ["server.js"]

Dengan konfigurasi ini, container dapat dijalankan dengan berbagai cara:


     # Menjalankan dengan konfigurasi default
     docker run -p 3000:3000 myapp

     # Menjalankan file JavaScript yang berbeda
     docker run -p 3000:3000 myapp app.js

     # Mengecek versi Node.js
     docker run myapp --version

Keuntungan dari setup ini adalah container akan selalu menggunakan Node.js sebagai executor (karena ENTRYPOINT), namun tetap fleksibel dalam hal file atau parameter yang dijalankan (melalui CMD). Ini mencegah eksekusi perintah yang tidak diinginkan sambil mempertahankan kemudahan penggunaan.

Format array yang digunakan dalam ENTRYPOINT dan CMD juga penting karena menghindari penggunaan shell tambahan, yang dapat meningkatkan performa dan keamanan. Pendekatan ini juga membuat Dockerfile lebih mudah dibaca dan dipahami oleh tim pengembang.

Dengan memahami dan menerapkan praktik ini, kita dapat membuat container yang lebih aman, konsisten, dan mudah dikelola untuk aplikasi Node.js kita. Penggunaan yang tepat dari ENTRYPOINT dan CMD adalah kunci untuk mencapai tujuan ini.

Kesimpulan

Mematuhi aturan dan panduan terbaik dalam menggunakan Docker memang tidak mudah, tapi sangat penting untuk kesehatan aplikasi kita. Bayangkan saja, ketika kita menerapkan cara-cara terbaik ini, tim kita bisa bekerja lebih tenang karena sistem yang kita bangun lebih aman dan mudah dikelola. Tidak hanya itu, ketika ada masalah, kita bisa lebih cepat menemukan dan memperbaikinya.

Di dunia teknologi yang bergerak cepat ini, belajar dan menerapkan praktik terbaik Docker bukan pilihan, tapi kebutuhan. Memang kadang terasa merepotkan di awal, tapi percayalah, usaha ini akan sangat berbuah manis di kemudian hari. Tim kita akan berterima kasih karena tidak perlu menghadapi masalah-masalah yang sebenarnya bisa dihindari sejak awal. Pada akhirnya, dengan Docker yang diterapkan secara benar, kita bisa fokus mengembangkan aplikasi yang lebih baik, bukan sibuk memadamkan api masalah teknis.

Share the article

Grow Your Knowledge

About Software Development with Our Free Guidebook

Grow Your Knowledge

About Software Development with Our Guidebook

You dream it.

We build it!

We provide several bonuses FOR FREE to help you in making decisions to develop your own system/application.

  • Risk Free Development Trial 
  • Zero Requirement and Consultation Cost 
  • Free Website/Mobile Audit Performance

Our Services

Software Development • Quality Assurance • Big Data Solution • Infrastructure • IT Training

You might also like

Mengenal bolt.new: Platform Development Environment Modern dari StackBlitz

saas

Software Custom vs. SaaS: Mana yang Lebih Cocok untuk Bisnis Anda?

On premise software

Cloud-Based vs. On-Premise Software Custom: Mana yang Lebih Baik untuk Bisnis Anda?

Silakan isi data di bawah sebelum mendownload file.

Silakan isi data di bawah sebelum mendownload file.

Silakan isi data di bawah sebelum mendownload file.

Silakan isi data di bawah sebelum mendownload file.

Signup for Free Software Development Guidebook: Input Email. Submit me.