Интеграция Redis в PHP-проекты на платформе Docker

Раздел: DevOps -> Redis в Docker

Основные сценарии и их реализация

Как создать работающую связку PHP и Redis в Docker?

Наиболее распространённый подход - использовать официальный образ PHP с установкой расширения phpredis и отдельный контейнер Redis, объединённые через docker-compose. Это обеспечивает минимальное время отклика и надёжную передачу данных.

Шаг 1. Создание Dockerfile для PHP


FROM php:8.2-fpm-alpine
RUN docker-php-ext-install pdo pdo_mysql redis
COPY . /var/www/html
 

Docker php redis (docker php redis)

Шаг 2. Файл docker-compose.yml


version: '3.8'
services:
  php:
    build: .
    ports:
      - "9000:9000"
    volumes:
      - ./app:/var/www/html
    depends_on:
      - redis
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
volumes:
  redis_data:
 

Шаг 3. Проверка подключения из PHP


$redis = new Redis();
$redis->connect('redis', 6379); // имя сервиса в compose
$redis->set('test', 'Hello from Docker!');
echo $redis->get('test');
 

Типичная ошибка: Connection refused при обращении к Redis через localhost. Внутри контейнера PHP нужно использовать имя сервиса (redis) или IP-адрес контейнера. Если контейнеры не в одной сети, они не увидят друг друга. Решение - использовать общую сеть в docker-compose (по умолчанию она создаётся). Также проверьте, что порт 6379 открыт внутри контейнера Redis.

Ещё одна проблема - не установлено расширение redis в PHP. Ошибка "Class 'Redis' not found". Убедитесь, что в Dockerfile используется docker-php-ext-install redis (доступно в официальных образах PHP).

Как подключиться к Redis через библиотеку predis без установки расширения?

Если расширение phpredis недоступно (например, при использовании shared-хостинга или ограниченной сборки PHP), можно воспользоваться чистым PHP-клиентом predis. Он устанавливается через Composer.

Dockerfile без расширения redis:


FROM php:8.2-fpm-alpine
RUN docker-php-ext-install pdo pdo_mysql
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
COPY composer.json .
RUN composer install --no-dev
COPY . .
 

composer.json должен содержать "predis/predis": "^2.0".

Пример использования в PHP:


require 'vendor/autoload.php';
$client = new Predis\Client([
    'scheme' => 'tcp',
    'host'   => 'redis',
    'port'   => 6379,
]);
$client->set('foo', 'bar');
echo $client->get('foo');
 

Основная проблема predis - скорость работы ниже, чем у phpredis, так как это pure-PHP решение. Для высоконагруженных проектов предпочтительнее расширение C. Также predis потребляет больше памяти. Ошибка подключения может быть связана с тем, что Composer не установлен внутри контейнера - тогда используйте многоэтапную сборку или копирование бинарника composer из другого образа.

Как настроить хранение PHP-сессий в Redis внутри Docker?

Redis идеально подходит для сессий благодаря высокой скорости чтения/записи и возможности TTL. Для этого нужно изменить конфигурацию PHP и убедиться, что расширение redis доступно.

Шаг 1. Установите расширение redis, как в основном варианте.

Шаг 2. В Dockerfile добавьте копирование конфигурационного файла php.ini или укажите настройки через переменные окружения.


COPY custom.ini /usr/local/etc/php/conf.d/custom.ini
 

custom.ini:


session.save_handler = redis
session.save_path = "tcp://redis:6379?auth=mysecretpassword"
 

Если пароль не требуется, опустите часть ?auth=....

Шаг 3. Убедитесь, что Redis контейнер работает и доступен по имени redis.

Проверка: в PHP-скрипте вызовите session_start() и проверьте, что данные сохраняются между запросами.

Частая ошибка: сессии не сохраняются, если Redis требует аутентификации, а пароль не передан в save_path. Также проверьте, что контейнер Redis запущен с опцией requirepass или --requirepass, если вы используете пароль. В docker-compose можно задать пароль через переменную окружения REDIS_PASSWORD (для официального образа).

Как настроить Redis в качестве кеша для Laravel в Docker?

Laravel поддерживает Redis из коробки. Достаточно правильно настроить файл .env и сервисы Docker.

Шаг 1. Docker-окружение аналогично основному варианту: контейнер PHP с расширением redis и контейнер Redis.

Шаг 2. В файле .env укажите:


REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
CACHE_DRIVER=redis
SESSION_DRIVER=redis
 

Шаг 3. При необходимости используйте несколько баз данных (по умолчанию db0 для кеша, db1 для сессий). Можно настроить в config/database.php.

Пример: кеширование результата запроса:


$users = Cache::remember('users.active', 3600, function () {
    return DB::table('users')->where('active', 1)->get();
});
 

Ошибка: Predis\ClientException или Connection refused, если имя хоста не разрешается. Убедитесь, что сервис Redis называется redis в docker-compose и контейнеры принадлежат одной сети. Также проверьте, что в .env не указан REDIS_PASSWORD, если пароль не задан.

Как обеспечить сохранение данных Redis после перезапуска контейнера?

По умолчанию Redis хранит все данные в памяти, но может периодически сохранять их на диск с помощью RDB или AOF. В Docker для этого нужно смонтировать volume в каталог /data.

docker-compose.yml (фрагмент):


redis:
  image: redis:7-alpine
  ports:
    - "6379:6379"
  volumes:
    - ./redis-data:/data
  command: ["redis-server", "--appendonly", "yes"]
 

