Evolution
Тема интерфейса

Развертывание отказоустойчивого веб-приложения с разделением компонентов во фреймворке Docker Swarm

В этой лабораторной работе вы научитесь развертывать в кластере Docker Swarm микросервисное веб-приложение, состоящее из трех компонентов: frontend, backend и база данных. Приложение будет работать с веб-интерфейсом, API-сервисом и централизованным хранилищем данных.

Отказоустойчивость архитектуры будет обеспечена за счет следующих технологий:

  • репликация сервисов frontend и backend на нескольких виртуальных машинах, объединенных в один кластер;

  • использование базы данных MySQL, развернутой как сервис внутри Swarm;

  • хранение данных в томах для обеспечения их устойчивости к сбоям контейнеров.

Вы будете использовать следующие сервисы:

  • Виртуальные машины — виртуальная машина в облаке для размещения приложения.

  • Публичный IP-адрес — для доступа к виртуальным машинам через интернет.

  • VPC сеть — изолированная виртуальная сеть для создания безопасной инфраструктуры.

  • Load Balancer — балансировщик нагрузки для виртуальных машин в облаке Evolution.

  • Artifact Registry — хранение, совместное использование и управление Docker-образами и Helm-чартами.

В конце лабораторной работы вы протестируете доступность системы при отключении одного из узлов кластера.

Шаги:

Перед началом работы

  1. Если вы уже зарегистрированы, войдите под своей учетной записью.

  2. Убедитесь, что у вас достаточно прав для создания реестра и загрузки артефактов в сервисе Artifact Registry.

  3. Создайте реестр в Artifact Registry. Скопируйте полученный URI реестра, он будет нужен для выполнения дальнейших шагов.

  4. Получите ключи доступа сервисного аккаунта. Запишите Key ID (логин) и Key Secret (пароль), они будут нужны для выполнения дальнейших шагов. После того как вы закроете окно, повторно посмотреть пароль будет нельзя.

1. Разверните ресурсы в облаке

Внимание

Все создаваемые ресурсы должны располагаться в одной зоне доступности.

  1. Создайте VPC-сеть или выберите одну из существующих сетей.

  2. Создайте подсеть в выбранной VPC-сети или выберите одну из существующих.

  3. Создайте группу безопасности и добавьте в нее правило входящего трафика со следующими параметрами:

    • Протокол: TCP;

    • Порт: 8080;

    • Тип источника: IP-адрес;

    • Источник: 0.0.0.0/0.

  4. Создайте три виртуальные машины со следующими параметрами:

    • Название: docker-swarm-manager-1, docker-swarm-worker-1 и docker-swarm-worker-2.

    • Зона доступности: та же, что у подсети и группы безопасности.

    • Образ: выберите образ с ОС Ubuntu 22.04. Вы можете выбрать другую ОС, но команды для выполнения лабораторной работы могут отличаться.

    • Подключить к подсети: включите опцию и укажите подсеть и группу безопасности, созданные ранее.

    • Подключить публичный IP: включите опцию и выберите прямой IP-адрес.

    • Метод аутентификации: выберите Публичный ключ и укажите SSH-ключ, созданный ранее.

    • Остальные параметры задайте по своему усмотрению.

  5. Запишите публичные IP-адреса каждой виртуальной машины. В этой лабораторной работе используются следующие IP-адреса:

    • docker-swarm-manager-1 — 176.123.162.37;

    • docker-swarm-worker-1 — 176.109.104.79;

    • docker-swarm-worker-2 — 176.123.162.146.

2. Настройте виртуальные машины

В терминале для каждой из созданных машин выполните действия:

  1. Подключитесь к виртуальной машине по SSH с использованием публичного IP-адреса.

  2. Установите Docker:

    curl -fsSL get.docker.com -o get-docker.sh && sudo sh get-docker.sh

