Динамическая балансировка нагрузки и плавный выпуск сервисов через Nginx, Consul и Upsync

задняя часть

помещение

Некоторое время назад весь сервисный кластер и промежуточное ПО были успешно заменены сUCloudПосле перехода на Alibaba Cloud автор взял на себя роль архитектуры и половину эксплуатации и обслуживания. Вот подробная запись оNginx,Consul,UpsyncОсновные точки знаний и этапы работы для достижения динамической балансировки нагрузки и плавного выпуска услуг, вся система работает без сбоев в производственной среде. Система виртуальных машин, использованная при написании этой статьи,CentOS7.x, интрасеть виртуальной машиныIPза192.168.56.200.

Основные принципы динамической балансировки нагрузки

обычно проходятupstreamнастроитьNginxПул обратного прокси:

http {
    
    upstream upstream_server{
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

    server {
        listen       80;
        server_name localhost;

        location / {
            proxy_pass http://upstream_server;
        }
    }
}

Сейчас если8081Сервисный экземпляр порта завис и его нужно удалить, поэтому его нужно модифицироватьupstreamза:

upstream upstream_server{
    # 添加down标记该端口的服务实例不参与负载
    server 127.0.0.1:8081 down;
    server 127.0.0.1:8082;
}

и черезnginx -s reloadПерезагрузите конфигурацию,upstreamконфигурация вступит в силу. Мы знаем, что когда служба выпущена, она находится в недоступном состоянии во время процесса перезапуска.Правильный процесс выпуска службы должен быть:

  • удалить услугу из соответствующегоupstreamудалить, обычно устанавливается наdown,сообщитьNginxСлужитьupstreamИзменения конфигурации, должны пройтиnginx -s reloadперезагрузить.
  • Сборка службы, развертывание и перезапуск.
  • Благодаря обнаружению активного сценария можно получить доступ к соответствующему порту службы, а к службе можно получить доступ с соответствующего порта.upstreamподтягиваться посередине, как правилоdownудалить, сообщитьNginxСлужитьupstreamИзменения конфигурации, нужно пройтиnginx -s reloadперезагрузить.

Первый шаг выше включаетupstreamконфигурация, вторая требуетNginxПерезагрузить конфигурацию (nginx -s reload), кажется громоздким, перезагружается при высокой нагрузкеNginxА перезагрузка конфигурации еще больше увеличивает нагрузку на систему и может временно снизить производительность.

Поэтому вы можете рассмотреть возможность использования распределенного кэша дляupstreamКонфигурация сохраняется в сервисе кеша, а затемNginxчитать непосредственно из этой службы кешаupstreamконфигурации, так что если естьupstreamИзменения конфигурации службы кеша могут напрямую изменять соответствующие свойства службы кеша иNginxсервис не нуженreload. В реальном бою выбирается упомянутая здесь служба кэширования.Consul,NginxСвойства конфигурации в кеше чтения предоставляются Sina Weibo.NginxизCязыковой модульnginx-upsync-module. Принципиальная схема примерно такая:

Консул установка и строительство кластера

ConsulдаHashicorpкомпания используетGolangРазработанный проект с открытым исходным кодом, это инструмент для обнаружения и настройки сервисов, который является распределенным и высокодоступным, а также чрезвычайно масштабируемым.ConsulВ основном обеспечивают следующие функции:

  • Обнаружение службы.
  • Проверка здоровья.
  • сервисное разбиение/сервисная сетка (Service Segmentation/Service Mesh).
  • Ключ/значение сохраняется.
  • Несколько дата-центров.

Вот процесс установки:

mkdir /data/consul
cd /data/consul
wget https://releases.hashicorp.com/consul/1.7.3/consul_1.7.3_linux_amd64.zip
# 注意解压后只有一个consul执行文件
unzip consul_1.7.3_linux_amd64.zip

После завершения распаковки используйте командуnohup /data/consul/consul agent -server -data-dir=/tmp/consul -bootstrap -ui -advertise=192.168.56.200 -client=192.168.56.200 > /dev/null 2>&1 &Может запустить одну машину в фоновом режимеConsulСлужить. запускатьConsulПосле экземпляра посетитеhttp://192.168.56.200:8500/Откройте его неслышимое управлениеUI:

Построим псевдокластер на базе одной виртуальной машины,Значение некоторых конфигурационных свойств кластера и пояснение параметров команд пока не будет расширено..

# 创建集群数据目录
mkdir /data/consul/node1 /data/consul/node2 /data/consul/node3
# 创建集群日志目录
mkdir /data/consul/node1/logs /data/consul/node2/logs /data/consul/node3/logs

существует/data/consul/node1добавление каталогаconsul_conf.jsonфайл со следующим содержимым:

{
  "datacenter": "es8-dc",
  "data_dir": "/data/consul/node1",
  "log_file": "/data/consul/node1/consul.log",
  "log_level": "INFO",
  "server": true,
  "node_name": "node1",
  "ui": true,
  "bind_addr": "192.168.56.200",
  "client_addr": "192.168.56.200",
  "advertise_addr": "192.168.56.200",
  "bootstrap_expect": 3,
  "ports":{
    "http": 8510,
    "dns": 8610,
    "server": 8310,
    "serf_lan": 8311,
    "serf_wan": 8312
    }
}

существует/data/consul/node2добавление каталогаconsul_conf.jsonфайл со следующим содержимым:

{
  "datacenter": "es8-dc",
  "data_dir": "/data/consul/node2",
  "log_file": "/data/consul/node2/consul.log",
  "log_level": "INFO",
  "server": true,
  "node_name": "node2",
  "ui": true,
  "bind_addr": "192.168.56.200",
  "client_addr": "192.168.56.200",
  "advertise_addr": "192.168.56.200",
  "bootstrap_expect": 3,
  "ports":{
    "http": 8520,
    "dns": 8620,
    "server": 8320,
    "serf_lan": 8321,
    "serf_wan": 8322
    }
}

существует/data/consul/node3добавление каталогаconsul_conf.jsonфайл со следующим содержимым:

{
  "datacenter": "es8-dc",
  "data_dir": "/data/consul/node3",
  "log_file": "/data/consul/node3/consul.log",
  "log_level": "INFO",
  "server": true,
  "node_name": "node3",
  "ui": true,
  "bind_addr": "192.168.56.200",
  "client_addr": "192.168.56.200",
  "advertise_addr": "192.168.56.200",
  "bootstrap_expect": 3,
  "ports":{
    "http": 8530,
    "dns": 8630,
    "server": 8330,
    "serf_lan": 8331,
    "serf_wan": 8332
    }
}

Создайте новый скрипт запуска кластера:

cd /data/consul
touch service.sh
# /data/consul/service.sh内容如下:
nohup /data/consul/consul agent -config-file=/data/consul/node1/consul_conf.json > /dev/null 2>&1 &
sleep 10
nohup /data/consul/consul agent -config-file=/data/consul/node2/consul_conf.json -retry-join=192.168.56.200:8311 > /dev/null 2>&1 &
sleep 10
nohup /data/consul/consul agent -config-file=/data/consul/node3/consul_conf.json -retry-join=192.168.56.200:8311 > /dev/null 2>&1 &

Если кластер запускается успешно, просмотрите журналы в узле 1 следующим образом:

через узел 1HTTPСтраница фонового управления доступом к конечной точке выглядит следующим образом (видно, что текущий узел 1 отмечен красной звездочкой, что указывает на то, что текущий узел 1Leaderузел):

Уже,ConsulАвтономный псевдокластер завершен (по сути, построение распределенного кластера аналогично, обратите внимание, что машине, где находится узел кластера, необходимо открыть права доступа используемого порта), т.к.ConsulиспользоватьRaftВ качестве алгоритма консенсуса используется алгоритмСильная модель лидера, которая толькоLeaderУзел может записать, поэтому следующие операции должны использовать узел 1HTTPконечная точка, то есть192.168.56.200:8510.

Ключевое примечание: еслиConsulПерезапуск или переизбрание кластера,LeaderУзлы могут измениться, рекомендуется использовать внешниеLeaderузлаHTTPКонечные точки извлекаются в динамически обновляемые элементы конфигурации или получаются динамически.LeaderузлаIPи порт.

Компиляция и установка Nginx

Загрузите бинарный установочный пакет прямо с официального сайта и разархивируйте его:

mkdir /data/nginx
cd /data/nginx
wget http://nginx.org/download/nginx-1.18.0.tar.gz
tar -zxvf nginx-1.18.0.tar.gz

Все исходные файлы после распаковки находятся в/data/nginx/nginx-1.18.0каталог, вам необходимо установить его перед компиляциейpcre-devel,zlib-develполагаться:

yum -y install pcre-devel
yum install -y zlib-devel

Команда компиляции выглядит следующим образом:

cd /data/nginx/nginx-1.18.0
./configure --prefix=/data/nginx

если./configureВ процессе выполнения проблем нет, тогда результат следующий:

Затем выполнитеmake:

cd /data/nginx/nginx-1.18.0
make

еслиmakeВ процессе выполнения проблем нет, тогда результат следующий:

Наконец, если это первая установка, вы можете выполнитьmake installУстановите (на самом деле просто скопируйте скомпилированные файлы в--prefixуказанный путь):

cd /data/nginx/nginx-1.18.0
make install

make installПосле казни,/data/nginxВ каталог добавлено несколько новых папок:

в,Nginxзапустить программу вsbinПод содержанием,logsэто его каталог журнала,confэто каталог, в котором находится его файл конфигурации. попробуй начатьNginx:

/data/nginx/sbin/nginx

затем получить доступ к виртуальной машине80порт, тем самым проверяяNginxзапустился нормально:

Компиляция с модулями nginx-upsync-module и nginx_upstream_check_module

сделал один вышеNginxМинимальный процесс компиляции, по сути, необходимо добавить при выполнении динамической балансировки нагрузки.nginx-upsync-moduleиnginx_upstream_check_moduleДва модуля, два модуля должны заранее загрузить исходный код и скомпилироватьNginxВам нужно указать физический путь во время двух модулей:

mkdir /data/nginx/modules
cd /data/nginx/modules
# 这里是Github的资源,不能用wget下载,具体是:
nginx-upsync-module需要下载release里面的最新版本:v2.1.2
nginx_upstream_check_module需要下载整个项目的源码,主要用到靠近当前版本的补丁,使用patch命令进行补丁升级

После завершения загрузки (распаковки) отдельно/data/nginx/modulesПод содержанием:

ll /data/nginx/modules
drwxr-xr-x. 6 root root   4096 Nov  3  2019 nginx_upstream_check_module-master
drwxrwxr-x. 5 root root     93 Dec 18 00:56 nginx-upsync-module-2.1.2

Перед компиляцией установите некоторые предварительные зависимости:

yum -y install libpcre3 libpcre3-dev ruby zlib1g-dev patch

Далее приступаем к компиляции и установкеNginx:

cd /data/nginx/nginx-1.18.0
patch -p1 < /data/nginx/modules/nginx_upstream_check_module-master/check_1.16.1+.patch
./configure --prefix=/data/nginx --add-module=/data/nginx/modules/nginx_upstream_check_module-master --add-module=/data/nginx/modules/nginx-upsync-module-2.1.2
make
make install

Как бы ни был настроен описанный выше процесс компиляции и установки, некоторые зависимости будут отсутствовать.makeНенормально, предполагается, что эти два модуля не поддерживают слишком старую версию.Nginx. (В производстве использовалась версия с более низкой версиейOpenResty, здесь я хочу восстановить, чтобы использовать относительно новую версиюNginxПроцесс наступления на яму) поэтому попробуй даунгрейд скомпилить, далее ссылка на несколькоIssueПосле получения относительно недавней комбинации доступных версий:

# 提前把/data/nginx下除了之前下载过的modules目录外的所有文件删除
cd /data/nginx
wget http://nginx.org/download/nginx-1.14.2.tar.gz
tar -zxvf nginx-1.14.2.tar.gz

Начинаем компилировать и устанавливать:

cd /data/nginx/nginx-1.14.2
patch -p1 < /data/nginx/modules/nginx_upstream_check_module-master/check_1.12.1+.patch
./configure --prefix=/data/nginx --add-module=/data/nginx/modules/nginx_upstream_check_module-master --add-module=/data/nginx/modules/nginx-upsync-module-2.1.2
make && make install

После завершения установки/data/nginx/sbin/nginxКоманду можно запускать.

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

Сначала напишите простуюHTTPобслуживание, потому чтоJavaОтносительно тяжелый, выберите здесьGolang, код показан ниже:

package main

import (
	"flag"
	"fmt"
	"net/http"
)

func main() {
    var host string
    var port int
    flag.StringVar(&host, "h", "127.0.0.1", "IP地址")
    flag.IntVar(&port, "p", 9000, "端口")
    flag.Parse()
    address := fmt.Sprintf("%s:%d", host, port)
    http.HandleFunc("/ping", func(writer http.ResponseWriter, request *http.Request) {
        _, _ = fmt.Fprintln(writer, fmt.Sprintf("%s by %s", "pong", address))
    })
    http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
        _, _ = fmt.Fprintln(writer, fmt.Sprintf("%s by %s", "hello world", address))
    })
    err := http.ListenAndServe(address, nil)
    if nil != err {
        panic(err)
    }
}

