使用 Ingres 訪問 Dapr 應用

方案

如何公開 Dapr 應用的訪問,方案有兩種:

  1. 像傳統用法一樣,配置應用的 Service 作爲後端,由入口控制器直接將流量轉發到應用容器,簡單說就是支持自動配置的 L7 負載均衡器。

  2. 不直接訪問應用容器,而是通過 Daprd 運行時來訪問。這時,我們就需要將入口控制器也聲明爲 Dapr 應用,爲其注入 Daprd 運行時容器。此時創建入口規則指向的後端,則是 Ingress 的 Dapr Service。

兩種方案各有優劣,前者架構簡單;後者雖然引入 Daprd 容器,架構複雜,消耗了更多的資源,但也因此可以使用 Dapr 的服務治理能力,如超時、重試、訪問控制等等。

接下來我們將通過示例來分別驗證兩種方案。

演示

前置條件

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 相關的註解。這裏需要注意由於默認情況下 Daprd 運行時監聽的端口是綁定在迴環地址上的,而 Ingress 向後端轉發請求時使用的是 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 \
  --version=0.2.1-alpha.3 \
  -f values.yaml \
  fsm fsm/fsm

部署示例應用

示例應用我們使用 kennethreitz/httpbin,爲其添加 dapr 相關的註解。

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],創建入口規則使用 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 將會被髮送到目標 Pod。

curl HOST_IP:80/httpbin/get
{
  "args"{},
  "headers"{
    "Accept""*/*",
    "Connection""keep-alive",
    "Content-Length""0",
    "Host""20.239.95.81:30508",
    "User-Agent""curl/7.86.0"
  },
  "origin""10.42.0.18",
  "url""http://20.239.95.81:30508/get"
}

方案 2

方案 2 也同樣需要配置入口規則,只是這次後端服務配置的入口控制器的 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 服務調用的 API[2]:通過請求頭指定 dapr-app-id 爲 httpbin.httpbin。由於入口控制器和目標應用不在同一命名空間下,在應用 id 需要帶上其命名空間。

同樣是成功返回,但是可以看到最終應用收到請求頭部會多了一些信息,這些信息都是來自 Daprd 運行時,比如 Dapr-Caller-App-Id"Dapr-Callee-App-Id 標識表示請求的發起方和接收方;Forwarded 標識了發起請求的主機名。如果開啓了 tracing,還會有鏈路相關的信息(本示例中並未開啓)。

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 雖然帶來了延遲增加了複雜度,但是可以使用 Dapr 的服務治理能力。這裏以 Dapr 的訪問控制功能 [3] 爲例。

不知道大家注意到沒,在部署入口控制器和示例應用時,有個註解 dapr.io/config: xxx。這個註解是爲應用的 Daprd 運行時指定配置,運行時啓動時會從名爲 xxx 的 Configuration 資源讀取配置。

執行下面的命令爲 httpbin 應用設置訪問控制規則:默認拒絕所有請求,只允許 flomesh 命名空間下的 id 爲 ingress 的應用通過 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

應用配置之後,需要重啓應用。

kubectl rollout restart deploy httpbin -n httpbin

等到重啓完成,再訪問 /get 路徑,正常響應。

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,會收到下面的響應:被禁止訪問 /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"
}

總結

文中使用的兩種入口方案各有優缺點,方案 1 架構簡單,也是我們常用的方案;方案 2 中相當於將入口控制器聲明爲 Dapr 應用,實際上暴露的是 Dapr 的 API,在實現上更像是一個全局的應用運行時。

參考資料

[1] 

文檔: https://fsm-docs.flomesh.io/docs/demos/ingress_basics

[2] 

Dapr 服務調用的 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/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/R7gwWjsmzqVGKk0KDYDtYA