Kodeloka
  1. Kodeloka
  2. Kelas Golang
  3. Materi

Domain 26 · 9 bab

Deployment & Production Go

256. Instalasi Go di Windows (PC Baru)

Panduan lengkap instalasi Go di Windows dari nol: download, setup PATH, verifikasi, dan konfigurasi editor VS Code.

Instalasi Go di Windows — Langkah Demi Langkah

Berikut panduan lengkap untuk menjalankan Go di PC Windows yang masih bersih (fresh install), tanpa asumsi apapun.

1

Download Installer Resmi Go

Kunjungi https://go.dev/dl/ dan download file .msi terbaru untuk Windows (misal: go1.26.0.windows-amd64.msi).

💡 Tips

Pilih versi amd64 untuk PC 64-bit (hampir semua PC modern). Jika PC Anda 32-bit, pilih versi 386.

2

Jalankan Installer MSI

Klik dua kali file .msi yang sudah didownload. Ikuti wizard instalasi (Next → Next → Install). Secara default Go akan diinstall ke C:\Program Files\Go.

3

Verifikasi Instalasi

Buka Command Prompt atau PowerShell baru (penting: buka yang baru agar PATH ter-refresh), lalu jalankan:

Command Prompt / PowerShell
go version
# Output: go version go1.26.0 windows/amd64

go env GOPATH
# Output: C:\Users\NamaUser\go

go env GOROOT
# Output: C:\Program Files\Go
4

Buat Project Pertama

Command Prompt
mkdir C:\Projects\hello-go
cd C:\Projects\hello-go
go mod init hello-go

echo package main > main.go
echo. >> main.go
echo import "fmt" >> main.go
echo. >> main.go
echo func main() { >> main.go
echo     fmt.Println("Hello, Go Windows!") >> main.go
echo } >> main.go

go run main.go
# Output: Hello, Go Windows!
5

Setup VS Code untuk Go

Install VS Code dari https://code.visualstudio.com, lalu install extension Go by Google dari Marketplace. Buka folder project, tekan F5 untuk run & debug otomatis.

ℹ️ Lokasi File Go Penting
  • GOROOT = C:\Program Files\Go — Lokasi toolchain/SDK Go
  • GOPATH = C:\Users\[User]\go — Workspace Go (bin, pkg, src)
  • GOPATH\bin — Folder binary tools Go (gofmt, gopls, dll) — tambahkan ke PATH!

Contoh program

package main

import (
	"fmt"
	"runtime"
)

func main() {
	fmt.Println("=== Info Environment Go ===")
	fmt.Println("Versi Go:", runtime.Version())
	fmt.Println("Sistem Operasi:", runtime.GOOS)
	fmt.Println("Arsitektur CPU:", runtime.GOARCH)
	fmt.Println("Jumlah CPU Core:", runtime.NumCPU())
	fmt.Println()
	fmt.Println("Go berhasil berjalan di sistem Anda!")
}

Praktik dan kuis bab ini di kelas interaktif →

257. Instalasi Go di Linux / Ubuntu

Instalasi Go di Ubuntu/Debian dari awal — via apt, manual tarball, dan update-alternatives untuk multi-versi.

Instalasi Go di Linux Ubuntu / Debian

Ada dua cara instalasi Go di Linux: via apt (mudah tapi versi bisa ketinggalan) atau manual tarball (recommended, selalu versi terbaru).

Metode 1: Manual Tarball (RECOMMENDED)

Bash — Ubuntu/Debian
# 1. Hapus Go lama jika ada
sudo rm -rf /usr/local/go

# 2. Download Go terbaru (cek versi di https://go.dev/dl/)
wget https://go.dev/dl/go1.26.0.linux-amd64.tar.gz

# 3. Ekstrak ke /usr/local
sudo tar -C /usr/local -xzf go1.26.0.linux-amd64.tar.gz

