嗨,在這篇文章中,我們將使用 Libvirt 和 Terraform 在本地配置 2 個 KVM,之後,我們將使用 Ansible 部署 Flask 應用程式和 PostgreSQL。
因此,我們將使用 Terraform 建立 2 個虛擬機,然後使用 Ansible 部署 Flask 專案和資料庫。
我使用 Ubuntu 22.04 LTS 作為該專案的作業系統。如果您使用不同的作業系統,請在安裝所需的依賴項時進行必要的調整。
此設定的主要先決條件是 KVM 管理程式。所以你需要在你的系統中安裝KVM。如果您使用 Ubuntu,可以按照以下步驟操作:
sudo apt -y install bridge-utils cpu-checker libvirt-clients libvirt-daemon qemu qemu-kvm
執行以下命令以確保您的處理器支援虛擬化功能:
$ kvm-ok INFO: /dev/kvm exists KVM acceleration can be used
$ wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list $ sudo apt update && sudo apt install terraform -y
驗證安裝:
$ terraform version Terraform v1.9.8 on linux_amd64
$ sudo apt update $ sudo apt install software-properties-common $ sudo add-apt-repository --yes --update ppa:ansible/ansible $ sudo apt install ansible -y
驗證安裝:
$ ansible --version ansible [core 2.15.1] ...
我們將使用 libvirt 提供者和 Terraform 來部署 KVM 虛擬機器。
建立main.tf,只需指定要使用的提供者和版本:
terraform { required_providers { libvirt = { source = "dmacvicar/libvirt" version = "0.8.1" } } } provider "libvirt" { uri = "qemu:///system" }
之後,執行 terraform init 指令來初始化環境:
$ terraform init Initializing the backend... Initializing provider plugins... - Reusing previous version of hashicorp/template from the dependency lock file - Reusing previous version of dmacvicar/libvirt from the dependency lock file - Reusing previous version of hashicorp/null from the dependency lock file - Using previously-installed hashicorp/template v2.2.0 - Using previously-installed dmacvicar/libvirt v0.8.1 - Using previously-installed hashicorp/null v3.2.3 Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
現在創建我們的變數.tf。此變數.tf 檔案定義 libvirt 磁碟區路徑的輸入、作為 VM 作業系統的 Ubuntu 20.04 映像 URL 以及 VM 主機名稱清單。
variable "libvirt_disk_path" { description = "path for libvirt pool" default = "default" } variable "ubuntu_20_img_url" { description = "ubuntu 20.04 image" default = "https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-amd64.img" } variable "vm_hostnames" { description = "List of VM hostnames" default = ["vm1", "vm2"] }
讓我們更新 main.tf:
resource "null_resource" "cache_image" { provisioner "local-exec" { command = "wget -O /tmp/ubuntu-20.04.qcow2 ${var.ubuntu_20_img_url}" } } resource "libvirt_volume" "base" { name = "base.qcow2" source = "/tmp/ubuntu-20.04.qcow2" pool = var.libvirt_disk_path format = "qcow2" depends_on = [null_resource.cache_image] } # Volume for VM with size 10GB resource "libvirt_volume" "ubuntu20-qcow2" { count = length(var.vm_hostnames) name = "ubuntu20-${count.index}.qcow2" base_volume_id = libvirt_volume.base.id pool = var.libvirt_disk_path size = 10737418240 # 10GB } data "template_file" "user_data" { count = length(var.vm_hostnames) template = file("${path.module}/config/cloud_init.yml") } data "template_file" "network_config" { count = length(var.vm_hostnames) template = file("${path.module}/config/network_config.yml") } resource "libvirt_cloudinit_disk" "commoninit" { count = length(var.vm_hostnames) name = "commoninit-${count.index}.iso" user_data = data.template_file.user_data[count.index].rendered network_config = data.template_file.network_config[count.index].rendered pool = var.libvirt_disk_path } resource "libvirt_domain" "domain-ubuntu" { count = length(var.vm_hostnames) name = var.vm_hostnames[count.index] memory = "1024" # VM memory vcpu = 1 # VM CPU cloudinit = libvirt_cloudinit_disk.commoninit[count.index].id network_interface { network_name = "default" wait_for_lease = true hostname = var.vm_hostnames[count.index] } console { type = "pty" target_port = "0" target_type = "serial" } console { type = "pty" target_type = "virtio" target_port = "1" } disk { volume_id = libvirt_volume.ubuntu20-qcow2[count.index].id } graphics { type = "spice" listen_type = "address" autoport = true } }
此腳本將使用 Libvirt 提供者設定多個 KVM 虛擬機器。它下載 Ubuntu 20.04 基本映像,為每個 VM 複製它,為使用者和網路設定配置 cloud-init,並部署具有指定主機名稱、1GB 記憶體和 SPICE 圖形的 VM。此設定會根據 var.vm_hostnames 中提供的主機名稱數量動態調整。
正如我所提到的,我正在使用 cloud-init,所以讓我們在 config 目錄下設定網路設定和 cloud init:
mkdir config/
然後建立我們的 config/cloud_init.yml,只需確保在設定中配置用於 ssh 存取的公共 ssh 金鑰:
#cloud-config runcmd: - sed -i '/PermitRootLogin/d' /etc/ssh/sshd_config - echo "PermitRootLogin yes" >> /etc/ssh/sshd_config - systemctl restart sshd ssh_pwauth: true disable_root: false chpasswd: list: | root:cloudy24 expire: false users: - name: ubuntu gecos: ubuntu groups: - sudo sudo: - ALL=(ALL) NOPASSWD:ALL home: /home/ubuntu shell: /bin/bash lock_passwd: false ssh_authorized_keys: - ssh-rsa AAAA...
然後是網路配置,在 config/network_config.yml 中:
version: 2 ethernets: ens3: dhcp4: true
我們的專案結構應該如下所示:
sudo apt -y install bridge-utils cpu-checker libvirt-clients libvirt-daemon qemu qemu-kvm
現在運行一個計劃,看看會做什麼:
$ kvm-ok INFO: /dev/kvm exists KVM acceleration can be used
並執行 terraform apply 來運行我們的部署:
$ wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list $ sudo apt update && sudo apt install terraform -y
使用 virsh 指令驗證虛擬機器建立:
$ terraform version Terraform v1.9.8 on linux_amd64
取得實例IP位址:
$ sudo apt update $ sudo apt install software-properties-common $ sudo add-apt-repository --yes --update ppa:ansible/ansible $ sudo apt install ansible -y
嘗試使用 ubuntu 使用者存取虛擬機器:
$ ansible --version ansible [core 2.15.1] ...
現在讓我們建立 Ansible Playbook 以在 Docker 上部署 Flask 和 Postgresql。首先你需要建立 ansible 目錄和 ansible.cfg 檔案:
terraform { required_providers { libvirt = { source = "dmacvicar/libvirt" version = "0.8.1" } } } provider "libvirt" { uri = "qemu:///system" }
$ terraform init Initializing the backend... Initializing provider plugins... - Reusing previous version of hashicorp/template from the dependency lock file - Reusing previous version of dmacvicar/libvirt from the dependency lock file - Reusing previous version of hashicorp/null from the dependency lock file - Using previously-installed hashicorp/template v2.2.0 - Using previously-installed dmacvicar/libvirt v0.8.1 - Using previously-installed hashicorp/null v3.2.3 Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
然後建立名為hosts的庫存檔案:
variable "libvirt_disk_path" { description = "path for libvirt pool" default = "default" } variable "ubuntu_20_img_url" { description = "ubuntu 20.04 image" default = "https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-amd64.img" } variable "vm_hostnames" { description = "List of VM hostnames" default = ["vm1", "vm2"] }
使用 ansible ping 指令檢查我們的虛擬機器:
resource "null_resource" "cache_image" { provisioner "local-exec" { command = "wget -O /tmp/ubuntu-20.04.qcow2 ${var.ubuntu_20_img_url}" } } resource "libvirt_volume" "base" { name = "base.qcow2" source = "/tmp/ubuntu-20.04.qcow2" pool = var.libvirt_disk_path format = "qcow2" depends_on = [null_resource.cache_image] } # Volume for VM with size 10GB resource "libvirt_volume" "ubuntu20-qcow2" { count = length(var.vm_hostnames) name = "ubuntu20-${count.index}.qcow2" base_volume_id = libvirt_volume.base.id pool = var.libvirt_disk_path size = 10737418240 # 10GB } data "template_file" "user_data" { count = length(var.vm_hostnames) template = file("${path.module}/config/cloud_init.yml") } data "template_file" "network_config" { count = length(var.vm_hostnames) template = file("${path.module}/config/network_config.yml") } resource "libvirt_cloudinit_disk" "commoninit" { count = length(var.vm_hostnames) name = "commoninit-${count.index}.iso" user_data = data.template_file.user_data[count.index].rendered network_config = data.template_file.network_config[count.index].rendered pool = var.libvirt_disk_path } resource "libvirt_domain" "domain-ubuntu" { count = length(var.vm_hostnames) name = var.vm_hostnames[count.index] memory = "1024" # VM memory vcpu = 1 # VM CPU cloudinit = libvirt_cloudinit_disk.commoninit[count.index].id network_interface { network_name = "default" wait_for_lease = true hostname = var.vm_hostnames[count.index] } console { type = "pty" target_port = "0" target_type = "serial" } console { type = "pty" target_type = "virtio" target_port = "1" } disk { volume_id = libvirt_volume.ubuntu20-qcow2[count.index].id } graphics { type = "spice" listen_type = "address" autoport = true } }
現在建立 playbook.yml 和角色,此 playbook 將安裝和設定 Docker、Flask 和 PostgreSQL:
mkdir config/
現在建立一個名為 Roles/docker 的新目錄:
#cloud-config runcmd: - sed -i '/PermitRootLogin/d' /etc/ssh/sshd_config - echo "PermitRootLogin yes" >> /etc/ssh/sshd_config - systemctl restart sshd ssh_pwauth: true disable_root: false chpasswd: list: | root:cloudy24 expire: false users: - name: ubuntu gecos: ubuntu groups: - sudo sudo: - ALL=(ALL) NOPASSWD:ALL home: /home/ubuntu shell: /bin/bash lock_passwd: false ssh_authorized_keys: - ssh-rsa AAAA...
在docker中建立一個名為tasks的新目錄,然後建立新檔案main.yml。此檔案將安裝 Docker 和 Docker Compose:
version: 2 ethernets: ens3: dhcp4: true
$ tree . ├── config │ ├── cloud_init.yml │ └── network_config.yml ├── main.tf └── variables.tf
然後建立一個名為 psql 的新目錄,建立一個名為 vars、tempalates &tasks 的子目錄:
$ terraform plan data.template_file.user_data[1]: Reading... data.template_file.user_data[0]: Reading... data.template_file.network_config[1]: Reading... data.template_file.network_config[0]: Reading... ... Plan: 8 to add, 0 to change, 0 to destroy
之後,在 vars 中建立 main.yml。這些是用於設定使用者名稱、密碼等的變數:
$ terraform apply ... null_resource.cache_image: Creation complete after 10m36s [id=4239391010009470471] libvirt_volume.base: Creating... libvirt_volume.base: Creation complete after 3s [id=/var/lib/libvirt/images/base.qcow2] libvirt_volume.ubuntu20-qcow2[1]: Creating... libvirt_volume.ubuntu20-qcow2[0]: Creating... libvirt_volume.ubuntu20-qcow2[1]: Creation complete after 0s [id=/var/lib/libvirt/images/ubuntu20-1.qcow2] libvirt_volume.ubuntu20-qcow2[0]: Creation complete after 0s [id=/var/lib/libvirt/images/ubuntu20-0.qcow2] libvirt_domain.domain-ubuntu[1]: Creating... ... libvirt_domain.domain-ubuntu[1]: Creation complete after 51s [id=6221f782-48b7-49a4-9eb9-fc92970f06a2] Apply complete! Resources: 8 added, 0 changed, 0 destroyed
接下來,我們將建立一個名為 docker-compose.yml.j2 的 jinja 檔案。使用此文件,我們將建立 postgresql 容器:
$ virsh list Id Name State ---------------------- 1 vm1 running 2 vm2 running
接下來,為任務建立 main.yml。因此,我們將複製 docker-compose.yml.j2 並使用 docker compose 運行:
$ virsh net-dhcp-leases --network default Expiry Time MAC address Protocol IP address Hostname Client ID or DUID ----------------------------------------------------------------------------------------------------------------------------------------------- 2024-12-09 19:50:00 52:54:00:2e:0e:86 ipv4 192.168.122.19/24 vm1 ff:b5:5e:67:ff:00:02:00:00:ab:11:b0:43:6a:d8:bc:16:30:0d 2024-12-09 19:50:00 52:54:00:86:d4:ca ipv4 192.168.122.15/24 vm2 ff:b5:5e:67:ff:00:02:00:00:ab:11:39:24:8c:4a:7e:6a:dd:78
首先,您需要建立一個名為flask的目錄,然後再次建立子目錄:
$ ssh ubuntu@192.168.122.15 The authenticity of host '192.168.122.15 (192.168.122.15)' can't be established. ED25519 key fingerprint is SHA256:Y20zaCcrlOZvPTP+/qLLHc7vJIOca7QjTinsz9Bj6sk. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '192.168.122.15' (ED25519) to the list of known hosts. Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-200-generic x86_64) ... ubuntu@ubuntu:~$
接下來,將 main.yml 加入變數中。該文件引用了先前的 posgtresql 變量,並添加了 VM2(資料庫 VM)的 IP 位址:
$ mkdir ansible && cd ansible
接下來,建立 config.py.j2 到模板。該檔案將取代 Flask 專案中的舊設定檔:
[defaults] inventory = hosts host_key_checking = True deprecation_warnings = False collections = ansible.posix, community.general, community.postgresql
接下來,建立 docker-compose.yml.j2 到範本。有了這個文件,我們將使用 docker compose 建立一個容器:
[vm1] 192.168.122.19 ansible_user=ubuntu [vm2] 192.168.122.15 ansible_user=ubuntu
接下來,在任務中建立main.yml。使用此文件,我們將克隆 Flask 項目,新增 compose 文件,取代 config.py 並使用 docker compose 建立新容器:
$ ansible -m ping all 192.168.122.15 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong" } 192.168.122.19 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong" }
我們的專案結構應該如下所示:
--- - name: Deploy Flask hosts: vm1 become: true remote_user: ubuntu roles: - flask - config - name: Deploy Postgresql hosts: vm2 become: true remote_user: ubuntu roles: - psql - config
最後,讓我們執行 ansible-playbook 來部署 PostgreSQL 和 Flask:
$ mkdir roles $ mkdir docker
完成後,確保沒有錯誤即可。然後你會看到創建了兩個。 VM1 中是 Flask,VM2 中是 Postgresql:
sudo apt -y install bridge-utils cpu-checker libvirt-clients libvirt-daemon qemu qemu-kvm
嘗試使用瀏覽器存取應用程序,只需輸入 http://
嘗試新增任務,然後資料將新增至資料庫:
最後,感謝您閱讀本文。如果您有任何問題、建議或回饋,請隨時發表評論。
NB:專案倉庫:danielcristho/that-i-write
以上是使用 Terraform 和 Ansible 在 KVM 上自動部署 Flask 和 PostgreSQL的詳細內容。更多資訊請關注PHP中文網其他相關文章!