Как настроить Nginx upstream для использования резервного варианта, когда основной хост не найден

Вопрос или проблема

В данный момент я мигрирую внутреннее приложение с docker на podman и мне приходится немного “умнеть”.

Каждый пользователь, обращающийся к приложению, должен иметь настроенные upstream и location, И контейнер podman должен быть запущен. Из-за природы приложения мы не можем гарантировать, что контейнер podman для пользователя будет действительно запущен в любой момент, хотя у нас будут определения для nginx location и upstream.

Пример директивы upstream выглядит следующим образом:

upstream goober_one_area {
    server goober_one_container:8080;
}

А location выглядит так:

location /goober_one {
        rewrite /goober_one/(.*) /$1 break;
        proxy_pass http://goober_one_area;

        proxy_intercept_errors on;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_next_upstream_tries 3;
        proxy_next_upstream_timeout 10s;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        #proxy_set_header X-Forwarded-Proto $scheme;

        # Поддержка WebSocket
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Accept-Encoding gzip;

        proxy_redirect off;
}

Когда goober_one_container не запущен, я, конечно, получаю [emerg] 1#1: host not found in upstream "goober_one_container:8080" in /etc/nginx/conf.d/environments/goober_one.upstream.conf:2. Это имеет смысл.

Чтобы исправить это, идея заключалась в том, чтобы создать приложение flask с одним представлением, которое будет использоваться как резервное, так что, если хост не может быть найден в upstream, оно просто перейдет к этому приложению flask, которое предоставит информацию о ситуации. Я настроил это в своем файле docker-compose.yml, который я запускаю через podman compose, и эта часть работает нормально, но изменение определения upstream на:

upstream goober_one_area {
    server goober_one_container:8080;
    server fallback:8080 backup;
}

не приносит результатов. Несмотря на то, что fallback:8080 запущен, доступен и служит предпосылкой для запуска nginx, он просто обращается к goober_one_container:8080 и прекращает выполнение.

Как мне заставить это перейти к следующему серверу, когда основной контейнер недоступен?

Ответ или решение

Чтобы обеспечить надежность работы вашего приложения с использованием Nginx и Podman, необходимо правильно настроить обработку запросов к upstream-серверам, особенно когда основной сервер недоступен. Давайте разберем, как это сделать.

Проблема

Согласно вашему описанию, Nginx не может разрешить имя хоста goober_one_container:8080, если контейнер не запущен, что приводит к ошибке. При этом, у вас уже есть доступный резервный сервер fallback:8080, который вы хотели бы использовать в случае недоступности основного сервера.

Решение

Для того чтобы настроить Nginx на использование резервного сервера, необходимо внести изменения в конфигурацию. Однако, важно понимать, что резервный сервер, обозначенный как backup, будет использован только в том случае, если все другие серверы в upstream не доступны из-за ошибки, а не из-за того, что Nginx не может их разрешить.

Когда Nginx пытается разрешить имя хоста goober_one_container, если контейнер не работает, это приводит к ошибке, и Nginx не сможет даже начать.

Шаги по исправлению

  1. Замена имени хоста: Убедитесь, что неверные конфигурации, такие как имя контейнера, действительно разрешаются до запуска Nginx. Вы можете использовать IP-адрес контейнера или локальный адрес, если он доступен.

  2. Настройка конфигурации upstream:
    Замена вашего upstream блока на следующее:

    upstream goober_one_area {
       server goober_one_container:8080;
       server fallback:8080 backup;
    }
  3. Обработка исключений:
    Ваша конфигурация location должна быть дополнена следующими директивами:

    location /goober_one {
       rewrite /goober_one/(.*) /$1 break;
       proxy_pass http://goober_one_area;
    
       proxy_intercept_errors on;
       proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
       proxy_next_upstream_tries 3;
       proxy_next_upstream_timeout 10s;
    
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header Host $http_host;
       proxy_set_header X-NginX-Proxy true;
    
       # Поддержка WebSocket
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       proxy_set_header Accept-Encoding gzip;
    
       proxy_redirect off;
    
       error_page 502 = @fallback;
    }
    
    location @fallback {
       proxy_pass http://fallback:8080;
    }

Пояснение

  • Ошибка 502: Если goober_one_container недоступен, Nginx будет возвращать ошибку 502. С директивой error_page выставляем страницу замены, которая указывает на резервный сервер fallback.

  • Обработка запросов: Это настроит Nginx так, что в случае ошибки 502 запрос автоматически перенаправляется на резервный сервер.

Заключение

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

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

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

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