# 4. Tambahkan Go ke PATH — edit ~/.bashrc atau ~/.zshrc
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
echo 'export GOPATH=$HOME/go' >> ~/.bashrc
echo 'export PATH=$PATH:$GOPATH/bin' >> ~/.bashrc

# 5. Reload konfigurasi shell
source ~/.bashrc

# 6. Verifikasi
go version
# go version go1.26.0 linux/amd64

Metode 2: Via apt (Ubuntu 22.04+)

Bash
sudo apt update
sudo apt install golang-go -y
go version
# Catatan: versi mungkin lebih lama dari upstream
⚠️ Catatan Versi

Package golang-go di repository apt Ubuntu seringkali tertinggal beberapa versi dari upstream resmi Go. Untuk production, gunakan metode tarball agar mendapat versi terbaru.

Membuat Project Pertama di Linux

Bash
mkdir ~/projects/hello-go && cd ~/projects/hello-go
go mod init hello-go

cat > main.go << 'EOF'
package main

import "fmt"

func main() {
    fmt.Println("Hello dari Go di Linux!")
}
EOF

go run main.go
# Output: Hello dari Go di Linux!

Build Binary untuk Production

Bash
# Build binary executable
go build -o hello-app main.go

# Jalankan binary
./hello-app

# Build untuk platform lain (cross-compile)
GOOS=windows GOARCH=amd64 go build -o hello-app.exe main.go
GOOS=darwin GOARCH=amd64 go build -o hello-app-mac main.go

Contoh program

package main

import (
	"fmt"
	"runtime"
	"os"
)

func main() {
	fmt.Println("=== System Info dari Go ===")
	fmt.Println("OS:", runtime.GOOS)
	fmt.Println("ARCH:", runtime.GOARCH)
	fmt.Println("Go Version:", runtime.Version())
	fmt.Println("CPU Count:", runtime.NumCPU())
	fmt.Println("Hostname:", getHostname())
}

func getHostname() string {
	hostname, err := os.Hostname()
	if err != nil {
		return "unknown"
	}
	return hostname
}

Praktik dan kuis bab ini di kelas interaktif →

258. Menjalankan Go App di VPS / Ubuntu Server

Setup Go di server Ubuntu dari nol: SSH ke VPS, install Go, deploy binary, dan jalankan sebagai background process.

Deploy Aplikasi Go ke VPS Ubuntu

Skenario: Anda memiliki VPS Ubuntu (misal dari DigitalOcean, Vultr, AWS Lightsail) dan ingin menjalankan aplikasi Go di sana.

1

Koneksi SSH ke VPS

Terminal Lokal
ssh root@IP_SERVER_ANDA
# atau dengan SSH key:
ssh -i ~/.ssh/id_rsa ubuntu@IP_SERVER_ANDA
2

Update Server & Install Go

Di dalam VPS
sudo apt update && sudo apt upgrade -y
wget https://go.dev/dl/go1.26.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.26.0.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
go version
3

Upload & Build Aplikasi

Terminal Lokal — Opsi A: SCP
# Upload source code ke VPS
scp -r ./my-go-app ubuntu@IP_SERVER:/home/ubuntu/

# Atau build di lokal dulu, upload binary
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o my-app main.go
scp my-app ubuntu@IP_SERVER:/home/ubuntu/
Di dalam VPS — Build di Server
cd /home/ubuntu/my-go-app
go build -o my-app main.go
chmod +x my-app
./my-app
4

Jalankan sebagai Background Process

Bash — nohup (sederhana)
# Jalankan di background, abaikan HUP signal
nohup ./my-app > /var/log/my-app.log 2>&1 &

# Cek apakah berjalan
ps aux | grep my-app

# Lihat log
tail -f /var/log/my-app.log
💡 Produksi: Gunakan systemd

Untuk deployment produksi yang proper, gunakan systemd service (lihat bab berikutnya) agar app otomatis restart jika crash dan berjalan saat server reboot.

