Initial commit: k8s cluster manifests and scripts
This commit is contained in:
35
README.md
Normal file
35
README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# K8s 叢集維護記錄
|
||||
|
||||
## 連線資訊
|
||||
- **主機**: 192.168.42.120
|
||||
- **帳號**: ubuntu (SSH Key 登入)
|
||||
- **sudo**: `sudo -i` 可直接成為 root
|
||||
|
||||
## K3s 目錄
|
||||
- **遠端**: `/root/k8s`
|
||||
- **本地**: `/Users/timmy/42_120/k8s`
|
||||
|
||||
## 已部署服務
|
||||
|
||||
### CodiMD (筆記應用)
|
||||
- manifests: `apps/codimd/manifests/`
|
||||
- 備份: `apps/codimd/backup/`
|
||||
- 腳本: deploy.sh, stop.sh, status.sh, backup.sh, restore.sh
|
||||
|
||||
### Opengist (Git Snippets)
|
||||
- manifests: `apps/opengist/manifests/`
|
||||
- 備份: `apps/opengist/backup/`
|
||||
- 腳本: deploy.sh, stop.sh, status.sh, backup.sh, restore.sh
|
||||
|
||||
### PostgreSQL (資料庫)
|
||||
- manifests: `apps/postgres/manifests/`
|
||||
- 備份: `apps/postgres/backups/`
|
||||
- 腳本: deploy.sh, stop.sh, status.sh, backup.sh
|
||||
|
||||
## 叢集工具
|
||||
- `cluster/check_access.sh` - 檢查存取權限
|
||||
- `cluster/check_nodes.sh` - 檢查節點狀態
|
||||
- `cluster/check_storage.sh` - 檢查儲存空間
|
||||
|
||||
---
|
||||
⚠️ 維護時請同步更新本地與遠端檔案
|
||||
35
apps/codimd/backup.sh
Executable file
35
apps/codimd/backup.sh
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
set -o pipefail
|
||||
|
||||
APP_NAME="codimd"
|
||||
BACKUP_DIR="./backup"
|
||||
TIMESTAMP=$(date +%Y%m%d-%H%M)
|
||||
DB_BACKUP_FILE="${BACKUP_DIR}/${APP_NAME}-db-${TIMESTAMP}.sql.gz"
|
||||
UPLOADS_BACKUP_FILE="${BACKUP_DIR}/${APP_NAME}-uploads-${TIMESTAMP}.tar.gz"
|
||||
|
||||
# 1. 取得 Pod 名稱
|
||||
DB_POD=$(kubectl get pod -l app=codimd-db -o jsonpath="{.items[0].metadata.name}" 2>/dev/null)
|
||||
APP_POD=$(kubectl get pod -l app=codimd -o jsonpath="{.items[0].metadata.name}" 2>/dev/null)
|
||||
|
||||
mkdir -p $BACKUP_DIR
|
||||
|
||||
# --- A. 備份資料庫 ---
|
||||
echo "--- 正在備份資料庫... ---"
|
||||
if kubectl exec $DB_POD -- pg_dump -U codimd codimd | gzip > $DB_BACKUP_FILE; then
|
||||
echo "✅ 資料庫備份成功: $(basename $DB_BACKUP_FILE) ($(stat -c%s $DB_BACKUP_FILE) bytes)"
|
||||
else
|
||||
echo "❌ 資料庫備份失敗!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- B. 備份上傳檔案 (Uploads) ---
|
||||
echo "--- 正在備份上傳圖片 (Uploads)... ---"
|
||||
# 使用 tar 打包 Pod 內的 uploads 資料夾,並傳回本機
|
||||
if kubectl exec $APP_POD -- tar zcf - -C /home/hackmd/app/public uploads > $UPLOADS_BACKUP_FILE; then
|
||||
echo "✅ 圖片備份成功: $(basename $UPLOADS_BACKUP_FILE) ($(stat -c%s $UPLOADS_BACKUP_FILE) bytes)"
|
||||
else
|
||||
echo "❌ 圖片備份失敗!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "--- ✨ 備份任務完成! ---"
|
||||
BIN
apps/codimd/backup/codimd-db-20260108-0543.sql.gz
Normal file
BIN
apps/codimd/backup/codimd-db-20260108-0543.sql.gz
Normal file
Binary file not shown.
BIN
apps/codimd/backup/codimd-uploads-20260108-0543.tar.gz
Normal file
BIN
apps/codimd/backup/codimd-uploads-20260108-0543.tar.gz
Normal file
Binary file not shown.
2
apps/codimd/deploy.sh
Executable file
2
apps/codimd/deploy.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
kubectl apply -f manifests/
|
||||
14
apps/codimd/manifests/00-codimd-middleware.yaml
Normal file
14
apps/codimd/manifests/00-codimd-middleware.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: codimd-cors
|
||||
namespace: default
|
||||
spec:
|
||||
headers:
|
||||
accessControlAllowMethods: ["GET", "OPTIONS", "PUT", "POST"]
|
||||
accessControlAllowOriginList: ["*"]
|
||||
accessControlAllowHeaders: ["*"]
|
||||
accessControlMaxAge: 100
|
||||
addVaryHeader: true
|
||||
customResponseHeaders:
|
||||
Cache-Control: "no-cache, no-store, must-revalidate"
|
||||
8
apps/codimd/manifests/01-codimd-secrets.yaml
Normal file
8
apps/codimd/manifests/01-codimd-secrets.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: codimd-secrets
|
||||
type: Opaque
|
||||
stringData:
|
||||
POSTGRES_PASSWORD: "8xG94tL0uKlGiBEd"
|
||||
CMD_DB_URL: "postgres://codimd:8xG94tL0uKlGiBEd@codimd-db/codimd"
|
||||
21
apps/codimd/manifests/02-codimd-pvc.yaml
Normal file
21
apps/codimd/manifests/02-codimd-pvc.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: codimd-db-pvc
|
||||
spec:
|
||||
storageClassName: longhorn
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: codimd-upload-pvc
|
||||
spec:
|
||||
storageClassName: longhorn
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
38
apps/codimd/manifests/03-codimd-db-deployment.yaml
Normal file
38
apps/codimd/manifests/03-codimd-db-deployment.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: codimd-db
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate # 確保 RWO 磁碟順利切換節點
|
||||
selector:
|
||||
matchLabels:
|
||||
app: codimd-db
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: codimd-db
|
||||
spec:
|
||||
containers:
|
||||
- name: postgres
|
||||
image: postgres:17-alpine
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
value: "codimd"
|
||||
- name: POSTGRES_DB
|
||||
value: "codimd"
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: codimd-secrets
|
||||
key: POSTGRES_PASSWORD
|
||||
- name: PGDATA
|
||||
value: "/var/lib/postgresql/data/pgdata"
|
||||
volumeMounts:
|
||||
- mountPath: "/var/lib/postgresql/data"
|
||||
name: db-data
|
||||
volumes:
|
||||
- name: db-data
|
||||
persistentVolumeClaim:
|
||||
claimName: codimd-db-pvc
|
||||
13
apps/codimd/manifests/04-codimd-db-svc.yaml
Normal file
13
apps/codimd/manifests/04-codimd-db-svc.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: codimd-db
|
||||
labels:
|
||||
app: codimd-db
|
||||
spec:
|
||||
selector:
|
||||
app: codimd-db
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5432
|
||||
targetPort: 5432
|
||||
43
apps/codimd/manifests/05-codimd-app-deployment.yaml
Normal file
43
apps/codimd/manifests/05-codimd-app-deployment.yaml
Normal file
@@ -0,0 +1,43 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: codimd-app
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: codimd
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: codimd
|
||||
spec:
|
||||
securityContext: # <-- 這裡開始
|
||||
runAsUser: 0
|
||||
fsGroup: 0 # <-- 結束
|
||||
containers:
|
||||
- name: codimd
|
||||
image: hackmdio/hackmd:latest
|
||||
env:
|
||||
- name: CMD_DB_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: codimd-secrets
|
||||
key: CMD_DB_URL
|
||||
- name: CMD_USECDN
|
||||
value: "false"
|
||||
- name: CMD_DOMAIN
|
||||
value: "codimd.lotimmy.com"
|
||||
- name: CMD_PROTOCOL_USESSL
|
||||
value: "true"
|
||||
- name: CMD_CSP_ENABLE
|
||||
value: "false"
|
||||
volumeMounts:
|
||||
- mountPath: "/home/hackmd/app/public/uploads"
|
||||
name: upload-data
|
||||
volumes:
|
||||
- name: upload-data
|
||||
persistentVolumeClaim:
|
||||
claimName: codimd-upload-pvc
|
||||
14
apps/codimd/manifests/06-codimd-app-svc.yaml
Normal file
14
apps/codimd/manifests/06-codimd-app-svc.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: codimd-service
|
||||
labels:
|
||||
app: codimd
|
||||
spec:
|
||||
selector:
|
||||
app: codimd
|
||||
ports:
|
||||
- protocol: TCP
|
||||
name: http
|
||||
port: 3000
|
||||
targetPort: 3000
|
||||
28
apps/codimd/manifests/07-codimd-ingress.yaml
Normal file
28
apps/codimd/manifests/07-codimd-ingress.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: codimd-ingress
|
||||
labels:
|
||||
app: codimd
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web, websecure
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-codimd-cors@kubernetescrd
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
- codimd.192.168.42.120.nip.io
|
||||
secretName: nip-io-cert
|
||||
rules:
|
||||
- host: codimd.lotimmy.com
|
||||
http: &http_rules
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: codimd-service
|
||||
port:
|
||||
number: 3000
|
||||
- host: codimd.192.168.42.120.nip.io
|
||||
http: *http_rules
|
||||
67
apps/codimd/restore.sh
Executable file
67
apps/codimd/restore.sh
Executable file
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
# ==========================================
|
||||
# CodiMD 一鍵完整還原腳本 (DB + Uploads)
|
||||
# ==========================================
|
||||
|
||||
# 1. 自動偵測最新的備份檔案
|
||||
LATEST_DB=$(ls -t ./backup/codimd-db-*.sql.gz 2>/dev/null | head -1)
|
||||
LATEST_IMG=$(ls -t ./backup/codimd-uploads-*.tar.gz 2>/dev/null | head -1)
|
||||
|
||||
if [ -z "$LATEST_DB" ] || [ -z "$LATEST_IMG" ]; then
|
||||
echo "❌ 錯誤: 在 ./backup 找不到完整的備份檔 (需要 .sql.gz 和 .tar.gz)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "--- 📋 準備還原以下備份 ---"
|
||||
echo "📅 資料庫: $LATEST_DB"
|
||||
echo "🖼️ 圖片檔: $LATEST_IMG"
|
||||
echo "------------------------------------------"
|
||||
read -p "⚠️ 警告:這將會覆蓋目前容器內的所有資料!確定要執行嗎?(y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "操作已取消。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. 取得 Pod 名稱
|
||||
DB_POD=$(kubectl get pod -l app=codimd-db -o jsonpath="{.items[0].metadata.name}" 2>/dev/null)
|
||||
APP_POD=$(kubectl get pod -l app=codimd -o jsonpath="{.items[0].metadata.name}" 2>/dev/null)
|
||||
|
||||
if [ -z "$DB_POD" ] || [ -z "$APP_POD" ]; then
|
||||
echo "❌ 錯誤: 找不到運作中的 CodiMD Pod,請確認 K8s 資源狀態。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- A. 還原資料庫 (含清空邏輯) ---
|
||||
echo "--- 🗄️ 正在還原資料庫結構與內容... ---"
|
||||
# 先清空 Schema 以避免重複匯入導致的鍵值衝突
|
||||
kubectl exec -i $DB_POD -- psql -U codimd -d codimd -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;" > /dev/null
|
||||
|
||||
zcat "$LATEST_DB" | kubectl exec -i $DB_POD -- psql -U codimd -d codimd > /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ 資料庫還原成功。"
|
||||
else
|
||||
echo "❌ 資料庫還原發生錯誤。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- B. 還原圖片檔案 ---
|
||||
echo "--- 🖼️ 正在同步圖片檔案 (Uploads)... ---"
|
||||
# 清空目前目錄避免新舊檔案混雜
|
||||
kubectl exec $APP_POD -- sh -c "rm -rf /home/hackmd/app/public/uploads/*"
|
||||
|
||||
# 解壓縮並傳入 Pod
|
||||
cat "$LATEST_IMG" | kubectl exec -i $APP_POD -- tar zxf - -C /home/hackmd/app/public/
|
||||
|
||||
# 修正權限確保 CodiMD 服務 (UID 1500) 擁有存取權
|
||||
kubectl exec $APP_POD -- chown -R 1500:1500 /home/hackmd/app/public/uploads
|
||||
echo "✅ 圖片還原與權限修正完成。"
|
||||
|
||||
# 3. 重啟服務以套用變更
|
||||
echo "--- 🔄 正在重啟應用程式容器... ---"
|
||||
kubectl rollout restart deployment codimd-app
|
||||
kubectl rollout status deployment codimd-app
|
||||
|
||||
echo "------------------------------------------"
|
||||
echo "✨ 還原程序已執行完畢。"
|
||||
echo "目前資料已同步至備份檔日期:$(echo $LATEST_DB | grep -oE '[0-9]{8}-[0-9]{4}')"
|
||||
44
apps/codimd/status.sh
Executable file
44
apps/codimd/status.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
APP_NAME="codimd"
|
||||
|
||||
echo "=== [ $APP_NAME 資源狀態 ] ==="
|
||||
|
||||
# 1. 檢查 Pods
|
||||
echo "--- Pods ---"
|
||||
kubectl get pods -l "app in (codimd, codimd-db)" -o wide
|
||||
|
||||
echo ""
|
||||
|
||||
# 2. 檢查 Services
|
||||
echo "--- Services ---"
|
||||
kubectl get svc -l "app in (codimd, codimd-db)"
|
||||
|
||||
echo ""
|
||||
|
||||
# 3. 檢查 PVC (儲存)
|
||||
echo "--- Storage (PVC) ---"
|
||||
kubectl get pvc | grep codimd
|
||||
|
||||
echo ""
|
||||
|
||||
# 4. 檢查存取入口 (Ingress)
|
||||
echo "--- Access Points (Ingress) ---"
|
||||
kubectl get ingress -l "app=codimd" 2>/dev/null || kubectl get ingress | grep codimd
|
||||
|
||||
echo ""
|
||||
|
||||
# 5. 檢查資料庫連通性 (如果 Pod 在 Running)
|
||||
DB_POD=$(kubectl get pod -l app=codimd-db -o jsonpath="{.items[0].metadata.name}" 2>/dev/null)
|
||||
POD_STATUS=$(kubectl get pod $DB_POD -o jsonpath="{.status.phase}" 2>/dev/null)
|
||||
|
||||
if [ "$POD_STATUS" == "Running" ]; then
|
||||
echo "--- Database Connection Check ---"
|
||||
if kubectl exec $DB_POD -- psql -U codimd -d codimd -c "SELECT 1;" > /dev/null 2>&1; then
|
||||
echo "✅ Database is Ready and Accessible."
|
||||
else
|
||||
echo "❌ Database is Running but Connection Failed."
|
||||
fi
|
||||
else
|
||||
echo "⏳ Waiting for Database Pod to be Ready..."
|
||||
fi
|
||||
12
apps/codimd/stop.sh
Executable file
12
apps/codimd/stop.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
echo "--- 安全停止 CodiMD (保留資料) ---"
|
||||
|
||||
# 只刪除 Deployment, Service, Ingress, Middleware
|
||||
# 故意跳過 02-codimd-pvc.yaml 和 01-codimd-secrets.yaml
|
||||
kubectl delete -f manifests/03-codimd-db-deployment.yaml --ignore-not-found
|
||||
kubectl delete -f manifests/05-codimd-app-deployment.yaml --ignore-not-found
|
||||
kubectl delete -f manifests/04-codimd-db-svc.yaml --ignore-not-found
|
||||
kubectl delete -f manifests/06-codimd-app-svc.yaml --ignore-not-found
|
||||
kubectl delete -f manifests/07-codimd-ingress.yaml --ignore-not-found
|
||||
|
||||
echo "✅ 服務已停止,Longhorn 磁碟資料安全保留。"
|
||||
55
apps/codimd/tmpdir/codimd/Makefile
Normal file
55
apps/codimd/tmpdir/codimd/Makefile
Normal file
@@ -0,0 +1,55 @@
|
||||
# Makefile: 適用於 docker-compose 專案,自動取用當前資料夾為專案名
|
||||
|
||||
COMPOSE := docker-compose
|
||||
COMPOSE_FILE := docker-compose.yml
|
||||
PROJECT_NAME := $(notdir $(CURDIR)) # 依照當前資料夾自動命名
|
||||
|
||||
# 可選自訂 .env 變數(預設 docker-compose 自動載入)
|
||||
# ENV_FILE := .env
|
||||
|
||||
.PHONY: help up down build restart logs ps exec clean prune
|
||||
|
||||
help:
|
||||
@echo "🔧 Docker Compose Makefile 指令:"
|
||||
@echo " make up - 啟動所有服務 (背景執行)"
|
||||
@echo " make up-fg - 前景執行(方便除錯)"
|
||||
@echo " make down - 停止並移除服務容器"
|
||||
@echo " make build - 重新建構服務映像檔"
|
||||
@echo " make restart - 重啟所有服務"
|
||||
@echo " make logs - 查看服務日誌"
|
||||
@echo " make ps - 顯示服務狀態"
|
||||
@echo " make exec - 進入主要容器 shell"
|
||||
@echo " make clean - 清理所有服務資料(含 volume)"
|
||||
@echo " make prune - 清除未使用的資源"
|
||||
|
||||
up:
|
||||
$(COMPOSE) -p $(PROJECT_NAME) -f $(COMPOSE_FILE) up -d
|
||||
|
||||
up-fg:
|
||||
$(COMPOSE) -p $(PROJECT_NAME) -f $(COMPOSE_FILE) up
|
||||
|
||||
down:
|
||||
$(COMPOSE) -p $(PROJECT_NAME) -f $(COMPOSE_FILE) down
|
||||
|
||||
build:
|
||||
$(COMPOSE) -p $(PROJECT_NAME) -f $(COMPOSE_FILE) build
|
||||
|
||||
restart:
|
||||
$(COMPOSE) -p $(PROJECT_NAME) -f $(COMPOSE_FILE) down
|
||||
$(COMPOSE) -p $(PROJECT_NAME) -f $(COMPOSE_FILE) up -d
|
||||
|
||||
logs:
|
||||
$(COMPOSE) -p $(PROJECT_NAME) -f $(COMPOSE_FILE) logs -f --tail=100
|
||||
|
||||
ps:
|
||||
$(COMPOSE) -p $(PROJECT_NAME) -f $(COMPOSE_FILE) ps
|
||||
|
||||
exec:
|
||||
$(COMPOSE) -p $(PROJECT_NAME) -f $(COMPOSE_FILE) exec app sh
|
||||
|
||||
clean:
|
||||
$(COMPOSE) -p $(PROJECT_NAME) -f $(COMPOSE_FILE) down -v --remove-orphans
|
||||
|
||||
prune:
|
||||
docker system prune -af --volumes
|
||||
|
||||
0
apps/codimd/tmpdir/codimd/backup_uploads/.gitkeep
Normal file
0
apps/codimd/tmpdir/codimd/backup_uploads/.gitkeep
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 555 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 247 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 252 KiB |
BIN
apps/codimd/tmpdir/codimd/codimd_backup.dump
Normal file
BIN
apps/codimd/tmpdir/codimd/codimd_backup.dump
Normal file
Binary file not shown.
42
apps/codimd/tmpdir/codimd/docker-compose.yml
Normal file
42
apps/codimd/tmpdir/codimd/docker-compose.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
services:
|
||||
database:
|
||||
image: postgres:17-alpine
|
||||
container_name: codimd_database
|
||||
environment:
|
||||
- POSTGRES_USER=codimd
|
||||
- POSTGRES_PASSWORD=C0d1MdP@ss2025
|
||||
- POSTGRES_DB=codimd
|
||||
volumes:
|
||||
- "database-data:/var/lib/postgresql/data"
|
||||
restart: always
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
networks:
|
||||
- codimd_net
|
||||
|
||||
codimd:
|
||||
image: hackmdio/hackmd:latest
|
||||
container_name: codimd_app
|
||||
environment:
|
||||
- CMD_DB_URL=postgres://codimd:C0d1MdP@ss2025@database/codimd
|
||||
- CMD_USECDN=false
|
||||
depends_on:
|
||||
- database
|
||||
ports:
|
||||
- "3001:3000"
|
||||
volumes:
|
||||
- upload-data:/home/hackmd/app/public/uploads
|
||||
restart: always
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
networks:
|
||||
- codimd_net
|
||||
|
||||
volumes:
|
||||
database-data: {}
|
||||
upload-data: {}
|
||||
|
||||
networks:
|
||||
codimd_net:
|
||||
driver: bridge
|
||||
|
||||
BIN
apps/codimd/tmpdir/codimd_20251229150418.tar.gz
Normal file
BIN
apps/codimd/tmpdir/codimd_20251229150418.tar.gz
Normal file
Binary file not shown.
23
apps/opengist/backup.sh
Executable file
23
apps/opengist/backup.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
BACKUP_DIR="./backup"
|
||||
TIMESTAMP=$(date +"%Y%m%d-%H%M")
|
||||
BACKUP_FILE="${BACKUP_DIR}/opengist-${TIMESTAMP}.tar.gz"
|
||||
|
||||
mkdir -p "${BACKUP_DIR}"
|
||||
|
||||
echo "📦 Creating backup: ${BACKUP_FILE}"
|
||||
|
||||
POD=$(kubectl get pod -l app=opengist -o jsonpath='{.items[0].metadata.name}')
|
||||
|
||||
if [ -z "${POD}" ]; then
|
||||
echo "❌ OpenGist pod not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
kubectl exec "${POD}" -- \
|
||||
tar czf - /opengist > "${BACKUP_FILE}"
|
||||
|
||||
echo "✅ Backup completed: ${BACKUP_FILE}"
|
||||
|
||||
BIN
apps/opengist/backup/opengist-20251231-0709.tar.gz
Normal file
BIN
apps/opengist/backup/opengist-20251231-0709.tar.gz
Normal file
Binary file not shown.
18
apps/opengist/deploy.sh
Executable file
18
apps/opengist/deploy.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
MANIFESTS_DIR="./manifests"
|
||||
|
||||
echo "🚀 Deploying OpenGist from ${MANIFESTS_DIR} ..."
|
||||
|
||||
kubectl apply -f "${MANIFESTS_DIR}/opengist-pvc.yaml"
|
||||
kubectl apply -f "${MANIFESTS_DIR}/opengist-deployment.yaml"
|
||||
kubectl apply -f "${MANIFESTS_DIR}/opengist-svc-web.yaml"
|
||||
kubectl apply -f "${MANIFESTS_DIR}/opengist-svc-ssh.yaml"
|
||||
kubectl apply -f "${MANIFESTS_DIR}/opengist-ingress.yaml"
|
||||
|
||||
echo "⏳ Waiting for deployment to be ready..."
|
||||
kubectl rollout status deployment/opengist
|
||||
|
||||
echo "✅ OpenGist deployed successfully"
|
||||
|
||||
38
apps/opengist/manifests/opengist-deployment.yaml
Normal file
38
apps/opengist/manifests/opengist-deployment.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: opengist
|
||||
namespace: default
|
||||
labels:
|
||||
app: opengist
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: opengist
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: opengist
|
||||
spec:
|
||||
containers:
|
||||
- name: opengist
|
||||
image: ghcr.io/thomiceli/opengist:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 6157
|
||||
- name: ssh
|
||||
containerPort: 2222
|
||||
resources:
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
volumeMounts:
|
||||
- name: data-vol
|
||||
mountPath: /opengist
|
||||
volumes:
|
||||
- name: data-vol
|
||||
persistentVolumeClaim:
|
||||
claimName: opengist-pvc
|
||||
|
||||
39
apps/opengist/manifests/opengist-ingress.yaml
Normal file
39
apps/opengist/manifests/opengist-ingress.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: opengist-ingress
|
||||
namespace: default
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web,websecure
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
|
||||
tls:
|
||||
- hosts:
|
||||
- opengist.lotimmy.com
|
||||
- opengist.192.168.42.120.nip.io
|
||||
secretName: nip-io-cert
|
||||
|
||||
rules:
|
||||
- host: opengist.lotimmy.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: opengist-web
|
||||
port:
|
||||
number: 6157
|
||||
|
||||
- host: opengist.192.168.42.120.nip.io
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: opengist-web
|
||||
port:
|
||||
number: 6157
|
||||
|
||||
13
apps/opengist/manifests/opengist-pvc.yaml
Normal file
13
apps/opengist/manifests/opengist-pvc.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: opengist-pvc
|
||||
namespace: default
|
||||
spec:
|
||||
storageClassName: longhorn
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
|
||||
16
apps/opengist/manifests/opengist-svc-ssh.yaml
Normal file
16
apps/opengist/manifests/opengist-svc-ssh.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: opengist-ssh
|
||||
namespace: default
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: opengist
|
||||
ports:
|
||||
- name: ssh
|
||||
port: 2222
|
||||
targetPort: 2222
|
||||
protocol: TCP
|
||||
nodePort: 32222
|
||||
|
||||
15
apps/opengist/manifests/opengist-svc-web.yaml
Normal file
15
apps/opengist/manifests/opengist-svc-web.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: opengist-web
|
||||
namespace: default
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: opengist
|
||||
ports:
|
||||
- name: http
|
||||
port: 6157
|
||||
targetPort: 6157
|
||||
protocol: TCP
|
||||
|
||||
81
apps/opengist/restore.sh
Executable file
81
apps/opengist/restore.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
BACKUP_DIR="./backup"
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "❌ Usage: ./restore.sh <backup-file>"
|
||||
echo
|
||||
echo "Example:"
|
||||
echo " ./restore.sh opengist-20250101-1200.tar.gz"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BACKUP_FILE="${BACKUP_DIR}/$1"
|
||||
|
||||
if [ ! -f "${BACKUP_FILE}" ]; then
|
||||
echo "❌ Backup file not found: ${BACKUP_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "⚠️ WARNING"
|
||||
echo "This will OVERWRITE data in /opengist inside the PVC."
|
||||
echo "Backup file: ${BACKUP_FILE}"
|
||||
echo
|
||||
read -p "Type YES to continue: " CONFIRM
|
||||
|
||||
if [ "${CONFIRM}" != "YES" ]; then
|
||||
echo "❌ Restore aborted"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🛑 Scaling down OpenGist deployment..."
|
||||
kubectl scale deployment opengist --replicas=0
|
||||
|
||||
echo "⏳ Waiting for pod termination..."
|
||||
kubectl wait --for=delete pod -l app=opengist --timeout=60s || true
|
||||
|
||||
echo "📦 Starting restore process..."
|
||||
|
||||
# 使用暫時 helper pod 掛載同一顆 PVC
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: opengist-restore
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: restore
|
||||
image: alpine:3.20
|
||||
command: ["sleep", "3600"]
|
||||
volumeMounts:
|
||||
- name: data-vol
|
||||
mountPath: /opengist
|
||||
volumes:
|
||||
- name: data-vol
|
||||
persistentVolumeClaim:
|
||||
claimName: opengist-pvc
|
||||
EOF
|
||||
|
||||
echo "⏳ Waiting for restore pod to be ready..."
|
||||
kubectl wait --for=condition=Ready pod/opengist-restore --timeout=60s
|
||||
|
||||
echo "🧹 Cleaning existing data..."
|
||||
kubectl exec opengist-restore -- rm -rf /opengist/*
|
||||
|
||||
echo "📂 Restoring backup..."
|
||||
kubectl exec -i opengist-restore -- \
|
||||
tar xzf - -C / < "${BACKUP_FILE}"
|
||||
|
||||
echo "🧹 Cleaning up restore pod..."
|
||||
kubectl delete pod opengist-restore
|
||||
|
||||
echo "🚀 Scaling OpenGist back up..."
|
||||
kubectl scale deployment opengist --replicas=1
|
||||
|
||||
echo "⏳ Waiting for OpenGist to be ready..."
|
||||
kubectl rollout status deployment/opengist
|
||||
|
||||
echo "✅ Restore completed successfully"
|
||||
|
||||
27
apps/opengist/status.sh
Executable file
27
apps/opengist/status.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo
|
||||
echo "=== Pod ==="
|
||||
kubectl get pod -l app=opengist -o wide
|
||||
|
||||
echo
|
||||
echo "=== Deployment ==="
|
||||
kubectl get deployment opengist
|
||||
|
||||
echo
|
||||
echo "=== Services ==="
|
||||
kubectl get svc opengist-web opengist-ssh
|
||||
|
||||
echo
|
||||
echo "=== Ingress ==="
|
||||
kubectl get ingress opengist-ingress
|
||||
|
||||
echo
|
||||
echo "=== PVC ==="
|
||||
kubectl get pvc opengist-pvc
|
||||
|
||||
echo
|
||||
echo "=== NodePort SSH ==="
|
||||
kubectl get svc opengist-ssh -o jsonpath='{.spec.ports[0].nodePort}'
|
||||
echo
|
||||
|
||||
12
apps/opengist/stop.sh
Executable file
12
apps/opengist/stop.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
echo "🛑 Stopping OpenGist (PVC will be kept)..."
|
||||
|
||||
kubectl scale deployment opengist --replicas=0
|
||||
|
||||
echo "⏳ Waiting for pod termination..."
|
||||
kubectl wait --for=delete pod -l app=opengist --timeout=60s || true
|
||||
|
||||
echo "✅ OpenGist stopped (data preserved)"
|
||||
|
||||
20
apps/postgres/backup.sh
Executable file
20
apps/postgres/backup.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
# 用途:備份 Postgres 到本機(使用 pg_dumpall)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BACKUP_DIR="$(cd "$(dirname "$0")" && pwd)/backups"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
POD=$(kubectl get pod -l app=postgres -o jsonpath='{.items[0].metadata.name}')
|
||||
TS=$(date +%Y%m%d_%H%M%S)
|
||||
OUT="$BACKUP_DIR/postgres-backup-$TS.sql"
|
||||
|
||||
echo "使用 Pod:$POD"
|
||||
echo "開始備份..."
|
||||
|
||||
kubectl exec "$POD" -- sh -c 'pg_dumpall -U "$POSTGRES_USER"' > "$OUT"
|
||||
|
||||
echo "備份完成:"
|
||||
echo "$OUT"
|
||||
|
||||
157
apps/postgres/backups/postgres-backup-20251231_055625.sql
Normal file
157
apps/postgres/backups/postgres-backup-20251231_055625.sql
Normal file
@@ -0,0 +1,157 @@
|
||||
--
|
||||
-- PostgreSQL database cluster dump
|
||||
--
|
||||
|
||||
\restrict pSRBFv7OawZlldF9mhkwy64jnMZdGanavWk5r24Tn672qcvRL3qpsGkiOe7oPNn
|
||||
|
||||
SET default_transaction_read_only = off;
|
||||
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
|
||||
--
|
||||
-- Roles
|
||||
--
|
||||
|
||||
CREATE ROLE myuser;
|
||||
ALTER ROLE myuser WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN REPLICATION BYPASSRLS PASSWORD 'SCRAM-SHA-256$4096:lcasyFLTxsODZ2pTQzSfaQ==$Q9F2EsOXVyUo6hdnr4vPuErS4wNLYQ4aiszK/l4uar0=:ZOUai23wJ0E2uPl9ZVldTF4Kf6IduAMK+smPEs8LcjE=';
|
||||
|
||||
--
|
||||
-- User Configurations
|
||||
--
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
\unrestrict pSRBFv7OawZlldF9mhkwy64jnMZdGanavWk5r24Tn672qcvRL3qpsGkiOe7oPNn
|
||||
|
||||
--
|
||||
-- Databases
|
||||
--
|
||||
|
||||
--
|
||||
-- Database "template1" dump
|
||||
--
|
||||
|
||||
\connect template1
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
\restrict z5l8f0bphx2JQX8DXx5DcxSoJZ8WO1YgETQRPbrS67n6Z3sZTrRi3rkJm5JGwlm
|
||||
|
||||
-- Dumped from database version 16.11 (Debian 16.11-1.pgdg13+1)
|
||||
-- Dumped by pg_dump version 16.11 (Debian 16.11-1.pgdg13+1)
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
SET idle_in_transaction_session_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
SELECT pg_catalog.set_config('search_path', '', false);
|
||||
SET check_function_bodies = false;
|
||||
SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
\unrestrict z5l8f0bphx2JQX8DXx5DcxSoJZ8WO1YgETQRPbrS67n6Z3sZTrRi3rkJm5JGwlm
|
||||
|
||||
--
|
||||
-- Database "mydb" dump
|
||||
--
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
\restrict gIv4VWnnqhIHCcWxK9Gh9vrZXiMGwbVvmKNa64CtJ60YohCgypC9k0tWmdgClrB
|
||||
|
||||
-- Dumped from database version 16.11 (Debian 16.11-1.pgdg13+1)
|
||||
-- Dumped by pg_dump version 16.11 (Debian 16.11-1.pgdg13+1)
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
SET idle_in_transaction_session_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
SELECT pg_catalog.set_config('search_path', '', false);
|
||||
SET check_function_bodies = false;
|
||||
SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
--
|
||||
-- Name: mydb; Type: DATABASE; Schema: -; Owner: myuser
|
||||
--
|
||||
|
||||
CREATE DATABASE mydb WITH TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'en_US.utf8';
|
||||
|
||||
|
||||
ALTER DATABASE mydb OWNER TO myuser;
|
||||
|
||||
\unrestrict gIv4VWnnqhIHCcWxK9Gh9vrZXiMGwbVvmKNa64CtJ60YohCgypC9k0tWmdgClrB
|
||||
\connect mydb
|
||||
\restrict gIv4VWnnqhIHCcWxK9Gh9vrZXiMGwbVvmKNa64CtJ60YohCgypC9k0tWmdgClrB
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
SET idle_in_transaction_session_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
SELECT pg_catalog.set_config('search_path', '', false);
|
||||
SET check_function_bodies = false;
|
||||
SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
\unrestrict gIv4VWnnqhIHCcWxK9Gh9vrZXiMGwbVvmKNa64CtJ60YohCgypC9k0tWmdgClrB
|
||||
|
||||
--
|
||||
-- Database "postgres" dump
|
||||
--
|
||||
|
||||
\connect postgres
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
\restrict irY5kyT2aM9aY0ej35sQkGD65pdtbEJK5iVUBxrNe906zZsVHZFLUljOIsxjKic
|
||||
|
||||
-- Dumped from database version 16.11 (Debian 16.11-1.pgdg13+1)
|
||||
-- Dumped by pg_dump version 16.11 (Debian 16.11-1.pgdg13+1)
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
SET idle_in_transaction_session_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
SELECT pg_catalog.set_config('search_path', '', false);
|
||||
SET check_function_bodies = false;
|
||||
SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
\unrestrict irY5kyT2aM9aY0ej35sQkGD65pdtbEJK5iVUBxrNe906zZsVHZFLUljOIsxjKic
|
||||
|
||||
--
|
||||
-- PostgreSQL database cluster dump complete
|
||||
--
|
||||
|
||||
1121
apps/postgres/backups/postgres-backup-20251231_064007.sql
Normal file
1121
apps/postgres/backups/postgres-backup-20251231_064007.sql
Normal file
File diff suppressed because it is too large
Load Diff
1830
apps/postgres/backups/postgres-backup-20260108_072927.sql
Normal file
1830
apps/postgres/backups/postgres-backup-20260108_072927.sql
Normal file
File diff suppressed because it is too large
Load Diff
20
apps/postgres/deploy.sh
Executable file
20
apps/postgres/deploy.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
# 用途:部署或更新 Postgres(Secret / PVC / Deployment / Service)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BASE_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
MANIFESTS="$BASE_DIR/manifests"
|
||||
|
||||
echo "部署 Postgres 中..."
|
||||
|
||||
kubectl apply -f "$MANIFESTS/postgres-secret.yaml"
|
||||
kubectl apply -f "$MANIFESTS/postgres-pvc.yaml"
|
||||
kubectl apply -f "$MANIFESTS/postgres-deployment.yaml"
|
||||
kubectl apply -f "$MANIFESTS/postgres-service.yaml"
|
||||
|
||||
echo "完成,等待 Pod 就緒..."
|
||||
kubectl wait --for=condition=ready pod -l app=postgres --timeout=120s
|
||||
|
||||
echo "Postgres 已啟動"
|
||||
|
||||
65
apps/postgres/manifests/postgres-deployment.yaml
Normal file
65
apps/postgres/manifests/postgres-deployment.yaml
Normal file
@@ -0,0 +1,65 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: postgres
|
||||
labels:
|
||||
app: postgres
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: postgres
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: postgres
|
||||
spec:
|
||||
containers:
|
||||
- name: postgres
|
||||
image: postgres:16
|
||||
imagePullPolicy: IfNotPresent
|
||||
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
|
||||
# 🔑 關鍵:避免 lost+found 問題
|
||||
env:
|
||||
- name: PGDATA
|
||||
value: /var/lib/postgresql/data/pgdata
|
||||
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: postgres-secret
|
||||
|
||||
volumeMounts:
|
||||
- name: postgres-data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
|
||||
# 官方建議的健康檢查方式
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- pg_isready -d "$POSTGRES_DB" -U "$POSTGRES_USER"
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- pg_isready -d "$POSTGRES_DB" -U "$POSTGRES_USER"
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
|
||||
volumes:
|
||||
- name: postgres-data
|
||||
persistentVolumeClaim:
|
||||
claimName: postgres-pvc
|
||||
|
||||
11
apps/postgres/manifests/postgres-pvc.yaml
Normal file
11
apps/postgres/manifests/postgres-pvc.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: postgres-pvc
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
|
||||
10
apps/postgres/manifests/postgres-secret.yaml
Normal file
10
apps/postgres/manifests/postgres-secret.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: postgres-secret
|
||||
type: Opaque
|
||||
stringData:
|
||||
POSTGRES_DB: mydb
|
||||
POSTGRES_USER: myuser
|
||||
POSTGRES_PASSWORD: mypassword
|
||||
|
||||
13
apps/postgres/manifests/postgres-service.yaml
Normal file
13
apps/postgres/manifests/postgres-service.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: postgres
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: postgres
|
||||
ports:
|
||||
- port: 5432
|
||||
targetPort: 5432
|
||||
nodePort: 31532
|
||||
|
||||
26
apps/postgres/status.sh
Executable file
26
apps/postgres/status.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
# 用途:查看 Postgres 狀態與連線資訊
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo
|
||||
echo "=== Pod ==="
|
||||
kubectl get pod -l app=postgres -o wide
|
||||
|
||||
echo
|
||||
echo "=== Service ==="
|
||||
kubectl get svc postgres
|
||||
|
||||
echo
|
||||
echo "=== PVC ==="
|
||||
kubectl get pvc postgres-pvc
|
||||
|
||||
echo
|
||||
echo "=== NodePort 連線 ==="
|
||||
NODE_IPS=$(kubectl get nodes -o jsonpath='{range .items[*]}{.status.addresses[?(@.type=="InternalIP")].address}{" "}{end}')
|
||||
PORT=$(kubectl get svc postgres -o jsonpath='{.spec.ports[0].nodePort}')
|
||||
|
||||
for ip in $NODE_IPS; do
|
||||
echo "- $ip:$PORT"
|
||||
done
|
||||
|
||||
13
apps/postgres/stop.sh
Executable file
13
apps/postgres/stop.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# 用途:停止 Postgres(不刪 PVC 與 Secret)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "停止 Postgres 中..."
|
||||
|
||||
kubectl delete deployment postgres --ignore-not-found
|
||||
kubectl wait --for=delete pod -l app=postgres --timeout=60s || true
|
||||
kubectl delete service postgres --ignore-not-found
|
||||
|
||||
echo "Postgres 已停止(資料仍保留)"
|
||||
|
||||
49
cluster/check_access.sh
Executable file
49
cluster/check_access.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
# 用途:快速查看目前所有服務「怎麼連」
|
||||
# 不修改任何資源,純顯示資訊
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo
|
||||
echo "=== Pods ==="
|
||||
kubectl get pods -o wide
|
||||
|
||||
echo
|
||||
echo "=== Services ==="
|
||||
kubectl get svc
|
||||
|
||||
echo
|
||||
echo "=== Node IPs ==="
|
||||
kubectl get nodes -o wide | awk 'NR>1 {print $1 "\t" $6}'
|
||||
|
||||
echo
|
||||
echo "=== NodePort Access ==="
|
||||
|
||||
NODE_IPS=$(kubectl get nodes -o jsonpath='{range .items[*]}{.status.addresses[?(@.type=="InternalIP")].address}{" "}{end}')
|
||||
kubectl get svc --no-headers | awk '$2=="NodePort"' | while read -r name type clusterip external ports age; do
|
||||
PORTS=$(echo "$ports" | tr ',' '\n' | awk -F: '{print $NF}')
|
||||
for ip in $NODE_IPS; do
|
||||
for p in $PORTS; do
|
||||
echo "- $ip:$p ($name)"
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
echo
|
||||
echo "=== Ingress (HTTP/HTTPS) ==="
|
||||
kubectl get ingress --no-headers | while read -r name class hosts address ports age; do
|
||||
echo "- $name"
|
||||
kubectl get ingress "$name" -o jsonpath='{range .spec.rules[*]}{.host}{"\n"}{end}' | while read -r host; do
|
||||
TLS=$(kubectl get ingress "$name" -o jsonpath='{.spec.tls}')
|
||||
if [[ -n "$TLS" && "$TLS" != "[]" ]]; then
|
||||
echo " https://$host"
|
||||
else
|
||||
echo " http://$host"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
echo
|
||||
echo "=== PVC ==="
|
||||
kubectl get pvc
|
||||
|
||||
7
cluster/check_nodes.sh
Executable file
7
cluster/check_nodes.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# 用途:檢查叢集節點狀態
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
kubectl get nodes -o wide
|
||||
|
||||
14
cluster/check_storage.sh
Executable file
14
cluster/check_storage.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# 用途:檢查儲存狀態(PVC / Longhorn)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "=== PVC ==="
|
||||
kubectl get pvc
|
||||
|
||||
if kubectl get ns longhorn-system >/dev/null 2>&1; then
|
||||
echo
|
||||
echo "=== Longhorn Volumes ==="
|
||||
kubectl -n longhorn-system get volumes
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user