
排查 Amazon EKS 環境中 Redis 連線逾時 (Timeout) 及除錯
Redis 已成為現代應用程式架構的基石,作為高速緩存、會話管理和 pub/sub 訊息傳輸等多功能記憶體資料儲存系統。然而,當與容器化應用程式一起部署在 Amazon Elastic Kubernetes Service (EKS) 環境中時,團隊經常遇到難以診斷和解決的連線問題。這些問題包括高流量期間的間歇性超時、叢集擴充時的連線問題、跨命名空間的效能不一致,以及突發的連線中斷。
簡介
Redis 連線問題和超時特別令人困擾,因為這些問題多半在高負載時才會間歇性出現,且難以在開發環境中重現。當團隊將系統遷移至 EKS 或擴充 Redis 使用規模時,經常會遇到以下問題:
- 高流量期間發生間歇性連線逾時
- 叢集擴充後出現連線問題
- 跨 Kubernetes 命名空間的效能表現不一致
- 應用程式 Pod 與 Redis 端點之間發生意外連線中斷
這些問題可能導致使用者體驗下降,嚴重時甚至造成服務完全中斷。因此,確保 Redis 連線的穩定性成為一項關鍵的營運任務。本文將提供完整指南,協助您診斷、排除及解決 EKS 環境中的 Redis 連線問題。
Kubernetes 環境中 Redis 超時的常見原因
在深入探討疑難排解技巧之前,先了解容器化環境中造成 Redis 超時的主要原因至關重要:
Connection Pool 用盡
現代應用程式通常採用連線池來有效管理 Redis 連線。在 Kubernetes 環境中,隨著 Pod 動態擴充,連線池會快速倍增:
- 每個 Pod 維護獨立的連線池(一般介於 5-50 個連線)
- Pod 擴充時,總連線數可能超出 Redis 的限制
- Redis 的預設連線上限(10,000 個連線1)雖然看似充裕,但系統效能通常在達到此限制前就會開始下降
以下是連線池用盡時的應用程式日誌範例:
2023-08-12T14:25:18.456Z ERROR [app-service] - Redis connection error: JedisPoolExhaustedException: Could not get a resource from the pool
網路延遲與不穩定性
Kubernetes 增加了額外的網路層,可能會造成延遲:
- 容器網路介面 (CNI) 路由開銷
- 如果 Pod 和 Redis 位於不同的 AWS Availability Zone,則會產生跨可用性區流量
- DNS 解析出現延遲或解析失敗
- Pod 被驅逐時引發大量連線重新建立
超時配置不一致
技術堆疊中的各層超時設定若不一致,往往會引發問題:
- Redis 伺服器本身的超時設定
- 用戶端程式庫對連線和操作各自的超時設定
- Kubernetes 的存活性與就緒性探測的超時設定
- 負載平衡器或代理層的超時設定
資源限制
Redis 或客戶端的資源受限可能導致連線異常:
- Redis 伺服器記憶體壓力過大,造成資料驅逐或效能降低
- 應用程式 Pod 的 CPU 限制影響 Redis 回應的處理速度
- 節點或 Pod 層級的網路流量限制
EKS Pod 與 Redis 之間的網路連線疑難排解
面對連線問題時,系統化的網路故障排除至關重要。從基礎的 TCP/IP 連線測試到應用程式層級的 Redis 指令驗證,每一層都需要仔細檢查。同時監控系統指標和日誌,以找出可能的瓶頸或故障點。透過這種結構化的排除流程,能更有效地找出並解決 EKS 環境中的 Redis 連線問題。
以下列舉幾個最常用來檢查和解決 Redis 連線問題的診斷步驟。從基礎連線測試到進階監控工具的使用,這些步驟將協助您有系統地找出問題所在。
基本連接測試
讓我們從基本的驗證連線開始:
# 在相同命名空間建立除錯用的 Pod(有時候可能需要部署到特定節點)
kubectl run redis-debug --rm -it --image=redis:alpine -- sh
# 測試基本連線能力
nc -zv my-redis-master.xxxx.ng.0001.use1.cache.amazonaws.com 6379
# 用 Redis CLI 做簡單測試
redis-cli -h my-redis-master.xxxx.ng.0001.use1.cache.amazonaws.com ping
# 檢查 DNS 解析是否正常
nslookup my-redis-master.xxxx.ng.0001.use1.cache.amazonaws.com
# 使用 tcpdump 觀察 TCP 連線的細節
tcpdump -i any port 6379 -vv
安全組 (Security Group) 組態
對於 ElastiCache Redis 或 EC2 託管的 Redis,Security Group 通常是造成連線問題的主要原因:
# 找出指派給 EKS 節點的安全群組
NODE_SG=$(aws eks describe-nodegroup --cluster-name my-cluster \\
--nodegroup-name my-nodegroup --query 'nodegroup.resources.remoteAccessSecurityGroup' \\
--output text)
# 確保 Redis 的安全群組允許來自節點安全群組的流量
aws ec2 authorize-security-group-ingress \\
--group-id sg-0123456789abcdef0 \\ # Redis 安全群組
--source-group $NODE_SG \\
--protocol tcp \\
--port 6379
網路政策 (Network Policy) 分析
如果您使用 Kubernetes 網路政策 (Network Policy),驗證它們是否阻擋 Redis 流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-redis-egress
namespace: application
spec:
podSelector:
matchLabels:
app: my-application
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/16 # Subnet containing Redis
ports:
- protocol: TCP
port: 6379
VPC 與子網路路由
對於複雜的 VPC 配置,驗證 EKS 子網路與 Redis 之間的路由和跨可用性區域的連線,確保應用程式可以可靠地訪問 Redis 實例。這需要檢查 VPC 內的路由表配置,以及任何 NAT Gateway 或 Transit Gateway 的設定狀態。此外,還要注意子網路的 CIDR 範圍是否有重疊的問題:
# 檢查 Pod 是否在預期的子網路中
kubectl get pods -o wide
# 檢查這些子網路關聯的路由表設定
aws ec2 describe-route-tables --filters "Name=association.subnet-id,Values=subnet-12345"
# 如果 Redis 在不同的 VPC,檢查 VPC Peering 連線狀態
aws ec2 describe-vpc-peering-connections
容器化環境中 Redis 用戶端的最佳配置實務
正確配置 Redis 用戶端可大幅提升 Kubernetes 環境中的可靠性。以下是針對不同語言和框架的 Redis 用戶端配置最佳實務,這些配置可以協助您建立更穩定和可靠的 Redis 連線。這些設定包括連線池管理、超時處理、錯誤重試策略等關鍵元素。透過遵循這些最佳實務,您可以大幅減少連線問題並提高應用程式的可用性。
Connection Pool
除了使用預設設定,您也可以根據工作負載明確配置 Connection Pool 2:
// Java example with Lettuce client
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.poolConfig(poolConfig())
.commandTimeout(Duration.ofMillis(500))
.shutdownTimeout(Duration.ZERO)
.build();
return new LettuceConnectionFactory(redisStandaloneConfig(), clientConfig);
}
@Bean
public GenericObjectPoolConfig poolConfig() {
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMaxTotal(20); // Max connections per pod
config.setMaxIdle(10); // Connections to maintain when idle
config.setMinIdle(5); // Minimum idle connections to maintain
config.setTestOnBorrow(true); // Validate connections when borrowed
config.setTestWhileIdle(true); // Periodically test idle connections
config.setMaxWait(Duration.ofMillis(1000)); // Max wait for connection
return config;
}
適用於 Node.js 應用程式:
// Node.js with ioredis
const Redis = require('ioredis');
const redis = new Redis({
host: process.env.REDIS_HOST,
port: 6379,
password: process.env.REDIS_PASSWORD,
db: 0,
maxRetriesPerRequest: 3,
connectTimeout: 1000,
commandTimeout: 500,
retryStrategy(times) {
const delay = Math.min(times * 50, 2000);
return delay;
}
});
逾時 (Timeout) 和重試
根據您的應用程式需求,適當地設定逾時時間:
# Python with redis-py
import redis
from redis.retry import Retry
retry = Retry(ExponentialBackoff(), 3)
redis_client = redis.Redis(
host=os.environ.get('REDIS_HOST'),
port=6379,
socket_timeout=1.0, # Operation timeout
socket_connect_timeout=1.0, # Connection timeout
retry_on_timeout=True,
retry=retry,
health_check_interval=30 # Verify connections every 30 seconds
)
Pod 生命週期中的優雅連線處理
在 Pod 啟動和關閉期間適當管理連線非常重要,但卻經常被忽略:
// Java Spring Boot example
@PreDestroy
public void cleanupRedisConnections() {
if (redisConnectionFactory instanceof LettuceConnectionFactory) {
((LettuceConnectionFactory) redisConnectionFactory).destroy();
log.info("Cleaned up Redis connections before pod termination");
}
}
對於 Kubernetes 部署,配置適當的終止寬限期:
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-client-app
spec:
template:
spec:
terminationGracePeriodSeconds: 30
containers:
- name: app
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"] # Allow time for connection cleanup
影響 Redis 連線的 EKS 網路概念
要解決 Redis 連線問題,我們可以先搞清楚 EKS 網路是怎麼運作的。這其實沒那麼複雜,主要包含幾個部分:AWS VPC CNI 外掛負責處理 Pod 的網路連線、Security Group 幫你管理 Pod 和 Redis 之間的連線權限、服務發現機制讓你可以找到 Redis 在哪裡,還有子網路設定確保你的 Pod 可以連到不同區域的 Redis 叢集。
AWS VPC CNI 概要
Amazon VPC CNI 外掛程式是 EKS 的預設網路解決方案,具有幾個重要的特性:
- 每個 Pod 都會收到一個真正的 VPC IP 位址
- Pod IP 位址來自於節點的子網路
- 根據實體類型,每個節點都有 IP 位址限制
- 節點的安全群組適用於 Pod 流量
此架構表示您的 Pod 可直接與 Redis 通訊,無需 NAT,簡化了安全設定,但需要適當的子網路和安全群組設定。
Pod 密度與 IP 位址耗盡
EKS 節點可使用的 IP 位址數量有限制3:
實體類型 | 最大 Pod IP |
---|---|
t3.small | 11 |
m5.large | 29 |
c5.xlarge | 58 |
如果 pod 無法取得 IP 位址,就會一直處於 Pending 狀態,影響應用程式的可用性。監控 IP 位址使用情況:
# Check available IP addresses per node
kubectl describe node | grep "Allocated"
服務發現機制
要存取 Redis 服務,以下是常見的方法:
1. 使用外部名稱服務:
apiVersion: v1
kind: Service
metadata:
name: redis-master
spec:
type: ExternalName
externalName: my-redis.xxxx.ng.0001.use1.cache.amazonaws.com
2. 使用環境變數和 ConfigMaps:
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-config
data:
redis-host: "my-redis.xxxx.ng.0001.use1.cache.amazonaws.com"
redis-port: "6379"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
template:
spec:
containers:
- name: app
envFrom:
- configMapRef:
name: redis-config
安全群組考量
EKS pod 流量會繼承節點的安全群組。對於 ElastiCache 連線:
- 識別節點安全群組:
aws eks describe-nodegroup --cluster-name your-cluster --nodegroup-name your-nodegroup --query 'nodegroup.remoteAccessSecurityGroup'
- 設定 ElastiCache 安全群組:
aws elasticache modify-replication-group --security-group-ids sg-012345abcdef --replication-group-id your-redis-cluster
監控與可觀察性策略
建立完整的監控機制,能讓您在 Redis 連線問題影響應用程式之前及早發現並處理。
監控的關鍵指標
針對 Redis:
- CurrConnections:目前的用戶端連線 (警示突然的變化)
- NetworkBytesIn/Out:網路流量模式
- CPUUtilization:CPU 使用率 (指令處理期間的峰值)
- SwapUsage:顯示記憶體壓力
- CommandLatency: 指令延遲:作業回應時間
用於用戶端應用程式:
- 連線超時與錯誤
- 連線池使用率
- 到 Redis 的請求延遲
- 重試次數和斷路器啟動
用於 Redis 監控的 CloudWatch 面板
為 Redis 監控建立全面的儀表板:
aws cloudwatch put-dashboard --dashboard-name "Redis-Monitoring" --dashboard-body '{
"widgets": [
{
"type": "metric",
"x": 0,
"y": 0,
"width": 12,
"height": 6,
"properties": {
"metrics": [
[ "AWS/ElastiCache", "CurrConnections", "CacheClusterId", "my-redis" ]
],
"period": 60,
"stat": "Average",
"region": "us-east-1",
"title": "Current Connections"
}
},
{
"type": "metric",
"x": 12,
"y": 0,
"width": 12,
"height": 6,
"properties": {
"metrics": [
[ "AWS/ElastiCache", "CPUUtilization", "CacheClusterId", "my-redis" ]
],
"period": 60,
"stat": "Average",
"region": "us-east-1",
"title": "CPU Utilization"
}
}
]
}'
Prometheus 和 Grafana 整合
如需更詳細的監控,請匯出 Redis 指標至 Prometheus:
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-exporter-config
data:
redis-exporter.conf: |
redis.addr=my-redis:6379
namespace=redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-exporter
spec:
template:
spec:
containers:
- name: exporter
image: oliver006/redis_exporter:latest
ports:
- containerPort: 9121
envFrom:
- configMapRef:
name: redis-exporter-config
分散式追蹤
實施分散式追蹤以找出 Redis 相關瓶頸:
// Using Spring Cloud Sleuth with Redis
@Bean
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory,
SpanCustomizer spanCustomizer) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(
new TracingRedisConnectionFactory(
redisConnectionFactory,
"redis",
spanCustomizer
)
);
return template;
}
結論與檢查清單
雖然 EKS 環境中的 Redis 連線問題看似複雜,但透過系統化的方法便能有效解決。在容器化環境中維持 Redis 的穩定運作,關鍵在於三個要素:妥善配置用戶端、有效管理容器生命週期,以及規劃合適的基礎架構規模。
要維持一個健康的 Redis 環境,除了剛才提到的這些技術面的設定,你還得常常做一些壓力測試,看看系統到底能撐多少負載。也透過增強監控和自動警報系統,這樣系統有問題時你馬上就知道。別忘了讓團隊的每個人都熟悉一下怎麼解決常見問題,這樣遇到狀況時才不會手忙腳亂。
以下是一份實用的檢查清單,裡面列出了處理 Redis 連線問題時要注意的項目:
Redis 連線疑難排解清單
網路連線
- 驗證 Redis 端點的 DNS 解析
- 確認安全群組允許來自 EKS 節點的流量
- 使用 nc、telnet 和 redis-cli 測試基本連線性
- 驗證 pod 子網路與 Redis 之間的路由是否正確
用戶端設定
- 使用明確的連線池設定,而非預設值
- 為連線和作業設定適當的逾時時間
- 實施連線健康檢查
- 在 pod 終止時設定適當的連線處理
Redis 實體大小
- 確保實體類型可以處理預期的連線數
- 監控記憶體使用量,並考慮啟用最大記憶體政策
- 針對讀取繁重的工作負載,使用讀取複本評估讀取/寫入分割
- 針對大型資料集或高吞吐量,考慮使用 Redis Cluster
擴充行為
- 在 HPA 配置中實施逐步擴充政策
- 在擴充事件中監控連線模式
- 考慮在應用程式層級限制連線
- 在縮放和縮入事件中測試應用程式行為
監控和警報
- 為 Redis 指標 (連線、CPU、記憶體) 設定 CloudWatch 警報
- 實施 Redis 作業的應用程式層級指標
- 配置分佈式追蹤,以獲得端對端的可視性
- 建立顯示相關應用程式和 Redis 指標的儀表板
透過留意這些不同面向,你的系統不只能輕鬆應對高峰期的流量,還能保持穩定。不過,在 EKS 環境中設定 Redis 連線只是第一步,更重要的是要打造具有彈性的系統,以優雅地因應容器化環境的動態特性。
參考資料