PHP Profi

Подключение к Redis через Unix-сокеты в Docker Перевод

php docker redis unix socket

Подключение к Redis через Unix-сокеты, как правило, быстрее, чем подключение через TCP/IP.

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

Имея 10М+ скачиваний, redis является четвертым среди самых скачиваемых Docker-образов, и большинство систем настраивают подключение к Redis через Docker'овский встроеный сетевой TCP-мост.

Docker'овский собственный сетевой TCP-мост — это, конечно, хорошо и прекрасно, но не хотели бы вы выжать еще немного скорости?

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

time redis-benchmark -h 127.0.0.1 -p 6379

48.92 real 36.43 user 11.85 sys

time redis-benchmark -s /tmp/redis.sock

42.90 real 34.96 user 7.42 sys

Разница в скорости в 14% существенна, и мы должны воспользоваться этим, даже при использовании Docker.

Но подождите минуту, разве Docker-контейнеры не самодостаточны и разделены? Как мы можем использовать Unix-сокеты в такой среде?

Ответ заключается в том, что мы должны воспользоваться общим (расшариваемым) дисковым пространством Docker'а.

Допустим у нас есть PHP-контейнер, в котором работает наше приложение с Redis-клиентом, который подключается к контейнеру Redis'а.

Нам нужно создать отдельный контейнер. Он будет базой для дискового пространства, которое мы собираемся расшарить PHP и Redis'у.

Давайте подключим его в /tmp/docker/, и скажем Redis'у, чтобы он располагал свой файл redis.sock в этой папке.

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

Мы можем использовать образ busybox для этого, потому что этот контейнер на самом деле не будет делать ничего, кроме как хранить файлы.

В конфигах контейнеров для PHP и Redis мы используем настройку volumes_from для подключения одного и того дискового пространства из busybox в наши контейнеры.

Так что наш файл docker-compose.yml будет выглядеть примерно так:

services:
    tmp:
        image: busybox
        command: chmod -R 777 /tmp/docker
        volumes:
            - /tmp/docker/

    php:
        image: php
        // other app bootstrapping options
        volumes_from:
            - tmp

    redis:
        image: redis
        command: redis-server /etc/redis.conf
        volumes:
            - /redis/redis.conf:/etc/redis.conf
        volumes_from:
            - tmp

Здесь важными моментами являются сервис tmp и настройки volumes_from в других контейнерах. Это просто наглядный пример и, конечно, предполагается, что вы правильно всё пропишите, включая redis.conf в контейнере c Redis'ом.

Вы должны убедиться, что Redis запускается с конфигом redis.conf, т.к. нам нужно явно указать Redis создать сокет-файл, который будет доступен для подключения.

В вашем redis.conf добавьте следующие строки:

unixsocket /tmp/docker/redis.sock
unixsocketperm 777

Эти настройки укажут Redis'у создать при запуске /tmp/docker/redis.sock и дать ему права 777. Я не сомневаюсь, что ‘777’ будет спорным, но это на ваше усмотрение. Я считаю, что в изолированной среде Docker'а это совершенно безопасно.

С учётом того, как мы настроили расшаренные папки, то после запуска этих контейнеров, /tmp/docker/ будет доступна и на PHP-контейнере, и мы сможем достучаться до неё из кода нашего приложения.

Далее в вашем PHP-приложении подключитесь к Redis через Unix-сокет. Если вы используете популярный пакет Predis, вы можете сделать это так:

$client = new Predis\Client([
    'scheme' => 'unix',
    'path' => '/tmp/docker/redis.sock',
]);

Если вы используете Laravel, вы должны будете сделать чуть больше, т.к. настройки по умолчанию предполагают, что вы хотите подключаться к Redis через TCP. В файле .env, добавьте следующее:

REDIS_SCHEME=unix
REDIS_PATH=/tmp/docker/redis.sock

Затем в config/database.phpизмените секцию 'redis' так, чтобы она выглядела следующим образом:

'redis' => [

    'client' => 'predis',

    'default' => [
        // 'host' => env('REDIS_HOST', '127.0.0.1'),
        // 'password' => env('REDIS_PASSWORD', null),
        // 'port' => env('REDIS_PORT', 6379),
        'scheme' => env('REDIS_SCHEME'),
        'path' => env('REDIS_PATH'),
        'database' => 0,
    ],

],

"Живые" результаты

Я сделал снова тест, но на этот раз настроив одно из моих приложений, которое работает на PHP и в значительной степени опирается на Redis.

Я был немного озорной ( прим. пер.: это дословно :) ) и запустил их вживую на моем боевом сервере, но я считаю, что это отличный стрес-тест на то, как мое боевое окружение держит нагрузку.

time --verbose docker-compose exec redis redis-benchmark -h 127.0.0.1 -p 6379

Command being timed: “docker-compose exec redis redis-benchmark -h 127.0.0.1 -p 6379”
 User time (seconds): 0.35
 System time (seconds): 0.09
 Percent of CPU this job got: 0%
 Elapsed (wall clock) time (h:mm:ss or m:ss): 1:21.64
 Average shared text size (kbytes): 0
 Average unshared data size (kbytes): 0
 Average stack size (kbytes): 0
 Average total size (kbytes): 0
 Maximum resident set size (kbytes): 25344
 Average resident set size (kbytes): 0
 Major (requiring I/O) page faults: 31
 Minor (reclaiming a frame) page faults: 14686
 Voluntary context switches: 1229
 Involuntary context switches: 293
 Swaps: 0
 File system inputs: 22640
 File system outputs: 0
 Socket messages sent: 0
 Socket messages received: 0
 Signals delivered: 0
 Page size (bytes): 4096
 Exit status: 0

time --verbose docker-compose exec redis redis-benchmark -s /tmp/docker/redis.sock

Command being timed: “docker-compose exec redis-server redis-benchmark -s /tmp/docker/redis.sock”
 User time (seconds): 0.37
 System time (seconds): 0.04
 Percent of CPU this job got: 0%
 Elapsed (wall clock) time (h:mm:ss or m:ss): 1:06.77
 Average shared text size (kbytes): 0
 Average unshared data size (kbytes): 0
 Average stack size (kbytes): 0
 Average total size (kbytes): 0
 Maximum resident set size (kbytes): 25232
 Average resident set size (kbytes): 0
 Major (requiring I/O) page faults: 46
 Minor (reclaiming a frame) page faults: 14708
 Voluntary context switches: 1167
 Involuntary context switches: 294
 Swaps: 0
 File system inputs: 21024
 File system outputs: 0
 Socket messages sent: 0
 Socket messages received: 0
 Signals delivered: 0
 Page size (bytes): 4096
 Exit status: 0

TCP-соединение выполняет бенчмарк на 22% дольше, чем тест с использованием Unix-сокетов.

Я надеюсь, что этот пост поможет вам. Если у вас есть какие-либо комментарии не забудьте поделиться свими мыслями. Вопросы по материалу также приветствуются.

2017-12-28 alek13 оригинал

Комментарии

авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий