在 ??上一篇?? 文章中分享了分布式運行時 Dapr 的使用,在示例中將狀態(tài)存儲能力分離到 Dapr 運行時中,應(yīng)用通過 Dapr API 來使用該能力。這篇文章將介紹如何通過 Ingress Controller(入口控制器)來訪問 Dapr 應(yīng)用。
方案
如何公開 Dapr 應(yīng)用的訪問,方案有兩種:
- 像傳統(tǒng)用法一樣,配置應(yīng)用的 Service 作為后端,由入口控制器直接將流量轉(zhuǎn)發(fā)到應(yīng)用容器,簡單說就是支持自動配置的 L7 負載均衡器。
- 不直接訪問應(yīng)用容器,而是通過 Daprd 運行時來訪問。這時,我們就需要將入口控制器也聲明為 Dapr 應(yīng)用,為其注入 Daprd 運行時容器。此時創(chuàng)建入口規(guī)則指向的后端,則是 Ingress 的 Dapr Service。
兩種方案各有優(yōu)劣,前者架構(gòu)簡單;后者雖然引入 Daprd 容器,架構(gòu)復(fù)雜,消耗了更多的資源,但也因此可以使用 Dapr 的服務(wù)治理能力,如超時、重試、訪問控制等等。
接下來我們將通過示例來分別驗證兩種方案。

