Spark-in.me Часть 3 DIY поддержка и админство сайта

Или как мы делаем то, чего не умеем, но делать надо

Posted by snakers41 on June 12, 2017

Статьи цикла

Spark-in.me - как, зачем и почему. Часть 1 - почему?

Spark-in.me - как, зачем и почему. Часть 2 - как? Архитектура приложения и структура БД

Spark-in.me Часть 3 - DIY поддержка и админство сайта

Spark-in.me Часть 4 - Базовое админство для обычных человеков (postgres и не только)

Spark-in.me Часть 5 - переход на HTTPS


Если вы не профессиональный админ, то зачастую деплой / саппорт / внесение правок в архитектуру может выглядеть для вас именно так. На моей практике, к сожалению, более 75% профессиональных админов, которых я встречал рассуждают с точки зрения "вам не нужно, то что вы просите", а не "я помогу решить вам вашу проблему моим способом". Об этом стоит задуматься. Ну и ссылка на интересный блог админа.


По запросу под статье хотел бы описать наш jобщий подход к админству наших проектов. Он не претендует на полноту, но работает для нас в комбинации с курением форумов и документации Ubuntu. Для начала несколько полезных ссылок на полезную копипасту, которую мы постили на канале и на сайте (общая мысль - любая команда может не работать на другой системе, читайте что пишете в консоли, если у вас есть критические замечания по сути - вы найдете наши контакты на сайте + комменты открыты):

  1. Азы админства постгреса и бекапов от автора Spark-in.me;
  2. Простое админство;
  3. Полезные скрипты для переноса и деплоя приложений 
  4. Курсы про админство Постгреса на русском;


0. Покупка VDS

Загрузив в мозг общее описание архитектуры нашего сайта, вырисовываются такие кусочки системы и деплоя:

  • Должна быть (в идеале отдельная при росте нагрузки) машина для базы;
  • Должна быть (в идеале отдельная при росте нагрузки) машина для админки;
  • Должна быть (в идеале отдельная при росте нагрузки) машина для фронтенда;


При росте нагрузки , понятное дело, основная нагрузка ложится на базу, т.к. на ней держится большая часть приложения. Фронтенд сделан на изоморфном реакте, он прожорлив, но он перекладывает нагрузку на клиент (браузер) пользователя. С учетом этого начнем нашу логическую цепочку. Пока пользователей мало (одновременно на сайте вряд ли бывает больше чем 10-20 человек), все можно задеплоить на одну машину. С учетом прожорливости ноды, у VDS, которую нужно купить для этого, должно быть 1-2 ГБ памяти. У Digital Ocean такое удовольствие стоит порядка 10 долларов в месяц (да, деплоить на своем сервере не хочется). У Digital Ocean также есть отличные гайды по админству Linux, когда будете гуглить - ищите в том числе на их сайте.

Не вижу смысла останавливать на выборе провайдера VDS - есть провайдеры и дешевле, чем за 10 баксов, но тут все подкупает наличием дополнительных бекапов и простотой и интуитивностью их интерфейса. Могу только отметить:

  • Чем чернее сервис и чем хреновее интерфейс, тем меньше гарантий и нет сервиса;
  • Есть старые конторы типа Hetzer, которые дают dedicated сервера и VDS, там чуть дешевле, но вряд ли все так просто;
  • Есть сервисы, которые сразу дают ячейки с установленным софтом, но они дороже в несколько раз и не поддерживают извращения;

1. Покупка доменов и прокидывание субдоменов

Первым шагом на нашей стороне будет покупка домена. Допустим мы купили домен spark-in.me и всю мелочевку, которая на него хоть как-то похожа. Далее нужно сделать редирект всех мусорных доменов на spark-in.me в кабинете регистратора. Далее мне кажется логичным завести такие субдомены через кабинет регистратора (это можно сделать через Apache или Nginx, но я выбрал самый простой способ) и направить их на машину.

  • author.spark-in.me, который ведет на админку автора;
  • api.spark-in.me, который ведет на АПИ;
  • spark-in.me, который ведет на сайт;
  • pics.spark-in.me, который ведет на микросервис по хранению картинок;


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

2. Настройка сервера

Поскольку мы не ищем простых путей и в паре мест мы срезаем углы (мы делаем RSS и Sitemap по крону), то проще купить дешевую VDS и самому настроить деплой и поставить софт.

Базовые вещи, нужные для системы - апдейт пакетов и софт для мониторинга нагрузки. Это не заменяет такие вещи как Zabbix, но Digital Ocean к примеру предоставляет базовую статистику по нагрузке и так

# installation of the key software
sudo apt-get update
# server monitoring tool
sudo apt-get install glances
# just invoke glances to monitor the system


Есть явные spikes во время ночных бекапов по крону, но ничего критического пока нет. 

Постгрес и апач (потом был заменен на nginx)

