Лаб. работа “Docker Compose: Управление многоконтейнерными приложениями”
Лабораторная работа по теме “Docker Compose: Управление многоконтейнерными приложениями”
Цель работы
Изучить инструмент Docker Compose для управления многоконтейнерными приложениями. Получить практические навыки создания и управления сложными приложениями, состоящими из нескольких сервисов, настройки сетей, томов и переменных окружения.
Краткие теоретические сведения
Docker Compose — это инструмент для определения и запуска многоконтейнерных Docker-приложений. Он позволяет описать всю инфраструктуру приложения в одном файле docker-compose.yml и управлять всем стеком с помощью простых команд.
Ключевые концепции Docker Compose:
- Сервисы (Services) — каждый сервис представляет собой контейнер, который будет запущен. Сервисы могут использовать готовые образы или собираться из Dockerfile.
- Сети (Networks) — обеспечивают изолированное взаимодействие между контейнерами. По умолчанию создается сеть
default, но можно создать собственные. - Тома (Volumes) — используются для персистентного хранения данных. Могут быть именованными томами или bind mounts.
- Зависимости (depends_on) — определяют порядок запуска сервисов и гарантируют, что сервис не запустится до готовности зависимых сервисов.
- Переменные окружения — позволяют гибко конфигурировать сервисы для разных окружений.
Преимущества Docker Compose: * Декларативное описание всей инфраструктуры * Простота развертывания и масштабирования * Воспроизводимость окружений * Управление сложными многокомпонентными приложениями
Краткий справочник по командам Docker Compose
Основные команды
docker-compose up— запуск всех сервисов, описанных в docker-compose.ymldocker-compose up -d— запуск в фоновом режиме (detached)docker-compose down— остановка и удаление всех контейнеров, сетей и томовdocker-compose ps— просмотр статуса сервисовdocker-compose logs— просмотр логов всех сервисовdocker-compose logs -f— просмотр логов в реальном времениdocker-compose restart— перезапуск всех сервисовdocker-compose build— пересборка образов для сервисовdocker-compose exec <service> <command>— выполнение команды в контейнере сервиса
Команды для работы с отдельными сервисами
docker-compose up <service>— запуск конкретного сервисаdocker-compose stop <service>— остановка конкретного сервисаdocker-compose start <service>— запуск остановленного сервисаdocker-compose logs <service>— логи конкретного сервисаdocker-compose exec <service> bash— подключение к командной оболочке сервиса
Команды для очистки
docker-compose down -v— удаление томов вместе с контейнерамиdocker-compose down --rmi all— удаление всех образовdocker-compose down --remove-orphans— удаление осиротевших контейнеров
Требования к окружению
- Операционная система: Debian 13 “Trixie” или новее
- Docker Engine установлен и настроен
- Docker Compose установлен (обычно входит в состав Docker)
- Доступ в Интернет для загрузки образов
- Учетная запись пользователя без прав
root - Текстовый редактор (nano, vim или другой)
Порядок выполнения работы
Часть 1. Проверка установки Docker Compose
Проверка версии Docker Compose Убедитесь, что Docker Compose установлен и работает корректно.
Проверка доступности Docker Убедитесь, что Docker Engine работает:
Часть 2. Создание простого веб-приложения
Создание рабочей директории Создайте директорию для работы с Docker Compose:
Создание простого веб-приложения Создайте файл
app.pyс простым Flask-приложением:from flask import Flask, jsonify import os import socket app = Flask(__name__) @app.route('/') def hello(): return jsonify({ 'message': 'Hello from Docker Compose!', 'hostname': socket.gethostname(), 'container_id': os.environ.get('HOSTNAME', 'unknown') }) @app.route('/health') def health(): return jsonify({'status': 'healthy'}), 200 @app.route('/info') def info(): return jsonify({ 'container_id': os.environ.get('HOSTNAME', 'unknown'), 'python_version': os.sys.version, 'environment': os.environ.get('ENVIRONMENT', 'development') }) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)Создание requirements.txt Создайте файл с зависимостями:
flask==2.3.3 requests==2.31.0Создание Dockerfile для приложения Создайте файл
Dockerfile:
Часть 3. Создание первого docker-compose.yml
Создание базового docker-compose.yml Создайте файл
docker-compose.yml:Запуск приложения Запустите приложение с помощью Docker Compose:
Приложение будет доступно по адресу
http://localhost:5000. Проверьте работу:Запуск в фоновом режиме Остановите приложение (Ctrl+C) и запустите в фоновом режиме:
Просмотр статуса сервисов Проверьте статус запущенных сервисов:
Просмотр логов Посмотрите логи приложения:
Часть 4. Добавление базы данных
Создание обновленного docker-compose.yml Обновите
docker-compose.yml, добавив PostgreSQL:version: '3.8' services: web: build: . ports: - "5000:5000" environment: - ENVIRONMENT=development - DATABASE_URL=postgresql://user:password@db:5432/myapp volumes: - .:/app depends_on: - db restart: unless-stopped db: image: postgres:15-alpine environment: POSTGRES_DB: myapp POSTGRES_USER: user POSTGRES_PASSWORD: password volumes: - db_data:/var/lib/postgresql/data ports: - "5432:5432" restart: unless-stopped volumes: db_data:Обновление приложения для работы с БД Обновите
app.pyдля работы с PostgreSQL:from flask import Flask, jsonify import os import socket import psycopg2 from psycopg2 import OperationalError app = Flask(__name__) def get_db_connection(): try: conn = psycopg2.connect( host=os.environ.get('DB_HOST', 'db'), database=os.environ.get('POSTGRES_DB', 'myapp'), user=os.environ.get('POSTGRES_USER', 'user'), password=os.environ.get('POSTGRES_PASSWORD', 'password') ) return conn except OperationalError as e: return None @app.route('/') def hello(): conn = get_db_connection() if conn: try: cur = conn.cursor() cur.execute("SELECT version();") db_version = cur.fetchone()[0] cur.close() conn.close() db_status = "connected" except Exception as e: db_status = f"error: {str(e)}" else: db_status = "disconnected" return jsonify({ 'message': 'Hello from Docker Compose!', 'hostname': socket.gethostname(), 'container_id': os.environ.get('HOSTNAME', 'unknown'), 'database_status': db_status }) @app.route('/health') def health(): conn = get_db_connection() if conn: conn.close() return jsonify({'status': 'healthy', 'database': 'connected'}), 200 else: return jsonify({'status': 'unhealthy', 'database': 'disconnected'}), 503 @app.route('/info') def info(): return jsonify({ 'container_id': os.environ.get('HOSTNAME', 'unknown'), 'python_version': os.sys.version, 'environment': os.environ.get('ENVIRONMENT', 'development'), 'database_url': os.environ.get('DATABASE_URL', 'not configured') }) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)Обновление requirements.txt Добавьте зависимость для PostgreSQL:
flask==2.3.3 requests==2.31.0 psycopg2-binary==2.9.7Перезапуск с новой конфигурацией Остановите текущие сервисы и запустите с новой конфигурацией:
Проверка работы с базой данных Проверьте работу приложения:
Часть 5. Работа с сетями
Создание пользовательских сетей Обновите
docker-compose.ymlдля использования пользовательских сетей:version: '3.8' services: web: build: . ports: - "5000:5000" environment: - ENVIRONMENT=development - DATABASE_URL=postgresql://user:password@db:5432/myapp volumes: - .:/app depends_on: - db networks: - frontend - backend restart: unless-stopped db: image: postgres:15-alpine environment: POSTGRES_DB: myapp POSTGRES_USER: user POSTGRES_PASSWORD: password volumes: - db_data:/var/lib/postgresql/data networks: - backend restart: unless-stopped networks: frontend: driver: bridge backend: driver: bridge internal: true volumes: db_data:Перезапуск с новой конфигурацией Примените изменения:
Проверка сетей Посмотрите созданные сети:
Часть 6. Добавление Redis
Добавление Redis в docker-compose.yml Обновите конфигурацию, добавив Redis:
version: '3.8' services: web: build: . ports: - "5000:5000" environment: - ENVIRONMENT=development - DATABASE_URL=postgresql://user:password@db:5432/myapp - REDIS_URL=redis://redis:6379 volumes: - .:/app depends_on: - db - redis networks: - frontend - backend restart: unless-stopped db: image: postgres:15-alpine environment: POSTGRES_DB: myapp POSTGRES_USER: user POSTGRES_PASSWORD: password volumes: - db_data:/var/lib/postgresql/data networks: - backend restart: unless-stopped redis: image: redis:7-alpine networks: - backend restart: unless-stopped networks: frontend: driver: bridge backend: driver: bridge internal: true volumes: db_data:Обновление приложения для работы с Redis Обновите
app.py:from flask import Flask, jsonify import os import socket import psycopg2 import redis from psycopg2 import OperationalError from redis import ConnectionError as RedisConnectionError app = Flask(__name__) def get_db_connection(): try: conn = psycopg2.connect( host=os.environ.get('DB_HOST', 'db'), database=os.environ.get('POSTGRES_DB', 'myapp'), user=os.environ.get('POSTGRES_USER', 'user'), password=os.environ.get('POSTGRES_PASSWORD', 'password') ) return conn except OperationalError as e: return None def get_redis_connection(): try: r = redis.Redis( host=os.environ.get('REDIS_HOST', 'redis'), port=6379, decode_responses=True ) r.ping() return r except RedisConnectionError: return None @app.route('/') def hello(): conn = get_db_connection() redis_conn = get_redis_connection() db_status = "disconnected" redis_status = "disconnected" if conn: try: cur = conn.cursor() cur.execute("SELECT version();") db_version = cur.fetchone()[0] cur.close() conn.close() db_status = "connected" except Exception as e: db_status = f"error: {str(e)}" if redis_conn: try: redis_conn.set('hello_count', 0, ex=3600) redis_conn.incr('hello_count') redis_status = "connected" except Exception as e: redis_status = f"error: {str(e)}" return jsonify({ 'message': 'Hello from Docker Compose!', 'hostname': socket.gethostname(), 'container_id': os.environ.get('HOSTNAME', 'unknown'), 'database_status': db_status, 'redis_status': redis_status }) @app.route('/health') def health(): conn = get_db_connection() redis_conn = get_redis_connection() db_healthy = conn is not None redis_healthy = redis_conn is not None if conn: conn.close() if redis_conn: redis_conn.close() if db_healthy and redis_healthy: return jsonify({'status': 'healthy', 'database': 'connected', 'redis': 'connected'}), 200 else: return jsonify({ 'status': 'unhealthy', 'database': 'connected' if db_healthy else 'disconnected', 'redis': 'connected' if redis_healthy else 'disconnected' }), 503 @app.route('/info') def info(): return jsonify({ 'container_id': os.environ.get('HOSTNAME', 'unknown'), 'python_version': os.sys.version, 'environment': os.environ.get('ENVIRONMENT', 'development'), 'database_url': os.environ.get('DATABASE_URL', 'not configured'), 'redis_url': os.environ.get('REDIS_URL', 'not configured') }) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)Обновление requirements.txt Добавьте зависимость для Redis:
flask==2.3.3 requests==2.31.0 psycopg2-binary==2.9.7 redis==4.6.0Перезапуск с полным стеком Запустите полный стек приложений:
Проверка работы всех сервисов Проверьте работу всех компонентов:
Часть 7. Использование переменных окружения
Создание .env файла Создайте файл
.env:Обновление docker-compose.yml для использования переменных Обновите
docker-compose.yml:version: '3.8' services: web: build: . ports: - "5000:5000" environment: - ENVIRONMENT=${ENVIRONMENT} - DEBUG=${DEBUG} - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:${POSTGRES_DB} - REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379 volumes: - .:/app depends_on: - db - redis networks: - frontend - backend restart: unless-stopped db: image: postgres:15-alpine environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - db_data:/var/lib/postgresql/data networks: - backend restart: unless-stopped redis: image: redis:7-alpine command: redis-server --requirepass ${REDIS_PASSWORD} networks: - backend restart: unless-stopped networks: frontend: driver: bridge backend: driver: bridge internal: true volumes: db_data:Перезапуск с переменными окружения Примените изменения:
Часть 8. Масштабирование сервисов
Масштабирование веб-сервиса Запустите несколько экземпляров веб-сервиса:
Проверка масштабирования Посмотрите статус всех сервисов:
Тестирование балансировки нагрузки Выполните несколько запросов для проверки распределения нагрузки:
Часть 9. Мониторинг и отладка
Просмотр логов всех сервисов Посмотрите логи всех сервисов:
Просмотр логов конкретного сервиса Посмотрите логи конкретного сервиса:
Выполнение команд в контейнерах Подключитесь к контейнеру веб-сервиса:
Внутри контейнера выполните:
Проверка сетевого взаимодействия Проверьте доступность сервисов из контейнера:
Часть 10. Очистка ресурсов
Остановка всех сервисов Остановите все сервисы:
Полная очистка с удалением томов Удалите все ресурсы, включая тома:
Проверка очистки Проверьте, что ресурсы удалены:
Задания для самостоятельного выполнения
- Создание многоэтапного приложения Создайте приложение, состоящее из:
- Frontend (React/Vue.js приложение)
- Backend API (Node.js/Python Flask)
- База данных (PostgreSQL)
- Кэш (Redis)
- Nginx как reverse proxy
- Использование профилей Создайте профили для development и production окружений. В development профиле добавьте:
- Adminer для управления БД
- Redis Commander для управления Redis
- Монтирование исходного кода
- Настройка health checks Добавьте health checks для всех сервисов в docker-compose.yml. Настройте:
- Проверку доступности БД
- Проверку API endpoints
- Проверку Redis
- Создание backup скрипта Создайте скрипт для автоматического backup базы данных с использованием volumes и cron.
Контрольные вопросы
- Что такое Docker Compose и в чем его отличие от Docker?
- Какие основные секции есть в docker-compose.yml файле?
- Как работает секция
depends_onи какие проблемы она решает? - В чем разница между volumes и bind mounts в Docker Compose?
- Как создать пользовательские сети и зачем это нужно?
- Как использовать переменные окружения в Docker Compose?
- Что такое масштабирование сервисов и как его реализовать?
- Как настроить health checks для сервисов?
- В чем преимущества использования профилей в Docker Compose?
- Как организовать backup и восстановление данных в Docker Compose?
Требования к отчету
Отчет по лабораторной работе должен содержать:
- Титульный лист
- Цель работы
- Пошаговое описание выполнения работы с приведением всех вводимых команд и их выводов (можно в виде скриншотов или текстовых блоков)
- Содержимое созданного docker-compose.yml файла с комментариями
- Результаты тестирования всех endpoints приложения
- Письменные ответы на контрольные вопросы
- Вывод, в котором необходимо кратко описать полученные знания и навыки, а также привести примеры использования Docker Compose в реальных проектах