Part 19 - Deploy pakai Docker & VPS
Aplikasi kita sudah lengkap secara fitur. Part ini yang terakhir berisi kode — kita bungkus jadi Docker image supaya bisa dijalankan konsisten di mesin manapun, dari laptop sampai VPS production.
Dockerfile Multi-Stage
Build Go menghasilkan satu binary statis — kita manfaatkan itu supaya image production tidak perlu membawa seluruh toolchain Go, cukup binary hasil compile-nya saja. Buat Dockerfile:
FROM golang:1.25-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/bin/blogfiber .
FROM alpine:3.20
RUN apk add --no-cache ca-certificates && \
addgroup -S blogfiber && adduser -S blogfiber -G blogfiber
WORKDIR /app
COPY --from=builder /app/bin/blogfiber .
RUN mkdir -p ./public/uploads && chown -R blogfiber:blogfiber /app
USER blogfiber
EXPOSE 3000
CMD ["./blogfiber"]
Dua stage di sini:
builder— pakai imagegolang:1.25-alpine(ada compiler Go), download dependency, compile jadi binary statis (CGO_ENABLED=0supaya tidak bergantung ke library C sistem operasi).- Stage final — pakai
alpine:3.20yang jauh lebih kecil (tidak ada compiler Go sama sekali), cuma menyalin binary hasil build dari stagebuilder. Hasilnya image production yang ringan.
Perhatikan juga addgroup/adduser dan USER blogfiber di baris-baris terakhir. Tanpa ini, container berjalan sebagai root secara default — kalau suatu saat ada celah keamanan di aplikasi yang berhasil dieksploitasi, penyerang otomatis punya akses root di dalam container. Menjalankan proses sebagai user biasa (non-root) adalah lapisan pertahanan tambahan yang murah: kalaupun aplikasi berhasil dieksploitasi, dampaknya terbatas pada hak akses user blogfiber, bukan root.
Buat juga .dockerignore supaya file yang tidak perlu (termasuk .env yang berisi kredensial lokal) tidak ikut ter-copy ke dalam image:
.git
.env
.env.test
tmp
public/uploads/*
!public/uploads/.gitkeep
*.md
Docker Compose: App + MySQL
Buat docker-compose.yml untuk menjalankan aplikasi bersama database-nya dalam satu perintah:
services:
app:
build: .
ports:
- "3000:3000"
environment:
DB_HOST: mysql
DB_PORT: "3306"
DB_USERNAME: blogfiber
DB_PASSWORD: ${DB_PASSWORD}
DB_DATABASE: blogfiber_dasar
JWT_SECRET: ${JWT_SECRET}
depends_on:
mysql:
condition: service_healthy
volumes:
- uploads:/app/public/uploads
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: blogfiber_dasar
MYSQL_USER: blogfiber
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
# Tanpa kredensial: mysqladmin ping mengembalikan exit 0 begitu server
# merespons, bahkan kalau otentikasinya sendiri ditolak - cukup untuk
# liveness check tanpa perlu menaruh password di argumen command.
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 5s
retries: 10
volumes:
mysql_data:
uploads:
Beberapa hal penting:
DB_HOST: mysql— di dalam jaringan Docker Compose, service lain dipanggil pakai nama service-nya (bukan127.0.0.1), Docker otomatis menyediakan DNS internal untuk ini.depends_ondengancondition: service_healthymemastikan containerappbaru start setelah MySQL benar-benar siap menerima koneksi (bukan cuma "container-nya sudah jalan", karena MySQL butuh waktu inisialisasi sebelum bisa dipakai).- Volume
mysql_datadanuploadsmembuat data database dan file upload persisten — tidak hilang setiap kali container di-restart atau di-rebuild. - Aplikasi tersambung ke database sebagai user
blogfiber(dibuat otomatis oleh image MySQL lewatMYSQL_USER/MYSQL_PASSWORD, dengan hak akses cuma ke databaseblogfiber_dasar), bukan sebagairoot.MYSQL_ROOT_PASSWORDtetap perlu diisi (MySQL mewajibkannya), tapi dipisah dari password aplikasi — root cuma dipakai untuk keperluan admin manual (backup, dst), bukan oleh aplikasi sehari-hari. Kalau kredensial aplikasi bocor, dampaknya terbatas ke satu database itu saja, bukan seluruh server MySQL. - Healthcheck
mysqladmin pingsengaja tidak menyertakan-u/-p— bukan karena tidak butuh autentikasi, tapi karenamysqladmin pingtetap mengembalikan exit code sukses selama server MySQL merespons, walau otentikasinya sendiri ditolak. Ini menghindari menaruh password sebagai argumen command line, yang kalau tidak, bisa terlihat oleh siapapun yang menjalankanps auxdi dalam container.
Environment Variable untuk Docker
Buat template .env.docker.example:
MYSQL_ROOT_PASSWORD=ganti-dengan-password-root-yang-berbeda-dan-kuat
DB_PASSWORD=ganti-dengan-password-user-blogfiber-yang-kuat
JWT_SECRET=ganti-dengan-secret-yang-panjang-dan-acak-minimal-32-karakter
Copy jadi .env.docker (bukan .env biasa — sengaja dipisah supaya tidak tertukar dengan .env yang dipakai untuk development lokal via go run), isi dengan nilai sungguhan, dan tambahkan ke .gitignore.
Menjalankan
docker compose --env-file .env.docker up --build -d
Cek status dan log:
docker compose ps
docker compose logs -f app
Test API-nya seperti biasa — dari luar container, semuanya tetap diakses lewat http://localhost:3000:
curl http://localhost:3000/
curl -X POST http://localhost:3000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"name":"Docker Test","email":"docker@test.local","password":"rahasia123"}'
Migration jalan otomatis setiap kali container app start (persis seperti saat go run biasa), jadi tabel akan otomatis terbentuk begitu MySQL siap.
Deploy ke VPS
Panduan lengkap ada di DEPLOYMENT.md di root project, ringkasannya:
- Install Docker & Docker Compose di VPS.
- Clone/upload project, siapkan
.env.dockerdengan kredensial production (jangan pernah pakai nilai contoh). docker compose --env-file .env.docker up --build -d.- Taruh di belakang reverse proxy (Nginx) untuk domain + HTTPS lewat Certbot.
- Untuk update:
git pulllaludocker compose up --build -dlagi — migration otomatis ter-apply.
Aplikasi kita sekarang siap dijalankan secara konsisten di mesin manapun, dari laptop untuk development sampai VPS untuk production, tanpa perlu instalasi Go/MySQL manual di server. Part terakhir (Part 20) adalah rangkuman penutup seri ini.
Bagian dari Series: GoFiber Dasar - Belajar Fundamental Lewat REST API Blog
Belajar konsep-konsep dasar GoFiber (routing, handler, GORM, MySQL, JWT auth) dengan membangun REST API blog sederhana dari nol - lengkap dengan auten...
Lihat Series Lengkap