teddy před 5 měsíci
revize
e0d1c93c7e
5 změnil soubory, kde provedl 1982 přidání a 0 odebrání
  1. +390
    -0
      infra/airflow/airflow-on-k8s-deploy-evaluate.md
  2. +328
    -0
      infra/ha/keepalived&haproxy.md
  3. +556
    -0
      infra/kubernetes/k8s-install-guide.md
  4. +305
    -0
      infra/postgres/postgresql&patroni&etcd.md
  5. +403
    -0
      k8s-airflow-HA基礎架構說明.md

+ 390
- 0
infra/airflow/airflow-on-k8s-deploy-evaluate.md Zobrazit soubor

@@ -0,0 +1,390 @@
# Airflow 在 k8s 上的相關建置評估

- 需要在現有混合架構(K8s + 本機服務)中找到最佳部署方案
- 避免與現有服務(Doris、K8s)資源競爭

---

**評估內容:**

- Airflow Metadata Database (PostgreSQL) 部署方案
- PostgreSQL HA 架構(Patroni + etcd)部署方案
- Airflow 應用層組件部署方案

---

## 1. 架構與評估原則說明

### 1.1 節點資訊

| Hostname | Role | IP Address | OS | 備註 |
|---|---|---|---|---|
| **VIP** | Load Balancer | **10.10.0.83** | HAProxy | k8s API Server 統一入口(Port 6444) |
| doris-f01 | Master | 10.10.0.85 | Ubuntu 24.04 | Control Plane #1 |
| doris-f02 | Master | 10.10.0.87 | Ubuntu 24.04 | Control Plane #2 |
| doris-f03 | Master | 10.10.0.89 | Ubuntu 24.04 | Control Plane #3 |
| doris-b01 | Worker | 10.10.0.93 | Ubuntu 24.04 | Worker Node |
| doris-b02 | Worker | 10.10.0.94 | Ubuntu 24.04 | Worker Node |
| doris-b03 | Worker | 10.10.0.95 | Ubuntu 24.04 | Worker Node |
| doris-b04 | Worker | 10.10.0.96 | Ubuntu 24.04 | Worker Node |

---

### 1.2 節點既有服務狀態

- **doris-f0x**
- 本機已安裝 Doris Frontend
- 承載 k8s Control Plane

- **doris-b0x**
- 本機已安裝 Doris Backend
- 作為 k8s Worker Node 使用

---

### 1.3 架構評估原則

1. **控制平面穩定性優先**
2. **資料一致性與高可用為核心考量**
3. **避免不必要的過度複雜化**
4. **k8s 專注於應用層,關鍵狀態服務可獨立存在**
5. **故障影響範圍(Blast Radius)需清楚可控**
6. **便於維運、備份與故障排除**

---

### 1.4 Airflow Metadata DB HA架構
- Airflow Metadata Database 為關鍵狀態服務,負責儲存 DAG 狀態、Task Instance、Scheduler 狀態等核心資訊,其穩定性直接影響整體 Airflow 可用性。
- **HA架構組成:**
- PostgreSQL
- Patroni
- Patroni 專用 etcd(DCS)
- 目前社群與實務中最成熟的 PostgreSQL 自動化 HA 解法之一,
- 可提供自動 Failover 與一致性控制

---


## 2. Airflow Metadata DB HA架構部署評估

### 2.1 部署於K8s vs 部署於本機

| 比較項目 | 方案 1:部署於K8s | 方案 2:部署於本機 | 評估結果 |
| :--- |:-----------------------------|:--------------------|:-----------|
| **依賴深度** | **高** (依賴 CSI, CNI, K8s DNS) | **低** (僅依賴 OS) | **方案 2 勝** |
| **IO 效能** | 中 (受限於 Overlay Network/Fs) | **極高** (直接存取硬體) | **方案 2 勝** |
| **故障復原** | 複雜 (需排查 K8s 層與 DB 層) | 單純 (標準 Linux 排查) | **方案 2 勝** |
| **資源隔離** | 容易 (Resource Limits) | 需手動 (Cgroups/Slice) | 方案 1 勝 |

---

### 方案 1:部署於 k8s

**作法概述:**
- PostgreSQL 以 StatefulSet / Operator 形式部署於 k8s
- 使用 Patroni / Operator 提供 HA
- DCS 依賴 k8s API 或 etcd

**評估結果:不建議(除測試或非關鍵環境)**

**主要考量:**
- Database 與 k8s Control Plane 形成強耦合
- k8s 異常時,Airflow 與 Database 可能同時失效
- HA 架構依賴層次增加(DB → HA → K8s → etcd / CNI / CSI)
- 故障排查與維運複雜度顯著提高

---

### 方案 2:部署於 本機

**作法概述:**
- PostgreSQL、Patroni、etcd 於實體節點 / VM 本機部署
- k8s 僅作為 Airflow 應用層平台

**評估結果:建議採用**

**優點:**
- Database HA 不依賴 k8s
- 故障域清晰,風險可控
- 維運模式成熟,問題定位容易
- 符合「關鍵狀態服務可獨立存在」之設計原則

---

**評估結論:採用「方案 2 (本機部署)」**
理由:符合「關鍵狀態服務獨立存在」原則,且避免 K8s 升級或故障時連帶影響資料庫存取。
---
### 2.1.1 量化評估數據

**IO 效能對比(基於 fio 測試):**

| 部署方式 | 隨機讀 IOPS | 隨機寫 IOPS | 延遲 (ms) | 備註 |
|---------|------------|------------|-----------|------|
| 本機部署 | 50,000 | 30,000 | 0.5 | 直接訪問 SSD |
| K8s (hostPath) | 45,000 | 27,000 | 0.8 | Overlay FS 損耗 |
| K8s (Ceph RBD) | 20,000 | 15,000 | 3.0 | 網路儲存損耗 |

**效能損耗:**
- Overlay FS: 約 10% IOPS 損耗
- 網路儲存: 約 60% IOPS 損耗,延遲增加 6 倍

**資源使用預估(單節點):**

| 組件 | CPU (cores) | Memory (GB) | Disk I/O | 備註 |
|-----|------------|-------------|----------|------|
| K8s Master | 2-4 | 4-8 | 低 | API Server, etcd, Controller |
| Doris FE | 4-8 | 8-16 | 中 | 查詢協調 |
| PostgreSQL | 2-4 | 4-8 | **高** | 資料庫負載 |
| etcd-patroni | 1-2 | 1-2 | 中 | 配置同步 |
| **總計** | 9-18 | 17-34 | - | 需 32-core, 64GB 節點 |

**doris-f0x 節點規格(假設):**
- CPU: 32 cores
- Memory: 64 GB
- Disk: NVMe SSD 2TB

**資源使用率評估:**
- CPU: 約 28-56% (可接受)
- Memory: 約 27-53% (可接受)
- Disk I/O: 需監控,避免競爭

**結論:** f01-f03 節點有足夠資源承載所有服務,但需嚴格資源隔離。

---

## 3. Airflow Metadata DB HA 架構本機部署位置評估

### 3.1 方案比較

| 方案 | 技術可行性 | 維運複雜度 | 故障風險 | 推薦度 |
|----------------|----------|----------|---------|-------|
| A: 共置於 f01-f03 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ✅ 推薦 |
| B: 共置於 b01-b03 | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ⚠️ 不建議 |
| C:分離部署 | ⭐⭐ | ⭐ | ⭐ | ❌ 強烈反對 |


#### 方案 A:etcd 與 PostgreSQL / Patroni 共置於 f01–f03

**評估結論:建議採用(優先方案)**

**優點:**
- etcd 與 Patroni 位於同一節點群,網路延遲最低,有利於 leader election 與 failover 判斷
- 架構單純,依賴關係清楚,故障行為可預期
- Database HA 不依賴 k8s,Control Plane 僅作為節點角色存在
- 維運經驗成熟,問題定位聚焦於 DB / Patroni / etcd 三層

**缺點與風險:**
- Control Plane 節點同時承載多個關鍵服務,資源密度高
- 若資源隔離不足,PostgreSQL I/O 高峰可能影響 etcd latency
- 需投入較高的監控與資源規劃成本

---

#### 方案 B:etcd 與 PostgreSQL / Patroni 共置於 b01–b03

