Докеризируем Socket.io, redis и php
Для передачи данных сервером на php клиенту можно использовать следующий алгоритм:
- Сервер php публикует данные в канал redis.
- Сервер node подписывается на события в соответствующем канале redis и при
наступлении события поступления данных публикует эти данные уже в
socket.io - Клиент подписывается на сообщения socket.io и обрабатывает их при поступлении
Исходный код проекта можно найти на github
Здесь я буду двигаться очень маленькими шагами.
В проекте будет использоваться связка nginx и php-fpm и начну я с настройки
nginx.
Настройка nginx
Начнем создавать docker-compose.yml
в корневой папке нашего проекта.
# docker-compose.yml
version: '3'
services:
nginx:
image: nginx
ports:
- 4400:80
Откроем в браузере: http://localhost:4400
и увидим стандартное приветствие
nginx.
Теперь настроим, чтобы nginx отдавал статическое содержимое папки./www/public
.
Сначала создадим папки
mkdir -pv www/public
Создадим файл ./www/pulbic/index.html
Hello World!
Создадим файл конфигурации nginx — nginx/conf/custom.conf
. Для начала
скопируем стандартный /etc/nginx/conf.d/default.conf
.
Изменим docker-compose.yml
services:
nginx:
image: nginx
+ volumes:
+ - ./nginx/conf/custom.conf:/etc/nginx/conf.d/default.conf
ports:
- 4400:80
Пересоздадим контейнер nginx
docker-compose up -d
И вновь наблюдаем по в браузере по адресу http://localhost:4400
стандартное
приветствие nginx.
Внесем измененияdocker-compose.yml
image: nginx
volumes:
- ./nginx/conf/custom.conf:/etc/nginx/conf.d/default.conf
+ - ./www:/www
ports:
- 4400:80
nginx/conf/custom.conf
#access_log /var/log/nginx/host.access.log main;
location / {
- root /usr/share/nginx/html;
+ root /www/public;
index index.html index.htm;
}
Теперь по адресу http://localhost:4400
отображается 'Hello World!' из файлаwww/public/index.html
.
Прорывом это назвать сложно, но мы определенно двигаемся в нужном направлении.
Настройка php
Начнем с создания папок для хранения файлов настроек контейнера.
mkdir -pv php/conf
Далее создадим php/Dockerfile
FROM php:7-fpm
RUN apt-get -qq update && apt-get -qq install \
curl \
> /dev/null
ENV PHPREDIS_VERSION 3.0.0
RUN mkdir -p /usr/src/php/ext/redis \
&& curl -L https://github.com/phpredis/phpredis/archive/$PHPREDIS_VERSION.tar.gz | tar xvz -C /usr/src/php/ext/redis --strip 1 \
&& echo 'redis' >> /usr/src/php-available-exts \
&& docker-php-ext-install redis
И внесем изменения в docker-compose.yml
- ./www:/www
ports:
- 4400:80
+ php:
+ build: ./php
+ volumes:
+ - ./www:/www
+ environment:
+ - REDIS_PASSWORD=${REDIS_PASSWORD}
Также нам нужно внести изменения в настройки nginx, чтобы файлы с расширением.php
обрабатывались php-fpm
.
Изменим файл nginx/conf/custom.conf
следующим образом
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
-#location ~ \.php$ {
-# root html;
-# fastcgi_pass 127.0.0.1:9000;
-# fastcgi_index index.php;
-# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
-# include fastcgi_params;
-#}
+location ~ \.php$ {
+ root /www;
+ fastcgi_pass php:9000;
+ fastcgi_index index.php;
+ fastcgi_param REQUEST_METHOD $request_method;
+ fastcgi_param CONTENT_TYPE $content_type;
+ fastcgi_param CONTENT_LENGTH $content_length;
+ fastcgi_param SCRIPT_FILENAME /www/public/$fastcgi_script_name;
+ include fastcgi_params;
+}
Осталось создать файл www/public/info.php
со следующим кодом
Перезапустим наш контейнер
docker-compose restart
И теперь по адресу http://localhost:4400/info.php
отображается информация о
настройках php.
Еще немного поэкспериментируем и создадим файл www/Test.php
А содержимое файла www/public/info.php
заменим на следующее:
prn();
Теперь по адресу http://localhost:4400/info.php
отображается 'success', а это
означает, что php-fpm
доступны скрипты расположенные в папке www
, а через
браузер они недоступны. Т.е. мы продолжаем двигаться в нужном направлении.
Настройка redis
Это, пожалуй, самая короткая часть.
Redis в этом проекте не будет доступен из внешней сети, но защиту паролем
настроим.
Для этого создадим файл .env
REDIS_PASSWORD=eustatos
Внесем изменения в docker-compose.yml
build: ./php
volumes:
- ./www:/www
+ redis:
+ image: redis
+ command: ["sh", "-c", "exec redis-server --requirepass \"${REDIS_PASSWORD}\""]
Чтобы протестировать подключение к redis изменим файл www/public/info.php
connect(
'redis',
6379
);
// авторизуемся. 'eustatos' - пароль, который мы задали в файле `.env`
$redis->auth($_ENV['REDIS_PASSWORD']);
// публикуем сообщение в канале 'eustatos'
$redis->publish(
'eustatos',
json_encode([
'test' => 'success'
])
);
// закрываем соединение
$redis->close();
Рестартуем контейнер
docker-compose restart
Теперь подключимся к серверу redis
docker-compose exec redis bash
Перейдем к командной строке. 'eustatos' — пароль, который мы ранее задали в
файле .env
# redis-cli -a eustatos
Подпишемся на канал 'eustatos' (название произвольное, чтобы все работало,
долно совпадать с названием канала, которое мы определили в файлеwww/public/info.php
)
> subscribe eustatos
После всех этих приготовлений, переходим в браузере по адресуhttp://localhost:4400/info.php
и наблюдаем, как в терминале, где мы
подключались к redis появляются примерно следующие строки:
1) "message"
2) "eustatos"
3) "{\"test\":\"success\"}"
Значит мы стали еще ближе к нашей цели.
Настройка socket.io
Созадим папку, где будут лежать файлы нашего socket.io сервера
mkdir socket
Внесем изменения в docker-compose.yml
redis:
image: redis
command: ["sh", "-c", "exec redis-server --requirepass \"${REDIS_PASSWORD}\""]
+ socket:
+ image: node
+ user: "node"
+ volumes:
+ - ./socket:/home/node/app
+ ports:
+ - 5000:5000
+ working_dir: /home/node/app
+ command: "npm start"
Перейдем в папку socket
cd socket
Установим необходимые пакеты
npm init -y
npm i -S socket.io redis express
После этого добавим в файл socket/package.json
строки
{
"name": "socket-php-example",
"version": "1.0.0",
"main": "index.js",
"author": "eustatos ",
"license": "MIT",
+ "scripts": {
+ "start": "node index.js"
+ },
"dependencies": {
"express": "^4.16.3",
"redis": "^2.8.0",
"socket.io": "^2.1.0"
}
}
Создадим файл socket/index.js
const express = require('express');
const app = express();
const http = require('http').Server(app);
const port = process.env.PORT || 5000;
app.get(
'/',
function(req, res, next) {
res.send('success');
}
);
http.listen(
port,
function() {
console.log('Listen at ' + port);
}
);
Перезапустим наш контейнер
docker-compose restart
После этого в браузере по адресу http://localhost:5000
отображается «success».
Значит мы еще чуть ближе к нашей цели. Осталось совсем немного.
Изменим файл socket/index.js
const express = require('express');
const app = express();
const http = require('http').Server(app);
const io = require('socket.io')(http);
// подключаемся к redis
const subscriber = require('redis').createClient({
host: 'redis',
port: 6379,
password: 'eustatos'
});
// подписываемся на изменения в каналах redis
subscriber.on('message', function(channel, message) {
// пересылаем сообщение из канала redis в комнату socket.io
io.emit('eustatosRoom', message);
});
// открываем соединение socket.io
io.on('connection', function(socket){
// подписываемся на канал redis 'eustatos' в callback
subscriber.subscribe('eustatos');
});
const port = process.env.PORT || 5000;
http.listen(
port,
function() {
console.log('Listen at ' + port);
}
);
На этом настройка контейнера socket.io завершена.
Настройка клиентского приложения
Клиентское приложение можно развернуть в любом из наших контейнеров, но
для чистоты эксперимента развернем его в отдельном контейнере.
Файлы клиентского приложения разместим в папке client
mkdir client
Создадим файл client/index.html
Изменим docker-compose.yml
ports:
- 5000:5000
command: "npm start"
+ client:
+ image: nginx
+ volumes:
+ - ./client:/usr/share/nginx/html
+ ports:
+ - 8000:80
Перезапустим наш контейнер
docker-compose restart
Откроем сначала в браузере http://localhost:8000
. Для демонстрации результата
наших трудов нужно открыть панель разработчика.
Пока ничего не отображается.
Откроем в другой вкладке или окне адрес http://localhost:4400/info.php
и посмотри на консоль панели разработчика нашего клиента. Мы должны увидеть:
{test: "success"}
А это значит, что наш сервер благополучно передал клиентскому приложению данные.