Продакшн сервера на Fornex - выбор площадки хостинга и настройка

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


Итак, давайте представим что Вы долго и упорно писали и создавали Ваш проект, но вот весь код написан, автоматические и ручные тесты успешно выполнены, ( пиво выпито, инвестиции заказчика спущены, нужное подчеркнуть) и вот настало время выходить в продакшен!

Однако этот самый продакшен должен быть как-то подготовлен… Если разработку вы вели у себя используя стенд на Vagrant или упаси высший разум на каком-нибудь Denver, то теперь надо как-то где то подготовить для этого площадку, настроить, задеплоить… Интересно? Поехали!


План разворачивания сайта

Итак, краткий план:

  1. Выбор площадки

  2. Развертывание инфраструктуры

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

  4. Подготовка мониторинга

  5. Перенос кода и данных

  6. Настройка DNS

Выбор площадки

Идеальным выбором для размещения своего проекта на мой взгляд является площадка, имеющая свой API, с помощью которого можно автоматизировать управление инфраструктурой - создание серверов например. То есть не сидеть и не тыкать в интерфейсе мышкой а написать один раз скрипт или использовать некоторое специализированное решение. Яркий пример - AWS, Digital Ocean и иже с ними. Там Вам даже не придется ничего придумывать в плане автоматизации.

