Устанавливаем Debian buster на сервер, часть вторая

debianeach

Опубликован:  2019-08-13T05:20:51.615051Z
Отредактирован:  2019-08-13T05:20:19.858156Z
Во второй части демонстрации рассмотрены вопросы подключения к серверу по ssh, установка специализированного серверного программного обеспечения, настройка и развёртывание web-приложения, настройка web-сервера и необходимых ему системных служб. В результате описанных действий в локальной виртуальной сети начнёт функционировать сайт на основе выбранного web-приложения.

1. В предыдущей серии

Первая часть описания доступна в предыдущем выпуске этого блога, который находится по этой ссылке, в ней я создал виртуальный сервер и установил на него базовую систему Debian buster. Далее мне необходимо развернуть и настроить на этом виртуальном сервере web-приложение - основу сайта. Этим я и займусь в этой части изложения.

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

2. Настраиваем ssh-сервер

Когда базовая операционная система на сервер установлена, сеть настроена, а виртуальная машина включена и слушает сеть, можно попробовать попинговать заданный для сервера IP-адрес по заданному доменному имени. Открываю терминал на своём десктопе и ввожу команду.

ping -c 3 auriz.net

MPVW9HwLLr.png

Как видно на снимке экрана, сервер откликается. Управлять сервером можно удалённо по сети, для этого на виртуальной машине установлен ssh-сервер. Сейчас я буду его настраивать.

Замечание: необходимо обратить внимание, как выглядит приглашение командной строки терминала моего десктопа, оно содержит имя пользователя - newbie, и имя компьютера - desktop.

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

ssh-keygen -t ed25519

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

1ub7KG61TU.png

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

Для подключения к ssh-серверу по ключу необходимо скопировать публичную часть ключа на сервер, к которому запланировано подключение по этому ключу.

ssh-copy-id -i ~/.ssh/aur.pub sadmin@auriz.net

Программа копирования ключа в интерактивном режиме запрашивает подтверждение, а затем ввод пароля. Необходимо ввести пароль зарегистрированного на сервере пользователя - sadmin.

iqAEJkdPBA.png

Подключаюсь к серверу.

ssh sadmin@auriz.net

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

U0KDq1DEB0.png

Следует обратить внимание, что после подключения приглашение командной строки изменило свой вид, теперь в нём указано имя пользователя - sadmin, и имя компьютера - server, теперь этот терминал управляет сервером, а все введённые в нём команды выполняются на сервере. Открываю в текстовом редакторе Nano файл /etc/ssh/sshd_config, этот файл управляет настройками демона ssh.

GBL0uZPiv0.png

В этом файле я изменю несколько ключевых параметров:

Port 2271
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no

Сохраняю файл и покидаю текстовый редактор. Чтобы изменения вступили в силу делаю рестарт сервиса.

sudo systemctl restart sshd.service

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

ssh sadmin@auriz.net -p 2271 -i ~/.ssh/aur

3ROjXnvPdA.png

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

Важное замечание: при развёртывании сервера на VDS в сети Интернет я столкнулся с довольно странным поведением ssh-сервера, он запускался не сразу, - это известный баг, лечится установкой пакета haveged, воспроизводится не везде, следует иметь ввиду.

sudo apt install -y haveged

cjdf2jj7vS.png

SSH-сервер настроен, и теперь у меня на десктопе есть терминал, который управляет сервером, в этом терминале я и буду осуществлять настройку web-сервера и необходимых моему web-приложению служб.

3. Устанавливаем программное обеспечение

Для правильного и эффективного обустройства моего сервера понадобятся дополнительные пакеты и программы из иных источников, которые я сейчас и буду поэтапно устанавливать.

Сначала установлю Git и Unzip, первый необходим, чтобы получить код web-приложения с git-сервера, а второй - для распаковки некоторых статических библиотек.

sudo apt install -y git unzip

06A9fICQE3.png

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

mkdir -p workspace/auriz.net
cd workspace/auriz.net

7JCeg0PA6z.png

Клонирую git-репозиторий своего приложения.

git clone https://github.com/newbie-c/auriz.git

qx7uvQcp49.png

