经过验证,这种方法可以通过terraform批量创建虚拟机并设置IP地址,Ubuntu和RHEL都可用。
环境准备
- vCenter服务器(设置好数据中心、集群、主机)
- CentOS 7 安装镜像(上传到datastore)
- packer和terraform
1. 编写kickstart文件
本实验采用CentOS 7.9镜像,不同的发行版可能有细微区别,下面是一份示例
|
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 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 |
# Base CentOS 7.x install firewall --disabled selinux --disabled #-------------------------------------------------------------------------- # set password for root #-------------------------------------------------------------------------- rootpw --plaintext VMware1! #-------------------------------------------------------------------------- # set bootloader and use eth0 #-------------------------------------------------------------------------- ## bootloader # elevator=noop - Use a simple FIFO queue for I/O algorithm since hypervisor will also manage this # pci=bfsort - Breadth-first pci order for NIC enumeration # net.ifnames=0 - Disable predictable network interface naming # biosdevname=0 - Disables consistent network interface naming bootloader --location=mbr --append="elevator=noop pci=bfsort net.ifnames=0 biosdevname=0 ipv6.disable=1" #-------------------------------------------------------------------------- # Config network use dhcp #-------------------------------------------------------------------------- # network --bootproto=static --ip=192.168.10.100 --netmask=255.255.255.0 --gateway=192.168.10.1 --nameserver 192.168.10.1,192.168.10.2 network --bootproto=dhcp --device=eth0 --noipv6 --onboot=yes --device=eth0 --hostname=CentOS7Template --activate authconfig --enableshadow --passalgo=sha512 keyboard --vckeymap=us --xlayouts='us' # Set language to use during installation and the default language to use on the installed system (required) lang en_US.UTF-8 --addsupport=zh_CN.UTF-8 skipx install #-------------------------------------------------------------------------- # set timezone #-------------------------------------------------------------------------- timezone Asia/Shanghai --ntpservers=3.centos.pool.ntp.org,0.centos.pool.ntp.org eula --agreed services --enabled=NetworkManager,sshd #-------------------------------------------------------------------------- # Setup disk and LVM #-------------------------------------------------------------------------- zerombr # Only partition sda, leave other disks unpartitioned ignoredisk --only-use=sda clearpart --all --drives=sda part /boot --fstype=xfs --size=512 part pv.01 --grow --size=1 volgroup sys_vg pv.01 logvol / --fstype=xfs --name=root --vgname=sys_vg --size=10240 logvol swap --name=swap --vgname=sys_vg --size=4096 logvol /tmp --fstype=xfs --name=tmp --vgname=sys_vg --size=4096 logvol /usr --fstype=xfs --name=usr --vgname=sys_vg --size=3072 logvol /var --fstype=xfs --name=var --vgname=sys_vg --size=2048 logvol /var/log --fstype=xfs --name=var_log --vgname=sys_vg --size=4096 logvol /app-data --fstype=xfs --name=app-data --vgname=sys_vg --size=1 --grow #-------------------------------------------------------------------------- # Select packages for installation #-------------------------------------------------------------------------- %packages --ignoremissing Require @Base @Base @core biosdevname sed perl less dmidecode bzip2 iproute iputils sysfsutils rsync nano mdadm setserial man-pages.noarch findutils tar net-tools tmpwatch lsof python screen lvm2 curl ypbind yp-tools smartmontools openssh-clients acpid irqbalance which bind-utils ntsysv ntp man open-vm-tools vim lrzsz wget tree screen tcpdump #mysql #postfix chkconfig gzip %end # End of %packages section #-------------------------------------------------------------------------- # Run post installation script #-------------------------------------------------------------------------- %post --log=/root/ks-post.log #!/bin/sh ( set -x #-------------------------------------------------------------------------- # Disable the tiered-progress bar during boot #-------------------------------------------------------------------------- /bin/sed -i -e 's/ rhgb//' -e 's/ quiet//' /boot/grub2/grub.cfg /bin/sed -i -e 's/ rhgb//' -e 's/ quiet//' /etc/grub2.cfg /bin/sed -i -e 's/ rhgb//' -e 's/ quiet//' /etc/default/grub plymouth-set-default-theme text /usr/libexec/plymouth/plymouth-update-initrd #-------------------------------------------------------------------------- # Remove default user/group accounts that are not needed #-------------------------------------------------------------------------- /usr/sbin/userdel operator /usr/sbin/userdel games /usr/sbin/userdel lp /usr/sbin/userdel sync /usr/sbin/userdel shutdown /usr/sbin/userdel halt /usr/sbin/groupdel games #-------------------------------------------------------------------------- # Create local ops user with password "VMware1!" #-------------------------------------------------------------------------- /usr/sbin/useradd ops; echo 'VMware1!' | passwd --stdin ops /usr/sbin/usermod -a -G wheel ops /usr/bin/chage -M -1 -E -1 ops #-------------------------------------------------------------------------- # Add local ops user to sudoers #-------------------------------------------------------------------------- /bin/cat <<'EOF'>> /etc/sudoers Defaults:ops !requiretty ops ALL=(ALL) NOPASSWD: ALL EOF #-------------------------------------------------------------------------- # sync hardware clock #-------------------------------------------------------------------------- /usr/sbin/ntpdate ntp.ntsc.ac.cn /sbin/hwclock --systohc --utc #-------------------------------------------------------------------------- # configure NTP #-------------------------------------------------------------------------- /bin/cat <<'EOF'> /etc/ntp.conf restrict default ignore restrict 127.0.0.1 driftfile /var/lib/ntp/drift logfile /var/log/ntpd broadcastdelay 0.008 server ntp.ntsc.ac.cn restrict ntp.ntsc.ac.cn mask 255.255.255.255 nomodify notrap noquery EOF /bin/cat <<'EOF'> /etc/ntp/step-tickers ntp.ntsc.ac.cn EOF /bin/touch /var/log/ntpd #-------------------------------------------------------------------------- # SSHD setup #-------------------------------------------------------------------------- /bin/sed -i /etc/ssh/sshd_config \ -e 's/^#UseDNS yes$/UseDNS no/' \ -e 's/^GSSAPIAuthentication yes$/GSSAPIAuthentication no/' \ # -e 's/^#PermitRootLogin yes/PermitRootLogin no/' #-------------------------------------------------------------------------- # Increase open file limmits #-------------------------------------------------------------------------- /bin/cat <<'EOF'>> /etc/sysctl.conf # Increases maximum open file limmit fs.file-max = 65536 EOF /bin/cat <<'EOF'>> /etc/security/limits.conf # Custom configuration files in /etc/security/limits.d EOF /bin/cat <<'EOF'> /etc/security/limits.d/10-nofile.conf * soft nofile 65535 * hard nofile 65535 EOF /bin/cat <<'EOF'> /etc/security/limits.d/11-stack.conf * soft stack 65535 * hard stack 65535 EOF #-------------------------------------------------------------------------- # Remove hard coded UUID + MAC from network device configs and DNS/Gateway information #-------------------------------------------------------------------------- /bin/sed -i '/^DNS1*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e* /bin/sed -i '/^DNS2*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e* /bin/sed -i '/^GATEWAY*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e* /bin/sed -i '/^HOSTNAME*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e* /bin/sed -i '/^HWADDR*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e* /bin/sed -i '/^NM_CONTROLLED*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e* /bin/sed -i '/^UUID*..*$/d' /etc/sysconfig/network-scripts/ifcfg-e* /bin/mv /etc/sysconfig/network-scripts/ifcfg-e* /etc/sysconfig/network-scripts/ifcfg-eth0 /bin/sed -i 's/ens192/eth0/g' /etc/sysconfig/network-scripts/ifcfg-eth0 /bin/sed -i "s\ONBOOT=no\ONBOOT=yes\g" /etc/sysconfig/network-scripts/ifcfg-eth0 /bin/sed -i "s\IPV6INIT=yes\IPV6INIT=no\g" /etc/sysconfig/network-scripts/ifcfg-eth0 #-------------------------------------------------------------------------- # Update ifcfg-eth0 to use static ip address #-------------------------------------------------------------------------- #/bin/rm -rf /etc/sysconfig/network-scripts/ifcfg-eth* #/bin/cat <<'EOF'>> /etc/sysconfig/network-scripts/ifcfg-eth0 #TYPE=Ethernet #PROXY_METHOD=none #BROWSER_ONLY=no #BOOTPROTO=static #DEFROUTE=yes #IPV4_FAILURE_FATAL=no #IPV6INIT=no #IPV6_AUTOCONF=no #IPV6_DEFROUTE=no #IPV6_FAILURE_FATAL=no #IPV6_ADDR_GEN_MODE=stable-privacy #NAME=eth0 #DEVICE=eth0 #ONBOOT=yes #IPADDR=192.168.10.100 #NETMASK=255.255.255.0 #GATEWAY=192.168.10.1 #DNS1=192.168.10.1 #DOMAIN=corp.local #EOF #-------------------------------------------------------------------------- # Configure NetworkManager #-------------------------------------------------------------------------- #/bin/cat <<'EOF'> /etc/NetworkManager/conf.d/11-corp.conf #[main] #no-auto-default=* #dns=none #EOF #-------------------------------------------------------------------------- # Configure DNS #-------------------------------------------------------------------------- #/bin/cat <<'EOF'> /etc/resolv.conf #nameserver 10.208.10.1 #EOF #-------------------------------------------------------------------------- # For root, disable color "ls", and use old style sorting order. #-------------------------------------------------------------------------- touch /root/.dir_colors /bin/cat <<'EOF'>> /root/.i18n LC_COLLATE=C EOF #-------------------------------------------------------------------------- # Setup logrotate configuration #-------------------------------------------------------------------------- /bin/cat <<'EOF'> /etc/logrotate.conf # see "man logrotate" for details # rotate log files monthly monthly # keep 12 months worth of backlogs rotate 12 # create new (empty) log files after rotating old ones create # uncomment this if you want your log files compressed compress # RPM packages drop log rotation information into this directory include /etc/logrotate.d # no packages own wtmp -- we'll rotate them here /var/log/wtmp { create 0664 root utmp } EOF #-------------------------------------------------------------------------- # Setup default yum repos #-------------------------------------------------------------------------- /bin/rm -f /etc/yum.repos.d/CentOS-* /bin/cat <<'EOF'> /etc/yum.repos.d/CentOS-Base.repo [base] name=CentOS-$releasever - Base - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/ gpgcheck=1 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 #released updates [updates] name=CentOS-$releasever - Updates - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/updates/$basearch/ gpgcheck=1 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 #additional packages that may be useful [extras] name=CentOS-$releasever - Extras - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/extras/$basearch/ gpgcheck=1 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 #additional packages that extend functionality of existing packages [centosplus] name=CentOS-$releasever - Plus - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/centosplus/$basearch/ gpgcheck=1 enabled=0 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 #contrib - packages by Centos Users [contrib] name=CentOS-$releasever - Contrib - mirrors.aliyun.com failovermethod=priority baseurl=http://mirrors.aliyun.com/centos/$releasever/contrib/$basearch/ gpgcheck=1 enabled=0 gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7 EOF /bin/cat <<'EOF'> /etc/yum.repos.d/epel-7.repo [epel] name=Extra Packages for Enterprise Linux 7 - $basearch baseurl=http://mirrors.aliyun.com/epel/7/$basearch failovermethod=priority enabled=1 gpgcheck=0 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 [epel-debuginfo] name=Extra Packages for Enterprise Linux 7 - $basearch - Debug baseurl=http://mirrors.aliyun.com/epel/7/$basearch/debug failovermethod=priority enabled=0 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 gpgcheck=0 [epel-source] name=Extra Packages for Enterprise Linux 7 - $basearch - Source baseurl=http://mirrors.aliyun.com/epel/7/SRPMS failovermethod=priority enabled=0 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 gpgcheck=0 EOF #-------------------------------------------------------------------------- # Remove UUID for /boot in fstab #-------------------------------------------------------------------------- /bin/sed -i "s/UUID*..*\/boot/\/dev\/sda1\t\t\/boot/" /etc/fstab #-------------------------------------------------------------------------- # Symlink /var/tmp to /tmp #-------------------------------------------------------------------------- /bin/rm -rf /var/tmp /bin/ln -s /tmp /var/tmp #-------------------------------------------------------------------------- # Enable or Disable Specific OS Services/Daemons #-------------------------------------------------------------------------- /usr/bin/systemctl enable autofs /usr/bin/systemctl enable ntpd /usr/bin/systemctl disable firewalld.service /usr/bin/systemctl disable auditd /usr/bin/systemctl disable mdmonitor /usr/bin/systemctl disable postfix /usr/bin/systemctl disable abrt-ccpp.service /usr/bin/systemctl disable abrt-oops.service /usr/bin/systemctl disable abrt-vmcore.service /usr/bin/systemctl disable abrt-xorg.service /usr/bin/systemctl disable abrtd.service /usr/bin/systemctl disable iscsi.service /usr/bin/systemctl disable iscsid.socket /usr/bin/systemctl disable iscsiuio.socket /usr/bin/systemctl disable libstoragemgmt.service /usr/bin/systemctl disable multipathd.service /usr/bin/systemctl disable wpa_supplicant.service #-------------------------------------------------------------------------- # End of post #-------------------------------------------------------------------------- ) 2>&1 %end # Reboot after the installation is complete (optional) # --eject attempt to eject CD or DVD media before rebooting reboot --eject |
### 2. 编写packer配置文件centos-vsphere.json
如下是packer的配置文件,在其中指定
1. vCenter的连接信息(包括地址、用户名、密码)
2. 镜像信息(datastore中的操作系统安装ISO路径)
3. 虚拟机模板基本信息(名称、vm-version版本、所在数据中心、集群等)
4. 虚拟机配置信息(CPU、内存、硬盘、网卡、显存等)
需要根据需要修改和自定义,vm-version和esxi对应关系可以参考官方文档[ESXi hosts and compatible virtual machine hardware versions](https://knowledge.broadcom.com/external/article?legacyId=2007240)
|
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 |
{ "builders": [ { "CPUs": "{{user `vm-cpu-num`}}", "RAM": "{{user `vm-mem-size`}}", "RAM_reserve_all": false, "boot_command": [ "", "linux ks=hd:fd0:/ks.cfg" ], "boot_order": "disk,cdrom,floppy", "boot_wait": "10s", "cluster": "{{user `vsphere-cluster`}}", "convert_to_template": true, "datacenter": "{{user `vsphere-datacenter`}}", "datastore": "{{user `vsphere-datastore`}}", "disk_controller_type": "pvscsi", "floppy_files": [ "ks.cfg" ], "folder": "{{user `vm-folder`}}", "guest_os_type": "centos7_64Guest", "insecure_connection": "true", "iso_paths": [ "{{user `iso_url`}}" ], "network_adapters": [ { "network": "{{user `vsphere-network`}}", "network_card": "vmxnet3" } ], "notes": "Build via Packer in {{ (isotime ) }}", "password": "{{user `vsphere-password`}}", "ssh_password": "VMware1!", "ssh_username": "root", "storage": [ { "disk_size": "{{user `vm-disk-size`}}", "disk_thin_provisioned": true } ], "type": "vsphere-iso", "username": "{{user `vsphere-user`}}", "vcenter_server": "{{user `vsphere-server`}}", "video_ram": "{{user `vm-video-ram`}}", "vm_name": "{{user `vm-name`}}-{{isotime \"2006-01-02\"}}", "vm_version": "{{user `vm-version`}}" } ], "provisioners": [ { "inline": [ "rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7", "yum install deltarpm -y", "yum update -y", "yum clean all" ], "type": "shell" } ], "variables": { "iso_url": "[datastore1] images/CentOS-7-x86_64-Everything-2009.iso", "vm-cpu-num": "1", "vm-disk-size": "81920", "vm-folder": "Templates", "vm-mem-size": "1024", "vm-name": "CentOS7-T", "vm-version": "19", "vm-video-ram": "16384", "vsphere-cluster": "Cluster1", "vsphere-datacenter": "Datacenter", "vsphere-datastore": "datastore1", "vsphere-network": "VM Network", "vsphere-password": "VMware1!", "vsphere-server": "192.168.111.11", "vsphere-user": "administrator@vsphere.local" } } |
## 开始制作镜像
首先验证一下文件是否编写正确
|
1 |
packer validate centos-vsphere.json |
正确无误则开始构建虚拟机模板
|
1 |
packer build centos-vsphere.json |
过程中需要网络正常不要断开,一般会在20分钟内完成构建,过程中你可以在vCenter中看到一个正在运行的虚拟机,实际上就是在执行kickstart文件中定义的内容,完成过后启动虚拟机并通过ssh连接然后执行一些后置的自定义命令(因此需要保证DHCP服务是正常的,并且packer命令的执行机可以连接到这个网段的IP)
## 使用terraform批量创建虚拟机
在工作中,我遇到过需要大量新建虚拟机的场景,但那时我们没有利用iac工具和其他更高级的技术手段,只是对每台虚拟机进行克隆,然后手工的进入console配置IP等信息,当几百台虚拟机需要创建时,大家都想丢给别人做这种dirty work。经过实验,可以通过纯脚本方式实现这个繁琐的步骤,简化工作,提高效率。
### 1. 准备需要的文件
#### 1.1 变量定义variables.tf
定义用来后续操作的变量类型和描述信息
|
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 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
#===========================# # VMware vCenter connection # #===========================# variable "vsphere-user" { type = string description = "VMware vSphere user name" } variable "vsphere-password" { type = string description = "VMware vSphere password" } variable "vsphere-vcenter" { type = string description = "VMWare vCenter server FQDN / IP" } variable "vsphere-unverified-ssl" { type = string description = "Is the VMware vCenter using a self signed certificate (true/false)" } variable "vsphere-datacenter" { type = string description = "VMWare vSphere datacenter" } variable "vsphere-cluster" { type = string description = "VMWare vSphere cluster" default = "" } variable "vsphere-template-folder" { type = string description = "Template folder" default = "Templates" } #================================# # VMware vSphere virtual machine # #================================# variable "vm-count" { type = string description = "Number of VM" default = 1 } variable "vm-name-prefix" { type = string description = "Name of VM prefix" default = "tftest" } variable "vm-datastore" { type = string description = "Datastore used for the vSphere virtual machines" } variable "vm-network" { type = string description = "Network used for the vSphere virtual machines" } variable "vm-network-cidr" { description = "Network CIDR like 192.168.111.0/24" type = string } variable "vm-network-mask" { description = "Network Mask" type = number } variable "vm-network-gateway" { description = "Default Gateway" type = string } variable "vm-dns-servers" { description = "DNS" type = list(string) } variable "vm-linked-clone" { type = string description = "Use linked clone to create the vSphere virtual machine from the template (true/false). If you would like to use the linked clone feature, your template need to have one and only one snapshot" default = "false" } variable "vm-cpu" { type = string description = "Number of vCPU for the vSphere virtual machines" default = "2" } variable "vm-ram" { type = string description = "Amount of RAM for the vSphere virtual machines (example: 2048)" } variable "vm-disk-size" { type = string description = "Amount of Disk for the vSphere virtual machines (example: 80)" default = "80" } variable "vm-name" { type = string description = "The name of the vSphere virtual machines and the hostname of the machine" } variable "vm-guest-id" { type = string description = "The ID of virtual machines operating system" } variable "vm-template-name" { type = string description = "The template to clone to create the VM" } variable "vm-domain" { type = string description = "Linux virtual machine domain name for the machine. This, along with host_name, make up the FQDN of the virtual machine" default = "" } variable "vm-folder" { type = string description = "The VM folder" } variable "vm-resource-pool" { type = string description = "The VM resource pool" } variable "vm-tag-category" { type = string description = "The category for tags" } variable "vm-tag-list" { type = list(string) description = "The VM tags" } variable "vm-annotation" { type = string description = "The VM notes" } variable "vm-application" { type = string description = "The VM Custom Attributes" } variable "vm-owner" { type = string description = "The VM Custom Attributes" } |
#### 1.2 准备terraform.tfvars变量内容文件
在这个文件中保存变量的值(虚拟机数量;名称;使用的模板信息;CPU/内存/磁盘容量;资源池/数据中心/文件夹;网络信息如IP起始地址、网关、DNS等;当然还有vCenter连接信息)
你可能需要先定义好vm-tag的信息,这个部分其实可以不需要,按照你自己的需求去调整,可能可以用来控制NSX网络策略等信息,自行摸索。
|
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 |
# ======================== # # VMware VMs configuration # # ======================== # vm-count = "1" vm-name = "devops-svc" vm-template-name = "CentOS7-T-2025-09-26" vsphere-template-folder = "Templates" vm-cpu = 8 vm-ram = 16384 vm-disk-size = 80 vm-guest-id = "centos7_64Guest" vm-resource-pool = "Resources" vm-folder = "Terraform" vsphere-datacenter = "Datacenter" vsphere-cluster = "Cluster1" vm-datastore = "datastore1" vm-network = "VM Network" vm-network-cidr = "192.168.111.0/24" vm-network-mask = 24 vm-network-gateway = "192.168.111.1" vm-dns-servers = ["192.168.111.1"] vm-domain = "cd.ikvmcn.com" vm-annotation = "Create by Terraform" # ============================ # # vm tags and category # # ============================ # vm-tag-category = "terraform-test-category" vm-tag-list = ["web-443", "web-80"] # ============================ # # VM Custom Notes # # ============================ # vm-application = "WebServer" vm-owner = "Terraform-user" # ============================ # # VMware vSphere configuration # # ============================ # # VMware vCenter IP/FQDN vsphere-vcenter = "192.168.111.11" # VMware vSphere username used to deploy the infrastructure vsphere-user = "administrator@vsphere.local" # VMware vSphere password used to deploy the infrastructure vsphere-password = "VMware1!" # Skip the verification of the vCenter SSL certificate (true/false) vsphere-unverified-ssl = "true" |
#### 1.3 main.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 150 151 152 153 154 155 156 157 158 159 |
# ==================== # # Deploying vSphere VM # # ==================== # # Connect to VMware vSphere vCenter provider "vsphere" { user = var.vsphere-user password = var.vsphere-password vsphere_server = var.vsphere-vcenter # If you have a self-signed cert allow_unverified_ssl = var.vsphere-unverified-ssl } # Define VMware vSphere data "vsphere_datacenter" "dc" { name = var.vsphere-datacenter } data "vsphere_datastore" "datastore" { name = var.vm-datastore datacenter_id = data.vsphere_datacenter.dc.id } data "vsphere_compute_cluster" "cluster" { name = var.vsphere-cluster datacenter_id = data.vsphere_datacenter.dc.id } data "vsphere_network" "network" { name = var.vm-network datacenter_id = data.vsphere_datacenter.dc.id } data "vsphere_virtual_machine" "template" { name = "/${var.vsphere-datacenter}/vm/${var.vsphere-template-folder}/${var.vm-template-name}" datacenter_id = data.vsphere_datacenter.dc.id } data "vsphere_resource_pool" "resource_pool" { name = var.vm-resource-pool datacenter_id = "${data.vsphere_datacenter.dc.id}" } data "vsphere_tag_category" "category" { name = var.vm-tag-category } # ==================== # # Get tag id from vars # # ==================== # data "vsphere_tag" "tag" { for_each = toset(var.vm-tag-list) name = each.value category_id = "${data.vsphere_tag_category.category.id}" } locals { tags = [ for tag-id in data.vsphere_tag.tag : tag-id.id ] } # =================== # # get CST Time. # # =================== # locals { time = "${formatdate("YYYY-MM-DD hh:mm",timeadd(timestamp(),"8h"))}" } # Create VMs resource "vsphere_virtual_machine" "vm" { count = var.vm-count name = "${var.vm-name}-${count.index + 1}" resource_pool_id = data.vsphere_resource_pool.resource_pool.id datastore_id = data.vsphere_datastore.datastore.id folder = var.vm-folder tags = local.tags annotation = "${var.vm-annotation}\nVM-Application:${var.vm-application}\nVM-Owner:${var.vm-owner}\nVM-CreateDate:${local.time}" num_cpus = var.vm-cpu memory = var.vm-ram guest_id = var.vm-guest-id network_interface { network_id = data.vsphere_network.network.id } disk { label = "${var.vm-name}-${count.index + 1}-disk" size = var.vm-disk-size } clone { template_uuid = data.vsphere_virtual_machine.template.id customize { timeout = 0 linux_options { host_name = "${var.vm-name}-${count.index + 1}" domain = var.vm-domain } network_interface { ipv4_address = cidrhost(var.vm-network-cidr, count.index + 100) ipv4_netmask = var.vm-network-mask } ipv4_gateway = var.vm-network-gateway dns_server_list = var.vm-dns-servers dns_suffix_list = [var.vm-domain] } } connection { type = "ssh" user = "root" password = "VMware1!" host = self.default_ip_address } provisioner "remote-exec" { inline = [ "echo hello > /root/test.txt" ] } } resource "null_resource" "post_deploy" { count = var.vm-count triggers = { vm_id = vsphere_virtual_machine.vm[count.index].id } connection { type = "ssh" user = "root" password = "VMware1!" host = vsphere_virtual_machine.vm[count.index].default_ip_address } provisioner "remote-exec" { inline = [ "echo 'tf applyed' >> /root/test.txt" ] } } # limit the terraform version terraform { required_version = ">= 0.12.20" } resource "local_file" "ansible_inventory" { content = templatefile("${path.module}/inventory.tmpl", { vm_inventory = [ for idx, v in vsphere_virtual_machine.vm : { name = "${var.vm-name}-${idx + 1}" ip = v.default_ip_address } ] }) filename = "${path.module}/inventory.ini" } |
#### 1.4 定义output.tf
这个文件定义我们的虚拟机信息的最后输出,可以用来生成ansible inventory或者是CMDB信息
|
1 2 3 4 5 6 7 8 9 |
output "vm_inventory" { value = [ for idx, v in vsphere_virtual_machine.vm : { name = "${var.vm-name}-${idx + 1}" ip = v.default_ip_address } ] } |
#### 1.5 定义ansible_inventory的输出模板
如果需要输出ansible inventory格式的虚拟信息,可以参考这个模板
|
1 2 3 4 5 |
[all] %{ for vm in vm_inventory ~} ${vm.name} ansible_host=${vm.ip} ansible_user=root ansible_password=VMware1! %{ endfor ~} |
### 2. 开始创建虚拟机
#### 2.1 初始化
初始化工作目录,下载插件等
|
1 |
terraform init |
#### 2.2 dry run测试
查看即将运行的terraform剧本可能带来的改变信息
|
1 |
terraform plan |
#### 2.3 创建
执行剧本,最好不要设置自动同意,terraform启动的虚拟机一般来说应该通过terraform来管理其信息例如增加磁盘、加内存等,但是实践中为了解决我们的问题使用它用来创建虚拟机已经满足我们的需求了。如果你需要保存terraform的state信息,并想要通过terraform来管理虚拟机的生命周期则需要更加详细的学习一下terraform。(这里需要注意的是修改名称后在此apply可能导致虚拟机删除后重建,因为我认为我们仅仅使用创建功能即可,管理由我们手动的去做)
|
1 |
terraform apply |
下面是最终的效果,一台虚拟机大约100秒能够创建成功:
[](https://https://typora-pics.432000.xyz/uPic/20250930111656_sgZ7t2.jpg)
## 参考资料
1. https://knowledge.broadcom.com/external/article?legacyId=2007240
2. https://www.guoqiangli.com/2020/02/20/template-automation-series-iii-use-terraform-and-packer-to/?swcfpc=1
3. https://www.guoqiangli.com/2020/02/16/template-automation-series-1-use-packer-automation-to-build/
4. https://www.guoqiangli.com/2020/02/20/template-automation-series-iii-use-terraform-and-packer-to/?swcfpc=1
5. https://github.com/6547709/packer-vsphere/blob/master/CentOS7/ks.cfg
6. https://wghdr.top/?p=3564
7. https://manual.sakura.ad.jp/cloud/server/os-packages/archive-iso/rocky88-kickstart.html
8. https://manual.sakura.ad.jp/cloud/server/os-packages/archive-iso/ubuntu22045-releasenote.html