? Einführung
Hallo, in diesem Beitrag werden wir Libvirt mit Terraform verwenden, um 2 KVM lokal bereitzustellen, und danach werden wir Flask App und PostgreSQL mit Ansible bereitstellen.
Inhalt
- Projektarchitektur
- Anforderungen
- KVM erstellen
-
Erstellen Sie ein Ansible-Playbook
- Playbook zur Installation von Docker
- Playbook zum Installieren und Konfigurieren von Postgresql
- Playbook zum Bereitstellen der Flask-App
- Playbook ausführen und testen
- Fazit
? Projektarchitektur
Also erstellen wir zwei VMs mit Terraform und stellen dann ein Flask-Projekt und die Datenbank mit Ansible bereit.

? Anforderungen
Ich habe Ubuntu 22.04 LTS als Betriebssystem für dieses Projekt verwendet. Wenn Sie ein anderes Betriebssystem verwenden, nehmen Sie bitte bei der Installation der erforderlichen Abhängigkeiten die erforderlichen Anpassungen vor.
Die wichtigste Voraussetzung für dieses Setup ist ein KVM-Hypervisor. Sie müssen also KVM in Ihrem System installieren. Wenn Sie Ubuntu verwenden, können Sie diesen Schritt ausführen:
1 | sudo apt -y install bridge-utils cpu-checker libvirt-clients libvirt-daemon qemu qemu-kvm
|
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Führen Sie den folgenden Befehl aus, um sicherzustellen, dass Ihr Prozessor Virtualisierungsfunktionen unterstützt:
1 2 3 4 | $ kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used
|
Nach dem Login kopieren
Nach dem Login kopieren
Installieren Sie Terraform
1 2 3 | $ wget -O - https:
$ 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
|
Nach dem Login kopieren
Nach dem Login kopieren
Installation überprüfen:
1 2 3 4 | $ terraform version
Terraform v1.9.8
on linux_amd64
|
Nach dem Login kopieren
Nach dem Login kopieren
Installieren Sie Ansible
1 2 3 4 | $ sudo apt update
$ sudo apt install software-properties-common
$ sudo add-apt-repository --yes --update ppa:ansible/ansible
$ sudo apt install ansible -y
|
Nach dem Login kopieren
Nach dem Login kopieren
Installation überprüfen:
1 2 3 4 | $ ansible --version
ansible [core 2.15.1]
...
|
Nach dem Login kopieren
Nach dem Login kopieren
KVM erstellen
Wir werden den libvirt-Anbieter mit Terraform verwenden, um eine virtuelle KVM-Maschine bereitzustellen.
Erstellen Sie main.tf, geben Sie einfach den Anbieter und die Version an, die Sie verwenden möchten:
1 2 3 4 5 6 7 8 9 10 11 12 | terraform {
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "0.8.1"
}
}
}
provider "libvirt" {
uri = "qemu:///system"
}
|
Nach dem Login kopieren
Nach dem Login kopieren
Führen Sie anschließend den Befehl terraform init aus, um die Umgebung zu initialisieren:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $ 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.
|
Nach dem Login kopieren
Nach dem Login kopieren
Erstellen Sie nun unsere Variables.tf. Diese Variable.tf-Datei definiert Eingaben für den libvirt-Datenträgerpoolpfad, die Ubuntu 20.04-Image-URL als Betriebssystem für die VMs und eine Liste von VM-Hostnamen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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" ]
}
|
Nach dem Login kopieren
Nach dem Login kopieren
Lassen Sie uns unsere main.tf aktualisieren:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 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
}
}
|
Nach dem Login kopieren
Nach dem Login kopieren
Das Skript stellt mehrere KVM-VMs mithilfe des Libvirt-Anbieters bereit. Es lädt ein Ubuntu 20.04-Basisimage herunter, klont es für jede VM, konfiguriert Cloud-Init für Benutzer- und Netzwerkeinstellungen und stellt VMs mit angegebenen Hostnamen, 1 GB Speicher und SPICE-Grafiken bereit. Das Setup passt sich dynamisch an die Anzahl der in var.vm_hostnames.
angegebenen Hostnamen an
Wie ich bereits erwähnt habe, verwende ich Cloud-Init. Richten wir also die Netzwerkkonfiguration und Cloud-Init im Konfigurationsverzeichnis ein:
Dann erstellen Sie unsere config/cloud_init.yml. Stellen Sie nur sicher, dass Sie Ihren öffentlichen SSH-Schlüssel für den SSH-Zugriff in der Konfiguration konfigurieren:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #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...
|
Nach dem Login kopieren
Nach dem Login kopieren
Und dann Netzwerkkonfiguration, in config/network_config.yml:
1 2 3 4 | version: 2
ethernets:
ens3:
dhcp4: true
|
Nach dem Login kopieren
Nach dem Login kopieren
Unsere Projektstruktur sollte so aussehen:
1 | sudo apt -y install bridge-utils cpu-checker libvirt-clients libvirt-daemon qemu qemu-kvm
|
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Erstellen Sie nun einen Plan, um zu sehen, was getan werden soll:
1 2 3 4 | $ kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used
|
Nach dem Login kopieren
Nach dem Login kopieren
Und führen Sie terraform apply aus, um unsere Bereitstellung auszuführen:
1 2 3 | $ wget -O - https:
$ 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
|
Nach dem Login kopieren
Nach dem Login kopieren
Überprüfen Sie die VM-Erstellung mit dem virsh-Befehl:
1 2 3 4 | $ terraform version
Terraform v1.9.8
on linux_amd64
|
Nach dem Login kopieren
Nach dem Login kopieren
Instanzen-IP-Adresse abrufen:
1 2 3 4 | $ sudo apt update
$ sudo apt install software-properties-common
$ sudo add-apt-repository --yes --update ppa:ansible/ansible
$ sudo apt install ansible -y
|
Nach dem Login kopieren
Nach dem Login kopieren
Versuchen Sie, mit dem Ubuntu-Benutzer:
auf die VM zuzugreifen
1 2 3 4 | $ ansible --version
ansible [core 2.15.1]
...
|
Nach dem Login kopieren
Nach dem Login kopieren
Erstellen Sie ein Ansible-Playbook
Jetzt erstellen wir das Ansible Playbook, um Flask und Postgresql auf Docker bereitzustellen. Zuerst müssen Sie ein Ansible-Verzeichnis und eine Ansible.cfg-Datei erstellen:
1 2 3 4 5 6 7 8 9 10 11 12 | terraform {
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "0.8.1"
}
}
}
provider "libvirt" {
uri = "qemu:///system"
}
|
Nach dem Login kopieren
Nach dem Login kopieren
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $ 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.
|
Nach dem Login kopieren
Nach dem Login kopieren
Dann erstellen Sie eine Inventardatei namens hosts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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" ]
}
|
Nach dem Login kopieren
Nach dem Login kopieren
Überprüfen unserer VMs mit dem Ansible-Ping-Befehl:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 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
}
}
|
Nach dem Login kopieren
Nach dem Login kopieren
Erstellen Sie nun playbook.yml und Rollen. Dieses Playbook installiert und konfiguriert Docker, Flask und PostgreSQL:
Playbook zur Installation von Docker
Erstellen Sie nun ein neues Verzeichnis mit dem Namen „roles/docker:“
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #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...
|
Nach dem Login kopieren
Nach dem Login kopieren
Erstellen Sie im Docker ein neues Verzeichnis mit dem Namen „Tasks“ und erstellen Sie dann die neue Datei „main.yml“. Diese Datei installiert Docker und Docker Compose:
1 2 3 4 | version: 2
ethernets:
ens3:
dhcp4: true
|
Nach dem Login kopieren
Nach dem Login kopieren
1 2 3 4 5 6 7 | $ tree
.
├── config
│ ├── cloud_init.yml
│ └── network_config.yml
├── main.tf
└── variables.tf
|
Nach dem Login kopieren
Playbook zum Installieren und Konfigurieren von Postgresql
Dann erstellen Sie ein neues Verzeichnis mit dem Namen psql und ein Unterverzeichnis mit dem Namen vars, tempalates & task:
1 2 3 4 5 6 7 8 9 | $ 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
|
Nach dem Login kopieren
Danach erstellen Sie in vars main.yml. Dies sind Variablen, die zum Festlegen von Benutzernamen, Passwörtern usw. verwendet werden:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ 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
|
Nach dem Login kopieren
Als nächstes erstellen wir eine Jinja-Datei mit dem Namen docker-compose.yml.j2. Mit dieser Datei erstellen wir einen Postgresql-Container:
1 2 3 4 5 6 | $ virsh list
Id Name State
----------------------
1 vm1 running
2 vm2 running
|
Nach dem Login kopieren
Als nächstes erstellen Sie main.yml für Aufgaben. Also kopieren wir docker-compose.yml.j2 und führen es mit docker compose:
aus
1 2 3 4 5 6 | $ 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
|
Nach dem Login kopieren
Playbook zum Bereitstellen der Flask-App
Zuerst müssen Sie ein Verzeichnis namens flask erstellen und dann erneut ein Unterverzeichnis erstellen:
1 2 3 4 5 6 7 8 9 10 11 | $ 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:~$
|
Nach dem Login kopieren
Als nächstes fügen Sie main.yml zu vars hinzu. Diese Datei bezieht sich zuvor auf die Variable posgtresql, mit zusätzlicher IP-Adresse von VM2 (Datenbank-VM):
1 | $ mkdir ansible && cd ansible
|
Nach dem Login kopieren
Als nächstes erstellen Sie config.py.j2 für Vorlagen. Diese Datei ersetzt die alte Konfigurationsdatei aus dem Flask-Projekt:
1 2 3 4 5 | [defaults]
inventory = hosts
host_key_checking = True
deprecation_warnings = False
collections = ansible.posix, community.general, community.postgresql
|
Nach dem Login kopieren
Als nächstes erstellen Sie docker-compose.yml.j2 für Vorlagen. Mit dieser Datei erstellen wir einen Container mit Docker Compose:
1 2 3 4 5 | [vm1]
192.168.122.19 ansible_user=ubuntu
[vm2]
192.168.122.15 ansible_user=ubuntu
|
Nach dem Login kopieren
Als nächstes erstellen Sie main.yml in Aufgaben. Mit dieser Datei klonen wir das Flask-Projekt, fügen eine Compose-Datei hinzu, ersetzen config.py und erstellen einen neuen Container mit Docker Compose:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ 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"
}
|
Nach dem Login kopieren
Unsere Projektstruktur sollte so aussehen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ---
- 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
|
Nach dem Login kopieren
Führen Sie Playbook aus und testen Sie es
Lassen Sie uns abschließend Ansible-Playbook ausführen, um PostgreSQL und Flask bereitzustellen:
1 2 | $ mkdir roles
$ mkdir docker
|
Nach dem Login kopieren
Stellen Sie nach Abschluss sicher, dass kein Fehler vorliegt. Dann sehen Sie, dass zwei erstellt wurden. In VM1 ist Flask und in VM2 ist Postgresql:
1 | sudo apt -y install bridge-utils cpu-checker libvirt-clients libvirt-daemon qemu qemu-kvm
|
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Versuchen Sie, über Browser auf die App zuzugreifen. Geben Sie einfach http://: ein.

Versuchen Sie, eine neue Aufgabe hinzuzufügen und dann werden die Daten zur Datenbank hinzugefügt:

Abschluss
Abschließend vielen Dank, dass Sie diesen Artikel gelesen haben. Hinterlassen Sie gerne einen Kommentar, wenn Sie Fragen, Anregungen oder Feedback haben.
Hinweis: Projekt-Repo: danielcristho/that-i-write
Das obige ist der detaillierte Inhalt vonAutomatisierung der Bereitstellung von Flask und PostgreSQL auf KVM mit Terraform und Ansible. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!