Лабораторные работы: Docker, Nginx, Docker Compose

«Создание статического веб-сайта с помощью Nginx»

1) HTML файл (index.html)

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Мое резюме</title>
    <link rel="stylesheet" href="style.css"> <!-- Подключаем CSS -->
</head>
<body>
    <div class="header">
        <img src="photo.jpg" alt="Мое фото" class="photo"> <!-- Добавляем картинку -->
        <h1>Иванов Иван Иванович</h1>
        <p>Студент группы 999 | Будущий СиСА</p>
    </div>
    <h2>Навыки:</h2>
    <ul>
        <li>HTML/CSS</li>
        <li>Linux</li>
        <li>Docker (основы)</li>
        <li>JavaScript (базовый)</li> <!-- Добавил новый навык для примера -->
    </ul>
    <p>Этот сайт запущен внутри Docker-контейнера!</p>
</body>
</html>

Примечание: В базовом задании кастомный конфиг Nginx не используется, но если хотите, скопируйте его в Dockerfile (добавьте строку COPY config/nginx.conf /etc/nginx/nginx.conf). Это позволит настроить Nginx более гибко.

2) Конфигурация Nginx (nginx.conf)

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;
        server_name localhost;
        root /usr/share/nginx/html;
        index index.html;

        location / {
            try_files $uri $uri/ =404;
        }
    }
}

3) Dockerfile

FROM nginx:alpine

