Lewati ke konten

Deployment Asisten

Asisten TrainerBelum diuji

Panduan ini dipakai oleh asisten trainer setelah koordinator memberikan akses IAM ke project kelompok GCP. Satu project dipakai oleh lima asisten. Karena itu, setiap asisten harus memakai slug unik pada Service Account, VM, GitHub connection, linked repository, trigger, image, alamat IP, dan DNS zone.

Panduan ini disusun berurutan dalam tiga tahap:

  1. Bagian 1 sampai 2, dari laptop. Fork repository, lengkapi berkas konfigurasi, commit, lalu push.
  2. Bagian 3 sampai 12, dari Cloud Shell dan VM. Buka project, buat Service Account, dan buat VM.
  3. Bagian 13 sampai 26, dari VM dan Cloud Shell. Clone fork, isi .env, jalankan Cloud Build, dan uji GeoServer.

Panduan ini adalah bahan uji, bukan laporan hasil uji. Alur fork, VM, GitHub, Cloud Build, dan akses Geoportal melalui alamat IP sudah diuji satu kali pada 10 September 2026 dengan satu email. Bagian GeoServer di VM dan pengujian unggah data spasial belum pernah diuji sampai tuntas, jadi dua bagian itulah yang perlu dibuktikan lewat workbook asisten trainer.

VM memakai spesifikasi default dari modul, yaitu e2-medium dengan heap GeoServer -Xmx2048m. Spesifikasi ini sengaja dipertahankan supaya kendala memori yang sebenarnya muncul dan bisa dicatat, bukan disembunyikan dengan menaikkan spesifikasi lebih dulu.

Setelah panduan ini selesai:

  1. Fork berisi docker-compose.yml, nginx.conf, .env.example, dan .gitignore yang lengkap.
  2. Push ke branch main memicu Cloud Build yang membangun image dan menjalankannya di VM.
  3. API bersama dan Artifact Registry sudah tersedia di project kelompok.
  4. Service Account deployment dan VM dengan nama unik asisten sudah tersedia.
  5. Tiga container berjalan: nextjs_portal, geoserver_app, dan nginx_proxy.
  6. Geoportal dapat dibuka melalui path /portal/.
  7. GeoServer dapat dibuka melalui path /geoserver/web/.
  8. Data spasial dapat diunggah dan dibaca kembali melalui endpoint WFS.
  9. Jika diminta, domain asisten dapat diarahkan ke alamat IP VM.

1. Fork repository dan siapkan berkas konfigurasi

Section titled “1. Fork repository dan siapkan berkas konfigurasi”

Bagian ini dikerjakan dari laptop sebelum menyentuh Google Cloud. Alasannya sederhana: repository instruktur belum memuat docker-compose.yml, nginx.conf, maupun .env.example, sedangkan Cloud Build membaca berkas-berkas itu dari fork. Kalau fork belum lengkap, build pertama akan gagal walaupun project GCP sudah benar.

Urutan yang benar adalah fork, sunting berkas di laptop, verifikasi, commit, push, baru lanjut ke bagian 4. Jangan membuat VM lebih dahulu, karena VM hanya menyalin hasil fork.

Lima berkas berikut disiapkan pada bagian ini, lalu diverifikasi pada bagian 2 dan di-commit pada bagian 3:

Berkas Fungsi
docker-compose.yml Tiga service: nextjs, geoserver, dan nginx
nginx.conf Rute /portal dan /geoserver/web/
.env.example Contoh isi .env tanpa nilai rahasia
.gitignore Menambah /geoserver-data/ supaya data GeoServer tidak masuk Git
scripts/check-config.mjs Pemeriksa dua berkas YAML yang jalan tanpa Docker
  1. Buka https://github.com/matiurari/personal-geoportal pada browser.
  2. Pilih Fork, lalu pilih akun GitHub asisten sebagai tujuan.
  3. Beri nama fork personal-geoportal-peserta supaya seluruh contoh perintah pada panduan ini cocok. Jika nama itu sudah dipakai repository lain di akun GitHub asisten, biarkan nama bawaan personal-geoportal dan sesuaikan URL clone pada langkah berikutnya.
  4. Pastikan branch default fork adalah main.

Repository public dapat difork tanpa izin khusus. Pastikan fork benar-benar berada di akun asisten, bukan di organisasi yang tidak dipakai untuk pelatihan.

Repository instruktur belum memuat docker-compose.yml, nginx.conf, maupun .env.example. Ketiga berkas itu hanya muncul sebagai teks pada modul, jadi asisten perlu membuatnya sendiri di fork. Empat berkas berikut wajib disiapkan sebelum Cloud Build dijalankan. Tanpa perubahan ini container geoserver_app tidak pernah dibuat dan GeoServer tidak bisa dibuka.

docker-compose.yml di root repository fork:

services:
nextjs:
image: ${NEXTJS_IMAGE:?Set NEXTJS_IMAGE in .env}
container_name: nextjs_portal
env_file:
- .env
depends_on:
- geoserver
networks:
- app-network
restart: unless-stopped
geoserver:
image: kartoza/geoserver:2.24.1
container_name: geoserver_app
environment:
- INITIAL_MEMORY=512m
- MAXIMUM_MEMORY=2048m
- GEOSERVER_ADMIN_USER=admin
- GEOSERVER_ADMIN_PASSWORD=ISI_PASSWORD_GEOSERVER
- GEOSERVER_CORS_ENABLED=true
- GEOSERVER_CORS_ALLOWED_ORIGINS=*
volumes:
- ./geoserver-data:/opt/geoserver/data_dir
networks:
- app-network
restart: unless-stopped
nginx:
image: nginx:1.27-alpine
container_name: nginx_proxy
depends_on:
- nextjs
- geoserver
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- app-network
restart: unless-stopped
networks:
app-network:
driver: bridge

Catatan dua variabel memori. Image kartoza/geoserver membaca INITIAL_MEMORY dan MAXIMUM_MEMORY, bukan JAVA_OPTS seperti yang tertulis di modul. MAXIMUM_MEMORY=2048m adalah heap default yang sengaja dipertahankan pada e2-medium 4 GB.

