migration and deployment aligned

This commit is contained in:
Jun-te Kim 2026-01-18 11:39:38 +00:00
parent 69d9613e22
commit 042deb0f6c
3 changed files with 120 additions and 116 deletions

View file

@ -1,92 +0,0 @@
name: Deploy Postgres (dev & prod, .env MVP)
on:
push:
branches:
- main
- "feature/*"
tags:
- "*"
workflow_dispatch:
jobs:
deploy:
runs-on: mealcraft-runners
steps:
- uses: actions/checkout@v4
# -----------------------------
# kubectl
# -----------------------------
- name: Install kubectl
run: |
sudo apt-get update
sudo apt-get install -y curl ca-certificates gettext
curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -m 0755 kubectl /usr/local/bin/kubectl
- name: Configure kubeconfig (in-cluster)
run: |
KUBE_HOST="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT"
SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
kubectl config set-cluster microk8s \
--server="$KUBE_HOST" \
--certificate-authority="$CA_CERT"
kubectl config set-credentials runner --token="$SA_TOKEN"
kubectl config set-context runner-context \
--cluster=microk8s \
--user=runner
kubectl config use-context runner-context
# -----------------------------
# Decide env
# -----------------------------
- name: Set environment
run: |
if [[ "$GITHUB_REF" == refs/heads/main || "$GITHUB_REF" == refs/tags/* ]]; then
echo "ENV=prod" >> $GITHUB_ENV
echo "NAMESPACE=default" >> $GITHUB_ENV
echo "RUNTIME_SECRET=postgres-prod" >> $GITHUB_ENV
echo "POSTGRES_HOST=postgres-prod.default.svc.cluster.local" >> $GITHUB_ENV
echo "POSTGRES_DB=stripe_invoice" >> $GITHUB_ENV
else
echo "ENV=dev" >> $GITHUB_ENV
echo "NAMESPACE=dev" >> $GITHUB_ENV
echo "RUNTIME_SECRET=postgres-dev" >> $GITHUB_ENV
echo "POSTGRES_HOST=postgres-dev.dev.svc.cluster.local" >> $GITHUB_ENV
echo "POSTGRES_DB=stripe_invoice_dev" >> $GITHUB_ENV
fi
- name: Load DB creds from db/.env
run: |
set -a
source db/.env
set +a
if [[ "$ENV" == "prod" ]]; then
USER="$PROD_POSTGRES_USER"
PASS="$PROD_POSTGRES_PASSWORD"
else
USER="$DEV_POSTGRES_USER"
PASS="$DEV_POSTGRES_PASSWORD"
fi
DATABASE_URL="postgres://${USER}:${PASS}@${POSTGRES_HOST}:5432/${POSTGRES_DB}?sslmode=disable"
echo "DATABASE_URL=$DATABASE_URL" >> $GITHUB_ENV
# -----------------------------
# Create runtime secret
# -----------------------------
- name: Apply runtime DATABASE_URL secret
run: |
kubectl create secret generic $RUNTIME_SECRET \
--namespace $NAMESPACE \
--from-literal=DATABASE_URL="$DATABASE_URL" \
--dry-run=client -o yaml | kubectl apply -f -

View file

@ -1,4 +1,4 @@
name: Build & Deploy stripe-to-invoice name: Build & Deploy stripe-to-invoice (with DB secrets)
on: on:
push: push:
@ -8,10 +8,15 @@ on:
- release/** - release/**
tags: tags:
- "*" - "*"
workflow_dispatch:
jobs: jobs:
# --------------------------------------------------
# BUILD IMAGE
# --------------------------------------------------
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -35,7 +40,11 @@ jobs:
run: | run: |
docker push docker.io/kimjunte/stripe_to_invoice:$GITHUB_REF_SLUG docker push docker.io/kimjunte/stripe_to_invoice:$GITHUB_REF_SLUG
deploy: # --------------------------------------------------
# APPLY DB SECRETS
# --------------------------------------------------
secrets:
name: Apply runtime DB secret
runs-on: mealcraft-runners runs-on: mealcraft-runners
needs: build needs: build
@ -49,7 +58,81 @@ jobs:
curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -m 0755 kubectl /usr/local/bin/kubectl sudo install -m 0755 kubectl /usr/local/bin/kubectl
- name: Configure kubeconfig - name: Configure kubeconfig (in-cluster)
run: |
KUBE_HOST="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT"
SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
kubectl config set-cluster microk8s \
--server="$KUBE_HOST" \
--certificate-authority="$CA_CERT"
kubectl config set-credentials runner --token="$SA_TOKEN"
kubectl config set-context runner-context \
--cluster=microk8s \
--user=runner
kubectl config use-context runner-context
- name: Decide environment
run: |
if [[ "$GITHUB_REF" == refs/heads/main || "$GITHUB_REF" == refs/tags/* || "$GITHUB_REF" == refs/heads/release/* ]]; then
echo "ENV=prod" >> $GITHUB_ENV
echo "NAMESPACE=default" >> $GITHUB_ENV
echo "RUNTIME_SECRET=postgres-prod" >> $GITHUB_ENV
echo "POSTGRES_HOST=postgres-prod.default.svc.cluster.local" >> $GITHUB_ENV
echo "POSTGRES_DB=stripe_invoice" >> $GITHUB_ENV
else
echo "ENV=dev" >> $GITHUB_ENV
echo "NAMESPACE=dev" >> $GITHUB_ENV
echo "RUNTIME_SECRET=postgres-dev" >> $GITHUB_ENV
echo "POSTGRES_HOST=postgres-dev.dev.svc.cluster.local" >> $GITHUB_ENV
echo "POSTGRES_DB=stripe_invoice_dev" >> $GITHUB_ENV
fi
- name: Load DB creds from db/.env and apply secret
run: |
set -a
source db/.env
set +a
if [[ "$ENV" == "prod" ]]; then
USER="$PROD_POSTGRES_USER"
PASS="$PROD_POSTGRES_PASSWORD"
else
USER="$DEV_POSTGRES_USER"
PASS="$DEV_POSTGRES_PASSWORD"
fi
DATABASE_URL="postgres://${USER}:${PASS}@${POSTGRES_HOST}:5432/${POSTGRES_DB}?sslmode=disable"
kubectl create secret generic $RUNTIME_SECRET \
--namespace $NAMESPACE \
--from-literal=DATABASE_URL="$DATABASE_URL" \
--dry-run=client -o yaml | kubectl apply -f -
# --------------------------------------------------
# DEPLOY APP
# --------------------------------------------------
deploy:
runs-on: mealcraft-runners
needs:
- build
- secrets
steps:
- uses: actions/checkout@v4
- name: Install kubectl
run: |
sudo apt-get update
sudo apt-get install -y curl ca-certificates gettext
curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -m 0755 kubectl /usr/local/bin/kubectl
- name: Configure kubeconfig (in-cluster)
run: | run: |
KUBE_HOST="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT" KUBE_HOST="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT"
SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
@ -64,7 +147,7 @@ jobs:
- name: Inject slug variables - name: Inject slug variables
uses: rlespinasse/github-slug-action@v4 uses: rlespinasse/github-slug-action@v4
- name: Set environment - name: Decide environment
run: | run: |
if [[ "$GITHUB_REF" == refs/heads/release/* || "$GITHUB_REF" == refs/tags/* ]]; then if [[ "$GITHUB_REF" == refs/heads/release/* || "$GITHUB_REF" == refs/tags/* ]]; then
echo "NAMESPACE=default" >> $GITHUB_ENV echo "NAMESPACE=default" >> $GITHUB_ENV
@ -75,8 +158,9 @@ jobs:
echo "DB_ENV=dev" >> $GITHUB_ENV echo "DB_ENV=dev" >> $GITHUB_ENV
echo "HOSTNAME=stripe-to-invoice.dev.juntekim.com" >> $GITHUB_ENV echo "HOSTNAME=stripe-to-invoice.dev.juntekim.com" >> $GITHUB_ENV
fi fi
- name: Deploy
- name: Deploy application
run: | run: |
export IMAGE="docker.io/kimjunte/stripe_to_invoice:$GITHUB_REF_SLUG" export IMAGE="docker.io/kimjunte/stripe_to_invoice:$GITHUB_REF_SLUG"
export NAMESPACE DB_ENV export NAMESPACE DB_ENV HOSTNAME
envsubst < stripe_to_invoice/deployment/deployment.yaml | kubectl apply -f - envsubst < stripe_to_invoice/deployment/deployment.yaml | kubectl apply -f -

View file

@ -24,6 +24,7 @@ mkdir -p "$BACKUP_DIR"
# NEVER touch raw Postgres data # NEVER touch raw Postgres data
TAR_EXCLUDES=( TAR_EXCLUDES=(
"$K8S_STORAGE_ROOT/postgres" "$K8S_STORAGE_ROOT/postgres"
"$K8S_STORAGE_ROOT/lost+found"
) )
# ================================================== # ==================================================
@ -31,7 +32,7 @@ TAR_EXCLUDES=(
# ================================================== # ==================================================
case "$ENVIRONMENT" in case "$ENVIRONMENT" in
dev) dev)
PG_SECRET_NAME="postgres-secret" PG_SECRET_NAME="postgres-dev"
PG_POD_SELECTOR="app=postgres-dev" PG_POD_SELECTOR="app=postgres-dev"
S3_PREFIX="dev" S3_PREFIX="dev"
NAMESPACE="dev" NAMESPACE="dev"
@ -43,7 +44,7 @@ case "$ENVIRONMENT" in
exit 1 exit 1
fi fi
PG_SECRET_NAME="postgres-prod-secret" PG_SECRET_NAME="postgres-prod"
PG_POD_SELECTOR="app=postgres-prod" PG_POD_SELECTOR="app=postgres-prod"
S3_PREFIX="prod" S3_PREFIX="prod"
NAMESPACE="default" NAMESPACE="default"
@ -56,14 +57,15 @@ esac
echo "=== Backup started ($(date -u)) ===" echo "=== Backup started ($(date -u)) ==="
echo "Environment: $ENVIRONMENT" echo "Environment: $ENVIRONMENT"
echo "Namespace: $NAMESPACE"
# ================================================== # ==================================================
# POSTGRES DUMP (SAFE) # LOCATE POSTGRES POD
# ================================================== # ==================================================
POSTGRES_POD=$(kubectl get pods \ POSTGRES_POD=$(kubectl get pods \
-n "$NAMESPACE" \ -n "$NAMESPACE" \
-l "$PG_POD_SELECTOR" \ -l "$PG_POD_SELECTOR" \
-o jsonpath='{.items[*].metadata.name}' | awk '{print $1}') -o jsonpath='{.items[0].metadata.name}')
if [[ -z "$POSTGRES_POD" ]]; then if [[ -z "$POSTGRES_POD" ]]; then
echo "❌ No Postgres pod found for selector: $PG_POD_SELECTOR" echo "❌ No Postgres pod found for selector: $PG_POD_SELECTOR"
@ -71,27 +73,37 @@ if [[ -z "$POSTGRES_POD" ]]; then
exit 1 exit 1
fi fi
POSTGRES_USER=$(kubectl get secret "$PG_SECRET_NAME" \ echo "Using Postgres pod: $POSTGRES_POD"
-n "$NAMESPACE" \
-o jsonpath='{.data.POSTGRES_USER}' | base64 -d)
POSTGRES_DB=$(kubectl get secret "$PG_SECRET_NAME" \ # ==================================================
# READ DATABASE_URL FROM SECRET
# ==================================================
DATABASE_URL=$(kubectl get secret "$PG_SECRET_NAME" \
-n "$NAMESPACE" \ -n "$NAMESPACE" \
-o jsonpath='{.data.POSTGRES_DB}' 2>/dev/null | base64 -d || true) -o jsonpath='{.data.DATABASE_URL}' | base64 -d)
if [[ -z "$POSTGRES_DB" ]]; then if [[ -z "$DATABASE_URL" ]]; then
echo "POSTGRES_DB missing in secret $PG_SECRET_NAME" echo "DATABASE_URL missing in secret $PG_SECRET_NAME"
exit 1 exit 1
fi fi
echo "Dumping database: $POSTGRES_DB" # Parse DATABASE_URL
POSTGRES_USER="$(echo "$DATABASE_URL" | sed -E 's|.*://([^:]+):.*|\1|')"
POSTGRES_DB="$(echo "$DATABASE_URL" | sed -E 's|.*/([^?]+).*|\1|')"
if [[ -z "$POSTGRES_USER" || -z "$POSTGRES_DB" ]]; then
echo "❌ Failed to parse DATABASE_URL"
exit 1
fi
echo "Dumping database: $POSTGRES_DB (user: $POSTGRES_USER)"
# ==================================================
# POSTGRES LOGICAL DUMP (SAFE)
# ==================================================
kubectl exec -n "$NAMESPACE" "$POSTGRES_POD" -- \ kubectl exec -n "$NAMESPACE" "$POSTGRES_POD" -- \
pg_dump \ pg_dump "$POSTGRES_DB" \
-h localhost \ > "$BACKUP_DIR/postgres.sql"
-U "$POSTGRES_USER" \
"$POSTGRES_DB" \
> "$BACKUP_DIR/postgres.sql"
echo "✔ pg_dump complete ($(du -h "$BACKUP_DIR/postgres.sql" | cut -f1))" echo "✔ pg_dump complete ($(du -h "$BACKUP_DIR/postgres.sql" | cut -f1))"
@ -145,6 +157,6 @@ echo " sudo tar -xzf k8s_storage_$DATE.tar.gz -C /"
echo "" echo ""
echo "Restore Postgres:" echo "Restore Postgres:"
echo " kubectl exec -n $NAMESPACE -i $POSTGRES_POD -- \\" echo " kubectl exec -n $NAMESPACE -i $POSTGRES_POD -- \\"
echo " psql -U $POSTGRES_USER $POSTGRES_DB < postgres.sql" echo " psql $POSTGRES_DB < postgres.sql"
echo "" echo ""
echo "=== Backup completed successfully ===" echo "=== Backup completed successfully ==="