tutorial

Docker Compose

What is Docker Compose?

Docker Compose adalah sebuah alat untuk mendefinisikan dan menjalankan aplikasi Docker multi-kontainer. Dengan Docker Compose, Anda dapat menggunakan sebuah file YAML untuk mendefinisikan layanan, jaringan, dan volume untuk aplikasi Anda, lalu menjalankan semua komponen tersebut hanya dengan satu perintah.

Fitur Utama Docker Compose

  1. Multiple Containers:
    • Mengelola banyak kontainer dalam satu perintah.
    • Misalnya, Anda dapat menjalankan database, backend, dan frontend dalam satu konfigurasi.
  2. Definisi dalam File YAML:
    • Semua konfigurasi layanan (image, port, volume, jaringan) didefinisikan dalam file docker-compose.yml.
  3. Deklaratif:
    • Anda hanya mendefinisikan konfigurasi, dan Docker Compose mengatur semua layanan sesuai definisi Anda.
  4. Orkestrasi Layanan:
    • Mengatur dependensi antar layanan (misalnya, layanan database harus siap sebelum backend berjalan).
  5. Skalabilitas:
    • Anda dapat dengan mudah memperbanyak layanan menggunakan perintah docker-compose up --scale.

Kapan Menggunakan Docker Compose?

  • Ketika aplikasi Anda memerlukan beberapa layanan yang berjalan bersama (misalnya, aplikasi web + database).
  • Saat Anda ingin mengotomatiskan pengaturan lingkungan pengembangan atau pengujian.
  • Untuk memudahkan deploy aplikasi lokal atau dalam tim.

Komponen Utama dalam Docker Compose

  1. Layanan (services):
    • Setiap kontainer didefinisikan sebagai layanan di dalam docker-compose.yml.
    • Contoh: web, db, redis.
  2. Jaringan (networks):
    • Docker Compose secara otomatis membuat jaringan terisolasi untuk layanan Anda.
  3. Volume (volumes):
    • Untuk menyimpan data yang persisten, bahkan jika kontainer dihentikan atau dihapus.

Writing a docker-compose.yml file

File docker-compose.yml adalah file YAML yang digunakan untuk mengkonfigurasi semua layanan aplikasi Anda. Dalam file ini, Anda dapat mendefinisikan layanan, bangunan, jaringan, dan lain-lain.

Running multi-container applications

Setelah file docker-compose.yml dibuat, Anda dapat menggunakan perintah docker-compose up untuk memulai semua layanan yang didefinisikan. Perintah ini juga akan memastikan semua layanan terhubung pada jaringan yang sesuai.

Main Syntax

  1. Build image
    • docker compose build
      • melakukan build semua image yang didefinikasi di docker-compose.yml dan memiliki sintaks build
      • tambahkan --no-cache jika ingin membuat ulang image tanpa menggunakan cache dari build sebelumnya docker compose build --no-cache.
  2. Menjalankan Layanan
    • docker compose up
      • Menjalankan semua layanan yang didefinisikan dalam docker-compose.yml.
      • tambahakn -d untuk menjalankan di latar belakang docker compose up -d
        • Menjalankan layanan di latar belakang (detached mode).
    • docker compose start
      • memulai kembali layanan
    • docker compose restart
      • restart layanan
  3. Menghentikan Layanan
    • docker compose down
      • Menghentikan dan menghapus semua layanan, jaringan, dan volume yang dibuat oleh Compose.
  4. Menghentikan Layanan tanpa menghapus container
    • docker compose stop
      • Hanya menghentikan kontainer yang sedang berjalan, tetapi tidak menghapusnya. Kontainer tersebut bisa dijalankan kembali menggunakan docker-compose start.
  5. Melihat Log
    • docker compose logs
      • Menampilkan log dari semua layanan.
      • tambahkan -f utk realtime logs docker compose logs -f
  6. Menskalakan Layanan
    • docker compose up --scale web=3
      • Menjalankan 3 instance dari layanan web.
  7. Melihat list container
    • docker compose ps
      • Menampilkan status dari container yang dikelola oleh Docker Compose.
  8. melihat list images
    • docker compose images
  9. Melihat docker compose config
    • docker compose config
  10. Menjalankan perintah di dalam container
    • docker compose exec <service_name> <command>
    • docker compose exec fastapi bash
      • Menjalankan shell di dalam container service fastapi
  11. Menampilkan perintah docker compose lainnya
    • docker compose help
  12. spesifik service ex. fastapi
    • docker compose build fastapi
    • docker compose up fastapi
    • docker compose stop fastapi
    • docker compose logs fastapi
    • docker compose down fastapi
    • docker compose up –scale fastapi=3
    • dll..

