Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Militsyn_work_3.docx
Binary file not shown.
5 changes: 5 additions & 0 deletions ansible/ansible/ansible.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[defaults]
roles_path = ./roles
connect_timeout = 30
host_key_checking = False
inventory = ./inventory.ini
14 changes: 14 additions & 0 deletions ansible/ansible/playbook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
- name: Deploy MySQL database
hosts: db_servers
become: true
roles:
- db

- name: Deploy Flask Todo application
hosts: app_servers
become: true
vars:
db_host: "{{ hostvars[groups['db_servers'][0]]['ansible_host'] }}"
roles:
- app
25 changes: 25 additions & 0 deletions ansible/ansible/roles/app/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
- name: Install python3 and pip
apt:
name:
- python3
- python3-pip
state: present
update_cache: yes

- name: Build and run Flask app container on host
shell: |
cd /mnt/c/Users/user/Desktop/homework3/app
docker build -t todoapp .
docker rm -f todoapp 2>/dev/null || true
docker run -d --name todoapp \
-e DB_HOST=localhost \
-e DB_USER=todouser \
-e DB_PASSWORD=todopassword \
-e DB_NAME=todoapp \
--network host \
-p 5000:5000 \
--restart always \
todoapp
delegate_to: localhost
become: false
21 changes: 21 additions & 0 deletions ansible/ansible/roles/db/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
- name: Install python3 and pip
apt:
name:
- python3
- python3-pip
state: present
update_cache: yes

- name: Pull and run MySQL container on host
shell: |
docker run -d --name mysql \
-e MYSQL_ROOT_PASSWORD=rootpassword \
-e MYSQL_DATABASE=todoapp \
-e MYSQL_USER=todouser \
-e MYSQL_PASSWORD=todopassword \
-p 3306:3306 \
--restart always \
mysql:8.0 || docker start mysql
delegate_to: localhost
become: false
38 changes: 38 additions & 0 deletions ansible/ansible/roles/docker/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
- name: Install dependencies
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
- python3-pip
state: present
update_cache: yes

- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present

- name: Add Docker repository
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present

- name: Install Docker
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
state: present
update_cache: yes

- name: Start Docker service
service:
name: docker
state: started
enabled: yes
6 changes: 6 additions & 0 deletions app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
73 changes: 73 additions & 0 deletions app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from flask import Flask, render_template, request, redirect
import mysql.connector
import os
import time

app = Flask(__name__)

def get_db():
for _ in range(10):
try:
return mysql.connector.connect(
host=os.environ.get("DB_HOST", "localhost"),
user=os.environ.get("DB_USER", "todouser"),
password=os.environ.get("DB_PASSWORD", "todopassword"),
database=os.environ.get("DB_NAME", "todoapp")
)
except:
time.sleep(3)