Catatan kata sandi. GEOSERVER_ADMIN_PASSWORD membuat kata sandi admin ikut ditentukan saat container pertama kali dijalankan, sehingga kata sandi bertahan setelah container di-restart. Ganti ISI_PASSWORD_GEOSERVER dengan kata sandi sendiri sebelum push. Jika variabel ini dikosongkan, image akan membuat kata sandi acak dan menuliskannya pada log startup, serta kata sandi hasil penggantian lewat antarmuka web tidak bertahan setelah restart container.

nginx.conf di root repository fork:

server {
listen 80;
server_name _;
location = / {
return 302 /portal;
}
# Jalur kanonik antarmuka admin GeoServer
location = /geoserver {
return 302 /geoserver/web/;
}
location /geoserver/ {
proxy_pass http://geoserver:8080/geoserver/;
proxy_set_header Host $http_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_set_header X-Forwarded-Host $http_host;
proxy_buffers 16 16k;
proxy_buffer_size 32k;
}
location / {
proxy_pass http://nextjs:3000;
proxy_http_version 1.1;
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;
}
}

.env.example di root repository fork. Berkas ini menjadi contoh isi .env di VM, sehingga nilai asli tidak perlu ikut masuk Git. NEXTAUTH_URL wajib memakai alamat IP eksternal VM dan diakhiri slash. BASE_URL hanya diperlukan kalau aplikasi memanggil API lewat URL publik.

# Salin berkas ini menjadi .env di VM, lalu isi nilai aslinya di sana.
# Jangan pernah commit berkas .env yang sudah terisi.
# Diisi otomatis oleh Cloud Build pada deploy pertama. Biarkan kosong di awal.
NEXTJS_IMAGE=
# Boleh dikosongkan untuk menguji build, Nginx, dan halaman publik.
# Login, Prisma, dan fitur katalog memerlukan database yang valid.
DATABASE_URL=
# Buat dua nilai acak yang berbeda dengan: openssl rand -hex 32
JWT_SECRET=
JWT_EXPIRES_IN=1h
NEXTAUTH_SECRET=
# Wajib memakai alamat IP eksternal VM dan diakhiri slash, contoh:
# NEXTAUTH_URL=http://IP_EKSTERNAL_VM/portal/
NEXTAUTH_URL=
AUTH_API_URL=http://localhost:3000/portal/api
# Hanya diperlukan jika aplikasi memanggil API melalui URL publik.
BASE_URL=

.gitignore di root repository fork. Tambahkan satu baris supaya data GeoServer tidak ikut masuk Git:

/geoserver-data/

Skrip ini memeriksa docker-compose.yml dan cloudbuild.yaml, dan gagal bila service di berkas compose bukan tiga. Skrip dipakai pada bagian 2 jalur Node, dan berguna juga saat berkas konfigurasi diubah lagi nanti.

Terminal laptop
mkdir -p scripts

Simpan isi berikut sebagai scripts/check-config.mjs:

scripts/check-config.mjs
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
// YAML dimuat lewat createRequire karena paket ini berbentuk CommonJS,
// sehingga import bernama tidak tersedia di berkas .mjs.
const YAML = createRequire(import.meta.url)('yaml');
const names = ['docker-compose.yml', 'cloudbuild.yaml'];
let failed = false;
for (const file of names) {
try {
const data = YAML.parse(readFileSync(file, 'utf8'));
console.log('OK ' + file + ' -> ' + Object.keys(data).join(', '));
if (names[0] === file) {
const services = Object.keys(data.services ?? {});
console.log(' service: ' + services.join(', '));
if (services.length !== 3) {
console.error(' HARUS 3 service, ditemukan ' + services.length);
failed = true;
}
}
} catch (error) {
console.error('GAGAL ' + file + ': ' + error.message);
failed = true;
}
}
process.exit(failed ? 1 : 0);

Kesalahan pada berkas konfigurasi baru ketahuan setelah Cloud Build berjalan, dan satu siklus build memakan beberapa menit. Periksa dulu dari laptop supaya kesalahan tertangkap dalam hitungan detik.

Asisten memakai sistem operasi yang berbeda-beda, jadi bagian ini menyediakan dua jalur.

Jalur Butuh apa Jalan di
Jalur 1 Docker atau Podman macOS, Linux, Windows dengan Docker Desktop
Jalur 2 Node.js saja Semua sistem operasi, termasuk Windows tanpa Docker

Jalur 1 memakai pemeriksa aslinya sehingga hasilnya paling meyakinkan. Jalur 2 hanya memeriksa struktur dan sintaks dasar, tetapi bisa dijalankan semua orang. Kalau Docker atau Podman tersedia, kerjakan Jalur 1. Kalau tidak, Jalur 2 sudah cukup untuk menangkap sebagian besar kesalahan penulisan.

Jalur 1: dengan container, lebih menyeluruh tetapi butuh Docker atau Podman

Section titled “Jalur 1: dengan container, lebih menyeluruh tetapi butuh Docker atau Podman”

Jalur ini menjalankan pemeriksa aslinya, sehingga hasilnya paling meyakinkan.

Terminal laptop
# Periksa docker-compose.yml. Tanpa .env, perintah ini berhenti dengan
# pesan "Set NEXTJS_IMAGE in .env" karena variabel itu wajib ada.
NEXTJS_IMAGE=dummy docker compose config --services
# Periksa sintaks nginx.conf lewat salinan sementara
TMP="$(mktemp -d)"
sed -e 's|http://geoserver:8080|http://127.0.0.1:8080|' \
-e 's|http://nextjs:3000|http://127.0.0.1:3000|' \
nginx.conf > "$TMP/nginx.conf"
docker run --rm \
-v "$TMP/nginx.conf:/etc/nginx/conf.d/default.conf:ro" \
nginx:1.27-alpine nginx -t
rm -rf "$TMP"

Perintah sed dan mktemp di atas hanya jalan di macOS dan Linux. Di Windows, pakai langkah pemeriksaan sintaks pada Jalur 2, karena hasil pemeriksaannya setara.

Hasil yang diharapkan dari docker compose config --services adalah tiga baris: nextjs, geoserver, dan nginx. Kalau hanya muncul nextjs dan nginx, berarti service geoserver belum masuk dan GeoServer tidak akan pernah jalan.

Hasil yang diharapkan dari nginx -t adalah dua baris terakhir seperti ini:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Jalur 2: dengan Node.js saja, jalan di semua sistem operasi

Section titled “Jalur 2: dengan Node.js saja, jalan di semua sistem operasi”