Konfigurasi Firewall (UFW)

Bash
# Izinkan port aplikasi Go (misal port 8080)
sudo ufw allow 8080/tcp
sudo ufw enable
sudo ufw status

Contoh program

package main

import (
	"fmt"
	"net/http"
	"log"
)

// Simple HTTP server untuk di-deploy ke VPS
func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello dari Go Server!\nMethod: %s\nPath: %s\n",
			r.Method, r.URL.Path)
	})

	http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintln(w, `{"status": "ok", "service": "go-app"}`)
	})

	fmt.Println("Server berjalan di :8080")
	fmt.Println("Akses: http://localhost:8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Praktik dan kuis bab ini di kelas interaktif →

259. Deploy Go dengan systemd Service

Membuat systemd unit file untuk aplikasi Go agar otomatis berjalan saat boot, auto-restart jika crash, dan manajemen log terpusat.

Mengapa systemd untuk Go App?

systemd adalah init system standar di Ubuntu/Debian modern. Dengan systemd, aplikasi Go Anda akan:

  • ✅ Auto-start saat server reboot
  • ✅ Auto-restart jika crash (Restart=on-failure)
  • ✅ Log terpusat via journalctl
  • ✅ Manajemen mudah: start/stop/restart/status via systemctl
1

Siapkan Binary & Direktori

Bash
# Buat direktori untuk aplikasi
sudo mkdir -p /opt/my-go-app

# Copy binary ke sana
sudo cp ./my-app /opt/my-go-app/
sudo chmod +x /opt/my-go-app/my-app

# Buat user dedicated (best practice keamanan)
sudo useradd -r -s /bin/false goapp
2

Buat systemd Unit File

Bash
sudo nano /etc/systemd/system/my-go-app.service
/etc/systemd/system/my-go-app.service
[Unit]
Description=My Go Application Server
Documentation=https://github.com/yourname/my-go-app
After=network.target

[Service]
Type=simple
User=goapp
Group=goapp
WorkingDirectory=/opt/my-go-app
ExecStart=/opt/my-go-app/my-app
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=my-go-app
Environment=PORT=8080
Environment=GIN_MODE=release

[Install]
WantedBy=multi-user.target
3

Aktifkan & Jalankan Service

Bash
# Reload systemd untuk membaca unit file baru
sudo systemctl daemon-reload

# Enable agar auto-start saat boot
sudo systemctl enable my-go-app

# Start service sekarang
sudo systemctl start my-go-app

# Cek status
sudo systemctl status my-go-app

# Lihat log real-time
sudo journalctl -u my-go-app -f

# Stop service
sudo systemctl stop my-go-app

# Restart service
sudo systemctl restart my-go-app
💡 Update Deployment

Saat ada update binary: build baru → stop service → copy binary → start service lagi.

sudo systemctl stop my-go-app
sudo cp ./new-app /opt/my-go-app/my-app
sudo systemctl start my-go-app

Contoh program

package main

import (
	"fmt"
	"net/http"
	"log"
	"os"
	"os/signal"
	"syscall"
)

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/health", healthHandler)

	// Graceful shutdown
	go func() {
		sigChan := make(chan os.Signal, 1)
		signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
		<-sigChan
		log.Println("Server shutting down gracefully...")
		os.Exit(0)
	}()

	log.Printf("Server berjalan di port %s", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Go App berjalan! (systemd service)")
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintln(w, `{"status":"healthy"}`)
}

Praktik dan kuis bab ini di kelas interaktif →

260. Dockerize Aplikasi Go — Dockerfile Dasar

Membuat Dockerfile untuk aplikasi Go, build image, run container, dan memahami layer caching Docker.

Mengapa Docker untuk Go?

Docker memungkinkan Anda mengemas aplikasi Go beserta seluruh dependensinya dalam sebuah container yang berjalan identik di manapun — laptop developer, staging server, maupun production cloud.

🐳 Konsep Docker untuk Go Developer
  • Image: Blueprint/template container (dari Dockerfile)
  • Container: Instance yang berjalan dari sebuah image
  • Layer: Setiap instruksi Dockerfile membuat layer yang di-cache
  • Registry: Tempat penyimpanan image (Docker Hub, ECR, GCR)

Struktur Project

Struktur Folder
my-go-app/
├── main.go
├── go.mod
├── go.sum
└── Dockerfile

Dockerfile — Versi Sederhana

Dockerfile
# Base image dengan Go toolchain
FROM golang:1.26-alpine

# Set working directory di dalam container
WORKDIR /app

# Copy dependency files dulu (untuk layer caching)
COPY go.mod go.sum ./
RUN go mod download

# Copy seluruh source code
COPY . .

# Build binary
RUN go build -o my-app main.go

# Expose port yang digunakan aplikasi
EXPOSE 8080

# Perintah yang dijalankan saat container start
CMD ["./my-app"]

Build & Run Container

Terminal
# Build Docker image
docker build -t my-go-app:latest .

# Lihat image yang sudah dibuat
docker images

# Jalankan container (port 8080 host → 8080 container)
docker run -d -p 8080:8080 --name go-server my-go-app:latest

# Cek container berjalan
docker ps

# Lihat log container
docker logs -f go-server

# Stop & remove container
docker stop go-server && docker rm go-server
⚠️ Masalah: Image Terlalu Besar

Dockerfile sederhana di atas menggunakan golang:alpine sebagai base image final, yang masih mengandung seluruh Go toolchain. Ukuran image bisa 300-400 MB. Solusi: Multi-Stage Build (lihat bab berikutnya).

Contoh program

package main

import (
	"fmt"
	"net/http"
	"log"
	"os"
)

// Aplikasi ini akan di-Dockerize
func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		host, _ := os.Hostname()
		fmt.Fprintf(w, "Hello dari Docker Container!\nContainer ID: %s\nPort: %s\n",
			host, port)
	})

	mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		fmt.Fprintln(w, `{"status":"ok","runtime":"go-docker"}`)
	})

	log.Printf("🐳 Docker Go Server running on :%s", port)
	log.Fatal(http.ListenAndServe(":"+port, mux))
}

