使用 KubernetesClient 操作 kubernetes

Intro

我们的应用都是部署在 Kubernetes 上的,我们有一个服务内部有一层 MemoryCache,之前会依赖 Redis 的 Pub/Sub 来做缓存的更新,而 Redis 的 Pub/Sub 是一种不可靠的更新机制,容易发生消息丢失从而导致数据不一致的情况,之后用Stream代替了PUB/SUB。

之前我们有一次问题就是因为这个导致的,在 k8s 集群里有几个 Pod 的数据还是老数据,导致接口拿到的数据有时候是正确的有时候是错误的,这就很尴尬了,发现有问题,每次都去一个一个Pod 去检查就很烦,于是就想写一个脚本或者小工具来自动检查所有集群所有 Pod 的返回值,于是就有了这篇文章的探索。

实现原理

Kubernetes 集群通过 API Server 对外提供了 REST API 以方便通过 API 来操作 Kubernetes 集群

Components of Kubernetes

A diagram showing how the parts of a Kubernetes cluster relate to one another

kubectl 实际工作方式就是一个和 API Server 进行交互的命令行工具,所以我们完全可以自己根据 Kubernetes 提供的 API 来实现我们需要的功能,而 Kubernetes 官方也维护了一个 dotnet 的客户端  KubernetesClient,从而我们可以少写很多代码,直接使用这个 SDK 就可以比较方便的对 Kubernetes 进行操作了。

想一下,如果我们使用 kubectl 的话要如何检查一个集群所有的 pod 的返回结果呢?

首先我们可以通过 kubectl get pod 来获取一个 pod 列表,拿到 pod 列表之后就可以依次访问各个 pod 的 API 拿返回结果了,这里我想到的有两种方式,一种是在 pod 里执行 curl 命令,访问 API 拿到返回的数据,另一种方式是针对 pod 进行 port-forward,然后访问 localhost 就可以请求接口拿到返回数据了。

最后选择的是 port-forward 的方式,因为有的容器里可能并没有 curl,不够通用,所以放弃了在容器里 curl 的方式。

InspectSample

首先我们来看一下 KubernetesClient 基本的使用吧,来看一个简单的示例,遍历所有的 namespace,依次获取每个 namespace 下的 pod

首先我们需要构建一个 IKubernetes 实例以和 Kubenetes 进行通信,在此之前我们需要先构建 KubernetesClientConfiguration,基本使用如下:

// 使用默认的配置,默认的 kubernetes 配置文件,Windows 是 `%PROFILE%/.kube/config`,Linux 是 `%HOME%/.kube/config`
var config = KubernetesClientConfiguration.BuildDefaultConfig();
// 使用指定的配置文件
//var config = KubernetesClientConfiguration.BuildConfigFromConfigFile(file);
IKubernetes kubernetes = new Kubernetes(config);

InspectSample:

var namespaces = _kubernetes.ListNamespace();
foreach (var ns in namespaces.Items)
{var namespaceName = ns.Metadata.Name;Console.WriteLine($"Namespace:{namespaceName}");var pods = _kubernetes.ListNamespacedPod(namespaceName);foreach (var pod in pods.Items){var podName = pod.Metadata.Name;Console.WriteLine($"  Pod: {podName}, Labels: {pod.Metadata.Labels.ToJson()}");var containers = pod.Spec.Containers;foreach (var container in containers){Console.WriteLine($"    Container: {container.Name}");}}
}

输出结果如下:

需要注意的是,如果用户没有权限访问所有的命名空间时,遍历命名空间的时候就会报错

CreatePodSample

上面是一个简单列出 pod 的使用,接着我们来看一个创建 Pod 的示例,我们执行 kubectl delete po/reservation 先把之前的 pod 删掉,然后再通过代码创建一个 pod,创建 pod 的代码如下:

const string namespaceName = "default";
const string podName = "reservation";
const string containerName = "reservation";
const string image = "weihanli/activityreservation:standalone";// // try delete pod if exits
// try
// {
//     await _kubernetes.DeleteNamespacedPodAsync(podName, namespaceName);
//     Console.WriteLine($"Pod:{podName} deleted");
// }
// catch
// {
//     //
// }
// await ListPods();var pod = new V1Pod
{Metadata = new V1ObjectMeta{Name = podName, NamespaceProperty = namespaceName,Labels = new Dictionary<string, string>(){{ "app", "reservation" }}},Spec = new V1PodSpec(new List<V1Container>(){new V1Container(containerName){Image = image,Ports = new List<V1ContainerPort> {new(80)}}}),
};
await _kubernetes.CreateNamespacedPodAsync(pod, namespaceName);await ListPods();async Task ListPods()
{var pods = await _kubernetes.ListNamespacedPodAsync(namespaceName);foreach (var item in pods.Items){Console.WriteLine($"{item.Metadata.Name}, {item.Metadata.Labels.ToJson()}");}
}