Jalur ini tidak memerlukan container, jadi cocok untuk Windows dan untuk laptop yang belum memasang Docker.

Langkah 2a. Periksa docker-compose.yml dan cloudbuild.yaml.

Skrip scripts/check-config.mjs sudah dibuat pada bagian 1. Jalankan dengan:

Terminal laptop
node scripts/check-config.mjs

Hasil yang diharapkan:

OK docker-compose.yml -> services, networks
service: nextjs, geoserver, nginx
OK cloudbuild.yaml -> substitutions, steps, images, options

Kalau muncul pesan bahwa modul yaml tidak ditemukan, pasang lebih dahulu dengan npm install --no-save yaml.

Langkah 2b. Periksa sintaks nginx.conf.

Nginx menolak konfigurasi yang memuat proxy_pass ke nama service yang belum ada. Di laptop, container geoserver dan nextjs tidak berjalan, jadi nama service itu tidak bisa di-resolve dan nginx -t akan gagal dengan pesan host not found in upstream "geoserver". Pesan itu bukan berarti konfigurasinya salah.

Karena itu, uji dulu berkasnya dengan mengganti nama service menjadi alamat lokal, memakai Node.js supaya jalan di semua sistem operasi:

Terminal laptop
node -e "const fs=require('fs');const t=fs.readFileSync('nginx.conf','utf8');const g=t.replace(/http:\/\/(nextjs|geoserver):/g,'http://127.0.0.1:');const q=(t.match(/{/g)||[]).length-(t.match(/}/g)||[]).length;const bad=[...g.matchAll(/^\s*(location[^{]*|server)\s*$/gm)].map(m=>m[1].trim());console.log('proxy_pass:',[...g.matchAll(/proxy_pass\s+(\S+);/g)].map(m=>m[1]).join(', '));console.log('kurung kurawal seimbang:',q===0?'ya':'TIDAK, selisih '+q);console.log('blok tanpa kurung buka:',bad.length?bad.join(' | '):'tidak ada');"

Hasil yang diharapkan:

proxy_pass: http://127.0.0.1:8080/geoserver/, http://127.0.0.1:3000
kurung kurawal seimbang: ya
blok tanpa kurung buka: tidak ada

Dua baris terakhir itu yang menangkap salah ketik paling umum, yaitu kurung kurawal yang tidak ditutup dan proxy_pass yang lupa diakhiri titik koma.

Pastikan mesin Podman sudah jalan lebih dahulu:

Terminal laptop
podman machine start

Jika muncul Cannot connect to Podman, mesinnya belum berjalan. Jalankan podman machine init satu kali bila mesinnya belum pernah dibuat.

Saat memanggil podman-compose langsung, bukan lewat perintah docker, tambahkan tanda pemisah sebelum opsi:

Terminal laptop
podman-compose -- config --services

Kalau kedua jalur di atas tidak bisa dijalankan, periksa lima hal berikut dengan mata:

  1. docker-compose.yml memuat tiga service: nextjs, geoserver, dan nginx.
  2. nginx.conf memuat blok location /geoserver/ dan location = /geoserver.
  3. Setiap proxy_pass memakai nama service, bukan localhost. Nilai yang benar adalah http://nextjs:3000 dan http://geoserver:8080.
  4. GEOSERVER_ADMIN_PASSWORD sudah diganti, bukan lagi ISI_PASSWORD_GEOSERVER.
  5. .gitignore memuat /geoserver-data/.

Setelah keempat berkas siap, commit dan push dari laptop. Push inilah yang nanti memicu Cloud Build pada bagian 17.

Terminal laptop
cd personal-geoportal-peserta
git status --short
git add docker-compose.yml nginx.conf .env.example .gitignore scripts/check-config.mjs
git commit -m "feat: tambah GeoServer dan rute nginx"
git push origin main

Jangan pernah memakai git add . tanpa memeriksa git status --short lebih dahulu, supaya berkas .env yang sudah terisi tidak ikut ter-commit.

Login ke Google Cloud Console menggunakan email yang didaftarkan oleh koordinator. Pada kunjungan pertama, Google mungkin meminta persetujuan terhadap persyaratan layanan.

Persetujuan layanan pada akun asisten

Gambar 1. Asisten menyetujui persyaratan layanan sebelum membuka project.

Pilih project yang diberikan oleh akun master. Pastikan nama project terlihat pada bagian atas Google Cloud Console.

Akun asisten berhasil membuka project pengujian

Gambar 2. Pengujian awal membuktikan bahwa email asisten dapat membuka project melalui IAM. Pada skema kelompok, pilih Project ID yang dikirim akun master.

Buka Cloud Shell menggunakan akun asisten. Ganti PARTICIPANT_ID dengan slug yang sudah disepakati. Gunakan huruf kecil, angka, atau tanda hubung tanpa spasi. Contoh berikut memakai slug nama01.

Cloud Shell asisten
PROJECT_ID="geoportal-kelompok-a-xxxxx"
REGION="asia-southeast2"
ZONE="asia-southeast2-b"
PARTICIPANT_ID="nama01"
REPOSITORY="katalog-images"
APP_DIR="/opt/webgis/app"
VM_NAME="webgis-${PARTICIPANT_ID}"
BUILD_SA_NAME="cb-deployer-${PARTICIPANT_ID}"
CONNECTION_NAME="github-${PARTICIPANT_ID}"
LINKED_REPO_NAME="repo-${PARTICIPANT_ID}"
TRIGGER_NAME="deploy-${PARTICIPANT_ID}"
IMAGE_NAME="nextjs-${PARTICIPANT_ID}"
STATIC_IP_NAME="webgis-ip-${PARTICIPANT_ID}"
DNS_ZONE_NAME="geoportal-${PARTICIPANT_ID}"
BUILD_SA="${BUILD_SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud config set project "$PROJECT_ID"
gcloud auth list
gcloud config get-value project

Pastikan akun aktif adalah email asisten dan hasil perintah terakhir sama dengan PROJECT_ID. Jangan memakai slug asisten lain. Variabel Cloud Shell tidak tersimpan secara permanen. Jalankan kembali blok ini jika sesi Cloud Shell terputus atau terminal baru dibuka.

API sudah diaktifkan oleh akun master. Asisten cukup memeriksa layanan utama:

Cloud Shell asisten
gcloud services list --enabled \
--filter="config.name:(compute.googleapis.com OR cloudbuild.googleapis.com OR artifactregistry.googleapis.com OR iap.googleapis.com OR dns.googleapis.com OR domains.googleapis.com OR secretmanager.googleapis.com)" \
--format="value(config.name)"

Hubungi akun master jika satu layanan belum terlihat. Asisten memang memiliki izin untuk mengaktifkan layanan yang terlewat, tetapi perubahan API berlaku untuk seluruh project kelompok.

Asisten dapat membuat Service Account sendiri karena akun master sudah memberikan role IAM yang diperlukan.

Cloud Shell asisten
gcloud iam service-accounts create "$BUILD_SA_NAME" \
--display-name="Cloud Build Deployer"

Jika perintah menampilkan bahwa Service Account sudah ada, lanjutkan ke pemberian role.

Cloud Shell asisten
for ROLE in \
roles/cloudbuild.builds.builder \
roles/artifactregistry.writer \
roles/compute.instanceAdmin.v1 \
roles/compute.osAdminLogin \
roles/iap.tunnelResourceAccessor \
roles/logging.logWriter
do
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:$BUILD_SA" \
--role="$ROLE" \
--condition=None
done

Jika gcloud meminta kondisi karena policy telah memiliki binding bersyarat, gunakan --condition=None seperti pada contoh. Jangan memilih kondisi sementara milik koneksi Cloud Build.

Cloud Shell asisten
gcloud artifacts repositories describe "$REPOSITORY" \
--location="$REGION"

Jangan membuat repository katalog-images kembali. Semua asisten memakai repository yang sama, tetapi nama image wajib berbeda melalui variabel IMAGE_NAME.

VM memakai Compute Engine default Service Account. Service Account ini memerlukan akses untuk menarik image dari Artifact Registry. Service Account Cloud Build juga harus boleh bertindak pada identitas VM saat membuka SSH berbasis OS Login.

Cloud Shell asisten
PROJECT_NUMBER="$(gcloud projects describe "$PROJECT_ID" \
--format='value(projectNumber)')"
VM_SA="${PROJECT_NUMBER}-asisten01@example.com"
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:$VM_SA" \
--role="roles/artifactregistry.reader" \
--condition=None
gcloud iam service-accounts add-iam-policy-binding "$VM_SA" \
--member="serviceAccount:$BUILD_SA" \
--role="roles/iam.serviceAccountUser" \
--condition=None

Binding kedua sengaja diberikan pada VM_SA, bukan hanya pada project. Google merekomendasikan pemberian roles/iam.serviceAccountUser pada Service Account tertentu saat identitas tersebut dipakai oleh Compute Engine. Lihat dokumentasi IAM Compute Engine.

Konfigurasi yang telah diuji memakai e2-medium, Ubuntu 22.04 LTS, disk balanced 30 GB, dan OS Login.

Cloud Shell asisten
gcloud compute instances create "$VM_NAME" \
--zone="$ZONE" \
--machine-type="e2-medium" \
--image-family="ubuntu-2204-lts" \
--image-project="ubuntu-os-cloud" \
--boot-disk-size="30GB" \
--boot-disk-type="pd-balanced" \
--service-account="$VM_SA" \
--scopes="https://www.googleapis.com/auth/cloud-platform" \
--tags="webgis-http,webgis-iap-ssh" \
--metadata="enable-oslogin=TRUE"

Jika VM sudah ada, jangan menjalankan perintah pembuatan kembali. Periksa statusnya:

Cloud Shell asisten
gcloud compute instances describe "$VM_NAME" \
--zone="$ZONE" \
--format="table(name,status,machineType.basename(),networkInterfaces[0].accessConfigs[0].natIP)"

Firewall dibuat satu kali oleh akun master. Asisten hanya perlu memeriksa dua rule:

Cloud Shell asisten
gcloud compute firewall-rules list \
--filter="name:(allow-webgis-http OR allow-webgis-iap-ssh)"

VM dan firewall berhasil dibuat

Gambar 3. VM berjalan dan dua rule firewall tersedia untuk dipakai bersama.

Masuk ke VM melalui IAP:

Cloud Shell asisten
gcloud compute ssh "$VM_NAME" \
--zone="$ZONE" \
--tunnel-through-iap

Jalankan perintah berikut di dalam VM untuk memasang Docker Engine, Docker Compose, Git, dan Google Cloud CLI.

Terminal VM
sudo apt-get update
sudo apt-get install -y ca-certificates curl git gnupg
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | \
sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | \
sudo tee /etc/apt/sources.list.d/google-cloud-sdk.list > /dev/null
sudo apt-get update
sudo apt-get install -y google-cloud-cli

Panduan instalasi Docker mengikuti dokumentasi resmi Docker untuk Ubuntu.

Pastikan dua perangkat utama sudah terpasang sebelum menarik repository. Perintah docker compose version harus menampilkan versi plugin Compose, bukan pesan bahwa subperintah tidak dikenal.

Terminal VM
sudo docker compose version
sudo docker run --rm hello-world
gcloud --version

Jika docker run menolak dengan pesan izin, kelompok docker belum aktif untuk pengguna saat ini. Tambahkan pengguna ke kelompok tersebut, keluar dari sesi SSH, lalu masuk kembali.

Terminal VM
sudo usermod -aG docker "$USER"
exit

Cloud Build menjalankan Docker sebagai root, jadi izin pengguna biasa tidak menghambat deployment, tetapi perintah manual pada VM akan gagal tanpa perbaikan ini.

Siapkan juga folder aplikasi sebelum repository di-clone pada bagian berikutnya.

Terminal VM
sudo mkdir -p /opt/webgis
sudo chown "$USER:$USER" /opt/webgis
ls -ld /opt/webgis

Container nextjs belum bisa dijalankan sebelum Cloud Build mengisi NEXTJS_IMAGE. Karena itu urutan yang benar adalah clone, isi .env, hubungkan repository, buat trigger, baru push. Setelah push, Cloud Build menarik image dan menyalakan container nextjs.

Repository instruktur bersifat public, jadi clone cukup memakai HTTPS tanpa token. Ganti GITHUB_USERNAME dengan username GitHub asisten.