Praktik dan kuis bab ini di kelas interaktif →

261. Multi-Stage Docker Build untuk Go

Optimasi Docker image Go dari 400MB menjadi <15MB menggunakan Multi-Stage Build dengan builder + distroless/scratch.

Masalah dengan Single-Stage Dockerfile

Dockerfile satu tahap menghasilkan image yang berisi seluruh Go toolchain (compiler, standard library, tools), padahal untuk menjalankan binary Go, Anda hanya butuh binarynya saja.

Perbandingan Ukuran Image
  • ❌ Single-stage (golang:alpine): ~350-400 MB
  • ✅ Multi-stage (scratch): ~8-15 MB
  • ✅ Multi-stage (distroless): ~20-30 MB

Multi-Stage Dockerfile — Versi Scratch (Paling Kecil)

Dockerfile — Multi-Stage + Scratch
# ── Stage 1: Builder ──
FROM golang:1.26-alpine AS builder

WORKDIR /build

# Install dependencies
COPY go.mod go.sum ./
RUN go mod download

# Copy & build dengan optimasi
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build \
    -ldflags="-s -w -extldflags=-static" \
    -o app main.go

# ── Stage 2: Final Image ──
# Gunakan scratch (image kosong, 0 byte)
FROM scratch

# Copy hanya binary dari stage builder
COPY --from=builder /build/app /app

# Copy SSL certificates (untuk HTTPS calls)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

# Expose port
EXPOSE 8080

# Jalankan binary
ENTRYPOINT ["/app"]

Multi-Stage dengan distroless (Lebih Aman)