Build image first or build image in docker-compose

Kedua pendekatan—build dulu image-nya atau build di Docker Compose—memiliki kelebihan dan kekurangannya masing-masing, dan pilihan terbaik sangat tergantung pada kebutuhan proyek dan workflow tim. Namun, secara umum, berikut adalah panduan best practice yang lebih disarankan berdasarkan beberapa pertimbangan:

Build di Docker Compose (Best Practice untuk Proyek Kolaboratif dan Otomatisasi)

Untuk sebagian besar proyek pengembangan aplikasi, menggunakan build langsung di Docker Compose lebih disarankan. Ini adalah pendekatan yang lebih otomatis, mudah diatur, dan mempermudah kolaborasi tim.

Keuntungan:

  • Kemudahan dan Otomatisasi: Docker Compose akan menangani pembuatan image secara otomatis setiap kali kamu menjalankan docker-compose up, yang membuat proses pengembangan menjadi lebih cepat dan mudah. Kamu tidak perlu secara manual membangun image terlebih dahulu.
  • Reproducibility (Reproduksi): Dengan menyertakan konfigurasi build dalam file docker-compose.yml, semua anggota tim dapat membangun dan menjalankan aplikasi yang sama persis tanpa perlu langkah-langkah tambahan. Cukup jalankan docker-compose up, dan semuanya akan dibangun dan dijalankan.
  • Fleksibilitas untuk Perubahan: Jika ada perubahan di dalam kode atau Dockerfile, kamu hanya perlu menjalankan docker-compose up --build, dan Docker Compose akan menghandle proses build ulang image, yang sangat memudahkan saat pengembangan.
  • Integrasi CI/CD: Dengan mengandalkan docker-compose.yml untuk build image, pipeline Continuous Integration (CI) dan Continuous Deployment (CD) dapat lebih mudah diterapkan, karena build image langsung terintegrasi dengan pengaturan file Compose.

Contoh Konfigurasi Docker Compose untuk Build:

version: '3'
services:
  fastapi:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8000:8000"

Build Dulu Image-nya (Best Practice untuk Pengelolaan Image Khusus atau Produksi)

Pendekatan ini lebih tepat untuk situasi di mana kamu ingin memiliki kontrol lebih besar atas pembuatan image, terutama dalam konteks pengelolaan image untuk lingkungan produksi atau dalam proyek yang membutuhkan versi image yang lebih stabil dan konsisten.

Keuntungan:

  • Kontrol yang lebih besar atas image: Dengan membuat image terlebih dahulu menggunakan docker build, kamu bisa memastikan image yang dibangun sudah stabil dan teruji, dan hanya image yang telah divalidasi yang digunakan dalam docker-compose.yml.
  • Pengelolaan Versi dan Penerapan Tagging: Jika kamu bekerja dengan image versi tertentu atau membutuhkan kontrol lebih besar dalam pengelolaan versi image, ini memungkinkan kamu untuk memberi tag pada image secara eksplisit (my-node-app:v1.0, misalnya). Ini penting untuk menghindari ketidakcocokan versi antara tim pengembang atau dalam lingkungan produksi.
  • Integrasi dengan Registri Docker: Jika kamu menggunakan Docker Hub atau registri pribadi untuk menyimpan image, membangun image terlebih dahulu dan menandainya memungkinkan kamu untuk mengunggahnya ke registri dan menarik image tersebut di berbagai lingkungan.

Contoh Workflow:

1.build image secara manual

docker build -t fast-api .

2.Definisikan image yang telah dibangun dalam docker-compose.yml:

version: '3'
services:
  fastapi:
    image: fast-api:latest
    ports:
      - "8000:8000"

Pendekatan ini lebih cocok untuk aplikasi yang sudah siap untuk produksi, di mana kamu ingin menggunakan image yang sudah teruji dan terverifikasi.

Kapan Memilih Masing-Masing Pendekatan?

Pilih Build di Docker Compose jika:

  • Kamu sedang dalam fase pengembangan dan ingin otomatisasi, kemudahan penggunaan, dan integrasi yang lebih baik dengan workflow tim.
  • Proyekmu berfokus pada kecepatan pengembangan dan iterasi, di mana membangun ulang image secara otomatis saat ada perubahan lebih menguntungkan.
  • Tim atau CI/CD pipeline akan lebih mudah beradaptasi dengan proses otomatis tanpa langkah tambahan untuk membangun image.