Замечание: приложение, которое я сейчас буду разворачивать на сервер, хранится в приватной репе, поэтому описанные здесь действия можно повторить только имея доступ к этому репозиторию, если обе части этого описания получат по 5000 лайков на сайте auriz.ru, я буду готов открыть доступ к коду. Чтобы поставить "лайк/дизлайк" необходима регистрация.

Вхожу в каталог приложения auriz и устанавливаю все перечисленные в соответствующем файле проекта пакеты.

cd auriz/
sudo apt install -y $(cat deployment/deps/dependencies.txt)

5XYjZrlLRP.png

Опять дожидаюсь полного завершения процесса установки всех запрошенных пакетов. После этого добавляю текущего пользователя sadmin в группы adm и redis.

sudo usermod -a -G adm,redis sadmin

MHQNsP9RNf.png

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

PQh3WGhpa6.png

Прогресс. Ещё чуточку терпения, сейчас всё будет...

4. Настраиваем PostgreSQL

В рамках этой демонстрации я покажу только минимально-необходимые для работы приложения настройки сервера PostgreSQL, полная настройка этой службы не вписывается в цели данной демонстрации, поэтому будет опущена.

На сервере PostgreSQL мне необходимо создать пользователя, для этого вхожу с помощью sudo как пользователь postgres.

sudo -i -u postgres

И создаю новую роль с именем sadmin.

createuser sadmin -P -d --interactive

После создания новой роли нужно покинуть сессию пользователя postgres.

exit

puidj4KZoz.png

Теперь от имени пользователя sadmin я могу создать базу данных для своего приложения.

createdb auriz

Так как в операционной системе моего сервера настроена русскоязычная локаль ru_RU.UTF-8, все созданные базы данных в дефолте будут иметь такую же локаль, что и требуется. Проверить локаль созданной базы данных можно подключившись к базе в клиенте psql.

psql -d auriz

Просмотреть список баз данных можно командой \l, а покинуть клиент командой \q.

ygacF74yw2.png

Предпринятых действий достаточно, чтобы подключить web-приложение к серверу PostgreSQL. Тонкую настройку сервера в этом описании я рассматривать не буду, так как это тема для отдельной большой статьи.

5. Восстанавливаем окружение web-приложения

Приложение auriz разработано и отлажено на языке программирования Python3, и для его правильного функционирования на сервере мне необходимо правильно восстановить виртуальное окружение. Создаю новое виртуальное окружение.

python3 -m venv ../venv

Следует обратить внимание, что созданное виртуальное окружение будет храниться в родительском каталоге ~/workspace/auriz.net/. Сразу после создания нужно это виртуальное окружение активировать.

source ../venv/bin/activate

026OS3p1sg.png

Ещё один важный момент: при активации виртуального окружения приглашение командной строки меняет свой вид и получает префикс с именем активного виртуального окружения. Устанавливать пакеты можно только когда виртуальное окружение активно. Обновляю это виртуальное окружение.

pip install --upgrade wheel

JjjDllPVQt.png

Теперь мне необходимо установить в это виртуальное окружение все зависимости приложения. Начинаю с файла requirements-first.txt.

pip install -r requirements-first.txt

Jn0YmjhaF4.png

Следующим этапом устанавливаю пакеты из файла requirements.txt, последовательность имеет значение.

pip install -r requirements.txt

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

JWMKq7STKw.png

Для полного и безусловного счастья мне понадобится ещё пара модулей: gunicorn и gevent. Устанавливаю их.

pip install gunicorn gevent

J499AIJZiO.png

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

6. Восстанавливаем конфигурацию web-приложения

Конфигурация web-приложения auriz хранится в файле config.py - в начальном состоянии репозитория этот файл отсутствует, но его можно восстановить из шаблона.

cp config.template.py config.py

Теперь файл конфигурации можно открыть в текстовом редакторе Nano и отредактировать. Вот содержание моего файла конфигурации:

import os

from datetime import timedelta

name = 'айтишные Блоги auriz.net'
about = """{0} - сервис для ведения тематических IT-блогов и
обсуждения затронутых тем с гостями сайта в общих комментариях или
приватно.""".format(name).replace('\n', ' ')