3. Создайте кластер Docker Swarm

  1. Откройте сессию терминала с подключением к виртуальной машине docker-swarm-manager-1.

  2. Создайте кластер при помощи команды:

    sudo docker swarm init --default-addr-pool 192.168.100.0/16 --advertise-addr 176.123.162.37

    Где:

    • --default-addr-pool — адрес overlay-сети, которая соединит контейнеры на разных машинах в одну виртуальную сеть. Без нее распределенные приложения в Swarm работать не будут. Адрес overlay-сети не должен совпадать с адресом подсети, к которой подключены виртуальные машины.

    • --advertise-addr — IP-адрес, который менеджер Swarm будет использовать для связи с другими узлами. Укажите здесь публичный IP-адрес основной машины. В этой лабораторной работе — 176.123.162.37.

    В ответе вернется сообщение, что текущая машина является менеджером кластера, и команда для добавления узлов в кластер:

    Swarm initialized: current node (zbjlb49a21tzg3ae0qthjsb7r) is now a manager.
    To add a worker to this swarm, run the following command:
    docker swarm join --token SWMTKN-1-example123 176.123.162.37:2377
    To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
  3. Скопируйте команду для добавления узлов.

  4. Для виртуальных машин docker-swarm-worker-1 и docker-swarm-worker-2 в терминале выполните скопированную команду под корневым пользователем:

    sudo docker swarm join --token SWMTKN-1-example123 176.123.162.37:2377

    В ответе вернется сообщение, что машина назначена worker-узлом в кластере.

Убедитесь, что в кластер добавлены все нужные узлы. Для этого перейдите в сессию терминала docker-swarm-manager-1 и выполните команду:

sudo docker node ls

В ответе вернется список узлов:

ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
2vl32ofyer2w7fmx6m5sjldjz * docker-swarm-manager-1 Ready Active Leader 28.3.2
35r3qrtgb7l4nq3n0ykughkdw docker-swarm-worker-1 Ready Active 28.3.2
cllbe9vic7tihon6qqjd9usz5 docker-swarm-worker-2 Ready Active 28.3.2

4. Создайте структуру каталогов и файлы проекта

  1. На виртуальной машине docker-swarm-manager-1 создайте новую директорию для проекта и перейдите в нее:

    mkdir swarm-app
    cd swarm-app
  2. Создайте директории для всех компонентов приложения и хранения файлов базы данных:

    mkdir backend frontend mysql_data mysql-init
  3. Убедитесь, что структура каталогов веб-приложения создана верно, выполнив команду ls.

  4. Перейдите в директорию backend и создайте файлы для приложения на Flask:

    cd backend
    touch app.py requirements.txt Dockerfile
    cd ..

    Где:

    • app.py — исходный код сервера backend;

    • requirements.txt — список зависимостей Python;

    • Dockerfile — инструкции для сборки образа backend-приложения.

  5. Перейдите в директорию frontend и создайте файлы для frontend-приложения:

    cd frontend
    touch default.conf Dockerfile
    cd ..

    Где:

    • default.conf — конфигурационный файл Nginx;

    • Dockerfile — инструкции для сборки образа frontend-приложения.

  6. Перейдите в директорию mysql-init и создайте файлы для frontend-приложения:

    cd mysql-init
    touch init.sql
    cd ..
  7. Создайте в корне проекта файл docker-swarm.yml, который будет описывать стек приложения:

    touch docker-swarm.yml

Проверьте итоговую структуру каталогов. На этом этапе она должна иметь следующий вид:

swarm-app/
├── backend/
│ ├── app.py
│ ├── requirements.txt
│ └── Dockerfile
├── frontend/
│ ├── default.conf
│ └── Dockerfile
├── mysql_data/ # directory for volume MySQL
├── mysql-init/
│ └── init.sql
└── docker-swarm.yml

5. Создайте backend-приложение

  1. Перейдите в директорию backend и откройте файл requirements.txt:

    cd backend
    nano requirements.txt
  2. Добавьте код для определения зависимости вашего приложения и сохраните изменения:

    flask
    flask-mysqldb
  3. Откройте файл backend/app.py:

    nano app.py
  4. Добавьте минимальный рабочий код Flask-приложения с подключением к MySQL:

    from flask import Flask, request, redirect, url_for, render_template_string
    from flask_mysqldb import MySQL
    app = Flask(__name__)
    # MySQL connection settings
    app.config['MYSQL_HOST'] = 'db'
    app.config['MYSQL_USER'] = 'user'
    app.config['MYSQL_PASSWORD'] = 'password'
    app.config['MYSQL_DB'] = 'appdb'
    # Initializing MySQL
    mysql = MySQL(app)
    # HTML-template with Bootstrap
    HTML_TEMPLATE = '''
    <!DOCTYPE html>
    <html lang="ru">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Notes in Docker Swarm</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body class="bg-light">
    <div class="container py-5">
    <h1 class="mb-4 text-center">📝 Notes in Swarm</h1>
    <form method="post" action="/" class="mb-4">
    <div class="input-group">
    <input type="text" name="note" class="form-control" placeholder="Enter a new note" required>
    <button class="btn btn-primary" type="submit">Add</button>
    </div>
    </form>
    <div class="card shadow">
    <div class="card-body">
    {% if notes %}
    <ul class="list-group">
    {% for id, content in notes %}
    <li class="list-group-item d-flex justify-content-between align-items-center">
    <span>{{ content }}</span>
    <span class="badge bg-secondary rounded-pill">#{{ id }}</span>
    </li>
    {% endfor %}
    </ul>
    {% else %}
    <p class="text-muted">There are no notes yet...</p>
    {% endif %}
    </div>
    </div>
    </div>
    </body>
    </html>
    '''
    @app.route('/', methods=['GET', 'POST'])
    def index():
    conn = mysql.connection
    cursor = conn.cursor()
    if request.method == 'POST':
    # читаем поле note из формы
    note = request.form.get('note')
    if note:
    cursor.execute("INSERT INTO entries (name) VALUES (%s)", (note,))
    conn.commit()
    return redirect(url_for('index'))
    # GET:
    cursor.execute("SELECT id, name FROM entries ORDER BY id")
    notes = cursor.fetchall()
    cursor.close()
    return render_template_string(HTML_TEMPLATE, notes=notes)
    if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

