Featured image of post 基于Cloud-init定制化虚拟机

基于Cloud-init定制化虚拟机

背景

在我家有几台设备常年开着提供一些基础服务或者随时做一些实验,它们基于PVE提供虚拟化能力。每当新创建一个虚拟机,我可能要做不少设置。之前有整理常见的新机器初始化步骤,要修改的东西不少。比如:

  • 内网DNS地址修改
  • 时区修改
  • 一些软件如:docker、glances等。
  • APT下载加速
  • docker下载走缓存

以上种种,不一而足。作为懒人我肯定不想重复这无聊的事情,于是在寻找方案统一做这事件。或许Ansible是可行的,有没更简单的呢?我调研了Cloud-init的功能,历经不少坑后,终于完美解决此问题,记录以帮助有类似诉求的人。

[!Warning] 注:文章提供的配置等仅为示意,你应视自己具体情况而定,比如网关、IP等设置,一定注意不要乱抄。

环境与工具

  • PVE(我们要在这上面创建新的虚拟机VM)
  • cloud-init(本方案主角,用于初始化)
  • terraform(基于声明式配置的工具,用它来集成cloud-init并且和PVE交互)

上篇:裸用cloud-init方案

这里我们先抛开terraform及通过它远程操作PVE等,先看看纯手工如何制作cloud-init数据源并且让VM使用它。下面操作基本都是在PVE下完成,不需要其它工具。

制作cloud-init镜像

cloud-init基础概念

cloud-init简单介绍它是当前云上很常用的一种初始化VM的方案,让我们基于一个基础的磁盘镜像上定制化的做一些安装或设置。官方这篇# Boot Stages看一下大概会有所了解,这里我就不展开聊了。

如果你的VM是通过PVE的这篇Cloud-Init_Support 创建的,那么在WebUI下看或许是这样的: 我们的PVE中此虚拟机配置如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
root@nuc:/var/lib/vz/snippets# cat /etc/pve/qemu-server/412.conf
## K3s-node
agent: 0
bios: seabios
boot: c
bootdisk: scsi0
ciuser: kevin
cores: 2
cpu: host
hotplug: network,disk,usb
ide2: local-lvm:vm-412-cloudinit,media=cdrom,size=4M
ipconfig0: ip=192.168.50.211/24,gw=192.168.50.252
kvm: 1
memory: 4096
meta: creation-qemu=6.2.0,ctime=1658038143
name: k3s-node-0
net0: virtio=AA:13:A0:BB:F6:DE,bridge=vmbr0
numa: 0
onboot: 1
ostype: l26
scsi0: local-lvm:vm-412-disk-0,iothread=1,size=10G
scsihw: virtio-scsi-pci
serial0: socket
smbios1: uuid=9c11f831-90c0-4477-beec-831cec9e682c
sockets: 1
sshkeys: [你的SSHKEY]
tablet: 1
vmgenid: f315444c-9b83-4be0-a6ad-ffaaa9bdb312

注意上面这一行:ide2: local-lvm:vm-412-cloudinit,media=cdrom,size=4M。 这是一种特殊的CDRom,挂载上作为cloud-init的DataSource

简单理解DataSource就是它定义了一系列配置,对应控制cloud-init的各个阶段做什么事情,比如Network阶段配置网络,Config阶段可以设置ntp服务、timezone等,各个阶段内置能做的事情在cloud-init中叫Module。你可以从一个已经安装cloud-init的机器的/etc/cloud/cloud.cfg看到这里细节,比如下面是其中一小段:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# The modules that run in the 'config' stage
cloud_config_modules:
 - snap
 - ssh-import-id
 - keyboard
 - locale
 - set-passwords
 - grub-dpkg
 - apt-pipelining
 - apt-configure
 - ubuntu-advantage
 - ntp
 - timezone
 - disable-ec2-metadata
 - runcmd
 - byobu

