Kubernetes 容器与VM的编排与监控实战

时间:2022-04-10 05:06:36

Kubernetes 容器与VM的编排与监控实战

使用Kubernetes进行容器编排的好处是众所周知的。但是,如果您的业务不适合容器化,那又该怎么办?也许您有一个基于第三方VM的应用,该应用不太容易也不太合适进行容器化,又或者可能需要与Kubernetes平台运行不同的内核或OS。

您真正想要的是让Kubernetes与基于标准容器一起编排VM的方式,就像普通Pod一样。最近,有两个比较好的项目旨在使您做到这一点。分别是是KubeVirt和OpenShift CNV。

在此博客中,我将逐步介绍KubeVirt,您可以按照自己的步骤,使用Calico网络将KubeVirt添加到群集中,然后使用Calico网络策略来保护VM。

开始之前

 

我在开发集群中使用Ubuntu 20.04和两个裸机服务器。尽管我在“第1步”中对如何创建类似的开发集群进行了解释,但是如果您已经选择了其他Kubernetes或OpenShift环境,则可以安全地跳过它。

要求:

至少一台具有2个CPU,4GB Ram和20GB存储空间的主机

kubectl命令行实用程序

SSH客户端

KubeVirt安装与管理

 

步骤1:建立集群

在开始创建集群之前,让我们对主机进行初始化配置以适合Kubernetes。查看这个官方的教程(https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/)并准备您的主机。

让我们创建一个Kubernetes集群

  1. sudo kubeadm init --pod-network-cidr=192.168.0.0/16

执行以下命令来配置kubectl:

  1. mkdir -p $HOME/.kube 
  2. sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 
  3. sudo chown $(id -u):$(id -g) $HOME/.kube/config 

移除master上的污点,以便您可以在其上调度pod。

  1. kubectl taint nodes --all node-role.kubernetes.io/master-

它应该返回以下内容:

  1. node/<your-hostname> untainted 

步骤2:安装Calico

使用清单安装Calico

  1. kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml 

步骤3:安装KubeVirt

使用namespace,我们可以将资源隔离到逻辑模块中,并且可以更轻松地管理它们。

  1. kubectl create namespace kubevirt 

建议使用支持硬件虚拟化的主机,以确保您的主机能够使用virt-host-validate二进制文件。

  1. virt-host-validate qemu 
  2. QEMU: Checking for hardware virtualization               :PASS 

如果主机缺少此命令,则可以使用发行版软件包管理器进行安装,也可以使用来检查kvm文件夹是否可用 ls /dev/kvm。

默认情况下,KubeVirt尝试利用硬件仿真。但是,此功能并非在所有环境中都可用,在这种情况下,您可以使用以下方式启用软件仿真:

  1. kubectl create configmap -n kubevirt kubevirt-config \ 
  2. --from-literal debug.useEmulation=true

应用这些清单并运行KubeVirt operator以自动安装所有必需的资源。

  1. kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/v0.38.1/kubevirt-operator.yaml 
  2. kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/v0.38.1/kubevirt-cr.yaml 

启用迁移功能。(该功能依赖于存储)

  1. kubectl create configmap -n kubevirt kubevirt-config --from-literal feature-gates="LiveMigration"

启用VNC代理功能组件。

  1. kubectl apply -f kubevirt/vnc.yaml 

您可以使用此命令检查KubeVirt的安装进度。

  1. kubectl -n kubevirt wait kv kubevirt --for condition=Available

步骤4:创建一个简单的VM

 

首先,我们创建一个namespace来隔离此演示的资源。

  1. kubectl create namespace kv-policy-demo 

