Dapr 集成 Flomesh 实现跨集群服务调用

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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。脚本运行时会进行检查,并安装 k3dhelmjqpv 等工具。

  • • 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-planecluster-1cluster-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-1cluster-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-1cluster-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-1cluster-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

以上是关于Dapr 集成 Flomesh 实现跨集群服务调用的主要内容,如果未能解决你的问题,请参考以下文章

Dapr集成之GRPC 接口

Dapr牵手.NET学习笔记:跨物理机负载均衡服务调用

Dapr + .NET 实战(十三)跨语言开发

使用 Flomesh 强化 Spring Cloud 服务治理

Flomesh Ingress 使用实践基础功能

Dapr 集成 APISIX 做API网关