simonov nikita lab 3 done

This commit is contained in:
Никита Симонов 2024-01-22 02:31:48 +04:00
parent 60ef5724cd
commit f5b175e1fe
16 changed files with 574 additions and 0 deletions

View File

@ -0,0 +1,37 @@
version: '3.8'
services:
geodata-service:
build:
context: ./geodata_service
ports:
- "5001:5001"
networks:
- my_network
restart: always
user-service:
build:
context: ./user_service
ports:
- "5002:5002"
networks:
- my_network
restart: always
nginx:
image: nginx:latest
ports:
- "80:80"
depends_on:
- geodata-service
- user-service
networks:
- my_network
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
restart: always
networks:
my_network:
driver: bridge

View File

@ -0,0 +1,6 @@
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . /app
CMD ["gunicorn", "--bind", "0.0.0.0:5001", "geodata_service:app"]

View File

@ -0,0 +1,70 @@
from flask import Flask, jsonify, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///geodata.db'
db = SQLAlchemy(app)
class GeoData(db.Model):
id = db.Column(db.Integer, primary_key=True)
location = db.Column(db.String(50), nullable=False)
latitude = db.Column(db.Float, nullable=False)
longitude = db.Column(db.Float, nullable=False)
with app.app_context():
db.create_all()
@app.route('/')
def index():
geodata = GeoData.query.all()
return render_template('index.html', geodata=geodata)
@app.route('/add', methods=['POST'])
def add():
location = request.form['location']
latitude = float(request.form['latitude'])
longitude = float(request.form['longitude'])
new_geodata = GeoData(location=location, latitude=latitude, longitude=longitude)
db.session.add(new_geodata)
db.session.commit()
return redirect(url_for('index'))
@app.route('/update/<int:id>', methods=['GET', 'POST'])
def update(id):
geodata = GeoData.query.get(id)
if request.method == 'POST':
geodata.location = request.form['location']
geodata.latitude = float(request.form['latitude'])
geodata.longitude = float(request.form['longitude'])
db.session.commit()
return redirect(url_for('index'))
return render_template('update.html', geodata=geodata)
@app.route('/delete/<int:id>')
def delete(id):
geodata = GeoData.query.get(id)
db.session.delete(geodata)
db.session.commit()
return redirect(url_for('index'))
@app.route('/geodata/<int:geodata_id>', methods=['GET'])
def get_geodata(geodata_id):
geodata = GeoData.query.get(geodata_id)
if geodata:
return jsonify({
'id': geodata.id,
'location': geodata.location,
'latitude': geodata.latitude,
'longitude': geodata.longitude
})
else:
return jsonify({'error': 'Геоданные не найдены'}), 404
if __name__ == '__main__':
app.run(debug=True, port=5001)

View File

@ -0,0 +1,5 @@
Flask==3.0.0
requests==2.31.0
gunicorn==21.2.0
SQLAlchemy==2.0.25
Flask-SQLAlchemy==3.1.1

View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoData CRUD</title>
<!-- Подключение стилей Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h1 class="mt-5 mb-4">Геоданные</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Местоположение</th>
<th>Широта</th>
<th>Долгота</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
{% for data in geodata %}
<tr>
<td>{{ data.id }}</td>
<td>{{ data.location }}</td>
<td>{{ data.latitude }}</td>
<td>{{ data.longitude }}</td>
<td>
<a href="{{ url_for('update', id=data.id) }}" class="btn btn-warning btn-sm">Изменить</a>
<a href="{{ url_for('delete', id=data.id) }}" class="btn btn-danger btn-sm">Удалить</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
<form action="{{ url_for('add') }}" method="post">
<div class="form-group">
<label for="location">Местоположение:</label>
<input type="text" name="location" class="form-control" required>
</div>
<div class="form-group">
<label for="latitude">Широта:</label>
<input type="number" name="latitude" class="form-control" required>
</div>
<div class="form-group">
<label for="longitude">Долгота:</label>
<input type="number" name="longitude" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Добавить геоданные</button>
</form>
</div>
<!-- Подключение скриптов Bootstrap (необходимо для некоторых компонентов) -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>

