dapr集成flomesh实现跨集群服务调用(代码片段)

dotNET跨平台 dotNET跨平台     2023-03-05     665

关键词:

背景

随着技术和架构的不断演进,有着多运行时的态势:现代应用程序的基础能力不断地以独立运行时的方式从应用程序分离出来。这其中就有分布式应用运行时和服务网格两种运行时,今天这篇文章就为大家介绍 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牵手.net学习笔记:跨物理机负载均衡服务调用

...的host中跑同一服务,看看dapr内部的负载均衡是怎么实现的。说说现有的服务,两个服务,订单服务,支付服务;下完订单后同步调支付服务。一、demo项目的mock代码OrderS 查看详情

dapr+.net实战(十三)跨语言开发

...看Dapr的跨语言开发。我们使用golang,python,.NET来实现跨语言的服务调用,拓扑如下 我们继续使用.NET5 查看详情

使用flomesh强化springcloud服务治理(代码片段)

...| AddoZhang来源|云原生指北写在最前这篇是关于如何使用 Flomesh[1] 服务网格来强化SpringCloud的服务治理能力,降低SpringCloud微服务架构落地服务网格的门槛,实现“自主可控”。架构Architect环境搭建搭建Kubernetes环境,... 查看详情

手把手教你学dapr-4.服务调用

...空间。比如调用命名空间:production,AppID:nodeapp这在K8s集群中的跨名称空间调用中特别有用通过托管平台上的相互(mTLS)身份验证,包括通过DaprSentry服务的自动证书转移,可以确保Dapr应用程序之间的所有调用的安全。下图显示了... 查看详情

dapr+net6+k8s最小原型项目架构和实现-12-将dapr项目部署到k8s集群(代码片段)

...编写了Dapr项目:Dapr+Net6+K8S最小原型项目架构和实现-11-使用AspNetCore6.0编写Dapr项目Dapr+Net6+K8S最小原型项目架构和实现-11-使用AspNetCore6.0编写Dapr项目https://shuaihj.blog.csdn.net/article/details/122971594这一章,我们将Dapr... 查看详情

dapr+.netcore实战服务调用(代码片段)

服务调用是什么在分布式应用程序中的服务之间进行调用会涉及到许多挑战。例如:维护其他服务的地址。如何安全地调用服务。在发生短暂的暂时性错误时如何处理重试。分布式应用程序调用链路追踪。服务调用构建块通... 查看详情

dapr集成openpolicyagent实现接口的访问控制

大型项目中基本都包含有复杂的访问控制策略,特别是在一些多租户场景中,例如Kubernetes中就支持RBAC,ABAC等多种授权类型。Dapr的中间件OpenPolicyAgent将Rego/OPA策略应用到传入的DaprHTTP请求中。OpenPolicyAgentOpenPolicyAgent(简... 查看详情

dapr集成apisix做api网关

在这篇文章中,我将展示如何创建一个APISIX控制器,该控制器在Kubernetes集群中公开启用Dapr的应用程序。本质上,APISIX控制器将配置相同的标准Daprannotations以注入daprdsidecar。通过公开这个sidecar,它将允许外部应用... 查看详情

dapr+net6+k8s最小原型项目架构和实现-11-使用aspnetcore6.0编写dapr项目(代码片段)

...群中安装了Dapr:Dapr+Net6+K8S最小原型项目架构和实现-10-在Master节点上给K8S集群安装Dapr_敦格-CSDN博客Dapr+Net6+K8S最小原型项目架构和实现-10-在Master节点上给K8S集群安装Daprhttps://blog.csdn.net/shuaihj/article/details/122969361这... 查看详情

集成dapr的azure容器应用(代码片段)

微软在Ignite2021大会上发布了预览版的AzureContainerApps,这是一个完全托管的无服务器容器运行时,用于大规模构建和运行现代应用程序。从2021年11月2日起,Azure容器应用程序可在公共预览中使用。虽在服务到达GA之前&#x... 查看详情

跨集群流量调度实现kubernetes集群金丝雀升级(代码片段)

有了多集群服务和跨集群的流量调度之后,使用Kubernetes的方式会发生很大的变化。流量的管理不再限制单一集群内,而是横向跨越了多个集群。最重要的是这一切“静悄悄地”发生,对应用来说毫无感知。就拿Kubernet... 查看详情

dapr客户端搭配webapiclientcore玩耍服务调用

使用Dapr客户端处理服务调用,需要遵循的他的模式,通常代码是这个样子的:varclient=DaprClient.CreateInvokeHttpClient(appId:"routing");varresponse=awaitclient.GetAsJsonAsync($"/accounts/17", 查看详情

dapr+.net实战服务调用之grpc

...PI开发,默认使用协议缓冲区,允许与语言无关的实现。可 查看详情

caller服务调用

...tpClient的方式调用,那么如果我们现在需要更换为通过dapr实现服务调用,我们需要做哪些事情呢?Caller.Dapr入门如果我们的项目原本使用的是Caller.HttpClient,现在希望改为使用Caller.Dapr,那么我们需要做什么呢?改造Call本文来自... 查看详情

dapr云原生应用开发系列7:工作流集成

...一个很有意思的东西,Dapr和LogicApps这样的工作流引擎集成。Dapr工作流在1年多前,Dapr的孵化团队搞了一个很有意思的东西:把Dapr和LogicApps集成起来,实现Dapr内置的工作流引擎。官方文档:https://docs.dapr.io/devel... 查看详情

dapr+.net实战服务监测

...录都很重要,我们日常开发中为了实现这些功能需要集成很多功能,替换监控组件时成本也很高。Dapr可观测性模块将服务监测与应用程序分离。它自动捕获由Daprsidecar和Dapr服务生成的流量。它还公开性能指标、资源利用... 查看详情

手把手教你学dapr-4.服务调用(代码片段)

介绍通过使用服务调用,您的应用程序可以使用标准的gRPC或HTTP协议与其他应用程序可靠、安全地通信。为什么不直接用HttpClientFactory呢先问几个问题:如何发现和调用不同服务的方法如何安全地调用其他服务,并对... 查看详情

dapr牵手.net学习笔记:状态管理之docker-compose发布

Dapr牵手.NET学习笔记:想入非非的服务调用Dapr牵手.NET学习笔记:跨物理机负载均衡服务调用Dapr牵手.NET学习笔记:用docker-compose部署服务说明:为了给出demo的全貌,这篇有点长,如果有上一篇的基础,... 查看详情