c# - 如何在 Kubernetes 集群中使用 FQDN 连接到 gRPC 服务器?
问题描述
我在本地机器上安装了 Docker Desktop 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
客户
客户端是一个 .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: {}
---
问题
客户端无法使用此 compute-server-svc.core-load.svc.cluster.local:5000 名称连接 gRPC 服务器。我也试过 compute-server-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
解决方案
您使用以下内容定义了您的服务:
clusterIP: None
用于创建无头服务。这可能是问题的原因,因此删除它可以解决您的错误。
当您创建ClusterIP
类型服务(这是默认类型)时,Kubernetes 会自动为服务分配一个虚拟 IP(也称为集群 IP,如类型所暗示的那样),然后用于代理与相关服务选择的 Pod 的通信。
这意味着有一个“新”IP 地址(仅从集群内部可见),不同于分配给服务后面的 Pod(或单个 Pod)的各种 IP,然后通过某种负载平衡将流量路由到站在后面的豆荚。
如果您指定
clusterIP: None
你创建了一个无头服务。您基本上是在告诉 Kubernetes,您不希望将虚拟 IP 分配给服务。代理没有负载平衡,因为没有 IP 来进行负载平衡。
相反,DNS 配置将为服务后面(选择)的每个 Pod 返回 A 记录(IP 地址)。
如果您的应用程序需要发现服务背后的每个 Pod,然后自己使用 IP 地址做任何他们想做的事情,这将很有用。
可能是为了通过内部实现进行负载平衡,可能是因为不同的 Pod(在同一个服务后面)用于不同的事情..或者可能是因为这些 Pod 中的每一个都想发现其他 Pod(考虑多实例主要应用程序,例如例如 Kafka 或 Zookeeper)
我不确定您的问题到底是什么,这可能取决于该特定应用程序如何解析主机名..但您不应该使用无头服务,除非您有必要决定选择哪个 Pod通过您要联系的 svc。
与虚拟 IP 相比,使用 DNS 循环进行负载平衡也(几乎总是)不是一个好主意。因为应用程序可以缓存 DNS 解析,并且如果 Pod 然后更改 IP 地址(因为 Pod 是短暂的,它们随时更改 IP 地址例如,它们重新启动),在到达它们时可能存在网络问题......等等。
文档中有大量信息: https ://kubernetes.io/docs/concepts/services-networking/service/
推荐阅读
- c++ - 在 tinkercad 中使用伺服库导致奇怪的行为
- html - 为什么使用 justify-content: center 时我的按钮不居中
- cron - 如何使用非标准 schedule_interval 控制计划 DAG 的首次运行
- python - 如何使用 exchangelib 连接到使用 Outlook Web App (OWA) 的工作电子邮件?
- python - 使用 scikit-neuralnetwork 时 scikit-learn 出错
- javascript - 测试 Lambda 函数时 index.handler 未定义或未导出
- python - 在 jinja-flask 框架中发布 json 数据
- jquery - 未选择选项并且不会清除错误时,Jquery 验证进行验证
- typescript - 将 pg-promise 任务与 graphql 和 dataloader 一起使用
- python - 如何根据用户组为字段设置odoo域过滤器?