Ở phần trước mình đã hướng dẫn tạo virtual machine trên vCenter bằng Terraform, nhược điểm ở bài trước là mỗi lần chúng ta cần ta cần tạo nhiều máy ảo thì cần phải chỉnh sửa rất nhiều thông tin ví dụ như hostname, ip address, số lượng máy ảo cần tạo, …. như vậy khá tốn thời gian và đôi khi còn dẫn đến sai sót.
Ở bài này mình sẽ hướng dẫn các bạn khắc phục các nhược điểm trên bằng code. Mình sử dụng ngôn ngữ python để viết trong dự án này.
Mình sẽ lấy ví dụ ở bài trước luôn nhé, ở bài trước thư mục terraform đang có cấu trúc như dưới (các bạn có thể xem lại bài trước để biết cách tạo ra folder có cấu trúc này nhé).
.
├── common
│ ├── authorized_keys
│ ├── id_rsa
│ ├── kickstart.yaml
│ └── metadata.yaml
├── main.tf
├── nodes
│ └── vm
│ └── cloudinit
│ └── userdata.yaml
├── scripts
│ └── sshconfig.sh
├── variables.tf
└── vsphere_data.tf
5 directories, 9 files
Tạo file /root/terraform/scripts/generatevars.py và dán vào nội dung như dưới.
Mình sử dụng 3 module bổ sung cho dự án này đó là os, re và sys. Đây toàn là module có sẵn ở python3, các bạn tìm hiểu ngôn ngữ python để hiểu rõ hơn bài viết này nhé.
– Đầu tiên mình khai báo các biến theo cú pháp ở dưới, ngoài ra mình cũng khai báo luôn các file và folder để lưu tạm các file.
#variable of virtual machine
vCPU = (argv[1])
vMEM = int((argv[2]))*1024
ip = (argv[3])
user = (argv[4])
password = (argv[5])
lsnodes = (argv[6])
#variable of provided
user_vsphere_provide = 'administrator@hoanghd.fun'
password_vsphere_provide = 'Mb@834228'
server_vsphere_provide = 'vcenter.hoanghd.fun'
datacenter = "Datacenter"
cluster = "cluster1"
datastore = "ssd-1tb"
template = "bionic-server-cloudimg-amd64"
#network config
network = "VM Network"
netmask = "23"
gw = "192.168.12.5"
dns = "8.8.8.8"
Phần này mình define một số hàm liên quan đến việc khởi tạo folder, file, ghi và đọc file, ….
def file_read(file_name):
with open(file_name) as rf:
value = rf.read()
return value
def file_write(file_name,value):
with open(file_name, 'w') as wf:
value = wf.writelines(value)
def val_add_to_file(file_name,value):
with open(file_name, 'a') as wf:
value = wf.writelines(value)
def file_remove(file_name):
if os.path.exists(file_name):
os.remove(file_name)
def check_and_make_folder(dir_name):
if not os.path.exists(dir_name):
os.makedirs(dir_name)
Biến tiếp theo để khai báo thông tin kết nối vCenter.
vsphere_provide = """vsphere_env = {
user = "%s"
password = "%s"
server = "%s"
}
""" %(user_vsphere_provide,password_vsphere_provide,server_vsphere_provide)
Tiếp theo mình khai báo 1 số biến có nội dung khởi chạy 1 số command khi khởi động máy ảo và ghi chúng vào file tạm.
sshconfig="""#!/bin/bash
sudo sed -i "s|#PermitRootLogin.*|PermitRootLogin yes|" /etc/ssh/sshd_config
sudo sed -i "s|#PasswordAuthentication.*|PasswordAuthentication yes|" /etc/ssh/sshd_config
sudo sed -i "s|# PasswordAuthentication.*|PasswordAuthentication yes|" /etc/ssh/ssh_config
sudo service ssh restart || sudo systemctl restart sshd
sudo echo 'root:%s' | chpasswd""" %(password)
cloudinit="""#cloud-config
packages:
- sudo chmod -R 777 /usr/local/bin/sshconfig
- sudo sshconfig
- sudo apt update -y
- sudo apt upgrade -y
runcmd:
- mkdir -p /mnt/sharedfolder"""
os.system('rm -rf ' + node_folder + '*')
file_remove(terraform_tfvars_filename)
file_write(terraform_tfvars_filename,vsphere_provide)
file_write(sshconfig_filename,sshconfig)
Phần cuối cùng mình sẽ for list chứa thông tin của các máy ảo và xử lý từng phần tử (mỗi phần tử là 1 máy ảo) sau đó ghi nó vào file terraform.tfvars
for nodes in lsnodes.split(' '):
i = 1
node = nodes.split('|')
vmname = node[0]
tempvm=vmname
vmnumber = int(node[1])
while True:
if i != 1 or (nodes.split('|')[0]) != thefistarray:
ip = ip.split('.')[0] + '.' + ip.split('.')[1] + '.' + ip.split('.')[2] + '.' + str(int(ip.split('.')[3]) + 1)
if vmnumber != 1:
vmname = tempvm + str(i)
hostname = tempvm + str(i)
domain = tempvm + str(i) + '.local'
else:
vmname = tempvm
hostname = tempvm
domain = tempvm + '.local'
vars = """
%s = {
vCPU = %s
vMEM = %s
vmname = "tf-%s"
datastore = "ssd-1tb"
datacenter = "Datacenter"
network = "VM Network"
cluster = "cluster1"
template = "bionic-server-cloudimg-amd64"
ip = "%s"
netmask = "23"
hostname = "%s"
domain_name = "%s"
user = "%s"
password = "%s"
}""" %(vmname, vCPU, vMEM, vmname, ip, vmname, domain, user, password)
check_and_make_folder(node_folder + 'tf-' + vmname)
check_and_make_folder(node_folder + 'tf-' + vmname + '/cloudinit')
file_write(node_folder + 'tf-' + vmname + '/cloudinit/' + 'userdata.yaml',cloudinit)
if i == 1 and (nodes.split('|')[0]) == thefistarray:
val_add_to_file(terraform_tfvars_filename,'vms = {')
val_add_to_file(terraform_tfvars_filename,vars)
i = i + 1
if i > vmnumber:
if (nodes.split('|')[0]) == theendarray:
val_add_to_file(terraform_tfvars_filename,'}')
break
Và đây là kết quả cuối cùng
#!/usr/bin/python3
import os, re, subprocess
from sys import argv
#variable of virtual machine
vCPU = (argv[1])
vMEM = int((argv[2]))*1024
ip = (argv[3])
user = (argv[4])
password = (argv[5])
lsnodes = (argv[6])
#variable of provided
user_vsphere_provide = 'administrator@hoanghd.fun'
password_vsphere_provide = 'Mb@834228'
server_vsphere_provide = 'vcenter.hoanghd.fun'
datacenter = "Datacenter"
cluster = "cluster1"
datastore = "ssd-1tb"
template = "bionic-server-cloudimg-amd64"
#network config
network = "VM Network"
netmask = "23"
gw = "192.168.12.5"
dns = "8.8.8.8"
#variable of folder and temp file
terraform_folder = '/root/terraform'
node_folder = '%s/%s' %(terraform_folder,'nodes/')
sshconfig_filename = '%s/%s' %(terraform_folder,'scripts/sshconfig.sh')
terraform_tfvars_filename = '%s/%s' %(terraform_folder,'terraform.tfvars')
def file_read(file_name):
with open(file_name) as rf:
value = rf.read()
return value
def file_write(file_name,value):
with open(file_name, 'w') as wf:
value = wf.writelines(value)
def val_add_to_file(file_name,value):
with open(file_name, 'a') as wf:
value = wf.writelines(value)
def file_remove(file_name):
if os.path.exists(file_name):
os.remove(file_name)
def check_and_make_folder(dir_name):
if not os.path.exists(dir_name):
os.makedirs(dir_name)
def action(vCPU,vMEM,datastore,datacenter,network,gw,dns,cluster,template,ip,netmask,user,password,lsnodes,terraform_folder,node_folder,sshconfig_filename,terraform_tfvars_filename,user_vsphere_provide,password_vsphere_provide,server_vsphere_provide):
if lsnodes.count("|") == 1:
lsnodes = lsnodes.replace(" ", "")
if lsnodes.count("|") == 0:
lsnodes = lsnodes + '|1'
vsphere_provide = """vsphere_env = {
user = "%s"
password = "%s"
server = "%s"
}
""" %(user_vsphere_provide,password_vsphere_provide,server_vsphere_provide)
before_gw = subprocess.check_output('cat /root/terraform/variables.tf | grep -oE "([0-9]{1,3}\.){3}([0-9]{1,3})" | head -1', shell=True).decode("utf-8").strip('\n')
before_dns = subprocess.check_output('cat /root/terraform/variables.tf | grep -oE "([0-9]{1,3}\.){3}([0-9]{1,3})" | tail -1', shell=True).decode("utf-8").strip('\n')
os.system("sed -i 's|%s|%s|' %s/variables.tf" %(before_gw,gw,terraform_folder))
os.system("sed -i 's|%s|%s|' %s/variables.tf" %(before_dns,dns,terraform_folder))
sshconfig="""#!/bin/bash
sudo sed -i "s|#PermitRootLogin.*|PermitRootLogin yes|" /etc/ssh/sshd_config
sudo sed -i "s|#PasswordAuthentication.*|PasswordAuthentication yes|" /etc/ssh/sshd_config
sudo sed -i "s|# PasswordAuthentication.*|PasswordAuthentication yes|" /etc/ssh/ssh_config
sudo service ssh restart || sudo systemctl restart sshd
sudo echo 'root:%s' | chpasswd""" %(password)
cloudinit="""#cloud-config
packages:
- sudo chmod -R 777 /usr/local/bin/sshconfig
- sudo sshconfig
- sudo apt update -y
- sudo apt upgrade -y
runcmd:
- mkdir -p /mnt/sharedfolder"""
os.system('rm -rf ' + node_folder + '*')
file_remove(terraform_tfvars_filename)
file_write(terraform_tfvars_filename,vsphere_provide)
file_write(sshconfig_filename,sshconfig)
countarray = len(lsnodes.split(' ')) - 1
thefistarray = lsnodes.split(' ')[0].split('|')[0]
theendarray = lsnodes.split(' ')[countarray].split('|')[0]
for nodes in lsnodes.split(' '):
i = 1
node = nodes.split('|')
vmname = node[0]
tempvm=vmname
vmnumber = int(node[1])
while True:
if i != 1 or (nodes.split('|')[0]) != thefistarray:
ip = ip.split('.')[0] + '.' + ip.split('.')[1] + '.' + ip.split('.')[2] + '.' + str(int(ip.split('.')[3]) + 1)
if vmnumber != 1:
vmname = tempvm + str(i)
hostname = tempvm + str(i)
domain = tempvm + str(i) + '.local'
else:
vmname = tempvm
hostname = tempvm
domain = tempvm + '.local'
vars = """
%s = {
vCPU = %s
vMEM = %s
vmname = "tf-%s"
datastore = "%s"
datacenter = "%s"
network = "%s"
cluster = "%s"
template = "%s"
ip = "%s"
netmask = "%s"
hostname = "%s"
domain_name = "%s"
user = "%s"
password = "%s"
}""" %(vmname, vCPU, vMEM, vmname, datastore, datacenter, network, cluster, template, ip, netmask, vmname, domain, user, password)
check_and_make_folder(node_folder + 'tf-' + vmname)
check_and_make_folder(node_folder + 'tf-' + vmname + '/cloudinit')
file_write(node_folder + 'tf-' + vmname + '/cloudinit/' + 'userdata.yaml',cloudinit)
if i == 1 and (nodes.split('|')[0]) == thefistarray:
val_add_to_file(terraform_tfvars_filename,'vms = {')
val_add_to_file(terraform_tfvars_filename,vars)
i = i + 1
if i > vmnumber:
if (nodes.split('|')[0]) == theendarray:
val_add_to_file(terraform_tfvars_filename,'}')
break
action(vCPU,vMEM,datastore,datacenter,network,gw,dns,cluster,template,ip,netmask,user,password,lsnodes,terraform_folder,node_folder,sshconfig_filename,terraform_tfvars_filename,user_vsphere_provide,password_vsphere_provide,server_vsphere_provide)
# python3 /root/terraform/scripts/generatevars.py 4 8 192.168.13.233 hoanghd mb@834228 'k8s-master|2 k8s-worker|2 k8s-loadbalancer|2'
Sau khi đã define hết các biến chúng ta chạy generatevars.py để nó tự động tạo ra file terraform.tfvars và các thành phần liên quan theo những gì chúng ta đã khai báo nhé.
Cú pháp chạy sẽ là python3 /root/terraform/script/generatevars.py <cpu> <memory> <ip address đầu tiên> <username> <password> ‘<nodes>|<số lượng> ….. <nodes>|<số lượng>’, ví dụ như dưới.
python3 /root/terraform/script/generatevars.py 4 8 192.168.13.233 hoanghd mb@834228 'k8s-master|2 k8s-worker|2 k8s-loadbalancer|2'
Sau khi chạy xong file generatevars.py chúng ta sẽ có cây thư mục dạng như vậy.
File terraform.tfvars và thư mục nodes được tự động tạo cùng với các thành phần ở phía trong.
.
├── common
│ ├── authorized_keys
│ ├── id_rsa
│ ├── kickstart.yaml
│ └── metadata.yaml
├── main.tf
├── nodes
│ ├── tf-k8s-master1
│ │ └── cloudinit
│ │ └── userdata.yaml
│ ├── tf-k8s-master11
│ │ └── cloudinit
│ │ └── userdata.yaml
│ ├── tf-k8s-master12
│ │ └── cloudinit
│ │ └── userdata.yaml
│ └── tf-k8s-master2
│ └── cloudinit
│ └── userdata.yaml
├── scripts
│ ├── generatevars.py
│ └── sshconfig.sh
├── sshconfig.sh
├── terraform.tfvars
├── variables.tf
└── vsphere_data.tf
11 directories, 15 files
Ta xem nội dung file terraform.tfvars, thông tin các máy ảo đã được tạo.
vsphere_env = {
user = "administrator@hoanghd.fun"
password = "Mb@834228"
server = "vcenter.hoanghd.fun"
}
vms = {
k8s-master1 = {
vCPU = 4
vMEM = 8192
vmname = "tf-k8s-master1"
datastore = "ssd-1tb"
datacenter = "Datacenter"
network = "VM Network"
cluster = "cluster1"
template = "bionic-server-cloudimg-amd64"
ip = "192.168.13.233"
netmask = "23"
hostname = "k8s-master1"
domain_name = "k8s-master1.local"
user = "hoanghd"
password = "mb@834228"
}
k8s-master2 = {
vCPU = 4
vMEM = 8192
vmname = "tf-k8s-master2"
datastore = "ssd-1tb"
datacenter = "Datacenter"
network = "VM Network"
cluster = "cluster1"
template = "bionic-server-cloudimg-amd64"
ip = "192.168.13.234"
netmask = "23"
hostname = "k8s-master2"
domain_name = "k8s-master2.local"
user = "hoanghd"
password = "mb@834228"
}
k8s-master11 = {
vCPU = 4
vMEM = 8192
vmname = "tf-k8s-master11"
datastore = "ssd-1tb"
datacenter = "Datacenter"
network = "VM Network"
cluster = "cluster1"
template = "bionic-server-cloudimg-amd64"
ip = "192.168.13.235"
netmask = "23"
hostname = "k8s-master11"
domain_name = "k8s-master11.local"
user = "hoanghd"
password = "mb@834228"
}
k8s-master12 = {
vCPU = 4
vMEM = 8192
vmname = "tf-k8s-master12"
datastore = "ssd-1tb"
datacenter = "Datacenter"
network = "VM Network"
cluster = "cluster1"
template = "bionic-server-cloudimg-amd64"
ip = "192.168.13.236"
netmask = "23"
hostname = "k8s-master12"
domain_name = "k8s-master12.local"
user = "hoanghd"
password = "mb@834228"
}
}
Bây giờ chúng ta terraform apply để xem thành quả nhé.
Xem hình dưới ta thấy các máy ảo đang được tạo
Nếu không thích nữa chúng ta có thể xóa chúng bằng lệnh terraform destroy
Plan: 0 to add, 0 to change, 4 to destroy.
vsphere_virtual_machine.allvms["k8s-master2"]: Destroying... [id=42129516-1804-5d22-518d-3ca34ce76089]
vsphere_virtual_machine.allvms["k8s-master12"]: Destroying... [id=421258e7-6748-4d93-1106-9d5f7451c5f4]
vsphere_virtual_machine.allvms["k8s-master11"]: Destroying... [id=42129c13-f9e5-6f93-a645-ec74103a3019]
vsphere_virtual_machine.allvms["k8s-master1"]: Destroying... [id=4212513c-b067-afe2-39a1-97e5b6f8b74c]
vsphere_virtual_machine.allvms["k8s-master1"]: Destruction complete after 3s
vsphere_virtual_machine.allvms["k8s-master11"]: Destruction complete after 3s
vsphere_virtual_machine.allvms["k8s-master12"]: Destruction complete after 3s
vsphere_virtual_machine.allvms["k8s-master2"]: Destruction complete after 3s
Destroy complete! Resources: 4 destroyed.
Quan sát trên vCenter chúng ta thấy các máy ảo đã được xóa hết.
Để mà ngồi tạo và cài từng máy ảo thì rất mất công, bài viết của mình giúp các bạn có thể biến quá trình tạo bằng tay thành code, nhìn rất phê phải không nào, với cách này mình có thể tạo và xóa hàng chục hoặc thậm chí hàng trăm máy ảo chỉ bằng vài cú enter, chúc các bạn thành công nhé.