Скомпилировать:

cd src
set GOARCH=amd64
set GOOS=linux
go build -o ../bin/app app.go

Вот так в проектеbinкаталог, чтобы получитьLinuxисполняемые двоичные файлы подappсоответственно в порту9000и9001Запустите два экземпляра службы:

# 记得先给app文件的执行权限chmod 773 app
nohup ./app -p 9000 >/dev/null 2>&1 &
nohup ./app -p 9001 >/dev/null 2>&1 &

немного отредактироватьNginxКонфигурация, Добавитьupstream:

# /data/nginx/conf/nginx.conf部分片段
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    upstream app {
       # 这里是consul的leader节点的HTTP端点
       upsync 192.168.56.200:8510/v1/kv/upstreams/app/ upsync_timeout=6m upsync_interval=500ms upsync_type=consul strong_dependency=off;
       # consul访问不了的时候的备用配置
       upsync_dump_path /data/nginx/app.conf;
       # 这里是为了兼容Nginx的语法检查
       include /data/nginx/app.conf;
       # 下面三个配置是健康检查的配置
       check interval=1000 rise=2 fall=2 timeout=3000 type=http default_down=false;
       check_http_send "HEAD / HTTP/1.0\r\n\r\n";
       check_http_expect_alive http_2xx http_3xx;
    }

    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://app;
        }
        # 健康检查 - 查看负载均衡的列表
        location /upstream_list {
            upstream_show;
        }
        # 健康检查 - 查看负载均衡的状态
        location /upstream_status {
            check_status;
            access_log off;
        }
    }
}