class Config:
    SECRET_KEY = 'v9G3JqaVVHFox4s@h^d)+F4bgbCk1E!leo4*KBQ)DQ&*M%mFB+'
    SITE_NAME = name
    SITE_DESCRIPTION = about
    PERMANENT_SESSION_LIFETIME = timedelta(hours=2)
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
    CELERY_BROCKER_URL = 'redis://localhost:6379/0'
    CELERY_IMPORTS = ('auriz.tasks',)
    CELERY_TASK_RESULT_EXPIRES = timedelta(hours=1)
    MAIL_SERVER = 'smtp.yandex.ru'
    MAIL_PORT = 465
    MAIL_USE_SSL = True
    MAIL_USERNAME = 'noreply@auriz.ru'
    MAIL_PASSWORD = '###############'
    AURIZ_SUBJECT_PREFIX = '[Auriz] '
    AURIZ_SENDER = 'robot <noreply@auriz.ru>'
    TOKEN_LENGTH = 12
    REQUEST_INTERVAL = 24
    MAX_CONTENT_LENGTH = 5 * pow(1024, 2)

    @staticmethod
    def init_app(app):
        pass


class Development(Config):
    DEBUG = True
    ASSETS_DEBUG = True
    SEND_FILE_MAX_AGE_DEFAULT = 0
    SESSION_PROTECTION = None
    CACHE_HOST = '192.168.56.102'
    # WTF_CSRF_TIME_LIMIT = 10
    MAIL_DEBUG = True
    MAIL_SUPPRESS_SEND = True
    TOKEN_LENGTH = 0.1
    REQUEST_INTERVAL = 0.12
    SQLALCHEMY_DATABASE_URI = \
        'postgresql+psycopg2://{0}:{1}@192.168.56.102/auriz_dev'.format(
            os.getenv('SDBU', 'sadmin'),
            os.getenv('SDBP', 'aa'))


