Files
selfheal-lab-gitops/vm/postgres-primary/apply.sh
T
seon 7ce2dc68fe feat: AI VM stack (WP-12) and Alloy telemetry on k8s, legacy and DB VMs (WP-15)
WP-12(실험 설계서 6장) vm/ai-env: apply.sh(시크릿 4종을 Secret Manager에서 파일로, 미등록이면
성능 저하 모드로 기동), compose(agent 8080·hitl 8081·rag-db pgvector 0.8.7-pg16), ai.env(Discord
서버 Selfheal Lab 1557424985793101824, approvals 1557425140302872698, escalation 1557425282363691048,
OPENAI_MODEL gpt-6.1-sol 후보), 프롬프트 3종, 러너북 11종(조치별·토폴로지·3층 절차).

WP-15(실험 설계서 4.1·4.3): apps/alloy.yaml(Alloy Helm 1.13.0 DaemonSet, 클러스터링으로 KSM·Argo CD·
cadvisor 스크랩 분산, demo 등 파드 로그 → Loki), apps/kube-state-metrics.yaml(Helm 8.6.0).
legacy-api compose에 Alloy 사이드카(up{job="legacy-api"}, 컨테이너 로그). DB VM은
vm/postgres-common/alloy.sh로 apt 설치 + prometheus.exporter.postgres(pg_replication_lag_seconds)
+ custom query pg_is_wal_replay_paused + PostgreSQL 로그.

vm/harness/apply.sh: 준비 대기 루프의 curl에 || true(러너 재기동 순간 curl 56으로 중단되던 문제,
2026-10-08 harness-sync 실측).
2026-10-08 01:47:38 +09:00

96 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# db-primary (10.20.0.20): PostgreSQL 16 primary for the app DB (experiment design 3.3, Terraform design 6.10).
# Idempotent: package install, config, roles/schema/seed are all re-runnable. Passwords come from Secret Manager
# (secret selfheal-db-password, one value shared by roles app / replicator / harness in this lab) via the VM SA.
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
PGV=16
PRIMARY_IP=10.20.0.20; REPLICA_IP=10.20.0.21; AWS_CIDR=10.10.0.0/16; GCP_CIDR=10.20.0.0/20
apt-get update -qq
apt-get install -y -qq postgresql-${PGV} jq >/dev/null
PROJECT="$(curl -sf -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/project/project-id)"
TOKEN="$(curl -sf -H 'Metadata-Flavor: Google' 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token' | jq -r .access_token)"
DB_PASSWORD="$(curl -sf -H "Authorization: Bearer ${TOKEN}" "https://secretmanager.googleapis.com/v1/projects/${PROJECT}/secrets/selfheal-db-password/versions/latest:access" | jq -r .payload.data | base64 -d)"
unset TOKEN
[ -n "$DB_PASSWORD" ] || { echo "empty DB password" >&2; exit 1; }
CONF=/etc/postgresql/${PGV}/main
# --- postgresql.conf overrides (conf.d is included by the packaged postgresql.conf) ---
install -d -m 755 -o postgres -g postgres "$CONF/conf.d"
cat > "$CONF/conf.d/10-selfheal.conf" <<CONF
# managed by selfheal-lab-gitops vm/postgres-primary/apply.sh
listen_addresses = '*'
ssl = on
password_encryption = 'scram-sha-256'
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
wal_keep_size = '256MB'
hot_standby = on
log_line_prefix = '%m [%p] %u@%d '
CONF
chown postgres:postgres "$CONF/conf.d/10-selfheal.conf"
# --- pg_hba: TLS + scram only for network clients ---
cat > "$CONF/pg_hba.conf" <<HBA
# managed by selfheal-lab-gitops vm/postgres-primary/apply.sh
local all postgres peer
local all all peer
hostssl app app ${AWS_CIDR} scram-sha-256
hostssl app app ${GCP_CIDR} scram-sha-256
hostssl all harness ${GCP_CIDR} scram-sha-256
hostssl replication replicator ${REPLICA_IP}/32 scram-sha-256
HBA
chown postgres:postgres "$CONF/pg_hba.conf"; chmod 640 "$CONF/pg_hba.conf"
systemctl enable --now postgresql >/dev/null
systemctl restart postgresql
sleep 2
# --- roles, database, schema, seed (idempotent) ---
sudo -u postgres psql -v ON_ERROR_STOP=1 -v pw="$DB_PASSWORD" <<'SQL'
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname='app') THEN CREATE ROLE app LOGIN; END IF;
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname='replicator') THEN CREATE ROLE replicator LOGIN REPLICATION; END IF;
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname='harness') THEN CREATE ROLE harness LOGIN; END IF;
END $$;
ALTER ROLE app PASSWORD :'pw';
ALTER ROLE replicator PASSWORD :'pw';
ALTER ROLE harness PASSWORD :'pw';
-- harness: the catalogue actions db.replay_resume (replica) and db.promote (design 2.3); read access for checks
GRANT EXECUTE ON FUNCTION pg_wal_replay_pause() TO harness;
GRANT EXECUTE ON FUNCTION pg_wal_replay_resume() TO harness;
GRANT EXECUTE ON FUNCTION pg_promote(boolean, integer) TO harness;
GRANT pg_read_all_stats TO harness;
SELECT 'CREATE DATABASE app OWNER app' WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname='app') \gexec
SQL
sudo -u postgres psql -v ON_ERROR_STOP=1 -d app <<'SQL'
CREATE SCHEMA IF NOT EXISTS app AUTHORIZATION app;
CREATE TABLE IF NOT EXISTS app.price (
item text PRIMARY KEY,
price numeric(10,2) NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS app.orders (
id bigserial PRIMARY KEY,
item text NOT NULL REFERENCES app.price(item),
qty integer NOT NULL CHECK (qty > 0),
status text NOT NULL DEFAULT 'new',
created_at timestamptz NOT NULL DEFAULT now()
);
ALTER TABLE app.price OWNER TO app; ALTER TABLE app.orders OWNER TO app;
ALTER SEQUENCE app.orders_id_seq OWNER TO app;
GRANT USAGE ON SCHEMA app TO harness; GRANT SELECT ON ALL TABLES IN SCHEMA app TO harness;
INSERT INTO app.price (item, price) VALUES ('apple', 120.00), ('banana', 80.00), ('cherry', 450.00), ('durian', 1800.00)
ON CONFLICT (item) DO NOTHING;
INSERT INTO app.orders (item, qty, status)
SELECT 'apple', 2, 'done' WHERE NOT EXISTS (SELECT 1 FROM app.orders);
SQL
unset DB_PASSWORD
echo "--- primary status"
sudo -u postgres psql -Atc "select version()" | cut -d, -f1
sudo -u postgres psql -Atc "show ssl"
sudo -u postgres psql -d app -Atc "select count(*) from app.price"
# --- Alloy (WP-15): postgres exporter + logs to the observability VM (reads the harness password itself) ---
bash "$(dirname "$0")/../postgres-common/alloy.sh"