输出结果如下:

reservation, {"app":"reservation"}

Port-Forward Sample

最后来看一下我们的 Port-Forward 的示例,示例代码如下:

首先定义一个通用一点的 Port-Forward 的方法,根据官方给出的示例做了一些改动,更好的支持了 CancellationToken

// Port-forward, modified from https://github.com/kubernetes-client/csharp/blob/master/examples/portforward/PortForward.cs#L24
private static async Task PortForward(IKubernetes client, V1Pod pod, CancellationToken cancellationToken,int hostPort)
{Console.WriteLine($"Port-forward started for pod {pod.Metadata.Name}");// Note this is single-threaded, it won't handle concurrent requests well...var webSocket = await client.WebSocketNamespacedPodPortForwardAsync(pod.Metadata.Name, pod.Namespace(),new[] {80}, "v4.channel.k8s.io", cancellationToken: cancellationToken);var demux = new StreamDemuxer(webSocket, StreamType.PortForward);demux.Start();var stream = demux.GetStream((byte?) 0, (byte?) 0);var ipAddress = IPAddress.Loopback;var localEndPoint = new IPEndPoint(ipAddress, hostPort);var listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);listener.Bind(localEndPoint);listener.Listen(100);var handler = listener.Accept();cancellationToken.Register(() =>{try{handler.Close();listener.Close();handler.Dispose();listener.Dispose();demux.Dispose();webSocket.Dispose();}catch{//}Console.WriteLine("Port-forward closed");});cancellationToken.ThrowIfCancellationRequested();// Note this will only accept a single connectionvar accept = Task.Run(() =>{var bytes = new byte[4096];while (!cancellationToken.IsCancellationRequested){var bytesRec = handler.Receive(bytes);stream.Write(bytes, 0, bytesRec);if (bytesRec == 0 || Encoding.ASCII.GetString(bytes, 0, bytesRec).IndexOf("<EOF>", StringComparison.OrdinalIgnoreCase) > -1) break;}}, cancellationToken);var copy = Task.Run(() =>{var buff = new byte[4096];while (!cancellationToken.IsCancellationRequested){var read = stream.Read(buff, 0, 4096);handler.Send(buff, read, 0);}}, cancellationToken);await Task.WhenAny(accept, copy);
}

使用示例如下,使用上面创建的 pod 来演示 port-forward:

