首页 > 后端开发 > Python教程 > 使用 Terraform 和 Ansible 在 KVM 上自动部署 Flask 和 PostgreSQL

使用 Terraform 和 Ansible 在 KVM 上自动部署 Flask 和 PostgreSQL

DDD
发布: 2025-01-02 14:49:42
原创
951 人浏览过

?简介

嗨,在这篇文章中,我们将使用 Libvirt 和 Terraform 在本地配置 2 个 KVM,之后,我们将使用 Ansible 部署 Flask 应用程序和 PostgreSQL。

内容

  • 项目架构
  • 要求
  • 创建KVM
  • 创建 Ansible 剧本
    • 安装 Docker 的剧本
    • 安装和配置 postgresql 的 Playbook
    • 部署 Flask 应用程序的 Playbook
    • 运行 Playbook 并测试
  • 结论

?项目架构

因此,我们将使用 Terraform 创建 2 个虚拟机,然后使用 Ansible 部署 Flask 项目和数据库。

Automating Deployment of Flask and PostgreSQL on KVM with Terraform and Ansible

?要求

我使用 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
登录后复制
登录后复制

安装 Terraform

$ 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
登录后复制
登录后复制

安装 Ansible

$ 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]
...
登录后复制
登录后复制

创建KVM

我们将使用 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 剧本

现在让我们创建 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/
登录后复制
登录后复制

安装 Docker 的 Playbook

现在创建名为 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
登录后复制

安装和配置 postgresql 的 Playbook

然后创建名为 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 应用程序的 Playbook

首先,您需要创建名为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
登录后复制

运行 Playbook 并进行测试

最后,让我们运行 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://:

Automating Deployment of Flask and PostgreSQL on KVM with Terraform and Ansible

尝试添加新任务,然后数据将添加到数据库中:

Automating Deployment of Flask and PostgreSQL on KVM with Terraform and Ansible

结论

最后,感谢您阅读本文。如果您有任何问题、建议或反馈,请随时发表评论。

NB:项目仓库:danielcristho/that-i-write

以上是使用 Terraform 和 Ansible 在 KVM 上自动部署 Flask 和 PostgreSQL的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板