MeteorJS, Nginx, mongodb, iptables… продакшен

  • Tutorial

Здравствуйте, меня зовут Александр Зеленин, и я веб-разработчик сисадмин.


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


Разворачивать будем на Ubuntu 16, но в целом схема на 99% совпадает и для Debian 8.


На самом деле я и близко не сисадмин, так что буду очень рад предложенным правкам, но в целом схема достаточно рабочая.


В качестве файловой системы при установке выбираем XFS — монга с ней хорошо дружит.


Готовим SSH

Если доступ у нас рутовый, то первое, что надо сделать — создать пользователя.


adduser zav # Используйте свой %username% вместо zav
apt-get install sudo 
usermod -aG sudo zav # Добавляем нашего пользователя в группу sudo, 
                     # чтобы иметь возможность далее всё делать из-под него
vi /etc/ssh/sshd_config # Если вы совсем не можете в vi — используйте тут nano вместо vi. 
                        # Если он не установлен — apt-get install nano

Нам надо изменить порт на случайный, по вашему выбору — это сразу защитит от части атак на ssh сервера.


Port 355 # Обычно изначально стоит Port 22
PermitRootLogin no # Запрещаем вход под рутом

Перезагружаем SSH


/etc/init.d/ssh restart

Теперь переподключаемся по SSH на новый порт (355), на нового, свежесозданного пользователя.


Подготавливаем второй диск и tmpfs (чтобы разместить монгу в памяти)

Я использую 2 раздельных физических диска чтобы минимизировать шанс отказа.
Вы можете размещать всё на 1 на свой страх и риск, схема от этого не изменится.
В этом случае можете точно так же использовать папку /secondary, но не монтировать в неё диск.


sudo cfdisk /dev/sdb # имя второго диска (sdb) может отличаться.
                     # Выбираем создать новый, 100% места, запись
sudo mkfs.xfs /dev/sdb1 # создаем ФС на втором диске
sudo mkdir /secondary
sudo vi /etc/fstab # делаем авто-монтирование при запуске системы

Добавляем в конец (если второго диска нет — первую строку опускаем)
tmpfs — файловая система будет расположена в оперативной памяти. Т.е. запись в раздел /data/inmemory будет делать запись в оперативную память, а не на диск. Надо понимать, что при перезагрузке оно очищается. size задает размер данной области. Ваша задача — что бы её хватило на размещение монги со всеми индексами. В моём случае оперативной памяти 128Gb, соответственно под tmpfs выделено 32 гигабайта.


/dev/sdb1       /secondary      xfs     defaults        0       2
tmpfs           /data/inmemory  tmpfs   size=25%        0       0

Ставим монгу, настраиваем реплику, подготавливаем реалтайм бекап

Актуальный способ установки можно посмотреть на официальном сайте.
В данном примере идёт установка версии 3.4 на Ubuntu 16.


Устанавливаем:


sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6
echo "deb [ arch=amd64,arm64 ] http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list
sudo apt-get update
sudo apt-get install mongodb-org

Создаем папки, конфиги и ставим права:


sudo mkdir /data
sudo mkdir /data/db
sudo mkdir /data/inmemory
sudo mkdir /secondary/data
sudo mkdir /secondary/data/db
sudo vi /data/mongod-memory.conf

Подход, в котором primary в памяти может быть опасен, т.к. в случае внезапного выключения питания данные, которые не успели перейти в реплики — теряются. В моём случае потеря 1-2 секунд не страшна, т.к. всё приложение пишется с учетом возможности восстановления из любой позиции. Финансовые данные пишутся с параметром, подтверждающим, что данные уже есть на реплике (т.е. на диске).
Если ваше приложение к такому не готово — вы можете отказаться от memory раздела и делать всё классически. В целом для этого достаточно будет убрать монтирование tmpfs и немного изменить конфиг, сделав его схожим с mongod-sec-d1.conf


mongod-memory.conf — primary, in memory
processManagement:
  fork: true 
  pidFilePath: "/data/inmemory/mongod.pid"

storage:
  dbPath: "/data/inmemory"
  journal:
    enabled: false # Отключаем журналирование
                   # т.к. в памяти в случае падения оно нас всё равно не спасёт
  indexBuildRetry: true
  wiredTiger:
    engineConfig:
      cacheSizeGB: 8 # Ограничиваем размер кеша. 
                     # Я, честно говоря, не уверен, но его можно убрать полностью,
                     # т.к. это то, что монга для оптимизации поместит 
                     # в оперативную память, а данный инстанс уже весь в ней

systemLog:
  destination: "file"
  path: "/var/log/mongodb/mongodb.log" # Для всех экземпляров указываем один инстанс.
                                       # Теоретически тут можно получить просадку, 
                                       # если монга в памяти начнёт писать
                                       # на диск, но при нормальном функционировании 
                                       # эти события редки (ошибки, медленный запросы)
  logAppend: true
  quiet: false
  verbosity: 0
  logRotate: "reopen"
  timeStampFormat: "iso8601-local"

net:
  bindIp: 127.0.0.1 # Делаем монгу доступной только для локального интерфейса
                    # в случае, если настраиваем реалтайм бекап — добавляем внешний интерфейс
  port: 27000 # указываем, на каких портах будем размещать, 
              # они должны быть разные для разных инстансов в пределах сервера
  http:
    enabled: false # отключаем доступ по http и прочим интерфейсам
    JSONPEnabled: false
    RESTInterfaceEnabled: false
  ssl: # ssl нам тоже не нужен в данном случае
    mode: disabled

security: # Мы будем настраивать корректный доступ по правам, 
          #чтобы клиенты видели только то, что им надо
  authorization: "enabled"
  keyFile: "/data/mongod-keyfile" # Это ключ для общения реплик между собой
  javascriptEnabled: false # Запрещаем исполнение JS в БД.

replication:
  oplogSizeMB: 4096 # Указываем максимальный размер oplog'а. 
                    # В случае падения реплик их восстановление будет быстрым
                    # только если они отстали не более чем на размер oplog'a. 
                    # Корректный размер можно определить
                    # в зависимости от приложения — как часто идёт изменение БД?
  replSetName: "consulwar"
  enableMajorityReadConcern: false # указываем, что мы НЕ дожидаемся подтверждение 
                                   # от реплики на запись для возврата значений поиска.

operationProfiling:
  slowOpThresholdMs: 30 # указываем, сколько максимум может выполнятся запрос, 
                        # после чего будет считаться медленным
                        # и записан в лог для последующей ручной обработки
  mode: "slowOp"

sudo vi /data/mongod-sec-d1.conf

mongod-sec-d1.conf — secondary, disk 1

По большей части конфиг повторяется, разница всего в паре мест.
Но оставляю для удобства


processManagement:
  fork: true
  pidFilePath: "/data/db/mongod.pid"

