跳至主要内容

Route53 / ACM / K8s Ingress — 完整關係解析

一個請求的完整旅程

假設瀏覽器打 https://grafana-central.example.com

1. DNS 解析(Route53 的工作)
瀏覽器問:「grafana-central.example.com 的 IP 是?」
→ Route53 查 example.com 這個 hosted zone
→ 找到一筆 A/alias 記錄,回答:「去問 ALB xxx.elb.amazonaws.com」

2. 連線與路由(ALB / NLB 的工作)
瀏覽器帶著 ALB 的位址去敲門
→ ALB 收到 Host header 是 grafana-central.example.com
→ 依照 Ingress 規則轉發到 Grafana Pod

三個角色,各管各的,互不依賴對方才能「存在」

角色是什麼管什麼
Route53 Hosted Zone一本電話簿「這個名字」對應「哪個位址」——純查詢服務,本身不轉發任何流量
A / Alias record電話簿裡的一行Zone 裡具體一筆對應關係
ALB / NLB真正在路上收信、轉信的郵局收到連線後決定轉去哪個 Pod/Service,跟 DNS 完全無關

沒有域名,ALB/NLB 一樣能用

ALB/NLB 建立時 AWS 就給了一個原生 DNS 名字(例如 internal-alb-xxx.us-east-1.elb.amazonaws.com),這個名字不需要 Route53 就能被解析——AWS 自己的 DNS 系統負責。

Route53 的 record 存在的唯一目的,是幫這串醜名字取一個好記的別名。沒有 record,ALB 一樣健康運作、一樣能被連到,只是你得用那串醜名字連。這也是為什麼「忘記加 Route53 record」不會讓 ALB 掛掉——ALB 沒事,只是沒人能用好記的域名找到它。

ACM cert validation 也是同一個道理

ACM 要證明「你有權管理這個域名」,做法是要求你在對應的 hosted zone 裡放一筆特定的驗證記錄。如果這個域名的 zone 不在你能操作的帳號裡(見下方「跨帳號」章節),你的 Terraform/Console 沒辦法自己放這筆記錄——cert 永遠卡在 pending validation。


這三個東西各自在幹嘛

用戶在瀏覽器輸入 https://grafana.example.com

│ ① Route53:域名 → IP(你要去哪)

ALB (IP: 10.0.1.50)

│ ② ACM cert:TLS 握手(證明我是誰)
│ ALB 出示憑證,瀏覽器驗證合法性

│ ③ K8s Ingress:路由規則(流量去哪個 Pod)
│ host=grafana.example.com → Grafana Pod :80

Grafana Pod
元件職責比喻
Route53DNS 解析:域名 → IP電話簿(查名字找到地址)
ACM CertificateTLS 憑證:證明伺服器身分身分證(證明「我是 grafana.example.com」)
K8s Ingress路由規則:請求分配到哪個 Pod大樓櫃台(看你找誰,指引到哪間辦公室)
ALB實際執行以上所有大樓本身(有地址、有門禁、有櫃台)

Route53 是什麼

Route53 是 AWS 的 DNS 服務,負責把域名翻譯成 IP 地址。

Hosted Zone 兩種類型

Public Hosted ZonePrivate Hosted Zone (PHZ)
誰能查全世界只有關聯的 VPC 內部
用途公開網站、API內部服務互連
域名範例grafana.example.comvmauth.monitoring.example.internal
費用$0.50/月 + 每百萬查詢 $0.40$0.50/月 + 每百萬查詢 $0.40

Record 類型

Type用途值的格式
A域名 → IPv4192.168.1.1
AAAA域名 → IPv62001:db8::1
CNAME域名 → 另一個域名internal-k8s-xxx.elb.amazonaws.com
Alias域名 → AWS 資源(Route53 專屬)選 ALB / CloudFront / S3 等

CNAME vs Alias

問題:ALB 沒有固定 IP,IP 會隨時變。怎麼讓域名指向 ALB?

CNAME(標準 DNS)

用戶查 grafana.example.com
→ DNS 回答:去問 internal-k8s-xxx.elb.amazonaws.com(CNAME)
→ DNS 再查一次 → 拿到 ALB 當下的 IP
→ 總共 2 次 DNS 查詢

Alias(Route53 專屬捷徑)

