- PVSM.RU - https://www.pvsm.ru -
Примечание: это перевод моего поста [1] (англ.), описывающий реализацию сервера комментариев, используемую на том же сайте, где находится оригинал.
TL;DR-версия: я разработал конфигурацию Commento-сервера, которая легко и просто развёртывается в полуавтоматическом режиме. Скопируйте себе этот репозиторий [2] с GitHub и следуйте инструкциям в README [3].
Некоторое время назад мне неудержимо захотелось сменить Disqus — который является, пожалуй, самой распространённой системой для добавления комментариев к страницам — на свободный и открытый Commento [4].
Проблема Disqus, как и многих других «бесплатных» продуктов, в том, что продуктом в данном случае является пользователь — то есть вы. Помимо этого, Disqus «обогащает» каждую страницу, где он используется, мегабайтами скриптов и более чем сотней дополнительных HTTP-запросов.
Плюс к этому, бесплатная его версия показывает рекламу, от которой можно откупиться «всего лишь» за 9 долларов в месяц (план Plus). Уже только этого достаточно, чтобы захотелось найти что-нибудь получше.
В какой-то момент я наткнулся на этот пост [5] и узнал о существовании свободного сервера комментариев под названием Commento [4]. По счастливому совпадению, Commento как раз не так давно стал полностью открытым — раньше он выпускался в двух вариантах, бесплатном Community и коммерческом Enterprise. Спасибо его разработчику Adhityaa Chandrasekar.
Commento на порядки эффективнее Disqus, типичный размер дополнительной загрузки с ним около 11 КБ, плюс сами комментарии, разумеется. Примерно такая же ситуация и с требуемыми HTTP-запросами.
Ещё один плюс сервера Commento в том, что он очень быстрый, так как написан на Go.
Ну и, в качестве вишенки на торте, у него есть импорт комментариев из Disqus, о чём ещё мечтать?
Для непродвинутых (в техническом плане) пользователей, у Commento есть готовый к использованию облачный сервис на commento.io [4]. Размер ежемесячной платы автор предлагает вам выбрать самостоятельно, но она не может быть меньше $3 «по техническим причинам».
Мистер Чандрасекар также великодушно предлагает бесплатный аккаунт на Commento.io в обмен на «нетривиальные патчи» к продукту.
Ну, а я выбрал третий вариант: поднять сервер Commento самостоятельно. В этом случае ты ни от кого не зависишь (помимо хостера, конечно), а я люблю независимость.
Я большой поклонник Docker-контейнеров и также часто использую Docker Compose [6], инструмент для управления группами нескольких связанных контейнеров. А у Commento есть готовый к употреблению Docker-образ в GitLab container registry.
Поэтому решение применить контейнеры созрело само собой — но сначала предстояло решить несколько моментов.
Commento требует сервера PostgreSQL довольно свежей версии, никакие другие SQL-серверы, к сожалению, не поддерживаются.
Ну ладно, мы всё равно всё запускаем в контейнерах, так что тут довольно просто.
Commento сам по себе является веб-сервером, но он поддерживает лишь незащищённый протокол HTTP.
Тут надо отметить, что такая практика в наши дни довольно распространена: сервер в данном случае прячут за обратным прокси [7], который также выполняет SSL offloading. Штука в том, что поддержка SSL/HTTPS в данном случае совершенно обязательна, в конце концов на дворе 2019 год и на попытки авторизовать пользователя с помощью незащищенного Интернет-протокола будут смотреть очень косо.
Я решил использовать сервер Nginx [8], во-первых, у меня был немалый опыт работы с ним, а во-вторых, он очень быстр, экономичен и стабилен. И публикует официальные сборки Docker-образов [9].
Вторым ингредиентом в рецепте HTTPS является SSL-сертификат для домена. Я бесконечно признателен EFF и Mozilla за то, что они создали центр сертификации Let's Encrypt [10], ежемесячно выдающий миллионы бесплатных сертификатов.
Let's Encrypt также предоставляет свободную утилиту командной строки под названием certbot [11], сильно упрощающую процесс получения и обновления сертификата. Ну и — разумеется — Docker-образ для него!
А вот эта заморочка более хитрая.
Мы хотим сослаться на SSL-сертификат в конфигурации нашего обратного прокси на Nginx, что означает, что без сертификата он просто откажется стартовать. В том же самое время, чтобы получить SSL-сертификат для домена, требуется рабочий HTTP-сервер, который докажет Let's Encrypt ваше владение этим доменом.
Мне удалось решить и эту проблему, причём, как мне кажется, довольно изящно:
Я сильно подозреваю, что вы хотите, чтобы комментарии пользователей сохранялись после перезагрузки или обновления системы.
Также, чтобы Let's Encrypt вас не забанил из-за слишком частых запросов, неплохо бы хранить полученные сертификаты в течение всего срока их годности.
Оба момента решены в предлагаемой конфигурации с помощью томов (volumes) Docker, автоматически создаваемых systemd при первом запуске Commento. Тома помечены как «внешние» (external
), поэтому Docker пропускает их при удалении контейнеров с помощью docker-compose down -v
.
Теперь можно взглянуть, как это всё вместе работает.
Рисунок ниже показывает взаимодействие и трафик между четырьмя контейнерами:
Я применил встроенную опцию Docker Compose depends_on
, чтобы обеспечить запуск контейнеров в правильном порядке.
Если вы только хотите запустить собственный сервер Commento, остаток статьи можно пропустить и сразу перейти к коду на GitHub [2].
Ну а я дальше расскажу о данной реализации чуть подробнее.
Как видно на рисунке выше, моя «композиция» состоит из четырёх сервисов:
certbot
— утилита certbot от EFFnginx
— обратный прокси, осуществляющий SSL offloadingapp
— сервер Commentopostgres
— база данных PostgreSQLФайл docker-compose.yml
[12] содержит декларации собственной Docker-сети, названной commento_network
, и трёх томов, из которых два являются внешними (то есть, должны создаваться вне Compose):
commento_postgres_volume
хранит данные сервера PostgreSQL для Commento: пользователей, модераторов, комментариев и т.д.certbot_etc_volume
содержит сертификаты, полученные certbot
-ом.Контейнер Nginx построен на базе легковесного официального образа, основанного на Alpine, и использует следующий скрипт для запуска:
#!/bin/sh
trap exit TERM
# Wait for the certificate file to arrive
wait_for_certs() {
echo 'Waiting for config files from certbot...'
i=0
while [[ ! -f /etc/letsencrypt/options-ssl-nginx.conf ]]; do
sleep 0.5
[[ $((i++)) -gt 20 ]] && echo 'No files after 10 seconds, aborting' && exit 2
done
}
# Watches for a "reload flag" (planted by certbot container) file and reloads nginx config once it's there
watch_restart_flag() {
while :; do
[[ -f /var/www/certbot/.nginx-reload ]] &&
rm -f /var/www/certbot/.nginx-reload &&
echo 'Reloading nginx' &&
nginx -s reload
sleep 10
done
}
# Wait for certbot
wait_for_certs
# Start "reload flag" watcher
watch_restart_flag &
# Run nginx in the foreground
echo 'Starting nginx'
exec nginx -g 'daemon off;'
certbot
. Без этого Nginx отказался бы запускаться..nginx-reload
, и, как только обнаружит его, подаёт Nginx команду перезагрузить конфигурацию. Этот файл также создаёт certbot, в момент когда сертификат обновляется.exec
означает, что текущий shell-процесс замещается процессом Nginx.Ещё один важный файл в данном образе — это конфигурация виртуального сервера Commento, заставляющая Nginx пересылать HTTPS-запросы в контейнер commento
:
server {
listen [::]:443 ssl ipv6only=on;
listen 443 ssl;
server_tokens off;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name __DOMAIN__;
location / {
proxy_pass http://app:8080/;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
ssl_certificate /etc/letsencrypt/live/__DOMAIN__/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/__DOMAIN__/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_tokens off;
server_name __DOMAIN__;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Redirect to HTTPS on port 80
location / {
return 301 https://$host$request_uri;
}
}
Первый server-блок (строки 1-21) описывает работу с HTTPS и правило форвардинга. Именно здесь и упоминаются файлы сертификата Let's Encrypt (или заглушки, используемые вместо них).
Домен, обслуживаемый сервером, передаётся в качестве аргумента при построении образа; он заменяет строку __DOMAIN__
в конфиге сервера.
Второй блок (строки 23-38) — это конфигурация HTTP-сервера, который используется certbot-ом для подтверждения владения доменом (так называемый «ACME challenge»). Все прочие запросы вызывают редирект на соответствующий адрес через HTTPS.
Наш образ certbot основан на официальной сборке [13] с добавлением нижеследующего скрипта:
#!/bin/sh
trap exit TERM
# Wait until nginx is up and running, up to 10 seconds
wait_for_nginx() {
echo 'Waiting for nginx...'
i=0
while ! nc -z nginx 80 &>/dev/null; do
sleep 0.5
[[ $((i++)) -gt 20 ]] && echo "nginx isn't online after 10 seconds, aborting" && exit 4
done
echo 'nginx is up and running'
}
# Check vars
[[ -z "$DOMAIN" ]] && echo "Environment variable 'DOMAIN' isn't defined" && exit 2
[[ -z "$EMAIL" ]] && echo "Environment variable 'EMAIL' isn't defined" && exit 2
TEST="${TEST:-false}"
# Check external mounts
data_dir='/etc/letsencrypt'
www_dir='/var/www/certbot'
[[ ! -d "$data_dir" ]] && echo "Directory $data_dir must be externally mounted"
[[ ! -d "$www_dir" ]] && echo "Directory $www_dir must be externally mounted"
# If the config/certificates haven't been initialised yet
if [[ ! -e "$data_dir/options-ssl-nginx.conf" ]]; then
# Copy config over from the initial location
echo 'Initialising nginx config'
cp /conf/options-ssl-nginx.conf /conf/ssl-dhparams.pem "$data_dir/"
# Copy dummy certificates
mkdir -p "$data_dir/live/$DOMAIN"
cp /conf/privkey.pem /conf/fullchain.pem "$data_dir/live/$DOMAIN/"
# Wait for nginx
wait_for_nginx
# Remove dummy certificates
rm -rf "$data_dir/live/$DOMAIN/"
# Run certbot to validate/renew certificate
test_arg=
$TEST && test_arg='--test-cert'
certbot certonly --webroot -w /var/www/certbot -n -d "$DOMAIN" $test_arg -m "$EMAIL" --rsa-key-size 4096 --agree-tos --force-renewal
# Reload nginx config
touch /var/www/certbot/.nginx-reload
# nginx config has been already initialised - just give nginx time to come up
else
wait_for_nginx
fi
# Run certbot in a loop for renewals
while :; do
certbot renew
# Reload nginx config
touch /var/www/certbot/.nginx-reload
sleep 12h
done
Краткая экскурсия по его строкам:
.nginx-reload
, намекающий Nginx, что пора перезагрузить конфиг.Контейнеры app
и postgres
используют исходные образы, предоставляемые разработчиками, без каких-либо изменений.
Последним кусочком этого пазла является юнит-файл systemd [14] commento.service
, на который нужно создать симлинк в /etc/systemd/system/commento.service
, чтобы он запускался в удачный момент при старте системы:
[Unit]
Description=Commento server
[Service]
TimeoutStopSec=30
WorkingDirectory=/opt/commento
ExecStartPre=-/usr/bin/docker volume create commento_postgres_volume
ExecStartPre=-/usr/bin/docker volume create certbot_etc_volume
ExecStartPre=-/usr/local/bin/docker-compose -p commento down -v
ExecStart=/usr/local/bin/docker-compose -p commento up --abort-on-container-exit
ExecStop=/usr/local/bin/docker-compose -p commento down -v
[Install]
WantedBy=multi-user.target
Строки:
/opt/commento
— так намного проще.--abort-on-container-exit
прибивает всю стаю контейнеров при останове любого из них. Благодаря этому systemd, как минимум, будет в курсе, что сервис остановлен.Полностью рабочая реализация, требующая лишь настройки переменных в docker-compose.yml
, имеется в наличии на GitHub [2]. Вам нужно лишь внимательно пройти по шагам, описанным в README [3].
Код распространяется на условиях MIT License [15].
Спасибо, что дочитали до этого места, комментарии неистово приветствуются!
Автор: Dmitry Kann
Источник [16]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/nginx/327452
Ссылки в тексте:
[1] моего поста: https://yktoo.solutions/blog/2019/07/28-self-hosting-commento-with-docker-compose/
[2] этот репозиторий: https://github.com/yktoo/commento-docker-compose
[3] README: https://github.com/yktoo/commento-docker-compose/blob/master/README.md
[4] Commento: https://commento.io/
[5] этот пост: https://habr.com/ru/post/446786/
[6] Docker Compose: https://docs.docker.com/compose/
[7] обратным прокси: https://ru.wikipedia.org/wiki/%d0%9e%d0%b1%d1%80%d0%b0%d1%82%d0%bd%d1%8b%d0%b9_%d0%bf%d1%80%d0%be%d0%ba%d1%81%d0%b8
[8] Nginx: https://www.nginx.com/
[9] Docker-образов: https://hub.docker.com/_/nginx
[10] Let's Encrypt: https://ru.wikipedia.org/wiki/Let%E2%80%99s_Encrypt
[11] certbot: https://certbot.eff.org/
[12] docker-compose.yml
: https://github.com/yktoo/commento-docker-compose/blob/master/docker-compose.yml
[13] официальной сборке: https://hub.docker.com/r/certbot/certbot
[14] юнит-файл systemd: https://www.freedesktop.org/software/systemd/man/systemd.service.html
[15] MIT License: https://opensource.org/licenses/MIT
[16] Источник: https://habr.com/ru/post/464105/?utm_source=habrahabr&utm_medium=rss&utm_campaign=464105
Нажмите здесь для печати.