class Testing(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = \
        'postgresql+psycopg2://{0}:{1}@192.168.56.102/auriz_test'.format(
            os.getenv('SDBU', 'sadmin'),
            os.getenv('SDBP', 'aa'))


class Production(Config):
    SEND_FILE_MAX_AGE_DEFAULT = int(timedelta(days=28).total_seconds())
    USERS_PER_PAGE = 30
    LINKS_PER_PAGE = 30
    LABELS_PER_PAGE = 30
    ALBUMS_PER_PAGE = 10
    POSTS_PER_PAGE = 30
    COMMENTS_PER_PAGE = 25
    ANNOUNCEMENTS_PER_PAGE = 10
    FRIENDS_PER_PAGE = 10
    PM_PER_PAGE = 30
    SQLALCHEMY_DATABASE_URI = \
        'postgresql+psycopg2://sadmin:#######@localhost/auriz'


config = {'development': Development,
          'testing': Testing,
          'production': Production,
          'default': Production}

Все параметры спецификации я описывать не буду, важно обратить внимание на свойство SECRET_KEY класса Config, соответствующие свойства отвечающие за сервис отправки регистрационных писем, а также свойство SQLALCHEMY_DATABASE_URI класса Production, в нём я вписал параметры созданной для приложения базы данных. Кроме этого в словаре config в ключе default я вписал класс Production.

Когда файл конфигурации восстановлен и сохранён, нужно обновить базу данных.

python manage.py db upgrade

JOwjuciGBW.png

Создаю администратора сайта.

python manage.py create_root

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

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

python manage.py shell

И воспроизвести в этой консоли вызов метода insert_data соответствующего класса.

>>> Captcha.insert_data(1000)
>>> exit()

Процесс займёт некоторое время, необходимо дождаться его завершения, а затем выйти из консоли вызовом exit().

OSKUjOtKv0.png

С этого момента виртуальное окружение приложения можно деактивировать.

deactivate

Распаковываю статические файлы вендоров.

unzip -d auriz/static deployment static-vendor.zip

cpSDGK6Nta.png

Создаю каталог для минимизированных статических файлов таблиц стилей и JS-скриптов.

mkdir auriz/static/generic

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

ln -s -T /home/sadmin/workspace/auriz.net/auriz/auriz/static/vendor/bootstrap-3.4.1/fonts/ /home/sadmin/workspace/auriz.net/auriz/auriz/static/generic/fonts
ln -s -T /home/sadmin/workspace/auriz.net/auriz/auriz/static/ /home/sadmin/workspace/auriz.net/static

Bof8SGXYQM.png

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

7. Настраиваем системные службы сервера

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

Приложению необходим домашний каталог для временных файлов, его я размещу в системном каталоге /run/, кроме этого приложение будет собирать логи с двух своих системных служб, для логов создаю в системном каталоге /var/log/ два каталога с именами соответствующих служб, для этого открываю в редакторе Nano файл /etc/tmpfiles.d/workspace.conf.

sudo nano /etc/tmpfiles.d/workspace.conf

Пишу в этом файле три строчки.

d /run/auriz.net 0755 sadmin sadmin -
d /var/log/gunicorn 0750 sadmin sadmin -
d /var/log/celery 0750 sadmin sadmin -

Wh7pESUgQS.png

Сохраняю изменения и покидаю текстовый редактор, в итоге, при первой перезагрузке сервера все необходимые системе каталоги будут созданы автоматически. Поскольку в системе появятся две службы, собирающие логи, необходимо обеспечить ротацию логов этих системных служб. Открываю в текстовом редакторе Nano файл /etc/logrotate.d/auriz.net.

sudo nano /etc/logrotate.d/auriz.net

И даю этому файлу следующее содержание.

/var/log/gunicorn/*.log {
    daily
    missingok
    rotate 20
    size 256k
    compress
    delaycompress
    notifempty
    create 0640 sadmin sadmin
}

/var/log/celery/*.log {
    daily
    missingok
    rotate 20
    size 256k
    compress
    delaycompress
    notifempty
    create 0640 sadmin sadmin
}

QAhwJmrxpM.png

Сохраняю изменения и покидаю текстовый редактор.

Web-сервер Nginx, который уже установлен в системе, тоже собирает логи, следует слегка подкорректировать ротацию логов и для него. Открываю файл /etc/logrotate.d/nginx.

sudo nano /etc/logrotate.d/nginx

И привожу его к следующему виду.

/var/log/nginx/*.log {
    daily
    missingok
    rotate 365
    size 256k
    maxsize 512k
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    prerotate
        if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
            run-parts /etc/logrotate.d/httpd-prerotate; \
        fi \
    endscript
    postrotate
        invoke-rc.d nginx rotate >/dev/null 2>&1
    endscript
}

kd8b9P9sDy.png

Сохраняю изменения в файл и покидаю текстовый редактор.

Теперь необходимо настроить сервер Nginx. Мне нужно, чтобы сервер слушал запросы и правильно выдавал ответы на трёх различных адресах. Первый адрес - это доменное имя auriz.net. Второй адрес - это алиас доменного имени www.auriz.net. И третий адрес - это собственно IP-адрес сервера, прямые запросы на этот адрес мне не нужны, поэтому я их заблокирую и настрою Nginx так, чтобы он выдавал 444. Начнём с первого адреса. Открываю файл /etc/sites-available/auriz.net

sudo nano /etc/sites-available/auriz.net

И пишу в этот файл следующее:

server {
    listen 80;

    server_name auriz.net;
    client_max_body_size 8M;

    location /static {
        alias /home/sadmin/workspace/auriz.net/static;
        expires 2419200;
        add_header Cache-Control "public";
    }

    location / {
        proxy_set_header Host $host;
        proxy_pass http://unix:/run/auriz.net/auriz.net.socket;
    }
}

dAuyXii5Z7.png

Это минимально-необходимая для auriz конфигурация Nginx, по ней видно, что web сервер будет слушать и транслировать заданный сокет, который хранится в созданном только что системном каталоге в /run/.

Для второго ключевого адреса создаю ещё один файл.

sudo nano /etc/nginx/sites-available/www.auriz.net
server {
    server_name www.auriz.net;
    return 301 $scheme://auriz.net$request_uri;
}

hjl57SeU1C.png

Все запросы по этому адресу Nginx будет переадресовывать на основной домен сайта. Сохраняю изменения в файл.

Редактирую ещё один файл, который будет отвечать за ответы Nginx на запросы отправленные по IP-адресу сервера.

sudo nano /etc/nginx/sites-available/default
server {
  listen 80 default_server;
  listen [::]:80 default_server;
  server_name "";
  return 444;
}

O27K504e5y.png

Чтобы Nginx начал слушать заданные адреса: auriz.net и www.auriz.net, необходимо создать две символические ссылки.

sudo ln -s -T /etc/nginx/sites-available/auriz.net /etc/nginx/sites-enabled/auriz.net
sudo ln -s -T /etc/nginx/sites-available/www.auriz.net /etc/nginx/sites-enabled/www.auriz.net

MinzJAg6Cu.png

Последний штрих для web-сервера - настройка механизмов сжатия. Открываю файл /etc/nginx/nginx.conf.

sudo nano /etc/nginx/nginx.conf

Нахожу в нём соответствующую секцию и привожу её к следующему виду:

iUiy7JdAqN.png

Все настройки будут применены автоматически при перезагрузке сервера.

Перехожу к настройке служб для очереди задач и wsgi-сервера. Обе службы должны запускаться автоматически при загрузке сервера. Для каждой из них я создам соответствующий юнит systemd.

Открываю в текстовом редакторе файл /etc/systemd/system/celery-auriz.net.service.

sudo nano /etc/systemd/system/celery-auriz.net.service

Содержание этого юнита.

[Unit]
Description=celery worker for auriz.net application
After=network.target
Requires=redis-server.service
After=redis-server.service

[Service]
User=sadmin
Group=sadmin
WorkingDirectory=/home/sadmin/workspace/auriz.net/auriz
ExecStart=/home/sadmin/workspace/auriz.net/venv/bin/celery worker \
           -A celery_worker.celery -l info \
           --max-tasks-per-child 100 \
           --logfile=/var/log/celery/worker.log \
           --pidfile=/run/auriz.net/celery.pid
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
WantedBy=gunicorn-auriz.net.service

5NQH227ASY.png

Сохраняю файл и покидаю текстовый редактор.

Второй юнит будет отвечать за запуск и настройки wsgi-сервера.

sudo nano /etc/systemd/system/gunicorn-auriz.net/service

Содержание этого юнита таково.

[Unit]
Description=gunicorn instance to serve auriz.net
After=network.target
Requires=celery-auriz.net.service
After=celery-auriz.net.service

[Service]
User=sadmin
Group=sadmin
WorkingDirectory=/home/sadmin/workspace/auriz.net/auriz
ExecStart=/home/sadmin/workspace/auriz.net/venv/bin/gunicorn \
           --workers 3 -k gevent \
           --worker-tmp-dir /run/auriz.net/ \
           -p /run/auriz.net/gunicorn.pid \
           --access-logfile /var/log/gunicorn/access.log \
           --error-logfile /var/log/gunicorn/error.log \
           --bind unix:/run/auriz.net/auriz.net.socket celery_worker:app

[Install]
WantedBy=multi-user.target

Hg8C7n3Bci.png

Включаю обе службы в систему.

sudo systemctl enable celery-auriz.net.service
sudo systemctl enable gunicorn-auriz.net.service

8ajryXjF4L.png

Перезагружаю сервер.

sudo systemctl reboot

pJyiauvCg2.png

Настройка системы завершена, если всё сделано верно, и в системных конфигах не допущено ошибок, то после полной загрузки операционной системы сайт auriz.net можно посетить в браузере.

jqxHPFlDUU.png

8. Настраиваем сайт

Если настройка сервера прошла успешно, и сайт стал доступен для посещений, следует настроить сайт в браузере. Для этого нужно войти системным администратором, который был создан на этапе конфигурации auriz.

z47Zvy1T8M.png

В главном меню сайта найти пункт "Сервис".

t9ecqZvaeV.png

В этом меню есть параграф "Разрешения", в нём можно настроить разрешения для всех регистрирующихся пользователей, по умолчанию все вновь регистрируемые получат группу "Блогеры", эту настройку можно изменить.

Кроме этого необходимо создать файл для поисковых роботов, для этого в меню "Сервис" имеется соответствующий параграф. Стартовую страницу сайта можно заполнить после перехода по ссылке index.html.

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

Комментарии:

newbie_

2019-08-13T05:50:31.206029Z

Поставил лайк... Я за публикацию кода в открытый доступ. Разверну свой сайт и буду вести свой блог там, а от тебя уйду...

debianeach

2019-08-13T11:48:13.228459Z

Не уйдёшь. 5000 лайков - цифра практически нереальная в текущем положении дел и посещаемости сайта.

newbie_

2019-08-14T09:06:19.867061Z

Наверно да... Помечтать не даёшь. (((