文章大纲
将 VM 连接到网络有两个步骤,首先需要在 spec.networks
声明网络,然后在 spec.domain.devices.interfaces
中添加网卡并指定使用的网络。
在 spec.networks
中声明的网络称为后端(Backend),在 spec.domain.devices.interfaces
中声明的网卡称为前端(Frontend)。
Backend
后端网络配置在 spec.networks
,网络必须名称唯一。
每个网络都需要声明类型:
类型 | 描述 |
---|---|
pod | 默认 Kubernetes 网络 |
multus | 由 Multus 提供的辅助网络 |
Frontend
网卡配置在 spec.domain.devices.interfaces
,里面定义的属性将在 VM 中可以看到。
每个网卡都需要声明类型:
类型 | 描述 |
---|---|
bridge | 使用 Linux Bridge 连接 |
slirp | 使用 QEMU 用户网络模式连接 |
sriov | 透传 SR-IOV PCI 设备通过 vfio |
masquerade | 使用 iptables 规则连接以对流量进行 NAT |
访问 VM 中的服务
VM 中的提供的服务通常需要暴露给集群中其它的工作负载访问或被集群外部进行访问。
集群内部访问
使用 fedora-vm1.yaml
部署 VM,VM 使用的 PV 已经事先通过 CDI 导入了 Fedora 镜像:
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
labels:
kubevirt.io/os: linux
name: fedora-vm1
spec:
running: true
template:
metadata:
labels:
kubevirt.io/domain: fedora-vm1
spec:
nodeSelector:
kubernetes.io/hostname: worker01.example.com
domain:
cpu:
cores: 1
devices:
disks:
- disk:
bus: virtio
name: disk0
- cdrom:
bus: sata
readonly: true
name: cloudinitdisk
machine:
type: q35
resources:
requests:
memory: 512M
volumes:
- name: disk0
persistentVolumeClaim:
claimName: fedora
- cloudInitNoCloud:
userData: |
#cloud-config
hostname: fedora-vm1
ssh_pwauth: True
disable_root: false
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCqjnw9Xnt4buBYlfuCnZNknm0oKDgVtdzhVTnH/S7Aa0P30D89RrlSSuIcKz0p+6ss3c9sT66R/J4dNUxHuyzYJucRLfmdyEEuVR8mlJ8LLTVR8NOK7r3Ao/mx0cIVJ/YS2y7U8T0uO6YoyoKklvGhlqZHli3fsv4JAVbrCXl05/gdqnQu3u+ang97hDtHg7oSiI3e0Wjm40hwPc+agtaSgypWgmPvdvPNB2YGlmm/QGppTzg0ao0NQduGrTzzYEI54SUx1MFBsM70ykN13IOw2J30fHnnLxlFCCoOwgxy4G9soV/e0vvn7dpqIPsr+Nc+sAuweChclKhUcmBVYa6Pr5mX2y3ZeulE+Nzok165mfF9q5TMC5XziInALDOBVtHIw0St1fgrbnnZPEBiMP6i3q814//TvA0m9xotgq8OrhUbNp2ezjpqyMa+31yPP3gmAVMqji2vIaQB3tluGsH2h/djh0I52YHhDO1S5bDs2OCHCrN2WGuA9qr8NT78ISs= vagrant@master01.example.com
name: cloudinitdisk
在上述资源定义中,没有定义任何关于网络和网卡的信息。
创建虚拟机后,可以看到默认使用的网络信息:
默认使用的桥接到 Pod 网络,所以在 VM 中的 IP 与 Pod 的 IP 是一致的:
[vagrant@master01 ~]$ kubectl get vmi/fedora-vm1 -n vm-net -o wide
NAME AGE PHASE IP NODENAME READY LIVE-MIGRATABLE PAUSED
fedora-vm1 10m Running 10.42.95.178 worker01.example.com True False
[vagrant@master01 ~]$
[vagrant@master01 ~]$ virtctl ssh fedora@fedora-vm1.vm-net
Last login: Thu May 9 13:28:11 2024 from 10.42.45.219
[fedora@fedora-vm1 ~]$ ip a s eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc fq_codel state UP group default qlen 1000
link/ether 9e:46:39:51:10:bc brd ff:ff:ff:ff:ff:ff
altname enp1s0
inet 10.42.95.178/32 scope global dynamic noprefixroute eth0
valid_lft 86313127sec preferred_lft 86313127sec
inet6 fe80::6bf8:8366:c521:5857/64 scope link noprefixroute
valid_lft forever preferred_lft forever
在 VM 中部署了 Web 服务,可以直接通过 Pod 的 IP 进行访问:
[vagrant@master01 ~]$ curl 10.42.95.178
KubeVirt: hi from fedora-vm1.
但使用 Bridge
模式会带来一个问题:
Message: cannot migrate VMI which does not use masquerade, bridge with kubevirt.io/allow-pod-bridge-network-live-migration VM annotation or a migratable plugin to connect to the pod network
原因是 Pod 的 IP 是不固定的,当发生迁移的时候,会创建新的 VM,此时 IP 将发生变化,导致 VM 中的网络也随着变化,如果要支持迁移,需要将网络类型修改为 masquerade
类型。
修改为 masquerade
:
需要更改 vm
资源,修改后,还需将 vmi
删除,重新根据 vm
创建 vmi
:
[vagrant@master01 ~]$ kubectl edit vm/fedora-vm1 -n vm-net
virtualmachine.kubevirt.io/fedora-vm1 edited
[vagrant@master01 ~]$ kubectl delete vmi fedora-vm1 -n vm-net
virtualmachineinstance.kubevirt.io "fedora-vm1" deleted
然后可以看到在 VM 中 eth0 网卡的网络是一个内部网络:
[vagrant@master01 ~]$ kubectl get vmi/fedora-vm1 -o wide -n vm-net
NAME AGE PHASE IP NODENAME READY LIVE-MIGRATABLE PAUSED
fedora-vm1 106m Running 10.42.95.189 worker01.example.com True True
[vagrant@master01 ~]$ virtctl ssh fedora@fedora-vm1.vm-net
Last login: Fri May 10 02:03:56 2024 from 10.42.45.232
[fedora@fedora-vm1 ~]$ ip a s eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc fq_codel state UP group default qlen 1000
link/ether 36:e6:18:9a:fb:04 brd ff:ff:ff:ff:ff:ff
altname enp1s0
inet 10.0.2.2/24 brd 10.0.2.255 scope global dynamic noprefixroute eth0
valid_lft 86307426sec preferred_lft 86307426sec
inet6 fe80::2224:443b:537b:8b64/64 scope link noprefixroute
valid_lft forever preferred_lft forever
此时访问 Pod 的 IP,依旧可以访问到 VM 中的服务:
[vagrant@master01 ~]$ curl 10.42.95.189
KubeVirt: hi from fedora-vm1.
Pod IP 不固定,需要使用 Service 资源使用固定的 IP 和集群内部的名称解析来访问 VM 中的服务。
创建 Service:
apiVersion: v1
kind: Service
metadata:
name: fedoravm1-svc
namespace: vm-net
spec:
ports:
- port: 80
targetPort: 80
selector:
kubevirt.io/domain: fedora-vm1
应用并检查:
[vagrant@master01 ~]$ kubectl apply -f fedora-vm1-svc.yaml
service/fedoravm1-svc created
[vagrant@master01 ~]$ kubectl describe svc fedoravm1-svc
Error from server (NotFound): services "fedoravm1-svc" not found
[vagrant@master01 ~]$ kubectl describe svc fedoravm1-svc -n vm-net
Name: fedoravm1-svc
Namespace: vm-net
Labels: <none>
Annotations: <none>
Selector: kubevirt.io/domain=fedora-vm1
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.43.5.191
IPs: 10.43.5.191
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.42.95.189:80
Session Affinity: None
Events: <none>
[vagrant@master01 ~]$ cat fedora-vm1-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: fedoravm1-svc
namespace: vm-net
spec:
ports:
- port: 80
targetPort: 80
selector:
kubevirt.io/domain: fedora-vm1
[vagrant@master01 ~]$ curl 10.43.5.191
KubeVirt: hi from fedora-vm1.
集群外部访问
对外提供 VM 的访问可以通过 Service NodePort 这是最简单的方式,还可以通过 Multus 辅助网络,如果是 HTTP 协议的服务,还可以使用 Ingress 资源。
使用 Service NodePort
修改 Service:
apiVersion: v1
kind: Service
metadata:
name: fedoravm1-svc
namespace: vm-net
spec:
ports:
- port: 80
targetPort: 80
selector:
kubevirt.io/domain: fedora-vm1
type: NodePort
应用并检查:
[vagrant@master01 ~]$ kubectl apply -f fedora-vm1-svc.yaml
service/fedoravm1-svc configured
[vagrant@master01 ~]$ kubectl get svc/fedoravm1-svc -n vm-net
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
fedoravm1-svc NodePort 10.43.5.191 <none> 80:30491/TCP 7m33s
#访问 master 节点上的 30491 端口
[vagrant@master01 ~]$ curl localhost:30491
KubeVirt: hi from fedora-vm1.
使用 Ingress
因为 vm 中提供的是 Web 服务,所以可以使用 Ingress,如果是非 HTTP/HTTPS 协议的服务则不行。
webserver
ingress 资源定义:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webserver
namespace: vm-net
spec:
defaultBackend:
service:
name: fedoravm1-svc
port:
number: 80
rules:
- host: webserver.example.com
http:
paths:
- backend:
service:
name: fedoravm1-svc
port:
number: 80
path: /
pathType: Prefix
创建 ingress 资源并验证:
[vagrant@master01 ~]$ kubectl apply -f ingress-webserver.yaml
ingress.networking.k8s.io/webserver created
[vagrant@master01 ~]$ kubectl get ingress -n vm-net
NAME CLASS HOSTS ADDRESS PORTS AGE
webserver <none> webserver.example.com 192.168.121.109,192.168.121.110,192.168.121.27 80 6s
[vagrant@master01 ~]$ curl webserver.example.com
KubeVirt: hi from fedora-vm1