Yomu
Architecture

Architecture Deployment

Deployment produksi di AWS EC2 dengan GHCR, blue-green deployment, dan stack observabilitas lengkap

Target Deployment: AWS EC2

Semua layanan berjalan pada satu instance AWS EC2 dengan konfigurasi berikut:

KomponenTipe InstanceCPUMemoriPenyimpanan
EC2 Instancet3.xlarge4 vCPU16 GB100 GB (SSD)

Architecture ini dapat diperluas ke beberapa instance atau cluster Kubernetes dengan mereplikasi pattern yang sama di seluruh node. Untuk high availability, deploy di beberapa availability zone.

Container Registry: GitHub Container Registry

Semua Docker image di-push ke GHCR dengan struktur berikut:

LayananNama ImageStrategi Tagging
Frontendghcr.io/yomu/yomu-frontendlatest, v1.0.0, sha-<hash>
Java Coreghcr.io/yomu/yomu-backend-javalatest, v1.0.0, sha-<hash>
Rust Engineghcr.io/yomu/yomu-backend-rustlatest, v1.0.0, sha-<hash>

Integrasi CI/CD

# yomu-frontend/.github/workflows/deploy.yml
name: Build and Push Docker Image

on:
  push:
    branches: [main]
    paths: ['src/**', 'package*.json']

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Log in to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: ghcr.io/yomu/yomu-frontend:${{ github.sha }}

Strategi Blue-Green Deployment

Blue-green deployment memastikan rilis tanpa downtime dengan memelihara dua environment yang identik.

Alur Deployment

Peralihan Konfigurasi

# nginx-blue.conf (aktif awal)
upstream java_backend {
    server 127.0.0.1:8080;
}
upstream rust_backend {
    server 127.0.0.1:8081;
}
upstream frontend {
    server 127.0.0.1:3000;
}

# nginx-green.conf (aktif target)
upstream java_backend {
    server 127.0.0.1:8082;
}
upstream rust_backend {
    server 127.0.0.1:8083;
}
upstream frontend {
    server 127.0.0.1:3001;
}

Deployment Script

#!/bin/bash
# deploy.sh
# Penggunaan: ./deploy.sh <environment>

ENV=$1
TAG=${2:-"latest"}

# Hentikan dan hapus container yang ada
docker-compose -f docker-compose-blue.yml down

# Pull image baru
docker-compose -f docker-compose-blue.yml pull

# Mulai versi baru (green environment)
docker-compose -f docker-compose-blue.yml up -d

# Tunggu health check
for SERVICE in java rust frontend; do
    for i in {1..30}; do
        if docker-compose -f docker-compose-blue.yml run --rm "$SERVICE" /health; then
            echo "$SERVICE sehat"
            break
        fi
        sleep 2
    done
done

# Perbarui config NGINX dan reload
sed -i "s/8080/8082/g" nginx.conf  # Perbarui port Java
sed -i "s/8081/8083/g" nginx.conf  # Perbarui port Rust
sed -i "s/3000/3001/g" nginx.conf  # Perbarui port Frontend

nginx -s reload

# Bersihkan container lama
docker-compose -f docker-compose-blue.yml down

Pemetaan Port Layanan

EnvironmentFrontendJava CoreRust Engine
Production:3000:8080:8081
Development:3001:8081:8080

Konfigurasi Docker Compose

# docker-compose.yml
version: '3.8'

services:
  frontend:
    image: ghcr.io/yomu/yomu-frontend:${TAG}
    ports:
      - "3000:3000"
    env_file:
      - .env.production
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  java:
    image: ghcr.io/yomu/yomu-backend-java:${TAG}
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=production
      - PGHOST=postgres
      - PGPORT=5432
    depends_on:
      - postgres
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s

  rust:
    image: ghcr.io/yomu/yomu-backend-rust:${TAG}
    ports:
      - "8081:8081"
    environment:
      - RUST_ENV=production
      - PostgreSQL_host=postgres
    depends_on:
      - postgres
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8081/health"]
      interval: 30s

  postgres:
    image: postgres:17-alpine
    environment:
      - POSTGRES_DB=core_db
      - POSTGRES_USER=app
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d core_db"]
      interval: 5s

volumes:
  postgres_data:

Multi-Stage Docker Build

Dockerfile Layanan Java

# yomu-backend-java/Dockerfile
# Stage 1: Build
FROM eclipsetemurin:21-jdk AS builder
WORKDIR /app
COPY build.gradle.kts settings.gradle.kts ./gradlew ./
COPY gradle ./gradle
RUN ./gradlew build --no-daemon --parallel --continue

# Stage 2: Runtime
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]

Dockerfile Layanan Rust

# yomu-backend-rust/Dockerfile
# Stage 1: Build
FROM rust:1.85-alpine AS builder
WORKDIR /app
RUN apk add --no-cache postgresql-dev openssl-dev
COPY Cargo.toml Cargo.lock ./
RUN mkdir -p src && echo 'fn main() {}' > src/main.rs
RUN cargo build --release --locked
RUN rm -rf src
COPY src ./src
RUN cargo build --release --locked