其实怎么执行的,执行顺序什么的你一般不必关心。我们真正需要的是理解DataSource并且学会怎么配置它。从哪里学习最快呢,当然直接从现有的cloud-init中去看看?

我们找一个启用了cloud-init的VM,先将/dev/sr0(其实就是上面的光驱)挂载到一个目录。

1
2
3
4
5
6
7
kevin@ubuntu-1:~/cloud-init-study$ sudo mount /dev/sr0 ~/cloud-init-study/
kevin@ubuntu-1:~/cloud-init-study$ ls -ltr
total 2
-rw-r--r-- 1 root root   0 Jul 28 12:45 vendor-data
-rw-r--r-- 1 root root 266 Jul 28 12:45 user-data
-rw-r--r-- 1 root root 317 Jul 28 12:45 network-config
-rw-r--r-- 1 root root  54 Jul 28 12:45 meta-data

这几个文件比较重要,简要概述:

  • user-data: 我们定义的大多数配置都放在这里。
  • network-config: 关于网络如ip/nameserver/dns等的定义。
  • meta-data: 一般是instance id,唯一的一个机器标识符。
  • vendor-data: 是供应商(云)定义的如user-data类似数据,我们这里的Cloud-init数据源是NoCloud,所以这个文件为空。

三个文件的内容默认下较少,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 这里是user-data的数据,定义了一个账户和连接的ssh key
kevin@k3s-node-0:~/cloud-init-study$ cat user-data
#cloud-config
hostname: k3s-node-0
manage_etc_hosts: true
user: kevin
ssh_authorized_keys:
  -   [你的SSH KEY]
chpasswd:
  expire: False
users:
  - default
package_upgrade: true

# 这里是meta-data,定义了实例id。 这个id随便取,长度也随意,只要不冲突
kevin@k3s-node-0:~/cloud-init-study$ cat meta-data
instance-id: 0b86bab2a13ddf3db495d0753b8c2a91d6f70543

# 这里是network-config。我这里定义了静态IP、DNS和nameserver等
kevin@k3s-node-0:~/cloud-init-study$ cat network-config
version: 1
config:
    - type: physical
      name: eth0
      mac_address: 'aa:13:a0:bb:f6:de'
      subnets:
      - type: static
        address: '192.168.50.211'
        netmask: '255.255.255.0'
        gateway: '192.168.50.252'
    - type: nameserver
      address:
      - '223.5.5.5'
      search:
      - '.'

以上是个较常见的cloud-init配置示例。我们接下来丰富它达成我前文提到的目标。

创建自定义的cloud-init

不太清楚我们创建的这个目标叫镜像好,还是叫配置好,或者是数据源。它是以配置的形式,被打包为一种iso镜像,用于cloud-init作为数据源。

我们创建一个目录,在下面建立上述三个文件,写上更完善的配置。 user-data文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#cloud-config
hostname: test
manage_etc_hosts: true
user: kevin
password: [密码]
ssh_authorized_keys:
  -   [SSH KEY]
chpasswd:
  expire: False
users:
  - default
package_upgrade: true


# ------------------
# apt 切换国内源
# ------------------
apt:
  primary:
    - arches: [default]
      uri: http://mirrors.tuna.tsinghua.edu.cn/ubuntu/

# ------------------
# timezone  设置一下时区
# ------------------
timezone: Asia/Shanghai

# ------------------
# packages 安装一些软件
# ------------------
packages:
  - glances
  - docker.io


# ------------------
# write some config:写文件。 APT代理/docker的镜像仓库代理等
# ------------------
write_files:
- content: |
      Acquire {
        HTTP::proxy "http://192.168.50.57:3142";
        HTTPS::proxy "http://192.168.50.57:3142";
      }      
  path: /etc/apt/apt.conf.d/proxy.conf
  permissions: '0644'
