Докеризируем Socket.io, redis и php

habr.png

Для передачи данных сервером на php клиенту можно использовать следующий алгоритм:


  1. Сервер php публикует данные в канал redis.
  2. Сервер node подписывается на события в соответствующем канале redis и при
    наступлении события поступления данных публикует эти данные уже в
    socket.io
  3. Клиент подписывается на сообщения 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"}


А это значит, что наш сервер благополучно передал клиентскому приложению данные.

© Habrahabr.ru