
Kubernetes .Net6 集群管理(一)
对于Kubernetes集群的管理,.net提供了KubernetesClient
库,可以对k8s集群通过简单的代码进行管理。其底层是通过找到本地的KuberConfig对集群发起的API请求。
安装
安装相关依赖库。
<PackageReference Include="KubernetesClient" Version="8.0.12" />
<!-- 解析库 -->
<PackageReference Include="JsonPatch.Net" Version="1.1.2" />
Demo展示
初始化
通过KubernetesClientConfiguration.BuildDefaultConfig();
方法可以获取到我们的本地的KUBECONFIG文件信息,它会先去环境变量中去找KUBECONFIG
的地址,若没有该环境变量,便去用户名称下的.kube
目录下去找该文件,如果还没有就判断自己本身是不是在集群里面,是就会通过集群中的ServiceAccount
拿到相关权限,还不是就返回本地的8080
的配置。(相关代码如图所示)
接着我们将初始化Kubernetes
的实例.
KubernetesClientConfiguration k8sConfig;
Kubernetes kubeClient;
public TestKubernetesClient()
{
// 使用凭据构建一个客户端
k8sConfig = KubernetesClientConfiguration.BuildDefaultConfig();
// k8s 实例
kubeClient = new Kubernetes(k8sConfig);
}
创建pod
可以通过定义V1Pod
实例执行CreateNamespacedPodAsync
方法进行创建相关Pod。
这里我们在default
命名空间下创建一个镜像为burlyluo/nettoolbox
名为busybox
的pod,并为它添加run=busybox
标签。
[DataTestMethod]
[DataRow("default", "busybox", "burlyluo/nettoolbox", "run=busybox")]
public async Task TestCreatePod(string mynamespace, string Podname,string image,string labels)
{
#region 创建一个pod
var pod = new V1Pod()
{
ApiVersion = "v1",
Kind = "Pod",
Spec = new V1PodSpec() { Containers = new List<V1Container>() },
Metadata = new V1ObjectMeta() { Labels = new Dictionary<string, string>() }
};
// 取名
pod.Metadata.Name = Podname;
// 标记标签
if (!string.IsNullOrEmpty(labels))
{
var labelsDictionary = labels.Split(',').Select(x => x.Split('=')).ToDictionary(x => x[0], y => y[1]);
foreach (var item in labelsDictionary)
{
pod.Metadata.Labels[item.Key] = item.Value;
}
}
// 创建一个容器
var container = new V1Container(Podname)
{
// Command
// Args
// Env
// Ports
// 定义镜像
Image = image,
// 拉取镜像策略
ImagePullPolicy = "IfNotPresent"
};
pod.Spec.Containers.Add(container);
#endregion
// 创建pod
await kubeClient.CreateNamespacedPodAsync(pod, mynamespace).ConfigureAwait(false);
}
执行该测试方法后,我们通过命令查看已经创建成功了。
kubectl get pod
获取Pod信息
我们这里我们通过ListNamespacedPodAsync
方法找到label相匹配的所有pod信息,输出了Pod的名称以及它的IP。
[DataTestMethod]
[DataRow("default", "run=busybox")]
public async Task TestGetPod(string mynamespace,string labels)
{
// 获取现有的Pod
var pods = await kubeClient.ListNamespacedPodAsync(mynamespace,
null, null, null, labels);
foreach (var pod in pods.Items)
{
Debug.WriteLine($"现有的 Lable[{labels}] Pod: {pod.Metadata.Name} Pod IP: {pod.Status.PodIP}");
}
}
我们还可以通过服务的方式去找到相关的Pod。方法为找到相关服务,获取服务所选取的标签再拿这些标签去找到相关Pod。
/// <summary>
/// 通过服务获取pod
/// </summary>
/// <param name="mynamespace"></param>
/// <param name="labels"></param>
/// <returns></returns>
[DataTestMethod]
[DataRow("swprinter", "swpapi")]
public async Task TestGetServicePod(string mynamespace, string servicename)
{
var myservice = await kubeClient.ReadNamespacedServiceAsync(servicename, mynamespace);
var labels = myservice.Spec.Selector.Select(x => $"{x.Key}={x.Value}")
.ToArray()
;
var labelString = string.Join(',', labels);
// 获取现有的Pod
var pods = await kubeClient.ListNamespacedPodAsync(mynamespace,
null, null, null, labelString);
foreach (var pod in pods.Items)
{
Debug.WriteLine($"现有的 Lable[{labelString}] Pod: {pod.Metadata.Name} Pod IP: {pod.Status.PodIP}");
}
}
添加标签
我们为默认名称空间下的带有run=busybox
标签的Pod,添加新的的标签name=busybox
,注意在修改完成后通过PatchNamespacedPodAsync
方法进行保存修改。
[DataTestMethod]
[DataRow("default", "run=busybox", "name=busybox")]
public async Task TestSetPod(string mynamespace, string searchlabels, string nowlabels)
{
// 获取现有的Pod
var pods = await kubeClient.ListNamespacedPodAsync(mynamespace,
null, null, null, searchlabels);
// label分割
var _nowlabels = nowlabels.Split(',')
.Select(x => x.Split('='))
.ToDictionary(x => x[0], y => y[1]);
// 获取共有部分
var all_labels = pods.Items.SelectMany(x => x.Metadata.Labels.Keys).Union(_nowlabels.Keys);
// 获取差的部分
foreach (V1Pod pod in pods.Items)
{
// 修改前
Debug.WriteLine($"修改前 Label--[{Newtonsoft.Json.JsonConvert.SerializeObject(pod.Metadata.Labels)}] Pod: {pod.Metadata.Name} Pod IP: {pod.Status.PodIP}");
var old = JsonSerializer.SerializeToDocument(pod);
foreach (var label in _nowlabels)
{
var boolvalue = pod.Metadata.Labels.TryAdd(label.Key, label.Value);
}
var expected = JsonSerializer.SerializeToDocument(pod);
var patch = old.CreatePatch(expected);
var patchn = new V1Patch(patch, V1Patch.PatchType.JsonPatch);
await kubeClient.PatchNamespacedPodAsync(patchn, pod.Name(), mynamespace);
// 修改后
Debug.WriteLine($"修改后 Label--[{Newtonsoft.Json.JsonConvert.SerializeObject(pod.Metadata.Labels)}] Pod: {pod.Metadata.Name} Pod IP: {pod.Status.PodIP}");
}
}
删除就是Remove什么的就不讲了。。。
监控事件情况
我们通过下面的代码可以监控Pod的事件,其实这里就是一个Watch
方法内部带了委托,然后上了个锁防止程序退出。我们先执行,然后打开窗口执行删除pod的命令。
/// <summary>
/// 监控命名空间下pod的情况与事件
/// 监听新创建的Pod
/// </summary>
/// <param name="mynamespace"></param>
/// <returns></returns>
[DataTestMethod]
[DataRow("default")]
public async Task TestWatchPod(string mynamespace)
{
// 获取现有的Pod消息任务
using var listTask = await kubeClient.CoreV1.ListNamespacedPodWithHttpMessagesAsync(mynamespace, watch: true).ConfigureAwait(false);
var connectionClosed = new AsyncManualResetEvent();
// 监听新的pod创建情况
listTask.Watch<V1Pod, V1PodList>(
(type, item) =>
{
// 过滤掉Add类型并不新创建的pod情况
if (type == WatchEventType.Added && !string.IsNullOrEmpty(item?.Metadata.Name))
{
return;
}
Debug.WriteLine($"监视到事件 '{type}',相关 Pod: {item!.Metadata.Name}");
},
error =>
{
Debug.WriteLine($"监视到错误 '{error.GetType().FullName}'");
},
connectionClosed.Set);
Debug.WriteLine("等待新的 Pod 事件...");
await connectionClosed.WaitAsync();
}
kubectl delete pod/busybox
在集群中监控
首先我们创建一个K8sWatch的控制台项目,安装相关依赖包,然后将我们刚刚的代码放入到Program.cs中。
using k8s;
using k8s.Models;
using Nito.AsyncEx;
// 使用凭据构建一个客户端
var k8sConfig = KubernetesClientConfiguration.BuildDefaultConfig();
// k8s 实例
var kubeClient = new Kubernetes(k8sConfig);
// 获取现有的Pod消息任务
using var listTask = await kubeClient.CoreV1.ListNamespacedPodWithHttpMessagesAsync("default", watch: true).ConfigureAwait(false);
var connectionClosed = new AsyncManualResetEvent();
// 监听新的pod创建情况
listTask.Watch<V1Pod, V1PodList>(
(type, item) =>
{
// 过滤掉Add类型并不新创建的pod情况
if (type == WatchEventType.Added && !string.IsNullOrEmpty(item?.Metadata.Name))
{
return;
}
Console.WriteLine($"监视到事件 '{type}',相关 Pod: {item!.Metadata.Name}");
},
error =>
{
Console.WriteLine($"监视到错误 '{error.GetType().FullName}'");
},
connectionClosed.Set);
Console.WriteLine("等待新的 Pod 事件...");
await connectionClosed.WaitAsync();
然后通过以下命令发布Linux的可执行程序。
dotnet publish -r linux-x64 -p:PublishSingleFile=true
然后我们在rbac-good-permission.yaml
文件下定义相关的ServiceAccount
以及Role
使我们需要运行的程序具有一定的权限,这里权限我们给得不多,只给了对pod的get
、list
、watch
三个权限。
kind: ServiceAccount
apiVersion: v1
metadata:
name: pod-operator
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-operator-role
namespace: default
rules:
- apiGroups: [ "" ]
resources: [ "pods", "pods/exec" ]
verbs: [ "get", "list", "watch" ]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-operator-role
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: pod-operator-role
subjects:
- kind: ServiceAccount
name: pod-operator
namespace: default
kubectl create -f .\rbac-good-permission.yaml
接着我们编写一个测试的Pod文件(test-pod.yaml
),并执行。
apiVersion: v1
kind: Pod
metadata:
labels:
app: test
name: test-pod
spec:
serviceAccountName: pod-operator
containers:
- image: mcr.microsoft.com/dotnet/runtime:6.0
imagePullPolicy: IfNotPresent
name: tester
args:
- infinity
command:
- sleep
kubectl create -f .\test-pod.yaml
然后将我们程序放入到该pod中,并执行。随后打开另一个窗口添加新的pod进行测试监控,发现没有问题。
# 到发布的路径下
cd D:\Learning\k8sPlugs\K8sWatch\bin\Debug\net6.0\linux-x64\publish\
# -c 表示指定的容器
kubectl cp K8sWatch default/test-pod:/home/ -c tester
# 进入容器
kubectl exec -it test-pod -c tester -- /bin/bash
cd /home
chmod +x ./K8sWatch
./K8sWatch
kubectl run busybox --image=burlyluo/nettoolbox
然后我们退出,创建一个rbac-no-permission.yaml
并更新pod-operator-role
角色的权限使它没有任何访问权限。
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-operator-role
namespace: default
rules:
- apiGroups: [ "" ]
resources: [ "pods" ]
verbs: [ "" ]
kubectl delete role/pod-operator-role
kubectl apply -f rbac-no-permission.yaml
再次运行它将报错。
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