- content: |
    {
            "registry-mirrors": [
                    "https://hub-mirror.c.163.com",
                    "https://mirror.baidubce.com",
                    "https://docker.mirrors.ustc.edu.cn"
            ],
            "dns" : [
                    "192.168.50.233",
                    "192.168.50.252"
            ]
    }    
  path: /etc/docker/daemon.json
  permissions: '0644'

# ------------------
# runcmd 执行一些命令,这里让docker命令免sudo执行
# ------------------
runcmd:
  - [groupadd, docker]
  - [gpasswd,-a,kevin,docker]
  - [systemctl,restart,docker]
  - [chmod,a+rw,/var/run/docker.sock]


# ------------------
# power_state 在cloud-init完成后重启一次
# ------------------
power_state:
    delay: now
    mode: reboot
    message: Rebooting machine
    condition: true


# ------------------
# final_message
# ------------------
final_message: |
  cloud-init has finished
  version: $version
  timestamp: $timestamp
  datasource: $datasource
  uptime: $uptime  

network-config文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#cloud-config
version: 1
config:
    - type: physical
      name: eth0
      mac_address: '1a:66:da:db:27:18'
      subnets:
      - type: static
        address: '192.168.50.33'
        netmask: '255.255.255.0'
        gateway: '192.168.50.252'
    - type: nameserver  # 这里指定DNS解析服务到内网
      address:
      - '192.168.50.233'
      - '192.168.50.252'
      search:
      - '.'

meta-data文件:

1
2
# 实例id,随便写个不冲突的
instance-id: 10086  

在创建完上面的几个文件后,我们使用genisoimage命令将其打包成一个iso文件(特殊的iso),此命令在PVE中是内置的,没有请自行安装。

1
genisoimage -output my-cloud-init.iso -volid cidata -joliet -rock user-data meta-data network-config

使用自制的cloud-init镜像

将新建的iso导入到目标虚拟机

1
qm importdisk 412 my-cloud-init.iso local-lvm

上面导入完成后,你或许可以从PVE WebUI中看到多了一块未使用磁盘,接下来我们来个李代桃僵,用它替换原来的cloud-init。

替换原来的cloud-init

编辑虚拟机配置文件,如上面412号虚拟机,其配置导入后可能如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
root@nuc:~/kv-cloud-init/cidata-versions# cat /etc/pve/qemu-server/412.conf
## K3s-node
agent: 0
bios: seabios
boot: c
bootdisk: scsi0
ciuser: kevin
cores: 2
cpu: host
hotplug: network,disk,usb
# 修改ide2这一条的vm-412-cloudinit替换为上面导入的磁盘,它名字在这里是vm-412-disk-1
ide2: local-lvm:vm-412-cloudinit,media=cdrom,size=4M
ipconfig0: ip=192.168.50.211/24,gw=192.168.50.252
kvm: 1
memory: 4096
meta: creation-qemu=6.2.0,ctime=1658038143
name: k3s-node-0
net0: virtio=AA:13:A0:BB:F6:DE,bridge=vmbr0
numa: 0
onboot: 1
ostype: l26
scsi0: local-lvm:vm-412-disk-0,iothread=1,size=10G
scsihw: virtio-scsi-pci
serial0: socket
smbios1: uuid=9c11f831-90c0-4477-beec-831cec9e682c
sockets: 1
sshkeys: [你的SSH KEY]
tablet: 1
# 删除下面这一行unused0
unused0: local-lvm:vm-412-disk-1
vmgenid: f315444c-9b83-4be0-a6ad-ffaaa9bdb312

很简单,上面已经注释了。删除一行修改一行。

虚拟机开机

现在虚拟机可以开机,它会走到我们新定义的一系列模块做一系列事情,设置账户,网络,安装软件等等,基本达到我们想要的目标了。

遗留问题