def init_db():
db = get_db()
cursor = db.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS tasks (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
done BOOLEAN DEFAULT FALSE
)
""")
db.commit()
db.close()

@app.route("/")
def index():
db = get_db()
cursor = db.cursor()
cursor.execute("SELECT * FROM tasks")
tasks = cursor.fetchall()
db.close()
return render_template("index.html", tasks=tasks)

@app.route("/add", methods=["POST"])
def add():
title = request.form.get("title")
if title:
db = get_db()
cursor = db.cursor()
cursor.execute("INSERT INTO tasks (title) VALUES (%s)", (title,))
db.commit()
db.close()
return redirect("/")

@app.route("/toggle/<int:task_id>")
def toggle(task_id):
db = get_db()
cursor = db.cursor()
cursor.execute("UPDATE tasks SET done = NOT done WHERE id = %s", (task_id,))
db.commit()
db.close()
return redirect("/")

@app.route("/delete/<int:task_id>")
def delete(task_id):
db = get_db()
cursor = db.cursor()
cursor.execute("DELETE FROM tasks WHERE id = %s", (task_id,))
db.commit()
db.close()
return redirect("/")

if __name__ == "__main__":
init_db()
app.run(host="0.0.0.0", port=5000)
2 changes: 2 additions & 0 deletions app/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
flask==3.0.3
mysql-connector-python==8.3.0
35 changes: 35 additions & 0 deletions app/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Todo App</title>
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 40px auto; padding: 0 20px; }
h1 { color: #333; }
form { display: flex; gap: 10px; margin-bottom: 20px; }
input[type=text] { flex: 1; padding: 8px; font-size: 16px; border: 1px solid #ccc; border-radius: 4px; }
button { padding: 8px 16px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
ul { list-style: none; padding: 0; }
li { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid #eee; }
.done { text-decoration: line-through; color: #999; }
a { color: #666; text-decoration: none; font-size: 14px; }
a:hover { color: #333; }
</style>
</head>
<body>
<h1>Todo List</h1>
<form action="/add" method="post">
<input type="text" name="title" placeholder="Новая задача..." required>
<button type="submit">Добавить</button>
</form>
<ul>
{% for task in tasks %}
<li>
<span class="{{ 'done' if task[2] else '' }}">{{ task[1] }}</span>
<a href="/toggle/{{ task[0] }}">{{ 'Отменить' if task[2] else 'Готово' }}</a>
<a href="/delete/{{ task[0] }}">Удалить</a>
</li>
{% endfor %}
</ul>
</body>
</html>
66 changes: 66 additions & 0 deletions terraform/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

99 changes: 99 additions & 0 deletions terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# SSH ключ
resource "openstack_compute_keypair_v2" "militsyn_key" {
name = var.ssh_key_name
region = var.region
public_key = file(pathexpand("~/.ssh/id_rsa.pub"))
}

# Flavor (конфигурация ВМ): 1 CPU, 2GB RAM, 10GB диск
resource "openstack_compute_flavor_v2" "militsyn_flavor" {
name = "militsyn_flavor"
ram = 2048
vcpus = 1
disk = 10
is_public = false
}

# Сеть
resource "openstack_networking_network_v2" "militsyn_network" {
name = "militsyn_network"
admin_state_up = true
region = var.region
}

# Подсеть
resource "openstack_networking_subnet_v2" "militsyn_subnet" {
name = "militsyn_subnet"
network_id = openstack_networking_network_v2.militsyn_network.id
cidr = "10.0.0.0/24"
region = var.region
dns_nameservers = ["8.8.8.8", "8.8.4.4"]
}

# Плавающий IP для app-сервера
resource "openstack_networking_floatingip_v2" "app_fip" {
pool = "external-network"
region = var.region
}

# Плавающий IP для db-сервера
resource "openstack_networking_floatingip_v2" "db_fip" {
pool = "external-network"
region = var.region
}

# ВМ 1 — app-сервер (Flask)
resource "openstack_compute_instance_v2" "app_server" {
name = "militsyn_app"
image_name = var.image_name
flavor_id = openstack_compute_flavor_v2.militsyn_flavor.id
key_pair = openstack_compute_keypair_v2.militsyn_key.name
region = var.region
availability_zone = var.zone
metadata = var.server_preemptible_tag

network {
uuid = openstack_networking_network_v2.militsyn_network.id
}
}

# ВМ 2 — db-сервер (MySQL)
resource "openstack_compute_instance_v2" "db_server" {
name = "militsyn_db"
image_name = var.image_name
flavor_id = openstack_compute_flavor_v2.militsyn_flavor.id
key_pair = openstack_compute_keypair_v2.militsyn_key.name
region = var.region
availability_zone = var.zone
metadata = var.server_preemptible_tag

network {
uuid = openstack_networking_network_v2.militsyn_network.id
}
}

# Привязка плавающих IP
resource "openstack_compute_floatingip_associate_v2" "app_fip_assoc" {
floating_ip = openstack_networking_floatingip_v2.app_fip.address
instance_id = openstack_compute_instance_v2.app_server.id
}

resource "openstack_compute_floatingip_associate_v2" "db_fip_assoc" {
floating_ip = openstack_networking_floatingip_v2.db_fip.address
instance_id = openstack_compute_instance_v2.db_server.id
}

# Генерация inventory.ini для Ansible
resource "local_file" "inventory" {
content = <<-EOT
[app_servers]
vm1 ansible_host=${openstack_networking_floatingip_v2.app_fip.address} ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa

[db_servers]
vm2 ansible_host=${openstack_networking_floatingip_v2.db_fip.address} ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa

[all:vars]
ansible_python_interpreter=/usr/bin/python3
EOT
filename = "${path.module}/../ansible/inventory.ini"
}
Loading