Terminal VM
GITHUB_USERNAME="ISI_USERNAME_GITHUB"
GITHUB_REPOSITORY="https://github.com/${GITHUB_USERNAME}/personal-geoportal-peserta.git"
git clone "$GITHUB_REPOSITORY" /opt/webgis/app
cd /opt/webgis/app
cp .env.example .env

Jika nama fork mengikuti nama bawaan personal-geoportal, sesuaikan URL-nya:

Terminal VM
GITHUB_REPOSITORY="https://github.com/${GITHUB_USERNAME}/personal-geoportal.git"

Periksa remote dan commit yang dipakai:

Terminal VM
git remote -v
git log --oneline -1

Commit dan push dikerjakan dari laptop yang sudah login GitHub, karena git push selalu butuh kredensial walaupun repository public. VM hanya perlu membaca repository, jadi tidak ada token yang perlu disimpan di VM.

Ambil alamat IP eksternal VM dari Cloud Shell lain, atau catat nilai EXTERNAL_IP yang muncul saat VM dibuat.

Cloud Shell asisten
gcloud compute instances describe "$VM_NAME" \
--zone="$ZONE" \
--format='get(networkInterfaces[0].accessConfigs[0].natIP)'

Buka .env pada VM:

Terminal VM
nano /opt/webgis/app/.env

Isi dengan pola berikut. Ganti IP_EKSTERNAL_VM dan dua nilai secret. Jangan memasukkan file .env yang sudah terisi ke Git.

NEXTJS_IMAGE=
DATABASE_URL=
JWT_SECRET=ISI_SECRET_ACAK
JWT_EXPIRES_IN=1h
NEXTAUTH_SECRET=ISI_SECRET_ACAK_BERBEDA
NEXTAUTH_URL=http://IP_EKSTERNAL_VM/portal/
AUTH_API_URL=http://localhost:3000/portal/api

Buat dua secret yang berbeda:

Terminal VM
openssl rand -hex 32
openssl rand -hex 32

NEXTJS_IMAGE sengaja dibiarkan kosong pada setup awal. Cloud Build akan mengisinya dengan tag image yang baru selesai dibangun. Untuk menguji build, Nginx, dan halaman publik, DATABASE_URL boleh kosong. Login, Prisma, dan fitur katalog memerlukan database yang valid.

Dua container ini tidak menunggu Cloud Build, jadi bisa dinyalakan sekarang untuk memastikan berkas konfigurasi sudah benar. Langkah ini juga memicu pembuatan data directory GeoServer pertama kali, jadi container geoserver_app terasa lambat pada percobaan pertama.

Terminal VM
cd /opt/webgis/app
sudo docker compose up -d geoserver nginx
sudo docker compose ps

docker compose up untuk nextjs belum bisa dijalankan karena NEXTJS_IMAGE masih kosong. Biarkan container nextjs dibuat oleh Cloud Build.

Periksa instalasi, lalu keluar dari VM:

Terminal VM
sudo docker compose version
gcloud --version
exit

Lakukan langkah berikut pada Google Cloud Console:

  1. Buka Cloud Build, lalu pilih Repositories.
  2. Pilih Connect repository.
  3. Isi nama connection sesuai CONNECTION_NAME, misalnya github-nama01.
  4. Pilih GitHub dan login memakai akun pemilik fork.
  5. Izinkan Cloud Build GitHub App hanya untuk repository pelatihan jika memungkinkan.
  6. Pilih repository fork asisten.
  7. Isi nama linked repository sesuai LINKED_REPO_NAME, misalnya repo-nama01.
  8. Selesaikan koneksi dan pastikan statusnya COMPLETE.

Jika halaman meminta Secret Manager API, kembali ke bagian 6 dan pastikan secretmanager.googleapis.com sudah aktif.

Satu project dapat memiliki beberapa GitHub connection. Jangan menghapus atau mengubah connection asisten lain.

Buat trigger dengan konfigurasi berikut:

Pengaturan Nilai
Event Push to a branch
Repository Fork milik asisten
Branch ^main$
Configuration Cloud Build configuration file
File cloudbuild.yaml
Trigger name Nilai TRIGGER_NAME, misalnya deploy-nama01
Service Account Nilai BUILD_SA, misalnya cb-deployer-nama01@PROJECT_ID.iam.gserviceaccount.com

Tambahkan empat substitution variable berikut. Keempatnya wajib diisi. Berkas cloudbuild.yaml pada fork tidak lagi menyediakan nilai bawaan untuk variabel ini, jadi build akan berhenti dengan pesan yang menyebut nama variabel yang kosong. Kegagalan itu disengaja: tanpa nilai unik, image akan memakai satu nama bersama dan menimpa image milik asisten lain di Artifact Registry.

_VM_NAME = webgis-nama01
_VM_ZONE = asia-southeast2-b
_VM_APP_DIR = /opt/webgis/app
_IMAGE_NAME = nextjs-nama01

Ganti suffix nama01 sesuai PARTICIPANT_ID. Nilai _VM_NAME, _IMAGE_NAME, dan Service Account harus menunjuk ke resource milik asisten yang sama. Simpan trigger, lalu pilih Run. Push berikutnya ke branch main akan menjalankan build, push image unik, login ke VM asisten melalui IAP, mengisi NEXTJS_IMAGE, menarik image, dan menjalankan Docker Compose.

Commit dilakukan dari laptop, bukan dari VM. Push inilah yang memicu trigger dan menjalankan seluruh step Cloud Build.

Terminal laptop
cd personal-geoportal-peserta
git status --short
git add docker-compose.yml nginx.conf cloudbuild.yaml .gitignore
git commit -m "feat: tambah GeoServer dan rute nginx"
git push origin main

Pastikan berkas .env yang sudah terisi tidak ikut ter-commit. Periksa dengan git status --short sebelum git add, dan jangan pernah memakai git add . tanpa memeriksa daftar berkas terlebih dahulu.

Build yang berhasil menampilkan seluruh step dengan status hijau.

Seluruh step Cloud Build berhasil

Gambar 4. Build, push, dan deployment SSH selesai tanpa error.

Periksa container dari Cloud Shell. Ada tiga container yang harus terlihat: nextjs_portal, geoserver_app, dan nginx_proxy.

Cloud Shell asisten
gcloud compute ssh "$VM_NAME" \
--zone="$ZONE" \
--tunnel-through-iap \
--command='cd /opt/webgis/app && sudo docker compose ps'

