前言
在現代雲端架構中,資料庫通常部署在受保護的私有網路環境(Private Subnet)中,以提升安全性。AWS RDS(Relational Database Service)作為主流的托管資料庫服務,提供了多種連線方式,但對於初學者來說,如何在不同環境(本機、Docker、Kubernetes)中正確連線到 RDS 往往充滿挑戰。
這篇文章將深入探討:
- AWS RDS 網路架構:公有子網 vs 私有子網的差異
- 直接連線方式:當 RDS 設為 Publicly Accessible 時
- SSH 隧道(SSH Tunneling):透過 Bastion Host 連線私有 RDS
- 容器環境連線:在 Docker 和 Kubernetes Pod 中使用 psql
- psql 完整命令參考:從基礎查詢到進階操作
- 安全最佳實踐:如何保護資料庫連線與憑證
- 常見問題排查:連線失敗的系統化診斷方法
無論你是在本機開發、容器化部署、或是 Kubernetes 叢集中操作,這篇文章都能幫助你建立安全可靠的資料庫連線。
AWS RDS 網路架構概覽
在開始連線之前,我們需要理解 AWS RDS 的網路架構。RDS 實例可以部署在不同的網路環境中,每種配置都有不同的連線方式和安全考量。
公有子網 vs 私有子網
graph TB
subgraph "AWS VPC"
subgraph "Public Subnet (10.0.1.0/24)"
IGW[Internet Gateway]
Bastion[Bastion Host<br/>EC2 Instance]
RDS_Public[RDS PostgreSQL<br/>Publicly Accessible: Yes<br/>10.0.1.100]
end
subgraph "Private Subnet (10.0.2.0/24)"
RDS_Private[RDS PostgreSQL<br/>Publicly Accessible: No<br/>10.0.2.100]
App[Application Servers<br/>ECS/EKS Pods]
end
subgraph "Security"
SG_RDS[Security Group: RDS<br/>Inbound: 5432 from App]
SG_Bastion[Security Group: Bastion<br/>Inbound: 22 from MyIP]
end
end
subgraph "外部網路"
Developer[開發者本機]
Web[網際網路]
end
Web --> IGW
Developer -->|SSH Port 22| Bastion
Developer -->|psql Port 5432<br/>⚠️ 不安全| RDS_Public
Bastion -.->|SSH Tunnel| RDS_Private
App -->|psql Port 5432| RDS_Private
RDS_Public -.->|套用| SG_RDS
RDS_Private -.->|套用| SG_RDS
Bastion -.->|套用| SG_Bastion
style RDS_Public fill:#ffe1e1
style RDS_Private fill:#e1ffe1
style Bastion fill:#e1f5ff
style Developer fill:#fff3e1
兩種部署方式的比較
| 特性 | 公有子網 (Publicly Accessible) | 私有子網 (Private) |
|---|---|---|
| 直接連線 | ✅ 可以從網際網路直接連線 | ❌ 無法直接連線 |
| 安全性 | ⚠️ 較低,暴露在公網 | ✅ 高,完全隔離 |
| 連線方式 | psql 直連 | 需要 Bastion Host / VPN |
| 適用場景 | 開發測試環境 | 生產環境(推薦) |
| 成本 | RDS 費用 | RDS + Bastion Host 費用 |
| 維護複雜度 | 低 | 中等(需管理 Bastion) |
最佳實踐:
- ✅ 生產環境:使用私有子網 + Bastion Host 或 VPN
- ✅ 開發環境:可使用公有子網,但必須嚴格限制 Security Group
- ❌ 絕對避免:生產環境使用 Publicly Accessible + 0.0.0.0/0 (完全開放)
連線方式一:直接連線(公有子網)
當 RDS 實例設定為 Publicly Accessible 時,可以直接從網際網路連線。這是最簡單的方式,但也是最不安全的。
完整連線流程
sequenceDiagram
participant Dev as 開發者本機
participant DNS as AWS Route 53
participant IGW as Internet Gateway
participant SG as Security Group
participant RDS as RDS PostgreSQL
Dev->>DNS: 1. 解析 RDS Endpoint
Note right of Dev: yourdb.xxx.ap-east-1.rds.amazonaws.com
DNS->>Dev: 2. 回傳公有 IP (3.x.x.x)
Dev->>IGW: 3. TCP SYN 到 3.x.x.x:5432
IGW->>SG: 4. 檢查 Inbound Rules
alt Security Group 允許
SG->>RDS: 5. 轉發到 RDS (10.0.1.100:5432)
RDS->>SG: 6. TCP SYN-ACK
SG->>IGW: 7. 轉發回應
IGW->>Dev: 8. 建立 TCP 連線
Dev->>RDS: 9. PostgreSQL 握手<br/>protocol version 3.0
RDS->>Dev: 10. 要求認證 (md5/scram-sha-256)
Dev->>RDS: 11. 傳送帳號密碼
RDS->>Dev: 12. 認證成功,回傳 Ready for Query
Note over Dev,RDS: ✅ 連線建立成功
else Security Group 拒絕
SG->>IGW: 丟棄封包
Note over Dev: ❌ 連線逾時
end
步驟 1:安裝 PostgreSQL Client
不同作業系統的安裝方式:
# macOS (使用 Homebrew)
brew install postgresql
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install postgresql-client
# RHEL/CentOS/Amazon Linux
sudo yum install postgresql
# Windows (使用 Chocolatey)
choco install postgresql
# 驗證安裝
psql --version
# 應該顯示:psql (PostgreSQL) 15.x
步驟 2:取得 RDS 連線資訊
在 AWS Console 中取得以下資訊:
Endpoint(終端節點):
- AWS Console > RDS > Databases > 選擇你的資料庫
- 在 “Connectivity & security” 頁籤中找到 “Endpoint”
- 格式:
yourdb.xxx.ap-east-1.rds.amazonaws.com
Port(埠號):
- 預設為
5432 - 可在同一頁面確認
- 預設為
Master username(主帳號):
- 在 “Configuration” 頁籤中找到
- 預設通常是
postgres
Database name(資料庫名稱):
- 建立時指定的初始資料庫名稱
- 預設為
postgres
密碼:
- 建立時設定的密碼
- 或從 AWS Secrets Manager 中取得
步驟 3:設定 Security Group
這是最關鍵的步驟!Security Group 就像是 RDS 的防火牆。
flowchart TD
Start[設定 Security Group] --> A[找到 RDS 的 SG]
A --> B[編輯 Inbound Rules]
B --> C{連線來源?}
C -->|開發測試| D[Type: PostgreSQL<br/>Port: 5432<br/>Source: My IP]
C -->|特定 IP| E[Type: PostgreSQL<br/>Port: 5432<br/>Source: 1.2.3.4/32]
C -->|VPC 內部| F[Type: PostgreSQL<br/>Port: 5432<br/>Source: sg-xxx]
C -->|❌ 不建議| G[Type: PostgreSQL<br/>Port: 5432<br/>Source: 0.0.0.0/0]
D --> H[儲存規則]
E --> H
F --> H
G --> I[⚠️ 安全風險<br/>僅用於短期測試]
I --> H
H --> J[等待約 30 秒生效]
J --> K[測試連線]
style G fill:#ffe1e1
style I fill:#ffe1e1
style D fill:#e1ffe1
style E fill:#e1ffe1
style F fill:#e1ffe1
AWS Console 操作步驟:
- AWS Console > RDS > Databases > 選擇你的資料庫
- 點擊 “VPC security groups” 連結
- 選擇 “Inbound rules” 頁籤
- 點擊 “Edit inbound rules”
- 新增規則:
Type: PostgreSQL Protocol: TCP Port range: 5432 Source: My IP (AWS 會自動偵測你的 IP) Description: Dev access from my IP - 點擊 “Save rules”
步驟 4:連線到 RDS
基本連線命令:
psql \
-h yourdb.xxx.ap-east-1.rds.amazonaws.com \
-p 5432 \
-U postgres \
-d postgres
# 參數說明:
# -h: RDS Endpoint (host)
# -p: 埠號 (port)
# -U: 使用者名稱 (username)
# -d: 資料庫名稱 (database)
執行後會提示輸入密碼:
Password for user postgres:
成功後會看到:
psql (15.4)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.
postgres=>
使用環境變數簡化連線
為了避免每次都輸入密碼,可以使用環境變數:
# 設定環境變數
export PGHOST=yourdb.xxx.ap-east-1.rds.amazonaws.com
export PGPORT=5432
export PGUSER=postgres
export PGDATABASE=postgres
export PGPASSWORD=your-password # ⚠️ 不安全,見下方說明
# 現在可以直接執行 psql
psql
更安全的做法:使用 .pgpass 檔案
# 建立 ~/.pgpass 檔案
echo "yourdb.xxx.ap-east-1.rds.amazonaws.com:5432:postgres:postgres:your-password" > ~/.pgpass
# 設定權限(必須是 0600,否則 psql 會拒絕使用)
chmod 0600 ~/.pgpass
# 現在 psql 會自動讀取密碼
psql -h yourdb.xxx.ap-east-1.rds.amazonaws.com -U postgres -d postgres
.pgpass 檔案格式:
hostname:port:database:username:password
可以使用萬用字元:
# 允許任何資料庫
yourdb.xxx.ap-east-1.rds.amazonaws.com:5432:*:postgres:your-password
# 允許任何 RDS 實例
*.ap-east-1.rds.amazonaws.com:5432:*:postgres:your-password
連線方式二:SSH 隧道(私有子網)
在生產環境中,RDS 通常部署在私有子網(Private Subnet)中,無法直接從網際網路連線。這時需要透過 Bastion Host(跳板機)建立 SSH 隧道。
SSH 隧道運作原理
SSH 隧道(SSH Tunneling)也稱為 SSH Port Forwarding,它可以讓你透過 SSH 連線,將本機的某個埠號轉發到遠端伺服器的另一個埠號。
sequenceDiagram
participant Local as 本機 (localhost:5433)
participant SSH as SSH Client
participant Bastion as Bastion Host<br/>(公有 IP)
participant RDS as RDS PostgreSQL<br/>(私有 IP: 10.0.2.100:5432)
Note over Local,RDS: 階段 1: 建立 SSH 隧道
SSH->>Bastion: 1. SSH 連線 (Port 22)
Note right of SSH: ssh -i key.pem ec2-user@bastion-ip
Bastion->>SSH: 2. SSH 認證成功
SSH->>Bastion: 3. 請求建立 Port Forwarding<br/>Local 5433 → RDS 10.0.2.100:5432
Bastion->>SSH: 4. 隧道建立成功
Note over SSH,Bastion: ✅ SSH 隧道已建立
Note over Local,RDS: 階段 2: 透過隧道連線 RDS
Local->>SSH: 5. 連線到 localhost:5433
Note right of Local: psql -h localhost -p 5433
SSH->>Bastion: 6. 透過 SSH 隧道傳送資料
Bastion->>RDS: 7. 轉發到 10.0.2.100:5432
RDS->>Bastion: 8. PostgreSQL 認證請求
Bastion->>SSH: 9. 透過隧道回傳
SSH->>Local: 10. 轉發到 localhost:5433
Local->>SSH: 11. 傳送認證資訊
SSH->>Bastion: 12. 透過隧道傳送
Bastion->>RDS: 13. 轉發到 RDS
RDS->>Bastion: 14. 認證成功
Bastion->>SSH: 15. 回傳成功訊息
SSH->>Local: 16. psql 連線建立
Note over Local,RDS: ✅ 可以開始執行 SQL 查詢
網路拓撲圖
graph TB
subgraph "開發者環境"
Dev[開發者本機<br/>192.168.1.100]
LocalPort[localhost:5433<br/>本機埠號]
end
subgraph "AWS VPC (10.0.0.0/16)"
subgraph "Public Subnet (10.0.1.0/24)"
IGW[Internet Gateway]
Bastion[Bastion Host<br/>公有 IP: 3.x.x.x<br/>私有 IP: 10.0.1.50]
end
subgraph "Private Subnet (10.0.2.0/24)"
RDS[RDS PostgreSQL<br/>私有 IP: 10.0.2.100<br/>Port: 5432]
end
subgraph "Security Groups"
SG_Bastion[SG: Bastion<br/>Inbound: 22 from MyIP<br/>Outbound: All]
SG_RDS[SG: RDS<br/>Inbound: 5432 from Bastion SG<br/>Outbound: None needed]
end
end
Dev -->|1. SSH 連線<br/>Port 22| IGW
IGW --> Bastion
Bastion -.->|套用| SG_Bastion
RDS -.->|套用| SG_RDS
LocalPort -.->|2. psql localhost:5433| Dev
Dev -.->|3. 透過 SSH 隧道| Bastion
Bastion -->|4. 轉發到<br/>10.0.2.100:5432| RDS
style Bastion fill:#e1f5ff
style RDS fill:#e1ffe1
style Dev fill:#fff3e1
style LocalPort fill:#ffe1e1
步驟 1:準備 Bastion Host
Bastion Host(跳板機)是部署在公有子網的 EC2 實例,用於安全地存取私有資源。
建立 Bastion Host:
啟動 EC2 實例:
- AMI: Amazon Linux 2 或 Ubuntu
- Instance Type: t3.micro(夠用)
- Network: 選擇 RDS 所在的 VPC
- Subnet: Public Subnet(必須有 Internet Gateway)
- Auto-assign Public IP: Enable
- Security Group: 建立新的 SG(見下方)
設定 Bastion Security Group:
Inbound Rules: - Type: SSH Protocol: TCP Port: 22 Source: My IP (限制只有你的 IP 可以連線) Outbound Rules: - Type: All traffic Destination: 0.0.0.0/0設定 RDS Security Group:
Inbound Rules: - Type: PostgreSQL Protocol: TCP Port: 5432 Source: Bastion Security Group ID (sg-xxx)這樣設定後,只有從 Bastion Host 發出的連線才能連到 RDS。
下載 SSH 私鑰:
- 建立 EC2 時會要求你建立或選擇 Key Pair
- 下載
.pem檔案到本機(例如~/Downloads/bastion.pem) - 移動到安全位置並設定權限:
mkdir -p ~/.ssh mv ~/Downloads/bastion.pem ~/.ssh/ chmod 400 ~/.ssh/bastion.pem
步驟 2:測試 SSH 連線
先確認可以 SSH 到 Bastion Host:
ssh -i ~/.ssh/bastion.pem ec2-user@BASTION_PUBLIC_IP
# Amazon Linux 使用 ec2-user
# Ubuntu 使用 ubuntu
成功後會看到:
[ec2-user@ip-10-0-1-50 ~]$
這時你已經在 Bastion Host 上了。可以測試是否能連到 RDS:
# 在 Bastion Host 上安裝 psql
sudo yum install postgresql -y # Amazon Linux
# 或
sudo apt-get install postgresql-client -y # Ubuntu
# 測試連線 RDS
psql -h yourdb.xxx.ap-east-1.rds.amazonaws.com -U postgres -d postgres
# 如果成功,代表 Bastion → RDS 的網路路徑是通的
# 輸入 \q 離開 psql
# 輸入 exit 離開 Bastion
步驟 3:建立 SSH 隧道
回到本機,建立 SSH 隧道:
ssh -i ~/.ssh/bastion.pem \
-L 5433:yourdb.xxx.ap-east-1.rds.amazonaws.com:5432 \
-N \
ec2-user@BASTION_PUBLIC_IP
參數說明:
-i ~/.ssh/bastion.pem: 指定 SSH 私鑰-L 5433:yourdb.xxx.ap-east-1.rds.amazonaws.com:5432: 本機埠號轉發- 格式:
本機埠號:目標主機:目標埠號 - 將本機的 5433 轉發到 RDS 的 5432
- 格式:
-N: 不執行遠端命令(只建立隧道)ec2-user@BASTION_PUBLIC_IP: 連線到 Bastion Host
執行後,終端機會卡住(這是正常的,表示隧道正在運作)。
步驟 4:透過隧道連線 RDS
另開一個終端機視窗,執行:
psql -h localhost -p 5433 -U postgres -d postgres
# 或使用 127.0.0.1
psql -h 127.0.0.1 -p 5433 -U postgres -d postgres
重要:
- Host 必須是
localhost或127.0.0.1(不是 RDS endpoint) - Port 必須是
5433(你在-L參數中設定的本機埠號)
成功後會看到:
psql (15.4)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.
postgres=>
背景執行 SSH 隧道
每次都要開一個終端機視窗很麻煩,可以將 SSH 隧道放到背景執行:
# 方法 1: 使用 -f 參數
ssh -f -i ~/.ssh/bastion.pem \
-L 5433:yourdb.xxx.ap-east-1.rds.amazonaws.com:5432 \
-N \
ec2-user@BASTION_PUBLIC_IP
# -f: 在背景執行
# 方法 2: 使用 nohup
nohup ssh -i ~/.ssh/bastion.pem \
-L 5433:yourdb.xxx.ap-east-1.rds.amazonaws.com:5432 \
-N \
ec2-user@BASTION_PUBLIC_IP &
# 檢查隧道是否運作
lsof -i :5433
# 應該會看到 ssh 程序正在監聽 5433 埠號
# 關閉隧道
kill $(lsof -t -i :5433)
使用 SSH Config 簡化設定
編輯 ~/.ssh/config:
# Bastion Host
Host bastion
HostName BASTION_PUBLIC_IP
User ec2-user
IdentityFile ~/.ssh/bastion.pem
ServerAliveInterval 60
ServerAliveCountMax 3
# RDS Tunnel
Host rds-tunnel
HostName localhost
User postgres
LocalForward 5433 yourdb.xxx.ap-east-1.rds.amazonaws.com:5432
ProxyJump bastion
設定後,連線變得更簡單:
# 建立隧道
ssh -N rds-tunnel
# 在另一個終端機連線
psql -h localhost -p 5433 -U postgres -d postgres
連線方式三:Docker 容器環境
在容器化環境中,應用程式通常運行在 Docker 容器內。我們需要從容器內部連線到 RDS。
Docker 網路架構
graph TB
subgraph "Docker Host"
subgraph "Docker Networks"
Bridge[bridge network<br/>172.17.0.0/16]
Custom[custom network<br/>app-network]
end
subgraph "Containers"
App[應用容器<br/>app-server<br/>172.17.0.2]
DB_Client[psql 客戶端容器<br/>postgres-client<br/>172.17.0.3]
end
end
subgraph "AWS"
RDS[RDS PostgreSQL<br/>yourdb.xxx.rds.amazonaws.com]
end
App -->|透過 Docker Host<br/>NAT 到網際網路| RDS
DB_Client -->|透過 Docker Host<br/>NAT 到網際網路| RDS
Bridge -.->|預設網路| App
Custom -.->|自訂網路| DB_Client
style App fill:#e1f5ff
style DB_Client fill:#e1f5ff
style RDS fill:#e1ffe1
情境 1:從現有容器連線
如果你的應用容器已經在運行,可以直接進入容器執行 psql:
# 列出所有運行中的容器
docker ps
# 輸出範例:
# CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
# abc123def456 node:18 "node app" 2 hours ago Up 2 hours app-server
# 進入容器(使用 bash)
docker exec -it app-server bash
# 或使用 sh(如果是 Alpine Linux)
docker exec -it app-server sh
# 在容器內安裝 psql(如果尚未安裝)
# Debian/Ubuntu 基底
apt-get update && apt-get install -y postgresql-client
# Alpine Linux 基底
apk add --no-cache postgresql-client
# 連線到 RDS
psql -h yourdb.xxx.ap-east-1.rds.amazonaws.com -U postgres -d postgres
情境 2:啟動專用的 psql 客戶端容器
如果只是想快速連線查詢,可以使用官方 PostgreSQL 映像檔:
# 方法 1: 互動模式(推薦用於臨時查詢)
docker run -it --rm \
--name postgres-client \
-e PGPASSWORD=your-password \
postgres:15 \
psql -h yourdb.xxx.ap-east-1.rds.amazonaws.com \
-U postgres \
-d postgres
# 參數說明:
# -it: 互動模式
# --rm: 容器結束後自動刪除
# --name: 容器名稱
# -e PGPASSWORD: 設定密碼環境變數(避免每次輸入)
# postgres:15: 使用 PostgreSQL 15 映像檔
# psql ...: 容器啟動後執行的命令
# 方法 2: 進入容器 shell
docker run -it --rm postgres:15 bash
# 在容器內執行
psql -h yourdb.xxx.ap-east-1.rds.amazonaws.com -U postgres -d postgres
情境 3:透過 Docker Compose
在 docker-compose.yml 中定義 psql 客戶端:
version: '3.8'
services:
# 你的應用服務
app:
image: your-app:latest
environment:
- DATABASE_URL=postgresql://postgres:password@yourdb.xxx.ap-east-1.rds.amazonaws.com:5432/postgres
networks:
- app-network
# psql 客戶端(用於管理)
db-client:
image: postgres:15
environment:
- PGHOST=yourdb.xxx.ap-east-1.rds.amazonaws.com
- PGPORT=5432
- PGUSER=postgres
- PGPASSWORD=your-password
- PGDATABASE=postgres
networks:
- app-network
# 保持容器運行
command: tail -f /dev/null
# 或使用 stdin_open 和 tty
stdin_open: true
tty: true
networks:
app-network:
driver: bridge
使用方式:
# 啟動所有服務
docker-compose up -d
# 進入 db-client 容器
docker-compose exec db-client bash
# 在容器內直接執行 psql(環境變數已設定)
psql
# 或從容器外直接執行 SQL
docker-compose exec db-client psql -c "SELECT version();"
# 停止服務
docker-compose down
從容器連線的安全考量
flowchart TD
Start[容器需要連線 RDS] --> A{環境類型?}
A -->|開發環境| B[使用環境變數<br/>PGPASSWORD]
A -->|測試環境| C[使用 Docker Secrets]
A -->|生產環境| D[使用外部 Secret 管理]
B --> B1[docker run -e PGPASSWORD=xxx]
B --> B2[⚠️ 密碼會出現在<br/>docker inspect 中]
C --> C1[建立 Docker Secret]
C1 --> C2[在 service 中掛載 secret]
C2 --> C3[從 /run/secrets/db_password 讀取]
D --> D1[AWS Secrets Manager]
D --> D2[HashiCorp Vault]
D --> D3[Kubernetes Secrets]
D1 --> E[容器啟動時從 API 取得密碼]
D2 --> E
D3 --> E
E --> F[將密碼注入環境變數或檔案]
F --> G[應用程式讀取並連線]
style B2 fill:#ffe1e1
style C fill:#e1f5ff
style D fill:#e1ffe1
連線方式四:Kubernetes Pod 環境
在 Kubernetes 叢集中(例如 AWS EKS),Pod 與 RDS 之間的連線涉及更複雜的網路架構。
Kubernetes 與 RDS 網路架構
graph TB
subgraph "AWS VPC"
subgraph "EKS Cluster Subnets"
subgraph "Pod Network (CNI)"
Pod1[Pod: app-xxx<br/>10.244.1.5]
Pod2[Pod: psql-client<br/>10.244.2.8]
end
NodeSG[Node Security Group]
Node[EC2 Worker Node<br/>10.0.3.100]
end
subgraph "RDS Subnet"
RDS[RDS PostgreSQL<br/>10.0.2.100:5432]
RDSSG[RDS Security Group]
end
subgraph "VPC Settings"
RT[Route Table]
NAT[NAT Gateway<br/>公有子網]
end
end
Pod1 -->|1. DNS 查詢| CoreDNS[CoreDNS<br/>叢集 DNS]
CoreDNS -->|2. 回傳 RDS IP| Pod1
Pod1 -->|3. 封包| Node
Node -->|4. SNAT<br/>來源 IP 變更為 Node IP| RDS
Pod2 -.->|相同流程| Node
NodeSG -.->|套用| Node
RDSSG -.->|套用| RDS
RDS -.->|檢查來源 IP| RDSSG
style Pod1 fill:#e1f5ff
style Pod2 fill:#e1f5ff
style RDS fill:#e1ffe1
style Node fill:#fff3e1
關鍵概念:Pod 到 RDS 的連線路徑
Pod Network(CNI):
- EKS 使用 VPC CNI,Pod 獲得 VPC 子網的 IP 位址
- Pod IP 範圍通常與 Node 所在的子網相同
Security Group 設定:
- RDS Security Group 必須允許來自 Node Security Group 的連線
- 因為 Pod 的流量會透過 Node 轉發(SNAT)
DNS 解析:
- Pod 內可以直接使用 RDS Endpoint
- CoreDNS 會將其解析為 RDS 的私有 IP
情境 1:從現有 Pod 連線
# 列出所有 Pod
kubectl get pods -n default
# 輸出範例:
# NAME READY STATUS RESTARTS AGE
# app-deployment-abc123 1/1 Running 0 2h
# redis-xyz789 1/1 Running 0 5h
# 進入 Pod(執行 bash)
kubectl exec -it app-deployment-abc123 -n default -- bash
# 或使用 sh
kubectl exec -it app-deployment-abc123 -n default -- sh
# 在 Pod 內安裝 psql(如果尚未安裝)
apt-get update && apt-get install -y postgresql-client
# 連線到 RDS
psql -h yourdb.xxx.ap-east-1.rds.amazonaws.com -U postgres -d postgres
# 離開 Pod
exit
情境 2:直接從 Pod 執行單一命令
不需要進入 Pod,直接執行 psql 命令:
# 執行單一 SQL 查詢
kubectl exec -it app-deployment-abc123 -n default -- \
psql -h yourdb.xxx.ap-east-1.rds.amazonaws.com \
-U postgres \
-d postgres \
-c "SELECT version();"
# 執行多個命令
kubectl exec -it app-deployment-abc123 -n default -- \
psql -h yourdb.xxx.ap-east-1.rds.amazonaws.com \
-U postgres \
-d postgres \
-c "SELECT * FROM users LIMIT 5;"
情境 3:部署專用的 psql 客戶端 Pod
建立一個臨時的 PostgreSQL 客戶端 Pod:
kubectl run psql-client \
--rm -i --tty \
--image=postgres:15 \
--namespace=default \
--env="PGHOST=yourdb.xxx.ap-east-1.rds.amazonaws.com" \
--env="PGPORT=5432" \
--env="PGUSER=postgres" \
--env="PGPASSWORD=your-password" \
--env="PGDATABASE=postgres" \
--restart=Never \
--command -- psql
# 參數說明:
# --rm: Pod 結束後自動刪除
# -i --tty: 互動模式
# --image: 使用的容器映像
# --env: 設定環境變數
# --restart=Never: 不自動重啟
# --command -- psql: 執行 psql 命令
成功後會直接進入 psql 互動介面:
psql (15.4)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.
postgres=>
離開 psql 後,Pod 會自動刪除。
情境 4:使用 Kubernetes Deployment
建立一個長期運行的 psql 客戶端 Deployment:
# psql-client-deployment.yaml
apiVersion: v1
kind: Secret
metadata:
name: rds-credentials
namespace: default
type: Opaque
stringData:
PGHOST: "yourdb.xxx.ap-east-1.rds.amazonaws.com"
PGPORT: "5432"
PGUSER: "postgres"
PGPASSWORD: "your-password"
PGDATABASE: "postgres"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: psql-client
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: psql-client
template:
metadata:
labels:
app: psql-client
spec:
containers:
- name: postgres-client
image: postgres:15
envFrom:
- secretRef:
name: rds-credentials
command:
- sh
- -c
- |
# 安裝額外工具
apt-get update && apt-get install -y vim curl
# 保持容器運行
tail -f /dev/null
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
部署並使用:
# 部署
kubectl apply -f psql-client-deployment.yaml
# 等待 Pod 就緒
kubectl wait --for=condition=ready pod -l app=psql-client --timeout=60s
# 進入 Pod
kubectl exec -it deployment/psql-client -- bash
# 在 Pod 內執行 psql(環境變數已設定)
psql
# 或從外部直接執行查詢
kubectl exec deployment/psql-client -- psql -c "SELECT current_database();"
# 刪除部署
kubectl delete -f psql-client-deployment.yaml
使用 Kubernetes Jobs 執行一次性查詢
如果只需要執行一次性的資料庫操作(如資料遷移),可以使用 Job:
# db-migration-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: db-migration
namespace: default
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migration
image: postgres:15
env:
- name: PGHOST
valueFrom:
secretKeyRef:
name: rds-credentials
key: PGHOST
- name: PGUSER
valueFrom:
secretKeyRef:
name: rds-credentials
key: PGUSER
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: rds-credentials
key: PGPASSWORD
- name: PGDATABASE
valueFrom:
secretKeyRef:
name: rds-credentials
key: PGDATABASE
command:
- sh
- -c
- |
# 執行資料遷移 SQL
psql <<EOF
CREATE TABLE IF NOT EXISTS migrations (
id SERIAL PRIMARY KEY,
version VARCHAR(50) NOT NULL,
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO migrations (version) VALUES ('v1.0.0');
EOF
echo "Migration completed successfully"
backoffLimit: 3
執行 Job:
# 建立 Job
kubectl apply -f db-migration-job.yaml
# 查看 Job 狀態
kubectl get jobs
# 查看 Job 日誌
kubectl logs job/db-migration
# 刪除 Job
kubectl delete job db-migration
psql 互動介面完整命令參考
進入 psql 後,你會看到 postgres=> 或 dbname=> 提示符。以下是完整的命令參考。
Meta-Commands(以 \ 開頭的命令)
這些命令不是 SQL,而是 psql 特有的管理命令:
graph TB
Start[psql 命令類型] --> A[Meta-Commands<br/>backslash 命令]
Start --> B[SQL Statements<br/>標準 SQL]
A --> A1[\l 列出資料庫]
A --> A2[\c 切換資料庫]
A --> A3[\dt 列出資料表]
A --> A4[\d 描述物件]
A --> A5[\x 切換顯示模式]
A --> A6[\q 離開]
B --> B1[SELECT 查詢]
B --> B2[INSERT 新增]
B --> B3[UPDATE 更新]
B --> B4[DELETE 刪除]
B --> B5[CREATE 建立]
B --> B6[DROP 刪除]
style A fill:#e1f5ff
style B fill:#e1ffe1
資料庫管理命令
| 命令 | 說明 | 範例 |
|---|---|---|
\l 或 \list | 列出所有資料庫 | \l |
\l+ | 列出資料庫(包含大小和描述) | \l+ |
\c dbname | 切換到指定資料庫 | \c my_database |
\conninfo | 顯示當前連線資訊 | \conninfo |
\du | 列出所有使用者(角色) | \du |
\du+ | 列出使用者詳細資訊 | \du+ |
範例輸出:
-- 列出所有資料庫
postgres=> \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+-------------+-------------+-----------------------
my_db | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
(4 rows)
-- 切換資料庫
postgres=> \c my_db
You are now connected to database "my_db" as user "postgres".
my_db=>
-- 顯示連線資訊
my_db=> \conninfo
You are connected to database "my_db" as user "postgres" on host "yourdb.xxx.rds.amazonaws.com" at port "5432".
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Schema 和資料表管理
| 命令 | 說明 | 範例 |
|---|---|---|
\dn | 列出所有 schema | \dn |
\dt | 列出當前 schema 的資料表 | \dt |
\dt *.* | 列出所有 schema 的資料表 | \dt *.* |
\dt public.* | 列出 public schema 的資料表 | \dt public.* |
\dt+ | 列出資料表(包含大小) | \dt+ |
\d tablename | 描述資料表結構 | \d users |
\d+ tablename | 詳細描述資料表 | \d+ users |
\di | 列出索引 | \di |
\dv | 列出 views | \dv |
\df | 列出函式 | \df |
\ds | 列出 sequences | \ds |
範例輸出:
-- 列出資料表
my_db=> \dt
List of relations
Schema | Name | Type | Owner
--------+---------------+-------+----------
public | users | table | postgres
public | posts | table | postgres
public | comments | table | postgres
(3 rows)
-- 描述資料表結構
my_db=> \d users
Table "public.users"
Column | Type | Collation | Nullable | Default
------------+-----------------------------+-----------+----------+-----------------------------------
id | integer | | not null | nextval('users_id_seq'::regclass)
email | character varying(255) | | not null |
name | character varying(100) | | |
created_at | timestamp without time zone | | | CURRENT_TIMESTAMP
Indexes:
"users_pkey" PRIMARY KEY, btree (id)
"users_email_key" UNIQUE CONSTRAINT, btree (email)
Referenced by:
TABLE "posts" CONSTRAINT "posts_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
顯示模式控制
| 命令 | 說明 | 範例 |
|---|---|---|
\x | 切換擴展顯示模式(垂直顯示) | \x |
\x auto | 自動判斷使用哪種顯示模式 | \x auto |
\pset format | 設定輸出格式 | \pset format wrapped |
\timing | 開啟/關閉查詢時間顯示 | \timing |
範例:
-- 預設水平顯示
my_db=> SELECT * FROM users LIMIT 1;
id | email | name | created_at
----+------------------+--------+--------------------
1 | user@example.com | John | 2024-01-01 10:00:00
(1 row)
-- 開啟擴展模式(垂直顯示)
my_db=> \x
Expanded display is on.
my_db=> SELECT * FROM users LIMIT 1;
-[ RECORD 1 ]------------------------
id | 1
email | user@example.com
name | John
created_at | 2024-01-01 10:00:00
-- 關閉擴展模式
my_db=> \x
Expanded display is off.
-- 開啟查詢時間顯示
my_db=> \timing
Timing is on.
my_db=> SELECT COUNT(*) FROM users;
count
-------
1000
(1 row)
Time: 5.234 ms
SQL 查詢命令
以下是常用的 SQL 查詢範例:
-- 查看資料庫版本
SELECT version();
-- 查看當前資料庫
SELECT current_database();
-- 查看當前使用者
SELECT current_user;
-- 查看所有資料表(SQL 方式)
SELECT tablename FROM pg_tables WHERE schemaname = 'public';
-- 查詢前 N 筆資料
SELECT * FROM users LIMIT 5;
-- 查詢特定欄位
SELECT id, email, name FROM users;
-- 條件查詢
SELECT * FROM users WHERE email = 'user@example.com';
-- 模糊查詢
SELECT * FROM users WHERE name LIKE 'John%';
-- 排序
SELECT * FROM users ORDER BY created_at DESC;
-- 計數
SELECT COUNT(*) FROM users;
-- 分組統計
SELECT DATE(created_at), COUNT(*)
FROM users
GROUP BY DATE(created_at)
ORDER BY DATE(created_at) DESC;
-- 查詢不重複值
SELECT DISTINCT email FROM users;
-- JOIN 查詢
SELECT u.name, p.title
FROM users u
INNER JOIN posts p ON u.id = p.user_id
LIMIT 10;
資料更新命令
-- 更新單一記錄
UPDATE users
SET name = 'John Doe'
WHERE id = 1;
-- 更新多筆記錄
UPDATE users
SET email = LOWER(email)
WHERE email LIKE '%@EXAMPLE.COM';
-- 批次更新
UPDATE users
SET updated_at = CURRENT_TIMESTAMP
WHERE created_at < '2024-01-01';
-- 刪除記錄
DELETE FROM users WHERE id = 999;
-- 刪除符合條件的記錄
DELETE FROM users WHERE created_at < '2020-01-01';
-- 清空資料表(保留結構)
TRUNCATE TABLE users;
-- 插入記錄
INSERT INTO users (email, name)
VALUES ('new@example.com', 'New User');
-- 插入多筆記錄
INSERT INTO users (email, name) VALUES
('user1@example.com', 'User 1'),
('user2@example.com', 'User 2'),
('user3@example.com', 'User 3');
-- 插入並回傳結果
INSERT INTO users (email, name)
VALUES ('test@example.com', 'Test User')
RETURNING id, email;
交易控制
-- 開始交易
BEGIN;
-- 執行多個操作
UPDATE users SET name = 'Updated' WHERE id = 1;
INSERT INTO audit_log (action, user_id) VALUES ('update', 1);
-- 提交交易
COMMIT;
-- 或回滾交易
ROLLBACK;
-- 設定儲存點
BEGIN;
UPDATE users SET name = 'First Update' WHERE id = 1;
SAVEPOINT sp1;
UPDATE users SET name = 'Second Update' WHERE id = 2;
-- 回滾到儲存點
ROLLBACK TO SAVEPOINT sp1;
-- 只有第一個更新會生效
COMMIT;
進階查詢技巧
-- 查看資料表大小
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
-- 查看資料庫大小
SELECT
datname,
pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
ORDER BY pg_database_size(datname) DESC;
-- 查看正在執行的查詢
SELECT
pid,
usename,
application_name,
client_addr,
state,
query,
query_start
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY query_start;
-- 終止慢查詢
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'active'
AND query_start < now() - interval '5 minutes';
-- 查看索引使用情況
SELECT
schemaname,
tablename,
indexname,
idx_scan,
idx_tup_read,
idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;
-- 查看未使用的索引
SELECT
schemaname,
tablename,
indexname
FROM pg_stat_user_indexes
WHERE idx_scan = 0
AND indexname NOT LIKE '%_pkey';
匯出與匯入
-- 匯出查詢結果到 CSV
\copy (SELECT * FROM users) TO '/tmp/users.csv' WITH CSV HEADER;
-- 從 CSV 匯入資料
\copy users FROM '/tmp/users.csv' WITH CSV HEADER;
-- 匯出整個資料表
\copy users TO '/tmp/users_backup.csv' WITH CSV HEADER;
注意:\copy 在客戶端執行,檔案路徑是相對於執行 psql 的機器。COPY(沒有反斜線)在伺服器端執行,檔案路徑是相對於資料庫伺服器。
其他實用命令
| 命令 | 說明 | 範例 |
|---|---|---|
\? | 顯示所有 backslash 命令 | \? |
\h SQL | 顯示 SQL 語法說明 | \h SELECT |
\q | 離開 psql | \q |
\e | 開啟編輯器編輯最後的命令 | \e |
\i filename | 執行檔案中的 SQL | \i /path/to/script.sql |
\o filename | 將輸出重導向到檔案 | \o /tmp/output.txt |
\! command | 執行 shell 命令 | \! ls -la |
\password | 更改密碼 | \password postgres |
\echo text | 顯示文字 | \echo 'Hello World' |
安全最佳實踐
密碼管理
flowchart TD
Start[資料庫密碼管理] --> A{環境類型?}
A -->|本機開發| B[.pgpass 檔案]
A -->|容器化環境| C[環境變數 + Secret]
A -->|Kubernetes| D[Kubernetes Secrets]
A -->|生產環境| E[Secret 管理服務]
B --> B1[~/.pgpass<br/>權限 0600]
B1 --> B2[不要提交到 Git]
C --> C1[Docker Secrets<br/>或掛載檔案]
C1 --> C2[不要硬編碼在 Dockerfile]
D --> D1[使用 Sealed Secrets<br/>或 External Secrets]
D1 --> D2[以 Volume 形式掛載]
E --> E1[AWS Secrets Manager]
E --> E2[HashiCorp Vault]
E --> E3[Azure Key Vault]
E1 --> F[應用啟動時<br/>從 API 取得密碼]
E2 --> F
E3 --> F
style B2 fill:#ffe1e1
style C2 fill:#ffe1e1
style E fill:#e1ffe1
1. 使用 IAM 認證(AWS RDS)
AWS RDS 支援使用 IAM 角色進行認證,無需管理密碼:
# 啟用 RDS IAM 認證
# AWS Console > RDS > Modify > Database authentication > Enable IAM authentication
# 在 EC2/EKS 上使用 IAM Role 產生臨時 token
TOKEN=$(aws rds generate-db-auth-token \
--hostname yourdb.xxx.ap-east-1.rds.amazonaws.com \
--port 5432 \
--username iamuser \
--region ap-east-1)
# 使用 token 連線(15 分鐘有效)
psql "host=yourdb.xxx.ap-east-1.rds.amazonaws.com \
port=5432 \
dbname=postgres \
user=iamuser \
password=$TOKEN \
sslmode=require"
2. 強制使用 SSL 連線
-- 檢查是否使用 SSL
SELECT ssl_is_used();
-- 查看 SSL 版本
\conninfo
-- 強制要求 SSL(在 psql 連線字串中)
psql "host=yourdb.xxx.rds.amazonaws.com \
port=5432 \
dbname=postgres \
user=postgres \
sslmode=require"
-- sslmode 選項:
-- disable: 不使用 SSL
-- allow: 嘗試 SSL,失敗則降級
-- prefer: 優先使用 SSL(預設)
-- require: 強制 SSL,不驗證憑證
-- verify-ca: 強制 SSL,驗證憑證
-- verify-full: 強制 SSL,驗證憑證和主機名
3. 最小權限原則
-- 建立唯讀使用者
CREATE ROLE readonly WITH LOGIN PASSWORD 'secure-password';
-- 授予連線權限
GRANT CONNECT ON DATABASE my_db TO readonly;
-- 授予 schema 使用權限
GRANT USAGE ON SCHEMA public TO readonly;
-- 授予所有資料表的 SELECT 權限
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
-- 對未來建立的資料表也生效
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO readonly;
-- 建立應用程式使用者(讀寫權限,但不能刪除資料表)
CREATE ROLE appuser WITH LOGIN PASSWORD 'secure-password';
GRANT CONNECT ON DATABASE my_db TO appuser;
GRANT USAGE ON SCHEMA public TO appuser;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO appuser;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO appuser;
4. 審計與監控
-- 啟用查詢日誌(AWS RDS Parameter Group)
-- log_statement = 'all' -- 記錄所有語句(效能影響大)
-- log_statement = 'ddl' -- 只記錄 DDL(CREATE, ALTER, DROP)
-- log_statement = 'mod' -- 記錄資料修改(INSERT, UPDATE, DELETE)
-- 查看最近的連線
SELECT
usename,
application_name,
client_addr,
backend_start,
state
FROM pg_stat_activity
ORDER BY backend_start DESC;
-- 查看失敗的連線嘗試(需啟用 log_connections)
-- 在 CloudWatch Logs 中查看:
-- /aws/rds/instance/your-db/postgresql
5. 網路隔離
graph TB
subgraph "推薦架構(生產環境)"
App1[應用程式] -->|私有連線| RDS1[RDS in Private Subnet]
Admin1[管理員] -->|SSH Tunnel| Bastion1[Bastion Host]
Bastion1 -->|私有連線| RDS1
end
subgraph "不推薦架構"
App2[應用程式] -->|公網連線<br/>⚠️ 風險| RDS2[RDS Publicly Accessible]
Admin2[管理員] -->|直接連線<br/>⚠️ 風險| RDS2
end
style RDS1 fill:#e1ffe1
style RDS2 fill:#ffe1e1
style Bastion1 fill:#e1f5ff
最佳實踐檢查清單:
- ✅ RDS 部署在私有子網
- ✅ Publicly Accessible 設為 No
- ✅ Security Group 限制來源 IP 或 Security Group
- ✅ 使用 Bastion Host 或 VPN 連線
- ✅ 啟用 SSL 連線(sslmode=require)
- ✅ 使用 IAM 認證(如可行)
- ✅ 定期輪換密碼
- ✅ 使用最小權限原則
- ✅ 啟用自動備份
- ✅ 啟用 Multi-AZ(高可用性)
- ✅ 啟用 CloudWatch 監控
- ✅ 啟用 Performance Insights
常見問題排查
問題 1:連線逾時(Connection Timeout)
flowchart TD
Start[連線逾時] --> A{連線方式?}
A -->|直接連線| B[檢查 Security Group]
A -->|SSH 隧道| C[檢查 Bastion 連線]
A -->|Kubernetes| D[檢查 Pod 網路]
B --> B1{Inbound Rules 是否允許?}
B1 -->|否| B2[新增規則:<br/>Type: PostgreSQL<br/>Source: My IP]
B1 -->|是| B3[檢查 VPC NACL]
C --> C1{可以 SSH 到 Bastion?}
C1 -->|否| C2[檢查 Bastion SG<br/>允許 Port 22]
C1 -->|是| C3{Bastion 可以連 RDS?}
C3 -->|否| C4[檢查 RDS SG<br/>允許 Bastion SG]
D --> D1[檢查 Node SG]
D1 --> D2[RDS SG 必須允許<br/>Node SG 的 5432]
B2 --> End[測試連線]
B3 --> End
C2 --> End
C4 --> End
D2 --> End
style B2 fill:#e1ffe1
style C2 fill:#e1ffe1
style C4 fill:#e1ffe1
style D2 fill:#e1ffe1
診斷命令:
# 1. 測試網路連通性
# 從本機測試
telnet yourdb.xxx.ap-east-1.rds.amazonaws.com 5432
# 或使用 nc
nc -zv yourdb.xxx.ap-east-1.rds.amazonaws.com 5432
# 成功會顯示:
# Connection to yourdb.xxx.ap-east-1.rds.amazonaws.com 5432 port [tcp/postgresql] succeeded!
# 2. DNS 解析測試
nslookup yourdb.xxx.ap-east-1.rds.amazonaws.com
dig yourdb.xxx.ap-east-1.rds.amazonaws.com
# 3. 路由追蹤
traceroute yourdb.xxx.ap-east-1.rds.amazonaws.com
# 4. 檢查本機防火牆(macOS)
sudo pfctl -sr | grep 5432
# 5. 從 Bastion Host 測試
ssh -i ~/.ssh/bastion.pem ec2-user@BASTION_IP
telnet yourdb.xxx.ap-east-1.rds.amazonaws.com 5432
問題 2:認證失敗(Authentication Failed)
psql: error: connection to server at "yourdb.xxx.rds.amazonaws.com" (10.0.2.100), port 5432 failed: FATAL: password authentication failed for user "postgres"
可能原因與解決方法:
# 1. 密碼錯誤
# 從 AWS Secrets Manager 取得正確密碼
aws secretsmanager get-secret-value \
--secret-id rds-db-credentials \
--query SecretString \
--output text | jq -r .password
# 2. 使用者不存在
# 連線為 master user,建立新使用者
psql -h ... -U postgres -c "CREATE USER myuser WITH PASSWORD 'mypassword';"
# 3. 資料庫不存在
# 列出所有資料庫
psql -h ... -U postgres -l
# 建立資料庫
psql -h ... -U postgres -c "CREATE DATABASE my_db;"
# 4. IP 位址被拒絕(pg_hba.conf)
# RDS 的 pg_hba.conf 由 AWS 管理,但可以透過 Parameter Group 調整
# 檢查連線是否使用 SSL
psql "host=... sslmode=require user=postgres dbname=postgres"
問題 3:SSL 連線問題
psql: error: connection to server at "yourdb.xxx.rds.amazonaws.com" failed: SSL connection has been closed unexpectedly
解決方法:
# 1. 下載 RDS SSL 憑證
wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
# 2. 使用憑證連線
psql "host=yourdb.xxx.rds.amazonaws.com \
port=5432 \
dbname=postgres \
user=postgres \
sslmode=verify-full \
sslrootcert=global-bundle.pem"
# 3. 或降低 SSL 要求(不建議用於生產環境)
psql "host=yourdb.xxx.rds.amazonaws.com \
port=5432 \
dbname=postgres \
user=postgres \
sslmode=require"
問題 4:Kubernetes Pod 無法連線
# 1. 檢查 Pod 是否可以解析 RDS Endpoint
kubectl exec -it pod-name -- nslookup yourdb.xxx.ap-east-1.rds.amazonaws.com
# 2. 檢查 Pod 是否可以連通 RDS 埠號
kubectl exec -it pod-name -- nc -zv yourdb.xxx.ap-east-1.rds.amazonaws.com 5432
# 3. 檢查 Node Security Group
# AWS Console > EC2 > Security Groups
# 找到 EKS Node 的 SG,記下 SG ID (例如 sg-0123456789abcdef0)
# 4. 確認 RDS Security Group 允許 Node SG
# AWS Console > RDS > Databases > Your DB > Connectivity & security
# VPC security groups > Edit inbound rules
# 新增規則:
# Type: PostgreSQL
# Source: sg-0123456789abcdef0 (Node SG)
# 5. 檢查環境變數是否正確設定
kubectl exec -it pod-name -- env | grep PG
問題 5:連線數過多
psql: error: FATAL: sorry, too many clients already
解決方法:
-- 查看當前連線數
SELECT COUNT(*) FROM pg_stat_activity;
-- 查看最大連線數限制
SHOW max_connections;
-- 查看各使用者的連線數
SELECT
usename,
COUNT(*) as connection_count
FROM pg_stat_activity
GROUP BY usename
ORDER BY connection_count DESC;
-- 終止閒置連線
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle'
AND state_change < now() - interval '10 minutes';
-- 增加最大連線數(需修改 RDS Parameter Group)
-- AWS Console > RDS > Parameter groups > Edit parameters
-- 找到 max_connections,修改為更大的值(例如 200)
-- 套用後需要重啟 RDS 實例
效能優化技巧
連線池(Connection Pooling)
對於高流量應用,使用連線池可以大幅提升效能:
# 安裝 PgBouncer(在應用伺服器或 Bastion Host)
sudo apt-get install pgbouncer
# 設定 /etc/pgbouncer/pgbouncer.ini
[databases]
my_db = host=yourdb.xxx.rds.amazonaws.com port=5432 dbname=my_db
[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20
# 應用程式連到 PgBouncer,而非直接連 RDS
psql -h pgbouncer-host -p 6432 -U postgres -d my_db
查詢優化
-- 使用 EXPLAIN ANALYZE 分析查詢
EXPLAIN ANALYZE
SELECT u.name, p.title
FROM users u
JOIN posts p ON u.id = p.user_id
WHERE u.email = 'user@example.com';
-- 建立索引加速查詢
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_posts_user_id ON posts(user_id);
-- 檢視慢查詢(需啟用 log_min_duration_statement)
-- 在 RDS Parameter Group 設定:
-- log_min_duration_statement = 1000 (記錄超過 1 秒的查詢)
-- 在 CloudWatch Logs 中查看慢查詢日誌
總結
本文深入探討了如何在各種環境中連線 AWS RDS PostgreSQL:
- 直接連線:適用於開發測試,需正確設定 Security Group
- SSH 隧道:生產環境最佳實踐,透過 Bastion Host 安全連線
- Docker 容器:使用官方 PostgreSQL 映像檔或在應用容器中安裝 psql
- Kubernetes Pod:理解 Pod 網路與 Node Security Group 的關係
- psql 命令:完整的互動介面命令參考,從基礎到進階
- 安全實踐:IAM 認證、SSL 連線、最小權限、網路隔離
- 問題排查:系統化診斷連線、認證、網路問題
關鍵要點:
- ✅ 生產環境永遠使用私有子網 + Bastion Host
- ✅ 使用
.pgpass檔案或 Secret 管理服務儲存密碼 - ✅ 強制使用 SSL 連線(sslmode=require)
- ✅ 遵循最小權限原則,不同用途使用不同帳號
- ✅ 啟用監控與審計,定期檢視連線與查詢日誌
- ✅ 使用連線池減少資料庫負擔
掌握這些技巧後,你就能在任何環境中安全、高效地操作 AWS RDS PostgreSQL!