现在,使用虚拟机实例(VMI)定制资源,我们可以创建与Kubernetes完全集成的VM。

  1. kubectl create -f - <<EOF 
  2. apiVersion: kubevirt.io/v1alpha3 
  3. kind: VirtualMachineInstance 
  4. metadata: 
  5.   name: vmi-cirros 
  6.   namespace: kv-policy-demo 
  7.   labels: 
  8.     special: l-vmi-cirros 
  9. spec: 
  10.   domain: 
  11.     devices: 
  12.       disks: 
  13.       - disk: 
  14.           bus: virtio 
  15.         name: containerdisk 
  16.     resources: 
  17.       requests: 
  18.         memory: 64M 
  19.   volumes: 
  20.   - name: containerdisk 
  21.     containerDisk: 
  22.       image: kubevirt/cirros-registry-disk-demo:latest 
  23. EOF 

请注意,如果您使用的是软件仿真,则启动虚拟机可能会非常慢,并且完成IP地址分配可能需要5到6分钟的时间。

  1. kubectl create -f - <<EOF 
  2. apiVersion: v1 
  3. kind: Service 
  4. metadata: 
  5.   name: vmi-cirros-ssh-svc 
  6.   namespace: kv-policy-demo 
  7. spec: 
  8.   ports: 
  9.   - name: crrios-ssh-svc 
  10.     nodePort: 30000 
  11.     port: 27017 
  12.     protocol: TCP 
  13.     targetPort: 22 
  14.   selector: 
  15.     special: l-vmi-cirros 
  16.   type: NodePort 
  17. EOF 

通过使用您节点的IP地址通过服务节点端口进行访问,确认我们可以使用SSH访问VM 。默认密码是gocubsgo。

  1. ssh cirros@10.1.2.3 -p 30000 

通过从新的虚拟机ping google来确认虚拟机可以访问外界。

  1. ping www.google.com -c 5 

步骤5:添加网络安全性

应用以下策略在其namespace中隔离VM。这将仅将允许入向为SSH的协议,并且阻止所有出现的流量VM。(取决于您的VM,您将需要其他策略,但是此简单策略对本教程很有用。)

  1. kubectl create -f - <<EOF 
  2. apiVersion: networking.k8s.io/v1 
  3. kind: NetworkPolicy 
  4. metadata: 
  5.  nameonly-allow-ingress-ssh-to-vm 
  6.  namespace: kv-policy-demo 
  7. spec: 
  8.  podSelector: 
  9.    matchLabels:  
  10.     special: l-vmi-cirros 
  11.  policyTypes: 
  12.  - Ingress 
  13.  - Egress 
  14.  ingress: 
  15.  - from
  16.    ports: 
  17.    - port: 22 
  18. EOF 

SSH进入虚拟机,然后尝试再次ping google。

您将无法执行此操作,因为该策略将阻止所有从Pod发起的与外界的通信。这非常强大–您可以使用与保护Pod相同的范例来保护VM!

步骤6:访问虚拟机

现在我们已经为基于Kubernetes的VM管理设置了Kubevirt,现在让我们访问我们的VM。

  • 使用NodePort服务和弱密码在外部公开VM时要小心
  • 串行控制台仅可通过kubectl virt插件使用
  • SSH以root用户身份登录

通过virt:

  • SSH访问:
  1. kubectl virt expose vmi test-vm --port=22 --name=test-vm-ssh --type=NodePort
  • 串行控制台:
  1. virtctl console test-vm 

不通过virt:

  1. apiVersion: v1 
  2. kind: Service 
  3. metadata: 
  4.   name: test-vm-ssh 
  5.   namespace: default
  6. spec: 
  7.   ports: 
  8.   - name: test-vm-ssh 
  9.     protocol: TCP 
  10.     port: 22 
  11.     targetPort: 22 
  12.   selector: 
  13.     kubevirt.io/name: test-vm 
  14.   type: NodePort 

使用VNC:

  • 如果尚未启用VNC
  1. kubectl apply -f kubevirt/vnc/vnc.yaml 
  • 查找VNC服务节点端口
  • 在以下位置访问VM

http://NODE_IP:NODEPORT/?namespace=VM_NAMESPACE

  • 仅显示namespace VM_NAMESPACE下的VM。选择所需虚拟机所在的namespace。