# /data/nginx/app.conf
server 127.0.0.1:9000 weight=1 fail_timeout=10 max_fails=3;
server 127.0.0.1:9001 weight=1 fail_timeout=10 max_fails=3;

Добавить два вручнуюHTTPслужба вConsulсередина:

curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10}' http://192.168.56.200:8510/v1/kv/upstreams/app/127.0.0.1:9000
curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10}' http://192.168.56.200:8510/v1/kv/upstreams/app/127.0.0.1:9001

последняя перезагрузкаNginxможно настроить.

Тест динамической балансировки нагрузки

Предварительная работа готова, теперь попробуйте динамическую балансировку нагрузки, начните сConsulне в сети9000Экземпляр службы порта:

curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10, "down":1}' http://192.168.56.200:8510/v1/kv/upstreams/app/127.0.0.1:9000

В списке видимых балансировщиков нагрузки9000Экземпляр службы для порта был установлен наdown, на этот раз сумасшедшая просьбаhttp://192.168.56.200, только выводhello world by 127.0.0.1:9001,видимый9000Экземпляр службы порта больше не участвует в загрузке. снова онлайн9000Экземпляр службы порта:

curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10, "down":0}' http://192.168.56.200:8510/v1/kv/upstreams/app/127.0.0.1:9000

