如何在 kubernetes 集群中使用 FQDN 连接到 gRPC 服务器?

时间:2021-07-09 17:28:24

标签: c# kubernetes .net-core grpc kubernetes-service

我在本地机器上设置了 Docker 桌面 Kubernetes 集群,并且运行良好。 现在我正在尝试将 .Net Core gRPC 服务器和 .Net Core Console 负载生成器部署到我的集群。

我正在为 gRPC 应用程序使用 VisualStudio(2019) 的默认模板

服务器:

原型文件

syntax = "proto3";

option csharp_namespace = "KubernetesLoadSample";

package greet;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply);
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings.
message HelloReply {
  string message = 1;
}

.net 核心 gRPC 应用

public class GreeterService : Greeter.GreeterBase
{
    private readonly ILogger<GreeterService> _logger;
    public GreeterService(ILogger<GreeterService> logger)
    {
        _logger = logger;
    }

    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        _logger.LogInformation("Compute started");
        double result = 0;
        for (int i = 0; i < 10000; i++)
        {
            for (int j = 0; j < i; j++)
            {
                result += Math.Sqrt(i) + Math.Sqrt(j);
            }
        }
        return Task.FromResult(new HelloReply
        {
            Message = "Completed"
        }); ;
    }
}

和此项目的 DockerFile 如下,

FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["KubernetesLoadSample.csproj", "KubernetesLoadSample/"]
RUN dotnet restore "KubernetesLoadSample/KubernetesLoadSample.csproj"

WORKDIR "/src/KubernetesLoadSample"
COPY . .
RUN dotnet build "KubernetesLoadSample.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "KubernetesLoadSample.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "KubernetesLoadSample.dll"]

我能够使用

在本地检查此图像
PS C:\Users\user> docker run -it -p 8000:80 kubernetesloadsample:latest
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /app
info: KubernetesLoadSample.GreeterService[0]
      Compute started // called from BloomRPC Client

客户

Client 是一个 .net 控制台应用程序,循环调用服务器

    static async Task Main(string[] args)
    {
        var grpcServer = Environment.GetEnvironmentVariable("GRPC_SERVER");
        Channel channel = new Channel($"{grpcServer}", ChannelCredentials.Insecure);

        Console.WriteLine($"Sending load to port {grpcServer}");
        while(true)
        {
            try
            {
                var client = new Greeter.GreeterClient(channel);
                var reply = await client.SayHelloAsync(
                                  new HelloRequest { Name = "GreeterClient" });

                Console.WriteLine("result: " + reply.Message);
                await Task.Delay(1000);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"{DateTime.UtcNow} : tried to connect : {grpcServer}  Crashed : {ex.Message}");
            }
        }
    }

客户端的 Docker 文件:

FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["GrpcClientConsole.csproj", "GrpcClientConsole/"]
RUN dotnet restore "GrpcClientConsole/GrpcClientConsole.csproj"

WORKDIR "/src/GrpcClientConsole"
COPY . .
RUN dotnet build "GrpcClientConsole.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "GrpcClientConsole.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "GrpcClientConsole.dll"]

和部署文件如下,

---
apiVersion: v1
kind: Namespace
metadata:
  name: core-load
---
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  name: compute-server
  namespace: core-load
spec:
  replicas: 4
  selector:
    matchLabels:
      app: compute-server-svc
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: compute-server-svc
    spec:
      containers:
      - env:
        image: kubernetesloadsample:latest
        imagePullPolicy: Never
        name: compute-server-svc
        ports:
        - containerPort: 80
          name: grpc
        resources: {}
status: {}
---
apiVersion: v1
kind: Service
metadata:
  name: compute-server-svc
  namespace: core-load
spec:
  clusterIP: None
  
  ports:
  - name: grpc
    port: 5000
    targetPort: 80
    protocol: TCP
  selector:
    app: compute-server-svc
---
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  name: compute-client
  namespace: core-load
spec:
  replicas: 1
  selector:
  
    matchLabels:
      app: compute-client
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: compute-client
    spec:
      containers:
      - env:
        - name: GRPC_SERVER
          value: compute-server-svc.core-load.svc.cluster.local:5000
        image: grpc-client-console:latest
        imagePullPolicy: Never
        name: compute-client
        resources: {}