6. Создайте Dockerfile для backend-приложения

  1. Откройте Dockerfile в редакторе:

    nano Dockerfile
  2. Вставьте в Dockerfile следующий код:

    FROM python:3.9-slim
    # Installing dependencies for compiling mysqlclient
    RUN apt-get update && apt-get install -y \
    gcc \
    default-libmysqlclient-dev \
    pkg-config \
    && rm -rf /var/lib/apt/lists/*
    WORKDIR /app
    COPY requirements.txt requirements.txt
    RUN pip install --no-cache-dir -r requirements.txt
    COPY . .
    CMD ["python", "app.py"]
  3. Соберите образ и загрузите его в реестр:

    sudo docker build . -t <registry_name>.cr.cloud.ru/backend:1.0 --platform linux/amd64
    sudo docker push <registry_name>.cr.cloud.ru/backend:1.0

    Где:

    • <registry_name>.cr.cloud.ru — URI реестра, в котором находится репозиторий.

    • backend — название репозитория, соответствует названию загружаемого образа.

    • platform linux/amd64 — флаг указывает, что образ должен быть собран для платформы linux/amd64. Это требуется для создания контейнера.

    • 1.0 — тег образа.

7. Создайте frontend-приложение

  1. Перейдите в директорию frontend, создайте файл default.conf и откройте его:

    cd ../frontend
    nano default.conf
  2. Вставьте следующий код:

    server {
    listen 80;
    # Proxy all requests (GET, POST, etc.) to backend
    location / {
    proxy_pass http://backend:5000;
    proxy_http_version 1.1;
    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;
    }
    }
  3. Создайте файл Dockerfile:

    nano Dockerfile
  4. Вставьте следующий код:

    FROM nginx:1.27.5-alpine # Using nginx as a web server
    COPY default.conf /etc/nginx/conf.d/default.conf # Copy the HTML file to the standart nginx directory
  5. Соберите ваш образ и загрузите его в реестр:

    sudo docker build . -t <registry_name>.cr.cloud.ru/frontend:1.0 --platform linux/amd64
    sudo docker push <registry_name>.cr.cloud.ru/frontend:1.0

8. Настройте структуру базы данных

  1. Создайте файл mysql-init/init.sql и перейдите к его редактированию:

    cd ../mysql-init
    nano init.sql
  2. Вставьте код, который создает целевую таблицу:

    CREATE DATABASE IF NOT EXISTS appdb;
    USE appdb;
    CREATE TABLE IF NOT EXISTS entries (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL
    );
  3. Сохраните файл mysql-init/init.sql и вернитесь на уровень выше, выполнив команду cd ...

9. Создайте файл для запуска приложения в Docker Swarm

Файл должен содержать декларацию трех компонентов проекта в Docker Swarm:

  • frontend — перенаправление к приложению, 2 реплики, подключен к overlay-сети;

  • backend — Flask-приложение (API), 2 реплики, подключен к overlay-сети;

  • база данных — MySQL, 1 реплика, подключена к overlay-сети, хранение данных в томе mysql_data.

  1. Откройте файл в корне проекта:

    nano docker-swarm.yml
  2. Вставьте следующий код:

    services:
    db:
    image: mysql:8.0
    environment:
    MYSQL_ROOT_PASSWORD: rootpass
    MYSQL_DATABASE: appdb
    MYSQL_USER: user
    MYSQL_PASSWORD: password
    volumes:
    - db-data:/var/lib/mysql
    - ./mysql-init:/docker-entrypoint-initdb.d
    networks:
    - appnet
    deploy:
    placement:
    constraints: [node.role == manager]
    backend:
    image: <registry_name>.cr.cloud.ru/backend:1.0
    depends_on:
    - db
    environment:
    MYSQL_DATABASE_HOST: db
    MYSQL_DATABASE_USER: user
    MYSQL_DATABASE_PASSWORD: password
    MYSQL_DATABASE_NAME: appdb
    networks:
    - appnet
    deploy:
    replicas: 2
    restart_policy:
    condition: on-failure
    frontend:
    image: <registry_name>.cr.cloud.ru/frontend:1.0
    ports:
    - "8080:80"
    networks:
    - appnet
    depends_on:
    - backend
    deploy:
    replicas: 2
    restart_policy:
    condition: on-failure
    volumes:
    db-data:
    networks:
    appnet:
    driver: overlay

    Где:

    • <registry_name> — название реестра.

    • volumes: db-data — сохраняет базу между перезапусками.

    • deploy.replicas — создает отказоустойчивость фронтенда и бэкенда.

    • networks.overlay — дает сервисам доступ друг к другу по имени, например http://backend:5000.

    • placement.constraints — закрепляет базу только за менеджером, где том будет доступен локально.

    Это простое решение — более сложное потребует использования Galera или Vitess.

10. Разверните приложение в Swarm

  1. Разверните ваше приложение в Swarm, используя команду:

    sudo docker stack deploy -c docker-swarm.yml --with-registry-auth myapp
  2. Подождите несколько минут, пока загрузятся образы и запустится приложение.

  3. Проверьте статус с помощью команды:

    sudo docker service ls

Все контейнеры должны быть в статусе «replicated» и с полным количеством реплик.

11. Настройте балансировщик нагрузки

Отвяжите публичные адреса от виртуальных машин и добавьте балансировщик нагрузки, чтобы приложение было доступно при выходе из строя рабочего сервера:

  1. Отвяжите публичный IP-адрес от каждой из трех виртуальных машин.

  2. Создайте балансировщик нагрузки со следующими параметрами:

    • Зона доступности: та же, в которой расположены виртуальные машины.

    • VPC: та же, в которой расположены виртуальные машины.

    • Тип балансировщика: выберите внешний тип балансировщика.

    • Правило балансировки трафика:

      • Создайте новую backend-группу и добавьте в нее три созданные виртуальные машины.

      • Порт балансировщика: 80.

      • Порт backend группы: 8080.

      • Включите проверку доступности:

        • Порт: 8080.

        • Интервал: 10.

        • Таймаут: 5.

        • Порог успешных ответов: 3.

        • Порог неуспешных ответов: 3.

  3. Дождитесь, когда балансировщик нагрузки перейдет в статус «Активен».

Протестируйте отказоустойчивость и работоспособность приложения

  1. Проверьте работоспособность приложения при активности всех рабочих узлов:

    1. Откройте в браузере страницу с адресом публичного IP балансировщика — http://<load_balancer_public_IP>.

    2. Убедитесь, что:

      • Загружается веб-интерфейс.

      • Отображаются записи, если добавлялись.

    3. Добавьте новую запись — она должна сохраниться и отобразиться после обновления.

  2. Проверьте работу отказоустойчивости при выходе из строя одного из рабочих узлов:

    1. Выключите виртуальную машину docker-swarm-worker-2.

    2. Откройте в браузере страницу с адресом публичного IP балансировщика — http://<load_balancer_public_IP>.

    3. Убедитесь, что:

      • Загружается веб-интерфейс.

      • Отображаются записи, если добавлялись.

    4. Добавьте новую запись — она должна сохраниться и отобразиться после обновления.

Что дальше

В ходе лабораторной работы вы научились:

  • настраивать кластер Docker Swarm и объединять узлы;

  • разворачивать многокомпонентные приложения с помощью docker stack deploy;

  • использовать overlay-сети для взаимодействия сервисов;

  • конфигурировать работу Flask-приложения с внешней базой MySQL;

  • обеспечивать сохранность данных с помощью Docker Volumes;

  • проверять отказоустойчивость путем симуляции отказа узлов;

  • диагностировать состояние кластера и отдельных компонентов с помощью команд Docker.

Узнавайте больше о работе с сервисами и получайте практические навыки управления облаком, выполняя лабораторные работы.