Сумасшедший запрос сноваhttp://192.168.56.200,Находитьhello world by 127.0.0.1:9000иhello world by 127.0.0.1:9001Альтернативный выход. На этом этапе можно убедиться, что динамическая балансировка нагрузки прошла успешно. В это время снова проверьте мониторинг работоспособности службы и передайтеkill -9Случайно убейте один из экземпляров службы и наблюдайте/upstream_statusКонечная точка:

безумный запросhttp://192.168.56.200, только выводhello world by 127.0.0.1:9001,видимый9000Экземпляр услуг порта больше не участвует в нагрузке, но см.Consulсередина9000Порты настроены для экземпляров службы, которые не помечены какdown, видно чтоnginx_upstream_check_moduleОтфильтруйте нам аномальные узлы, чтобы эти узлы больше не участвовали в нагрузке.

В общем, эта относительно полная функция динамической балансировки нагрузки требуетnginx_upstream_check_moduleиnginx-upsync-moduleРаботать вместе, чтобы завершить.

Плавный сервисный выпуск

Плавный выпуск сервисов зависит от функции динамической балансировки нагрузки, анализу которой мы уделили много времени ранее. Команда авторов относительно невелика, поэтому я выбрал облачный эффект Alibaba Cloud в качестве платформы управления производством и исследованиями и реализовал плавный выпуск услуг через функцию конвейера внутри.Ниже приведен конвейер развертывания одной из производственных сред службы:

На самом деле плавная публикация не имеет ничего общего с платформой, общие шаги таковы:

Есть много шагов и включает в себя многоshellСценарий, подробное содержание сценария здесь не приводится, но кратко перечисляется действие каждого шага (обратите внимание, что некоторые разумные шаги могут быть вставлены междуsleep nУбедитесь, что предыдущий шаг выполнен):

  • Сканирование кода, модульное тестирование и многое другое.
  • Код собран, а сжатый пакет после сборки сгенерирован.
  • Загрузите сжатый пакет на серверX, распакуйте в соответствующий каталог.
  • В направленииConsulОтправить инструкцию, чтобы поставить текущую опубликованнуюX_IP:PORTКонфигурация загрузки обновлена ​​доdown=1.
  • stopСлужитьX_IP:PORT.
  • startСлужитьX_IP:PORT.
  • Проверить сервисX_IP:PORT(Вы можете установить период времени, например, проверять каждые 10 секунд в течение 120 секунд), если запуск не удается, он будет прерван и возвращен непосредственно, чтобы убедиться, что в загрузке участвует еще один нормальный старый узел, и требуется ручное вмешательство. .
  • В направленииConsulОтправить инструкцию, чтобы поставить текущую опубликованнуюX_IP:PORTКонфигурация загрузки обновлена ​​доdown=0.

Вышеупомянутый процесс проходит черезhard codeГотово, для разных серверов просто добавьте узел процесса публикации и изменитеIPЗаполнителя достаточно, не нужноNginxВыполните перезагрузку конфигурации. Платформа, на которой находится автор, не имеет большого трафика.В настоящее время развертывание двух узлов для каждого сервиса может удовлетворить потребности производства.Представьте, если вы хотите добиться динамического расширения, как вы должны построить конвейер?

резюме

Сервис Smooth Release ДаCI/CDОдно из наиболее важных звеньев в сервисе, и динамическая балансировка нагрузки является основой для бесперебойного выпуска сервисов. Хотя многие облачные платформы в настоящее время предоставляют очень удобные инструменты непрерывной интеграции, при использовании этих инструментов и процессов настройки лучше всего понимать основные принципы, лежащие в их основе, чтобы, когда инструменты неприменимы или возникают проблемы, быстро принимать решения и реагировать.

Использованная литература:

(Конец этой статьи c-7-d e-a-20200613 Спасибо за поддержку, предоставленную Хао Гэ, руководителем эксплуатации и обслуживания финансовой технологической компании в Гуанчжоу)

Технический публичный аккаунт ("Throwable Digest"), который время от времени выкладывает оригинальные технические статьи автора (никогда не занимайтесь плагиатом и не перепечатывайте):