步骤7:测试CDI

  • 在应用之前,请确保填写DataVolume/VM清单中的所有变量以适合您的环境
  • 尝试创建一个DataVolume
  1. kubectl apply -f datavolume/datavolume-cirros.yaml 
  2. kubectl apply -f datavolume/datavolume-ubuntu.yaml 
  3. kubectl get datavolumes 
  • 在VM清单中即时使用DataVolume
  1. kubectl apply -f vm/CDI-PVC.yaml 

步骤8:清理

要清除本指南中使用的namespace和VM,可以运行以下命令

  1. kubectl delete namespace kv-policy-demo 

其他:故障排除

  • 确保Kubernetes有足够的备用CPU/RAM来部署您请求的VM
  • 确保硬件虚拟化受支持并且可用,或者ConfigMap中存在软件虚拟化标志
  • 更改标志需要重新启动部署
  • 确保服务选择器正确定位到VM Pod
  • 检查Docker MTU和CNI插件MTU是否适合您的网络
  • 使用kubectl virt console $VM_NAME_HERE以确保虚拟机已启动

从内部监控KubeVirt VM

 

部署Prometheus Operator

一旦准备好了k8s集群,就是部署Prometheus Operator。原因是KubeVirt CR安装在群集上时将检测ServiceMonitor CR是否已存在。如果是这样,那么它将创建ServiceMonitors,这些ServiceMonitors被配置为可立即监控所有KubeVirt组件(virt-controller,virt-api和virt-handler)。

尽管本文中没有介绍监控KubeVirt本身,但是还是在部署KubeVirt之前先部署Prometheus Operator。

要部署Prometheus Operator,您需要首先创建其namespace,例如monitoring:

  1. kubectl create ns monitoring 

然后在新的namespace中部署operator:

  1. helm fetch stable/prometheus-operator 
  2. tar xzf prometheus-operator*.tgz 
  3. cd prometheus-operator/ && helm install -n monitoring -f values.yaml kubevirt-prometheus stable/prometheus-operator 

部署完所有内容后,您可以删除helm下载的所有内容:

  1. cd .. 
  2. rm -rf prometheus-operator* 

要记住的一件事是我们在此处添加的版本名称:kubevirt-prometheus。ServiceMonitor稍后声明我们时将使用版本名称。

部署具有持久性存储的VirtualMachine

现在,我们已经准备好所需要的一切。下面让我们配置虚拟机。