用戶查 grafana.example.com
→ Route53 內部直接知道 ALB 的 IP(因為 ALB 也在 AWS 裡)
→ 一次就回答 ALB 當下的 IP
→ 總共 1 次 DNS 查詢

比較

CNAMEAlias
運作方式「去問另一個名字」「我直接告訴你 IP」
DNS 查詢次數2 次1 次
費用要錢指向 AWS 資源免費
Zone apex不能(example.com 不行)可以
指向非 AWS 資源可以不行(Route53 專屬)

結論: 指向 AWS 資源(ALB、NLB、CloudFront)→ 一律用 Alias。


ACM 是什麼

AWS Certificate Manager,負責管理 TLS 憑證。憑證的作用是讓瀏覽器確認「這個伺服器真的是它說的那個域名」。

憑證類型

類型涵蓋範圍範例
單域名一個域名grafana.example.com
SAN (Subject Alternative Name)多個指定域名grafana.example.com + api.example.com
Wildcard所有子域名(一層)*.example.com(涵蓋 grafana.example.comapi.example.com 等)

ARN 是什麼

ARN 是 ACM 憑證的唯一識別碼(地址):

arn:aws:acm:us-east-1:067240665187:certificate/021ab848-33de-4117-8782-05449a7bcb65
│ │ │ │ │
│ │ │ │ └── 憑證 ID
│ │ │ └── AWS Account
│ │ └── Region
│ └── 服務 = ACM
└── AWS 資源

ACM cert 建立後 ARN 永遠不變,即使 renew 也不會換。

DNS 驗證流程

1. ACM Console → Request certificate → 填域名
2. ACM 給你一筆 CNAME record(用來證明你擁有這個域名)
3. 你把 CNAME 加到 Route53
4. ACM 驗證 CNAME 存在 → 發憑證 → 狀態變成 Issued
5. 只要 CNAME 一直在,ACM 會自動 renew

K8s Ingress 怎麼串起 Route53 和 ACM

Ingress YAML 裡的關鍵設定

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
# ② 告訴 ALB 用哪張 ACM cert 做 TLS
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:...
# ALB 類型
alb.ingress.kubernetes.io/scheme: internal
spec:
ingressClassName: alb
rules:
# ③ 路由規則:什麼 host → 哪個 Pod
- host: grafana.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: grafana
port:
number: 80

三者的依賴關係

Route53 (DNS) ACM (TLS cert) K8s Ingress (路由)
│ │ │
│ 不依賴任何人 │ DNS 驗證需要 Route53 │ 需要 cert ARN
│ │ │ 需要 host 域名
▼ ▼ ▼
┌─────────────────── ALB ───────────────────────┐
│ │
│ 1. Route53 把域名解析到 ALB IP │
│ 2. ALB 用 ACM cert 做 TLS 握手 │
│ 3. ALB 根據 Ingress host rule 轉發到 Pod │
└───────────────────────────────────────────────┘

建立順序

1. Route53 Public HZ ← 可以獨立建(不需要 ACM)
2. ACM Certificate ← DNS 驗證需要在 Route53 加 CNAME
3. K8s Ingress + Deploy ← 需要 cert ARN 填入 annotation
4. Route53 A Record ← 指向 ALB(ALB 要先存在才能選)

ALB 做了三件獨立的事

重點:這三件事是獨立運作的,一個錯不代表全部不通。

請求進來:https://grafana.example.com

1. TLS 握手(用 certificate-arn 指定的 cert)
→ ALB 不管 cert 域名對不對,直接出示
→ 瀏覽器負責驗證域名是否匹配
→ 匹配 → 🔒 安全
→ 不匹配 → ⚠️ 警告,但用戶可以選擇繼續

2. Host Routing(用 Ingress rules 的 host)
→ 看 HTTP header 的 Host 是什麼
→ 匹配 → 轉發到對應的 Pod
→ 不匹配 → 404 或 default backend

3. Target 轉發(用 Ingress backend 的 service/port)
→ 把解密後的 HTTP 流量送到 Pod

為什麼 cert 域名錯了還能連?

你訪問:grafana-central.example.com
ALB 出示的 cert:ti2-stg.example.com(域名不匹配)