Pilih Build Dulu Image-nya jika:

  • Kamu bekerja di lingkungan yang lebih stabil, terutama dalam konteks produksi atau untuk aplikasi dengan pengelolaan versi yang ketat.
  • Kamu perlu mengelola image secara manual dan menggunakan registri image (Docker Hub atau private registry) untuk distribusi image ke berbagai lingkungan.
  • Proyekmu membutuhkan kontrol penuh atas proses pembuatan image dan penandaan (tagging) yang lebih eksplisit untuk distribusi.

Kesimpulan:

  • Untuk pengembangan aplikasi yang lebih cepat dan lebih dinamis, build di Docker Compose lebih disarankan.
  • Untuk proyek yang lebih fokus pada produksi, stabilitas, dan manajemen versi image, membangun image terlebih dahulu adalah pilihan terbaik.

Namun, dalam kebanyakan proyek pengembangan aplikasi modern, terutama jika bekerja dengan tim, build di Docker Compose sering kali menjadi pilihan yang lebih praktis dan efisien.

Perbandingan:

AspekBuild Dulu Image-nyaBuild di Docker Compose
Kontrol terhadap ImageLebih besar kontrol terhadap versi image dan tag.Kurang kontrol, Docker Compose akan otomatis build.
Kemudahan PenggunaanHarus build manual dengan docker build.Build otomatis saat menjalankan docker-compose up.
Cocok untuk Proyek Apa?Proyek dengan kebutuhan stabilitas image atau versi.Proyek pengembangan yang sering membutuhkan rebuild.
Keuntungan dalam CI/CDPerlu konfigurasi tambahan untuk otomatisasi.Lebih mudah diintegrasikan dengan pipeline CI/CD.
Keuntungan untuk PengembanganMemungkinkan untuk kontrol penuh terhadap image.Mudah dan cepat saat bekerja dalam tim atau saat pengembangan.

Build Context

jika dalam file docker-compose.yml kamu tidak menggunakan build.context, maka Docker Compose tidak akan membangun image dari sumber kode di direktori proyek. Sebaliknya, Docker Compose hanya akan menggunakan image yang sudah ada, yang sebelumnya telah dibangun atau ditarik dari registry Docker (seperti Docker Hub atau registry lain).

Penjelasan:

  • build.context: .: Ini memberi tahu Docker Compose untuk membangun image dari konteks build (direktori proyek saat ini). Jika kamu mendefinisikan bagian build seperti ini, Docker Compose akan menjalankan proses build berdasarkan Dockerfile yang ada di dalam konteks tersebut.
  • Tanpa build (hanya image): Jika kamu tidak menyertakan bagian build dan hanya menyebutkan image, Docker Compose akan menggunakan image yang sudah ada. Jika image tersebut tidak ada di lokal, Docker Compose akan mencoba untuk menariknya (pull) dari registry Docker (misalnya, Docker Hub).
#dengan build
version: '3'
services:
  app:
    build:
      context: ./app_fastapi
      dockerfile: Dockerfile
    ports:
      - "8000:8000"


#tanpa build
version: '3'
services:
  app:
    image: fastapi_app:v1.0  # Menggunakan image yang sudah ada
    ports:
      - "8000:8000"

Build multiple image in docker compose

Jika ada banyak image yang perlu dibangun di dalam satu proyek menggunakan Docker Compose, kamu bisa mengatur beberapa layanan di dalam file docker-compose.yml, dan masing-masing layanan bisa memiliki Dockerfile yang berbeda. Dalam hal ini, kamu perlu menentukan nama file Dockerfile secara eksplisit jika nama file Dockerfile-nya bukan Dockerfile default atau berada di direktori yang berbeda.

contoh struktur direktori

/my_project

├── /app_fastapi
│   ├── Dockerfile
│   ├── main.py
│   └── requirements.txt

├── /app_frontend
│   ├── Dockerfile
│   ├── index.html
│   └── package.json

├── /db
│   └── Dockerfile

└── docker-compose.yml

contoh file docker-compose.yml

version: '3'
services:
  fastapi_app:
    build:
      context: ./app_fastapi
      dockerfile: Dockerfile  # Gunakan nama default 'Dockerfile'
    image: fastapi-app:latest   
    container_name: fs-app
    ports:
      - "8000:8000"
  
  frontend_app:
    build:
      context: ./app_frontend
      dockerfile: Dockerfile  # Gunakan nama default 'Dockerfile'
    image: frontend-app:latest   
    container_name: fe-app
    ports:
      - "3000:3000"

  db_app:
    build:
      context: ./db
      dockerfile: Dockerfile  # Gunakan nama default 'Dockerfile'
    image: database-app:latest   
    container_name: db-app
    ports:
      - "5432:5432"  # Contoh untuk database PostgreSQL