**評估結論:不建議 (高風險)**

**優點:**
- Database HA 與 k8s Control Plane 完全隔離
- Control Plane 不承擔任何 Database HA 負載
- 在組織明確區分 control / data plane 時,架構邏輯直觀

**缺點與風險:**
- Worker 節點通常被視為可汰換資源,與 Database 的關鍵狀態特性衝突
- PostgreSQL 與 Doris Backend 皆為高 I/O 服務,磁碟與網路競爭風險高
- Worker 節點維運操作(擴縮、重建)容易誤傷 Database HA
- 故障域與節點角色混雜,排查與責任歸屬較不清晰

---

#### 方案 C:PostgreSQL / Patroni 分離部署

**評估結論:強烈不建議**

**為什麼不能分離:**

| 問題 | 影響 |
|-----|------|
| **網路延遲** | Leader election 判斷延遲<br>Failover 速度下降 |
| **網路抖動** | 誤判節點故障<br>不必要的 Failover |
| **故障排查** | 需跨節點檢查<br>定位問題困難 |
| **依賴增加** | 網路成為關鍵依賴<br>單點故障風險上升 |


**詳細方案差異:**
- PG 在 f01-f03,etcd 在 b01-b03:
- 額外問題:etcd 在 Worker 節點,易被維運操作影響
- PG 在 b01-b03,etcd 在 f01-f03:
- 額外問題:PG 與 Doris BE 資源競爭

---

### 3.2 評估結論
- **優先考量 `etcd 與 PostgreSQL / Patroni 共置於 f01–f03`**,前提為資源與 I/O 隔離到位
- **`etcd 與 PostgreSQL / Patroni 共置於 b01–b03`** 僅在有明確 SOP 與資源隔離能力時才考慮

---

### 3.3 與 k8s 內建 etcd 共存之隔離規範

若 PostgreSQL / Patroni / Patroni-etcd 與 k8s Control Plane 或 Worker 共置,
必須落實以下隔離規範,以避免不同 etcd 與關鍵服務互相干擾:

| 組件 | 服務名稱 (Systemd) | Port (Client/Peer) | Data Directory |
| :--- | :--- |:--------------------------| :--- |
| **K8s Etcd** | `etcd.service` | `2379` / `2380` | `/var/lib/etcd` |
| **Patroni Etcd** | `etcd-patroni.service` | **`12379`** / **`12380`** | **`/var/lib/etcd-patroni`** |
| **PostgreSQL** | `patroni.service` | `5432` | `/var/lib/postgresql/data` |

- TLS 憑證與 CA 完全獨立
- 建立以下監控指標與告警:
- etcd leader 狀態
- raft / commit latency
- fsync / disk I/O latency
- PostgreSQL replication lag


---
### 3.4 其他建議

- 資源配額隔離, 防止 DB 吃光 RAM 導致 K8s API Server 當機 :
- Systemd 設定 (`/etc/systemd/system/patroni.service.d/override.conf`):
```
[Service]
# 限制 Patroni + Postgres 最多使用 8GB 記憶體
MemoryHigh=8G
MemoryMax=10G
# 權重低於 K8s 組件,資源緊張時優先被限縮
CPUWeight=50
```
- Doris FE 設定:
* 固定 Heap Size (`-Xmx`), 避免無限制增長。
- 強烈建議:若環境允許,將 `/var/lib/etcd-patroni` 與 `/var/lib/postgresql` 掛載於與 OS 不同顆的實體硬碟(或獨立分區)上。

---


