A/B тестирование через Nginx на экземпляре EC2

Вопросы и ответы

В настоящее время я работаю над приложением на Python, которое разрабатываю на AWS EC2.
Это приложение предоставляет API, который имеет 2 конечные точки:

  • test/ -> возвращает status_code=200 & сообщение “test”
  • test1/ -> возвращает status_code=200 & сообщение “test1”

Как только оно будет работать и запущено на порту 8000, я написал Dockerfile и файлы docker-compose для развертывания его через docker.
Вот пример моего API docker сервиса:

version: '3.9'
services:
  api:
    container_name: api 
    build:
      context: ..
      dockerfile: ./deploy/Dockerfile.api
    image: api-image-name
    ports:
      - "8000:8000"

На данный момент всё работает, я могу запрашивать мой API после развертывания, используя GET-запрос, например:

curl -X GET http://0.0.0.0:8000/test

Обратите внимание, что мой Dockerfile содержит следующие строки, но, вероятно, здесь есть ошибка:

ENV no_proxy="localhost,127.0.0.1"
ENV http_proxy="http://127.0.0.1:80"

Теперь я хочу поставить NGINX, чтобы он работал как балансировщик нагрузки, направляя 50% запросов на test1. Я хочу сделать AB тестирование

Для этого я разворачиваю docker-сервис под названием nginx, который зависит от api:

nginx:
    image: nginx:alpine
    container_name: ab_testing
    volumes:
      - ../nginx.conf:/etc/nginx/nginx.conf
    environment:
      - TZ=Europe/Paris
      - ENV=dev
    depends_on:
      - api

На данный момент всё работает правильно, я могу развернуть оба сервиса, и API все еще работает на http://0.0.0.0:8000/test или http://0.0.0.0:8000/test1

Вы можете заметить, что я монтирую том под названием nginx.conf, который задает конфигурацию. Вот текущее содержимое этого файла:

events {
}
http {
    server {
        listen 81;
        location / {
            proxy_pass http://0.0.0.0:8000/test;
        }
    }

    server {
        listen 82;
        location / {
            proxy_pass http://0.0.0.0:8000/test1;
        }
    }

    upstream app {
        server 127.0.0.1:81 weight=50;
        server 127.0.0.1:82 weight=50;
    }

    server {
        listen 80;
        location / {
            proxy_pass http://app/;
        }
    }
}

В этой конфигурации nginx вы можете заметить, что я создаю 2 сервера на портах 81 и 82. Они будут использоваться для перенаправления запросов на мой API.

Затем я создаю балансировщик нагрузки, который использует мои 2 сервера и распределяет нагрузку. Балансировщик нагрузки обрабатывает нагрузку, разделяя её на 50%.

Наконец, я настраиваю свой основной сервер, слушающий на порту:80.

К сожалению, когда я запрашиваю http://0.0.0.0:80/, это не распределяет нагрузку, а выдает ошибку:

Не удалось подключиться к 127.0.0.1, порт 81, после 0 мс: Не удалось подключиться к серверу

Я хотел бы это исправить.

Более того, эта система работает на ECS, и я не знаю, как заставить её работать должным образом.
Можете помочь мне? 🙂

С уважением,

Ответ

Конечно! Давайте разберёмся с вашей задачей по установке NGINX в качестве балансировщика нагрузки для вашего Python приложения, развернутого с использованием Docker на EC2.

Ошибка в конфигурации NGINX

Во-первых, проблема, с которой вы сталкиваетесь (Failed to connect to 127.0.0.1 port 81), возникает из-за того, что NGINX, работающий в контейнере, не может обращаться к localhost контейнера. В Docker, localhost внутри контейнера (127.0.0.1) ссылается на сам контейнер, а не на хост-машину или другие контейнеры. Вам нужно использовать api (имя вашего сервиса), чтобы выполнить проксирование, поскольку это имя вашего контейнера API.

Исправление конфигурации NGINX

Вот исправленная версия вашего nginx.conf:

events {
}

http {
    upstream test1 {
        server api:8000;  # Ваш API-сервис
    }

    upstream test {
        server api:8000;  # Ваш API-сервис
    }

    server {
        listen 80;
        location / {
            # Осуществляем случайный выбор между test и test1
            set $backend "";

            # Используем $request_uri для переключения между test и test1
            if ($request_uri ~ "^/test") {
                set $backend test;
            }

            if ($request_uri ~ "^/test1") {
                set $backend test1;
            }

            # Если не определен $backend, проксируем на test с помощью случайного выбора
            if ($backend = "") {
                set $backend $request_uri;
                # Если переменная пуста, выбираем наугад
                if ($request_uri ~ "/") {
                    if ($random < 0.5) {
                        rewrite / /test break;
                    } else {
                        rewrite / /test1 break;
                    }
                }
            }

            proxy_pass http://$backend;
        }
    }
}

Обратите внимание на следующее:

  1. upstream: Мы определяем два upstream блока, один для каждого из ваших тестовых маршрутов. Они оба ссылаются на ваш API-сервис по адресу api:8000.

  2. Проверка URI: При получении запросов к порту 80, NGINX проверяет, запрашивается ли /test или /test1, и отправляет запрос на соответствующий конец.

  3. Балансировка нагрузки: Если запрос идет на корень сервера (/), NGINX будет случайным образом перенаправлять запросы на /test или /test1.

Проверьте ваши файлы Docker и Docker Compose

Убедитесь, что ваши файлы docker-compose.yml и Dockerfile.api правильно настроены и все контейнеры могут быть связаны. Вот пример вашего файла Docker Compose с соответствующими изменениями:

version: '3.9'
services:
  api:
    container_name: api 
    build:
      context: ..
      dockerfile: ./deploy/Dockerfile.api
    image: api-image-name
    expose:
      - "8000"  # Используем expose, а не ports, если не нужна прямой доступ извне

  nginx:
    image: nginx:alpine
    container_name: ab_testing
    volumes:
      - ../nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - api
    ports:
      - "80:80"  # Прокси на порт 80

Развертывание на ECS

Когда вы будете развертывать подобную архитектуру на AWS ECS, вам нужно будет настроить службы и задачи с учетом параметров, похожих на то, что мы сделали выше. Вы также можете использовать Application Load Balancer (ALB) для обеспечения балансировки нагрузки между различными задачами.

Примеры тестирования

Когда вы завершите настройки, вы сможете протестировать вашу API:

curl -X GET http://<EC2 IP>:80/

Это должно случайным образом выводить "test" или "test1".

Заключение

Эти изменения должны помочь вам справиться с проблемами связи между контейнерами, и настроить NGINX для корректной балансировки нагрузки между вашими тестовыми API. Если у вас возникнут дополнительные вопросы, не стесняйтесь их задавать!

Оцените материал
Добавить комментарий

Капча загружается...