storage:
  dbPath: "/data/db"
  journal:
    enabled: true # Обращаем внимание, что тут журнал уже включен, 
                  # диск у нас считается надёжным источником
  indexBuildRetry: true
  wiredTiger:
    engineConfig:
      cacheSizeGB: 8 # Фактически, даже если по какой-то причине Primary упадёт, 
                     # secondary будет использовать память, но только для чтения

systemLog:
  destination: "file"
  path: "/var/log/mongodb/mongodb.log"
  logAppend: true
  quiet: false
  verbosity: 0
  logRotate: "reopen"
  timeStampFormat: "iso8601-local"

net:
  bindIp: 127.0.0.1
  port: 27001
  http:
    enabled: false
    JSONPEnabled: false
    RESTInterfaceEnabled: false
  ssl:
    mode: disabled

security:
  authorization: "enabled"
  keyFile: "/data/mongod-keyfile"
  javascriptEnabled: false

replication:
  oplogSizeMB: 4096
  replSetName: "consulwar"
  enableMajorityReadConcern: false

operationProfiling:
  slowOpThresholdMs: 30
  mode: "slowOp"

sudo vi /data/mongod-sec-d2.conf

mongod-sec-d2.conf — secondary, disk 2

Разница, по сути, только в пути до БД и в зависимости от используемого порта.


processManagement:
  fork: true
  pidFilePath: "/secondary/data/db/mongod.pid"

storage:
  dbPath: "/secondary/data/db"
  journal:
    enabled: true
  indexBuildRetry: true
  wiredTiger:
    engineConfig:
      cacheSizeGB: 8

systemLog:
  destination: "file"
  path: "/var/log/mongodb/mongodb.log"
  logAppend: true
  quiet: false
  verbosity: 0
  logRotate: "reopen"
  timeStampFormat: "iso8601-local"

net:
  bindIp: 127.0.0.1
  port: 27002
  http:
    enabled: false
    JSONPEnabled: false
    RESTInterfaceEnabled: false
  ssl:
    mode: disabled

security:
  authorization: "enabled"
  keyFile: "/data/mongod-keyfile"
  javascriptEnabled: false

replication:
  oplogSizeMB: 4096
  replSetName: "consulwar"
  enableMajorityReadConcern: false

operationProfiling:
  slowOpThresholdMs: 30
  mode: "slowOp"

Добавляем ключ для корректной работы реплики, устанавливаем права на папки


sudo openssl rand -base64 741 > ~/mongod-keyfile
sudo mv mongod-keyfile /data/mongod-keyfile
sudo chmod 600 /data/mongod-keyfile
sudo chown mongodb:mongodb -R /data
sudo chown mongodb:mongodb -R /secondary/data

Создаем скрипты автозапуска


sudo apt-get install numactl
sudo mv /lib/systemd/system/mongod.service /lib/systemd/system/mongod@.service
sudo vi /lib/systemd/system/mongod@.service

mongod@.service

@ в названии сервиса означает что мы можем запускать его с параметрами.
Данный скрипт устанавливает все необходимые параметры ОС для работы с монгой — удобно.


[Unit]
Description= Mongo Database on %i
After=network.target

[Service]
Type=forking
ExecStartPre=/bin/sh -c '/bin/echo never > /sys/kernel/mm/transparent_hugepage/enabled'
ExecStartPre=/bin/sh -c '/bin/echo never > /sys/kernel/mm/transparent_hugepage/defrag'
User=mongodb
Group=mongodb
PermissionsStartOnly=true
ExecStart=/usr/bin/numactl --interleave=all /usr/bin/mongod --config /data/mongod-%i.conf
LimitFSIZE=infinity
LimitCPU=infinity
LimitAS=infinity
LimitNOFILE=64000
LimitNPROC=64000
TasksMax=infinity
TasksAccounting=false

[Install]
WantedBy=multi-user.target

Говорим БД запускаться при старте системы, и так же запускаем экземпляры прямо сейчас.
Наш параметр устанавливается после @, например, memory укажет использовать при запуске /data/mongod-memory.conf


sudo systemctl enable mongod@memory
sudo systemctl enable mongod@sec-d1
sudo systemctl enable mongod@sec-d2
sudo service start mongod@memory
sudo service start mongod@sec-d1
sudo service start mongod@sec-d2

Подключаемся к монге, инициализируем реплику, устанавливаем пользователей


mongo localhost:27000/admin

Выполняем в консоли монги


rs.initiate({
  _id: "consulwar", // название реплики
  version: 1,
  protocolVersion: 1,
  writeConcernMajorityJournalDefault: false, // Говорим чтобы реплика не дожидалась 
                                             // подтверждения о записи от других
  configsvr: false, // Указываем, что это не конфигурационный сервер для кластера 
                    // (кластер — тема отдельной статьи)
  members: [
    {
      _id: 0, // id начинаем от нуля и далее инкрементим
      host: 'localhost:27000', // по какому адресу доступна данная монга
      arbiterOnly: false, // может ли отдавать и хранить данные
      buildIndexes: true, // указываем, что строить индексы надо
      hidden: false, // не скрытая, т.е. приложение к ней может подключиться
      priority: 100, // приоритет при выборе Primary — чем больше, тем приоритетнее
      slaveDelay: 0, // задержка репликации. Нам задержка не нужна.
      votes: 1 // может ли голосовать при выборе Primary
    },
    {
      _id: 1,
      host: 'localhost:27001',
      arbiterOnly: false,
      buildIndexes: true,
      hidden: false,
      priority: 99, // Приоритет ниже
      slaveDelay: 0,
      votes: 1
    },
    {
      _id: 2,
      host: 'localhost:27002',
      arbiterOnly: false,
      buildIndexes: true,
      hidden: false,
      priority: 98,
      slaveDelay: 0,
      votes: 1
    }
  ],
  settings: {
    chainingAllowed : true, // Разрешаем репликам читать друг с друга, а не только с мастера
    electionTimeoutMillis : 5000, // Указываем время на переголосование, 
                                  // в случае падения одной из БД. ~7 секунд. 
                                  // Если все экземпляры у нас на 1 машине 
                                  // можем уменьшить до 500мс, скажем
    catchUpTimeoutMillis : 2000
  }
});

// Создаем первого пользователя, он должен иметь права root'а
db.createUser({user: 'zav', pwd: '7Am9859dcb82jJh', roles: ['root']}); 
// Выходим - ctrl+c, ctrl+c

Подключаемся под нашим пользователем


mongo localhost:27000/admin -u zav -p '7Am9859dcb82jJh' --authenticationDatabase admin

Добавляем пользователя для работы с приложением


use consulwar // вместо consulwar название основной БД вашего приложения
db.createUser({
    user: 'consulwar', 
    pwd: '37q4Re7m432dtDq', 
    roles: [{ // Права на чтение и запись в нашу БД. 
              // Включает ещё ряд прав, типа создание индексов и т.п.
        role: "readWrite", db: "consulwar" 
    }, { // Права на чтение oplog'а, для быстрой работы приложения метеора
        role: 'read', db: 'local'
    }]
});