RUN rm -rf /usr/share/nginx/html/*

COPY src/ /usr/share/nginx/html/

EXPOSE 80

«Развертывание многоконтейнерного веб-приложения с помощью Docker Compose»

1) docker-compose.yml

version: '3.8'

services:
  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "5432:5432"

  backend:
    build: ./backend
    ports:
      - "5000:5000"
    depends_on:
      - db
    environment:
      - FLASK_ENV=${FLASK_ENV}

  frontend:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./frontend:/usr/share/nginx/html
    depends_on:
      - backend

volumes:
  postgres_data:

2) .env файл

POSTGRES_DB=labdb
POSTGRES_USER=labuser
POSTGRES_PASSWORD=labpassword
FLASK_ENV=development

3) frontend/index.html

<!DOCTYPE html>
<html>
<head>
    <title>Docker Compose Lab</title>
    <style>
        body { font-family: Arial; margin: 40px; }
        .container { max-width: 800px; margin: 0 auto; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Многоконтейнерное приложение</h1>
        <p><strong>Студент:</strong> [Ваше ФИО]</p>
        <p><strong>Группа:</strong> [Ваша группа]</p>
        <div id="data"></div>
    </div>
    <script>
        // Запрос к backend API
        fetch('http://localhost:5000/api/data')
            .then(response => response.json())
            .then(data => {
                document.getElementById('data').innerHTML = 
                    `<p>Сообщение от backend: <strong>${data.message}</strong></p>
                     <p>Время на сервере: ${data.timestamp}</p>`;
            });
    </script>
</body>
</html>

4) backend/app.py

from flask import Flask, jsonify
from datetime import datetime
import psycopg2
import os

app = Flask(__name__)

# Подключение к PostgreSQL
def get_db_connection():
    conn = psycopg2.connect(
        host='db',
        database='labdb',
        user='labuser',
        password='labpassword'
    )
    return conn

@app.route('/api/data')
def get_data():
    # Простое возвращение JSON данных
    return jsonify({
        'message': 'Привет из Docker Compose!',
        'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        'status': 'success'
    })

@app.route('/api/health')
def health_check():
    return jsonify({'status': 'healthy'})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

5) backend/requirements.txt

Flask==2.3.3
psycopg2-binary==2.9.7

6) backend/Dockerfile

FROM python:3.9-alpine

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .

CMD ["python", "app.py"]

7) database/init.sql

CREATE TABLE IF NOT EXISTS students (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    group_name VARCHAR(50) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO students (name, group_name) VALUES
    ('Иванов Иван', 'П-4201'),
    ('Петров Петр', 'П-4202');

«Настройка сетевой инфраструктуры и управление данными в Docker»

1) docker-compose.yml для сетевой инфраструктуры

version: '3.8'

services:
  web-server:
    image: nginx:alpine
    container_name: web-server
    ports:
      - "80:80"
    volumes:
      - web_data:/usr/share/nginx/html
      - ./web:/usr/share/nginx/html
    networks:
      frontend-net:
        ipv4_address: 172.20.0.10
    depends_on:
      - app-server

  load-balancer:
    image: haproxy:alpine
    container_name: load-balancer
    ports:
      - "8080:8080"
    volumes:
      - ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
    networks:
      frontend-net:
        ipv4_address: 172.20.0.20
      backend-net:
        ipv4_address: 172.30.0.100

  app-server:
    image: python:3.9-alpine
    container_name: app-server
    working_dir: /app
    volumes:
      - ./app.py:/app/app.py
      - app-logs:/var/log/app
    command: sh -c "pip install flask && python app.py"
    networks:
      backend-net:
        ipv4_address: 172.30.0.10
      database-net:
        ipv4_address: 172.40.0.50
    depends_on:
      - database

  database:
    image: postgres:13
    container_name: database
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
      - db_backup:/backup
    networks:
      database-net:
        ipv4_address: 172.40.0.20

  backup-service:
    image: alpine:latest
    container_name: backup-service
    volumes:
      - db_backup:/backup-source
      - backup_volume:/backup-dest
      - ./backup/backup-script.sh:/scripts/backup.sh
    environment:
      DB_NAME: ${DB_NAME}
      DB_USER: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
      DB_HOST: database
      DB_PORT: 5432
    command: sh -c "apk add --no-cache postgresql-client && chmod +x /scripts/backup.sh && /scripts/backup.sh"
    networks:
      database-net:
        ipv4_address: 172.40.0.30
    depends_on:
      - database

  network-monitor:
    image: alpine:latest
    container_name: network-monitor
    volumes:
      - monitor_data:/monitor
    networks:
      frontend-net:
        ipv4_address: 172.20.0.30
      backend-net:
        ipv4_address: 172.30.0.30
      database-net:
        ipv4_address: 172.40.0.40
    command: sh -c "apk add --no-cache iputils bind-tools && ping -c 10 web-server > /monitor/web-ping.log && ping -c 10 database > /monitor/db-ping.log && tail -f /dev/null"
    depends_on:
      - web-server
      - app-server
      - database

networks:
  frontend-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24
  backend-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.30.0.0/24
  database-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.40.0.0/24

volumes:
  web_data:
  app-logs:
  db_data:
  db_backup:
  backup_volume:
  monitor_data:

2) .env файл

DB_NAME=company
DB_USER=admin
DB_PASSWORD=secret

3) app.py

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "App Server: 172.30.0.10"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001)

4) index.html

<!DOCTYPE html>
<html>
<head>
    <title>Network Lab</title>
</head>
<body>
    <h1>Сетевая лабораторная работа</h1>
    <p><strong>Студент:</strong> [Ваше ФИО]</p>
    <p><strong>Группа:</strong> [Ваша группа]</p>
    <p>Сетевые сегменты:</p>
    <ul>
        <li>Frontend: 172.20.0.0/24</li>
        <li>Backend: 172.30.0.0/24</li>
        <li>Database: 172.40.0.0/24</li>
    </ul>
</body>
</html>

5) init.sql

CREATE TABLE IF NOT EXISTS students (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    group_name VARCHAR(50) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS courses (
    course_id SERIAL PRIMARY KEY,
    course_name VARCHAR(100) NOT NULL,
    instructor VARCHAR(100) NOT NULL
);

CREATE TABLE IF NOT EXISTS student_courses (
    student_id INTEGER REFERENCES students(id),
    course_id INTEGER REFERENCES courses(course_id),
    enrollment_date DATE DEFAULT CURRENT_DATE,
    PRIMARY KEY (student_id, course_id)
);

INSERT INTO students (name, group_name) VALUES
    ('Иванов Иван Иванович', 'П-4201'),
    ('Петров Петр Петрович', 'П-4202'),
    ('Сидорова Мария Ивановна', 'П-4201'),
    ('Кузнецов Алексей Викторович', 'П-4203'),
    ('Смирнова Анна Сергеевна', 'П-4202');

INSERT INTO courses (course_name, instructor) VALUES
    ('Базы данных', 'Профессор Иванов'),
    ('Веб-технологии', 'Доцент Петрова'),
    ('Сетевые технологии', 'Профессор Сидоров');

INSERT INTO student_courses (student_id, course_id) VALUES
    (1, 1), (1, 2),
    (2, 1), (2, 3),
    (3, 2), (3, 3),
    (4, 1),
    (5, 2), (5, 3);

CREATE INDEX IF NOT EXISTS idx_students_group ON students(group_name);
CREATE INDEX IF NOT EXISTS idx_student_courses_student ON student_courses(student_id);
CREATE INDEX IF NOT EXISTS idx_student_courses_course ON student_courses(course_id);

6) backup-script.sh

#!/bin/sh

echo "Starting backup at $(date)" > /backup-dest/backup.log

pg_dump -h database -U admin company > /backup-dest/backup.sql 2>/backup-dest/error.log

echo "Backup completed at $(date)" >> /backup-dest/backup.log

7) haproxy.cfg

global
    daemon
    maxconn 256

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
    option forwardfor

frontend http_front
    bind *:8080
    stats uri /haproxy?stats
    default_backend http_back

backend http_back
    balance roundrobin
    server app1 app-server:5001 check

Команды для выполнения лабораторных работ

Запуск инфраструктуры

docker-compose up -d
docker-compose ps

Откройте браузер:

Задание 1: Анализ сетевой архитектуры

1. Просмотр всех сетей:
docker network ls

2. Детальная информация о каждой сети:
docker network inspect laba5_frontend-net
docker network inspect laba5_backend-net
docker network inspect laba5_database-net

Задание 2: Тестирование связности

1. Проверка связи между сетевыми сегментами:
docker exec network-monitor ping -c 3 web-server
docker exec network-monitor ping -c 3 database
docker exec network-monitor ping -c 3 app-server

2. Проверка недоступности (изолированная сеть):
docker exec web-server ping database # Должно не работать (ошибка)

Задание 3: Настройка маршрутизации

1. Просмотр маршрутов в контейнерах:
docker exec network-monitor ip route
docker exec web-server ip route

Часть 4: Работа с томами и данными

1. Просмотр всех томов:
docker volume ls

2. Проверка содержимого томов:
docker run --rm -v laba5_web_data:/data alpine ls -la /data
docker run --rm -v laba5_db_data:/data alpine ls -la /data

3. Создание резервной копии данных:
mkdir backups # Создайте папку backups в Laba5/
docker run --rm -v laba5_web_data:/source -v $(pwd)/backups:/backup alpine tar czf /backup/web-backup.tar.gz -C /source .

Задание 5: Мониторинг использования томов

1. Проверка использования диска:
docker system df -v

2. Просмотр логов из томов:
docker run --rm -v laba5_monitor_data:/data alpine cat /data/web-ping.log

Часть 5: Аварийные ситуации и восстановление

1. Симуляция сбоя:
docker-compose stop database

2. Проверка доступности сервисов:
curl http://localhost:80

3. Восстановление:
docker-compose start database

4. Проверка восстановления резервной копии:
docker run --rm -v laba5_backup_volume:/data alpine ls -la /data

Задание 7: Миграция данных

1. Создание нового тома:
docker volume create migrated_data

2. Копирование данных между томами:
docker run --rm -v laba5_web_data:/source -v migrated_data:/dest alpine cp -r /source/. /dest/

3. Проверка миграции:
docker run --rm -v migrated_data:/data alpine ls -la /data

Дополнительные советы

  • Для остановки всей инфраструктуры: docker-compose down
  • Если возникнут ошибки, проверьте логи: docker-compose logs <service-name>
  • Убедитесь, что порты 80 и 8080 свободны
  • После выполнения всех заданий очистите ресурсы: docker-compose down -v (удалит тома)