# postgresql
# installing contrib version via ppa
sudo apt-get install postgresql postgresql-contrib
# installing apache2
sudo apt-get install apache2
# checking syntax of apache2 config files
sudo apache2ctl configtest
sudo systemctl status apache2
# restarting apache2
sudo systemctl restart apache2
# apache2 specific mods
sudo a2enmod proxy # for reverse proxy


Php и node-js (все гуглится по ссылкам ниже или ключевым словам)

Также при использовании apache2 рекоммендуется сразу выключить directory listing мод, иначе будут видны папки на вашем сервере (было заменено на nginx).

В официальном репозитории Убунты на момент настройки была устаревшая нода - но в интернете также были гайды по апгрейду.

# checking ufw
sudo ufw app list
# disable apache2 directory listing mod
sudo a2dismod autoindex -f
# install php
# https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-on-ubuntu-16-04
sudo apt-get install php libapache2-mod-php php-mcrypt
# install postgresql driver
sudo apt-get install php-pgsql
# install curl
sudo apt-get install php-curl
# locate the php.ini file ans set
# memory to 256m, time to 60s and error reporting to on
# installing node js and npm
sudo apt-get install nodejs
# this actually installs node 4.3
# do this to install 6.9+
wget -qO- https://deb.nodesource.com/setup_7.x | sudo bash -
sudo apt-get install -y nodejs
sudo apt-get install npm 
# install yarn
# https://yarnpkg.com/lang/en/docs/install/#linux-tab
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn
# disable the apache2 default conf
sudo a2dissite 000-default.conf
sudo systemctl restart apache2


VSFTPD для быстрого доступа по ftp. Не рекоммендуется использовать для нормальных проектов, но у нас проект не бизнесовый

Сборник ссылок про vsftpd

# vsftpd installation and config
sudo apt-get install vsftpd
sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.original
sudo nano /etc/vsftpd.conf
# key changes
# uncomment local_umask=022
# local_enable=YES
# write_enable=YES
# anonymous_enable=NO
# chroot_local_user=YES
sudo adduser spark-in-me
sudo chown spark-in-me:spark-in-me /var/www/spark-in-me/
sudo usermod -d /var/www/spark-in-me spark-in-me
sudo chmod a-w /var/www/spark-in-me
sudo service vsftpd restart
chown spark-in-me:spark-in-me /var/www/spark-in-me/blog/
chown spark-in-me:spark-in-me /var/www/spark-in-me/admin/
# after this commands vsftpd should start properly working


Настройка файла крона

Ссылки про то, как начать использовать крон 1 2 3 4

# crontab setup and usage
crontab -e
# CRONTAB CONTENTS START HERE
# Borrowed from anacron
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=aveysov@gmail.com
#End borrowed from anacron
# 0 1 * * * /home/snakers41/bash-scripts/WHOLE_SYSTEM_BACKUP.sh
# CRONTAB CONTENTS END HERE


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

# backups
# plain backup script
mkdir /home/bash-scripts
mkdir /media/backups
sudo nano /home/bash-scripts/WHOLE-SYSTEM-BACKUP.sh
# SCRIPT STARTS HERE
# http://help.ubuntu.ru/wiki/backup#восстановление_из_архива
#/bin/bash
#Purpose = Whole system backup
#Created on 25-08-2016
#Last modified on 29-08-2016
#Author = aveysov@gmail.com
#Version 1.1
#START
# This Command will add date in Backup File Name.
TIME=`date +%b-%d-%y`
# Here i define Backup file name format.
FILENAME=test-system-backup-$TIME.tar.gz
# Backed up folder (system root) location
SRCDIR=/
# Destination of backup file
DESDIR=/media/backups
# exclude folder list
EXCLUDE='--exclude=/media/server --exclude=/media/ext_storage --exclude=/media --exclude=/proc --exclude=/lost+found --exclude=/backup.tgz --exclude=/mnt --exclude=/sys'
#  Do not include files on a different filesystem
ONEFSYSPARAM='--one-file-system'
# test command validity
# echo -e tar -cvpzf $DESDIR/$FILENAME $EXCLUDE $ONEFSYSPARAM $SRCDIR
sudo tar -cpzf $DESDIR/$FILENAME $EXCLUDE $ONEFSYSPARAM $SRCDIR
echo "WHOLE_SYSTEM_BACKUP is successful: $(date)" >> /home/bash-scripts/cron_log.log
#END
# SCRIPT ENDS HERE


Настройка почтовых уведомлений через ssmpt

# email alerts set-up - useful for cron
sudo apt-get install ssmtp
# START CONFIG
# Config file for sSMTP sendmail
# The person who gets all mail for userids < 1000
# Make this empty to disable rewriting.
# root=postmaster
root=gmail-addresscom
# The place where the mail goes. The actual machine name is required no
# MX records are consulted. Commonly mailhosts are named mail.domain.com
# mailhub=mail
mailhub=smtp.gmail.com:587
AuthUser=gmail-addresscom
AuthPass=your_pass
UseTLS=YES
UseSTARTTLS=YES
# Where will the mail seem to come from?
rewriteDomain=gmail.com
# The full hostname
# hostname=snakers41-ubuntu
hostname=localhost
# Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
FromLineOverride=YES
# END CONFIG
# IMPORTANT - turn on less secure apps in google account settings
# https://support.google.com/accounts/answer/6010255
# test email
# echo "Test message from Linux server using ssmtp" | sudo ssmtp -vvv destination-email-address@some-domain.com