var pod = (await _kubernetes.ListNamespacedPodAsync("default")).Items.First(x => x.Name().Equals("reservation", StringComparison.Ordinal));using var cts = new CancellationTokenSource();
var portForwardTask = PortForward(_kubernetes, pod, cts.Token, 8000);try
{using var httpClient = new HttpClient {Timeout = TimeSpan.FromSeconds(10)};while (true){try{var response = await httpClient.GetAsync("http://localhost:8000/api/notice", cts.Token);Console.WriteLine(response.StatusCode);if (response.IsSuccessStatusCode){Console.WriteLine(await response.Content.ReadAsStringAsync(cts.Token));break;}}catch{//}Console.WriteLine("Waiting for port-forward ready...");await Task.Delay(1000, cts.Token);}
}
catch (Exception e)
{Console.WriteLine(e);
}
finally
{cts.Cancel();
}// wait for portForward exit
try
{await portForwardTask;
}
catch
{// ignore port-forward exit exception
}

输出结果如下:

More

通过上面的代码,我们已经可以实现访问 pod 里容器的接口了,只需要将找到 pod 的代码和 port-forward 的代码组合一下就可以达到我们的目标了,对于多个集群可以使用多个配置文件,遍历一下就可以了,如果是在一个配置文件中也可以先获取所有的 cluster,然后在构建 config 的时候指定一个 currentContext 就可以了

有了这个工具下次想检查每个 Pod 返回结果就只需要跑一下就可以比较方便的拿到所有 Pod 的返回结果了

更多 KubernetesClient 使用示例可以参考官方给出的示例:https://github.com/kubernetes-client/csharp/tree/master/examples

上面的代码也可以从我的 Github 上获取:https://github.com/WeihanLi/SamplesInPractice/tree/master/KubernetesClientSample

希望对你有所帮助~

References

  • https://github.com/kubernetes-client/csharp

  • https://kubernetes.io/docs/reference/using-api/client-libraries/

  • https://kubernetes.io/docs/reference/using-api/

  • https://github.com/WeihanLi/SamplesInPractice/tree/master/KubernetesClientSample

使用 KubernetesClient 操作 kubernetes相关推荐

  1. java mesos kubernete_Fabric8操作Kubernetes(一)

    背景 机器学习平台资源管理之前使用的是Mesos+Marathon,随着业务的发展,Mesos+Marathon已经不能满足我们的需求,所以需要使用K8s取代Mesos+Marathon.Alpha管 ...

  2. job for nginx.service failed_用Python操作Kubernetes的Job

    关于Kubernetes的Python SDK,几乎只有官方项目的examples.关于Job的基本增删改查操作,可以参考job_crud.py.但是,这只是基本用法,缺乏一些实用细节. 本文给出Py ...

  3. 抓狐狸python_​用Python操作Kubernetes的Job

    本文给出Python SDK操作Kubernetes Job的更多示例代码,以及相关解释. pip install kubernetes 初始化 from kubernetes.client impo ...

  4. Java api 操作 kubernetes

    文章目录 Java api 操作 kubernetes 一.api接口访问方式 授权方式 不授权方式 二.Java api 操作 k8s k8s初始化 创建命名空间 创建Deployment应用 创建 ...

  5. ABAP和Go语言的初始化操作, Kubernetes的Init Container

    ABAP Go 作用同上: Kubernetes Init Container在所有容器运行之前执行(run-to-completion),常用来初始化配置. YMAL文件里的具体用法: 要获取更多J ...

  6. kubernetes 客户端KubeClient使用及常用api

    KubeClient是kubernetes 的C#语言客户端简单易用,KubeClient是.NET Core(目标netstandard1.4)的可扩展Kubernetes API客户端, gith ...

  7. Kubernetes 1.5安装

    2019独角兽企业重金招聘Python工程师标准>>> Kubernetes 1.5安装 博客分类: Kubernetes Kubernetes从1.3开始引入kubeadm来试图简 ...

  8. 使用 C# 开发 Kubernetes 组件,获取集群资源信息

    Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/cou ...

  9. Kubernetes参数:dryRun理解

    声明式(Declarative)配置管理,也称为配置即代码(configuration-as-code),是Kubernetes的关键优势之一. 它允许用户提交所需的集群状态,并跟踪不同的版本,通过C ...

最新文章

  1. @2021 高考生,用 Python 分析专业“钱景”
  2. centos7下搭建git和gitlab版本库
  3. SaltStack介绍——SaltStack是一种新的基础设施管理方法开发软件,简单易部署,可伸缩的足以管理成千上万的服务器,和足够快的速度控制,与他们交流...
  4. spoj 375 Query on a tree
  5. CSS选择器的声明与嵌套
  6. scatter python_python数据可视化(matplotlib、scatter)
  7. 816 - Abbott's Revenge
  8. 初识【jQuery】,入门必看!
  9. Java基础学习总结(34)——HTTP协议详解
  10. 《 .NET软件设计新思维》一书作者MSDN课程日程
  11. Oracle从10g升级到11g详细步骤
  12. Matlab科研绘图颜色补充(特别篇5)—176种美国传统颜色
  13. Bootstrap文字排版方面css实用类
  14. 我的十年十念 ——十年工作感言
  15. 【九层之台】Web开发教程:0. 准备
  16. matlab corner 舍弃,成长就是不断地丢弃与拾取 — 读The Glass Castle《玻璃城堡》有感...
  17. 信息系统项目管理师---第十五、十六章 知识管理及项目变更管理历年考题
  18. 访问者模式的java语言_Java 设计模式系列(二三)访问者模式(Vistor)
  19. isFinite() 如果参数是 NaN,正无穷大或者负无穷大,会返回 false,其他返回 true
  20. 为战而生的联想拯救者Pro,你值得拥有的手游神器

热门文章

  1. 查看/修改Linux时区和时间
  2. WPF DataGrid根据内容设置行颜色
  3. java虚拟机之内存分配
  4. angularjs中使用swiper时不起作用,最后出现空白位
  5. 关于8位AD_DA转换芯片的采样率问题
  6. Linux中一些常用的很巧妙的命令
  7. sybase sp_procxmode简述
  8. [Programming WCF Services]Chapter 1. WCF Essentials - Metadata Exchange
  9. perl学习笔记——目录操作
  10. 推荐我看过的几本好书给大家!(2)