Thursday, November 21, 2024

[VMWare] Phần 13 – Tạo virtual machine trên vCenter bằng Terraform

-

Ở 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é.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

4,956FansLike
256FollowersFollow
223SubscribersSubscribe
spot_img

Related Stories