Penjelasan:

  • build.context: Ini adalah direktori tempat Dockerfile dan aplikasi kamu berada. Misalnya, untuk fastapi_app, Docker Compose akan mencari Dockerfile di dalam folder ./app_fastapi.
  • dockerfile: Di sini, kamu menentukan nama file Dockerfile. Jika kamu menggunakan nama file default yang bernama Dockerfile, kamu tidak perlu menambahkannya (Docker Compose secara otomatis akan mencarinya). Jika file Dockerfile-nya memiliki nama lain, misalnya Dockerfile.dev, maka kamu bisa menulisnya seperti ini: dockerfile: Dockerfile.dev.

Contoh Jika Dockerfile Memiliki Nama Berbeda:

Misalnya, jika kamu memiliki beberapa file Dockerfile untuk pengembangan dan produksi, kamu dapat menamai file Dockerfile yang berbeda, seperti Dockerfile.dev dan Dockerfile.prod. Berikut contoh penggunaan nama file Dockerfile yang berbeda:

version: '3'
services:
  fastapi_app:
    build:
      context: ./app_fastapi
      dockerfile: Dockerfile.dev  # Menggunakan Dockerfile.dev untuk build
    image: fastapi-app:latest   
    container_name: fs-app
    ports:
      - "8000:8000"
  
  frontend_app:
    build:
      context: ./app_frontend
      dockerfile: Dockerfile.prod  # Menggunakan Dockerfile.prod untuk build
    image: frontend-app:latest   
    container_name: fe-app
    ports:
      - "3000:3000"

  db_app:
    build:
      context: ./db
      dockerfile: Dockerfile  # Menggunakan Dockerfile default untuk database
    image: database-app:latest   
    container_name: db-app
    ports:
      - "5432:5432"

environment vs env_file

env_file

  • Fungsi: Mengimpor variabel lingkungan dari file eksternal (biasanya .env) ke dalam kontainer.
  • Penggunaan: Kamu cukup merujuk file .env yang berisi variabel lingkungan yang ingin digunakan.
  • Keuntungan:
    • Manajemen konfigurasi lebih mudah: Jika kamu memiliki banyak variabel lingkungan, lebih baik memisahkannya dalam file .env dan hanya merujuk file tersebut di docker-compose.yml.
    • Pengelolaan versi lebih baik: Kamu bisa memiliki file .env yang berbeda untuk berbagai lingkungan (misalnya, .env.development, .env.production), dan cukup mengganti file tersebut sesuai dengan kebutuhan.
  • Kekurangan:
    • Tidak dapat mengubah variabel lingkungan secara dinamis: Semua variabel yang ada di dalam file .env akan dimuat pada saat kontainer dimulai. Kamu tidak bisa menambahkan atau mengubah variabel lingkungan setelah kontainer berjalan tanpa me-restart kontainer.

contoh

#.env

MONGO_URI=mongodb://mongodb:27017/userdb

#docker-compose.yml

services:
  app:
    env_file:
      - ./.env

environment

  • Fungsi: Mendefinisikan variabel lingkungan langsung di dalam file docker-compose.yml.
  • Penggunaan: Variabel lingkungan didefinisikan langsung di bagian environment untuk setiap service.
  • Keuntungan:
    • Lebih langsung dan eksplisit: Kamu bisa melihat dan mengonfigurasi variabel lingkungan langsung di dalam file docker-compose.yml.
    • Fleksibilitas tinggi: Sangat berguna jika hanya ada sedikit variabel lingkungan yang perlu dikonfigurasi atau jika kamu ingin mendefinisikan variabel secara langsung dalam file Compose.
  • Kekurangan:
    • File Compose bisa menjadi panjang dan berantakan: Jika ada banyak variabel lingkungan, file docker-compose.yml bisa menjadi sangat besar dan susah dibaca.
    • Kurang terstruktur: Tidak ada pembagian atau struktur terpisah antara kode aplikasi dan konfigurasi lingkungan, yang dapat mengurangi fleksibilitas dalam pengelolaan berbagai lingkungan (misalnya pengembangan vs produksi).

contoh

#docker-compose.yml

services:
  app:
    environment:
      - MONGO_URI=mongodb://mongodb:27017/userdb

Perbandingan:

Fiturenv_fileenvironment
Sumber VariabelMengimpor dari file .env eksternalMendefinisikan variabel langsung di Compose
PengelolaanLebih terorganisir, file .env terpisahLangsung dalam file Compose, lebih eksplisit
FleksibilitasKurang fleksibel untuk perubahan dinamisFleksibel, bisa langsung mengubah variabel
Kapan DigunakanBanyak variabel, pemisahan lingkunganSedikit variabel atau spesifik untuk service