如果你按我说的一步步操作,或许能成功,也许不行,这过程中可能还会遇到几个坑,别问我为什么知道的,说多了都是˃̣̣̥᷄⌓˂̣̣̥᷅

  • 默认的磁盘空间不够安装软件,导致有些逻辑没正确走完。所以如何给磁盘扩容?
  • 使用静态IP地址,它需要在上面的network-config填写mac地址,这就让此cloud-init镜像生成后不能通用了,这很不能忍受。而一旦你不指定mac地址(官方文档说是可选?神TM可选),你的网络将不通。

怎么办呢,带着这些问题我可不能罢休。

下篇:通过Terraform+cloud-init创建定制化VM

上篇的方案解决了一些棘手问题,仿佛能用了,然而还有遗留的问题,是时候更进一步了。

在讲具体方案之前,先简单的介绍一下Terraform。

Terraform来自于某NB的技术公司HashiCorp。
Automate Infrastructure on Any Cloud。基础设施即代码。

简单说两句,就是我们通过定义一些配置文件,再用它的工具可以操作云上的基础设施,如申请机器,分配XX等。在这里请出它是因为,可以用它来操作PVE。不光是云上使用,咱内部自己服务也用配置文件描述然后动态创建了,瞬间上流有木有。

前置准备

你需要安装Terraform,请自行解决。以下操作在自己本地进行或其它机器均可,不必在PVE机器上操作。

然后有一篇文章特别重要:Terraform文档教你如何用它来操作PVE。重点内容就是在PVE中执行如下命令:

1
2
3
pveum role add TerraformProv -privs "VM.Allocate VM.Clone VM.Config.CDROM VM.Config.CPU VM.Config.Cloudinit VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.Monitor VM.Audit VM.PowerMgmt Datastore.AllocateSpace Datastore.Audit"
pveum user add terraform-prov@pve --password <password>
pveum aclmod / -user terraform-prov@pve -role TerraformProv

接着可以试一下用Terraform来创建一个常规的VM。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
terraform {
  required_providers {
    proxmox = {
      source = "telmate/proxmox"
    }
  }
}

// 配置 provider
provider "proxmox" {
  pm_tls_insecure     = true
  # nuc.home替换成你的pve的地址,后面的:8006/api2/json保留
  pm_api_url          = "https://nuc.home:8006/api2/json"
  pm_api_token_id     = "terraform-prov@pve!terraform-token"
  pm_api_token_secret = "[上面创建pveum时的token]"
}

// 配置虚拟机资源
resource "proxmox_vm_qemu" "proxmox-ubuntu" {
  count = 1
  vmid = "${count.index + 400 + 1}"
  name  = "ubuntu-${count.index + 1}"
  desc  = "Ubuntu develop environment from Template[9100] create by terraform"

  # 节点名
  target_node = "nuc"

  # cloud-init template
  clone = "ubuntu22-cloud-init-tmpl"

  # 关机 guest agent
  agent   = 0
  os_type = "ubuntu"
  onboot  = true
  # CPU
  cores   = 2
  sockets = 1
  cpu     = "host"
  # 内存
  memory   = 4096
  scsihw   = "virtio-scsi-pci"
  bootdisk = "scsi0"

  # 硬盘设置
  disk {
    slot     = 0
    size     = "4096M"
    type     = "scsi"
    storage  = "local-lvm"
    iothread = 1
  }

  # 网络
  network {
    model  = "virtio"
    bridge = "vmbr0"
  }

  lifecycle {
    ignore_changes = [
      network,
    ]
  }
  # 记住这里要使用IP CIDR
  ipconfig0 = "ip=192.168.50.${count.index + 32}/24,gw=192.168.50.252"

  # 用户名和 SSH key
  ciuser  = "kevin"
  sshkeys = <<EOF
  [你的SSH KEY]
  EOF
}

如果执行如下能正常创建VM并且可登录正常使用,我们再往下走。

1
terraform apply

现在你会用Terraform控制PVE了,有种远程操控的感觉了有木有:)

基于cloud-init配置Terraform