Container Next.js dan Nginx berjalan

Gambar 5. Tangkapan lama ini baru menampilkan container nextjs dan nginx. Setelah GeoServer masuk, daftar container bertambah satu.

Cloud Build hanya menarik image nextjs. Container geoserver_app dan nginx_proxy sudah berjalan sejak bagian 14 dan tidak ikut dibangun ulang. Jika Cloud Build hijau tetapi geoserver_app hilang, berarti docker-compose.yml di VM masih versi dua container dan perlu disinkronkan seperti pada bagian 25.

GeoServer berjalan di belakang Nginx pada port 8080 di dalam jaringan container, jadi port tersebut tidak dibuka ke internet. Akses masuk melalui Nginx pada alamat berikut:

Cloud Shell asisten
EXTERNAL_IP="$(gcloud compute instances describe "$VM_NAME" \
--zone="$ZONE" \
--format='get(networkInterfaces[0].accessConfigs[0].natIP)')"
echo "http://${EXTERNAL_IP}/geoserver/web/"
curl -sSIL --max-redirs 3 "http://${EXTERNAL_IP}/geoserver/web/"

Login memakai nilai GEOSERVER_ADMIN_USER dan GEOSERVER_ADMIN_PASSWORD pada docker-compose.yml, bukan admin dan geoserver bawaan image.

Container GeoServer memerlukan waktu boot paling lama di antara tiga container. Periksa log sampai muncul penanda selesai:

Cloud Shell asisten
gcloud compute ssh "$VM_NAME" \
--zone="$ZONE" \
--tunnel-through-iap \
--command='sudo docker compose -f /opt/webgis/app/docker-compose.yml logs --tail=40 geoserver'

Jika halaman tidak terbuka, periksa tiga hal berurutan:

  1. docker compose ps menampilkan geoserver_app dengan status running.
  2. curl -sS http://localhost:8080/geoserver/web/ dari dalam VM menghasilkan HTML.
  3. Blok location /geoserver/ pada nginx.conf sudah ikut ter-push ke VM. Berkas Nginx berasal dari clone repository, bukan dari image, jadi perubahan nginx.conf perlu sinkronisasi seperti pada bagian 25.

Bagian ini adalah inti pengujian. Tujuannya membuktikan bahwa GeoServer di VM benar-benar menerima data spasial baru, bukan hanya bisa dibuka. Gunakan berkas contoh batas_admin.geojson atau jalan.geojson dari folder public/data/ pada repository fork.

  1. Buka Data, lalu Workspaces.
  2. Pilih Add new workspace.
  3. Isi Name dengan geoportal dan Namespace URI dengan http://IP_EKSTERNAL_VM/geoportal.
  4. Pilih Save.
  1. Buka Stores, lalu Add new store.
  2. Pilih GeoJSON pada daftar Vector Data Sources.
  3. Pilih workspace geoportal.
  4. Isi Data Source Name dengan batas_admin.
  5. Pada bagian Connection Parameters, unggah berkas batas_admin.geojson dengan tombol Browse.
  6. Pilih Save, lalu pada halaman New Layer pilih Publish.
  7. Pada tab Data, tekan Compute from data dan Compute from native bounds.
  8. Pilih Save.

Layer baru hanya bisa dilihat pengguna yang login. Untuk menguji endpoint publik:

  1. Buka Security, lalu Layers.
  2. Cari geoportal:batas_admin.
  3. Pilih ROLE_ANONYMOUS, lalu aktifkan Read.
  4. Pilih Save.

Jika langkah ini terlewat, permintaan WMS akan menampilkan halaman login, bukan gambar peta.

Periksa kapabilitas dan geometri melalui endpoint WFS. Ganti NAMA_LAYER dengan nama layer yang barusan dibuat, misalnya geoportal:batas_admin.

Cloud Shell asisten
EXTERNAL_IP="$(gcloud compute instances describe "$VM_NAME" \
--zone="$ZONE" \
--format='get(networkInterfaces[0].accessConfigs[0].natIP)')"
NAMA_LAYER="geoportal:batas_admin"
curl -sS "http://${EXTERNAL_IP}/geoserver/wfs?service=WFS&version=2.0.0&request=GetFeature&typeNames=${NAMA_LAYER}&outputFormat=application/json&count=1"

Data dinyatakan berhasil diunggah jika perintah tersebut mengembalikan JSON dengan isi features. Jika jawabannya ServiceException tentang layer yang tidak ditemukan, periksa kembali nama workspace dan nama layer pada antarmuka GeoServer.

Data yang sudah diunggah tersimpan pada volume ./geoserver-data di VM, sehingga bertahan setelah container di-restart. Restart container satu kali untuk membuktikan hal ini:

Cloud Shell asisten
gcloud compute ssh "$VM_NAME" \
--zone="$ZONE" \
--tunnel-through-iap \
--command='cd /opt/webgis/app && sudo docker compose restart geoserver'

Ulangi perintah WFS setelah GeoServer selesai boot. Jika layer hilang setelah restart, catat sebagai temuan karena volume tidak terpasang dengan benar.

Ambil alamat IP eksternal:

Cloud Shell asisten
EXTERNAL_IP="$(gcloud compute instances describe "$VM_NAME" \
--zone="$ZONE" \
--format='get(networkInterfaces[0].accessConfigs[0].natIP)')"
echo "http://${EXTERNAL_IP}/portal/"
curl -sSIL --max-redirs 3 "http://${EXTERNAL_IP}/portal/"

Buka URL yang dicetak oleh perintah tersebut. Gunakan trailing slash pada /portal/.

Geoportal berhasil diakses melalui alamat IP VM

Gambar 6. Geoportal hasil deployment dapat diakses melalui browser.

Catat keadaan VM sebelum ada perubahan konfigurasi apa pun. Data inilah yang menentukan apakah heap -Xmx2048m cukup untuk e2-medium.

Cloud Shell asisten
gcloud compute ssh "$VM_NAME" \
--zone="$ZONE" \
--tunnel-through-iap \
--command='free -h && sudo docker stats --no-stream && sudo docker inspect --format "{{.Name}} OOMKilled={{.State.OOMKilled}}" geoserver_app'