Kapan Menggunakan env_file vs environment?

  1. Gunakan env_file ketika:
    • Kamu memiliki banyak variabel lingkungan dan ingin mengelola mereka secara terpisah dalam file .env untuk kemudahan pengelolaan.
    • Kamu ingin memisahkan konfigurasi aplikasi dan environment, seperti .env.development, .env.production, dll.
    • Kamu ingin menjaga docker-compose.yml tetap bersih dan tidak terlalu panjang.
  2. Gunakan environment ketika:
    • Kamu hanya memiliki sedikit variabel lingkungan atau hanya perlu menambahkan satu atau dua variabel.
    • Kamu ingin mendefinisikan variabel lingkungan secara eksplisit dan langsung di dalam file docker-compose.yml.
    • Kamu perlu mendefinisikan variabel lingkungan yang bersifat spesifik atau dinamis dalam konteks sebuah service.

Contoh Gabungan:

Kamu juga bisa menggunakan keduanya bersama-sama. Misalnya, kamu bisa menggunakan env_file untuk memuat variabel umum, dan menggunakan environment untuk menambahkan beberapa variabel spesifik:

services:
  app:
    env_file:
      - ./.env
    environment:
      - APP_ENV=production

Best Practices Docker Compose

1. Gunakan versi yang tepat

  • Selalu gunakan versi terbaru dari Docker Compose yang didukung
  • Alasan: Versi terbaru menawarkan fitur dan kompatibilitas yang lebih baik.

2. Strukturkan file dengan rapi

  • Pisahkan bagian utama seperti services, volumes, networks, dan configs.
  • alasan
    • Membantu membaca dan memahami file dengan lebih mudah.
    • Memisahkan definisi layanan, volume, dan jaringan.
version: '3.9'

services:
  app:
    image: my-app:latest
    ..

  db:
    image: postgres:15
    ..

volumes:
  db_data:

networks:
  app_network:
    driver: bridge

3. Gunakan build context untuk aplikasi kustom

  • Jika Anda membangun image aplikasi sendiri, gunakan direktori konteks dan Dockerfile secara eksplisit:
  • alasan
    • Memastikan konteks build spesifik.
    • Menghindari konflik jika ada banyak Dockerfile di proyek.
build:
  context: .
  dockerfile: Dockerfile

4. Gunakan Variabel environment

  • Gunakan file .env untuk mengelola variabel lingkungan agar tidak mengungkapkan informasi sensitif di file docker-compose.yml
  • alasan
    • keamanan: Informasi sensitif seperti password tidak langsung disimpan di docker-compose.yml.
    • Kemudahan konfigurasi untuk berbagai lingkungan (development, production, dll.).
environment:
  POSTGRES_USER: ${POSTGRES_USER}
  POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

file .env

POSTGRES_USER=admin
POSTGRES_PASSWORD=securepassword

5. Gunakan depends_on dengan bijak

  • Gunakan depends_on hanya untuk menentukan urutan startup layanan, tetapi tidak mengandalkan ini untuk memastikan layanan siap:
  • alasan:
    • depends_on hanya memastikan bahwa layanan dimulai lebih dulu, tetapi tidak memeriksa apakah layanan siap.
    • Gunakan alat seperti healthcheck untuk memastikan layanan siap.
depends_on:
  - db

6. Tambahkan healthcheck

  • Tambahkan healthcheck untuk memastikan layanan benar-benar siap sebelum layanan lain mencoba berkomunikasi:
  • alasan:
    • Memastikan layanan hanya tersedia jika benar-benar siap.
    • Menghindari kesalahan koneksi antar-layanan.
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
  interval: 30s
  timeout: 10s
  retries: 5

7. Gunakan volume untuk data persistent

  • Selalu gunakan volume untuk menyimpan data penting seperti database:
  • alasan:
    • Volume bersifat persisten, sehingga data tetap ada meskipun container dihapus.
    • Menghindari hilangnya data saat container dihentikan atau diperbarui.
volumes:
  - db_data:/var/lib/postgresql/data

8. Definisikan Jaringan Secara Eksplisit

  • Gunakan jaringan khusus untuk memisahkan layanan jika diperlukan:
  • alasan
    • Isolasi jaringan antar-layanan.
    • Mengurangi risiko keamanan dari akses tak terduga.
networks:
  app_network:
    driver: bridge