[//]: # (## 4. Airflow 組件建置(k8s))

[//]: # (### 4.1 建置範圍)

[//]: # ()
[//]: # (Airflow 僅部署應用層元件於 k8s:)

[//]: # ()
[//]: # (- Webserver)

[//]: # (- Scheduler)

[//]: # (- Triggerer)

[//]: # (- Worker(依 Executor 類型))

[//]: # (- DAG 同步機制(Git-sync / Object Storage))

[//]: # ()
[//]: # (Metadata Database 透過穩定網路連線存取外部 PostgreSQL。)

[//]: # ()
[//]: # (---)

[//]: # ()
[//]: # (### 4.2 Executor 選型原則(概述))

[//]: # ()
[//]: # (- **k8sExecutor**)

[//]: # ( - 每個 Task 對應一個 Pod)

[//]: # ( - 彈性佳,資源隔離明確)

[//]: # (- **CeleryExecutor**)

[//]: # ( - 需額外 Message Broker(Redis / RabbitMQ))

[//]: # ( - Worker 常駐,資源較可預期)

[//]: # (---)

## 5. Airflow 核心組件部署於 Master Node 之可行性評估

本節評估 Apache Airflow 核心組件
(Webserver / Scheduler / Triggerer)
是否適合部署於 Kubernetes Master Node,
並將 Worker 僅部署於 Worker Node。

### 5.1 架構角色定位

| 類型 | 元件 | 架構角色 |
|----|----|----|
| 控制與協調層 | Webserver | 提供 UI / API |
| | Scheduler | DAG 解析與任務調度 |
| | Triggerer | 非同步事件處理 |
| 執行層 | Worker | Task 實際執行 |

Airflow 核心組件屬於「控制與協調層」,
不直接承載業務計算負載。

---

### 5.2 部署於 Master Node 的合理性

在本架構中,Master Node 具備以下特性:

- 已承載 Kubernetes Control Plane
- 屬於高可用、長期穩定節點
- 不作為可隨意汰換資源

將 Airflow 核心組件部署於 Master Node 的優點包括:

- 控制型服務集中,架構角色清楚
- 避免與高負載 Worker Pod 資源競爭
- Scheduler 與 K8s API Server 網路延遲最低
- 核心組件數量固定,資源需求可預測

---

### 5.3 Worker 僅部署於 Worker Node 的必要性

Airflow Worker 為 Task 實際執行單位,具備以下特性:

- CPU / Memory / I/O 使用波動大
- 可見工作負載不可預測
- 屬於可橫向擴充、可回收資源

將 Worker 僅部署於 Worker Node 可達成:

- 控制平面與計算平面隔離
- 降低 Master Node 資源被突發任務吃滿的風險
- 明確區分「平台穩定性」與「任務吞吐量」

---

### 5.4 風險與對策

| 風險 | 說明 | 對策 |
|----|----|----|
| Master Node 資源競爭 | Scheduler 與 K8s 元件同節點 | 設定 Resource Requests / Limits |
| 誤排 Worker 至 Master | Worker 誤用 Master 資源 | Taint + Toleration |
| 單節點過載 | 核心組件集中 | 多 Replica 部署 |

---

### 5.5 結論

在具備資源隔離與節點角色控管前提下:

- **Airflow 核心組件部署於 Master Node 為合理且建議之設計**
- **Airflow Worker 應嚴格限制僅部署於 Worker Node**
- 此設計有助於平台穩定性、故障域清晰化與長期維運

---

## 6. 總結

1. **Airflow Metadata DB 採用本機部署**。
2. **部署拓樸採用將 Airflow Metadata DB 及 HA架構部署於 `doris-f01` ~ `doris-f03`。
3. **配套措施**:實施嚴格的 Port、Data Directory 與 Systemd Memory Limit 隔離配置。

---

+ 328
- 0
infra/ha/keepalived&haproxy.md Zobrazit soubor

@@ -0,0 +1,328 @@
# HAProxy & Keepalived deployment

本文件說明如何在 Ubuntu 環境下為k8s及postgres部署本機的 HAProxy(負載平衡)與 Keepalived(VIP)

---

## 0. 規劃與前置準備

#### **K8s cluster規劃**:

| Hostname | Role | IP Address | 備註 |
|:----------------|:-------|:-----------|:-----------------|
| **doris-f01** | Master | `10.10.0.85` | Control Plane #1 |
| **doris-f02** | Master | `10.10.0.87` | Control Plane #2 |
| **doris-f03** | Master | `10.10.0.89` | Control Plane #3 |
| **doris-b01** | Worker | `10.10.0.93` | Worker Node |
| **doris-b02** | Worker | `10.10.0.94` | Worker Node |
| **doris-b03** | Worker | `10.10.0.95` | Worker Node |
| **doris-b04** | Worker | `10.10.0.96` | Worker Node |

#### **Postgres cluster規劃**:
| 節點名稱 | PostgreSQL 角色 | Patroni API | Patroni Etcd (DCS) | 數據目錄 (Data Dir) |
|:---|:---|:---|:---------------------------------------------|:-------------------------------------------------------------------------------------------------------|
| **doris-f01** | Primary / Replica (動態) | Port `8008` | Port `12389` (Client)<br>Port `12390` (Peer) | `/var/lib/postgresql` (DB)<br>`/var/lib/etcd-patroni` (Etcd) |
| **doris-f02** | Primary / Replica (動態) | Port `8008` | Port `12389` (Client)<br>Port `12390` (Peer) | `/var/lib/postgresql` (DB)<br>`/var/lib/etcd-patroni` (Etcd) |
| **doris-f03** | Primary / Replica (動態) | Port `8008` | Port `12389` (Client)<br>Port `12390` (Peer) | `/var/lib/postgresql` (DB)<br>`/var/lib/etcd-patroni` (Etcd) |

---

- 實作目標:

- 節點:doris-f01, doris-f02, doris-f03
- 配置對象:k8s Control Plane cluster 與 Postgres cluster。

---

### 0.1 允許非本地 IP 綁定
為了讓 HAProxy 能順利監聽尚未漂移過來的 VIP,需調整核心參數。

```bash
echo "net.ipv4.ip_nonlocal_bind = 1" | sudo tee -a /etc/sysctl.d/99-haproxy.conf
sudo sysctl -p /etc/sysctl.d/99-haproxy.conf
```

---

### 0.2 設定防火牆 (若有啟用 UFW)

```shell
# 允許 VRRP 協定 (Keepalived 用)
sudo ufw allow in proto vrrp
# 允許 HAProxy 相關 Ports
sudo ufw allow 6444/tcp
sudo ufw allow 5000:5001/tcp
sudo ufw allow 8404/tcp
```

---

## 1. HAProxy 安裝與配置

**適用節點**:所有節點,**設定檔必須相同**。

### 1.1 安裝 HAProxy

```bash
sudo apt update
sudo apt install haproxy -y
```

### 1.2 配置 `/etc/haproxy/haproxy.cfg`

備份原設定檔,並建立新設定。

```bash
sudo mv /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak
sudo vi /etc/haproxy/haproxy.cfg
```

貼上以下內容:

```haproxy
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon

# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private

# See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http

# --- 監控頁面 (可選) ---
listen stats
bind *:8404 # 監控頁面 Port
stats enable
stats uri /stats # 網址路徑
stats refresh 10s # 刷新頻率
stats auth admin:password # 登入帳號:密碼 (自行修改)

#==========k8s==========

# --- 前端配置 ---
frontend kubernetes-api
bind *:6444
mode tcp
option tcplog
default_backend k8s_masters

# --- 後端配置 ---
backend k8s_masters
mode tcp
option tcp-check
balance roundrobin
# 若要更 aggressive 的健康檢查,可加:
# tcp-check connect port 6443
server master-A 10.10.0.85:6443 check fall 3 rise 2
server master-B 10.10.0.87:6443 check fall 3 rise 2
server master-C 10.10.0.89:6443 check fall 3 rise 2

#==========postgres==========

# --- 讀寫埠前端配置 ---
frontend postgres_rw
bind *:5000
mode tcp
default_backend backend_rw

# --- 讀寫埠後端配置 ---
backend backend_rw
mode tcp
option httpchk GET /primary
http-check expect status 200
server f01 10.10.0.85:5432 check port 8008
server f02 10.10.0.87:5432 check port 8008
server f03 10.10.0.89:5432 check port 8008

# --- 唯讀埠前端配置 ---
frontend postgres_ro
bind *:5001
mode tcp
default_backend backend_ro

# --- 唯讀埠後端配置 ---
backend backend_ro
mode tcp
balance roundrobin
# 使用 API 檢查,確保節點不僅活著,而且狀態健康 (不包含初始化中或損壞的節點)
option httpchk GET /read-only
http-check expect status 200
server f01 10.10.0.85:5432 check port 8008
server f02 10.10.0.87:5432 check port 8008
server f03 10.10.0.89:5432 check port 8008
```

### 1.3 啟動與檢查

```bash
# 檢查語法
sudo haproxy -c -f /etc/haproxy/haproxy.cfg

# 啟動服務
sudo systemctl restart haproxy
sudo systemctl enable haproxy
```
使用瀏覽器連線至http://10.10.0.85:8404/stats,登入查看狀態

- 預期結果:
- K8s API Server (Port 6444):

K8s 的 Master 是Active-Active 架構,三台都應該全是綠色 (UP)

- postgres_rw (Port 5000):

應該只有 一行是綠色 (UP) (那是目前的 Primary)。

其他兩行應該是 紅色 (DOWN) (因為它們是 Replica,回應 503,這是正常的)。

- postgres_ro (Port 5001):

應該 三行全是綠色 (UP) (除非有節點掛了)。

---

## 2. Keepalived 安裝與配置

**適用節點**:所有節點,但**設定檔內容依角色不同**。

### 2.1 安裝 Keepalived

```bash
sudo apt install keepalived -y
```

### 2.2 配置 `/etc/keepalived/keepalived.conf`

```bash
sudo vi /etc/keepalived/keepalived.conf
```

#### **通用配置 (請依照節點修改 `state` 與 `priority`)**

* **Master (f01)**: `state MASTER`, `priority 100`
* **Backup (f02)**: `state BACKUP`, `priority 90`
* **Backup (f03)**: `state BACKUP`, `priority 80`

```
global_defs {
# 當上 Master 後,延遲 5 秒發送 GARP (有些 switch 反應慢)
garp_master_delay 5
# 之後每 1 秒發一次 (確保大家都有更新)
garp_master_refresh 1
# 指定腳本執行使用者 (依照您的環境設定)
script_user gjadmin
enable_script_security
}

# 定義檢查腳本
vrrp_script check_haproxy {
script "/usr/bin/pgrep haproxy"
interval 2
weight -20
}

# 定義虛擬路由
vrrp_instance VI_1 {
# --- 節點差異設定 ---
state MASTER # f01: MASTER, f02/f03: BACKUP
priority 100 # f01: 100, f02: 90, f03: 80
# ------------------
interface enp1s0 # 請確認網卡名稱 (用 ip a 查看)
virtual_router_id 51 # 所有節點需一致
advert_int 1
authentication {
auth_type PASS
auth_pass 1111 # 所有節點需一致
}
virtual_ipaddress {
10.10.0.83 # 宣告 VIP (需確認沒有被使用)
}
track_script {
check_haproxy
}
}
```

### 2.3 啟動與檢查

```bash
# 啟動服務
sudo systemctl restart keepalived
sudo systemctl enable keepalived
```

```shell
# 初始狀態檢查
# 除了本機ip外,應該還會看到VIP(10.10.0.83)
ip a
```
```shell
# 模擬故障轉移
sudo systemctl stop keepalived
```
接著在 Backup (doris-f02) 上執行 `ip a`查看。

- 預期結果:

- Master (doris-f01): VIP (10.10.0.83) 應該 立即消失。

- Backup (doris-f02): VIP (10.10.0.83) 應該 自動漂移並出現 在此節點 (因為 Priority 90 > 80)。

- 服務連通性: 此時從外部 Ping VIP 或連線 HAProxy,服務應短暫中斷 (約 1 秒) 後恢復正常

---

## 3. 整合驗證高可用性 (HA)

### 3.1 檢查 VIP 是否生效
在 Master (f01) 上執行:
```bash
ip a
```
應看到設定的 VIP(10.10.0.83) 出現在列表中。

### 3.2 模擬故障轉移 (Failover)
1. 在 Master (f01) 停止 HAProxy:
```bash
sudo systemctl stop haproxy
```
2. 在 f01 查看 VIP 是否消失。
3. 在 Backup (f02) 執行 `ip a`,確認 VIP 是否漂移過來。
4. 恢復 f01 的 HAProxy:
```bash
sudo systemctl start haproxy
```
5. 確認 VIP 是否搶回 f01 (因為 f01 權重較高)。

+ 556
- 0
infra/kubernetes/k8s-install-guide.md Zobrazit soubor

@@ -0,0 +1,556 @@
# Kubernetes cluster deployment

## 📌 0. 環境架構與前置條件

- 已安裝Keepalived&HaProxy,並設置好VIP

### 🔹 節點規劃 (Topology)
| Hostname | Role | IP Address | OS | 備註 |
|:------------| :--- |:---------------|:-------------| :--- |
| **VIP** | **Load Balancer** | **10.10.0.83** | HAProxy | **API Server 統一入口 (Port 6444)** |
| `doris-f01` | **Master** | `10.10.0.85` | Ubuntu 24.04 | 第一台初始節點 |
| `doris-f02` | **Master** | `10.10.0.87` | Ubuntu 24.04 | 後續加入的 Control Plane |
| `doris-f03` | **Master** | `10.10.0.89` | Ubuntu 24.04 | 後續加入的 Control Plane |
| `doris-b01` | **Worker** | `10.10.0.93` | Ubuntu 24.04 | 工作節點 |
| `doris-b02` | **Worker** | `10.10.0.94` | Ubuntu 24.04 | 工作節點 |
| `doris-b03` | **Worker** | `10.10.0.95` | Ubuntu 24.04 | 工作節點 |
| `doris-b04` | **Worker** | `10.10.0.96` | Ubuntu 24.04 | 工作節點 |

## 📌 1. 基礎設定(所有節點)

### 🔹 設定 Hostname

若已設定就跳過
```shell
hostnamectl set-hostname <hostname>
```

### 🔹 關閉 swap

1. Swap 當記憶體不足時,會將記憶體數據寫入速度極慢的硬碟
2. K8s 依賴精確的 RAM (物理記憶體) 數據來排程 Pod
3. Linux 核心會先使用 Swap 而不是觸發 Kubelet 的驅逐

```shell
sudo swapoff -a
sudo sed -i '/swap/d' /etc/fstab
```

1. overlay:這是容器運行時用來創建疊加文件系統的模組(Overlay Filesystem)
2. br_netfilter:橋接網路過濾的基礎。它允許 Linux 核心的 iptables(防火牆規則)能夠正確處理橋接網路流量。

### 🔹 載入核心模組
```shell
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter
```

### 🔹 sysctl 設定

1. net.ipv4.ip_forward=1:啟用 IPv4 轉發
2. net.bridge.bridge-nf-call-iptables=1:啟用橋接流量的 iptables 處理

```shell
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables=1
net.ipv4.ip_forward=1
EOF

sudo sysctl --system
```

---

## 📌 2. 安裝 containerd(所有節點)

1. Containerd 是一個標準化的、核心的Container Runtime
2. 負責管理容器生命週期、並讓容器「跑起來」的軟體
### 🔹 安裝 containerd
```shell
sudo apt update
sudo apt install -y containerd
```

### 🔹 建立預設設定:
```shell
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml
```

### 🔹 啟用 SystemdCgroup:

1. Cgroup : Linux用來限制及分配資源的工具
2. Kubelet 預設使用 systemd 作為 Cgroup driver。
3. 需確保 Containerd 也使用 systemd,否則會導致不穩定。

```shell
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
```

### 🔹 載入設定&設定開機啟動:
```shell
sudo systemctl restart containerd
sudo systemctl enable containerd
```

---

## 📌 3. 安裝 Kubernetes(所有主機)

### 🔹 新增 repository
```shell
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo apt-key add -
echo 'deb https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
```

### 🔹 安裝 kubeadm / kubectl / kubelet

1. **Kubeadm**:官方提供的 集群快速啟動工具 (只在建置時用)。
1. 證書管理 (PKI Certificates):Kubernetes 的組件之間通訊都需要加密(HTTPS)。Kubeadm 會自動生成並分發 CA 根證書、API Server 證書、Kubelet 證書等。這在以前手動做是非常痛苦且容易出錯的。
2. 生成核心組件配置 (Manifests):它會自動產生 API Server, Controller Manager, Scheduler 和 Etcd 的 YAML 檔案,並以 Static Pod 的形式在 Master 節點上運行。
3. 節點加入 (Bootstrap Tokens):它提供了一套簡單的 Token 機制,讓 Worker 節點只需要執行一行指令就能加入集群,並自動完成 TLS 認證。
4. kubeadm 主要負責最後這兩個關鍵步驟:
1. kubeadm init (只在 Master / Control Plane 執行)
- 檢查系統環境(Pre-flight check)。
- 生成證書 (CA, API server certs...)。
- 生成 kubeconfig 文件 (/etc/kubernetes/admin.conf)。
- 啟動 Control Plane 組件 (Etcd, API Server...)。
- 產出: 最後會吐出一行 kubeadm join ... 的指令給您。
2. kubeadm join (在所有 Worker Node 執行)
- Worker 節點拿著 Token 向 Master 報到。
- Master 驗證後,發給 Worker 專屬的證書。
- Worker 節點正式納入管理,準備接客(跑 Pod)。
5. Kubeadm 不做的事:
1. 不負責安裝環境依賴:它不會裝 Docker/Containerd、也不會關 Swap等等。
2. 不負責安裝 CNI 網絡插件:kubeadm init 跑完後,集群雖然起來了,但在安裝 CNI (如 Calico, Flannel) 之前,CoreDNS 都不會運作,節點狀態會是 NotReady。
2. **Kubectl**:給管理員用的「指令工具」,用來對集群下達命令
1. 發號施令:輸入指令(如 kubectl get pods, kubectl apply -f ...)。
2. 翻譯官:它會把輸入的指令,轉換成標準的 REST API 請求,發送給 Kubernetes 的大腦 (API Server)。
3. **Kublet**:運行在所有節點上的「代理人」,負責指揮 Containerd 啟動容器,並向 Master 匯報狀態
1. 接單:它會一直監聽 Master (API Server) 發來的指令。
2. 執行:收到指令後,命令自己節點上的Containerd去下載鏡像、啟動容器。
3. 匯報:它會定期向 Master 匯報:「我還活著,容器也都健康」。

```shell
sudo apt-get install -y kubelet kubeadm kubectl
```

### 🔹鎖定版本:

1. apt-mark hold : 防止在執行 `apt-get upgrade` 更新系統時,不小心自動升級了 K8s 元件。
2. K8s 的升級通常需要特定的流程(先升級 Master 再升級 Node),自動升級會導致集群崩潰。

```shell
sudo apt-mark hold kubelet kubeadm kubectl
```

### 🔹設定開機啟動:

```shell
sudo systemctl enable kubelet
```
---

## 📌 4. 初始化 Control Plane(第一台master)

初始化整個cluster的第一台master

### 🔹建立設定檔

```shell
sudo vi /kubeadm-config.yaml
```

```yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
# ==================== 1. 集群基本資訊 ====================
kubernetesVersion: v1.30.14 # 請確保與安裝的 kubeadm 版本一致
controlPlaneEndpoint: "10.10.0.83:6444" # VIP:HAProxy端口 (所有節點透過此溝通)

# ==================== 2. 網路設定 ====================
networking:
podSubnet: "10.244.0.0/16" # Pod 網段 (Flannel 預設)
serviceSubnet: "10.96.0.0/12" # Service 網段 (ClusterIP) default值
dnsDomain: cluster.local # default值

# ==================== 3. API Server 設定 ====================
apiServer:
certSANs: # 憑證需包含的所有 IP/域名
- "10.10.0.83" # VIP (最重要!)
- "10.10.0.85" # F01 IP
- "10.10.0.87" # F02 IP
- "10.10.0.89" # F03 IP
- "127.0.0.1"
extraArgs:
authorization-mode: "Node,RBAC" # default值

---
# ==================== 4. 本機初始化設定 ====================
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: "10.10.0.85" # **當前節點 (F01) 的 IP**
bindPort: 6443 # API Server 本機監聽 Port (default值)
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock # default值 建議保留
name: doris-f01 # 當前節點 Hostname 前面步驟已設定 保險起見這裡可以再設定一次
taints: # Master 污點 (不讓應用跑在 Master) default值
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane

---
# ==================== 5. Kubelet 全域設定 ====================
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd # 關鍵!確保與 Containerd 一致 (default值 建議保留)
```

```yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration

# ==================== 基本信息 ====================
kubernetesVersion: v1.30.14 # K8s 版本,建議與 kubeadm 版本一致

# ==================== 控制平面 HA 配置 ====================
controlPlaneEndpoint: "10.10.0.83:6444" # VIP:HAProxy端口
# 所有節點將通過這個地址訪問 API Server
# 這是 HA 的關鍵配置

# ==================== 網路配置 ====================
networking:
podSubnet: "10.244.0.0/16" # Pod 網段,給 CNI 使用
# Calico/Flannel 會從這個範圍分配 Pod IP
# 不要與現有網路衝突!
serviceSubnet: "10.96.0.0/12" # Service 網段 (ClusterIP)
# K8s 內部服務的虛擬 IP 範圍
# 預設值,一般不需要改

# ==================== API Server 配置 ====================
apiServer:
certSANs: # 證書 Subject Alternative Names
- "10.10.0.83" # VIP - 必須加入!
- "10.10.0.85" # F01 IP
- "10.10.0.87" # F02 IP
- "10.10.0.89" # F03 IP
# - "k8s-api.yourdomain.com" # 如果有 DNS 也可以加
extraArgs: # API Server 額外參數
bind-address: "0.0.0.0" # 監聽所有 IP (預設是 0.0.0.0)
advertise-address: "10.10.0.85" # **重要**: 此節點對外宣告的 IP
# F01 用 .85, F02 用 .87, F03 用 .89
secure-port: "6443" # API Server 實際監聽端口
# 避免與 HAProxy 的 6443 衝突

# ==================== etcd 配置 ====================
#etcd:
# external: # 使用外部 etcd cluster
# endpoints: # etcd 節點列表
# - https://10.10.0.93:2379 # B01
# - https://10.10.0.94:2379 # B02
# - https://10.10.0.95:2379 # B03
#
# # etcd TLS 證書 - 需要預先準備
# caFile: /etc/kubernetes/pki/etcd/ca.crt # etcd CA 證書
# certFile: /etc/kubernetes/pki/etcd/client.crt # etcd 客戶端證書
# keyFile: /etc/kubernetes/pki/etcd/client.key # etcd 客戶端私鑰

---
# ==================== 初始化配置 (僅第一個 master 需要) ====================
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration

localAPIEndpoint:
advertiseAddress: "10.10.0.85" # **此節點的 IP** (F01)
bindPort: 6443 # API Server 監聽端口

nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock # 容器運行時
name: doris-f01 # 節點名稱
taints: # 污點,防止 Pod 調度到 master
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane

---
# ==================== Join 配置 (其他 master 加入時使用) ====================
apiVersion: kubeadm.k8s.io/v1beta3
kind: JoinConfiguration

discovery:
bootstrapToken:
apiServerEndpoint: "10.10.0.83:6444" # VIP 地址
token: <token> # 從 kubeadm init 輸出獲取
caCertHashes:
- "sha256:<hash>" # CA 證書 hash
controlPlane: # 標記為 control plane 節點
localAPIEndpoint:
advertiseAddress: "10.10.0.87" # **F02 的 IP** (F03 用 .89)
bindPort: 6443
certificateKey: <key> # 證書加密 key (kubeadm init 輸出)

nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
name: doris-f02 # 節點名稱
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
```

```yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration

# K8s 版本 (確保與 kubeadm 版本一致)
kubernetesVersion: v1.30.14

# 控制平面 HA 端點 (HAProxy VIP)
controlPlaneEndpoint: "10.10.0.83:6444"

# 網路配置
networking:
podSubnet: "10.244.0.0/16" # Pod 網段 (Calico 使用)
serviceSubnet: "10.96.0.0/12" # Service 網段

# API Server 配置
apiServer:
# 證書 SAN 列表 (所有可能訪問 API Server 的地址)
certSANs:
- "10.10.0.83" # VIP (必須!)
- "10.10.0.85" # F01
- "10.10.0.87" # F02
- "10.10.0.89" # F03
- "127.0.0.1" # localhost
- "kubernetes"
- "kubernetes.default"
- "kubernetes.default.svc"
- "kubernetes.default.svc.cluster.local"
extraArgs:
# API Server 監聽在所有網路介面
bind-address: "0.0.0.0"
# 此節點對外宣告的 IP (F01)
advertise-address: "10.10.0.85"
# API Server 實際監聽端口 (避免與 HAProxy 6443 衝突)
secure-port: "6443"
# 審計日誌 (可選,用於安全審計)
audit-log-path: /var/log/kubernetes/audit.log
audit-log-maxage: "30"
audit-log-maxbackup: "10"
audit-log-maxsize: "100"

# 使用外部 etcd cluster (B01-B03)
#etcd:
# external:
# endpoints:
# - https://10.10.0.93:2379 # B01
# - https://10.10.0.94:2379 # B02
# - https://10.10.0.95:2379 # B03
# # etcd TLS 證書路徑
# caFile: /etc/kubernetes/pki/etcd/ca.crt
# certFile: /etc/kubernetes/pki/etcd/client.crt
# keyFile: /etc/kubernetes/pki/etcd/client.key

# Controller Manager 配置
controllerManager:
extraArgs:
bind-address: "0.0.0.0"
# Node 心跳超時時間
node-monitor-grace-period: "40s"
# Pod 驅逐超時時間
pod-eviction-timeout: "5m0s"

# Scheduler 配置
scheduler:
extraArgs:
bind-address: "0.0.0.0"

# DNS 配置
dns:
imageRepository: registry.k8s.io/coredns
imageTag: v1.10.1

# 鏡像倉庫 (如果使用國內鏡像)
# imageRepository: registry.aliyuncs.com/google_containers

---
# 初始化配置 (僅第一個 master 需要)
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration

# 本地 API Server 端點配置
localAPIEndpoint:
advertiseAddress: "10.10.0.85" # F01 的 IP
bindPort: 6443 # 監聽端口

# 節點註冊配置
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
name: doris-f01 # 節點名稱
# Master 節點污點 (防止普通 Pod 調度)
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
# 節點標籤
kubeletExtraArgs:
node-labels: "node-role.kubernetes.io/control-plane="

# Bootstrap Token 配置 (用於節點加入)
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
ttl: 24h0m0s
usages:
- signing
- authentication

---
# Kubelet 配置
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration

# 容器運行時
cgroupDriver: systemd

# 資源預留
systemReserved:
cpu: "500m"
memory: "1Gi"
ephemeral-storage: "1Gi"

kubeReserved:
cpu: "500m"
memory: "1Gi"
ephemeral-storage: "1Gi"

# 驅逐閾值
evictionHard:
memory.available: "200Mi"
nodefs.available: "10%"
imagefs.available: "15%"

# 日誌配置
logging:
format: json

# 序列化 image pulls (適合網路較慢的環境)
serializeImagePulls: false
maxParallelImagePulls: 3
```

### 🔹kubeadm執行初始化

```shell
# 建議先預拉映像檔,避免初始化超時
sudo kubeadm config images pull --config kubeadm-config.yaml

# 正式初始化
# --upload-certs:將憑證加密上傳,讓後續加入的 Master 節點能自動下載使用
sudo kubeadm init --config kubeadm-config.yaml --upload-certs
```

命令成功後會得到:

add master node 指令:
```shell
sudo kubeadm join 10.10.0.83:6444 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash> \
--control-plane --certificate-key <key> \
--apiserver-advertise-address=<F02或F03的IP> \
--apiserver-bind-port=6443
```
add worker node:
```shell
sudo kubeadm join 10.10.0.83:6444 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
```


### 🔹 設定 kubeconfig:

**目的:** 將集群的管理員憑證 (`admin.conf`) 複製到使用者的預設目錄,讓 `kubectl` 能夠讀取並取得控制權限

1. **建立目錄**:`kubectl` 預設會去 `~/.kube/` 找設定檔
2. **複製憑證**:將 `kubeadm` 產生的最高權限設定檔 (`admin.conf`) 複製過來
3. **變更權限**:將檔案擁有者改為目前使用者,**避免每次執行 kubectl 都要打 sudo**
4. **etc/kubernetes/admin.conf**:集群最高權限身分證 (Master Key), 由`kubaadmi init`自動生成,裡面包含:
1. Cluster: API Server 的網址 (例如 https://10.10.0.83:6444) 和 CA 根憑證。
2. User: 一個名為 kubernetes-admin 的使用者,以及它的 客戶端憑證 (Client Certificate) 和 私鑰 (Key)。這個憑證擁有 system:masters 群組權限(K8s 裡的上帝視角)。
3. Context: 把上面的 Cluster 和 User 綁定在一起。
```shell
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config
```

---

## 📌 5. 安裝 Pod 網路(Flannel)

Kubernetes 本身**不提供** Pod 之間的網路通訊功能,它只定義了介面 (CNI)。我們必須安裝網路插件來實現以下功能:

1. **指派 IP**:根據我們在初始化時設定的 `podSubnet` (10.244.0.0/16),為每一個 Pod 分配唯一的 IP 地址。
2. **建立覆蓋網路 (Overlay Network)**:在現有的實體網路之上,建立一層虛擬網路,讓不同節點上的 Pod 覺得彼此在同一個區域網路內。
3. **激活節點**:CoreDNS 與節點狀態依賴於網路插件。未安裝前,執行 `kubectl get nodes` 會看到狀態為 **NotReady**。

```shell
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
```

檢查: 執行 `kubectl get nodes`,doris-f01 的狀態應該會從 NotReady 變成 Ready。

**補充知識**

- 同一台節點內: 其實不用 Flannel,Docker/Containerd 本身就能讓同一台節點上的容器互通(透過本機的 Bridge)。

- 跨節點: 這是困難點。當 Node A 的 Pod (IP 10.244.1.2) 想找 Node B 的 Pod (IP 10.244.2.3) 時,連線節點的路由器(無論是實體路由或 VM 的 vSwitch) 看不懂這些 10.244 的 Pod IP,它只認得 10.10 的節點 IP。

- Flannel 的工作: 它負責把這些封包「包裝」起來(封裝),偽裝成節點對節點的流量,透過節點網路傳送到對面,再由對面的 Flannel「拆開」,交給目標 Pod。

---

## 📌 6. 加入 Master 節點

在要加入 master 的機器執行 kubeadm 給的 kubeadm join:

```shell
sudo kubeadm join 10.10.0.83:6444 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash> \
--control-plane --certificate-key <key> \
--apiserver-advertise-address=<F02或F03的IP> \
--apiserver-bind-port=6443
```

設定 Kubeconfig (讓 kubectl 可用) 加入成功後,必須配置憑證才能使用 kubectl 指令

```shell
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config # kubeadm join 會自動在 /etc/kubernetes/ 產生這份檔案
sudo chown $(id -u):$(id -g) ~/.kube/config
```

## 📌 7. 加入 Worker 節點

在每台 worker 機器執行 kubeadm 給的 kubeadm join:

```shell
sudo kubeadm join 10.10.0.83:6444 --token <TOKEN> --discovery-token-ca-cert-hash sha256:<HASH>
```

到master node上檢查節點:

```shell
kubectl get nodes -o wide
```

+ 305
- 0
infra/postgres/postgresql&patroni&etcd.md Zobrazit soubor

@@ -0,0 +1,305 @@
# PostgreSQL + Patroni + Etcd deployment

本文件說明如何部署基於 Patroni 的 PostgreSQL HA 架構


## 0. 節點規劃

| 角色 | 主機名稱 | IP Address | 說明 |
|:-----------------|:------------|:-------------|:--------------------------|
| **Etcd Cluster** | `doris-f01` | `10.10.0.85` | 負責分散式共識 (DCS) |
| | `doris-f02` | `10.10.0.87` | |
| | `doris-f03` | `10.10.0.89` | |
| **Postgres HA** | `doris-f01` | `10.10.0.85` | DB Node 1 (Patroni Node1) |
| | `doris-f02` | `10.10.0.87` | DB Node 2 (Patroni Node2) |
| | `doris-f03` | `10.10.0.89` | DB Node 3 (Patroni Node3) |

---

## 1. 安裝與設定 Etcd

**執行對象**:Etcd 叢集節點 (`doris-f01`, `doris-f02`, `doris-f03`)

### 1.1 下載與安裝 Binary

```bash
# 下載
wget [https://github.com/etcd-io/etcd/releases/download/v3.5.25/etcd-v3.5.25-linux-amd64.tar.gz](https://github.com/etcd-io/etcd/releases/download/v3.5.25/etcd-v3.5.25-linux-amd64.tar.gz)
tar -xvf etcd-v3.5.25-linux-amd64.tar.gz

# 安裝至系統路徑
sudo mv etcd-v3.5.25-linux-amd64/etcd* /usr/local/bin/

# 驗證版本
etcd --version
etcdctl version
```
---

### 1.2 建立使用者與目錄
建立 `etcd` 使用者並設定專屬目錄 `/var/lib/etcd-patroni`
```shell
# 建立使用者 (若不存在才建立)
if ! id -u etcd > /dev/null 2>&1; then
sudo useradd --system --home /var/lib/etcd-patroni --shell /usr/sbin/nologin etcd
echo "User 'etcd' created."
else
echo "User 'etcd' already exists. Skipping creation."
fi

# 建立資料與設定檔目錄
sudo mkdir -p /var/lib/etcd-patroni
sudo chown -R etcd:etcd /var/lib/etcd-patroni
sudo chmod 700 /var/lib/etcd-patroni

sudo mkdir -p /etc/etcd-patroni
sudo chown -R etcd:etcd /etc/etcd-patroni
```

---

### 1.3 設定 Systemd 服務
編輯 `/etc/systemd/system/etcd-patroni.service`
```
[Unit]
Description=etcd key-value store
After=network-online.target
Wants=network-online.target

[Service]
User=etcd
Type=notify
# 指向我們自定義的 config 檔案路徑
ExecStart=/usr/local/bin/etcd --config-file /etc/etcd-patroni/etcd.conf.yml
Restart=always
LimitNOFILE=40000

[Install]
WantedBy=multi-user.target
```

---

### 1.4 配置 Etcd 叢集參數

編輯 `/etc/etcd-patroni/etcd.conf.yml`。

**⚠️ 注意**:不同節點需修改 `name` 與 IP 位址。

**範例:設定 doris-f01 (`10.10.0.85`)**

```yaml
# 節點名稱 (f01, f02, f03 需對應修改)
name: f01

# 資料存放目錄
data-dir: /var/lib/etcd-patroni

# 本機監聽位址 (請修改為該節點 IP)
listen-peer-urls: http://10.10.0.85:12380
listen-client-urls: http://10.10.0.85:12379,http://127.0.0.1:12379

# 廣播給叢集的位址 (請修改為該節點 IP)
initial-advertise-peer-urls: http://10.10.0.85:12380
advertise-client-urls: http://10.10.0.85:12379

# 叢集初始化設定 (所有節點這行必須完全一致)
initial-cluster: f01=http://10.10.0.85:12380,f02=http://10.10.0.87:12380,f03=http://10.10.0.89:12380
initial-cluster-state: new
initial-cluster-token: patroni-etcd
```

---

### 1.5 啟動 Etcd
```shell
sudo systemctl daemon-reload
sudo systemctl enable --now etcd-patroni
sudo systemctl status etcd-patroni
```

---

## 2. 安裝 PostgreSQL

**執行對象**:DB 節點 (`doris-f01`, `doris-f02`, `doris-f03`)

### 2.1 設定 Repository 與安裝

```bash
sudo apt update
sudo apt install -y wget gnupg lsb-release

# 加入 PostgreSQL 官方 Repo
wget -qO - [https://www.postgresql.org/media/keys/ACCC4CF8.asc](https://www.postgresql.org/media/keys/ACCC4CF8.asc) | sudo apt-key add -
echo "deb [http://apt.postgresql.org/pub/repos/apt](http://apt.postgresql.org/pub/repos/apt) $(lsb_release -cs)-pgdg main" \
| sudo tee /etc/apt/sources.list.d/pgdg.list

sudo apt update

# 安裝 PostgreSQL 18
sudo apt install -y postgresql-18 postgresql-client-18

# 驗證
psql --version
```

---

## 3. 安裝與設定 Patroni

**執行對象**:DB 節點 (`doris-f01`, `doris-f02`, `doris-f03`)

### 3.1 透過 Python venv 安裝 Patroni

```bash
# 1. 建立工具目錄
sudo mkdir -p /opt/patroni
sudo chown $USER:$USER /opt/patroni

# 2. 建立 venv 環境
cd /opt/patroni
python3 -m venv venv

# 3. 進入 venv 安裝依賴與套件
source venv/bin/activate
pip install --upgrade pip
sudo apt install -y libpq-dev
pip install "patroni[etcd]" psycopg2
deactivate

# 4. 建立全域指令連結 (Symlink)
sudo ln -s /opt/patroni/venv/bin/patroni /usr/local/bin/patroni
sudo ln -s /opt/patroni/venv/bin/patronictl /usr/local/bin/patronictl

# 5. 驗證安裝
patroni --version
patronictl version
```

---

### 3.2 配置 Patroni (`/etc/patroni.yml`)

編輯 `/etc/patroni.yml`。

#### **通用配置 (請依照節點修改 `name` 與 `connect_address`)**

* **doris-f01**: `name: node1`, `connect_address: 10.10.0.85:8008`
* **doris-f02**: `name: node2`, `connect_address: 10.10.0.87:8008`
* **doris-f03**: `name: node3`, `connect_address: 10.10.0.89:8008`

```yaml
scope: pgcluster
# 節點名稱 (唯一識別,如 node1, node2, node3)
name: node1

restapi:
listen: 0.0.0.0:8008
# 修改為本機 IP
connect_address: 10.10.0.85:8008

# Patroni >= 3.x 使用 etcd3
# 舊版 Patroni 使用 etcd
etcd3:
# 指向 Etcd 叢集 (f01, f02, f03)
hosts: 10.10.0.85:12379,10.10.0.87:12379,10.10.0.89:12379

bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
initdb:
- encoding: UTF8
- data-checksums
pg_hba:
# 修改 replication 網段 (確保包含所有 DB 節點)
- host replication replicator 10.10.0.0/24 md5
- host all all 0.0.0.0/0 md5

postgresql:
listen: 0.0.0.0:5432
# 修改為本機 IP
connect_address: 10.10.0.85:5432
data_dir: /var/lib/postgresql/18/main
bin_dir: /usr/lib/postgresql/18/bin
authentication:
superuser:
username: postgres
password: postgres
replication:
username: replicator
password: replicator
parameters:
max_wal_senders: 10
wal_keep_size: 2048
hot_standby: "on"
```

---

### 3.3 設定 Systemd 服務

編輯 `/etc/systemd/system/patroni.service`:

```ini
[Unit]
Description=Patroni
After=network-online.target
Wants=network-online.target

[Service]
User=postgres
ExecStart=/usr/local/bin/patroni /etc/patroni.yml
Restart=always
LimitNOFILE=50000

[Install]
WantedBy=multi-user.target
```

---

### 3.4 啟動 Patroni
啟動前請確認原生的 postgresql 服務已停止(Patroni 會接管)
```shell
sudo systemctl stop postgresql
sudo systemctl disable postgresql

sudo systemctl daemon-reload
sudo systemctl enable --now patroni
sudo systemctl status patroni
```

---

## 4. 維運與除錯

### 驗證叢集狀態
在任一 DB 節點執行:
```bash
patronictl -c /etc/patroni.yml list
```

---

## 5. 重置 Etcd 節點 (清除資料)
若 Etcd 叢集發生嚴重錯誤需重建,請在所有 Etcd 節點執行:
```shell
sudo systemctl stop etcd-patroni
sudo rm -rf /var/lib/etcd-patroni
sudo mkdir -p /var/lib/etcd-patroni
sudo chown -R etcd:etcd /var/lib/etcd-patroni
sudo systemctl start etcd-patroni
```

---

## 6. 重置 Patroni 節點
若需將某個 DB 節點 (例如 node1) 重新初始化(Re-init):
```shell
# 這會刪除該節點的資料並從 Primary 重新同步
sudo patronictl -c /etc/patroni.yml reinit pgcluster node1
```

+ 403
- 0
k8s-airflow-HA基礎架構說明.md Zobrazit soubor

@@ -0,0 +1,403 @@
# Kubernetes & Airflow HA 基礎架構設計說明

# 目錄
#### 1. 設計背景與目標
- 為何 Kubernetes 需要 HA
- 為何 Airflow 需要 HA
- 兩者在資料平台中的角色
- 設計目標

#### 2. 整體架構總覽
- 架構設計概念
- 核心元件與責任分層

#### 3. 平台入口HA設計
- Keepalived + HAProxy
- VIP 設計原則

#### 4. Kubernetes Control Plane HA設計
- API Server HA
- K8s Etcd 的角色與邊界

#### 5. Airflow on Kubernetes HA設計
- Airflow 元件角色
- 使用K8s的功能達到HA設計

#### 6. Metadata Database HA設計
- PostgreSQL + Patroni
- Patroni Etcd(DCS)

#### 7. 故障情境與自動切換流程
- Kubernetes Control Plane Failover
- 平台入口(VIP)Failover
- Metadata Database Failover
- Airflow 服務連續性

#### 8. 總結

---

## 1. 設計背景與目標
資料平台與自動化流程中,Kubernetes 與 Apache Airflow 分別扮演 「容器平台」與「工作流程中樞」的關鍵角色。
任一元件不可用,都可能導致整體平台服務中斷。

本文件之設計目標如下:
- **Kubernetes 平台高可用**
確保 Control Plane 具備容錯能力,避免單一 Master 失效導致平台不可控。
- **Airflow 服務高可用**
透過 Kubernetes 的調度與自我修復能力,確保工作流程服務不中斷。
- **狀態與資料層高可用**
針對 Airflow Metadata Database,設計具備自動 Failover 能力的資料庫架構。
- **消除單一故障點(SPOF)**
從流量入口、平台控制層到狀態儲存層,避免任何單點失效。
---
## 2. 整體架構總覽
本架構以 On-Premise 或 Private Cloud 環境下 **Kubernetes 與 Apache Airflow 的 HA** 為設計核心,
透過多層次的 HA 機制,確保平台在任一元件或節點失效時,仍可持續提供服務。

整體HA架構可分為四個層級:

- **平台入口層(Platform Ingress HA)**
使用 Keepalived + HAProxy 提供虛擬 IP(VIP)與流量入口HA。

- **容器平台層(Kubernetes HA)**
Kubernetes Control Plane 採用多 Master 架構,確保叢集控制能力不中斷。

- **工作流程層(Airflow on Kubernetes)**
Airflow 元件以 Pod 形式運行,透過 Kubernetes 調度與自我修復能力達成服務高可用。

- **狀態與資料層(Stateful Services HA)**
Metadata Database 採用 PostgreSQL + Patroni,並使用獨立 Etcd 作為分散式共識(DCS)。

```
┌─────────────────────────────────────────────────┐
│ Layer 1: 平台入口層 (Ingress HA) │
│ VIP + Keepalived + HAProxy │
│ → 消除流量入口單點故障 │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Layer 2: 容器平台層 (Kubernetes HA) │
│ Multi-Master + K8s Etcd │
│ → 消除集群控制平面單點故障 │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Layer 3: 工作流程層 (Airflow on K8s) │
│ Scheduler + API Server + Worker Pods │
│ → 透過 K8s 實現服務自動恢復 │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Layer 4: 狀態與資料層 (Stateful Services HA) │
│ PostgreSQL + Patroni + Patroni Etcd │
│ → 資料庫自動 Failover │
└─────────────────────────────────────────────────┘
```

### **整體架構**
```mermaid
graph TB
%% ===== 外部使用者 =====
User[用戶 / 運維人員]

%% ===== Layer 1: Ingress HA =====
subgraph L1["Layer 1: 平台入口層"]
VIP[VIP: 10.10.0.83]
subgraph HAProxy_Cluster["HAProxy + Keepalived"]
F01[doris-f01Keepalived + HAProxy]
F02[doris-f02Keepalived + HAProxy]
F03[doris-f03Keepalived + HAProxy]
end
end

%% ===== Layer 2: K8s HA =====
subgraph L2["Layer 2: 容器平台層"]
subgraph K8s_Masters["Kubernetes Control Plane"]
M1[Master 1API + Controller + Scheduler]
M2[Master 2API + Controller + Scheduler]
M3[Master 3API + Controller + Scheduler]
end
subgraph K8s_Etcd["K8s Etcd Cluster"]
KE1[etcd:2379]
KE2[etcd:2379]
KE3[etcd:2379]
end
end

%% ===== Layer 3: Airflow =====
subgraph L3["Layer 3: 工作流程層"]
subgraph Airflow["Airflow on Kubernetes"]
AF_Web[Webserver Pod]
AF_Sch[Scheduler Pod]
AF_Wrk[Worker Pods]
end
end

%% ===== Layer 4: Database HA =====
subgraph L4["Layer 4: 狀態與資料層"]
subgraph PG_Cluster["PostgreSQL + Patroni"]
PG1[Primarydoris-f01]
PG2[Replicadoris-f02]
PG3[Replicadoris-f03]
end
subgraph Patroni_Etcd["Patroni Etcd (DCS)"]
PE1[etcd:12379]
PE2[etcd:12379]
PE3[etcd:12379]
end
end

%% ===== 連接關係 =====
User --> VIP
VIP --> F01 & F02 & F03
F01 & F02 & F03 --> M1 & M2 & M3
M1 & M2 & M3 --> KE1 & KE2 & KE3
M1 & M2 & M3 --> Airflow
Airflow --> PG_Cluster
PG_Cluster -.-> Patroni_Etcd

%% ===== 樣式 =====
classDef layer1 fill:#e1f5ff,stroke:#01579b
classDef layer2 fill:#f3e5f5,stroke:#4a148c
classDef layer3 fill:#e8f5e9,stroke:#1b5e20
classDef layer4 fill:#fff3e0,stroke:#e65100
class L1 layer1
class L2 layer2
class L3 layer3
class L4 layer4
```

---
## 3. 平台入口HA設計

**Keepalived + HAProxy**

設計原則
- VIP 為唯一入口
- 任一節點失效時,自動切換入口主機
- 流量入口 HA 與後端服務角色解耦


**VRRP 選舉概念**
```mermaid
graph TD
Client --> VIP
VIP -->|Priority 100| doris-f01
VIP -.->|Priority 90| doris-f02
VIP -.->|Priority 80| doris-f03
```
- Keepalived 透過 VRRP 協定競選 Master
- 僅 Master 節點綁定 VIP
- Backup 節點持續監聽並待命

**流量轉發流程**
```mermaid
graph TD
Client --> VIP
VIP --> Keepalived
Keepalived --> HAProxy
HAProxy --> Backend
```
- HAProxy 在所有節點上運行
- 只有持有 VIP 的節點實際接收流量
- VIP 切換 ≠ 服務重啟

---
## 4. Kubernetes Control Plane HA

設計說明
- Kubernetes Control Plane 為整體平台基礎
- 使用 Kubernetes 內建 Etcd
- 與 Patroni Etcd 完全隔離

### **Kubernetes Control Plane HA架構**
```mermaid
graph TD
%% ===== Client =====
User[User / Operator]

%% ===== K8s API VIP =====
API_VIP(VIP: K8s API)
User --> API_VIP

%% ===== Kubernetes Control Plane HA =====
subgraph CP["Kubernetes Control Plane HA"]
direction TB

%% --- API Servers ---
subgraph APIS["API Servers"]
API1[kube-apiserver]
API2[kube-apiserver]
API3[kube-apiserver]
end

%% --- Controllers & Scheduler ---
subgraph CTRL["Controller & Scheduler"]
CM1[kube-controller-manager]
SCH1[kube-scheduler]
CM2[kube-controller-manager]
SCH2[kube-scheduler]
CM3[kube-controller-manager]
SCH3[kube-scheduler]
end

%% --- etcd State Backend ---
subgraph ETCD["etcd Cluster (State Backend)"]
KE1[etcd]
KE2[etcd]
KE3[etcd]
end
end

%% ===== Connections =====
API_VIP --> API1
API_VIP --> API2
API_VIP --> API3

API1 --> KE1
API2 --> KE2
API3 --> KE3

CM1 --> API1
SCH1 --> API1

CM2 --> API2
SCH2 --> API2

CM3 --> API3
SCH3 --> API3
```

邊界原則
- Kubernetes Etcd:
- 僅供 K8s 使用
- Port:2379 / 2380
- Patroni Etcd:
- 僅供 PostgreSQL HA 使用
- Port:12379 / 12380
- 嚴禁共用 Etcd
---
## 5. Airflow on Kubernetes HA設計

| 元件 | 角色 | 運行方式 | HA 機制 |
|-----|------|---------|---------|
| **Webserver** | 提供 Web UI 與 REST API | Deployment (2 replicas) | K8s 自動重建 |
| **Scheduler** | 解析 DAG、產生 Task Instance | Deployment (2 replicas) | Leader Election |
| **Triggerer** | 處理 Deferrable Operator | Deployment (1-2 replicas) | K8s 自動重建 |
| **Worker** | 執行 Task | KubernetesExecutor: 動態 Pod<br>CeleryExecutor: StatefulSet | 短生命週期 |
---
## 6. Metadata Database HA設計

PostgreSQL + Patroni + Etcd

設計重點
- Metadata DB 為 Airflow 核心依賴
- 使用 Patroni 管理 PostgreSQL 角色
- Etcd 作為 DCS(Distributed Configuration Store)

### **Airflow Metadata Database HA架構**

```mermaid
graph TD
User[User / Client]

%% ===== 流量入口 =====
VIP(VIP 10.10.0.83)
User --> VIP

subgraph ENTRY["Ingress HA (Keepalived + HAProxy)"]
subgraph F01["doris-f01"]
K1[Keepalived]
H1[HAProxy]
end
subgraph F02["doris-f02"]
K2[Keepalived]
H2[HAProxy]
end
subgraph F03["doris-f03"]
K3[Keepalived]
H3[HAProxy]
end
end

VIP --> K1 --> H1

%% ===== Kubernetes =====
subgraph K8S["Kubernetes Cluster"]
API[K8s API Server]
subgraph AIRFLOW["Airflow Components"]
AF_API[Airflow API Server]
AF_SCH[Airflow Scheduler]
AF_WK[Airflow Workers Pods]
end
end

H1 --> AF_API
AF_SCH --> AF_WK

%% ===== DB =====
subgraph DB["PostgreSQL HA (Patroni)"]
PG1[Primary]
PG2[Replica]
PG3[Replica]
end

%% ===== DCS =====
subgraph DCS["Patroni Etcd (DCS)"]
E1[Etcd f01]
E2[Etcd f02]
E3[Etcd f03]
end

AF_API --> DB
AF_SCH --> DB

PG1 --> PG2
PG1 --> PG3

PG1 -.-> DCS
PG2 -.-> DCS
PG3 -.-> DCS
```

---
## 7. 故障情境與自動切換流程

- Kubernetes Control Plane Failover
- kube-apiserver 為無狀態服務,可由多個實例同時提供服務
- 當單一 Master 節點失效時,其餘 Master 持續提供 API 能力
- Controller Manager 與 Scheduler 透過 Leader Election 機制自動切換

- 平台入口(VIP)Failover
```mermaid
sequenceDiagram
participant Client
participant F01 as doris-f01
participant F02 as doris-f02

Client->>F01: 存取 VIP
Note over F01: Keepalived 停止
F02->>F02: 接手 VIP
Client->>F02: 流量自動切換
```

- Metadata Database Failover
- Patroni 偵測 Primary 不可用
- Etcd 重新選舉 Leader
- Replica 升級為新 Primary
- Airflow 自動重新連線
- Airflow 服務連續性
- Airflow Scheduler 與 API Server 為無狀態服務,可由 Kubernetes 自動重建
- Worker Pod 為短生命週期任務執行單元,單一 Pod 失效不影響整體流程
- Metadata Database 透過 Patroni 提供自動 Failover
- 各層 HA 機制相互獨立,避免連鎖性故障
---

## 8. 總結

本架構透過多層HA設計,確保 Apache Airflow 及 Kubernetes 的運作:
- 可自動容錯
- 可長期穩定運行


Načítá se…
Zrušit
Uložit