View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Обновление геоданных</title>
<!-- Подключение стилей Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h1 class="mt-5 mb-4">Обновление геоданных</h1>
<form action="{{ url_for('update', id=geodata.id) }}" method="post">
<div class="form-group">
<label for="location">Местоположение:</label>
<input type="text" name="location" value="{{ geodata.location }}" class="form-control" required>
</div>
<div class="form-group">
<label for="latitude">Широта:</label>
<input type="number" name="latitude" value="{{ geodata.latitude }}" class="form-control" required>
</div>
<div class="form-group">
<label for="longitude">Долгота:</label>
<input type="number" name="longitude" value="{{ geodata.longitude }}" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Обновить геоданные</button>
</form>
</div>
<!-- Подключение скриптов Bootstrap (необходимо для некоторых компонентов) -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>

View File

@ -0,0 +1,4 @@
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View File

@ -0,0 +1,27 @@
events {
worker_connections 1024;
}
http {
server {
listen 80;
listen [::]:80;
server_name localhost;
location /geodata-service/ {
proxy_pass http://geodata-service:5001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /user-service/ {
proxy_pass http://user-service:5002/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}

View File

@ -0,0 +1,137 @@
# Лабораторная работа №3.
## Задание
- Создать 2 микросервиса, реализующих CRUD на связанных сущностях.
- Реализовать механизм синхронного обмена сообщениями между микросервисами.
- Реализовать шлюз на основе прозрачного прокси-сервера nginx.
Вариант: геоданные и пользователи
## Выбранные сервисы
Были написаны два сервиса на языке python с технологией flask:
- Сервис geodata_service, хранящий геоданные и реализующий CRUD операции с ними через HTTP запросы.
- Сервис user_service, хранящий данные о пользователях и реализующий CRUD операции с ними через HTTP запросы.
## Docker-файл для geodata_service
```dockerfile
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . /app
CMD ["gunicorn", "--bind", "0.0.0.0:5001", "geodata_service:app"]
```
## Docker-файл для user_service
```dockerfile
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . /app
CMD ["gunicorn", "--bind", "0.0.0.0:5002", "user_service:app"]
```
## Docker-файл для nginx
```dockerfile
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
## nginx.conf
```nginx
events {
worker_connections 1024;
}
http {
server {
listen 80;
listen [::]:80;
server_name localhost;
location /geodata-service/ {
proxy_pass http://geodata-service:5001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /user-service/ {
proxy_pass http://user-service:5002/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
```
Docker-compose
```yaml
version: '3.8'
services:
geodata-service:
build:
context: ./geodata_service
ports:
- "5001:5001"
networks:
- my_network
restart: always
user-service:
build:
context: ./user_service
ports:
- "5002:5002"
networks:
- my_network
restart: always
nginx:
image: nginx:latest
ports:
- "80:80"
depends_on:
- geodata-service
- user-service
networks:
- my_network
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
restart: always
networks:
my_network:
driver: bridge
```
## Запуск
Командой в консоли проекта:
```bash
docker-compose up -d --build
```
## Ссылка на видео
https://drive.google.com/file/d/1dXwl8p4sL60CcTIrtSztYgMNt0RL4qXe/view?usp=sharing

View File

@ -0,0 +1,6 @@
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . /app
CMD ["gunicorn", "--bind", "0.0.0.0:5002", "user_service:app"]

Binary file not shown.

View File

@ -0,0 +1,5 @@
Flask==3.0.0
requests==2.31.0
gunicorn==21.2.0
SQLAlchemy==2.0.25
Flask-SQLAlchemy==3.1.1

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Пользователи</title>
<!-- Подключение стилей Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h1 class="mt-5 mb-4">Пользователи</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Имя пользователя</th>
<th>Email</th>
<th>Местоположение</th>
<th>Широта</th>
<th>Долгота</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.id }}</td>
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
<td>{{ user.location }}</td>
<td>{{ user.latitude }}</td>
<td>{{ user.longitude }}</td>
<td>
<a href="{{ url_for('update', id=user.id) }}" class="btn btn-warning btn-sm">Изменить</a>
<a href="{{ url_for('delete', id=user.id) }}" class="btn btn-danger btn-sm">Удалить</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<br>
<form action="{{ url_for('add') }}" method="post">
<div class="form-group">
<label for="username">Имя пользователя:</label>
<input type="text" name="username" class="form-control" required>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" name="email" class="form-control" required>
</div>
<div class="form-group">
<label for="geodata">ID геоданных:</label>
<input type="text" name="geodata" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Добавить пользователя</button>
</form>
</div>
<!-- Подключение скриптов Bootstrap (необходимо для некоторых компонентов) -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>

View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Обновление пользователя</title>
<!-- Подключение стилей Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h1 class="mt-5 mb-4">Обновление пользователя</h1>
<form action="{{ url_for('update', id=user.id) }}" method="post">
<div class="form-group">
<label for="username">Имя пользователя:</label>
<input type="text" name="username" value="{{ user.username }}" class="form-control" required>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" name="email" value="{{ user.email }}" class="form-control" required>
</div>
<div class="form-group">
<label for="geodata">ID геоданных:</label>
<input type="text" name="geodata" value="{{ user.geodata }}" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Обновить пользователя</button>
</form>
</div>
<!-- Подключение скриптов Bootstrap (необходимо для некоторых компонентов) -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>

View File

@ -0,0 +1,77 @@
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
import requests
app_users = Flask(__name__)
app_users.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
db_users = SQLAlchemy(app_users)
class User(db_users.Model):
id = db_users.Column(db_users.Integer, primary_key=True)
username = db_users.Column(db_users.String(50), nullable=False)
email = db_users.Column(db_users.String(50), nullable=False)
geodata = db_users.Column(db_users.Integer, nullable=True)
# Убедимся, что создание таблицы происходит внутри контекста приложения
with app_users.app_context():
db_users.create_all()
# Функция для получения геоданных по ID
def get_geodata(geodata_id):
geodata_service_url = f'http://localhost:5001/geodata/{geodata_id}' # Замените на ваш реальный URL
response = requests.get(geodata_service_url)
if response.status_code == 200:
return response.json()
else:
return {'error': 'Геоданные не найдены'}
@app_users.route('/')
def index():
users_data = User.query.all()
# Обновляем информацию о пользователе с геоданными
for user in users_data:
geodata_id = user.geodata
user_geodata = get_geodata(geodata_id)
user.location = user_geodata.get('location', 'N/A')
user.latitude = user_geodata.get('latitude', 'N/A')
user.longitude = user_geodata.get('longitude', 'N/A')
return render_template('index.html', users=users_data)
@app_users.route('/add', methods=['POST'])
def add():
username = request.form['username']
email = request.form['email']
geodata = request.form['geodata']
new_user = User(username=username, email=email, geodata=geodata)
db_users.session.add(new_user)
db_users.session.commit()
return redirect(url_for('index'))
@app_users.route('/update/<int:id>', methods=['GET', 'POST'])
def update(id):
user = User.query.get(id)
if request.method == 'POST':
user.username = request.form['username']
user.email = request.form['email']
user.geodata = request.form['geodata']
db_users.session.commit()
return redirect(url_for('index'))
return render_template('update.html', user=user)
@app_users.route('/delete/<int:id>')
def delete(id):
user = User.query.get(id)
db_users.session.delete(user)
db_users.session.commit()
return redirect(url_for('index'))
if __name__ == '__main__':
app_users.run(debug=True, port=5002)