9. Gunakan nama container yang jelas

  • Tentukan nama container agar mudah dikenali, terutama dalam pengelolaan log dan debugging:
  • alasan
    • Membantu mengidentifikasi layanan dalam perintah seperti docker logs atau docker ps.
container_name: my-app-container

10. Atur Restart Policy

  • Tambahkan kebijakan restart untuk meningkatkan ketersediaan layanan:
  • alasan
    • Secara otomatis memulai kembali layanan yang gagal.
    • Meningkatkan ketahanan aplikasi.
restart: unless-stopped

11. Gunakan Resource Limits

  • Batasi sumber daya yang dapat digunakan oleh container untuk menghindari satu layanan membebani sistem:
  • alasan:
    • Membantu mengelola penggunaan sumber daya di lingkungan yang terbatas.
deploy:
  resources:
    limits:
      cpus: "1.0"
      memory: "512M"
    reservations:
      memory: "256M"

12. Gunakan Versi Tag untuk Image

  • Selalu gunakan tag versi spesifik untuk image, bukan latest:
  • alasan:
    • Menjamin konsistensi lingkungan saat membangun atau menjalankan aplikasi.
    • Menghindari masalah yang disebabkan oleh pembaruan otomatis ke versi terbaru.
image: postgres:15

13. Gunakan Logging yang Dikelola

  • Tambahkan konfigurasi log driver untuk layanan penting:
  • alasan:
    • Membatasi ukuran log untuk menghemat ruang disk.
    • Mempermudah pengelolaan log container.
logging:
  driver: json-file
  options:
    max-size: "10m"
    max-file: "3"

14. Gunakan Mode Tertentu untuk Jaringan

  • Jika layanan memerlukan akses jaringan yang spesifik, gunakan mode seperti host atau none:
  • alasan:
    • Memberikan fleksibilitas pengaturan jaringan.
    • Mengoptimalkan komunikasi antar-layanan.
network_mode: bridge

15. Dokumentasikan Konfigurasi

  • Tambahkan komentar di file docker-compose.yml untuk menjelaskan konfigurasi:
  • alasan:
    • Membantu tim lain memahami konfigurasi.
    • Memudahkan debugging dan pemeliharaan.
services:
  app:
    image: my-app:latest  # Backend aplikasi
    ports:
      - "8080:8080"       # Ekspos API di port 8080

Study case

Endpoint CRUD untuk Data User dengan FastAPI dan MongoDB

struktur folder & file project

my_project/

├── app/                  # Direktori untuk kode sumber aplikasi
│   ├── main.py           # File aplikasi FastAPI
│   └── requirements.txt  # File dependensi Python

├── mongo_data/           # Direktori untuk data MongoDB (bind mount)
│   └── (data files)      # File data MongoDB

├── docker-compose.yml    # File konfigurasi Docker Compose
└── Dockerfile            # File untuk build image FastAPI

main.py

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List
from pymongo import MongoClient
from bson import ObjectId
import os

app = FastAPI()

# Mengambil URI MongoDB dari environment variable
MONGO_URI = os.getenv("MONGO_URI", "mongodb://localhost:27017")
client = MongoClient(MONGO_URI)
db = client["myDatabase"]
collection = db["users"]

class User(BaseModel):
    name: str
    email: str

@app.post("/user/", response_model=User)
def create_user(user: User):
    user_id = collection.insert_one(user.dict()).inserted_id
    return user

@app.get("/user/{user_id}", response_model=User)
def get_user(user_id: str):
    user = collection.find_one({"_id": ObjectId(user_id)})
    return user

@app.get("/users/", response_model=List[User])
def list_users():
    users = list(collection.find())
    return users

@app.put("/user/{user_id}", response_model=User)
def update_user(user_id: str, user: User):
    collection.update_one({"_id": ObjectId(user_id)}, {"$set": user.dict()})
    return collection.find_one({"_id": ObjectId(user_id)})

@app.delete("/user/{user_id}")
def delete_user(user_id: str):
    collection.delete_one({"_id": ObjectId(user_id)})
    return {"message": "User deleted"}

Dockerfile for fastapi image

# Gunakan image Python sebagai base image
FROM python:3.10-slim

# Setel working directory di dalam container
WORKDIR /app

# Salin file requirements.txt ke dalam container
COPY ./app/requirements.txt /app/

# Install dependencies yang dibutuhkan
RUN pip install --no-cache-dir -r requirements.txt

# Salin semua kode aplikasi ke dalam container
COPY ./app /app/

# Setel variabel lingkungan untuk FastAPI
ENV UVICORN_CMD="uvicorn main:app --host 0.0.0.0 --port 8000 --reload"