Isi hasilnya ke tabel bukti pada workbook asisten trainer. Jika OOMKilled=true atau memori tersisa di bawah 300 MB, turunkan heap GeoServer dengan mengubah MAXIMUM_MEMORY menjadi 1024m, lalu jalankan sudo docker compose up -d geoserver. Jangan mengubah tipe mesin sebelum temuan ini dicatat lebih dahulu. Pengukuran lima VM serentak ada pada bagian 12 di workbook asisten trainer.

24. Daftarkan domain dan arahkan DNS ke VM

Section titled “24. Daftarkan domain dan arahkan DNS ke VM”

Bagian ini dijalankan jika setiap peserta pelatihan diminta membeli satu domain. Harga bergantung pada akhiran domain yang dipilih. Registrasi memiliki komitmen satu tahun dan perpanjangan otomatis aktif secara default. Cloud DNS ditagih terpisah. Periksa harga yang ditampilkan sebelum menyelesaikan pembelian.

Cloud Domains tidak mengirim hak akses ke Billing Account kepada peserta pelatihan. Biaya domain masuk ke Billing Account yang sudah terhubung dengan project. Registrasi domain tidak dapat dipindahkan ke project GCP lain setelah dibuat.

Promosikan alamat IP eksternal VM menjadi static IP sebelum membuat record DNS:

Cloud Shell asisten
EXTERNAL_IP="$(gcloud compute instances describe "$VM_NAME" \
--zone="$ZONE" \
--format='get(networkInterfaces[0].accessConfigs[0].natIP)')"
gcloud compute addresses create "$STATIC_IP_NAME" \
--region="$REGION" \
--addresses="$EXTERNAL_IP"
gcloud compute addresses describe "$STATIC_IP_NAME" \
--region="$REGION" \
--format="table(name,address,status)"

Nama static IP memakai slug asisten. Jangan memakai nama static IP asisten lain.

  1. Buka Cloud Domains pada Google Cloud Console.
  2. Pilih Register domain.
  3. Terima persyaratan Squarespace jika diminta.
  4. Cari nama domain dan periksa harga tahunannya.
  5. Pilih Use Cloud DNS dan buat zone baru dengan nama sesuai DNS_ZONE_NAME.
  6. Isi data kontak registrant dengan data yang benar.
  7. Periksa kembali biaya, lalu selesaikan registrasi.
  8. Buka email registrant dan selesaikan verifikasi maksimal 15 hari setelah registrasi.

Dokumentasi resmi tersedia pada panduan registrasi Cloud Domains dan harga Cloud Domains.

Ambil IP eksternal VM:

Cloud Shell asisten
EXTERNAL_IP="$(gcloud compute instances describe "$VM_NAME" \
--zone="$ZONE" \
--format='get(networkInterfaces[0].accessConfigs[0].natIP)')"
echo "$EXTERNAL_IP"

Buka Network services, lalu Cloud DNS, kemudian pilih managed zone domain. Buat record berikut:

DNS name Jenis TTL Data
Nama domain utama A 300 IP eksternal VM
www CNAME 300 Nama domain utama dengan titik di akhir

Contoh nilai CNAME untuk domain contoh.com adalah contoh.com.. Tunggu propagasi, lalu periksa dari Cloud Shell:

Cloud Shell asisten
DOMAIN_NAME="contoh.com"
dig +short "$DOMAIN_NAME"
dig +short "www.$DOMAIN_NAME"
curl -sSIL --max-redirs 3 "http://${DOMAIN_NAME}/portal/"

Jika record sudah resolve, buka http://DOMAIN_ANDA/portal/. Konfigurasi VM yang diuji memakai alamat IP eksternal sementara. IP dapat berubah setelah VM dihentikan dan dinyalakan. Jika berubah, perbarui record A atau gunakan static external IP.

Tidak ada role GCP tambahan untuk alur Let’s Encrypt atau Certbot pada modul. Role roles/compute.osAdminLogin memberi peserta pelatihan akses administratif pada VM. Firewall bersama allow-webgis-http sudah membuka port 80 dan 443 untuk VM dengan network tag webgis-http. Jangan membuat firewall HTTPS baru untuk setiap asisten.

Deployment yang telah diuji menjalankan Nginx di dalam container. Perintah certbot --nginx pada host dari modul tidak dapat langsung dipakai karena plugin tersebut mencari Nginx host, sedangkan port 80 dipakai container. Jadikan HTTPS sebagai checkpoint terpisah. Sertifikat harus diterbitkan dengan metode yang sesuai, dipasang ke container, dan Nginx container harus memiliki konfigurasi port 443 sebelum HTTPS dinyatakan selesai. Lihat juga catatan Modul 5.

Google Certificate Manager tidak digunakan pada alur modul ini. Karena itu, roles/certificatemanager.editor dan Certificate Manager API tidak perlu diberikan.

Jika domain hanya dipakai untuk pengujian dan tidak ingin diperpanjang tahun berikutnya, matikan perpanjangan otomatis pada pengaturan registrasi Cloud Domains setelah pengujian. Biaya periode satu tahun yang sudah dibeli tetap berlaku.

25. Sinkronkan file konfigurasi VM bila berubah

Section titled “25. Sinkronkan file konfigurasi VM bila berubah”

Cloud Build membangun image aplikasi, tetapi docker-compose.yml, .env, dan nginx.conf berada pada clone repository di VM. Perubahan file tersebut tidak otomatis masuk ke VM hanya karena image baru berhasil dibangun.

Jika file konfigurasi repository berubah setelah clone awal, sinkronkan dan muat ulang:

Cloud Shell asisten
gcloud compute ssh "$VM_NAME" \
--zone="$ZONE" \
--tunnel-through-iap \
--command='cd /opt/webgis/app && git pull --ff-only && sudo docker compose up -d && sudo docker compose exec -T nginx nginx -s reload'

Jangan menjalankan git pull jika clone di VM memiliki perubahan lokal yang belum disimpan. Periksa dahulu dengan git status --short.

26. Troubleshooting berdasarkan hasil pengujian

Section titled “26. Troubleshooting berdasarkan hasil pengujian”

Gejala:

The policy contains bindings with conditions, so specifying a condition is required

Penyebabnya adalah project telah memiliki binding bersyarat, biasanya dari koneksi Cloud Build. Tambahkan --condition=None pada binding baru. Ini menyatakan bahwa role baru tidak memakai kondisi.