Опция --appendonly yes включает AOF (Append Only File) - каждое изменение записывается в лог-файл. Альтернативно можно настроить RDB (снапшоты) через --save 900 1.

Конфигурация через файл: создайте redis.conf:


appendonly yes
appendfsync everysec
save 900 1
save 300 10
save 60 10000
 

и укажите в compose: command: ["redis-server", "/usr/local/etc/redis/redis.conf"] + volume с конфигом.

Проблема: после пересоздания контейнера данные не восстанавливаются, если volume не примонтирован или права доступа на каталог не позволяют Redis записывать. Решение - проверить владельца (chown 999:999, где 999 - UID пользователя redis в контейнере).

Как защитить Redis паролем и изолировать сеть Docker?

Redis по умолчанию не требует аутентификации. Для продакшена необходимо задать пароль и ограничить доступ к порту.

Вариант 1: Установка пароля через аргументы командной строки:


redis:
  image: redis:7-alpine
  command: ["redis-server", "--requirepass", "SuperSecretPassword"]
 

Вариант 2: Через конфигурационный файл redis.conf:


requirepass SuperSecretPassword
 

Тогда в PHP-коде при подключении нужно указать пароль:


$redis = new Redis();
$redis->connect('redis', 6379);
$redis->auth('SuperSecretPassword');
 

Для изоляции сети используйте отдельную сеть в docker-compose, чтобы только необходимые контейнеры имели доступ к Redis.

Пример сети:


networks:
  internal:
    driver: bridge
services:
  php:
    networks:
      - internal
  redis:
    networks:
      - internal
    ports:
      - "127.0.0.1:6379:6379" # только localhost
 

Ошибка: при использовании auth() в predis синтаксис отличается - $client->auth('password'). Если пароль не задан, попытка аутентификации приведёт к ошибке.

Расширенные примеры и нестандартные сценарии

Очередь задач с помощью списков (BRPOP/LPUSH)

Redis списки отлично подходят для реализации очередей. Один процесс (producer) добавляет задачи в конец списка, другой (worker) ждёт новые элементы.

Пример

// producer.php
$redis = new Redis();
$redis->connect('redis', 6379);
$redis->lPush('queue:email', 'user1@example.com');
$redis->lPush('queue:email', 'user2@example.com');

// worker.php
$redis = new Redis();
$redis->connect('redis', 6379);
while (true) {
    $result = $redis->brPop('queue:email', 0); // блокируется до появления элемента
    echo "Отправляем письмо: " . $result[1] . "\n";
}
Отправляем письмо: user2@example.com
Отправляем письмо: user1@example.com

Транзакции Redis (MULTI/EXEC)

Группа команд выполняется атомарно. Пример - атомарный инкремент с проверкой.

Пример

$redis = new Redis();
$redis->connect('redis', 6379);
$redis->watch('counter');
$current = $redis->get('counter');
$redis->multi();
$redis->set('counter', $current + 1);
$redis->exec();

Если другой клиент изменит counter во время выполнения, транзакция будет отменена.

Публикация и подписка (Pub/Sub)

Redis поддерживает асинхронный обмен сообщениями через каналы.

Пример

// subscriber.php
$redis = new Redis();
$redis->connect('redis', 6379);
$redis->subscribe(['channel:notifications'], function ($instance, $channel, $message) {
    echo "Получено сообщение: $message из канала $channel\n";
});

// publisher.php
$redis = new Redis();
$redis->connect('redis', 6379);
$redis->publish('channel:notifications', 'Привет, подписчики!');
(в subscriber.php) Получено сообщение: Привет, подписчики! из канала channel:notifications

Выполнение Lua-скриптов на стороне сервера

Скрипты выполняются атомарно и снижают нагрузку на сеть.

Пример

$script = <<<'LUA'
local current = redis.call('GET', KEYS[1])
if not current then
    redis.call('SET', KEYS[1], ARGV[1])
    return ARGV[1]
else
    return current
end
LUA;
$redis->eval($script, ['mykey', 'default_value'], 1);

Результат: если ключ не существует, будет установлено значение из аргумента, иначе вернётся текущее.

Мониторинг состояния Redis через PHP

Метод INFO возвращает множество статистик.

Пример

$redis = new Redis();
$redis->connect('redis', 6379);
$info = $redis->info();
echo "Версия Redis: " . $info['redis_version'] . "\n";
echo "Количество ключей: " . $info['db0']['keys'] . "\n";
echo "Использовано памяти: " . $info['used_memory_human'] . "\n";
Версия Redis: 7.0.12
Количество ключей: 142
Использовано памяти: 1.23M

Настройка phpredis с опциями соединения

Можно задать таймауты, повторные попытки и serialiser.

Пример

$redis = new Redis();
$redis->connect('redis', 6379, 2.5); // таймаут 2.5 сек
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
$redis->setOption(Redis::OPT_PREFIX, 'myapp:');
$redis->set('user:1', ['name' => 'Иван', 'age' => 30]);
$data = $redis->get('user:1');
echo $data['name']; // Иван

Healthcheck для Redis в docker-compose

Чтобы убедиться, что Redis готов к работе до запуска PHP, добавьте healthcheck.

Пример

redis:
  image: redis:7-alpine
  healthcheck:
    test: ["CMD", "redis-cli", "ping"]
    interval: 5s
    timeout: 3s
    retries: 3

Тогда в сервисе PHP можно указать depends_on: redis: condition: service_healthy.

Docker PHP Redis - comments

En
Docker php redis (php)