Оптимизация SSL-терминации HAProxy (с бэкэндом Nginx в Ubuntu)

Как я могу оптимизировать HAProxy с SSL-терминацией для бэкэндов Nginx в Ubuntu?

Настройка работает нормально и маршруты правильно. Однако, когда я выполняю SSL-завершение с помощью HAProxy, это приводит к огромному снижению производительности (тесты приведены ниже). Ключ 4096-битный RSA. HAProxy заставляет HTTPS для проверки, затем прекращает работу SSL и передает HTTP для внутренних серверов Nginx. Серверы Nginx идентичны и обслуживают несколько статических страниц, например, 192.168.1.xx/page1.html, 192.168.1.xx/page2.html и т. Д. (Я включил NodeJS для полноты своей системы, но добавляю задержку только <1 мс. NodeJS можно игнорировать.)

Вот настройки, конфиги и текущие тесты. Каждая виртуальная машина (ВМ) работает под управлением Ubuntu 14.04 и может иметь различное количество процессоров и оперативной памяти.

  • HAProxy (1.5.14): 192.168.1.10
  • Nginx-1: 192.168.1.20
  • Nginx-2: 192.168.1.21
  • Nginx-3: 192.168.1.22
  • Узел JS-1: 192.168.1.30

Вот конфигурация HAProxy:

    global
            maxconn 40000
            tune.ssl.default-dh-param 2048
            log /dev/log    local0
            log /dev/log    local1 notice
            chroot /var/lib/haproxy
            stats socket /run/haproxy/admin.sock mode 660 level admin
            stats timeout 30s
            user haproxy
            group haproxy

            # Default SSL material locations
            ca-base /etc/ssl/certs
            crt-base /etc/ssl/private

            # Default ciphers to use on SSL-enabled listening sockets.
            # For more information, see ciphers(1SSL). This list is from:
            #  https://hynek.me/articles/harding-your-web-servers-ssl-ciphers/
            ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
            ssl-default-bind-options no-sslv3


    defaults
            option forwardfor
            option http-server-close
            stats enable
            stats uri /stats
            stats realm Haproxy\ Statistics
            stats auth user:password
            log     global
            mode    http
            option  httplog
            option  dontlognull
            timeout connect 5000
            timeout client 50000
            timeout server 50000
            errorfile 400 /etc/haproxy/errors/400.http
            errorfile 403 /etc/haproxy/errors/403.http
            errorfile 408 /etc/haproxy/errors/408.http
            errorfile 500 /etc/haproxy/errors/500.http
            errorfile 502 /etc/haproxy/errors/502.http
            errorfile 503 /etc/haproxy/errors/503.http
            errorfile 504 /etc/haproxy/errors/504.http


    frontend www-http
            bind 192.168.1.10:80
            reqadd X-Forwarded-Proto:\ http
            default_backend www-backend

    frontend www-https
            bind 192.168.1.10:443 ssl crt /etc/ssl/private/company.pem
            reqadd X-Forwarded-Proto:\ https
            use_backend node-backend if { path_beg /socket.io }
            default_backend www-backend

    backend www-backend
            redirect scheme https if !{ ssl_fc }
            server www-1 192.168.1.20:80 check
            server www-2 192.168.1.21:80 check
            server www-3 192.168.1.22:80 check

    backend node-backend
            server node-1 192.168.1.30:8888 check