我们要写的配置和官方这个示例有点像,但是示例缺少注释和说明,并且有不少坑,不要再问我怎么知道的:(

我们创建如下结构的目录,再写我们的配置。 先看最主要的ubuntu.tf这个terraform的配置内容:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
terraform {
  required_providers {
    proxmox = {
      source = "telmate/proxmox"
    }
      macaddress = {
      source = "ivoronin/macaddress"
      version = "0.3.0"
    }
  }
}

// 配置 provider
provider "proxmox" {
  pm_tls_insecure     = true
  pm_api_url          = "https://nuc.home:8006/api2/json"
  pm_api_token_id     = "terraform-prov@pve!terraform-token"
  pm_api_token_secret = "[你的token]"
}

variable "vm_count" {
  type = number
  default = 1
}

variable "vm_id" {
  type = number
  default = 402
}

// 基于模版定义如何生成user-data 
data "template_file" "user_data" {
    count    = 1
    template = file("${path.module}/cloud-init/user-data.tmpl")
    vars     = {
      pubkey   = file(pathexpand("${path.module}/cloud-init/home_id_ed25519.pub"))
      hostname = "vm-${count.index}"
      passwd = "你的password,也可以不用,只用ssh key登录"
    }
}

// 真实渲染生成user-data放在本地
resource "local_file" "cloud_init_user_data_file" {
    count    = 1
    content  = data.template_file.user_data[count.index].rendered
    filename = "${path.module}/files/user_data_${count.index}.cfg"
}

// 生成network配置
data "template_file" "network_config" {
    count    = var.vm_count
    template = file("${path.module}/cloud-init/network-config.tmpl")
    vars     = {
      ip = "192.168.50.${count.index + 31}"
      macaddr = lower(macaddress.vm_mac_address.address)
    }
}

resource "local_file" "cloud_init_network_config_file" {
  count    = var.vm_count
  content  = data.template_file.network_config[count.index].rendered
  filename = "${path.module}/files/network-config_${count.index}.cfg"
}

resource "macaddress" "vm_mac_address" {
}

resource "null_resource" "cloud_init_config_files" {
  count = 1
  connection {
    type = "ssh"
    user     = "root"
    password = "[你的PVE机器密码]"
    host     = "192.168.50.225"
  }

  # 上传user-data
  provisioner "file" {
    source      = local_file.cloud_init_user_data_file[count.index].filename
    destination = "/var/lib/vz/snippets/user_data_vm-${count.index}.yml"
  }

  # 上传network config
  provisioner "file" {
    source      = local_file.cloud_init_network_config_file[count.index].filename
    destination = "/var/lib/vz/snippets/network-config_vm-${count.index}.yml"
  }
}



// 配置虚拟机资源
resource "proxmox_vm_qemu" "proxmox-ubuntu" {
  depends_on = [
    null_resource.cloud_init_config_files,
  ]
  count = 1
  vmid = var.vm_id
  name  = "ubuntu-${count.index + 1}"
  desc  = "Ubuntu develop environment from Template[u22] create by terraform"

  # 节点名
  target_node = "nuc"

  # cloud-init template
  #clone = "ubuntu22-cloud-init-tmpl"
  # 特别注意1/2: 这里必须选择一个没有带cloud-init磁盘的一个模版,不然会有两个cloud-init设备挂载会冲突
  clone = "u22"

  # 关机 guest agent
  agent   = 0
  # 这里要用cloud-init
  os_type = "cloud-init"
  onboot  = true
  # CPU
  cores   = 2
  sockets = 1
  cpu     = "host"
  # 内存
  memory   = 4096
  scsihw   = "virtio-scsi-pci"
  bootdisk = "scsi0"

  # 硬盘设置
  disk {
    slot     = 0
    size     = "4096M"
    type     = "scsi"
    storage  = "local-lvm"
    iothread = 1
  }

  # 网络
  network {
    model  = "virtio"
    bridge = "vmbr0"
    # 特别注意2/2: 这里要显式指定macaddr。如果不写,那样会随机生成的mac地址,但是我们cloud-init需要mac地址才能有静态IP,所以这里引用了一个外部生成mac地址的库。
    macaddr = lower(macaddress.vm_mac_address.address)
  }

  lifecycle {
    ignore_changes = [
      network,
    ]
  }

  cicustom                = "user=local:snippets/user_data_vm-${count.index}.yml,network=local:snippets/network-config_vm-${count.index}.yml"
  cloudinit_cdrom_storage = "local-lvm"
}

上面配置文件注释我已经尽量把坑和意图写明了。

简单的说做了几件事:

  • 显式指定了虚拟机磁盘空间,这对Terraform来说很简单。
  • 通过Terraform的模版渲染了cloud-init所需要的两个文件user-datanetwork-config,并且将两个文件上传到PVE机器上。这一切都是自动化进行的。
  • 通过telmate/proxmoxcicustom参数指定了我们上传的配置。这一步我们不必再手动手成cloud-init镜像了。

特别注意的两点还想说明一下。第一是通过这种方式来使用cloud-init,切记你clone的虚拟机模版不能是已经包含cloud-init的,不然会有 lvcreate相关错误,所以你可以重新生成一个VM模版镜像。我也给个参考脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
TMPID=9200
TMPNAME=u22-tmpl
qm create ${TMPID} --name ${TMPNAME} --memory 2048 --net0 virtio,bridge=vmbr0
# 这里导入你的支持cloud-init的系统镜像,还是参考官方这篇文章
# https://pve.proxmox.com/wiki/Cloud-Init_Support
qm importdisk ${TMPID} jammy-server-cloudimg-amd64.img local-lvm
qm set ${TMPID} --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-${TMPID}-disk-0

# 注释这一行
#qm set ${TMPID} --ide2 local-lvm:cloudinit
qm set ${TMPID} --boot c --bootdisk scsi0
qm set ${TMPID} --serial0 socket --vga serial0
qm set ${TMPID} --agent enabled=1

qm template ${TMPID}

接下来是两个cloud-init的配置,还有我们这次把SSH KEY写在文件中,方便统一管理了。为了减少篇幅,我就缩减一下配置内容了。

user-data配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#cloud-config
hostname: ${hostname}
manage_etc_hosts: true
user: kevin
password: ${passwd}
ssh_authorized_keys:
  -  ${pubkey}
chpasswd:
  expire: False
users:
  - default
package_upgrade: true

这个配置可以使用一些变量替换了。

network-config配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#cloud-config
version: 1
config:
    - type: physical
      name: eth0
      mac_address: "${macaddr}"
      subnets:
      - type: static
        address: '${ip}'
        netmask: '255.255.255.0'
        gateway: '192.168.50.252'
    - type: nameserver
      address:
      - '192.168.50.233'
      - '192.168.50.252'
      search:
      - '.'

这里终于可以指定mac地址了,这样我们的工具通用化了。

通过Terraform部署

接下来只要执行一句:

1
terraform apply 

等待一阵子,闲着无聊你可以点开PVE的WebUI中,观察这个虚拟机的创建过程,也可以打开控制台,看它在帮你设置各种功能并且下载软件。

后话

一切完毕后,直接使用你定义的名字登录即可。

生产力机器已经Ready。于是你

  • ping了ping内网的几个域名,通了!
  • 敲了敲几个被自动安装好的工具,有了!
  • 看了看因APT加速后下载百M的速度,爽了!
  • 又拉了拉docker镜像结果直接秒下载,完美!!

你满意的点了点头,今天的生产到此也宣告结束:)

参考资料

  1. 快速搭建实验环境:使用 Terraform 部署 Proxmox 虚拟机
  2. # Cloud-Init Support
  3. Telmate/proxmox
  4. terraform-provider-macaddress
  5. # Cloud config examples
  6. # 准备 cloud-init