演示
前置條件
export INSTALL_K3S_VERSION=v1.23.8+k3s2
curl -sfL https://get.k3s.io | sh -s - --disable traefik --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config
安裝 Dapr
dapr init --kubernetes --wait
安裝入口控制器
這里使用 Flomesh 的入口控制器。通過 valus.yaml? 為入口控制器添加 dapr 相關(guān)的注解。這里需要注意由于默認情況下 Daprd 運行時監(jiān)聽的端口是綁定在回環(huán)地址上的,而 Ingress 向后端轉(zhuǎn)發(fā)請求時使用的是 Pod IP,因此需要注解 dapr.io/sidecar-listen-addresses: "[::],0.0.0.0" 讓 Daprd 運行時可以接收來自 Pod IP 的請求。
# values.yaml
fsm:
ingress:
podAnnotations:
dapr.io/sidecar-listen-addresses: "[::],0.0.0.0"
dapr.io/enabled: "true"
dapr.io/app-id: "ingress"
dapr.io/app-port: "8000"
dapr.io/enable-api-logging: "true"
dapr.io/config: "ingressconfig"
helm repo add fsm https://charts.flomesh.io
helm repo update
helm install \
--namespace flomesh \
--create-namespace \
--versinotallow=0.2.1-alpha.3 \
-f values.yaml \
fsm fsm/fsm
部署示例應(yīng)用
示例應(yīng)用我們使用 kennethreitz/httpbin,為其添加 dapr 相關(guān)的注解。
kubectl create ns httpbin
kubectl apply -n httpbin -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: httpbin
name: httpbin
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
strategy: {}
template:
metadata:
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "httpbin"
dapr.io/app-port: "80"
dapr.io/enable-api-logging: "true"
dapr.io/config: "httpbinconfig"
labels:
app: httpbin
spec:
containers:
- image: kennethreitz/httpbin
name: httpbin
resources: {}
---
apiVersion: v1
kind: Service
metadata:
labels:
app: httpbin
name: httpbin
namespace: httpbin
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: httpbin
EOF
方案 1
參考 Flomesh Ingress 的 文檔[1],創(chuàng)建入口規(guī)則使用 Service httpbin 作為后端。
kubectl apply -n httpbin -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpbin
annotations:
pipy.ingress.kubernetes.io/rewrite-target-from: ^/httpbin/?
pipy.ingress.kubernetes.io/rewrite-target-to: /
spec:
ingressClassName: pipy
rules:
- http:
paths:
- backend:
service:
name: httpbin
port:
number: 80
path: /httpbin
pathType: Prefix
EOF
使用主機 IP 地址和入口控制器的 80? 端口來訪問 /httpbin/get?,由于上面配置了路徑重寫,最終請求 /get 將會被發(fā)送到目標 Pod。
curl HOST_IP:80/httpbin/get
{
"args": {},
"headers": {
"Accept": "*/*",
"Connection": "keep-alive",
"Content-Length": "0",
"Host": "20.239.95.81:80",
"User-Agent": "curl/7.86.0"
},
"origin": "10.42.0.18",
"url": "http://20.239.95.81:80/get"
}
方案 2
方案 2 也同樣需要配置入口規(guī)則,只是這次后端服務(wù)配置的入口控制器的 Dapr Service?,通過 kubectl get svc -n flomesh? 就可以找到名字帶有 -dapr? 后綴的 Service。
kubectl apply -n flomesh -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dapr
annotations:
pipy.ingress.kubernetes.io/rewrite-target-from: ^/dapr/?
pipy.ingress.kubernetes.io/rewrite-target-to: /
spec:
ingressClassName: pipy
rules:
- http:
paths:
- backend:
service:
name: ingress-dapr
port:
number: 80
path: /dapr
pathType: Prefix
EOF
仍然是訪問 httpbin? 的路徑 /get?,但是訪問方式要遵循 Dapr 服務(wù)調(diào)用的 API[2]:通過請求頭指定 dapr-app-id? 為 httpbin.httpbin。由于入口控制器和目標應(yīng)用不在同一命名空間下,在應(yīng)用 id 需要帶上其命名空間。
同樣是成功返回,但是可以看到最終應(yīng)用收到請求頭部會多了一些信息,這些信息都是來自 Daprd 運行時,比如 Dapr-Caller-App-Id、"Dapr-Callee-App-Id? 標識表示請求的發(fā)起方和接收方;Forwarded 標識了發(fā)起請求的主機名。如果開啟了 tracing,還會有鏈路相關(guān)的信息(本示例中并未開啟)。
curl HOST_IP:80/dapr/get -H 'dapr-app-id:httpbin.httpbin'
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip",
"Connection": "keep-alive",
"Dapr-App-Id": "httpbin.httpbin",
"Dapr-Callee-App-Id": "httpbin",
"Dapr-Caller-App-Id": "ingress",
"Forwarded": "for=10.42.0.18;by=10.42.0.18;host=fsm-ingress-pipy-864d8d9c76-4kb7r",
"Host": "127.0.0.1:80",
"Proto": "HTTP/1.1",
"Protomajor": "1",
"Protominor": "1",
"Referer": "",
"Traceparent": "00-00000000000000000000000000000000-0000000000000000-00",
"User-Agent": "curl/7.86.0",
"X-Forwarded-Host": "fsm-ingress-pipy-864d8d9c76-4kb7r"
},
"origin": "10.42.0.18",
"url": "http://fsm-ingress-pipy-864d8d9c76-4kb7r/get"
}
訪問控制
文章開頭提到使用 方案 2 雖然帶來了延遲增加了復(fù)雜度,但是可以使用 Dapr 的服務(wù)治理能力。這里以 Dapr 的訪問控制功能[3] 為例。
不知道大家注意到?jīng)],在部署入口控制器和示例應(yīng)用時,有個注解 dapr.io/config: xxx?。這個注解是為應(yīng)用的 Daprd 運行時指定配置,運行時啟動時會從名為 xxx 的 Configuration 資源讀取配置。
執(zhí)行下面的命令為 httpbin? 應(yīng)用設(shè)置訪問控制規(guī)則:默認拒絕所有請求,只允許 flomesh? 命名空間下的 id 為 ingress? 的應(yīng)用通過 GET? 方式訪問路徑 /get。更多訪問控制配置,可以參考 Dapr 訪問控制官方文檔[4]。
kubectl apply -n httpbin -f - <<EOF
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: httpbinconfig
spec:
accessControl:
defaultAction: deny
trustDomain: "public"
policies:
- appId: ingress
defaultAction: deny
trustDomain: 'public'
namespace: "flomesh"
operations:
- name: /get
httpVerb: ['GET']
action: allow
EOF
應(yīng)用配置之后,需要重啟應(yīng)用。
kubectl rollout restart deploy httpbin -n httpbin
等到重啟完成,再訪問 /get 路徑,正常響應(yīng)。
curl HOST_IP:80/dapr/get -H 'dapr-app-id:httpbin.httpbin'
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip",
"Connection": "keep-alive",
"Dapr-App-Id": "httpbin.httpbin",
"Dapr-Callee-App-Id": "httpbin",
"Dapr-Caller-App-Id": "ingress",
"Forwarded": "for=10.42.0.40;by=10.42.0.40;host=fsm-ingress-pipy-864d8d9c76-jctcx",
"Host": "127.0.0.1:80",
"Proto": "HTTP/1.1",
"Protomajor": "1",
"Protominor": "1",
"Referer": "",
"Traceparent": "00-00000000000000000000000000000000-0000000000000000-00",
"User-Agent": "curl/7.86.0",
"X-Forwarded-Host": "fsm-ingress-pipy-864d8d9c76-jctcx"
},
"origin": "10.42.0.40",
"url": "http://fsm-ingress-pipy-864d8d9c76-jctcx/get"
}
但是,如果是訪問路徑 /headers?,會收到下面的響應(yīng):被禁止訪問 /headers。
curl HOST_IP:80/dapr/headers -H 'dapr-app-id:httpbin.httpbin'
{
"errorCode": "ERR_DIRECT_INVOKE",
"message": "fail to invoke, id: httpbin.httpbin, err: rpc error: code = PermissionDenied desc = access control policy has denied access to appid: ingress operation: headers verb: GET"
}
總結(jié)
文中使用的兩種入口方案各有優(yōu)缺點,方案 1 架構(gòu)簡單,也是我們常用的方案;方案 2 中相當于將入口控制器聲明為 Dapr 應(yīng)用,實際上暴露的是 Dapr 的 API,在實現(xiàn)上更像是一個全局的應(yīng)用運行時。
參考資料
[1] 文檔: https://fsm-docs.flomesh.io/docs/demos/ingress_basics
[2] Dapr 服務(wù)調(diào)用的 API: https://docs.dapr.io/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services/
[3] Dapr 的訪問控制功能: https://docs.dapr.io/operations/configuration/invoke-allowlist/
[4] Dapr 訪問控制官方文檔: https://docs.dapr.io/operations/configuration/invoke-allowlist/