Dapr 集成 Flomesh 實現跨集羣服務調用
背景
隨着技術和架構的不斷演進,有着多運行時的態勢:現代應用程序的基礎能力不斷地以獨立運行時的方式從應用程序分離出來。這其中就有分佈式應用運行時和服務網格兩種運行時,今天這篇文章就爲大家介紹 Dapr 與 Flomesh 服務網格的集成進行跨集羣的服務調用來實現 “真正的” 多集羣互聯互通。
多集羣
Kubernetes 秉持着松耦合和可擴展的設計理念,帶來了 Kubernetes 生態的蓬勃發展。但這些大部分先限制在單一集羣內,然後由於種種原因和目的企業內部創建的集羣越來越多,比如單集羣故障、監管要求、異地多機房可用區容災、出於敏捷、降本考慮的混合雲、多雲部署、單一集羣的承載能力受限、多版本 Kubernetes 集羣共存等。
Dapr
Dapr[1] 是一個分佈式應用工具包,通過提供簡單而穩定的 API 實現應用程序和外圍功能組件的解耦合,讓開發人員可以聚焦在業務功能的研發。同時與外圍組件的解耦,也使得應用程序更加的便攜、更加雲原生,企業可以輕鬆低成本地將應用遷移到不同的環境中。
Dapr 工具包提供了豐富的功能,如服務調用、彈性策略、狀態存儲、發佈 / 訂閱、綁定、分佈式鎖、名稱解析等,但對於高級的服務治理功能如灰度、跨集羣服務調用沒有支持。
Flomesh 服務網格
微服務架構興起之後,隨着規模越來越大,服務治理的難度和碎片化顯著提升,服務網格的出現使得這些問題迎刃而解。服務網格是一個處理服務間通訊的專用的基礎設施層,通過它可以透明地添加可觀測性、流量管理和安全性等功能,而無需將其添加到你的代碼中。
Flomesh 服務網格使用可編程代理 Pipy[2] 爲核心提供東西、南北向的流量管理。通過基於 L7 的流量管理能力,突破計算環境間的網絡隔離,建議一個虛擬的平面網絡,使不同計算環境中應用可以互相通信。可以想象,Flomesh 服務網格是覆蓋多集羣的 “大網格”。
示例介紹
服務端 NodeApp 是個 Dapr 應用, 在 Dapr hello-kubernetes 示例 [3] 中的 NodeApp 基礎上做了修改,返回響應時會顯示當前的集羣名;客戶端 curl 用於向 NodeApp 發送請求,但並沒有聲明爲 Dapr 應用。
NodeApp 中有三個 endpoint:
-
•
GET /ports
返回當前應用可訪問的端口 -
•
POST /neworder
創建新的訂單 -
•
GET /order
查詢訂單
下面的演示中會從集羣的創建開始,一步步介紹環境的配置、各個組件的安裝和配置、應用的部署等等。
一鍵安裝腳本
我們也準備腳本進行一鍵安裝和快速的體驗,免除環境和組件配置的繁瑣。可以 訪問 GitHub 獲取腳本 [4] 內容。
使用該腳本之前,需要確保系統裝已經安裝了 Docker
和 kubectl
。腳本運行時會進行檢查,並安裝 k3d
、helm
、jq
、pv
等工具。
-
•
flomesh.sh
- 不提供任何參數,腳本會創建 4 個集羣、完成環境安裝配置並運行演示 -
•
flomesh.sh -h
- 打印幫助信息 -
•
flomesh.sh -i
- 創建 4 個集羣、完成環境的安裝和配置 -
•
flomesh.sh -d
- 運行演示 -
•
flomesh.sh -r
- 清理演示相關的資源 -
•
flomesh.sh -u
- 刪除所有集羣
執行下面的命令,即可完成環境安裝配置和演示的運行。
curl -sL https://raw.githubusercontent.com/addozhang/flomesh-dapr-demo/main/flomesh.sh | bash -
逐步演示
前提條件
進行演示,我們需要如下的工具:
-
• Docker
-
• Kubectl
-
• K3d
-
• Helm
-
• kubectx
創建多集羣
獲取本機 IP 地址作爲集羣間的通信地址。
export HOST_IP=10.0.0.13
執行下面創建 4 個集羣:control-plane
、cluster-1
、cluster-2
和 cluster-3
。
API_PORT=6444 #6444 6445 6446 6447
PORT=80 #81 82 83
for CLUSTER_NAME in control-plane cluster-1 cluster-2 cluster-3
do
k3d cluster create ${CLUSTER_NAME} \
--image docker.io/rancher/k3s:v1.23.8-k3s2 \
--api-port "${HOST_IP}:${API_PORT}" \
--port "${PORT}:80@server:0" \
--servers-memory 4g \
--k3s-arg "--disable=traefik@server:0" \
--network multi-clusters \
--timeout 120s \
--wait
((API_PORT=API_PORT+1))
((PORT=PORT+1))
done
安裝 FSM
helm repo add fsm https://charts.flomesh.io
helm repo update
export FSM_NAMESPACE=flomesh
export FSM_VERSION=0.2.1-alpha.3
for CLUSTER_NAME in control-plane cluster-1 cluster-2 cluster-3
do
kubectx k3d-${CLUSTER_NAME}
sleep 1
helm install --namespace ${FSM_NAMESPACE} --create-namespace --version=${FSM_VERSION} --set fsm.logLevel=5 fsm fsm/fsm
sleep 1
kubectl wait --for=condition=ready pod --all -n $FSM_NAMESPACE --timeout=120s
done
將集羣 cluster-1
、cluster-2
和 cluster-3
納入集羣 control-plane
的管理。
kubectx k3d-control-plane
sleep 1
PORT=81
for CLUSTER_NAME in cluster-1 cluster-2 cluster-3
do
kubectl apply -f - <<EOF
apiVersion: flomesh.io/v1alpha1
kind: Cluster
metadata:
name: ${CLUSTER_NAME}
spec:
gatewayHost: ${HOST_IP}
gatewayPort: ${PORT}
kubeconfig: |+
`k3d kubeconfig get ${CLUSTER_NAME} | sed 's|^| |g' | sed "s|0.0.0.0|$HOST_IP|g"`
EOF
((PORT=PORT+1))
done
安裝 osm-edge
下載 CLI
system=$(uname -s | tr [:upper:] [:lower:])
arch=$(dpkg --print-architecture)
release=v1.3.1
curl -L https://github.com/flomesh-io/osm-edge/releases/download/$release/osm-edge-$release-$system-$arch.tar.gz | tar -vxzf -
./${system}-${arch}/osm version
cp ./${system}-${arch}/osm /usr/local/bin/
將服務網格 osm-edge 安裝到集羣 cluster-1
、cluster-2
和 cluster-3
。控制平面不處理應用流量,無需安裝。
export OSM_NAMESPACE=osm-system
export OSM_MESH_NAME=osm
for CLUSTER_NAME in cluster-1 cluster-2 cluster-3
do
kubectx k3d-${CLUSTER_NAME}
DNS_SVC_IP="$(kubectl get svc -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].spec.clusterIP}')"
osm install \
--mesh-name "$OSM_MESH_NAME" \
--osm-namespace "$OSM_NAMESPACE" \
--set=osm.certificateProvider.kind=tresor \
--set=osm.image.pullPolicy=Always \
--set=osm.sidecarLogLevel=error \
--set=osm.controllerLogLevel=warn \
--timeout=900s \
--set=osm.localDNSProxy.enable=true \
--set=osm.localDNSProxy.primaryUpstreamDNSServerIPAddr="${DNS_SVC_IP}"
done
kubectl get svc -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 43h
放行 pod 中訪問 apiserver 的流量,不經過 sidecar。
for CLUSTER_NAME in cluster-1 cluster-2 cluster-3
do
kubectx k3d-${CLUSTER_NAME}
kubectl patch meshconfig osm-mesh-config -n $OSM_NAMESPACE -p '{"spec":{"traffic":{"outboundIPRangeExclusionList":["10.43.0.1/32"]}}}' --type=merge
done
安裝 Dapr
將 Dapr 安裝到集羣 cluster-1
、cluster-2
和 cluster-3
。
for CLUSTER_NAME in cluster-1 cluster-2 cluster-3
do
kubectx k3d-${CLUSTER_NAME}
dapr init --kubernetes \
--enable-mtls=false \
--wait
done
查看組件運行狀態。
dapr status -k
NAME NAMESPACE HEALTHY STATUS REPLICAS VERSION AGE CREATED
dapr-placement-server dapr-system True Running 1 1.9.6 2m 2023-02-09 10:36.51
dapr-operator dapr-system True Running 1 1.9.6 2m 2023-02-09 10:36.51
dapr-dashboard dapr-system True Running 1 0.11.0 2m 2023-02-09 10:36.51
dapr-sentry dapr-system True Running 1 1.9.6 2m 2023-02-09 10:36.51
dapr-sidecar-injector dapr-system True Running 1 1.9.6 2m 2023-02-09 10:36.51
查看組件 Service 及端口。
kubectl get svc -n dapr-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dapr-placement-server ClusterIP None <none> 50005/TCP,8201/TCP 5h50m
dapr-sidecar-injector ClusterIP 10.43.12.213 <none> 443/TCP 5h50m
dapr-webhook ClusterIP 10.43.103.31 <none> 443/TCP 5h50m
dapr-dashboard ClusterIP 10.43.172.156 <none> 8080/TCP 5h50m
dapr-api ClusterIP 10.43.126.14 <none> 80/TCP 5h50m
dapr-sentry ClusterIP 10.43.41.10 <none> 80/TCP 5h50m
不攔截 dapr 組件和 redis 端口的流量。
for CLUSTER_NAME in cluster-1 cluster-2 cluster-3
do
kubectx k3d-${CLUSTER_NAME}
kubectl patch meshconfig osm-mesh-config -n $OSM_NAMESPACE -p '{"spec":{"traffic":{"outboundPortExclusionList":[50005,8201,6379]}}}' --type=merge
done
部署 Redis
version: '3'
services:
redis:
image: redis:latest
container_name: redis
ports:
- 6379:6379
volumes:
- ./data:/data
command: redis-server --appendonly yes --requirepass changeme
創建示例命名空間
export NAMESPACE=dapr-test
for CLUSTER_NAME in cluster-1 cluster-2 cluster-3
do
kubectx k3d-${CLUSTER_NAME}
kubectl create namespace $NAMESPACE
osm namespace add $NAMESPACE
done
註冊 Redis State Store 組件
export NAMESPACE=dapr-test
for CLUSTER_NAME in cluster-1 cluster-2 cluster-3
do
kubectx k3d-${CLUSTER_NAME}
kubectl create secret generic redis -n $NAMESPACE --from-literal=redis-password=changeme
kubectl apply -n $NAMESPACE -f - <<EOF
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: 10.0.0.13:6379
- name: redisPassword
secretKeyRef:
name: redis
key: redis-password
auth:
secretStore: kubernetes
EOF
done
部署示例應用
在集羣 cluster-1
和 cluster-3
的 httpbin
命名空間(由網格管理,會注入 sidecar)下,部署 nodeapp
應用。
export NAMESPACE=dapr-test
for CLUSTER_NAME in cluster-1 cluster-2 cluster-3
do
kubectx k3d-${CLUSTER_NAME}
kubectl apply -n $NAMESPACE -f - <<EOF
kind: Service
apiVersion: v1
metadata:
name: nodeapp
labels:
app: node
spec:
selector:
app: node
ports:
- protocol: TCP
port: 3000
targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodeapp
labels:
app: node
spec:
replicas: 1
selector:
matchLabels:
app: node
template:
metadata:
labels:
app: node
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "nodeapp"
dapr.io/app-port: "3000"
dapr.io/enable-api-logging: "true"
spec:
containers:
- name: node
image: addozhang/dapr-nodeapp
env:
- name: APP_PORT
value: "3000"
- name: CLUSTER_NAME
value: ${CLUSTER_NAME}
ports:
- containerPort: 3000
imagePullPolicy: Always
EOF
done
在集羣 cluster-2
的命名空間 curl
下部署 curl
應用,這個命名空間是被網格管理的,注入的 sidecar 會完全流量的跨集羣調度。
export NAMESPACE=curl
kubectx k3d-cluster-2
kubectl create namespace ${NAMESPACE}
osm namespace add ${NAMESPACE}
kubectl apply -n ${NAMESPACE} -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: curl
---
apiVersion: v1
kind: Service
metadata:
name: curl
labels:
app: curl
service: curl
spec:
ports:
- name: http
port: 80
selector:
app: curl
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: curl
spec:
replicas: 1
selector:
matchLabels:
app: curl
template:
metadata:
labels:
app: curl
annotations:
dapr.io/enabled: "true"
dapr.io/app-id: "curl"
dapr.io/enable-api-logging: "true"
dapr.io/log-level: "debug"
spec:
serviceAccountName: curl
containers:
- image: curlimages/curl
imagePullPolicy: IfNotPresent
name: curl
command: ["sleep", "365d"]
EOF
sleep 3
kubectl wait --for=condition=ready pod -n ${NAMESPACE} --all --timeout=60s
導出服務
export NAMESPACE=dapr-test
for CLUSTER_NAME in cluster-1 cluster-3
do
kubectx k3d-${CLUSTER_NAME}
kubectl apply -n $NAMESPACE -f - <<EOF
apiVersion: flomesh.io/v1alpha1
kind: ServiceExport
metadata:
name: nodeapp
spec:
serviceAccountName: '*'
pathRewrite:
from: '^/nodeapp/?'
to: '/'
rules:
- portNumber: 3000
path: '/nodeapp'
pathType: Prefix
EOF
sleep 1
done
導出後的服務,FSM 會自動爲其創建 Ingress 規則,有了規則之後就可以通過 Ingress 來訪問這些服務。
for CLUSTER_NAME_INDEX in 1 3
do
CLUSTER_NAME=cluster-${CLUSTER_NAME_INDEX}
((PORT=80+CLUSTER_NAME_INDEX))
kubectx k3d-${CLUSTER_NAME}
echo "Getting service exported in cluster ${CLUSTER_NAME}"
echo '-----------------------------------'
kubectl get serviceexports.flomesh.io -A
echo '-----------------------------------'
curl -s "http://${HOST_IP}:${PORT}/ports"
echo '-----------------------------------'
done
測試
切換到集羣 cluster-2
在 curl pod 中發起請求進行測試。
kubectx k3d-cluster-2
curl_client="$(kubectl get pod -n curl -l app=curl -o jsonpath='{.items[0].metadata.name}')"
發送請求訪問 nodeapp 時報錯,這是因爲在 cluster-2
中並未部署 nodeapp 應用。默認情況下,在不指定跨集羣的流量策略時,只會嘗試調用本地服務,不會將流量調度到其他的集羣。
kubectl exec "${curl_client}" -n curl -c curl -- curl -s http://nodeapp.dapr-test:3000/ports
command terminated with exit code 7
設置流量策略
跨集羣的流量策略支持三種:
-
•
Locality
:只使用本集羣的服務,也是默認類型。這就是爲什麼我們不提供任何全局策略的時候,訪問nodeapp
應用會失敗,因爲在集羣cluster-2
中並沒有該服務。 -
•
FailOver
:當本集羣訪問失敗時纔會代理到其他集羣,也就是常說的故障遷移,類似主備。 -
•
ActiveActive
:正常情況下也會代理到其他集羣,類似多活。
接下來我們創建並應用如下的 ActiveActive
策略,同樣是在 cluster-2
集羣中進行操作。
kubectl apply -n dapr-test -f - <<EOF
apiVersion: flomesh.io/v1alpha1
kind: GlobalTrafficPolicy
metadata:
name: nodeapp
spec:
lbType: ActiveActive
targets:
- clusterKey: default/default/default/cluster-1
weight: 100
- clusterKey: default/default/default/cluster-3
weight: 100
EOF
再次嘗試發送請求,可以收到成功的響應。
kubectl exec "${curl_client}" -n curl -c curl -- curl -s http://nodeapp.dapr-test:3000/ports
{"DAPR_HTTP_PORT":"3500","DAPR_GRPC_PORT":"50001"}, from cluster: cluster-3
這時請求 /neworder
嘗試寫入訂單數據。
kubectl exec "${curl_client}" -n curl -c curl -- curl -si --request POST --data '{"data":{"orderId":"42"}}' --header Content-Type:application/json --header dapr-app-id:nodeapp http://nodeapp.dapr-test:3000/neworder
created order via cluster: cluster-1
多次請求 /order
,可以發現請求被轉發到了不同的集羣進行處理。
kubectl exec "${curl_client}" -n curl -c curl -- curl -s http://nodeapp.dapr-test:3000/order
{"orderId":"42"}, from cluster: cluster-3
kubectl exec "${curl_client}" -n curl -c curl -- curl -s http://nodeapp.dapr-test:3000/order
{"orderId":"42"}, from cluster: cluster-1
引用鏈接
[1]
Dapr: https://dapr.io
[2]
Pipy: https://github.com/flomesh-io/pipy
[3]
Dapr hello-kubernetes 示例: https://github.com/dapr/quickstarts/tree/master/tutorials/hello-kubernetes
[4]
訪問 GitHub 獲取腳本: https://github.com/addozhang/flomesh-dapr-demo
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Y-MewxHVMULKDi4_cbl6Yw