Dockerfile — Multi-Stage + distroless
FROM golang:1.26-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o app main.go

# Google Distroless: punya user non-root, certs, tzdata
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /build/app /app
EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/app"]
Terminal — Build & Cek Ukuran
docker build -t my-go-app:optimized .
docker images | grep my-go-app
# REPOSITORY     TAG         SIZE
# my-go-app      optimized   12.4MB  ← jauh lebih kecil!
💡 Flag Penting: CGO_ENABLED=0

CGO_ENABLED=0 menonaktifkan cgo (C bindings), menghasilkan binary yang pure Go dan fully static — tidak bergantung pada shared library C di sistem operasi. Wajib jika menggunakan image scratch.

Contoh program

package main

import (
	"fmt"
	"net/http"
	"log"
	"runtime"
)

// Aplikasi siap untuk Multi-Stage Docker Build
// Binary yang dihasilkan: fully static, ~8MB
func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		fmt.Fprintf(w, `{
  "message": "Go App berjalan dalam container!",
  "go_version": "%s",
  "os": "%s",
  "arch": "%s"
}`, runtime.Version(), runtime.GOOS, runtime.GOARCH)
	})

	log.Println("Multi-stage Go Docker container ready: :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Praktik dan kuis bab ini di kelas interaktif →

262. Docker Compose untuk Go App

Menjalankan Go app bersama database (PostgreSQL/Redis) menggunakan Docker Compose dengan networking, volumes, dan environment variables.

Docker Compose: Multi-Container Setup

Docker Compose memungkinkan Anda mendefinisikan dan menjalankan aplikasi multi-container dalam satu file konfigurasi YAML. Ideal untuk Go app + database + cache.

docker-compose.yml
version: '3.9'

services:
  # ── Go Application ──
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: go-app
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USER=gouser
      - DB_PASS=secretpass
      - DB_NAME=myapp
      - REDIS_URL=redis:6379
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - app-network
    restart: unless-stopped

  # ── PostgreSQL Database ──
  postgres:
    image: postgres:16-alpine
    container_name: go-postgres
    environment:
      POSTGRES_USER: gouser
      POSTGRES_PASSWORD: secretpass
      POSTGRES_DB: myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U gouser -d myapp"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - app-network

  # ── Redis Cache ──
  redis:
    image: redis:8-alpine
    container_name: go-redis
    volumes:
      - redis_data:/data
    networks:
      - app-network

volumes:
  postgres_data:
  redis_data:

networks:
  app-network:
    driver: bridge

Perintah Docker Compose

Terminal
# Jalankan semua service (build jika perlu)
docker compose up -d

# Rebuild & restart hanya service app
docker compose up -d --build app

# Lihat status semua service
docker compose ps

# Lihat log semua service
docker compose logs -f

# Lihat log service spesifik
docker compose logs -f app

# Stop semua service
docker compose down

# Stop + hapus volumes
docker compose down -v
💡 Healthcheck pada depends_on

Gunakan condition: service_healthy bersama healthcheck agar Go app menunggu database benar-benar siap (bukan hanya containernya yang started).

Contoh program

package main

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"os"

	// Simulasi struktur yang akan digunakan dengan PostgreSQL
)

type Config struct {
	DBHost     string
	DBPort     string
	DBUser     string
	DBPassword string
	DBName     string
}

func getConfig() Config {
	return Config{
		DBHost:     getEnv("DB_HOST", "localhost"),
		DBPort:     getEnv("DB_PORT", "5432"),
		DBUser:     getEnv("DB_USER", "postgres"),
		DBPassword: getEnv("DB_PASS", "password"),
		DBName:     getEnv("DB_NAME", "myapp"),
	}
}

func getEnv(key, defaultVal string) string {
	if val := os.Getenv(key); val != "" {
		return val
	}
	return defaultVal
}