Вот тест ApacheBench (ab) для одного из серверов Nginx:

    $ ab -c 200 -n 10000 http://192.168.1.20/

    Server Software:        nginx/1.4.6
    Server Hostname:        192.168.1.20
    Server Port:            80

    Document Path:          /
    Document Length:        3130 bytes

    Concurrency Level:      200
    Time taken for tests:   2.257 seconds
    Complete requests:      10000
    Failed requests:        0
    Total transferred:      33720000 bytes
    HTML transferred:       31300000 bytes
    Requests per second:    4430.21 [#/sec] (mean)
    Time per request:       45.145 [ms] (mean)
    Time per request:       0.226 [ms] (mean, across all concurrent requests)
    Transfer rate:          14588.55 [Kbytes/sec] received

    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        4   27 104.3     16    1187
    Processing:     4   18   8.2     16     358
    Waiting:        3   18   7.9     16     334
    Total:          9   45 105.8     32    1225

    Percentage of the requests served within a certain time (ms)
      50%     32
      66%     41
      75%     43
      80%     44
      90%     49
      95%     52
      98%     55
      99%     57
     100%   1225 (longest request)

Вот тест ApacheBench (ab) для HAProxy с http:

    $ ab -c 200 -n 10000 http://192.168.1.10/

    Server Software:        nginx/1.4.6
    Server Hostname:        192.168.1.10
    Server Port:            80

    Document Path:          /
    Document Length:        3130 bytes

    Concurrency Level:      200
    Time taken for tests:   1.918 seconds
    Complete requests:      10000
    Failed requests:        0
    Total transferred:      33720000 bytes
    HTML transferred:       31300000 bytes
    Requests per second:    5215.09 [#/sec] (mean)
    Time per request:       38.350 [ms] (mean)
    Time per request:       0.192 [ms] (mean, across all concurrent requests)
    Transfer rate:          17173.14 [Kbytes/sec] received

    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        3   18   3.5     18      32
    Processing:     7   20   3.5     19      36
    Waiting:        7   20   3.4     19      36
    Total:         15   38   4.2     37      57

    Percentage of the requests served within a certain time (ms)
      50%     37
      66%     38
      75%     39
      80%     40
      90%     44
      95%     46
      98%     50
      99%     51
     100%     57 (longest request)

Вот тест ApacheBench (ab) для HAProxy с https:

    $ ab -c 200 -n 10000 https://192.168.1.10/

    Server Software:        nginx/1.4.6
    Server Hostname:        192.168.1.10
    Server Port:            443
    SSL/TLS Protocol:       TLSv1,DHE-RSA-AES256-SHA,2048,256

    Document Path:          /
    Document Length:        3130 bytes

    Concurrency Level:      200
    Time taken for tests:   566.303 seconds
    Complete requests:      10000
    Failed requests:        0
    Total transferred:      33720000 bytes
    HTML transferred:       31300000 bytes
    Requests per second:    17.66 [#/sec] (mean)
    Time per request:       11326.069 [ms] (mean)
    Time per request:       56.630 [ms] (mean, across all concurrent requests)
    Transfer rate:          58.15 [Kbytes/sec] received

    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:      483 8982 3326.6  11090   14031
    Processing:    16 2255 3313.0     43   11399
    Waiting:       14 2186 3253.3     35   11252
    Total:       5648 11237 879.1  11212   22732

    Percentage of the requests served within a certain time (ms)
      50%  11212
      66%  11274
      75%  11308
      80%  11321
      90%  11395
      95%  11641
      98%  11847
      99%  14063
     100%  22732 (longest request)

Вот тест OpenSSL на виртуальной машине HAProxy.

    $ openssl speed rsa

                      sign    verify    sign/s verify/s
    rsa  512 bits 0.000081s 0.000006s  12314.6 179042.8
    rsa 1024 bits 0.000277s 0.000017s   3603.7  60563.8
    rsa 2048 bits 0.001852s 0.000058s    539.8  17231.3
    rsa 4096 bits 0.013793s 0.000221s     72.5   4517.4

Таким образом, с моей точки зрения HAProxy не может превзойти тест скорости openssl 72,5 знака / с, 4517,4 проверки / с. Однако HAProxy с завершением SSL выполняет около 17 запросов / с. Конечно, мы можем получить меньший ключ для повышения общей производительности, но это не решает проблему (если проблема существует) увеличения скорости ~4.5x от теста скорости openssl до HAProxy.

Итак, учитывая эту информацию, существует ли оптимальная конфигурация HAProxy, которая увеличит производительность? Например, я что-то упускаю: когда пользователь заходит на страницу в первый раз, ему нужно только "подписать" один раз, а все одновременные запросы к странице только "подтвердить". Если это так, то тест АБ не измеряет это должным образом (поправьте меня, если я ошибаюсь). И чтобы это произошло, должен ли пользователь посещать один и тот же сервер Nginx? Если так, это требует сессий палки?

Пытаясь ответить на мои собственные вопросы, я попытался добавить липкие сессии из этого поста: HAProxy с SSL и липкими сессиями и использовал Siege для тестирования с несколькими URL-адресами. Тем не менее, по-прежнему не было повышения производительности.

    $ siege -c 100 -r 10 -b -f urls.txt

    Transactions:               1000 hits
    Availability:             100.00 %
    Elapsed time:              22.56 secs
    Data transferred:           2.84 MB
    Response time:              2.17 secs
    Transaction rate:          44.33 trans/sec
    Throughput:             0.13 MB/sec
    Concurrency:               96.06
    Successful transactions:        1000
    Failed transactions:               0
    Longest transaction:            8.96
    Shortest transaction:           0.16

Где urls.txt

    URL=https://192.168.1.10/
    $(URL)
    $(URL)page1.html
    $(URL)page2.html

Итак, я застрял в этом спектакле? В некоторых местах упоминается аналогичная частота запросов ~75 мс / запрос для 4096-битных ключей. https://certsimple.com/blog/measuring-ssl-rsa-keys

Или мой HAProxy плохо настроен и где-то обрабатывает SSL 2x? ServerFault: /questions/574582/nginx-ssl-termination-slow

2 ответа

Следует учитывать, что многие клиенты HTTP (включая браузеры) пытаются амортизировать стоимость рукопожатия SSL по нескольким запросам HTTP. То есть они устанавливают одно TCP-соединение с сервером, выполняют SSL-квитирование, а затем повторно используют это TCP-соединение (с его сеансом SSL) для нескольких запросов / ответов, вместо того чтобы выполнять SSL-квитирование для каждого запроса.

Чтобы включить этот тип сценария / теста в настройках, вы можете включить -k опция командной строки для ab, для вашего веб-интерфейса HTTP-запросов.

Еще одним соображением является использование вами option http-server-close Настройка HAproxy. Это говорит haproxy создать новое внутреннее TCP-соединение для каждого HTTP-запроса; это может добавить свои собственные 40-100 мс (если не больше), в зависимости от внутренней сети. Если вы позволили HAproxy сохранять эти внутренние TCP-соединения открытыми, это также может уменьшить задержку для каждого запроса, о которой сообщает ab,

В зависимости от количества ожидаемых сеансов SSL также возможно, что увеличение объема памяти для кэширования сеансов SSL (HAproxy's tune.ssl.cachesize настройка, возможно, в сочетании с tune.ssl.lifetime увеличение времени ожидания кеша и, следовательно, повышение вероятности повторного использования кеша сеанса) позволило бы проводить более возобновленные сеансы SSL (и быстрее / менее требовательные к использованию рукопожатия SSL).

Но я думаю, что цифры, сообщенные ab, при использовании keep-alive (-k), лучше продемонстрирует эффективность повторного использования одного и того же сеанса SSL (через одно и то же соединение TCP) для многих HTTP-запросов.

Надеюсь это поможет!

Сравните яблоки с яблоками.

Ваш эталон openssl speed rsa вероятно, не измеряет время DHE, потому что он не использует эфемерные ключи. Это означает, что вы тестируете менее безопасный алгоритм по сравнению с алгоритмом DHE. Последний медленнее, с другой стороны, он обеспечивает идеальную секретность вперед (PFS).

Но DHE довольно старый, неэффективный, и современные браузеры обычно используют лучшие ECDHE (или даже ECDSA).

Я думаю, что вы должны настроить свой ab тест для обеспечения ECDHE-RSA-AES128-SHA256. Я думаю, вы должны запрограммировать свой тест openssl, чтобы использовать цикл openssl s_client -cipher ECDHE-RSA-AES128-SHA256 (вместо упрощенного openssl speed).

Другие вопросы по тегам