如何在KubeVirt中使用 Cloud-init
本文目录
Cloud-init 官方支持云平台种类很多常见的公有云如 Aliyun、AWS、Azure,常见的私有云解决方案如 OpenStack、ZStack、OVF 等都有支持。
但是如果我不使用已经支持的私有云,而是自己通过 Libvirt 配合 Ceph 实现了一套虚拟化平台,想要使用 Cloud-init,则可以使用 ConfigDrive 或者 NoCloud 方式。
而这两种方法,都是 kubevirt 支持的数据源,更准确的说是,KubeVirt 目前仅支持这两种数据源。
从效果上来看,这两种方法,并没有区别,都能够在没有网络情况下,通过给虚拟机挂载一个 vfat 或者 iso9660 格式的文件系统到虚拟机里来,给 cloudinit 提供 metadata 和 userdata 达到虚拟机启动任务的定制。
第一种数据源:NoCloud
API 字段参考:cloudinitnocloud
创建出来的虚拟机,会固定生成一个 /dev/vdb 的设备,将它挂载到 /mnt 下,该设备下只有三个文件:
- meta-data:固定只有 instance-id 、local-hostname 两个字段
- network-config:关于网络的一些信息
- user-data:用户自定义所需的信息
配置静态ip
networkData 有两种写法
第一种写法
version: 1
config:
- type: physical
name: eth0
mac_address: "fa:16:3e:18:a6:a2"
subnets:
- type: static
address: 192.168.10.10
netmask: 255.255.255.0
routes:
- network: 0.0.0.0
netmask: 0.0.0.0
gateway: 192.168.10.1
第二种写法
当前的cloudinit版本不支持 version2
version: 2
ethernets:
interface0:
match:
mac_address: "fa:16:3e:18:a6:a2"
set-name: eth0
addresses:
- 192.168.10.10/255.255.255.0
gateway: 192.168.10.1
设置网卡多队列
与 ConfigDrive 一样。
注入密码和密钥
与 ConfigDrive 一样。
第二种数据源:ConfigDrive
API 字段参考:loudinitconfigdrivesource
创建出来的虚拟机会固定多出一个磁盘 vdb,将这个 vdb 挂载到 /mnt ,进入 /mnt/openstack/latest/
目录,固定会有如下三个文件
[root@wbm-vm2 latest]# cd /mnt/openstack/latest/
[root@wbm-vm2 latest]# ls -l
total 2
-rw------- 1 root root 100 Jun 4 05:28 meta_data.json
-rw------- 1 root root 4 Jun 4 05:28 network_data.json
-rw------- 1 root root 40 Jun 4 05:28 user_data
meta_data.json
:代码写死固定只有如下三项内容,不支持自定义
{"instance_id":"wbm-vm.default","hostname":"wbm-vm2","uuid":"0a2027fc-b600-44d5-a6e1-5336b50d352f"}
network_data.json
:你要传入的网络信息(以下是参考 OpenStack 虚拟机的 network_data.json 的内容)
{"services": [{"type": "dns", "address": "119.29.29.29"}, {"type": "dns", "address": "114.114.114.114"}, {"type": "dns", "address": "223.5.5.5"}], "networks": [{"network_id": "84c285d6-286c-4701-870f-96f99589c9b3", "type": "ipv4", "netmask": "255.255.255.0", "link": "taped271a3f-c4", "routes": [{"netmask": "0.0.0.0", "network": "0.0.0.0", "gateway": "27.148.165.1"}], "ip_address": "27.148.165.25", "id": "network0"}], "links": [{"ethernet_mac_address": "fa:16:3e:18:a6:a2", "mtu": 1500, "type": "ovs", "id": "taped271a3f-c4", "vif_id": "ed271a3f-c436-4e73-bc80-d0fca6e2fc7d"}]}
user_data
:你可以自定义传入内容,在 yaml 中,不管你有没有需不需要 userdata ,都至少要写入#cloud-config
配置静态ip
要使用 configdrive,网卡上的 mac 地址,一定得提前指定好,并且和 cloudInitConfigDrive.networkData
里的 ethernet_mac_address
保持一致才可以。
interfaces:
- name: default
bridge: {}
macAddress: "fa:16:3e:18:a6:a2"
然后在 cloudinit 的 networkData 中写入如下信息
- name: cloud-init
cloudInitConfigDrive:
userData: |
#cloud-config
networkData: |
{"services": [{"type": "dns", "address": "119.29.29.29"}, {"type": "dns", "address": "114.114.114.114"}, {"type": "dns", "address": "223.5.5.5"}], "networks": [{"network_id": "84c285d6-286c-4701-870f-96f99589c9b3", "type": "ipv4", "netmask": "255.255.255.0", "link": "taped271a3f-c4", "routes": [{"netmask": "0.0.0.0", "network": "0.0.0.0", "gateway": "192.168.56.200"}], "ip_address": "192.168.56.223", "id": "network0"}], "links": [{"ethernet_mac_address": "fa:16:3e:18:a6:a2", "mtu": 1500, "type": "ovs", "id": "taped271a3f-c4", "vif_id": "ed271a3f-c436-4e73-bc80-d0fca6e2fc7d"}]}
设置网卡多队列
先 cloudinit 的对应的插件 cc_ws_virtio_net_multiqueue.py
net_macs = get_mac_list()
mac_map_eth_get = mac_map_eth(net_macs)
userdata = yaml.load(cloud.datasource.userdata_raw)
int_vhost_queues = int(userdata.get('vhost_queues'))
if int_vhost_queues and mac_map_eth_get:
for get_eth in mac_map_eth_get:
os.popen('ethtool -L %s combined %s'%(get_eth,int_vhost_queues))
LOG.warning('Multi-queue network deploy success %s queues:%s',get_eth,int_vhost_queues)
else:
LOG.error("metadata Nic queue number %s Not for the int type",int_vhost_queues)
在 yaml 中的 spec.domain.devices
开启多队列
devices:
networkInterfaceMultiqueue: true
然后在 cloudinit 中的 userData 指定队列数:
- name: cloud-init
cloudInitConfigDrive:
userData: |
#cloud-config
vhost_queues: 2
注入密码和密钥
先修改 cloudinit 中的对应的插件代码 cc_ws_set_login_way.py
在yaml 中的 userData 里指定 admin_pass
,需要使用密码加密后的字符串,而不是密码。
- name: cloud-init
cloudInitConfigDrive:
userData: |-
#cloud-config
ssh_pwauth: True
admin_pass: "6nWR5NgAU/F/mc$DwJeiQk6Um0vEq26tZ8MYVTMWa39M5FCQzlHJU5fM7lkxfN1Dfj/3Zxfw4DfiwLzUDZAmWLkl.YNdmfqvwQZz."
也可以指定密钥
- name: cloud-init
cloudInitConfigDrive:
userData: |-
#cloud-config
ssh_pwauth: True
keys:
- data: "172.20.56.201 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBN7Vww9EXHoIibiN11nkacySCyCrYQua036LIbF2qCRhnw1YWo2jq5iWJTRK/I5pXaa9ikkfTL9fCerhkRj8b6g="
- data: "172.20.56.202 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBD02DW3hnVMIDS5A0MTIEEowr5EaxXOJYkh5zCzNLCWwjG7Bi2l3e8ZjS7dRE/ZgiCYggqzLyEE0ceBQlKcjqvI="
第三种数据源:NoCloud-Net
根据 cloudinit – nocloud 的介绍,cloudinit 的 nocloud-net 数据源,支持从 metadata server 上获取 metadata 和 userdata。
但是 future-metadata-server-based-data-sources 中提到,目前 kuevirt 还不支持 nocloud-net 这种数据源,未来有支持的可能。
使用 nocloud-net ,是通过传递给 qemu 命令如下参数来告诉虚拟机从哪个url获取的 metadata 和 userdata。
-smbios type=1,serial=ds=nocloud-net;s=http://10.10.0.1:8000/
- 如果要获取 metadata ,就请求 http://10.10.0.1:8000/meta-data
- 如果要获取 userdata,就请求 http://10.10.0.1:8000/user-data
- 如果要获取 vendor-data,就请求 http://10.10.0.1:8000/vendor-data
数据的三种形式
userdata 和 networkdata 等数据有三种形式:
- 指定明文:简单直观,上面为了方便举例,都使用的这种方法
- base64 编码的字符串
- 使用 K8S 的 Secret
为了方便类比学习,下面使用三种方式展示相同的配置
第一种:明文
- name: cloud-init
cloudInitNoCloud:
userData: |
#cloud-config
vhost_queues: 2
admin_pass: "6nWR5NgAU/F/mc$DwJeiQk6Um0vEq26tZ8MYVTMWa39M5FCQzlHJU5fM7lkxfN1Dfj/3Zxfw4DfiwLzUDZAmWLkl.YNdmfqvwQZz."
networkData: |
version: 1
config:
- type: physical
name: eth0
mac_address: "fa:16:3e:18:a6:a2"
subnets:
- type: static
address: 192.168.10.10
netmask: 255.255.255.0
routes:
- network: 0.0.0.0
netmask: 0.0.0.0
gateway: 192.168.10.1
第二种:base64
分别将上面的 userdata 和 network data 写入文本文件中
# 将信息写入userdata文件中
cat << END>userdata
#cloud-config
vhost_queues: 2
admin_pass: "6nWR5NgAU/F/mcDwJeiQk6Um0vEq26tZ8MYVTMWa39M5FCQzlHJU5fM7lkxfN1Dfj/3Zxfw4DfiwLzUDZAmWLkl.YNdmfqvwQZz."
END
# 将网络信息写入 network-config 文件中
$ cat << END > network-config
version: 1
config:
- type: physical
name: eth0
mac_address: "fa:16:3e:18:a6:a2"
subnets:
- type: static
address: 192.168.10.10
netmask: 255.255.255.0
routes:
- network: 0.0.0.0
netmask: 0.0.0.0
gateway: 192.168.10.1
END
按照官方文档中的示例, vm 的 yaml 如下
- name: cloud-init
cloudInitNoCloud:
userDataBase64: (cat userdata | base64 -w0)
networkDataBase64:(cat network-config | base64 -w0)
经验证,在 kubevirt.io/v1alpha3 这个版本下,会报如下错误
The request is invalid:
* spec.template.spec.volumes[1].cloudInitNoCloud.userDataBase64: spec.template.spec.volumes[1].cloudInitNoCloud.userDataBase64.cloudInitNoCloud.userDataBase64 is not a valid base64 value.
* spec.template.spec.volumes[1].cloudInitNoCloud.networkDataBase64: spec.template.spec.volumes[1].cloudInitNoCloud.networkDataBase64.cloudInitNoCloud.networkDataBase64 is not a valid base64 value.
因此需要先将 base64 算出来
$ cat userdata | base64 -w0
I2Nsb3VkLWNvbmZpZwp2aG9zdF9xdWV1ZXM6IDIKYWRtaW5fcGFzczogIi9GL21jLzNaeGZ3NERmaXdMelVEWkFtV0xrbC5ZTmRtZnF2d1Faei4iCg==
$ cat network-config | base64 -w0
dmVyc2lvbjogMQpjb25maWc6CiAgIC0gdHlwZTogcGh5c2ljYWwKICAgICBuYW1lOiBldGgwCiAgICAgbWFjX2FkZHJlc3M6ICJmYToxNjozZToxODphNjphMiIKICAgICBzdWJuZXRzOgogICAgICAgIC0gdHlwZTogc3RhdGljCiAgICAgICAgICBhZGRyZXNzOiAxOTIuMTY4LjEwLjEwCiAgICAgICAgICBuZXRtYXNrOiAyNTUuMjU1LjI1NS4wCiAgICAgICAgICByb3V0ZXM6IAogICAgICAgICAgICAtIG5ldHdvcms6IDAuMC4wLjAKICAgICAgICAgICAgICBuZXRtYXNrOiAwLjAuMC4wCiAgICAgICAgICAgICAgZ2F0ZXdheTogMTkyLjE2OC4xMC4xCg==
再将算出来的值传入
- name: cloud-init
cloudInitNoCloud:
userDataBase64: "I2Nsb3VkLWNvbmZpZwp2aG9zdF9xdWV1ZXM6IDIKYWRtaW5fcGFzczogIi9GL21jLzNaeGZ3NERmaXdMelVEWkFtV0xrbC5ZTmRtZnF2d1Faei4iCg=="
networkDataBase64: "dmVyc2lvbjogMQpjb25maWc6CiAgIC0gdHlwZTogcGh5c2ljYWwKICAgICBuYW1lOiBldGgwCiAgICAgbWFjX2FkZHJlc3M6ICJmYToxNjozZToxODphNjphMiIKICAgICBzdWJuZXRzOgogICAgICAgIC0gdHlwZTogc3RhdGljCiAgICAgICAgICBhZGRyZXNzOiAxOTIuMTY4LjEwLjEwCiAgICAgICAgICBuZXRtYXNrOiAyNTUuMjU1LjI1NS4wCiAgICAgICAgICByb3V0ZXM6IAogICAgICAgICAgICAtIG5ldHdvcms6IDAuMC4wLjAKICAgICAgICAgICAgICBuZXRtYXNrOiAwLjAuMC4wCiAgICAgICAgICAgICAgZ2F0ZXdheTogMTkyLjE2OC4xMC4xCg=="
第三种:Secret
先创建第一个 Secret 并将网络信息传进去
# 创建 Kubernetes Secret 对象,并将 network-config 的信息传进去
$ kubectl create secret generic vmi-network-secret --from-file=networkdata=network-config
再创建一个Secret 并将 userdata 的信息传进去
# 创建 Kubernetes Secret 对象,并将 userdata 的信息传进去
$ kubectl create secret generic vmi-userdata-secret --from-file=userdata=userdata
然后在 vm 的 yaml 中指定这两个 secret
- name: cloud-init
cloudInitNoCloud:
secretRef:
name: vmi-userdata-secret
networkDataSecretRef:
name: vmi-network-secret
使用哪种数据源比较好?
从使用效果上来看,NoCloud 和 ConfigDrive 没有区别。但官方文档上 NoCloud 的例子更多,更倾向于使用 NoCloud。