func getDBConnectionString(cfg Config) string {
	return fmt.Sprintf(
		"host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
		cfg.DBHost, cfg.DBPort, cfg.DBUser, cfg.DBPassword, cfg.DBName,
	)
}

func main() {
	cfg := getConfig()
	connStr := getDBConnectionString(cfg)
	fmt.Println("Connection string:", connStr)

	// Di production, uncomment ini:
	// db, err := sql.Open("postgres", connStr)
	_ = sql.ErrNoRows // prevent unused import
	
	http.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "DB: %s@%s:%s/%s", cfg.DBUser, cfg.DBHost, cfg.DBPort, cfg.DBName)
	})

	log.Println("App siap. DB config loaded dari environment.")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Praktik dan kuis bab ini di kelas interaktif →

263. Nginx sebagai Reverse Proxy untuk Go

Konfigurasi Nginx sebagai reverse proxy di depan Go app: load balancing, SSL termination, static file serving, dan security headers.

Arsitektur: Nginx + Go App

Dalam production, sangat umum meletakkan Nginx di depan Go app sebagai reverse proxy. Nginx menangani:

  • 🔒 SSL/TLS termination (HTTPS)
  • ⚡ Static file serving (CSS, JS, images)
  • ⚖️ Load balancing ke multiple Go instances
  • 🛡️ Security headers, rate limiting
  • 📝 Access logging terpusat
Bash — Install Nginx
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx

Konfigurasi Nginx sebagai Reverse Proxy

/etc/nginx/sites-available/my-go-app
upstream go_backend {
    server 127.0.0.1:8080;
    # Load balancing ke multiple instances:
    # server 127.0.0.1:8081;
    # server 127.0.0.1:8082;
}

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    # Redirect HTTP ke HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;

    # SSL Certificates (via Certbot)
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    # Security Headers
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
    add_header X-XSS-Protection "1; mode=block";

    # Static files served directly by Nginx
    location /static/ {
        alias /opt/my-go-app/static/;
        expires 30d;
    }

    # Proxy semua request lain ke Go app
    location / {
        proxy_pass http://go_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 86400;
    }
}
Bash — Aktifkan & Test Konfigurasi
# Buat symlink ke sites-enabled
sudo ln -s /etc/nginx/sites-available/my-go-app /etc/nginx/sites-enabled/

# Test konfigurasi Nginx
sudo nginx -t

# Reload Nginx
sudo systemctl reload nginx

# Install SSL via Certbot (free Let's Encrypt)
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
💡 Header X-Real-IP

Dengan proxy_set_header X-Real-IP $remote_addr, Go app bisa mendapatkan IP address asli client (bukan IP Nginx) melalui r.Header.Get("X-Real-IP").

Contoh program

package main

import (
	"fmt"
	"net/http"
	"log"
)

// Go app yang di-proxy oleh Nginx
func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// Dapatkan IP asli client dari header Nginx
		realIP := r.Header.Get("X-Real-IP")
		if realIP == "" {
			realIP = r.RemoteAddr
		}
		
		proto := r.Header.Get("X-Forwarded-Proto")
		if proto == "" {
			proto = "http"
		}

		fmt.Fprintf(w, "Request diterima!\nClient IP: %s\nProtocol: %s\nHost: %s\nPath: %s",
			realIP, proto, r.Host, r.URL.Path)
	})

	http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		fmt.Fprintln(w, `{"status":"ok"}`)
	})

	// Go app hanya listen di localhost (Nginx yang expose ke internet)
	log.Println("Go backend berjalan di :8080 (behind Nginx)")
	log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))
}

Praktik dan kuis bab ini di kelas interaktif →

264. CI/CD Dasar untuk Go dengan GitHub Actions

Otomasi build, test, dan deploy aplikasi Go menggunakan GitHub Actions workflow — dari push code hingga deploy otomatis ke server.

CI/CD untuk Go App

