Kubernetes .Net6 集群管理(一) 电脑版发表于:2022/7/5 17:56  >#Kubernetes .Net6 集群管理(一) [TOC] tn2>对于Kubernetes集群的管理,.net提供了`KubernetesClient`库,可以对k8s集群通过简单的代码进行管理。其底层是通过找到本地的KuberConfig对集群发起的API请求。 安装 ------------ tn2>安装相关依赖库。  ```xml <PackageReference Include="KubernetesClient" Version="8.0.12" /> <!-- 解析库 --> <PackageReference Include="JsonPatch.Net" Version="1.1.2" /> ``` Demo展示 ------------ >### 初始化 tn2>通过`KubernetesClientConfiguration.BuildDefaultConfig();`方法可以获取到我们的本地的KUBECONFIG文件信息,它会先去环境变量中去找`KUBECONFIG`的地址,若没有该环境变量,便去用户名称下的`.kube`目录下去找该文件,如果还没有就判断自己本身是不是在集群里面,是就会通过集群中的`ServiceAccount`拿到相关权限,还不是就返回本地的`8080`的配置。(相关代码如图所示) 接着我们将初始化`Kubernetes`的实例. ```csharp KubernetesClientConfiguration k8sConfig; Kubernetes kubeClient; public TestKubernetesClient() { // 使用凭据构建一个客户端 k8sConfig = KubernetesClientConfiguration.BuildDefaultConfig(); // k8s 实例 kubeClient = new Kubernetes(k8sConfig); } ```    创建pod ------------ tn2>可以通过定义`V1Pod`实例执行`CreateNamespacedPodAsync`方法进行创建相关Pod。 这里我们在`default`命名空间下创建一个镜像为`burlyluo/nettoolbox`名为`busybox`的pod,并为它添加`run=busybox`标签。 ```csharp [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); } ``` tn2>执行该测试方法后,我们通过命令查看已经创建成功了。 ```bash kubectl get pod ```  获取Pod信息 ------------ tn2>我们这里我们通过`ListNamespacedPodAsync`方法找到label相匹配的所有pod信息,输出了Pod的名称以及它的IP。 ```csharp [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}"); } } ```  tn2>我们还可以通过服务的方式去找到相关的Pod。方法为找到相关服务,获取服务所选取的标签再拿这些标签去找到相关Pod。 ```csharp /// <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}"); } } ```  添加标签 ------------ tn2>我们为默认名称空间下的带有`run=busybox`标签的Pod,添加新的的标签`name=busybox`,注意在修改完成后通过`PatchNamespacedPodAsync`方法进行保存修改。 ```csharp [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}"); } } ```   tn2>删除就是Remove什么的就不讲了。。。 监控事件情况 ------------ tn2>我们通过下面的代码可以监控Pod的事件,其实这里就是一个`Watch`方法内部带了委托,然后上了个锁防止程序退出。我们先执行,然后打开窗口执行删除pod的命令。 ```csharp /// <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(); } ``` ```bash kubectl delete pod/busybox ```  在集群中监控 ------------  tn2>首先我们创建一个K8sWatch的控制台项目,安装相关依赖包,然后将我们刚刚的代码放入到Program.cs中。 ```csharp 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(); ``` tn2>然后通过以下命令发布Linux的可执行程序。 ```csharp dotnet publish -r linux-x64 -p:PublishSingleFile=true ```  tn2>然后我们在`rbac-good-permission.yaml`文件下定义相关的`ServiceAccount`以及`Role`使我们需要运行的程序具有一定的权限,这里权限我们给得不多,只给了对pod的`get`、`list`、`watch`三个权限。 ```yaml 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 ``` ```bash kubectl create -f .\rbac-good-permission.yaml ``` tn2>接着我们编写一个测试的Pod文件(`test-pod.yaml`),并执行。 ```csharp 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 ``` ```bash kubectl create -f .\test-pod.yaml ``` tn2>然后将我们程序放入到该pod中,并执行。随后打开另一个窗口添加新的pod进行测试监控,发现没有问题。 ```csharp # 到发布的路径下 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 ``` ```csharp kubectl run busybox --image=burlyluo/nettoolbox ```  tn2>然后我们退出,创建一个`rbac-no-permission.yaml`并更新`pod-operator-role`角色的权限使它没有任何访问权限。 ```bash --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: pod-operator-role namespace: default rules: - apiGroups: [ "" ] resources: [ "pods" ] verbs: [ "" ] ``` ```bash kubectl delete role/pod-operator-role kubectl apply -f rbac-no-permission.yaml ``` tn2>再次运行它将报错。 