1. DNS 解析 → ✅ Route53 有設,解析到 ALB IP
2. TCP 連線 → ✅ ALB :443 可以連
3. TLS 握手 → ⚠️ ALB 出示 cert,瀏覽器發現域名不匹配
→ 瀏覽器顯示警告「不安全」
→ 但用戶可以點「繼續前往」
4. Host Routing → ✅ Host header 匹配 Ingress rule
5. 轉發到 Pod → ✅ Grafana 正常回應

結論: cert 域名錯了不影響連線,只影響瀏覽器是否顯示「安全」。ALB 不負責驗證 cert 跟域名匹不匹配,那是瀏覽器的工作。


完整實例:Central Grafana 的 HTTPS 設定

目標

讓內部網路可以用 https://grafana-central-stg.example.com 安全連到 Central Grafana。

步驟拆解

步驟做什麼在哪裡做
1申請 ACM cert(域名 grafana-central-stg.example.comACM Console
2DNS 驗證(加 CNAME 到 Route53)Route53 Console
3cert ARN 填入 Ingress annotationupdate_env.shcertificate-arn
4host 填入 Ingress ruleupdate_env.shGRAFANA_CENTRAL_HOSTNAME_PLACEHOLDER
5部署 → ALB Controller 自動設定 ALBCI/CD deploy
6Route53 加 A record (Alias) → ALBRoute53 Console
7瀏覽器訪問 → Route53 解析 → ALB TLS → Grafana驗證

流量路徑

瀏覽器: https://grafana-central-stg.example.com

│ Route53 Public HZ: A (Alias) → Internal ALB

ALB :443
│ ACM cert: grafana-central-stg.example.com
│ TLS 握手 → 瀏覽器驗證 → 🔒 安全

│ Ingress host rule 匹配 → 轉發

Grafana Central Pod :80

驗證 cert 是否正確

# Terminal
echo | openssl s_client -connect grafana-central-stg.example.com:443 \
-servername grafana-central-stg.example.com 2>/dev/null | \
openssl x509 -noout -subject -issuer -dates

# 預期輸出
subject= CN = grafana-central-stg.example.com
issuer= O = Amazon, CN = Amazon RSA 2048 M03
notBefore= ...
notAfter= ...

瀏覽器:點網址列 🔒 鎖頭 → Certificate → 看 Issued to 是否匹配。


Private Hosted Zone (PHZ) 跨 VPC 使用

PHZ 預設只在建立時關聯的 VPC 內可查。要讓其他 VPC 也能解析,需要 associate:

PHZ: monitoring.example.internal
├── VPC A (Central) — 建立時自動關聯
├── VPC B (Region B) — 需要手動 associate
└── VPC C (Region C) — 需要手動 associate

同 Account

aws route53 associate-vpc-with-hosted-zone \
--hosted-zone-id Z0123456789 \
--vpc VPCRegion=us-east-1,VPCId=vpc-xxx

跨 Account

需要兩步:

  1. Zone owner account 授權:create-vpc-association-authorization
  2. VPC owner account 關聯:associate-vpc-with-hosted-zone

注意:Internal NLB DNS 跟 PHZ 不同

Internal NLB 的 auto-generated DNS(如 k8s-xxx.elb.us-east-1.amazonaws.com)只能在它所在的 VPC 內解析。跨 VPC 即使有 TGW,DNS 也不會自動通。

解法:在 PHZ 加一筆 CNAME 指向 NLB DNS name,讓關聯的 VPC 都能解析。


Route53 是帳號層級資源,不是全域共享的電話簿

Route53 本身是「全球服務」(不分 region,見上方 Console 操作一律用同一個 endpoint),但每個 AWS 帳號的 Route53 是彼此獨立的。帳號 A 建的 hosted zone,帳號 B 完全查不到,除非明確做跨帳號授權(類似上面 PHZ 跨帳號 associate 的模式,但 public zone 通常是走 zone delegation:帳號 B 在自己的 zone 裡加一筆 NS record 指到帳號 A 的 zone)。

一個真實會踩到的情境

Terraform 裡有一個 data "aws_route53_zone"data "aws_route53_zone" "parent" 之類的 data source,用 root_domain 變數查 zone:

data "aws_route53_zone" "parent" {
name = var.root_domain # 例如 "prod.example.com"
}

這個 data source 用的是當前 provider 認證的那個帳號去查。如果你想用來建 cert 的域名(例如 grafana.other-brand.example.com)實際 zone 存在於另一個帳號,即使域名看起來像是同一個公司的同一套命名規則,aws_route53_zone 一樣查不到——會直接報錯「zone not found」,或更隱蔽地,如果 code 剛好也建了 other-brand.example.com 這個 zone 名字在這個帳號(同名但不同帳號的兩個獨立 zone),查詢會成功但查到的是錯的那個 zone,導致:

  • ACM cert 的 DNS validation record 被寫進錯的 zone → cert 永遠 pending,因為公開解析走的是另一個帳號的真正 zone。
  • Alias A record 建在錯的 zone 裡 → 用戶端解析走的是另一個帳號的 zone,永遠看不到你剛建的這筆記錄。

怎麼判斷是不是踩到這個坑

先查你要用的域名,zone 到底存在哪個帳號:

# 在你以為的目標帳號查
aws route53 list-hosted-zones --query "HostedZones[].Name" --output text

# 沒看到預期的域名?→ 這個域名的 zone 在別的帳號

如果 zone 真的在別的帳號,選項:

  1. 改用當前帳號已有的域名——最簡單,不用跨帳號協調。
  2. 跨帳號委派(delegation):在當前帳號建一個子域名 zone(如 sub.prod.example.com),在域名真正的 owner 帳號裡加一筆 NS record 指過來,之後這個子域名的查詢會被轉發到你的帳號處理。
  3. 請 zone owner 帳號的人幫忙代放 validation record / alias record——不用委派整個 zone,只是單筆記錄借用對方的管理權限。

「這幾個帳號名字聽起來像同一個系統」不代表 Route53 zone 互通——Route53 沒有『同公司自動信任』這種機制,帳號邊界永遠是硬邊界,除非你明確設定委派或授權。


Route53 Console 操作:建立 Alias A Record

  1. Route53Hosted zones → 選擇你的 hosted zone
  2. Create record
  3. 填入:
欄位
Record namegrafana-central(會自動帶上 hosted zone 的域名)
Record typeA
Alias打開(toggle on)
Route traffic toAlias to Application and Classic Load Balancer
Region選 ALB 所在的 region
選擇 LB從下拉選單選你的 ALB
Routing policySimple routing
  1. Create records

Terraform 範例

Public Hosted Zone + Alias A Record

data "aws_route53_zone" "public" {
name = "example.com"
private_zone = false
}

resource "aws_route53_record" "grafana_central" {
zone_id = data.aws_route53_zone.public.zone_id
name = "grafana-central.example.com"
type = "A"

alias {
name = aws_lb.alb.dns_name
zone_id = aws_lb.alb.zone_id
evaluate_target_health = true
}
}

Private Hosted Zone + CNAME

resource "aws_route53_zone" "internal" {
name = "monitoring.example.internal"

vpc {
vpc_id = module.vpc.vpc_id
}

lifecycle {
ignore_changes = [vpc]
}
}

resource "aws_route53_record" "vmauth" {
zone_id = aws_route53_zone.internal.zone_id
name = "vmauth.monitoring.example.internal"
type = "CNAME"
ttl = 300
records = ["internal-k8s-xxx.elb.us-east-1.amazonaws.com"]
}

常見問題

Q: Route53 建立 Public HZ 需要先建 ACM cert 嗎?

不需要,兩個獨立。反過來,ACM DNS 驗證需要 Route53。

Q: cert 域名錯了為什麼還能連?

ALB 不驗證 cert 域名是否匹配,它只出示 cert。驗證是瀏覽器做的。域名不匹配時瀏覽器會警告,但用戶可以手動繼續。

Q: PHZ 的 record 在 VPC 外面查得到嗎?

不行。PHZ 只有關聯的 VPC 內部可以解析。

Q: Alias record 可以指向其他 account 的 ALB 嗎?

可以,但你需要知道 ALB 的 DNS name 和 hosted zone ID。

Q: 一個域名可以同時有 CNAME 和其他 record 嗎?

不行。CNAME 是排他的。這也是 zone apex 不能用 CNAME 的原因(apex 需要 SOA 和 NS record)。Alias 沒有這個限制。

Q: Wildcard cert *.example.com 跟單域名 cert 怎麼選?

看公司規定。有些公司要求每個 app 有自己的 cert(安全隔離),有些允許 wildcard(方便管理)。Wildcard 不涵蓋 zone apex(example.com 本身)和二層子域名(a.b.example.com)。