status: {}
---

问题

客户端无法使用此计算服务器-svc.core-load.svc.cluster.local:5000 名称连接 gRPC 服务器。我也尝试过计算服务器-svc.core-load,但面临以下问题

PS E:\study\core\k8sgrpc\KubernetesLoadSample> k get pods -n core-load
NAME                              READY   STATUS    RESTARTS   AGE
compute-client-bff5f666-cjwf5     1/1     Running   0          15s
compute-server-545567f589-5blkv   1/1     Running   0          15s
compute-server-545567f589-bv4r2   1/1     Running   0          15s
compute-server-545567f589-mdp2x   1/1     Running   0          15s
compute-server-545567f589-wdff5   1/1     Running   0          15s
PS E:\study\core\k8sgrpc\KubernetesLoadSample> k logs compute-client-bff5f666-cjwf5 -n  core-load --tail 5
07/09/2021 17:18:35 : tried to connect : compute-server-svc.core-load.svc.cluster.local:5000 Crashed : Status(StatusCode=Unavailable, Detail="failed to connect to all addresses")
07/09/2021 17:18:35 : tried to connect : compute-server-svc.core-load.svc.cluster.local:5000 Crashed : Status(StatusCode=Unavailable, Detail="failed to connect to all addresses")
07/09/2021 17:18:35 : tried to connect : compute-server-svc.core-load.svc.cluster.local:5000 Crashed : Status(StatusCode=Unavailable, Detail="failed to connect to all addresses")
07/09/2021 17:18:35 : tried to connect : compute-server-svc.core-load.svc.cluster.local:5000 Crashed : Status(StatusCode=Unavailable, Detail="failed to connect to all addresses")
07/09/2021 17:18:35 : tried to connect : compute-server-svc.core-load.svc.cluster.local:5000 Crashed : Status(StatusCode=Unavailable, Detail="failed to connect to all addresses")

我没有从与此类似的 stackoverflow 问题中得到任何解决方案,所以我创建了这个。

谁能告诉我我错过了什么或做错了什么?

TIA

1 个答案:

答案 0 :(得分:3)

您定义了您的服务:

clusterIP: None

用于创建无头服务。这可能是问题的原因,因此删除它可以解决您的错误。


当您创建 ClusterIP 类型的服务(这是默认类型)时,Kubernetes 会自动为该服务分配一个虚拟 IP(也称为集群 IP,正如类型所建议的),然后用于代理与 Pod 的通信由相关服务选择。

这意味着有一个“新”的 IP 地址(仅从集群内部可见),不同于分配给服务背后的 Pod(或单个 Pod)的各种 IP,然后使用某种方式路由流量对站在后面的 Pod 进行负载平衡。

如果你指定

clusterIP: None

您创建了一个无头服务。您基本上是在告诉 Kubernetes,您不希望将虚拟 IP 分配给服务。代理没有负载平衡,因为没有 IP 进行负载平衡。

相反,DNS 配置将为服务后面(选择)的每个 Pod 返回 A 记录(IP 地址)。

如果您的应用程序需要发现服务背后的每个 Pod,然后使用自己的 IP 地址做任何他们想做的事情,这会很有用。

也许是为了通过内部实现进行负载平衡,也许是因为不同的 Pod(隐藏在同一个服务之下)用于不同的事情......或者也许是因为这些 Pod 中的每一个都想发现其他 Pod(考虑多实例主Kafka 或 Zookeeper 等应用程序,例如)


我不确定您的问题到底是什么,这可能取决于该特定应用程序如何解析主机名..但是您不应该使用无头服务,除非您有必要决定哪个您要联系的 svc 选择的 Pod。

与虚拟 IP 相比,使用 DNS 循环进行负载平衡也(几乎总是)不是一个好主意。例如,每当它们重新启动时的 IP 地址),在访问它们时可能会出现网络问题......等等。

文档中有大量信息: https://kubernetes.io/docs/concepts/services-networking/service/

相关问题