Aplikasi selesai secara fungsional, tetapi “jalan di laptop saya” belum berarti “siap dijalankan di server”. Tiga hal memisahkan keduanya: lokasi database tidak boleh tertanam di kode, versi dependensi tidak boleh mengapung, dan lingkungan jalannya harus dapat direproduksi. Bagian ini menuntaskan ketiganya — environment variable, pyproject.toml final, dan Dockerfile — mengikuti pola chapter Containerization dengan Docker dan Deployment Aplikasi Web.

Konfigurasi lewat Environment Variable #

Satu-satunya nilai konfigurasi proyek ini adalah lokasi berkas database. Ubah tiga baris pertama app/database.py dari Bagian 3 — sisanya tidak berubah:

python
import os
import sqlite3
from collections.abc import Iterator

DB_PATH = os.environ.get("TUGAS_DB_PATH", "tugas.db")

os.environ.get("TUGAS_DB_PATH", "tugas.db") membaca variable TUGAS_DB_PATH bila ada, dan memakai "tugas.db" bila tidak — pola yang sama dengan chapter Environment Variables. Pengembangan lokal tidak berubah sama sekali, sementara lingkungan lain cukup menyetel satu variable tanpa menyentuh kode. Bukti keduanya lewat skrip sementara cek_api.py:

python
from fastapi.testclient import TestClient

from app.main import app

client = TestClient(app)

respons = client.post("/tugas", json={"judul": "Cek konfigurasi"})
print(respons.status_code, respons.json())

Output yang Dihasilkan:

text
201 {'id': 1, 'judul': 'Cek konfigurasi', 'selesai': False}

Tanpa variable, berkas tugas.db muncul di direktori kerja. Sekarang jalankan ulang dengan variable yang menunjuk ke berkas lain:

bash
TUGAS_DB_PATH=db-lain.db python3 cek_api.py

Output yang Dihasilkan:

text
201 {'id': 1, 'judul': 'Cek konfigurasi', 'selesai': False}

Kali ini yang tercipta adalah db-lain.db — tugas.db tidak tersentuh. Test dari Bagian 5 pun tidak terganggu: fixture di sana memakai monkeypatch.setattr(database, "DB_PATH", ...) yang menimpa atribut module secara langsung, mengesampingkan environment. Menjalankan ulang pytest tetap menghasilkan 7 passed.

pyproject.toml Final #

toml
[project]
name = "api-manajemen-tugas"
version = "1.0.0"
description = "REST API manajemen tugas dengan FastAPI dan SQLite"
requires-python = ">=3.12"
dependencies = [
    "fastapi==0.128.0",
    "uvicorn==0.44.0",
]

[project.optional-dependencies]
dev = [
    "httpx==0.28.1",
    "pytest==9.0.2",
]