Теперь довольно важный и сложный вопрос — реалтайм репликация для бекапа. Целиком он рассмотрен не будет, ввиду необходимости настройки ещё ряда оборудования, чего я бы хотел избежать в данной статье.


Настройка реалтайм-бекапа

Реплицировать мы будем на внешний сервер (иначе в чём смысл бекапа? :-)).
На внешнем сервере должна быть установлена монга схожим образом.
Примерный конфиг:


mongod-backup.conf
processManagement:
  fork: true
  pidFilePath: "/data/db/mongod.pid"

storage:
  dbPath: "/data/db"
  journal:
    enabled: true
  indexBuildRetry: false # Отключаем построение индексов
  wiredTiger:
    engineConfig:
      cacheSizeGB: 0 # Отключаем кеш

systemLog:
  destination: "file"
  path: "/var/log/mongodb/mongodb.log"
  logAppend: true
  quiet: false
  verbosity: 0
  logRotate: "reopen"
  timeStampFormat: "iso8601-local"

net:
  bindIp: 222.222.222.222 # Данный экземпляр должен быть доступен извне
  port: 27000
  http:
    enabled: false # но не по http, само собой
    JSONPEnabled: false
    RESTInterfaceEnabled: false
  ssl:
    mode: disabled

security:
  authorization: "enabled"
  keyFile: "/data/mongod-keyfile" # mongod-keyfile берем с основного сервера
  javascriptEnabled: false

replication:
  oplogSizeMB: 0
  replSetName: "consulwar"
  enableMajorityReadConcern: false

operationProfiling:
  mode: "off" # нам не нужно профилирование на бекапе

В фаерволе сервера-бекапа разрешаем коннект на 27000 порт ТОЛЬКО с IP сервера-приложения/БД.
Тоже самое — на сервере приложения/БД в bindIp указываем смотреть ещё и во внешний интерфейс (ip внешний сервера), и в iptables разрешаем доступ на 27000-27002 порты ТОЛЬКО с ip севера-бекапа.


При инициализации/реинициализации реплики добавляем


{
    _id: 4,
    host: '222.222.222.222:27000', // собственно интерфейс, на который смотрит бекап
    arbiterOnly: false,
    buildIndexes: false, // не строим индексы вообще на бекапе 
    hidden: true, // скрытый! Используется ТОЛЬКО для хранения информации
    priority: 0, // Не участвует в выборах
    slaveDelay: 0, // Задержка бекапа нам не нужна, 
                   // но может использоваться, если нужно пару бекапов в реалтайме "час назад"
    votes: 0 // Не участвует в голосовании
}

Готово! Теперь данные будут литься в реалтайме ещё и во внешний бекап, что очень круто.
В случае полного краха приложения мы можем инициализировать реплику точно так же, и она восстановится из бекапа.
Поверьте, это намного быстрее, чем mongodump/mongorestore (по личным прикидкам в 25-100 раз).


Nodejs, npm, app user, meteor build

Устанавливаем ноду, ставим модуль n, ставим им версию ноды 4.8.1 (последняя, официально поддерживаемая метеором версия).
Устанавливаем pm2, т.к. именно им будем запускать процессы.


sudo apt-get install nodejs
sudo apt-get install npm
sudo npm install -g n
sudo n 4.8.1
sudo npm install pm2@latest -g

Добавляем пользователя, из-под которого будет всё запускаться и который будет отвечать за деплой


sudo adduser consulwar

Заходим за данного пользователя


su consulwar
mkdir app
mkdir app/current

На локальной машине заходим в директорию с нашим meteor проектом.
Создаем папку для билдов, собираем проект в эту папку.


mkdir ../build
meteor build ../build/ --architecture os.linux.x86_64

Полученный архив загружаем на наш сервер, например, по sftp. Заходим под нашим пользователем для приложения.
Загружаем в папку ~/app.
Заходим по ssh за нашего пользователя (consulwar у меня).


cd app
mkdir 20170429 # создаем папку по сегодняшней дате
tar -xvzf consulwar-master.tar.gz -C 20170429
ln -s 20170429/bundle ~/app/current # Создаем симлинк, чтобы быстро переключаться
(cd current/programs/server && npm install)
vi pm2.config.js # создаем конфиг для нашего pm2

pm2.config.js
var settings = { ... }; // Объект заменяем объектом из settings.json вашего приложения

var instances = 10; // Сколько экземпляров запускаем? Советую не более N-1
                    // Где N — количество ядер

var apps = [];

for (var i = 0; i < instances; i++) {
  apps.push({
    "script": "/home/consulwar/app/current/bundle/main.js", // укажите корректный путь
    "exec_mode": "fork", // т.к. рулить будем через Nginx, создаем форками
    "name": "consulwar", // имя приложения в списке процессов
    "env": {
      "ROOT_URL": "http://consulwar.ru/", // Адрес приложения
      "HTTP_FORWARDED_COUNT": 1, // Указываем количество прокси перед приложением
                                 // Чтобы корректно разрулить IP пользователя в приложении
      "PORT": 3000 + i, // Порты начинаются с 3000 (3000, 3001, 3002...)
      "MONGO_URL": "mongodb://consulwar:37q4Re7m432dtDq@localhost:27000,localhost:27001,localhost:27002/consulwar?replicaSet=consulwar&readPreference=primary&authSource=consulwar",
      "MONGO_OPLOG_URL": "mongodb://consulwar:37q4Re7m432dtDq@localhost:27000,clocalhost:27001,localhost:27002/local?replicaSet=consulwar&authSource=consulwar",
      "METEOR_SETTINGS": settings
    }
  });
}

module.exports = {
  apps : apps
}

И запускаем


pm2 startup pm2.js # Выведет команду, которую надо выполнить для автозапуска после перезагрузки
... # Выполняем команду
pm2 start pm2.js
pm2 status # Валидируем, что всё запустилось. В случае ошибок можем посмотреть pm2 logs

Готово, приложение развернуто и уже должно быть доступно по ip сервера/адресу с указанием порта, например http://consulwar.ru:3000


Nginx балансировка
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:nginx/stable
sudo apt-get install nginx
sudo vi /etc/nginx/nginx.conf

nginx.conf
user www-data; # из-под кого запускаем nginx
worker_processes  6; # Указываем количество воркеров
worker_rlimit_nofile 65535;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  4000; # Кол-во подключений на воркера
                              # В нашем случае мы можем обработать 6 * 4000 = 24 000 запросов 
                              # в момент времени
}