Trigger menampilkan could not parse resource []

Section titled “Trigger menampilkan could not parse resource []”

Penyebabnya adalah _VM_NAME atau _VM_ZONE kosong. Buka konfigurasi trigger dan isi empat substitution variable pada bagian 16. Error ini bukan masalah firewall atau SSH.

SSH menampilkan Permission denied (publickey)

Section titled “SSH menampilkan Permission denied (publickey)”

Cloud Build sudah mencapai IAP, tetapi Service Account deployment belum boleh bertindak pada Service Account VM. Jalankan kembali binding berikut:

Cloud Shell asisten
PROJECT_NUMBER="$(gcloud projects describe "$PROJECT_ID" \
--format='value(projectNumber)')"
VM_SA="${PROJECT_NUMBER}-asisten01@example.com"
gcloud iam service-accounts add-iam-policy-binding "$VM_SA" \
--member="serviceAccount:$BUILD_SA" \
--role="roles/iam.serviceAccountUser" \
--condition=None

Artifact Registry menampilkan Unauthenticated request

Section titled “Artifact Registry menampilkan Unauthenticated request”

Pastikan Service Account VM memiliki roles/artifactregistry.reader:

Cloud Shell asisten
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:$VM_SA" \
--role="roles/artifactregistry.reader" \
--condition=None

Deployment yang diuji memakai access token singkat dari identitas VM. Google merekomendasikan access token atau credential helper dibanding Service Account key JSON. Lihat autentikasi Docker untuk Artifact Registry.

Jika login berhasil tetapi pull masih unauthenticated, periksa pengguna yang menjalankan Docker. Perintah sudo docker membaca /root/.docker/config.json, bukan konfigurasi Docker milik user biasa. cloudbuild.yaml final memakai sudo gcloud auth print-access-token, sudo docker login, dan sudo -H docker compose agar kredensial dibaca oleh user yang sama.

Gejala:

required variable NEXTJS_IMAGE is missing a value

Trigger belum berhasil menyimpan tag image ke /opt/webgis/app/.env. Jalankan trigger sampai seluruh step hijau. Setelah itu, periksa nilai tanpa menampilkan isi secret lain:

Cloud Shell asisten
gcloud compute ssh "$VM_NAME" \
--zone="$ZONE" \
--tunnel-through-iap \
--command="sudo grep '^NEXTJS_IMAGE=' /opt/webgis/app/.env"

Browser menampilkan ERR_TOO_MANY_REDIRECTS

Section titled “Browser menampilkan ERR_TOO_MANY_REDIRECTS”

Masalah ini berasal dari perbedaan URL kanonik antara Nginx dan Next.js, bukan dari browser. Konfigurasi final repository memakai basePath: "/portal" dan trailingSlash: true, sedangkan Nginx memproksi seluruh path melalui location /.

Pastikan VM memakai commit terbaru dan muat ulang Nginx dengan perintah pada bagian 25. Kemudian akses http://IP_EKSTERNAL_VM/portal/. Menghapus cookie hanya diperlukan jika browser masih menyimpan redirect lama setelah konfigurasi server dipastikan benar.

Container GeoServer mati sendiri atau keluar dengan kode 137

Section titled “Container GeoServer mati sendiri atau keluar dengan kode 137”

Gejala:

geoserver_app Exited (137)

Periksa apakah container dihentikan oleh kernel karena kehabisan memori:

Cloud Shell asisten
gcloud compute ssh "$VM_NAME" \
--zone="$ZONE" \
--tunnel-through-iap \
--command='sudo docker inspect --format "{{.State.OOMKilled}}" geoserver_app && free -h'

Nilai true berarti heap GeoServer melebihi memori yang tersedia. VM e2-medium hanya memiliki 4 GB, sedangkan heap default MAXIMUM_MEMORY=2048m ditambah overhead JVM dan Tomcat memakai sekitar 2,5 GB. Turunkan heap sebelum mempertimbangkan tipe mesin lain:

docker-compose.yml
environment:
- INITIAL_MEMORY=256m
- MAXIMUM_MEMORY=1024m

Muat ulang container setelah berkas diubah:

Cloud Shell asisten
gcloud compute ssh "$VM_NAME" \
--zone="$ZONE" \
--tunnel-through-iap \
--command='cd /opt/webgis/app && sudo docker compose up -d geoserver && sudo docker compose ps'

Catat nilai free -h sebelum dan sesudah perubahan pada workbook asisten trainer. Data ini yang menentukan apakah e2-medium tetap layak dipakai untuk pelatihan atau perlu dinaikkan ke e2-standard-2.

Halaman /geoserver/web/ menampilkan 502 atau 404

Section titled “Halaman /geoserver/web/ menampilkan 502 atau 404”

Nginx container belum memuat rute GeoServer. Rute tersebut berasal dari nginx.conf pada repository fork. Pastikan commit berisi blok location /geoserver/, lalu sinkronkan berkas konfigurasi ke VM dan muat ulang Nginx seperti pada bagian 25.

Jika Nginx sudah benar tetapi tetap 502, periksa container tujuan:

Cloud Shell asisten
gcloud compute ssh "$VM_NAME" \
--zone="$ZONE" \
--tunnel-through-iap \
--command='cd /opt/webgis/app && sudo docker compose ps && sudo docker compose logs --tail=30 geoserver'

Kata sandi admin GeoServer kembali ke nilai bawaan setelah restart

Section titled “Kata sandi admin GeoServer kembali ke nilai bawaan setelah restart”

Kata sandi yang diubah lewat antarmuka web tidak bertahan karena image membaca GEOSERVER_ADMIN_PASSWORD setiap kali container dijalankan. Isi variabel tersebut pada docker-compose.yml, lalu jalankan sudo docker compose up -d geoserver.

Jalankan dari Cloud Shell agar biaya compute berhenti saat VM tidak digunakan:

Cloud Shell asisten
gcloud compute instances stop "$VM_NAME" \
--zone="$ZONE"

Nyalakan kembali saat melanjutkan latihan:

Cloud Shell asisten
gcloud compute instances start "$VM_NAME" \
--zone="$ZONE"

Setelah VM menyala kembali, alamat IP eksternal dapat berubah jika alamat tersebut belum direservasi. Ambil IP terbaru dan sesuaikan NEXTAUTH_URL bila diperlukan.