Masalah yang Ingin Diselesaikan
Banyak mahasiswa dan fresh graduate bingung membayangkan seperti apa pekerjaan nyata di bidang yang mereka incar. Teori di kelas tidak selalu sama dengan praktik di dunia kerja. Di sisi lain, perusahaan sulit menilai kemampuan hard skill maupun soft skill kandidat hanya dari CV.
KerjaLab adalah aplikasi web simulasi kerja yang menjawab masalah itu. Pengguna memilih bidang (Teknologi, Sains, Bisnis, atau Soft Skill), mengerjakan simulasi dengan batas waktu, lalu mendapat koin untuk kustomisasi profil dan XP untuk menaikkan level skill. Jika gagal, pengguna boleh mencoba lagi tanpa penalti, sehingga KerjaLab menjadi ruang aman untuk mencoba pekerjaan impian sebelum berkomitmen.
Fitur utamanya:
- Login Google dengan persetujuan (consent) eksplisit sesuai UU No. 27 Tahun 2022 tentang Pelindungan Data Pribadi.
- 12 simulasi dalam 4 bidang dengan 3 tingkat kesulitan. Semakin sulit, semakin besar hadiahnya.
- Tantangan harian dengan streak sebagai bonus.
- Profil yang menampilkan level hard skill dan soft skill, jumlah dan total waktu simulasi, serta link LinkedIn dan GitHub.
- Hak penghapusan data: pengguna dapat menghapus akun dan seluruh datanya.
Mengapa Proyek Ini
Saya mengarah ke peran Cloud/DevOps Engineer. Aplikasi ini memberi alasan nyata untuk merancang arsitektur multi-tier: frontend, backend, dan database yang masing-masing berjalan di container, lalu dirakit dengan Docker Compose. Fokus penilaiannya ada pada containerization, jadi fitur dibuat secukupnya.
Arsitektur
Tiga tier berjalan pada satu user-defined network bernama kerjalab-net:
| Container | Teknologi | Port ke localhost |
|---|---|---|
| frontend | Next.js | 3000 |
| backend | Node.js (Express) | 4000 |
| db | PostgreSQL 16 | tidak dibuka |
adminer (profile tools) | Adminer | 8080 |
Alur request: browser mengakses http://localhost:3000, lalu Next.js meneruskan panggilan /api/* ke http://backend:4000. Backend terhubung ke database lewat db:5432. Data disimpan di named volume kerjalab_pgdata. Database tidak dipublikasikan ke localhost, sehingga hanya container di dalam network yang bisa mengaksesnya.
Container dipilih dibanding virtual machine karena berbagi kernel host. Isolasinya memakai namespaces dan pembatasan sumber daya memakai cgroups, sehingga image jauh lebih kecil dan startup hanya hitungan detik, bukan menit seperti VM.
Dockerfile: Multi-Stage Build
Backend memakai dua stage. Stage builder memasang dependency, sedangkan stage runtime hanya membawa hasil yang diperlukan.
FROM node:22.14.0-alpine3.21 AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci --omit=dev COPY src ./src FROM node:22.14.0-alpine3.21 AS runtime ENV NODE_ENV=production PORT=4000 WORKDIR /app COPY --from=builder --chown=node:node /app ./ USER node EXPOSE 4000 CMD ["node", "src/index.js"] Penjelasan singkat:
- Tag versi spesifik (
22.14.0-alpine3.21), bukanlatest, agar build dapat diulang dengan hasil yang sama. - File dependency disalin lebih dulu sebelum source code. Selama
package.jsontidak berubah, layernpm cidiambil dari cache sehingga build jauh lebih cepat. - Multi-stage membuat image akhir tidak membawa cache npm dan tool build. Hanya
node_modulesproduksi dansrcyang ikut terbawa. -
COPY, bukanADD.COPYhanya menyalin file.ADDpunya fungsi tambahan (ekstraksi tar dan unduh dari URL) yang membuat perilakunya kurang transparan. -
CMDdalam exec form membuat prosesnodemenjadi PID 1 dan menerima sinyal penghentian langsung dari Docker. -
.dockerignoremencegahnode_modules,.env, dan.gitikut masuk ke konteks build.
Frontend memakai tiga stage (deps, builder, runtime) dengan mode standalone bawaan Next.js, sehingga image runtime hanya berisi server minimal.
Docker Compose
services: db: image: postgres:16.6-alpine volumes: - kerjalab_pgdata:/var/lib/postgresql/data networks: [kerjalab-net] healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 5s retries: 10 backend: build: ./backend environment: DB_HOST: db depends_on: db: condition: service_healthy networks: [kerjalab-net] adminer: image: adminer:4.8.1 profiles: ["tools"] Poin pentingnya:
- Backend memakai
DB_HOST: db, yaitu nama service, bukan IP ataulocalhost. DNS internal Docker yang menerjemahkannya. -
healthcheckpada database dandepends_ondengancondition: service_healthymemastikan backend baru berjalan setelah PostgreSQL benar-benar siap menerima koneksi. - Named volume menjaga data tetap ada setelah
docker compose down. Hanyadocker compose down -vyang ikut menghapus volume. - Konfigurasi dibaca dari file
.env. Yang masuk repository hanya.env.exampleberisi nilai dummy, sehingga tidak ada secret yang ter-commit. - Service
adminerberada di profiletools, jadi hanya aktif saat dibutuhkan dengandocker compose --profile tools up -d.
Cara Menjalankan di Localhost
git clone https://github.com/valentntnt/KerjaLab.git cd KerjaLab cp .env.example .env docker compose up -d --build Buka http://localhost:3000. Untuk mengecek kesehatan service:
docker compose ps curl -i http://localhost:4000/health Untuk menguji persistensi, buat data lalu jalankan docker compose down dan docker compose up -d. Data masih ada karena tersimpan di named volume.
Pelajaran yang Didapat
- Urutan perintah Dockerfile memengaruhi kecepatan build. Menaruh instruksi yang jarang berubah di atas membuat layer cache terpakai maksimal.
- Multi-stage build adalah cara paling sederhana menjaga image tetap ramping tanpa mengorbankan kemudahan proses build.
-
depends_onsaja tidak cukup. Container yang sudah start belum tentu siap. Healthcheck denganservice_healthymenyelesaikan masalah itu. - Pemisahan konfigurasi dari kode lewat
.envdan.env.examplemenjaga secret tetap aman sekaligus memudahkan orang lain menjalankan proyek. - Container memudahkan perpindahan ke cloud. Karena aplikasi sudah dikemas bersama dependensinya, memindahkannya ke VM cloud kelak cukup dengan menginstal Docker lalu menjalankan
docker compose up.
Tautan
- Repository GitHub: https://github.com/valentntnt/KerjaLab
- Video demo YouTube: GANTI_DENGAN_LINK_YOUTUBE



