Мы пытаемся создать кластер HA Kubernetese с 3 основными узлами, каждый из которых имеет полный набор жизненно важных компонентов: ETCD + APIServer + Scheduller + ControllerManager и внешний балансировщик. Поскольку ETCD может создавать кластеры самостоятельно, мы работаем над созданием HA APIServers. То, что пару недель назад казалось очевидной задачей, теперь превратилось в «не беду» ...
Мы решили использовать nginx в качестве балансировщика для 3 независимых APIServers. Предполагается, что все остальные части нашего кластера, которые взаимодействуют с APIServer (Kublets, Kube-Proxys, Schedulers, ControllerManager ...), используют балансировщик для доступа к нему. Все прошло хорошо, прежде чем мы начали «деструктивные» тесты (как я это называю) с несколькими запущенными стручками.
Вот часть конфигурации APIServer, которая набирает с HS:
.. --apiserver-count=3 --endpoint-reconciler-type=lease ..
Вот наш nginx.conf:
user nginx;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
worker_processes auto;
events {
multi_accept on;
use epoll;
worker_connections 4096;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
gzip on;
underscores_in_headers on;
include /etc/nginx/conf.d/*.conf;
}
И apiservers.conf:
upstream apiserver_https {
least_conn;
server core1.sbcloud:6443; # max_fails=3 fail_timeout=3s;
server core2.sbcloud:6443; # max_fails=3 fail_timeout=3s;
server core3.sbcloud:6443; # max_fails=3 fail_timeout=3s;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 6443 ssl so_keepalive=1m:10s:3; # http2;
ssl_certificate "/etc/nginx/certs/server.crt";
ssl_certificate_key "/etc/nginx/certs/server.key";
expires -1;
proxy_cache off;
proxy_buffering off;
proxy_http_version 1.1;
proxy_connect_timeout 3s;
proxy_next_upstream error timeout invalid_header http_502; # non_idempotent # http_500 http_503 http_504;
#proxy_next_upstream_tries 3;
#proxy_next_upstream_timeout 3s;
proxy_send_timeout 30m;
proxy_read_timeout 30m;
reset_timedout_connection on;
location / {
proxy_pass https://apiserver_https;
add_header Cache-Control "no-cache";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header Authorization $http_authorization;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-SSL-CLIENT-CERT $ssl_client_cert;
}
}
После некоторых тестов выяснилось, что Kubernetes, похоже, использует одиночное долгоживущее соединение вместо традиционных сеансов открытого закрытия. Это, вероятно, роса для SSL. Таким образом, мы должны увеличить proxy_send_timeout и proxy_read_timeout до смешных 30 м (значение по умолчанию для APIServer - 1800 с). Если этот параметр меньше 10 м, то все клиенты (такие как Scheduler и ControllerManager) будут генерировать тонны, если INTERNAL_ERROR из-за прерванных потоков.
Итак, для краш-теста я просто положил один из серверов APIServer, аккуратно выключив его. Затем я перезагружаю другой, чтобы nginx увидел, что восходящий поток отключился, и переключил все текущие соединения на последнее. Через пару секунд перезапущенный APIserver возвращается обратно, и у нас работают 2 APIServer. Затем я отключил сеть на третьем сервере APIServer, запустив на этом сервере 'systemctl stop network', чтобы у него не было возможности сообщить Kubernetes или nginx, что он отключается.
Теперь кластер полностью разбит! Кажется, что nginx распознает, что восходящий поток вышел из строя, но он не сбросит и без того захватывающие соединения с мертвым восходящим потоком. Я все еще могу видеть их с помощью 'ss -tnp'. Если я перезапущу службы Kubernetes, они снова подключатся и продолжат работать, то же самое, если я перезапущу nginx - новые сокеты будут отображаться в выводе ss.
Это происходит, только если я делаю APIserver недоступным, отключая сеть (не позволяя ему закрыть существующие соединения с nginx и сообщая Kubernetes, что он отключается). Если я просто остановлю это - все работает как шарм. Но это не настоящий случай. Сервер может отключиться без предупреждения - просто мгновенно.
Что мы делаем не так? Есть ли способ заставить nginx разорвать все соединения с вышестоящим апстримом? Что-нибудь, что можно попробовать, прежде чем мы перейдем на HAProxy или LVS и разрушим неделю, чтобы пнуть nginx в наших попытках сбалансировать его, вместо того, чтобы сломать наш не очень HA-кластер.