嗨,在这篇文章中,我们将使用 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中文网其他相关文章!