# Ekspose port 8000 untuk aplikasi FastAPI
EXPOSE 8000

# Perintah untuk menjalankan FastAPI menggunakan Uvicorn
CMD ["sh", "-c", "$UVICORN_CMD"]

Dengan menyalin requirements.txt terlebih dahulu sebelum salinan seluruh kode aplikasi, Docker dapat memanfaatkan layer caching. Ini mempercepat build image karena pip hanya akan diinstal ulang ketika ada perubahan pada file requirements.txt, bukan setiap kali ada perubahan di dalam kode aplikasi.

Penjelasan:

  1. FROM python:3.10-slim:
    • Menggunakan image Python 3.10 yang lebih ringan (slim) sebagai base image untuk container.
  2. WORKDIR /app:
    • Menetapkan direktori kerja di dalam container sebagai /app, tempat aplikasi FastAPI akan ditempatkan.
  3. COPY requirements.txt /app/:
    • Menyalin file requirements.txt dari host ke dalam container. File ini berisi daftar dependensi yang perlu diinstal untuk aplikasi FastAPI.
  4. RUN pip install --no-cache-dir -r requirements.txt:
    • Menggunakan pip untuk menginstal dependensi yang ada di requirements.txt ke dalam container.
  5. COPY ./app /app/:
    • Menyalin seluruh kode aplikasi dari folder ./app di host ke dalam container di folder /app.
  6. ENV UVICORN_CMD="...":
    • Menetapkan variabel lingkungan untuk perintah yang menjalankan FastAPI menggunakan Uvicorn. Dalam hal ini, aplikasi FastAPI akan dijalankan di port 8000 dengan opsi --reload untuk pengembangan.
  7. EXPOSE 8000:
    • Mengekspos port 8000 dari container, karena FastAPI akan berjalan pada port ini di dalam container.
  8. CMD ["sh", "-c", "$UVICORN_CMD"]:
    • Menjalankan perintah untuk memulai aplikasi FastAPI menggunakan uvicorn. Perintah ini akan menjalankan aplikasi di dalam container pada saat container dijalankan.

requirements.txt

fastapi
uvicorn
pymongo

docker-compose.yml

  • 2 services :
    • fastapi
      • build image in docker-comp using Dockerfile
    • mongodb
      • build image first by pull from registry
  • network :
    • bridge
version: '3'
services:
  fastapi:
    build: 
      context: .
      dockerfile: Dockerfile
    image: my-fastapi-app:latest  # Menentukan nama image dan tag
    container_name: fastapi-app
    ports:
      - "8000:8000" # Menyambungkan port 800 di host ke port 8000 di container (MongoDB)
    depends_on:
      - mongodb # Ensure service mongodb starts before fastapi
    environment:
      MONGO_URI: mongodb://root:admin12345@mongodb:27017 # URI untuk menghubungkan FastAPI ke MongoDB
      APP_ENV: production  # Menetapkan lingkungan aplikasi (production)
    volumes:
      - ./app:/app  # Bind mount untuk source code aplikasi
    networks:
      - app_network  # Menghubungkan ke jaringan kustom yang didefinisikan di bawah
    restart: unless-stopped  # Mengatur agar container restart otomatis jika terhenti
    
  mongodb:
    image: "mongo:4.4"
    container_name: mongodb
    ports:
      - "27017:27017" # Menyambungkan port 27017 di host ke port 27017 di container (MongoDB)
    environment:
      MONGO_INITDB_ROOT_USERNAME: root  # Username admin untuk MongoDB
      MONGO_INITDB_ROOT_PASSWORD: admin12345  # Password admin untuk MongoDB
      MONGO_INITDB_DATABASE: myDatabase # Nama database yang akan dibuat pada inisialisasi
    volumes:
      - ./mongo_data:/data/db  # Bind mount untuk penyimpanan data MongoDB
    networks:
      - app_network  # Menghubungkan ke jaringan kustom
    healthcheck:  # Menetapkan healthcheck untuk memastikan MongoDB siap digunakan
      test: ["CMD", "mongo", "--eval", "db.runCommand({ ping: 1 })"]
      interval: 10s  # Pemeriksaan dilakukan setiap 10 detik
      timeout: 5s  # Timeout jika tidak mendapat respons dalam 5 detik
      retries: 5  # Coba hingga 5 kali
    restart: unless-stopped  # Mengatur agar container MongoDB restart otomatis jika terhenti

#volumes:
  # Bind mount is already used, so volumes section remains empty
  
networks:
  app_network:
    name: app_network  # Menentukan nama jaringan secara eksplisit
    driver: bridge # Custom bridge network

