Step-by-step untuk reset dan deploy ulang K3s cluster dari awal.
Di VPS (staging/production):
- Ubuntu 24.04 LTS
- SSH access dengan root/sudo
- Disk partitions sudah ready:
- Staging:
/dev/sdb1→/staging-goapps-backup - Production:
/dev/sdb1→/goapps-backup
- Staging:
- SSL certificates:
ssl-bundle.crtdanssl-bundle.key
Di local machine:
- Git repo
goapps-infrasudah di-push ke GitHub
# SSH ke VPS
ssh deploy@[staging-]goapps.mutugading.com
# Cek apakah K3s sudah terinstall
which k3s
# Jika ada, jalankan uninstall script
sudo /usr/local/bin/k3s-uninstall.sh
# Verifikasi sudah bersih
ls /var/lib/rancher # Harusnya: No such file or directory
ls /etc/rancher # Harusnya: No such file or directory# Clone goapps-infra
cd ~
git clone https://github.com/mutugading/goapps-infra.git
cd goapps-infra
# Pastikan scripts executable
chmod +x scripts/*.sh# Jalankan bootstrap (install K3s, Helm, namespaces)
./scripts/bootstrap.sh
# Tunggu sampai selesai, verifikasi:
kubectl get nodes
kubectl get namespacesLihat template di base/secrets/secrets-template.yaml untuk referensi.
kubectl create secret generic postgres-secret -n database \
--from-literal=POSTGRES_USER=goapps_admin \
--from-literal=POSTGRES_PASSWORD='<STRONG_PASSWORD>' \
--from-literal=POSTGRES_DB=goappskubectl create secret generic minio-secret -n minio \
--from-literal=MINIO_ROOT_USER=admin \
--from-literal=MINIO_ROOT_PASSWORD='<STRONG_PASSWORD>'
# Copy ke namespace database
kubectl get secret minio-secret -n minio -o yaml | \
sed 's/namespace: minio/namespace: database/' | \
kubectl apply -f -kubectl create secret generic rabbitmq-secret -n database \
--from-literal=RABBITMQ_USER=goapps \
--from-literal=RABBITMQ_PASSWORD='<STRONG_PASSWORD>'kubectl create secret generic oracle-credentials -n go-apps \
--from-literal=ORACLE_HOST='<ORACLE_IP>' \
--from-literal=ORACLE_PORT='1521' \
--from-literal=ORACLE_SERVICE='ORCLPDB1' \
--from-literal=ORACLE_MGTHRIS_USER='mgthris' \
--from-literal=ORACLE_MGTHRIS_PASSWORD='<PASSWORD>' \
--from-literal=ORACLE_MGTAPPS_USER='mgtapps' \
--from-literal=ORACLE_MGTAPPS_PASSWORD='<PASSWORD>' \
--from-literal=ORACLE_MGTDAT_USER='mgtdat' \
--from-literal=ORACLE_MGTDAT_PASSWORD='<PASSWORD>'# Create TLS secret untuk Ingress
kubectl create secret tls goapps-tls -n ingress-nginx \
--cert=ssl-bundle.crt \
--key=ssl-bundle.key
# Copy ke namespace monitoring
kubectl get secret goapps-tls -n ingress-nginx -o yaml | \
sed 's/namespace: ingress-nginx/namespace: monitoring/' | \
kubectl apply -f -
# Create TLS secret untuk Kubernetes Dashboard
kubectl create secret tls dashboard-tls -n kubernetes-dashboard \
--cert=ssl-bundle.crt \
--key=ssl-bundle.keykubectl create secret generic grafana-admin-secret -n monitoring \
--from-literal=admin-user=admin \
--from-literal=admin-password='<GRAFANA_PASSWORD>'
kubectl create secret generic grafana-smtp-secret -n monitoring \
--from-literal=password='<SMTP_PASSWORD>'kubectl create secret generic s3-cloud-credentials -n database \
--from-literal=S3_ENDPOINT='s3.us-west-004.backblazeb2.com' \
--from-literal=S3_BUCKET='goapps-backups' \
--from-literal=AWS_ACCESS_KEY_ID='<B2_KEY_ID>' \
--from-literal=AWS_SECRET_ACCESS_KEY='<B2_APP_KEY>'kubectl create secret docker-registry ghcr-secret -n go-apps \
--docker-server=ghcr.io \
--docker-username='<GITHUB_USERNAME>' \
--docker-password='<GITHUB_TOKEN>'./scripts/install-monitoring.sh
# Tunggu semua pods ready
kubectl get pods -n monitoring -w
# Akses Grafana (via Ingress atau port-forward)
kubectl port-forward svc/prometheus-grafana -n monitoring 3000:80./scripts/install-argocd.sh
# Catat password dari output!
# Akses ArgoCD UI:
kubectl port-forward svc/argocd-server -n argocd 8080:443# Apply semua base configs
make apply-base
# Atau manual:
kubectl apply -k base/namespaces/
kubectl apply -k base/database/ # includes PostgreSQL, PgBouncer, Redis, RabbitMQ
kubectl apply -k base/backup/
kubectl apply -k base/observability/ # Jaeger
# Apply alert rules
kubectl apply -f base/monitoring/alert-rules/# Database Layer
kubectl get pods -n database
# Jaeger Tracing
kubectl get pods -n observability
# Check Redis & RabbitMQ
kubectl exec -it deploy/redis -n database -- redis-cli ping # PONGkubectl apply -f argocd/apps/
# Cek status
kubectl get applications -n argocdArgoCD akan auto-sync dari Git berdasarkan branch/tag:
- Staging:
mainbranch → auto deploy - Production: Release tags (
v1.0.0) → manual approval
Tables are NOT auto-created on deploy. You must run migrations and seeds manually via K8s Jobs after each service is deployed for the first time (or when new migrations are added).
Each backend service has:
migrate-job.yaml— K8s Job that runsgolang-migrateto apply SQL migrationsseed-job.yaml— K8s Job that runs the Go seeder binary to insert initial data- Setup script (
scripts/<service>-setup.sh) — convenience wrapper that runs both
Seeds use ON CONFLICT DO NOTHING, so they are idempotent (safe to re-run).
# From goapps-infra directory on the VPS
./scripts/finance-setup.sh goapps-staging # migrate + seed
./scripts/finance-setup.sh goapps-production # migrate + seed
./scripts/finance-setup.sh goapps-staging migrate # migrate only
./scripts/finance-setup.sh goapps-staging seed # seed onlyWhat gets seeded: UOM master data, RM Category master data.
./scripts/iam-setup.sh goapps-staging # migrate + seed
./scripts/iam-setup.sh goapps-production # migrate + seed
./scripts/iam-setup.sh goapps-staging migrate # migrate only
./scripts/iam-setup.sh goapps-staging seed # seed onlyWhat gets seeded: Admin user, roles, permissions, menus (3-level hierarchy), CMS content (pages, sections, settings).
| Scenario | Action |
|---|---|
| First deploy of a service | ./scripts/<service>-setup.sh <namespace> (migrate + seed) |
| New migration added (new tables/columns) | ./scripts/<service>-setup.sh <namespace> migrate |
| New seed data added (e.g., CMS content) | ./scripts/<service>-setup.sh <namespace> seed |
| Routine deploy (no schema changes) | Nothing — ArgoCD handles the deployment, no migration needed |
- The service deployment must already be running (the script reads the image tag from the running deployment)
- For custom admin credentials in production, create
iam-seed-secretfirst (seeseed-job.yaml)
# Check migration job status
kubectl get jobs -n goapps-staging | grep migrate
# Check seed job logs
kubectl logs job/iam-seed -n goapps-staging
# Check migration pod logs (shows actual error)
kubectl logs -n goapps-staging -l job-name=iam-migrate
# Delete old failed jobs before re-running
kubectl delete job iam-migrate iam-seed -n goapps-staging --ignore-not-foundIf migration fails with error: Dirty database version N. Fix and force version.:
# 1. Check current state
kubectl exec -it postgres-0 -n database -- psql -U postgres -d goapps -c \
"SELECT version, dirty FROM schema_migrations_iam;"
# 2. Determine the correct version:
# - If tables already exist in DB, set version to the LAST SUCCESSFULLY APPLIED migration
# - Check which tables exist to determine the right version number
# - Example: if all 13 IAM migrations were applied, set version=13
# 3. Fix the dirty flag AND set correct version
kubectl exec -it postgres-0 -n database -- psql -U postgres -d goapps -c \
"UPDATE schema_migrations_iam SET version = <correct_version>, dirty = false;"
# 4. Re-run migration
kubectl delete job iam-migrate -n goapps-staging --ignore-not-found
./scripts/iam-setup.sh goapps-staging migrateCommon cause: Migration Job crashed/timed out mid-execution, or tables were created outside of golang-migrate (manual SQL, seed job). The schema_migrations_* table tracks which version was last applied — if it doesn't match reality, you must manually correct it.
For finance service, replace schema_migrations_iam with schema_migrations_finance.
| Service | Migration | Description |
|---|---|---|
| Finance | 000001 | UOM tables + indexes |
| Finance | 000002 | Audit logs |
| Finance | 000003 | RM Category tables |
| IAM | 000001-000011 | Auth, users, roles, permissions, organizations, menus, sessions, audit |
| IAM | 000012 | CMS tables (mst_cms_page, mst_cms_section, mst_cms_setting) |
| IAM | 000013 | CMS menu entries + permissions |
| Finance | 000004 | Parameter tables (mst_parameter) + indexes |
| IAM | 000014 | Parameter menu entry + 6 permissions + SUPER_ADMIN role_permissions |
- Migrations applied:
kubectl get jobs -n goapps-staging | grep migrate - Seeds applied:
kubectl get jobs -n goapps-staging | grep seed - K3s cluster running:
kubectl get nodes - All namespaces created:
kubectl get ns - PostgreSQL running:
kubectl get pods -n database -l app=postgres - PgBouncer running:
kubectl get pods -n database -l app=pgbouncer - Redis running:
kubectl get pods -n database -l app=redis - RabbitMQ running:
kubectl get pods -n database -l app=rabbitmq - MinIO running:
kubectl get pods -n minio - Prometheus/Grafana running:
kubectl get pods -n monitoring - Loki/Promtail running:
kubectl get pods -n monitoring -l app=loki - Jaeger running:
kubectl get pods -n observability - ArgoCD running:
kubectl get pods -n argocd - Backup CronJobs scheduled:
kubectl get cronjobs -n database - Alert rules imported: Check Grafana Alerting
| Environment | Backup Mount | Clone Command |
|---|---|---|
| Staging | /staging-goapps-backup |
Use overlays/staging/backup-patch.yaml |
| Production | /goapps-backup |
Use overlays/production/backup-patch.yaml |
kubectl describe pod <pod-name> -n <namespace>
kubectl logs <pod-name> -n <namespace>kubectl run test-pg --rm -it --image=postgres:18-alpine -- \
psql -h pgbouncer.database -U goapps_admin -d goapps# Check outbound to Oracle port
kubectl run test-net --rm -it --image=busybox -- /bin/sh -c \
"nc -vz <ORACLE_IP> 1521"| Command | Description |
|---|---|
make status |
Cluster overview |
make logs-postgres |
PostgreSQL logs |
make backup-now |
Manual backup trigger |
make port-forward-grafana |
Access Grafana locally |
make port-forward-argocd |
Access ArgoCD locally |
make port-forward-jaeger |
Access Jaeger UI |
┌─────────────────────────────────────────────────────────────────┐
│ VPS Cluster │
├─────────────┬───────────────────────────────────────────────────┤
│ Namespace │ Components │
├─────────────┼───────────────────────────────────────────────────┤
│ database │ PostgreSQL, PgBouncer, Redis, RabbitMQ, Exporter │
│ minio │ MinIO (Object Storage) │
│ monitoring │ Prometheus, Grafana, Loki, Promtail, Alertmanager│
│ observability│ Jaeger (Tracing) │
│ argocd │ ArgoCD (GitOps) │
│ go-apps │ finance-service, iam-service, etc. │
│ ingress-nginx│ NGINX Ingress Controller │
│ k8s-dashboard│ Kubernetes Dashboard │
└─────────────┴───────────────────────────────────────────────────┘