CI/CD (Continuous Integration / Continuous Deployment) mengotomasi proses: setiap kali push code ke GitHub, pipeline secara otomatis menjalankan test, build, dan deploy aplikasi.

Struktur Direktori GitHub Actions

Struktur Project
my-go-app/
├── .github/
│   └── workflows/
│       ├── ci.yml       ← Test & Build (setiap push/PR)
│       └── deploy.yml   ← Deploy ke server (saat merge ke main)
├── main.go
├── main_test.go
└── Dockerfile

Workflow CI — Test & Build

.github/workflows/ci.yml
name: Go CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    name: Test & Build
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v5

    - name: Setup Go
      uses: actions/setup-go@v5
      with:
        go-version: '1.26'
        cache: true

    - name: Download dependencies
      run: go mod download

    - name: Run vet
      run: go vet ./...

    - name: Run tests
      run: go test -v -race -coverprofile=coverage.txt ./...

    - name: Upload coverage
      uses: codecov/codecov-action@v4
      with:
        file: ./coverage.txt

    - name: Build binary
      run: |
        CGO_ENABLED=0 GOOS=linux go build \
          -ldflags="-s -w" \
          -o my-app ./...
      
    - name: Upload artifact
      uses: actions/upload-artifact@v4
      with:
        name: go-binary
        path: my-app

Workflow Deploy — SSH ke VPS

.github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    needs: test  # Hanya deploy jika CI berhasil

    steps:
    - uses: actions/checkout@v5
    - uses: actions/setup-go@v5
      with:
        go-version: '1.26'
    
    - name: Build
      run: CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o my-app .

    - name: Deploy via SSH
      uses: appleboy/ssh-action@v1
      with:
        host: ${{ secrets.SERVER_HOST }}
        username: ${{ secrets.SERVER_USER }}
        key: ${{ secrets.SERVER_SSH_KEY }}
        script: |
          systemctl stop my-go-app
          cp /opt/backup/my-app /opt/backup/my-app.prev
    
    - name: Upload binary
      uses: appleboy/scp-action@v1
      with:
        host: ${{ secrets.SERVER_HOST }}
        username: ${{ secrets.SERVER_USER }}
        key: ${{ secrets.SERVER_SSH_KEY }}
        source: "my-app"
        target: "/opt/my-go-app/"
    
    - name: Restart service
      uses: appleboy/ssh-action@v1
      with:
        host: ${{ secrets.SERVER_HOST }}
        username: ${{ secrets.SERVER_USER }}
        key: ${{ secrets.SERVER_SSH_KEY }}
        script: systemctl start my-go-app
🔐 GitHub Secrets

Simpan credential sensitif (SSH key, server IP) di GitHub → Settings → Secrets and Variables → Actions. Jangan pernah hardcode di file workflow!

Contoh program

package main

import (
	"fmt"
	"testing"
)

// Fungsi yang akan ditest dalam CI/CD pipeline
func add(a, b int) int {
	return a + b
}

func multiply(a, b int) int {
	return a * b
}

// Test functions (file ini biasanya di main_test.go)
func TestAdd(t *testing.T) {
	result := add(3, 4)
	if result != 7 {
		t.Errorf("add(3, 4) = %d, want 7", result)
	}
}

func TestMultiply(t *testing.T) {
	cases := []struct {
		a, b, expected int
	}{
		{2, 3, 6},
		{0, 5, 0},
		{-1, 4, -4},
	}
	for _, tc := range cases {
		result := multiply(tc.a, tc.b)
		if result != tc.expected {
			t.Errorf("multiply(%d, %d) = %d, want %d",
				tc.a, tc.b, result, tc.expected)
		}
	}
}

func main() {
	fmt.Println("go test ./... menjalankan semua test otomatis")
	fmt.Println("CI/CD memastikan kode ter-test setiap push ke GitHub")
	fmt.Println("add(10, 20) =", add(10, 20))
}

Praktik dan kuis bab ini di kelas interaktif →