Penjelasan:

  1. version: '3':
    • Menentukan versi Docker Compose yang digunakan. Versi 3 adalah versi yang umum dan mendukung fitur-fitur modern.
  2. services:
    • Di bagian ini, kita mendefinisikan dua services : fastapi dan mongodb:
    • fastapi service
      • build: Bagian ini menginstruksikan Docker Compose untuk membangun image menggunakan Dockerfile yang ada di direktori saat ini (context: .).
      • image : my-fastapi-app:latest. menentukan nama image dan tag ketika di build
      • container_name: fastapi-app: Nama container yang digunakan untuk layanan FastAPI.
      • ports: "8000:8000": Mengalihkan port 8000 di host ke port 8000 di container, di mana aplikasi FastAPI akan berjalan.
      • depends_on: Menjamin bahwa MongoDB akan dimulai terlebih dahulu sebelum FastAPI.
      • environment: Menyediakan variabel lingkungan, seperti MONGO_URI yang digunakan oleh FastAPI untuk menghubungkan ke MongoDB.
      • volumes: Mengaitkan direktori ./app dari host ke /app di dalam container, sehingga kode aplikasi bisa diakses oleh container.
      • networks: Menghubungkan container FastAPI ke jaringan kustom app_network.
      • restart: unless-stopped: Mengatur agar container FastAPI secara otomatis restart jika terhenti, kecuali dihentikan secara eksplisit.
    • mongodb service:
      • image: mongo:4.4: Menggunakan image resmi MongoDB versi 4.4 dari Docker Hub.
        • study kasus ini kita menggunakan versi 4 karena versi 5 keatas membutuhkan ram 4 gb
      • container_name: mongodb: Nama container untuk MongoDB.
      • ports: "27017:27017": Mengalihkan port 27017 di host ke port 27017 di container, port default untuk MongoDB.
      • environment: Menyediakan variabel lingkungan untuk MongoDB, seperti username, password, dan nama database (userdb).
      • volumes: Mengaitkan direktori ./mongo_data dari host ke /data/db di container untuk memastikan data MongoDB bertahan.
      • healthcheck: Menetapkan pemeriksaan kesehatan untuk memastikan MongoDB sudah siap digunakan.
      • restart: unless-stopped: Mengatur agar container MongoDB secara otomatis restart jika terhenti, kecuali dihentikan secara eksplisit.
  3. networks:
    • app_network: Menetapkan jaringan kustom bernama app_network, dengan driver bridge yang merupakan jaringan default Docker untuk komunikasi antar container di satu host.
    • name : app_network , menentukan nama jaringan secara explisit
    • driver : bridge , menentukan jenis jaringan yang digunakan pada kasus ini menggunakan jaringan bridge

running

1. Jalankan code dibawah untuk melakukan build image utk service yg membutuhkan build dalam hal ini fastapi

docker compose build

build image process
docker images list

2. Aktifkan service dengan perintah docker compose up -d

  • docker compose up akan mengecek file docker-compose.yml, pastikan anda berada pada path dimana file tersebut berada
pulling image mongodb from registry
  • jika image belum ada di local, maka docker-compose akan mendownload (pull) image di docker hub
docker compose process
  • docker-compose.yml telah berhasil dijalankan dengan mengaktifkan network, dan 2 container (fastapi-app dan mongodb)

3. cek container

docker container list
  • 2 container telah berhasil diaktifkan dan dijalankan

4. Cek mongodb client

  • anda dapat menggunakan mogodb compas,
  • kemudian add new connection
  • pada bagian proxy/SSH masukkan credential server seperti host, username, password
  • pada bagian authentication masukkan username dan password yang sudah didefine di file docker-compose.yml untuk database nya dikosongkan saja.
add connection process
  • saat pertamakali build, database dan collection masih kosong,
  • setelah dilakukan insert maka otomatis database dan collection akan dibuat

5. cek browser

  • kita dapat mengakses aplikasi fastapi di url localhost:8000
  • untuk melihat api documentation swagger di /docs
swagger api documentation

6. cek api function dengan swagger atau postman

  • insert data user dengan swagger
insert data process
  • lihat data nya di mongodb compass
mongodb compass data
  • data berhasil masuk
  • database dan collection berhasil terbuat
bind mount verification
  • directory mongodata di host computer yg di bind mount otomatis terisi
    • sehingga jika container mongodb nya mati atau terhapus data masih ada ketika service nya di aktifkan lagi

shutdown

mematikan semua service di docker-compose.yml jalankan code docker compose down

  • container dan network akan dinonaktifkan dan dihapus dari list
docker compose down process

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button
Index