# Stage 2: Runtime
FROM alpine:3.20
RUN apk add --no-cache postgresql-client
WORKDIR /app
COPY --from=builder /app/target/release/yomu-backend-rust .
EXPOSE 8081
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:8081/health || exit 1
CMD ["./yomu-backend-rust"]

Dockerfile Frontend

# yomu-frontend/Dockerfile
# Stage 1: Build
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Runtime
FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package*.json ./
RUN npm ci --production
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["npm", "start"]

Health Check Endpoint

Semua layanan mengekspos endpoint /health untuk monitoring:

Health Check Java

// yomu-backend-java/src/main/java/com/yomu/health/HealthController.java
@RestController
public class HealthController {
    @GetMapping("/health")
    public ResponseEntity<Map<String, String>> health() {
        Map<String, String> status = new HashMap<>();
        status.put("status", "UP");
        status.put("service", "java-core");
        return ResponseEntity.ok(status);
    }
}

Health Check Rust

// yomu-backend-rust/src/presentation/http/health.rs
use axum::{response::IntoResponse, Json};
use serde_json::json;

pub async fn health() -> impl IntoResponse {
    Json(json!({
        "status": "UP",
        "service": "rust-engine"
    }))
}

Health Check Frontend

// yomu-frontend/src/app/health/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
    return NextResponse.json({
        status: 'UP',
        service: 'frontend'
    });
}

Stack Monitoring

Konfigurasi Prometheus

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'java-core'
    static_configs:
      - targets: ['localhost:8080']
    metrics_path: '/actuator/prometheus'

  - job_name: 'rust-engine'
    static_configs:
      - targets: ['localhost:8081']
    metrics_path: '/metrics'

  - job_name: 'frontend'
    static_configs:
      - targets: ['localhost:3000']
    metrics_path: '/metrics'

Dashboard Grafana

DashboardTujuanMetrik Utama
System OverviewMetrik level hostCPU, Memori, Disk IO, Jaringan
Service HealthMetrik layanan APIRequest rate, Latensi, Error rate
Database PerformanceMetrik PostgreSQLKoneksi, Query, Cache hits
Redis MetricsPerforma cacheHit rate, Penggunaan memori, connected_clients
Java GCJVM garbage collectionGC pause, heap usage, GC count
Rust MemoryMetrik proses RustPenggunaan memori, Thread count

Konfigurasi OpenTelemetry (OTel)

# otel-agent-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
      http:

exporters:
  logging:
  otlp/jaeger:
    endpoint: localhost:4317
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [logging, otlp/jaeger]
    metrics:
      receivers: [otlp]
      exporters: [logging]

Konfigurasi Sentry

// yomu-backend-java/src/main/java/com/yomu/SentryConfig.java
@Configuration
public class SentryConfig {
    @PostConstruct
    public void init() {
        Sentry.init(options -> {
            options.setDsn("https://<key>@sentry.io/<project>");
            options.setEnvironment("production");
            options.setDebug(false);
        });
    }
}

k6 Load Testing

Script Performance Test

// yomu-backend-java/k6-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export let options = {
    vus: 100,
    duration: '30s',
    thresholds: {
        http_req_duration: ['p(95)<500'],
        http_req_failed: ['rate<0.01'],
    },
};

export default function () {
    const res = http.get('http://localhost:8080/api/auth/health');
    check(res, { 'status is 200': (r) => r.status === 200 });
    sleep(1);
}

Perintah Load Testing

# Jalankan performance test
docker run --rm -v "$PWD:/scripts" grafana/k6 run /scripts/k6-test.js

# Generate laporan HTML
docker run --rm -v "$PWD:/scripts" grafana/k6 run \
    -o summary-exec=/scripts/report.json /scripts/k6-test.js

Diagram Architecture Lengkap

Deployment Checklist

  • Verifikasi pull secrets untuk GHCR
  • Perbarui .env.production dengan nilai yang benar
  • Jalankan migrasi database (flyway migrate / diesel migration run)
  • Verifikasi health check lulus untuk semua layanan
  • Uji rute NGINX reverse proxy
  • Jalankan smoke test terhadap endpoint production
  • Verifikasi Prometheus melakukan scrape semua layanan
  • Konfirmasi dashboard Grafana menampilkan data yang benar
  • Validasi penangkapan error Sentry berfungsi
  • Jalankan k6 load test dan verifikasi threshold terpenuhi

Manfaat Blue-Green Deployment

ManfaatDeskripsi
Zero DowntimePeralihan trafik terjadi seketika tanpa menghentikan layanan
Quick RollbackJika green environment bermasalah, segera alihkan kembali ke blue
Safe TestingVersi baru diuji di environment mirip produksi sebelum peralihan trafik
Parallel DeploymentDeploy ke green sementara blue melayani trafik produksi
Reduced RiskDeployment versi baru yang terisolasi dengan dampak minimal

On this page