[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["."]

Dua perubahan dibanding Bagian 1. Pertama, setiap dependensi dikunci dengan == alih-alih >= — prinsip chapter Dependency Pinning: build hari ini harus menghasilkan aplikasi yang identik dengan build bulan depan. Kedua, [tool.pytest.ini_options] memberi tahu pytest bahwa test tinggal di tests/ dan direktori root proyek adalah tempat mencari package — dengan pythonpath = ["."], perintah pytest polos kini sama sahnya dengan python -m pytest:

bash
pytest

Output yang Dihasilkan:

text
============================== 7 passed in 0.95s ===============================

Baris durasi bervariasi antar mesin dan antarjalankan — inti pesannya tetap 7 passed.

Dockerfile #

Buat berkas Dockerfile di root proyek:

dockerfile
FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    TUGAS_DB_PATH=/data/tugas.db

WORKDIR /app

RUN useradd --create-home apiuser

COPY pyproject.toml ./
RUN pip install --no-cache-dir fastapi==0.128.0 uvicorn==0.44.0

COPY app ./app

RUN mkdir /data && chown apiuser:apiuser /data

USER apiuser

EXPOSE 8000

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

Mari kita bedah keputusan-keputusannya:

  1. FROM python:3.12-slim: image dasar versi Python yang sama dengan pengembangan, varian slim tanpa toolchain compiler yang tidak terpakai.
  2. ENV ...: tiga variable sekaligus. PYTHONDONTWRITEBYTECODE=1 menghentikan pembuatan __pycache__ di dalam container, PYTHONUNBUFFERED=1 membuat log stdout muncul seketika tanpa buffering, dan TUGAS_DB_PATH=/data/tugas.db memindahkan database ke direktori data yang akan dipasangkan volume.
  3. RUN useradd ... dan USER apiuser: container tidak dijalankan sebagai root — bila penyerang lolos ke dalam proses, kewenangannya terbatas.
  4. Urutan COPY: pyproject.toml disalin dan dependensi dipasang lebih dulu, baru app/ disalin. Layer instalasi tetap valid selama dependensi tidak berubah, sehingga mengubah kode aplikasi tidak memicu pengunduhan ulang seluruh dependensi.
  5. RUN mkdir /data && chown ...: menyiapkan rumah untuk tugas.db milik apiuser.
  6. EXPOSE 8000: dokumentasi port yang dipakai container; CMD menjalankan uvicorn di 0.0.0.0 agar API terjangkau dari luar container.

Build dan Run #

bash
docker build -t api-manajemen-tugas:1.0.0 .

Output yang Dihasilkan (ilustratif):

text
[+] Building 24.3s (11/11) FINISHED
 => [1/4] FROM docker.io/library/python:3.12-slim
 => [2/4] RUN useradd --create-home apiuser
 => [3/4] COPY pyproject.toml ./
 => [4/4] COPY app ./app
 => exporting to image
 => => naming to docker.io/library/api-manajemen-tugas:1.0.0

Jalankan container dengan volume untuk data dan kebijakan restart otomatis:

bash
docker run -d --restart unless-stopped -p 8000:8000 -v tugas-data:/data api-manajemen-tugas:1.0.0

Setelah container aktif, endpoint kesehatan siap diperiksa:

bash
curl http://127.0.0.1:8000/health

Output yang Dihasilkan (ilustratif):

text
{"status":"ok"}

Daftar Periksa Deployment #

Sebelum API benar-benar melayani pengguna, lewati daftar periksa ini:

  • Test hijau sebelum build — jalankan pytest pada setiap perubahan, idealnya otomatis di CI seperti chapter *Continuous Integration`.
  • Dependensi terkunci di pyproject.toml dan Dockerfile, versi Python seragam antara laptop dan image.
  • Container berjalan sebagai apiuser, bukan root.
  • Data berada di volume (tugas-data:/data), sehingga container boleh dibuat ulang kapan saja tanpa kehilangan tugas.
  • Endpoint /health dipantau — sinyal pertama saat service bermasalah, dasar dari praktik chapter *Monitoring Aplikasi Web`.
  • Akses dari luar melewati reverse proxy dengan TLS, bukan langsung ke uvicorn — pembahasan chapter TLS dan HTTPS dari Course Python.
  • Kebutuhan naik skala disadari: perintah --workers 4 pada CMD memakai beberapa core CPU, tetapi empat proses berbagi satu berkas SQLite. Untuk beban tulis yang padat, pindahkan ke PostgreSQL atau MySQL seperti chapter Pengenalan PostgreSQL dan MySQL.

Dengan ini proyek tuntas: API manajemen tugas yang tervalidasi, teruji, terkonfigurasi, dan siap dibangun kapan pun. Berkas pyproject.toml dan Dockerfile tahap ini tersedia pada bagian Berkas Sumber di bawah halaman — perubahan tiga baris pada app/database.py diterapkan pada berkas dari Bagian 3. Dari sini, arah pengembangan selanjutnya terbuka: autentikasi dari chapter Authentication dan Authorization, atau pagination dan filtering untuk daftar tugas yang memanjang.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

pyproject.toml

Unduh berkas
toml
[project]
name = "api-manajemen-tugas"
version = "1.0.0"
description = "REST API manajemen tugas dengan FastAPI dan SQLite"
requires-python = ">=3.12"
dependencies = [
    "fastapi==0.128.0",
    "uvicorn==0.44.0",
]

[project.optional-dependencies]
dev = [
    "httpx==0.28.1",
    "pytest==9.0.2",
]

[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["."]