Дефакто стандартом для управления инфраструктурой в виде кода ( IAAC - Infrastructure As A Code) является Hashicorp Terraform - поэтому просто смотрим, есть ли в списке его плагин - провайдеров поддержка нужного Вам сервиса, либо идем на Github и ищем там ( возможно плагин уже есть просто не вошел в официальный список - я так находил для DNS от GoDaddy.

Плюсы такого решения - Вы тратите от пары часов до дня чтобы разобраться как это работает и написать подходящий код (лучше при этом сразу загуглить best practices), а потом забываете про настройку как про страшный сон и просто добавляете пару строчек кода если надо. Ну и конечно храните это как проект в Git.

Минусы - стоимость вычислительных ресурсов на таких площадках ( сиречь аренда виртуалок) стоит несравнимо дорого по сравнению с теми кто еще не имеет таких функций. Поэтому если у Вас еще не очень большой проект или более того - он учебный, оставим пока эти возможности в голове, так сказать на память, и посмотрим более простое и недорогое решение. Например, площадка хорошего европейского качества, быстрый, качественный провайдер Fornex (да, не лишним будет упомянуть, что выбирая отечественных хостинг провайдеров вы должны учитавать состояние правовой системы и практики ещё применения (а тут бывают промашки), яркий тому пример - недавняя попытка рейдерского захвата датацентра компании IHOR)
(с другой стороны, бывают ситауации, когда без сервера в России не обойтись, но не будет отклоняться от темы) .
Специально для читателей этого сайта Fornex предоставил промокод fkn.ktu10 который дает скидку 10% на покупку всех услуг провайдера. Для активации промокода, нужно зарегистрироваться, выбрать нужную услугу и тариф, а затем нажать на ссылку «у меня есть промо-код».
Отлично. площадку выбрали, займемся инфраструктурой!


Развертывание инфраструктуры

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

На каждый сервер ставим Ubuntu Linux без всяких там надстроек, панелей управления и пр

И не забываем закинуть туда автоматически ssh ключ:

Итак, вот для начала и наша инфраструктура. Для тех, чей проект будет серьезней, советую все же подробней ознакомиться с Terraform!


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

Настраивать сервера вручную, подключаясь к каждому по ssh - это моветон в 2019 году! Во первых, это монотонно и утомительно, во вторых - это плохо масштабируется ( вот надо Вам настроить не 2 сервера а 20 - так же будете команды вбивать?) а в третьих, велик риск получить сервера “снежинки”, конфигурация каждого из которых уникальна, в силу того что каждый раз Вы могли ошибиться или привнести некий “кастом”, просто потому что в процессе работы Вы думали и решили немного изменить сценарий а перенастраивать уже ранее настроенные хосты лень, нет времени, как нибудь потом.

Именно поэтому уже не первый год знающие люди прибегают к использованию специализированных средств управления конфигурацией: puppet, chief, ansible и тд. Про преимущества каждого предлагаю прочитать соответствующие статьи в интернете, коих много. В рамках данного руководства предлагаю использовать Ansible. И нет, не потому что стильно, модно, молодежно ( нужное подчеркнуть), а потому что очень простой yaml декларативный синтаксис, отсутствие необходимости ставить агенты, доступ через ssh (то есть сразу из коробки да еще и безопасно), плюс есть возможность просто одновременно выполнять команды на хостах - эдакий parallel ssh.

Если Вас это заинтересовало, вот тут по ссылке хорошее руководство.

В нашем случае все будет предельно просто… Мы лишь хотим вначале проверить возможность “достучаться” до серверов с помощью Ansible а потом с его помощью поставить часть пакетов и сконфигурировать. Для начала напишем тн “inventory” файл, который будет в себя включать информацию о самих хостах - как до них достучаться и т.д. Выглядит он следующим образом (обзовем его hosts.yml):

И опишем заготовку тн “playbook” файла - то есть нашего сценария, в котором будут располагаться задачи:

Теперь с помощью командной строки проверим что сценарий отработает - это покажет нам что синтаксис верен, хосты доступны, ssh ключ подходит и тп:

Отлично! До хостов мы достучались, можно и сконфигурировать! Итак, что мы хотим:

  1. Поставить ряд пакетов с дополнительными утилитами по списку,

  2. Установить веб сервер ( пусть в одном случае это будет Apache, а в другом Nginx)

  3. СУБД ( Конечно же Mysql)

  4. И настроить фаервол ( откроем 22, 80, 443 и еще 10050-10051 это порты системы мониторинга но об этом позже)


Приступаем к написанию! По итогу у нас получается следующий playbook ( Довольно длинный):


---

- hosts: all

gather_facts: yes

tasks:

- name: Install a list of packages

apt:

name: "{{ packages }}"

vars:

packages: "{{ packages_list }}"


- name: Install apache

apt:

name: apache2

when: inventory_hostname == "infrastructure"


- name: Install nginx

apt:

name: nginx

when: inventory_hostname == "service"


- name: DB | Specify MySQL root password before installing

debconf:

name: 'mysql-server'

question: 'mysql-server/root_password'

value: '{{ db_root_password | quote}}'

vtype: 'password'

become: true


- name: DB | Confirm MySQL root password before installing

debconf:

name: 'mysql-server'

question: 'mysql-server/root_password_again'

value: '{{ db_root_password | quote}}'

vtype: 'password'

become: true


- name: DB | Install MySQL server

apt:

name: "{{ db_packet_install_list }}"

state: present


- name: DB | Create a new database

mysql_db:

name: "{{ database_name }}"

state: present

encoding: utf8

login_host: localhost

login_port: "{{ db_port }}"

login_user: "{{ db_root }}"

login_password: "{{ db_root_password }}"


- name: DB | Create user and grant privileges for database

mysql_user:

name: "dbadmin"

host: "localhost"

password: "{{ db_non_root_password }}"

priv: '{{ database_name }}.*:ALL'

state: present

login_host: localhost

login_port: "{{ db_port }}"

login_user: "{{ db_root }}"

login_password: "{{ db_root_password }}"


- name: Firewall | Allow access to list of ports

ufw:

rule: allow

port: "{{ item }}"

proto: tcp

with_items:

- "{{ firewall_open_ports }}"


- name: Firewall | Enable UFW and set default policy to Deny

ufw:

state: enabled

policy: deny



Он довольно простой - лишь ставит пакеты да что-то немного конфигурирует. На самом деле, с помощью Ansible можно сделать куда больше! Но у нас же учебный пример, ведь так? Иначе мне придется переписать все руководство… Если Вы заметили - в некоторых местах стоят не значения а переменные, в фигурных скобках. Их значения я вынес в файл inventory:


all:

hosts:

service:

ansible_host: 5.187.7.24

infrastructure:

ansible_host: 5.187.4.37

vars:

ansible_port: 22

ansible_connection: ssh

ansible_user: root

ansible_ssh_private_key_file: ~/.ssh/your_ssh_key

ansible_python_interpreter: /usr/bin/python3

db_root: root

db_port: 3306

db_packet_install_list:

- mysql-client

- mysql-common

- mysql-server

database_name: "test"

db_root_password: "Qwerty.123"

db_non_root_password: "Test!123"

firewall_open_ports:

- 80

- 443

- 22

- 10050

- 10051

packages_list:

- vim

- mc

- git

- htop

- iotop

- sysstat

- tmux

- molly-guard

- software-properties-common

- host

- git

- mtr

- ufw

- tree

- ntp

- python3-pymysql

- debconf-utils


Пароль так хранить кстати не стоит - познакомьтесь с такой штукой как Ansible vault.

Итак, попробуем запустить и посмотрим, каков же будет результат:

Отлично, ansible прошелся по всем нашим серверам (коих пока только 2 но можно и 20 и 100) и все настроил! А теперь посмотрите на текст плейбука и прикиньте, сколько бы вы все это писали на bash, с проверкам иусловий и обработкой ошибок….


Подготовка мониторинга

Отлично, мы настроили сервера, пришло время подключить мониторинг.

В качестве учебного примера мы возьмем не модные сейчас TICK и TIG связки а классический Zabbix, тем более что для малого проекта его нам должно хватить за глаза (Плюсы для новичков - отличная русскоязычная документация и возможность интегрировать его с кучей решений за счет наработок сообщества).

Для установки самого zabbix мы будем использовать машину под именем infrastructure, а наблюдать он будет за машиной service ( ну и за другими если таковые найдутся в Вашем проекте). Помимо наблюдения за доступностью самой ОС, можно подключить наблюдение за любым сервисом - с помощью встроенных или кастомных шаблонов ( вы можете написать их сами или найти в интернете).

Установку было бы неплохо автоматизировать при помощи Ansible, но во первых такой playbook уже есть, а во вторых не будем концентрироваться на установке ибо статей и инструкций в интернете вагон ( не считая официальной документации откуда и надо это все брать).

Итак, сервер поставили, агент на другой сервер тоже поставили, весь конфиг там будет вот такой:

По сути нам нужно поменять 2 директивы - имя хоста ( должно быть уникальным) и IP адрес сервера zabbix, те. адрес с которого мы разрешаем делать запросы. Сохраняем, перезапускаем, идем в веб интерфейс zabbix:

Configuration - Hosts - create host - добавляем наш узел:

Навешиваем ему несколько стандартных шаблонов:

И гордо жмем кнопку ADD! В списке хостов у нас будет их уже два - первый это сам zabbix сервер, второй это наш хост. Ждем пока он приобретет зеленый статус в колонке “Availability”

И Можно начинать любоваться данными которые потекли к нам рекой (Monitoring - latest data)

А можно и графиками

Отлично, телеметрия у нас есть. Но zabbix нам нужен и для алертинга - предупреждения на случай если что то пошло не так. По умолчанию он умеет отправлять уведомления по эл почте, но при желании можно научить его слать и в telegram, и в slack.

Если же Вы решили что для Вашего проекта такая схема это перебор ( ну действительно учебный или тестовый), тот же Fornex “из корбки” предоставляет простой мониторинг со сбором стандартных метрик с виртуальных машин- процессор, память, диск, сеть:

Так, с мониторингом закончили, идем дальше!

Перенос кода и данных

Итак, пришло время “задеплоить” Ваш проект - то есть развернуть его в рабочей среде для дальнейшей эксплуатации.

Рассмотрим 2 сценария:

  1. Вы вели разработку локально, не пользуясь никакой системой контроля версий. Накодили и готово. В таком случае достаточно собрать Ваш проект в один архив, загрузить на удаленный сервер, разархивировать там в нужную папку, выставить права доступа и готово! Плюс такого решения - простота, минус- есть шанс потерять наработки в процессе из за ошибок. В этом месте хочу дать ровно 1 совет - выкиньте уже в топку FTP протокол. Есть же SFTP и SCP которые вы получаете “из коробки”, установив ssh сервер на свой vds ( а он обычно и так установлен)/

  2. Вы использовали.. скажем GIT! Отлично! Тогда заходим на Ваш гит сервер, получаем там ссылку для скачивания архива актуальной версии или делаем “git clone” на машине с последующим очищением каталога от метаданных git-а ( удалить скрытую папку git)... И выставляем права. Звучит уже больше как CI/CD процесс - например если Вы пользователь GitLab можете настроить себе такой pipeline - чтобы он логинился на сервер, и делал за вас все три этих действия ( клон, очистка, выставление прав). В итоге вы будете получать код по нажатию кнопки.


Но как быть, если в сценарии 2 если вы хотите попробовать развернуть разные версии? То есть действительно реализовать что то близкое к настоящему CI/CD ? Ведь вы не сможете клонировать постоянно все в одну и ту же папку… Очень просто - при клонировании, укажите как назвать создаваемый в результате каталог, прибавляя к его имени скажем… временную отметку.. И создавайте на него некоторый чистый “симлинк”... Давайте объясню на примере:

  1. Пусть Ваш веб-сервер ожидает увидеть код сайта и все сопутствующие файлы в каталоге “/var/www/sitename”.

  2. Делая git clone, просим клонировать его содержимое репозитория в папку “/var/www/sitename_date_time_1”

  3. Делаем симлинк “/var/www/sitename”, который указывает на “/var/www/sitename_date_time_1”

  4. Удаляем из папки “/var/www/sitename_date_time_1” подпапку “.git” и выставляем корректные права.

  5. Перезапускаем веб сервер

  6. В случае повторения операции, при клонировании будет создана папка “/var/www/sitename_date_time_2” ( дата и время будет отличаться)

  7. Симлинк будет переключен на новую папку, права изменены, так же перезапускаем веб сервер.

Бонусом вы получите легкую возможность “откатиться” на предыдущую версию сайта за пару команд! ( Вернуть симлинк, перезапустить веб сервер).


Если gitlab не является Вашим хостингом, можете на второй, инфраструктурной машине развернуть jenkins или любое другое ПО для реализации CI/CD процесса которое будет Вам по душе.

Отлично, код мы перенесли и даже научились его непрерывно доставлять, как это модно сейчас… Осталось перетащить еще и данные. И тут я имею ввиду не контент типа картинок и прочих статических файлов - для них сценарий аналогичен сценарию 1 - грузите их архивом по scp/sftp… Кстати если Вы хотите выкатывать код по сценарию 2, каталог с статическими файлами типа картинок и документов лучше держать отдельно от кода и ни в коем случае не работать с ним с помощью git!

Так вот, сейчас я хотел поговорить о переносе содержимого базы данных. Для каждого движка СУБД есть свои рекомендуемые утилиты для создания и восстановления резервной копии - mysqldump для Mysql, pg_dump для PostgreSQL, mongodump для MongoDB и тд… Вполне возможно что они подойдут Вам в этом сценарии, однако стоит упомянуть про то, что в большинстве случаев процесс дампинга базы данных приводит к блокировке ( тн локу) таблиц на момент создания дампов - то есть у Вас нет возможности в этот момент ничего в базу писать. В случае учебного проекта или небольшого сайта это не проблема от слова совсем, а вот в случае когда у Вас клиенты на сайте в режиме 24/7 это может быть проблема! И тут есть два выхода:

  1. использование более гибких решений

  2. перенос данных за счет репликации

По п.1 - нужно просто найти подходящий инструмент, ознакомиться с тем как он работает, потестить ( убедиться что это то, что надо) и применять! Например для Mysql есть замечательная утилита Xtrabackup.

По п.2 - этот вариант очень хорошо подходит если Вам необходимо иметь минимальный временной разрыв с т.з. актуальности данных между Вашими площадками ( предположим что Вы занимаетесь не развертыванием сайта после разработки а переносите решение с хостинга А - скажем какого нибудь ruvds или timeweb на хостинг В - в тот же Fornex). Тогда этот вариант предпочтительнее - Вы просто настраиваете на новом месте свежеустановленный экземпляр СУБД в качестве реплика- слейва ( то есть тот кто реплицирует а не тот с кого…) и ждете пока он скачает все данные… Останется только переключить его обратно в нормальный режим когда закончите перенос! Вот примеры для MySQL и PostgreSQL.

Настройка DNS

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

Самый простой шаг - зайти в консоль Вашего регистратора DNS и создать там запись типа А, которая направила бы основное имя вашего домена ( например abcdefg.com) на IP адрес сервера с сайтом.

По хорошему, рекомендуется сделать для его выделенное имя в Вашем домене, например www.abcdefg.com, а abcdefg.com уже направить на это имя с помощью записи типа CNAME.


Еще более удобное и интересное решение- не использовать консоль Вашего регистратора ( в большинстве случаев они увы убогие и неудобные), а делегировать управление Вашим доменом кому нибудь - у любого хостера есть такая услуга, в том числе и у FORNEX. После этого управлять своим доменом ( совсем правильно говорить “своей зоной DNS”) через его консоль. Давайте например я покажу это на примере “abcdefg.com” ( так как я лишь демонстрирую порядок действий, я не буду проводить всю процедуру целиком тк не владею этим доменом. Полный порядок описан в документации по ссылке выше).


Вначале мы добавляем нужный нам домен

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


После добавления зайдем в настройки



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

Однако я бы рекомендовал поправить их вот так

Чтобы в будущем, при очередном переезде Вам пришлось отредактировать на 1 запись меньше.


Успехов Вам и высокого аптайма!