From 6f030312a7e4d1bedf06254af68fdc4825290d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D0=BE=D0=BF=D0=BE=D0=BB=D1=8C=D1=86?= =?UTF-8?q?=D0=B5=D0=B2=20=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4?= =?UTF-8?q?=D1=80?= Date: Wed, 4 Dec 2024 12:12:21 +0400 Subject: [PATCH] novopolcev_alexander_lab_2 is ready --- novopolcev_alexander_lab_2/.gitignore | 10 +++ novopolcev_alexander_lab_2/README.md | 62 ++++++++++++++++++ novopolcev_alexander_lab_2/app_one/Dockerfile | 7 ++ novopolcev_alexander_lab_2/app_one/main.py | 38 +++++++++++ novopolcev_alexander_lab_2/app_two/Dockerfile | 7 ++ novopolcev_alexander_lab_2/app_two/main.py | 32 +++++++++ .../data_generator/Dockerfile | 7 ++ .../data_generator/main.py | 29 ++++++++ novopolcev_alexander_lab_2/docker-compose.yml | 21 ++++++ novopolcev_alexander_lab_2/lab_2.1.png | Bin 0 -> 8986 bytes novopolcev_alexander_lab_2/lab_2.2.png | Bin 0 -> 3980 bytes 11 files changed, 213 insertions(+) create mode 100644 novopolcev_alexander_lab_2/.gitignore create mode 100644 novopolcev_alexander_lab_2/README.md create mode 100644 novopolcev_alexander_lab_2/app_one/Dockerfile create mode 100644 novopolcev_alexander_lab_2/app_one/main.py create mode 100644 novopolcev_alexander_lab_2/app_two/Dockerfile create mode 100644 novopolcev_alexander_lab_2/app_two/main.py create mode 100644 novopolcev_alexander_lab_2/data_generator/Dockerfile create mode 100644 novopolcev_alexander_lab_2/data_generator/main.py create mode 100644 novopolcev_alexander_lab_2/docker-compose.yml create mode 100644 novopolcev_alexander_lab_2/lab_2.1.png create mode 100644 novopolcev_alexander_lab_2/lab_2.2.png diff --git a/novopolcev_alexander_lab_2/.gitignore b/novopolcev_alexander_lab_2/.gitignore new file mode 100644 index 0000000..49065ec --- /dev/null +++ b/novopolcev_alexander_lab_2/.gitignore @@ -0,0 +1,10 @@ +data/ +result/ +__pycache__/ +*.py[cod] +*.env +*.venv +.env.local +venv/ +env/ +.idea/ \ No newline at end of file diff --git a/novopolcev_alexander_lab_2/README.md b/novopolcev_alexander_lab_2/README.md new file mode 100644 index 0000000..14da49f --- /dev/null +++ b/novopolcev_alexander_lab_2/README.md @@ -0,0 +1,62 @@ +# Лабораторная работа №2 - Разработка простейшего распределённого приложения + +## Задание + +1. Разработать два приложения: + - **app_one**: Ищет в каталоге /var/data файл с наибольшим количеством строк и копирует его содержимое в /var/result/data.txt. + - **app_two**: Ищет наименьшее число из файла /var/result/data.txt и сохраняет количество таких чисел из последовательности в /var/result/result.txt. +2. Разработать файлы сборки Docker для каждого приложения. +3. Собрать файл docker-compose.yml для запуска обоих приложений. +4. Настроить монтирование директорий для обмена данными между контейнерами. +5. Правильно закоммитить решение с использованием .gitignore для исключения лишних файлов. + +## Сборка и запуск: + +В директории, где находится файл docker-compose.yml, выполним команду для сборки и запуска всех контейнеров: + + +docker-compose up --build + + +Эта команда: +1. Собирает все Docker-образы для сервисов. +2. Запускает контейнеры. +3. Автоматически подготавливает данные и выполняет приложения последовательно. + +### Результаты: + +После успешного завершения работы контейнеров можно проверить результаты в папке result: +- **data.txt** — файл, полученный после выполнения первого приложения (содержит копию файла с наибольшим количеством строк из папки data). +![](lab_2.1.png "") +- **result.txt** — файл, полученный после выполнения второго приложения (содержит количество наименьших чисел из файла data.txt). +![](lab_2.2.png "") + +## Описание работы + +### Программы: + +1. **app_one/main.py**: + - Ищет файл с наибольшим количеством строк в каталоге /var/data. + - Копирует содержимое этого файла в /var/result/data.txt. + +2. **app_two/main.py**: + - Читает файл /var/result/data.txt. + - Ищет наименьшее число в файле и возводит его в третью степень. + - Сохраняет результат в файл /var/result/result.txt. + +### Генерация данных: + +Для создания случайных данных был написан скрипт generate_data.py: +- Создает несколько файлов с целыми числами в каталоге /var/data. +- Каждый файл содержит случайные числа, которые будут использоваться первым приложением. + +### Dockerfile: + +Каждое приложение имеет собственный Dockerfile, где указаны шаги для сборки Python-образов и запуска программ. + +## Вывод + +В результате лабораторной работы было создано простейшее распределенное приложение, которое использует Docker и Docker Compose для запуска двух программ, обрабатывающих данные в контейнерах. + +## Видео +https://disk.yandex.ru/i/V7k_SepqnxID5Q diff --git a/novopolcev_alexander_lab_2/app_one/Dockerfile b/novopolcev_alexander_lab_2/app_one/Dockerfile new file mode 100644 index 0000000..dab7528 --- /dev/null +++ b/novopolcev_alexander_lab_2/app_one/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.9-slim + +WORKDIR /app_one + +COPY main.py /app_one/ + +CMD ["python", "main.py"] diff --git a/novopolcev_alexander_lab_2/app_one/main.py b/novopolcev_alexander_lab_2/app_one/main.py new file mode 100644 index 0000000..1b2d430 --- /dev/null +++ b/novopolcev_alexander_lab_2/app_one/main.py @@ -0,0 +1,38 @@ +import os + +def get_file_with_most_lines(directory): + max_lines = 0 + target_file = "" + + for filename in os.listdir(directory): + filepath = os.path.join(directory, filename) + if os.path.isfile(filepath): + with open(filepath, 'r') as f: + line_count = sum(1 for _ in f) + if line_count > max_lines: + max_lines = line_count + target_file = filename + + return target_file + +def copy_file(src_directory, dest_directory, filename): + src_filepath = os.path.join(src_directory, filename) + dest_filepath = os.path.join(dest_directory, 'data.txt') + + os.makedirs(dest_directory, exist_ok=True) + + with open(src_filepath, 'r') as src_file: + with open(dest_filepath, 'w') as dest_file: + dest_file.write(src_file.read()) + +def main(): + src_directory = '/var/data' + dest_directory = '/var/result' + + target_file = get_file_with_most_lines(src_directory) + if target_file: + copy_file(src_directory, dest_directory, target_file) + print(f"File {target_file} copied to {dest_directory}/data.txt") + +if __name__ == "__main__": + main() diff --git a/novopolcev_alexander_lab_2/app_two/Dockerfile b/novopolcev_alexander_lab_2/app_two/Dockerfile new file mode 100644 index 0000000..8fb663a --- /dev/null +++ b/novopolcev_alexander_lab_2/app_two/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.9-slim + +WORKDIR /app_two + +COPY main.py /app_two/ + +CMD ["python", "main.py"] diff --git a/novopolcev_alexander_lab_2/app_two/main.py b/novopolcev_alexander_lab_2/app_two/main.py new file mode 100644 index 0000000..d7128c2 --- /dev/null +++ b/novopolcev_alexander_lab_2/app_two/main.py @@ -0,0 +1,32 @@ +import os + +def find_min_and_count(input_filepath): + if not os.path.exists(input_filepath): + raise FileNotFoundError(f"Input file {input_filepath} not found!") + + with open(input_filepath, 'r') as f: + numbers = [int(line.strip()) for line in f.readlines()] + + min_value = min(numbers) + + count = numbers.count(min_value) + + return min_value, count + +def save_result(count, output_filepath): + with open(output_filepath, 'w') as f: + f.write(str(count)) + +def main(): + input_filepath = '/var/result/data.txt' + output_filepath = '/var/result/result.txt' + + try: + min_value, count = find_min_and_count(input_filepath) + save_result(count, output_filepath) + print(f"File ({min_value}) copied to {output_filepath}: {count}") + except FileNotFoundError as e: + print(e) + +if __name__ == "__main__": + main() diff --git a/novopolcev_alexander_lab_2/data_generator/Dockerfile b/novopolcev_alexander_lab_2/data_generator/Dockerfile new file mode 100644 index 0000000..980d2ee --- /dev/null +++ b/novopolcev_alexander_lab_2/data_generator/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.9-slim + +WORKDIR /data_generator + +COPY main.py /data_generator/ + +CMD ["python", "main.py"] diff --git a/novopolcev_alexander_lab_2/data_generator/main.py b/novopolcev_alexander_lab_2/data_generator/main.py new file mode 100644 index 0000000..e8f2db5 --- /dev/null +++ b/novopolcev_alexander_lab_2/data_generator/main.py @@ -0,0 +1,29 @@ +import os +import random + + +def generate_random_files(directory, num_files, num_lines_per_file, min_value, max_value): + os.makedirs(directory, exist_ok=True) + + for i in range(num_files): + file_path = os.path.join(directory, f"file_{i + 1}.txt") + with open(file_path, 'w') as f: + for _ in range(num_lines_per_file): + random_number = random.randint(min_value, max_value) + f.write(f"{random_number}\n") + print(f"Generated file: {file_path}") + + +def main(): + data_directory = '/var/data' + num_files = 10 + num_lines_per_file = 12 + min_value = 1 + max_value = 100 + + generate_random_files(data_directory, num_files, num_lines_per_file, min_value, max_value) + print(f"Generated {num_files} files in {data_directory}") + + +if __name__ == "__main__": + main() diff --git a/novopolcev_alexander_lab_2/docker-compose.yml b/novopolcev_alexander_lab_2/docker-compose.yml new file mode 100644 index 0000000..f93eb32 --- /dev/null +++ b/novopolcev_alexander_lab_2/docker-compose.yml @@ -0,0 +1,21 @@ +services: + data_generator: + build: + context: ./data_generator + volumes: + - ./data:/var/data + app_one: + build: + context: ./app_one + volumes: + - ./data:/var/data + - ./result:/var/result + depends_on: + - data_generator + app_two: + build: + context: ./app_two + volumes: + - ./result:/var/result + depends_on: + - app_one diff --git a/novopolcev_alexander_lab_2/lab_2.1.png b/novopolcev_alexander_lab_2/lab_2.1.png new file mode 100644 index 0000000000000000000000000000000000000000..5de3c7d8bfb6b6b73b059c54b00c0ca18f948204 GIT binary patch literal 8986 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 BsCNJW literal 0 HcmV?d00001 diff --git a/novopolcev_alexander_lab_2/lab_2.2.png b/novopolcev_alexander_lab_2/lab_2.2.png new file mode 100644 index 0000000000000000000000000000000000000000..e50e7d9dfa29ac3e81622f863058ca018630cd90 GIT binary patch literal 3980 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 WbVy_dU&13(f