我们将从CDI的DataVolume(https://github.com/kubevirt/containerized-data-importer/blob/master/doc/datavolumes.md)资源PersistenVolume开始。由于我没有动态存储提供程序,因此我将创建2个PV,引用将声明它们的PVC。注意每个PV的claimRef。

  1. apiVersion: v1 
  2. kind: PersistentVolume 
  3. metadata: 
  4.   name: example-volume 
  5. spec: 
  6.   storageClassName: ""
  7.   claimRef: 
  8.     namespace: default
  9.     name: cirros-dv 
  10.   accessModes: 
  11.     - ReadWriteOnce 
  12.   capacity: 
  13.     storage: 2Gi 
  14.   hostPath: 
  15.     path: /data/example-volume/ 
  16. ---
  17. apiVersion: v1 
  18. kind: PersistentVolume 
  19. metadata: 
  20.   name: example-volume-scratch 
  21. spec: 
  22.   storageClassName: ""
  23.   claimRef: 
  24.     namespace: default
  25.     name: cirros-dv-scratch 
  26.   accessModes: 
  27.     - ReadWriteOnce 
  28.   capacity: 
  29.     storage: 2Gi 
  30.   hostPath: 
  31.     path: /data/example-volume-scratch/ 

有了永久性存储后,我们可以使用以下清单创建虚拟机:

  1. apiVersion: kubevirt.io/v1alpha3 
  2. kind: VirtualMachine 
  3. metadata: 
  4.   name: monitorable-vm 
  5. spec: 
  6.   running: true
  7.   template: 
  8.     metadata: 
  9.       name: monitorable-vm 
  10.       labels: 
  11.         prometheus.kubevirt.io: "node-exporter"
  12.     spec: 
  13.       domain: 
  14.         resources: 
  15.           requests: 
  16.             memory: 1024Mi 
  17.         devices: 
  18.           disks: 
  19.           - disk: 
  20.               bus: virtio 
  21.             name: my-data-volume 
  22.       volumes: 
  23.       - dataVolume: 
  24.           name: cirros-dv 
  25.         name: my-data-volume 
  26.   dataVolumeTemplates: 
  27.   - metadata: 
  28.       name"cirros-dv"
  29.     spec: 
  30.       source: 
  31.           http: 
  32.              url: "https://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img"
  33.       pvc: 
  34.         storageClassName: ""
  35.         accessModes: 
  36.           - ReadWriteOnce 
  37.         resources: 
  38.           requests: 
  39.             storage: "2Gi"

注意,KubeVirt的VirtualMachine资源有一个VirtualMachine模板和一个dataVolumeTemplate。在VirtualMachine模板上,重要的是要注意我们已将VM命名为VM monitorable-vm,以后将使用该名称连接到其控制台virtctl。我们添加的标签prometheus.kubevirt.io: "node-exporter"也很重要,因为在VM内部安装与配置node-exporter时将会使用到它。

在dataVolumeTemplate上,需要注意的是,我们将PVC命名为cirros-dv,DataVolume资源将用它创建2个PVC,cirros-dv和cirros-dv-scratch。注意,cirros-dv和cirros-dv-scratch是PersistentVolume清单上引用的名称。名称必须匹配才能工作。

在VM内安装node-exporter

一旦VirtualMachineInstance运行,我们就可以使用virtctl console monitorable vm连接到它的控制台。如果需要用户和密码,请提供相应的凭据。如果您使用的是本指南中的同一磁盘映像,则用户和密码分别为cirros和gocubsgo

以下脚本将安装node-exporter并将虚拟机配置为在启动时候自启:

  1. curl -LO -k https://github.com/prometheus/node_exporter/releases/download/v1.0.1/node_exporter-1.0.1.linux-amd64.tar.gz 
  2. gunzip -c node_exporter-1.0.1.linux-amd64.tar.gz | tar xopf - 
  3. ./node_exporter-1.0.1.linux-amd64/node_exporter & 
  4.  
  5. sudo /bin/sh -c 'cat > /etc/rc.local <<EOF 
  6. #!/bin/sh 
  7. echo "Starting up node_exporter at :9100!"
  8.  
  9. /home/cirros/node_exporter-1.0.1.linux-amd64/node_exporter 2>&1 > /dev/null & 
  10. EOF' 
  11. sudo chmod +x /etc/rc.local

PS:如果您使用其他基础映像,请配置node-exporter以在启动时相应地启动

配置Prometheus抓取VM的node-exporter

配置Prometheus来抓取node-exporter非常简单。我们需要做的就是创建一个新的Service和一个ServiceMonitor:

  1. apiVersion: v1 
  2. kind: Service 
  3. metadata: 
  4.   name: monitorable-vm-node-exporter 
  5.   labels: 
  6.     prometheus.kubevirt.io: "node-exporter"
  7. spec: 
  8.   ports: 
  9.   - name: metrics 
  10.     port: 9100 
  11.     targetPort: 9100 
  12.     protocol: TCP 
  13.   selector: 
  14.     prometheus.kubevirt.io: "node-exporter"
  15. ---
  16. apiVersion: monitoring.coreos.com/v1 
  17. kind: ServiceMonitor 
  18. metadata: 
  19.   name: kubevirt-node-exporters-servicemonitor 
  20.   namespace: monitoring 
  21.   labels: 
  22.     prometheus.kubevirt.io: "node-exporter"
  23.     release: monitoring 
  24. spec: 
  25.   namespaceSelector: 
  26.     anytrue
  27.   selector: 
  28.     matchLabels: 
  29.       prometheus.kubevirt.io: "node-exporter"
  30.   endpoints: 
  31.   - port: metrics 
  32.     interval: 15s 

让我们分解一下,以确保我们正确设置了所有内容。从Service开始:

  1. spec: 
  2.   ports: 
  3.   - name: metrics 
  4.     port: 9100 
  5.     targetPort: 9100 
  6.     protocol: TCP 
  7.   selector: 
  8.     prometheus.kubevirt.io: "node-exporter"

在规范中,我们正在创建一个名为metrics的新端口,该端口将重定向到每个标记为prometheus.kubevirt.io: "node-exporter",此处为端口9100,这是node_exporter的默认端口号。

  1. apiVersion: v1 
  2. kind: Service 
  3. metadata: 
  4.   name: monitorable-vm-node-exporter 
  5.   labels: 
  6.     prometheus.kubevirt.io: "node-exporter"

我们还在为服务本身贴上标签prometheus.kubevirt.io: "node-exporter",将由ServiceMonitor对象使用。现在让我们看看我们的ServiceMonitor规范:

  1. spec: 
  2.   namespaceSelector: 
  3.     anytrue
  4.   selector: 
  5.     matchLabels: 
  6.       prometheus.kubevirt.io: "node-exporter"
  7.   endpoints: 
  8.   - port: metrics 
  9.     interval: 15s 

由于我们的ServiceMonitor将部署在monitoring namespace中,而我们的服务则在default namespace中,因此我们需要设置namespaceSelector.any=true。

我们还告诉ServiceMonitor,Prometheus需要从标记为prometheus.kubevirt.io: "node-exporter"以及哪些端口被命名为metrics。幸运的是,我们的service就是这么做的!

最后一件要注意的事。Prometheus配置可以设置为监视多个ServiceMonitors。我们可以通过以下命令查看prometheus正在监视的服务:

  1. # Look for Service Monitor Selector 
  2. kubectl describe -n monitoring prometheuses.monitoring.coreos.com monitoring-prometheus-oper-prometheus 

确保我们的ServiceMonitor具有Prometheus的Service Monitor Selector的所有标签。一个常见的选择器是我们在部署helm的prometheus时设置的版本名称!

测试

 

您可以通过端口转发Prometheus Web UI并执行一些PromQL来进行快速测试:

  1. kubectl port-forward -n monitoring prometheus-monitoring-prometheus-oper-prometheus-0 9090:9090 

为确保一切正常,请访问localhost:9090/graph并执行PromQL up{pod=~"virt-launcher.*"}。Prometheus应该返回从monitorable-vm的node-exporter收集的数据。

您可以试用virtctl,停止和启动VM,以查看指标的行为。您会注意到,使用停止VM时virtctl stop monitorable-vm,VirtualMachineInstance被杀死,因此它也是Pod。这将导致我们的服务无法找到pod的端点,然后将其从Prometheus的目标中删除。

由于这种行为,下面的告警规则将无法正常工作,因为我们的目标实际上已经消失了,而不是降级了。

  1. - alert: KubeVirtVMDown 
  2.     expr: up{pod=~"virt-launcher.*"} == 0 
  3.     for: 1m 
  4.     labels: 
  5.       severity: warning 
  6.     annotations: 
  7.       summary: KubeVirt VM {{ $labels.pod }} is down. 

但是,如果VM在不停止的情况下连续崩溃,则pod不会被杀死,并且仍将监视目标。node-exporter永远不会启动或将与VM一起不断关闭,因此这样的警报可能会起作用:

  1. - alert: KubeVirtVMCrashing 
  2.     expr: up{pod=~"virt-launcher.*"} == 0 
  3.     for: 5m 
  4.     labels: 
  5.       severity: critical 
  6.     annotations: 
  7.       summary: KubeVirt VM {{ $labels.pod }} is constantly crashing before node-exporter starts at boot. 

结论:

 

在此博客文章中,首先,我们简单介绍以及快速安装体验了kubevirt, 然后我们使用node-exporter从KubeVirt VM中公开了监控指标。我们还配置了Prometheus Operator来收集这些指标。本文也是将Kubernetes监控实践与KubeVirt VM运行的应用程序结合使用。

原文地址:https://mp.weixin.qq.com/s/JencHmJo_Dqu11fQtTQSuw