ECS 服務發現概述
在微服務架構中,服務發現是一個關鍵元件,它允許服務在不需要硬編碼 IP 位址或端點的情況下相互尋找和通訊。AWS ECS 提供了與 AWS Cloud Map 整合的原生服務發現功能,讓容器化應用程式能夠透過 DNS 或 API 呼叫自動發現其他服務。
服務發現的核心優勢
- 動態服務註冊:當 ECS 任務啟動時,自動註冊到服務發現命名空間
- 自動健康檢查:不健康的任務會自動從服務發現中移除
- DNS 解析:透過 DNS 名稱存取服務,無需管理 IP 位址
- 跨服務通訊:簡化微服務間的網路通訊配置
- 與負載平衡器整合:可同時使用服務發現和負載平衡器
Cloud Map 整合
AWS Cloud Map 是一個雲端資源發現服務,與 ECS 緊密整合。它提供了一個統一的方式來命名和發現所有雲端資源。
Cloud Map 架構元件
1
2
3
4
5
6
7
8
9
10
11
12
13
| ┌─────────────────────────────────────────────────────────┐
│ AWS Cloud Map │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 命名空間 (Namespace) │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 服務 A │ │ 服務 B │ │ │
│ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │ │
│ │ │ │ 實例 1 │ │ │ │ 實例 1 │ │ │ │
│ │ │ │ 實例 2 │ │ │ │ 實例 2 │ │ │ │
│ │ │ └─────────┘ │ │ └─────────┘ │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
|
啟用 Cloud Map 服務
1
2
3
4
5
6
| # 確認 Cloud Map 服務可用
aws servicediscovery list-namespaces
# 查看特定命名空間詳情
aws servicediscovery get-namespace \
--id ns-xxxxxxxxxxxxxxxxx
|
建立命名空間
命名空間是服務發現的最上層容器,定義了服務的 DNS 域名。AWS Cloud Map 支援兩種類型的命名空間。
DNS 命名空間類型
- 私有 DNS 命名空間:僅在 VPC 內部可解析
- 公有 DNS 命名空間:可透過公網 DNS 解析
建立私有 DNS 命名空間
1
2
3
4
5
6
7
8
9
10
11
12
| # 建立私有 DNS 命名空間
aws servicediscovery create-private-dns-namespace \
--name internal.myapp.local \
--vpc vpc-0123456789abcdef0 \
--description "Internal service discovery namespace"
# 取得命名空間 ID
NAMESPACE_ID=$(aws servicediscovery list-namespaces \
--query "Namespaces[?Name=='internal.myapp.local'].Id" \
--output text)
echo "Namespace ID: $NAMESPACE_ID"
|
建立公有 DNS 命名空間
1
2
3
4
| # 建立公有 DNS 命名空間(需要已存在的 Route 53 託管區域)
aws servicediscovery create-public-dns-namespace \
--name services.example.com \
--description "Public service discovery namespace"
|
查看命名空間狀態
1
2
3
4
5
6
7
8
| # 列出所有命名空間
aws servicediscovery list-namespaces \
--query "Namespaces[*].{Name:Name,Id:Id,Type:Type}" \
--output table
# 取得命名空間詳細資訊
aws servicediscovery get-namespace \
--id ns-xxxxxxxxxxxxxxxxx
|
服務註冊
在 Cloud Map 命名空間中建立服務定義,用於註冊 ECS 任務實例。
建立 Cloud Map 服務
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # 建立支援 DNS 的服務
aws servicediscovery create-service \
--name api-service \
--namespace-id ns-xxxxxxxxxxxxxxxxx \
--dns-config '{
"NamespaceId": "ns-xxxxxxxxxxxxxxxxx",
"DnsRecords": [
{
"Type": "A",
"TTL": 10
}
],
"RoutingPolicy": "MULTIVALUE"
}' \
--health-check-custom-config '{
"FailureThreshold": 1
}'
# 取得服務 ID
SERVICE_ID=$(aws servicediscovery list-services \
--query "Services[?Name=='api-service'].Id" \
--output text)
|
建立 ECS Service 並啟用服務發現
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| # 建立 ECS Cluster
aws ecs create-cluster --cluster-name my-app-cluster
# 建立整合服務發現的 ECS Service
aws ecs create-service \
--cluster my-app-cluster \
--service-name api-service \
--task-definition api-task:1 \
--desired-count 3 \
--launch-type FARGATE \
--network-configuration '{
"awsvpcConfiguration": {
"subnets": ["subnet-private-1a", "subnet-private-1b"],
"securityGroups": ["sg-ecs-tasks"],
"assignPublicIp": "DISABLED"
}
}' \
--service-registries '[
{
"registryArn": "arn:aws:servicediscovery:ap-northeast-1:123456789012:service/srv-xxxxxxxxx",
"containerName": "api-container",
"containerPort": 8080
}
]'
|
驗證服務註冊
1
2
3
4
5
6
7
8
9
10
11
| # 列出服務實例
aws servicediscovery list-instances \
--service-id srv-xxxxxxxxx \
--query "Instances[*].{Id:Id,Attributes:Attributes}" \
--output table
# 使用 discover-instances API 發現服務
aws servicediscovery discover-instances \
--namespace-name internal.myapp.local \
--service-name api-service \
--query-parameters HealthStatus=HEALTHY
|
DNS 服務發現
透過 DNS 查詢發現服務是最常見的服務發現方式,應用程式只需要知道服務的 DNS 名稱即可。
DNS 查詢範例
1
2
3
4
5
6
7
8
| # 在 VPC 內的 EC2 實例或容器中執行 DNS 查詢
# 服務 DNS 格式:<service-name>.<namespace-name>
# 查詢 A 記錄
dig api-service.internal.myapp.local A +short
# 查詢 SRV 記錄(包含端口資訊)
dig api-service.internal.myapp.local SRV +short
|
應用程式中使用服務發現
1
2
3
4
5
| # 範例:使用 curl 呼叫透過服務發現找到的服務
curl http://api-service.internal.myapp.local:8080/health
# 在容器內使用環境變數配置服務端點
export API_ENDPOINT="http://api-service.internal.myapp.local:8080"
|
DNS TTL 設定考量
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 更新服務的 DNS TTL 設定
aws servicediscovery update-service \
--id srv-xxxxxxxxx \
--service '{
"DnsConfig": {
"DnsRecords": [
{
"Type": "A",
"TTL": 5
}
]
}
}'
|
ALB 整合
Application Load Balancer(ALB)提供進階的 Layer 7 負載平衡功能,適合 HTTP/HTTPS 應用程式。
建立 ALB 與目標群組
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| # 建立 Application Load Balancer
aws elbv2 create-load-balancer \
--name my-app-alb \
--type application \
--scheme internet-facing \
--subnets subnet-public-1a subnet-public-1b \
--security-groups sg-alb
# 建立目標群組(使用 IP 目標類型供 Fargate 使用)
aws elbv2 create-target-group \
--name my-app-tg \
--protocol HTTP \
--port 8080 \
--vpc-id vpc-0123456789abcdef0 \
--target-type ip \
--health-check-protocol HTTP \
--health-check-path /health \
--health-check-interval-seconds 30 \
--health-check-timeout-seconds 5 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 3
# 建立監聽器
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:loadbalancer/app/my-app-alb/xxx \
--protocol HTTP \
--port 80 \
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/my-app-tg/xxx
|
建立整合 ALB 的 ECS Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # 建立使用 ALB 的 ECS Service
aws ecs create-service \
--cluster my-app-cluster \
--service-name web-frontend \
--task-definition web-task:1 \
--desired-count 3 \
--launch-type FARGATE \
--load-balancers '[
{
"targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/my-app-tg/xxx",
"containerName": "web-container",
"containerPort": 8080
}
]' \
--health-check-grace-period-seconds 60 \
--network-configuration '{
"awsvpcConfiguration": {
"subnets": ["subnet-private-1a", "subnet-private-1b"],
"securityGroups": ["sg-ecs-tasks"],
"assignPublicIp": "DISABLED"
}
}'
|
設定路徑型路由
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 建立額外的目標群組
aws elbv2 create-target-group \
--name api-tg \
--protocol HTTP \
--port 8080 \
--vpc-id vpc-0123456789abcdef0 \
--target-type ip
# 建立路徑型路由規則
aws elbv2 create-rule \
--listener-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:listener/app/my-app-alb/xxx/xxx \
--priority 10 \
--conditions '[{"Field":"path-pattern","Values":["/api/*"]}]' \
--actions '[{"Type":"forward","TargetGroupArn":"arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/api-tg/xxx"}]'
|
NLB 整合
Network Load Balancer(NLB)提供 Layer 4 負載平衡,適合需要極低延遲或 TCP/UDP 協定的應用程式。
建立 NLB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| # 建立 Network Load Balancer
aws elbv2 create-load-balancer \
--name my-app-nlb \
--type network \
--scheme internet-facing \
--subnets subnet-public-1a subnet-public-1b
# 建立 TCP 目標群組
aws elbv2 create-target-group \
--name my-app-nlb-tg \
--protocol TCP \
--port 8080 \
--vpc-id vpc-0123456789abcdef0 \
--target-type ip \
--health-check-protocol TCP \
--health-check-interval-seconds 10 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 2
# 建立 TCP 監聽器
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:loadbalancer/net/my-app-nlb/xxx \
--protocol TCP \
--port 80 \
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/my-app-nlb-tg/xxx
|
建立整合 NLB 的 ECS Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # 建立使用 NLB 的 ECS Service
aws ecs create-service \
--cluster my-app-cluster \
--service-name grpc-service \
--task-definition grpc-task:1 \
--desired-count 2 \
--launch-type FARGATE \
--load-balancers '[
{
"targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/my-app-nlb-tg/xxx",
"containerName": "grpc-container",
"containerPort": 50051
}
]' \
--network-configuration '{
"awsvpcConfiguration": {
"subnets": ["subnet-private-1a", "subnet-private-1b"],
"securityGroups": ["sg-ecs-tasks"],
"assignPublicIp": "DISABLED"
}
}'
|
健康檢查設定
健康檢查確保只有健康的任務接收流量,對於服務發現和負載平衡都至關重要。
容器層級健康檢查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| {
"containerDefinitions": [
{
"name": "app",
"image": "my-app:latest",
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
}
}
]
}
|
ALB 健康檢查設定
1
2
3
4
5
6
7
8
9
10
| # 更新目標群組健康檢查設定
aws elbv2 modify-target-group \
--target-group-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/my-app-tg/xxx \
--health-check-protocol HTTP \
--health-check-path /health \
--health-check-interval-seconds 15 \
--health-check-timeout-seconds 5 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 3 \
--matcher '{"HttpCode":"200-299"}'
|
Cloud Map 健康檢查設定
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 建立帶有自訂健康檢查的 Cloud Map 服務
aws servicediscovery create-service \
--name monitored-service \
--namespace-id ns-xxxxxxxxx \
--dns-config '{
"DnsRecords": [{"Type": "A", "TTL": 10}],
"RoutingPolicy": "WEIGHTED"
}' \
--health-check-config '{
"Type": "HTTP",
"ResourcePath": "/health",
"FailureThreshold": 3
}'
|
健康檢查寬限期
1
2
3
4
5
| # 設定健康檢查寬限期,避免容器啟動期間被標記為不健康
aws ecs update-service \
--cluster my-app-cluster \
--service web-frontend \
--health-check-grace-period-seconds 120
|
最佳實踐
服務發現最佳實踐
- 使用私有命名空間:內部服務間通訊應使用私有 DNS 命名空間
- 設定適當的 DNS TTL:較低的 TTL 可更快反映服務變化,但會增加 DNS 查詢
- 啟用健康檢查:確保不健康的實例自動從服務發現中移除
負載平衡最佳實踐
- 選擇正確的負載平衡器類型:HTTP/HTTPS 用 ALB,TCP/UDP 或需要低延遲用 NLB
- 使用多可用區域部署:確保高可用性
- 配置連線排空:優雅處理任務終止
1
2
3
4
| # 設定目標群組連線排空時間
aws elbv2 modify-target-group-attributes \
--target-group-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/my-app-tg/xxx \
--attributes Key=deregistration_delay.timeout_seconds,Value=30
|
網路安全最佳實踐
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 設定安全群組只允許來自負載平衡器的流量
aws ec2 authorize-security-group-ingress \
--group-id sg-ecs-tasks \
--protocol tcp \
--port 8080 \
--source-group sg-alb
# 為內部服務發現設定安全群組規則
aws ec2 authorize-security-group-ingress \
--group-id sg-ecs-tasks \
--protocol tcp \
--port 8080 \
--source-group sg-ecs-tasks
|
監控與告警
1
2
3
4
5
6
7
8
9
10
11
12
| # 建立 ALB 目標健康告警
aws cloudwatch put-metric-alarm \
--alarm-name "ALB-UnHealthyHosts" \
--metric-name "UnHealthyHostCount" \
--namespace "AWS/ApplicationELB" \
--statistic Average \
--period 60 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--dimensions Name=TargetGroup,Value=targetgroup/my-app-tg/xxx Name=LoadBalancer,Value=app/my-app-alb/xxx \
--evaluation-periods 2 \
--alarm-actions arn:aws:sns:ap-northeast-1:123456789012:my-alerts
|
參考資料