http {
    map $http_upgrade $connection_upgrade { # Для корретной установки сокет-подключения
        default upgrade;
        ''      close;
    }

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    server_tokens off;

    sendfile on; # Для отправки статики
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;

    gzip on; # Включаем gzip
    gzip_comp_level 6;
    gzip_vary on;
    gzip_proxied any;
    gzip_buffers 16 8k;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
    gzip_static on; # Разрешаем отдачу файлов с .gz на конце. Например, main.js.gz будет отдаваться при запросе main.js
    gzip_http_version 1.1;
    gzip_disable "MSIE [1-6]\.(?!.*SV1)"

    proxy_connect_timeout      60;
    proxy_read_timeout         620;
    proxy_send_timeout         320;
    proxy_set_header        X-Real-IP       $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

    upstream backends {
        #ip_hash; # указываем, если нам надо что бы пользователь всегда подключался к одному и тому же экземпляру приложения
        least_conn; # Я выбрал, чтобы подключение отдавалось наименее нагруженному процессу

        # Прописываем каждый из интерфейсов
        # Я запускал их 10, значит тут 10 записей
        server 127.0.0.1:3000 weight=5 max_fails=3 fail_timeout=60; 
        server 127.0.0.1:3001 weight=5 max_fails=3 fail_timeout=60;
        server 127.0.0.1:3002 weight=5 max_fails=3 fail_timeout=60;
        server 127.0.0.1:3003 weight=5 max_fails=3 fail_timeout=60;
        server 127.0.0.1:3004 weight=5 max_fails=3 fail_timeout=60;
        server 127.0.0.1:3005 weight=5 max_fails=3 fail_timeout=60;
        server 127.0.0.1:3006 weight=5 max_fails=3 fail_timeout=60;
        server 127.0.0.1:3007 weight=5 max_fails=3 fail_timeout=60;
        server 127.0.0.1:3008 weight=5 max_fails=3 fail_timeout=60;
        server 127.0.0.1:3009 weight=5 max_fails=3 fail_timeout=60;

        # Интерфейс, который будет отрабатывать в случае недоступности приложения
        server 127.0.0.1:3100 backup;
    }

    # Указываем пути до ssl сертификатов. Мы их создадим чуть позже
    ssl_certificate /etc/letsencrypt/live/consulwar.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/consulwar.ru/privkey.pem;

    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    ssl_stapling on;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers         'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!EXP:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

    server {
        server_name consulwar.ru;
        # указываем слушать 80 и 443 порты
        listen 80;
        listen 443 ssl http2;

        # любой запрос переадресовываем на одно из наших приложений 
        location / {
            proxy_pass http://backends;

            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_set_header X-Forwarded-For $remote_addr;
        }

        # Статику раздаем с помошью nginx
        location ~* \.(jpg|jpeg|gif|ico|png)$ {
            root /home/consulwar/app/current/programs/web.browser/app;
        }

        # Основной css и js файл лежит отдельно, добавляем правило дополнительное
        location ~* "^/[a-z0-9]{40}\.(css|js)$" {
            root /home/consulwar/app/current/programs/web.browser;
        }

        location ~ "^/packages" {
            root /home/consulwar/app/current/programs/web.browser;
        }

        # В случае, если у вас никаких систем мониторинга не стоит, 
        # можно убрать следующую конструкцию.
        location /nginx_status {
            stub_status on;
            access_log off;
            allow 127.0.0.1;
            deny all;
        }

        # Для получения SSL сертификата
        location ~ "^/.well-known" {
            root /home/consulwar/app/current/programs/web.browser/app/.well-known;
        }
    }

    include /etc/nginx/conf.d/*.conf;
    client_max_body_size 128m;
}

Перезапускаем nginx


sudo service nginx restart

Получаем SSL от Let's Enctypt.
Само собой, домен уже должен быть привязан к этому IP адресу.


sudo apt-get install letsencrypt
sudo letsencrypt certonly -a webroot --webroot-path=/home/consulwar/app/current/programs/web.browser/app -d consulwar.ru
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048 # Генерируем дополнительный сертификат безопасности

Вжух! SSL Работает


iptables
sudo vi /etc/network/if-up.d/00-iptables

00-iptables
#!/bin/sh
iptables-restore < /etc/firewall.conf
ip6tables-restore < /etc/firewall6.conf

sudo chmod +x /etc/network/if-up.d/00-iptables
apt-get install xtables-addons-dkms
sudo vi /etc/firewall.conf

firewall.conf
*filter
:INPUT ACCEPT [193038:64086301]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [194475:60580083]
-A INPUT -i lo -j ACCEPT # Разрешаем локальное общение
-A INPUT -m state --state RELATED,ESTABLISHED -p all -j ACCEPT # Разрешаем существующие подключения

# Разрешаем SSH
-A INPUT -m state --state NEW -p tcp -m multiport --dport 355 -j ACCEPT

# Доступ к Nginx
-A INPUT -m state --state NEW -p tcp -m multiport --dport 80,443 -j ACCEPT

# Ловушка для кривых подключений, будет тратить ресурсы атакующего, а не сервера
-A INPUT -p tcp -m tcp -j TARPIT

# Дропаем всю фигню
-A INPUT -p udp -j DROP 

COMMIT

sudo vi /etc/firewall6.conf # оставляем пустым или заполняем тем, что вам надо
sudo iptables-restore < /etc/firewall.conf

itables настроены, и лишние порты закрыты.


DONE!


Если понравится подход/формат — выложу настройку почтовой системы, мониторинга инфраструктуры, CI через Bamboo и ещё ряд используемых у нас вещей.


Вполне вероятно, что-то я упустил (но в целом прямо вот так должно работать) — спрашивайте, дополню.
Надеюсь, у кого-то это теперь займет меньше времени, чем у меня :-)


PS: У нас открыта вакансия front-end разработчика.

Support the author
Share post

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 69

    –1
    Полезно!
      +1
      /etc/init.d/ssh restart
      

      Всё же лучше использовать systemctl/service. Systemd же.

      -A INPUT -m state --state NEW -p tcp -m multiport --dport 355 -j ACCEPT
      

      не понятно, зачем нужен multiport, если открывается только один порт.
        0
        Дело в том, что лично у меня там было несколько портов :-)
        А так хорошее замечание, да, спасибо.
        • UFO just landed and posted this here
            0

            Оно стоит не в середине, а в начале, на самом деле. Т.к. после действия идёт список юнитов.

          –1
          Спасибо за интересную статью — у вас классный стиль изложения. Хотя, можно легко уменьшить в несколько раз, если использовать docker.
            +2
            Если ты хочешь решить проблему с помощью Docker, то теперь у тебя две проблемы :-)
            Вообще ни одна строка тут бы не сэкономилась при использовании докера.
            С этой логикой можно сказать — используй meteor up и он поставит всё за тебя. Но только разница в производительности будет катастрофической.
              0
              Неплохо бы подкрепить нагрузочными тестами. А то все как-то через mup деплоят.
                0
                Это почти бесполезная тема, т.к. всё индивидуально.
                Каждый выбирает инструменты для своих задач.
                Минимальная просадка от использования докера — 5-10%.
                От использования nginx для статики, честно говоря, я не считал. Имеено в плане отправке файлов они используют один и тот же модуль (sendfile) и скорость именно отдачи схожа, но Nginx обладает значительно меньшем оверхедом, и потребляет намного меньше памяти и CPU, что вполне ощущается по графикам нагрузки.
                  0
                  Минимальная просадка от использования докера — 5-10%.

                  Как вы считаете, из-за чего происходит эта просадка?
                    0
                    Так понимаю, что вопрос, на самом деле, провокационный.
                    В идеальном случае контейнеризация работает, по сути, через алиасы к хост-машине, и тут просадка не ощущается почти совсем, если не считать использования методов которых на хост-машине нет (подходящим примером будут jail'ы c линуксом внутри, да ещё и какой-нибудь хитрой сборки).
                    А вот когда дело касается сетевых интерфейсов всё становится веселее, т.к. контейнер обладает своим сетевым интерфейсом, через которой и происходит общение, и, в итоге, в добавок мы получаем ещё и излишнее сетевое общение в пределах машины.
                    Я мог ошибиться в ряде мест, но по тем замерам, которые я делал, что-то такое и выходит. При большом количестве запросов (без перманентного соединения) всё становится ещё хуже.
                      0
                      Мне правда интересно, как это воспроизвести. Если речь об одной машинке, то сетевые интерфейсы на линукс машинке это обычный бридж и несколько правил в iptables. При желании можно использовать host network, тогда вообще ни каких отличий не будет.
                +1
                Почитаем на пальцах? SSH не нужен. Образ monogdb и конфиг один вместо трех — разницу задаем через переменные окружения и volume. Трюки с симлинками и пользователями убираем — версионность образов «изкаропки». pm2 так же исключается — у докера свой супервизор. Итого 2/3 статьи можно просто было не писать. Сценарии в виде Dockerfile, docker-compose вы бы могли выложить на github и дать возможность читателям на 100% воспроизвести решение, с точностью до приложения meteor.
                  0
                  Одно из метеор-решению хостю в докерах. Mup (или mupx, уже не помню) настроил, и на продакшене только git pull и mup deploy для обновления. Для постоянной разработки неудобно до невозможности, но как-то работает. Бэкаплю докер вместе с монгой, тут у меня сомнения как это делать правильно, сделал через какой-то npm-пакет docker-backup что ли… еще и руками в нём пути правил… свои заморочки тоже есть.
                    +1
                    Сейчас до нагрузок дойдете и поймете цену этого удобства :-)
                      +1
                      Вообще после этого случая я докеры как раз не люблю. Предпочитаю pm2 для node.js. Конкретно этот деплой внутрикорпоративный, нагрузок там не будет…
                0

                Докер не уменьшает объём конфигурирования в целом, более того, частенько требует более объёмного конфигурирования. Другое дело, что это конфигурирование by design делается автоматическим и легко воспроизводимым

                  +1
                  При условии что образы поддерживают. В ином случае приходим к ситуации когда без проблем разворачивается версия полугодовалой давности, как встречалось на многих проектах.

                  Реально это жутко бесит, когда говорят — да у нас тут докер, ща всё развернём за пол часа, и ты, как спец, тратишь целый день рабочий, думая что что-то не понял, что сам дибил. Зовёшь потом местного спеца и с ним ещё два сраный дня настраиваешь работоспособность. Это фан.

                  Само собой это проблема не докера, и он хорош в плане переиспользования. Но многие забывают его поддерживать.
                    +1

                    Ну, я о ситуации, когда разработчик приложения если не сам пишет докерфайлы и скрипты/конфиги оркестрации, то, хотя бы, постоянно ими пользуется.


                    А так, да, если кто-то придёт на текущие проекты без меня, то увидев там файлы докера может опрометчиво решить, что нужно разворачивать проект с их помощью, а они уже несколько месяцев (с первого релиза) не обновляются, поскольку поддерживать две версии сценариев разработки (для дев и тест окружений — докер, для приемочного и продакшена — башскрипты и ручная правка конфигов) ресурсов не хватает.

                      +1
                      > для дев и тест окружений — докер, для приемочного и продакшена — башскрипты и ручная правка конфигов

                      Подскажите — в чём преимущество такого подхода? Docker прекрасен (удобен? полезен? даже не знаю как точнее выразиться) именно тем, что он даёт 99% идентичности среды, что вообще незаменимо при Continuous Delivery.

                      Я имею ввиду, что Docker даёт одно и то же окружение (образ), которое вы и гоняете на dev/qa/etc/prod. В чём смысл на дев использвовать одно — а на проде другое? Или вы просто не хотите Docker в продакшене вообще, и он у вас чисто как утилита тестирования?

                      Упоминание про ручное редактирование конфигов правда смутило ещё больше…
                        +1

                        Собственно используем докер, чтобы максимально приблизить среды разработки и тестирования к производственным, без необходимости поднимать десятки виртуалок на рабочих местах разработчиков и прочих. Грубо, как более легковесную замену виртуалбоксу и вагранту. Докер (с докер-композ особенно) гораздо удобнее для локальной эмуляции прод окружений сложных систем, состоящих из десятков сервисов, шлюзов и клиентов, чем виртуалки, пускай и дает не 99% идентичности системного окружения, как дали бы виртуалки, а 90%. Да и эти 10% на практике выливаются в "на проде работает, а в докере — нет", что гораздо лучше чем наоборот :)


                        Не то, что мы не хотим, а отдел разработки не может преодолеть инертность отдела эксплуатации. Да и сам я как техруководитель разработки не готов настаивать на переносе в продакшен стейтфулл сервисов типа СУБД и файловых хранилищ. В теории хочу всё на докер перевести, но на практике просто не могу оценить риски потери данных и простоев. На "голом железе" всё более-менее предсказуемо и обкатано годами, а вот что может случиться с многослойной ФС и как её восстанавливать я просто не знаю, а случись что, эксплуатация ко мне прибежит.

                          0
                          > а вот что может случиться с многослойной ФС и как её восстанавливать я просто не знаю

                          Я далеко не мега-гуру Докера, но мне кажется в вопросе сохранности данных вы можете не переживать. Мы, например, просто данные монтируем к виртуальным машинам с Docker Host как внешние диски (на Azure) и/или шары NFS (AWS EFS) — со своей ФС, бекапированием и т.д. — ничего нестандартного. Ну а далее — через volumes к сервисам.
                            0

                            Монтировать данные БД по сети не самая лучшая идея в плане производительности, по-моему.

                    0
                    По моему опыту, наличие штатного репозитория для артефактов сборки, супервизора процессов, а так же изоляции по файловой системе и сетевым портам снимает здоровый пласт с инфраструктуры и в разы упрощает деплой.
                      +2
                      Вот только найти человека, который всё это настроит и за приемлемые деньги будет изредка поддерживать нереально.
                      Как итог — ничего не экономит, а только удорожает.
                        +1

                        Репозиторий артефактов сборки имеется (gitlab ci), супервизор штатный (systemd). Изоляция по портам требует дополнительного слоя маршрутизации на хосте в лучшем случае (например в http), а то и вручную перенастраивать клиентов. Изоляция по файловой системе тоже создаёт проблемы, когда нужно шарить файлы между процессами.

                    +2
                    интересные теги…
                      +4
                      Мне очень сложно даётся администрирование.
                      Поиск что же поставить в ту или иную опцию. Документация далеко не всегда даёт исчерпывающий ответ.
                      Последний тег отражает моё ощущение :-)
                      Ну и так же выпало, что обращался к проф. сисадмину, он 2 недели тянул, а по итогу было почти ничего из запрошенного. Пришлось бросать все планы и сидеть разбираться во всём самому.
                      +3
                      Я ожидал что тема не популярная. Ну т.е. там ну 10 рейтинга… но отрицательный.
                      Пожалуйста, дайте обратный отклик — что не так?
                      Мне видится что вышла отличная статья, для тех кто разрабатывает на метеоре и переходит к этапу развёртывания.
                      Спасибо.
                        0
                        Лично мне полезно, я в избранное добавлю. Половину и так знал, другую половину как-то делал иначе (или вообще не делал) — но пост как шпаргалка очень нужен.
                          +1

                          Возможно по нынешним временам многие считают что ручное развертывание плохая практика. Если не "модными" инструментами, то хотя бы баш-скриптом, или, на худой конец, полным логом команд консоли с чем-то типа sed вместо визуального редактирования в vim и т. п..

                            +2
                            Само собой повторяться это ручками более не будет — я подготавливаю всю автоматизацию в bamboo.
                            Но чисто «готовый скрипт» будет бесполезен из-за кучи специфики.
                            В таком формате есть возможность понять что и зачем делается.
                            Я, вроде, ещё постарался разъяснить большую часть опций — что, зачем, почему и так далее.

                            Автоматизация же и начинается именно с того, что первый раз надо как-то пройти этот весь путь :-)
                            Ну и запускать чужие скрипты без разбора — такая себе идея.

                            Но спасибо за пояснение, пока это выглядит разумнее всего.
                            0
                            Отличная статья. Большое спасибо вам. Очень было бы интересно почитать про настройку почтовых сервисов и т.п.
                            0
                            А это нормально устанавливать SSH порт на значение из диапазона так называемых «well-known ports» (0-1023)?
                              0
                              Я на разных серверах проверял, 99% сканов (и подбора ssh пароля) именно SSH идёт на 22 порт. Если мы банально перенесли, то уже вышло значительно лучше. Я советую ставить рандомное значение туда. Поставьте 9358, например :-)
                              По грамотному так вообще пароль отключаем и используем ключ.
                              А ещё лучше через iptables разрешаем подключение на ssh только со своего ip.
                                0
                                Вполне нормально. Можно использовать любой порт, главное чтобы с другими не пересекался.

                                PS. Но, если очень хочется, то можно повесить и на один порт через мультиплексор, например sslh
                                0

                                При балансировке без ip_hash, на сколько я понимаю, могут быть проблемы с веб-сокетами, так как соединение устанавливается в несколько запросов. Я с socket.io сталкивался с этой проблемой, но разницы в том, что лежит поверх ws, ddp или socket.io, в данном случае наверное нет. Вот тут есть немного информации.

                                  +1
                                  Nginx не настолько глуп :-) Соединение устанавливается без каких либо проблем и всё работает достаточно шустро.
                                  В моём случае, так же, переключение не пренципиально, т.к. состояния не хранятся (точнее хранятся только в БД).
                                    +1

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

                                    +2
                                    Вполне вероятно, что-то я упустил

                                    По разделу iptables могу посоветовать включить SYNPROXY.
                                    Вещь простая и полезная, не позволяет атакам типа SYN-FLOOD доходить до уровня приложения, что достаточно важно для конфигураций типа вашей.

                                    По моим наблюдениям этого не делают абсолютное большинство сисадминов.

                                    Ниже пример, как исходная точка.
                                    iptables+synproxy.sh
                                    #!/bin/sh
                                    DEV="eth0"
                                    PORTS="80,443"
                                    
                                    # SYNPROXY works on untracked conntracks
                                    #  it will create the appropiate conntrack proxied TCP conn
                                    # NOTICE: table "raw"
                                    
                                    /sbin/iptables -t raw -I PREROUTING -i $DEV -p tcp -m tcp --syn -m multiport --dports $PORTS -j CT --notrack
                                    
                                    # Catching state
                                    #  UNTRACKED == SYN packets
                                    #  INVALID   == ACK from 3WHS
                                    
                                    /sbin/iptables -I INPUT 1 -i $DEV -p tcp -m tcp -m multiport --dports $PORTS -m conntrack --ctstate INVALID,UNTRACKED \
                                        -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460
                                    
                                    # Drop rest of state INVALID
                                    #  This will e.g. catch SYN-ACK packet attacks
                                    
                                    /sbin/iptables -I INPUT 2 -i $DEV -p tcp -m tcp -m multiport --dports $PORTS -m conntrack --ctstate INVALID -j DROP
                                    
                                    # More strict conntrack handling to get unknown ACKs (from 3WHS) to be
                                    #  marked as INVALID state (else a conntrack is just created)
                                    #
                                    /sbin/sysctl -w net/netfilter/nf_conntrack_tcp_loose=0
                                    
                                    # Enable timestamping, because SYN cookies uses TCP options field
                                    /sbin/sysctl -w net/ipv4/tcp_timestamps=1
                                    
                                    # Adjusting maximum number of connection tracking entries possible
                                    #
                                    # Conntrack element size 288 bytes found in /proc/slabinfo
                                    #  "nf_conntrack" <objsize> = 288
                                    #
                                    # 288 * 2000000 / 10^6 = 576.0 MB
                                    /sbin/sysctl -w net/netfilter/nf_conntrack_max=2000000
                                    
                                    # IMPORTANT: Also adjust hash bucket size for conntracks
                                    #   net/netfilter/nf_conntrack_buckets writeable
                                    #   via /sys/module/nf_conntrack/parameters/hashsize
                                    #
                                    # Hash entry 8 bytes pointer (uses struct hlist_nulls_head)
                                    #  8 * 2000000 / 10^6 = 16 MB
                                    echo 2000000 > /sys/module/nf_conntrack/parameters/hashsize
                                    
                                    # Hint: Monitor nf_conntrack usage searched, found, new, etc.:
                                    #  lnstat -c -1 -f nf_conntrack
                                    



                                    Ссылки на тему:
                                    Можно посмотреть слайды доклада DDoS protection Using Netfilter/iptables (автор Jesper Dangaard Brouer)
                                    На Хабре была статья, основанная на этом докладе.
                                      0
                                      Спасибо, почитаю, и, попозже, добавлю в статью.
                                      +1
                                      Ну я бы еще добавил ipset с множеством пациентов, забаненных по IP — может пригодиться при DDoS-ах ограниченного масштаба.
                                        +1
                                        «Если понравится подход/формат — выложу настройку почтовой системы, мониторинга инфраструктуры, CI через Bamboo и ещё ряд используемых у нас вещей.»
                                        будет не лишним, да, пожалуйста.
                                          0
                                          Интересно и полезно, спасибо за пост!
                                          Пишите дальше про почту и прочее. Весь негатив в топку.
                                            +1

                                            Так вот он, какой, продакшен рептилоидов...


                                            Я думаю, автор и так понимает, что держать мастер монги на tmpfs — затея с последствиями.
                                            Поэтому просто прошу рассказать, большой ли выигрыш по скорости у вас получился от такой схемы?
                                            Вроде бы монга и так неплохо справляется с кешированием в оперативке.

                                              0
                                              Я думаю, автор и так понимает, что держать мастер монги на tmpfs — затея с последствиями.

                                              Более того, об этом даже в статье поясняется. :-)

                                              Пока мало данных что бы полноценно оценить прирост, но в плане стабильности времени запросов стало лучше. Даже топовые SSD диски иногда давали спайки задержки (у всех провайдеров, как на виртуальных так и на физических), что рандомно давала задержку ответа — мне это дико не нравилось. Сейчас спайков нет от слова совсем.
                                              +1

                                              Установка node.js в статье:


                                              sudo apt-get install nodejs
                                              sudo apt-get install npm
                                              sudo npm install -g n
                                              sudo n 4.8.1

                                              То есть вы сначала ставите старую ноду из репозитория, чтобы через нее установить установщик новых версий node.js и, наконец, установить новую версию node.js.


                                              Почему бы просто не последовать официальной документации, и не поставить ноду сразу из их репозитория?

                                                0
                                                Например потому что мне нужна версия 4.8.1, которая является последней поддерживаемой, а не 4.8.2
                                                В случае если следовать официальной, поставилась бы именно 4.8.2 и мне всё равно пришлось бы ровно так же откатываться назад.
                                                Можно тут сказать что — ну это же минорная версия, тип, в чём проблема? Проблема в том, что я предпочитаю использовать официально совместимые версии, а не сидеть разбираться что же там обновили такое, повиляет ли оно на моё приложение или нет?
                                                В общем, думаю, позицию пояснил.
                                                  0

                                                  На ветку 4.x сейчас выходят только исправления дырок в безопасности.
                                                  Так что обновление повлияет на ваш проект только положительно.

                                                    0
                                                    У меня в прошлом уже был опыт подобных обновлений минорных версий без тестирования.
                                                    Спасло то что были бекапы и грамотная система откатов.
                                                    Теперь каждое обновление — в строго контролируемых условиях. Как минимум чтение всех чейнджлогов и мануальной проверки связанных частей. Это всё занимает время. Подобные траты времени (как и обновления) в большинстве случаев просто не оправданны.
                                                    Я реально не вижу абсолютно никакого смысла пытаться спорить по данному вопросу. Тут есть несколько вариантов:
                                                    а) Вы проверяете все обновляемые компоненты системы, т.е. затрачиваете какую-то часть времени именно на актуализацию, которая, в большинстве случае не даёт ничего.
                                                    б) Вы слепо верите, что минорная версия не поломает в вашем конкретному случае ничего (верить будете ровно пока не погорите на этом)
                                                    в) Кто-то отдельный занимается чисто поддержкой и обновлением пакетов в вашей компании и это экономически оправдано
                                                    г) Ваш вариант
                                                      0

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


                                                      И во-вторых, обновления системы вы тоже не ставите, а то "как бы чего не вышло"?

                                                        0
                                                        Как часто предлагаете читать чейнджлоги для каждой используемой библиотеки/компонента в проекте?
                                                        Для всех пакетов в списке ниже уже есть обновления.
                                                        Угадайте что произойдет когда я сделаю meteor update?
                                                        Проект станет не рабочим. Ломается миллион мест.
                                                        Что должно мотивировать меня обновить эти пакеты здесь и сейчас?
                                                          0

                                                          Вообще-то, мы здесь говорим не про meteor update, а про системный apt-get update.

                                                            +1
                                                            Так, и чем он принципиально отличается? :-) Это ровно точно такое же обновление пакетов / зависимостей, которые написали люди, которые совершают ошибки. Я уверен что в текущей конфигурации всё работает корректно. Для исправления уязвимостей в некоторых пакетах я обновляю их в ручную. Информацию об этих проблемах получаю из публичных источников.

                                                            У вас apt-get update никогда ничего не ломал? Вам повезло.
                                                              0

                                                              И все же, я оцениваю шансы на поломку от apt-get update намного меньше, чем от того, что сервер сложится от известной уязвимости, которая уже закрыта, но вы не обновили софт вовремя.


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

                                                                0
                                                                Я уже описал все варианты чуть выше.
                                                                Вы предпочитаете действовать на веру.
                                                                Я предпочитаю больше контроля.
                                                                Это просто разница подходов.
                                                                  0

                                                                  А насколько меньше в процентах? Вот с месяц назад пришло с реп Ubuntu 16.04 обновление системное, закрыли какую-то уязвимость безопасности в DNS, а у нас стали появляться ошибки резолвинга в логах php, причём исключительно из под php-fpm, и баг не 100%, а плавающий, где-то один запрос на сотню, что уменьшило конверсию в 3 раза, а смок-тесты не выявили — число запросов оказалось недостаточно большим.


                                                                  Ставить на продакшен обновления лишь потому, что они вышли — не самая хорошая практика. А уж без чтения ченжлогов — просто опасно. Хотя оно бы нас не спасло.

                                                                    0

                                                                    Мы здесь node.js обсуждаем, а вы приводите пример с PHP. Ничего не могу сказать, потому что не занимаюсь эксплуатацией php.


                                                                    А с node.js работаю довольно давно и ни разу не сталкивался с проблемами при обновлении в пределах одной мажорной версии. А уж тем более, с LTS версией, патчи в которую попадают только после релиза и обкатки в более новых версиях.

                                                                      0
                                                                      У ноды ситуация куда хуже чем в пхп, говорю вам ровно так же, как человек много раблтающий и с тем и с тем :-)
                                                                      Чего стоит, например, собственная внезапная интерпритация стандартов в одной из библиотек, когда они решили все http заголовки к одному виду приводить и изменили это в миноргой версии.
                                                                        0
                                                                        в одной из библиотек

                                                                        Речь идет о самом node.js а не библиотеках в npm.


                                                                        С npm-модулями конечно же нужно фиксировать версию. Для этого даже есть немало решений: npm-shrinkwrap, yarn.lock.


                                                                        Node.js же развивается с обратно-совместимыми изменениями и обновления в рамках одной версии, например 4.x.x ставить не только можно, но и нужно.

                                                  0
                                                  Вы уж извините, но то то вы сделали с монгой это жесть.
                                                  1. Ваш бекап — это не бекап. Он вас не спасет от случайных drop|delete. И никак он вам не поможет откатить базу. Это просто еще одна реплика. У вас нет бекапа.
                                                  2. Монгу на рам диск? есть же специальный storage — memory. Тогда монга сама будет знать как использовать память и какого размера использовать буфферы.
                                                  3. Реплика сет на 4 ноды? это вот так выглядит
                                                  — В субботу терям бекап сервер — и откладываем его ремонт до понедельника( ну все ж работает и еще 3 есть)
                                                  — В воскресенье у нас что-то скушало рам (или умер один из дисков, или любая другая причина) — упал еще один процесс монги. Осталось два, но они оба в SECONDARY.
                                                  4. Задайте приоритеты у нод. А то у вас однажды при падении мастера все проголосуют за бекап. Который как я понял не особо сильный сервер.
                                                    +1
                                                    Вы читали очень невнимательно.
                                                    1) Начнём с того, откуда возьмуться случайные drop/delete на мастер-монге? :-) Ниоткуда. Туда руками никто не лазит, а приложение такого не делает. Далее реплика на моём сервере располагается на ZFS, с которой делаются снимки 2 раза в день, которые ещё в добавок льются иногда на внешний диск и регулярно в Glaicer. Если интереснее подробнее — смотрите предыдущую статью про инфраструктуру.
                                                    2) Отлично. Вы ей пользовались? Нет. А почему? Потому что она стоит 15 000$ в год. Но вы этого не знали, само собой, и просто указали будто бы я решил по приколу сделать что-то странное. Мне важна была запись без спайков и я её получил. ВСЁ реализованно безопасно.
                                                    3) 3+1 будет сказать корректнее, т.к. бекап скрытый и на него нет переключение он не голосует. Я вообще работаю круглосуточно 7 дней в неделю уже 2 года :-) С чего мне откладывать? У нас миллиард мониторингов, добавим сотрудников, назначим ответственных — оповещения будут отправляться, может и отдохнуть удастся. А вообще в предыдущем сетапе у меня монга не падала НИ РАЗУ за год.
                                                    4) Не понимаю как вы вообще всё смотрели :-) Ну серьёзно. Все приоритеты указаны при инициализации реплики.
                                                      0
                                                      Согласен читал по диагонале и много упустил.
                                                      1. Про ZFS, снапшоты и предидущую статью у вас ни слова в статье. Откуда берутся delete? Конечно из кода. От необдуманных действий пользователей до неидеального кода. Не стоит называть реплику бекапом. Читатель статьи может подумать, что этого будет достаточно и без снапшотов
                                                      2. Вы меня не поняли. Я не про Atlas). Я про In-Memory Storage Engine
                                                      3. Это очень хорошо что вы можите сами все поднять. Но падает все — процессы, ядро, память, винты, сетевые карты. Про добавим сотрудников — даже смешно. Добавить админа вы не смогли(У вас же они есть, но почему-то делаете все вы)
                                                      4. вот тут я пропустил. Извиняюсь
                                                        0
                                                        1. Данный бекап защищает не от ошибок приложения, а от потери сервера. Вопрос бекапирования это, блин, вообще отдельная огроменная статья. Задачу что у нас полетят диски в том или ином виде мы решим. Про ZFS и прочее читателю знать не надо, что бы совсем крыша не поехала :-) Когда человек решит заняться бекапами — он и будет изучать тему по бекапам.

                                                        2.) In-memory storage доступен ТОЛЬКО в enterprice версии. Поверьте, я всем этим интересовался, созванивался и обсуждал условия. Всё что мне сказали — 15 000$ в год за 1 (один) сервер. По другому in-memory не поюзать. Всё.

                                                        3) Про отказоустойчивость см. предыдущую статью про инфраструктуру (там всё заменялось как раз). Задачи получить отказоустойчивость в рамках данной статьи не было в принципе — была задача настроить MVP имея 1 сервер. Админов у нас нет, потому что на постоянку не нужен, а по фрилансу фиг найдёшь надёжного + ещё организовать это всё безопасно не просто.
                                                          0
                                                          Данный бекап защищает не от ошибок приложения, а от потери сервера.

                                                          Защита от потери основного сервера путём практически мгновенного переключения на уже готовый резервный сервер без потерь данных или с минимальными потерями на настоящий момент — одна из основных задачи именно репликации. Задача же бэкапа — восстановить сервер состоянием из прошлого.

                                                            0
                                                            Окей, как назовём реплику с задержкой? ;-)
                                                            Ну т.е. реплику, которая отстатёт на час. Это бекап?
                                                            А на сутки. Бекап?
                                                            Разница настройки — 1 строка (прокомментирована в настройках).
                                                            Запуск любого количества реплик с задержкой не представляет проблем.
                                                              +1

                                                              Реплика с задержкой :)


                                                              Реплики осуществляются потоково, бэкап — снэпшотами. Восстановление из бэкапа операция идемпотентная — восстанавливать из одного бэкапа можно сколько угодно раз с одним и тем же результатом. С репликой же (работающей) мы каждый раз имеем разные результаты, синхронные с мастером или отстающие на случайную или детерминированную величину, но разные.

                                                                0
                                                                Ок :-) Как писал ранее для такого у меня снимнки делаются. Но в целом вы правы.
                                                                Хотя, мне видится, что несколько реплик с хорошо подобраными задержками дадут результат лучше (хотябы по времени восстановления).
                                                    0
                                                    Спасибо за статью! Разрабатываю на метеоре уже достаточно долгое время, но темы развертывания приложения касаюсь только сейчас. До данной статьи думал что реально запускать приложение только через mup с помощью Docker. Но ваша статья доказывает иное. Используя mup, у меня возникла проблема с нужным репозиторием, а именно ppa.launchpad. При mup setup консоль выдает данную ошибку (Использую свое железо (Ubuntu Server 16.04.1 LTS)):

                                                    W: Репозиторий «http://ppa.launchpad.net/chris-lea/node.js/ubuntu xenial Release» не содержит файла Release.
                                                    E: Не удалось получить http://ppa.launchpad.net/chris-lea/node.js/ubuntu/dis… 404 Not Found
                                                    E: Некоторые индексные файлы не скачались. Они были проигнорированы или вместо них были использованы старые версии.

                                                    Only users with full accounts can post comments. Log in, please.