Конфигурация БД

# database setup

mkdir -p /etc/postgresql/dumps
root@ubuntu-512mb-fra1-01:~# scp -P 8023 snakers41@77.37.164.97:'/media/server/04_BACKUP/localdbrestore/03-20-2017/news.sql' /etc/postgresql/dumps
root@ubuntu-512mb-fra1-01:~# passwd postgres
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
root@ubuntu-512mb-fra1-01:~# su postgres
postgres@ubuntu-512mb-fra1-01:/root$ psql
could not change directory to "/root": Permission denied
psql (9.5.6)
Type "help" for help.
postgres=# create database news
postgres-# ;
CREATE DATABASE
postgres=# create role aveysov login superuser password 'yourpassword';
postgres=# \q
postgres@ubuntu-512mb-fra1-01:/root$ psql news < /etc/postgresql/dumps/news.sql
postgres@ubuntu-512mb-fra1-01:/root$ nano /etc/postgresql/9.5/main/postgresql.conf
#?????? ???????? ??: listen_addresses = *
postgres@ubuntu-512mb-fra1-01:/root$ nano /etc/postgresql/9.5/main/pg_hba.conf  
#?????? ???????? ?????? 
#host news aveysov 0.0.0.0/0 md5
postgres@ubuntu-512mb-fra1-01:/root$ exit
exit
root@ubuntu-512mb-fra1-01:~# service postgresql restart


3. Пример деплоя

Деплой сайта из гита с созданием RSS Sitemap и прочим (rm -rf детектед!)

Доступ к репозиторию делается через SSH-ключ.

# redeploy script
rm -rf /var/www/spark-in-me/blog/*
rm -rf /var/www/spark-in-me/blog/.*
eval $(ssh-agent -s)
ssh-add ~/.ssh/git
cd /var/www/spark-in-me/blog
git clone git@github.com:snakers4/spark-in-me-vds .
sudo yarn install
sudo yarn run build --release
touch /var/www/spark-in-me/blog/build/public/sitemap.xml
touch /var/www/spark-in-me/blog/build/public/main.rss
chmod 777 /var/www/spark-in-me/blog/build/public/sitemap.xml
chmod 777 /var/www/spark-in-me/blog/build/public/robots.txt
chmod 777 /var/www/spark-in-me/blog/build/public/main.rss
echo "# www.robotstxt.org/
Sitemap: http://www.spark-in.me/sitemap.xml
# Allow crawling of all content
User-agent: *
Disallow:" > /var/www/spark-in-me/blog/build/public/robots.txt
php /var/www/spark-in-me/admin/Api/Rss/rssMakerBash.php


После деплоя сервер перезагружается (пока руками) и по крону выполняется незатейливый скрипт старта yarn

#/bin/bash
#Purpose = Start node js webserver via yarn
#Created on 21-03-2017
#Last modified on  21-03-2017
#Author = aveysov@gmail.com
#Version 1.1
#START
# This Command will add date in Backup File Name.
cd  /var/www/spark-in-me/blog/build
yarn start
#END


4. Минимальная безопасность

Я не фанат security through obscurity, но когда в одиночку нужно поддерживать все компоненты сайта, не хочется особо много париться памятую про анекдот про неуловимого Джо. Для безопасности мы используем:

  • Доступ к серверу и репозиторию через SSH ключ;
  • Tar-бекапы и pg_dump бекапы с неизвестной никому машины через SSH ключ;
  • Бекапы через Digital Ocean;
  • Все порты закрыты через ufw, открыты только нужные;
  • Доступ к базе разрешен только из локалхоста и набора фиксированных IP, откуда я вношу правки;
  • АПИ стучится в базу через локалхост, если хоть раз нет коннекта, я получаю email;
  • В php коде соблюдается параноидальное правило: если не коннекта, нет данных или есть ошибка - коннект  закрывается;
  • Ограничений на коннекты почти не стоит, но они не плодятся и все по сути работает на 5-10 коннектах (число одновременных пользователей); 


Вот как-то так. Ваши комментарии будут очень полезны, ибо админство это не самая сильная моя область знаний, но деплой сам себя не сделает!

Статьи цикла

Spark-in.me - как, зачем и почему. Часть 1 - почему?

Spark-in.me - как, зачем и почему. Часть 2 - как? Архитектура приложения и структура БД

Spark-in.me Часть 3 - DIY поддержка и админство сайта

Spark-in.me Часть 4 - Базовое админство для обычных человеков (postgres и не только)

Spark-in.me Часть 5 - переход на HTTPS