Dapr 入門教程之中間件
Dapr 允許通過鏈接一系列中間件組件來定義自定義處理管道。一個請求在被路由到用戶代碼之前會經過所有定義的中間件組件,然後在返回到客戶端之前以相反的順序經過定義的中間件。
Dapr 中間件
當啓動的時候,Dapr sidecar 會構造一箇中間件處理管道。默認情況下,管道由 tracing 中間件和 CORS 中間件組成。通過 Dapr 配置添加的其他中間件會按定義順序添加到管道中,該管道適用於所有 Dapr API 端點,包括狀態、發佈 / 訂閱、服務調用、綁定、安全等。
如以下配置示例定義了一個使用 OAuth 2.0 中間件和大寫中間件組件的自定義管道。在這種情況下,所有請求都通過 OAuth 2.0 協議進行授權,並轉換爲大寫,然後再轉發給用戶代碼。
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: pipeline
namespace: default
spec:
httpPipeline:
handlers:
- name: oauth2
type: middleware.http.oauth2
- name: uppercase
type: middleware.http.uppercase
與其他構建塊組件一樣,中間件組件是可擴展的,目前 Dapr 已經支持的中間件如下表所示。
這些中間件的實現源碼位可以在 Github 倉庫 https://github.com/dapr/components-contrib/tree/master/middleware/http
中找到。
自定義中間件
Dapr 使用 FastHTTP 來實現其 HTTP 服務器,所以自定義的 HTTP 中間件需要編寫爲 FastHTTP handler,你的中間件需要實現一個如下所示的 Middleware
接口:
package middleware
import (
"github.com/valyala/fasthttp"
)
// Middleware is the interface for a middleware.
type Middleware interface {
GetHandler(metadata Metadata) (func(h fasthttp.RequestHandler) fasthttp.RequestHandler, error)
}
該接口定義了一個返回 fasthttp.RequestHandler
和 error
的 GetHandler
方法。我們自定義的中間件處理程序實現可以包括任何入站邏輯、出站邏輯:
func (m *customMiddleware) GetHandler(metadata Metadata) (func(fasthttp.RequestHandler) fasthttp.RequestHandler, error) {
var err error
return func(h fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
// 入站邏輯
h(ctx) // 調用下游處理程序
// 出站邏輯
}
}, err
}
然後你可以將你自定義的中間件組件貢獻給 components-contrib 存儲庫。當 components-contrib
接受你的提交後,然後需要在 Dapr 運行時的倉庫中的 https://github.com/dapr/dapr/tree/master/cmd/daprd/components
下面添加一箇中間件註冊文件。
比如 uppercase
中間件的註冊如下代碼所示:
package components
import (
"strings"
"github.com/valyala/fasthttp"
"github.com/dapr/components-contrib/middleware"
httpMiddlewareLoader "github.com/dapr/dapr/pkg/components/middleware/http"
httpMiddleware "github.com/dapr/dapr/pkg/middleware/http"
"github.com/dapr/kit/logger"
)
func init() {
httpMiddlewareLoader.DefaultRegistry.RegisterComponent(func(log logger.Logger) httpMiddlewareLoader.FactoryMethod {
return func(metadata middleware.Metadata) (httpMiddleware.Middleware, error) {
return func(h fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
body := string(ctx.PostBody())
ctx.Request.SetBody([]byte(strings.ToUpper(body)))
h(ctx)
}
}, nil
}
}, "uppercase")
}
不過我們也可以發現 Dapr 對於中間件的擴展並沒有完全放開,如果用戶有特定的需求需要將代碼在主倉庫中進行更新,這勢必也降低了靈活性,不過也可以避免因爲低質量的中間件造成 Dapr 各種問題。
OAuth 中間件示例
接下來我們配置一個 OAuth 中間件來說明下 Dapr 中中間件的使用方法。通過配置一個 OAuth 中間件,在不修改應用程序的情況下在 Web API 上啓用 OAuth 授權。這種設計將認證 / 授權問題從應用程序中分離出來,因此應用程序運維人員可以採用和配置認證 / 授權提供者而不影響應用程序代碼。
這裏我們在 K8s 集羣中使用 ingress-nginx 作爲 ingress 控制器,如果沒有安裝可以使用下面的 Helm chart 來快速安裝:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install my-release ingress-nginx/ingress-nginx
首先我們編寫了一個使用 Node.js 開發的 echoapp
,如下所示:
// app.js
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.json());
const port = 3000;
app.get("/echo", (req, res) => {
var text = req.query.text;
console.log("Echoing: " + text);
res.send("Access token: " + req.headers["authorization"] + " Text: " + text);
});
app.listen(port, () => console.log(`Node App listening on port ${port}!`));
然後編寫了一個 /echo
接口,獲取了請求 header 頭中的 authorization
信息和客戶端的請求文本信息。
接下來我們這裏使用 Github 來實現認證授權,首先前往 https://github.com/settings/developers 註冊一個 OAuth 應用,如下所示:
這裏我們指定的應用 URL 爲 http://echo.dapr.local
,點擊 Register application
註冊新應用,註冊後在應用詳情頁面可以獲取到 clientId
信息,clientSecret
信息需要手動點擊 Generate a new client secret
按鈕獲取,將該兩個參數值記錄下來,後續會使用到。
然後將我們的 echoapp 應用部署到 Kubernetes 集羣中去,對應的資源清單如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
name: echoapp
labels:
app: echo
spec:
selector:
matchLabels:
app: echo
template:
metadata:
labels:
app: echo
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "echoapp"
dapr.io/app-port: "3000"
dapr.io/config: "pipeline"
spec:
containers:
- name: echo
image: dapriosamples/middleware-echoapp:latest
ports:
- containerPort: 3000
imagePullPolicy: Always
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo-ingress
spec:
ingressClassName: nginx
rules:
- host: echo.dapr.local
http:
paths:
- backend:
service:
name: echoapp-dapr
port:
number: 80
path: /
pathType: Prefix
注意上面我們創建了一個 Ingress 對象,用來暴露 echoapp 應用,需要注意的是我們並沒有主動創建 Service 對象,而是直接關聯的 echoapp-dapr
這個 Service,其 80 端口映射到 echoapp 應用的 dapr sidecar 中的 3500 端口。
另外注意上面爲應用添加的註解,其中有一個 dapr.io/config: "pipeline"
,這是用來指定使用的配置對象的,所以我們還需要創建一個名爲 pipeline
的 Configuration
對象:
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: pipeline
spec:
tracing:
samplingRate: "1"
zipkin:
endpointAddress: "http://zipkin.default.svc.cluster.local:9411/api/v2/spans"
httpPipeline:
handlers:
- type: middleware.http.oauth2
name: oauth2
這裏我們爲 echoapp 應用配置了一個 middleware.http.oauth2
類型的中間件,對應處理器的名稱爲 oauth2
,該處理器對應中 Dapr 中的一個 Component
組件,如下所示:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: oauth2
spec:
type: middleware.http.oauth2
version: v1
metadata:
- name: clientId
value: "<client-id>"
- name: clientSecret
value: "<client-secret>"
- name: scopes
value: ""
- name: authURL
value: "https://github.com/login/oauth/authorize"
- name: tokenURL
value: "https://github.com/login/oauth/access_token"
- name: redirectURL
value: "http://echo.dapr.local"
- name: authHeaderName
value: "authorization"
要使用 Dapr OAuth 中間件,需要配置以下信息:
-
Client ID
-
Client secret
-
Scopes
-
Authorization URL
-
Token URL
下表是一些比較熱門授權服務器的 Authorization/Token URLs:
我們這裏使用的 GitHub 的授權服務器配置,還要注意最後添加的 authHeaderName: authorization
屬性,上面我們代碼中就是從 Header 頭從獲取的 authorization
的值。
接下來創建上面的所有資源對象即可,創建完成後記得要將 echo.dapr.local
域名映射到 ingress 控制器。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
echoapp-b7f5469cb-hzvth 2/2 Running 0 51s
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
echoapp-dapr ClusterIP None <none> 80/TCP,50001/TCP,50002/TCP,9090/TCP 69m
$ kubectl get ingress echo-ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
echo-ingress nginx echo.dapr.local 192.168.0.52 80 59m
然後接下來我們就可以在瀏覽器中輸入 http://echo.dapr.local/v1.0/invoke/echoapp/method/echo?text=hello
來訪問應用中的 echo
方法了,正常訪問的時候會出現 502 錯誤,不符合預期。
這是因爲從 Dapr 1.4 版本開始,daprd 進程就被鎖定爲只接受來自 pod 邊界的連接,以實現良好的安全措施。如果要啓用外部調用 Dapr,則需要在應用中添加以下註解:
dapr.io/sidecar-listen-addresses: "0.0.0.0"
將該註解添加到 Deployment 中,更新後再次訪問 http://echo.dapr.local/v1.0/invoke/echoapp/method/echo?text=hello
就正常了,第一次會跳轉到 GitHub 進行授權。
授權後會自動跳轉回來,正常就會顯示 echo
接口返回的數據,包括 Access Token 的數據和 text
參數的值。
到這裏我們就實現了在 Dapr 中爲應用啓用 OAuth 中間件,對原始應用沒有任何侵入性。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Hx3msjMcSkGJ8Ce0xbpz5A