Servidor Commento nativo con Docker Compose

Nota: esta es una traducción de mi publicación (en inglés), que describe la implementación del servidor de comentarios utilizado en el mismo sitio donde se encuentra el original.


Versión TL; DR: desarrollé la configuración del servidor Commento, que se implementa fácil y simplemente en modo semiautomático. Copie este repositorio para usted desde GitHub y siga las instrucciones en README .

Hace algún tiempo, quería irresistiblemente cambiar Disqus, que es quizás el sistema más común para agregar comentarios a las páginas, a un Commento gratuito y abierto.


¿Por qué Commento?


El problema con Disqus, como muchos otros productos "gratuitos", es que el producto en este caso es el usuario, es decir, usted. Además, Disqus "enriquece" cada página donde se usa con megabytes de scripts y más de cien solicitudes HTTP adicionales.


Además, su versión gratuita muestra anuncios desde los cuales puede pagar "solo" por $ 9 por mes (plan Plus). Esto solo es suficiente para querer encontrar algo mejor.


En algún momento, me topé con esta publicación y descubrí la existencia de un servidor de comentarios gratuito llamado Commento . Por una coincidencia afortunada, Commento recientemente se abrió por completo, antes de que estuviera disponible en dos versiones, Comunidad gratuita y Empresa comercial. Gracias a su desarrollador Adhityaa Chandrasekar.


Commento es un orden de magnitud más eficiente que Disqus, el tamaño típico de la carga adicional con él es de aproximadamente 11 KB , más los comentarios en sí, por supuesto. Aproximadamente la misma situación con las solicitudes HTTP requeridas.


Otra ventaja del servidor Commento es que es muy rápido, ya que está escrito en Go.


Bueno, como guinda del pastel, tiene una gran cantidad de comentarios de Disqus, ¿con qué más podría soñar?


Casos de uso para Commento


Para usuarios no avanzados (técnicamente), Commento tiene un servicio en la nube listo para usar en commento.io . El autor le ofrece elegir la tarifa mensual usted mismo, pero no puede ser inferior a $ 3 "por razones técnicas".


El Sr. Chandrasekar también ofrece generosamente una cuenta gratuita en Commento.io a cambio de "parches no triviales" para el producto.


Bueno, elegí la tercera opción: subir el servidor Commento yo mismo. En este caso, no dependes de nadie (además del hoster, por supuesto), y me encanta la independencia.


Dificultades


Soy un gran admirador de los contenedores Docker y también uso a menudo Docker Compose , una herramienta para administrar grupos de varios contenedores relacionados. Commento tiene una imagen Docker lista para usar en el registro de contenedor de GitLab.


Por lo tanto, la decisión de usar recipientes maduró por sí sola, pero primero debían decidirse algunas cosas.


Dificultad No. 1: PostgreSQL


Commento requiere una versión bastante reciente del servidor PostgreSQL, desafortunadamente no se admiten otros servidores SQL.


Bueno, todavía ejecutamos todo en contenedores, por lo que es bastante simple.


Dificultad # 2: No hay soporte HTTPS


Commento en sí es un servidor web, pero solo admite el protocolo HTTP inseguro.


Cabe señalar que esta práctica es bastante común en estos días: en este caso, el servidor está oculto detrás del proxy inverso , que también realiza la descarga de SSL. La cuestión es que la compatibilidad con SSL / HTTPS es absolutamente necesaria en este caso, después de todo, en el patio 2019 y mirar los intentos de autorizar a un usuario utilizando un protocolo de Internet no seguro será muy irónico.


Decidí usar el servidor Nginx , en primer lugar, tenía una experiencia considerable trabajando con él y, en segundo lugar, es muy rápido, económico y estable. Y publica las compilaciones oficiales de las imágenes de Docker .


El segundo ingrediente en la receta HTTPS es el certificado SSL para el dominio. Estoy eternamente agradecido con EFF y Mozilla por crear la Autoridad de certificación Let's Encrypt , que emite millones de certificados gratuitos cada mes.


Let's Encrypt también proporciona una utilidad de línea de comandos gratuita llamada certbot , que simplifica enormemente el proceso de obtención y actualización de un certificado. Bueno, y, por supuesto, ¡una imagen de Docker para él!


Dificultad # 3: Problema del huevo de gallina Certbot


Pero este truco es más complicado.


Queremos referirnos al certificado SSL en la configuración de nuestro proxy inverso en Nginx, lo que significa que sin un certificado simplemente se niega a comenzar. Al mismo tiempo, para obtener un certificado SSL para un dominio, necesita un servidor HTTP que funcione, que Let's Encrypt demostrará su propiedad de este dominio.


Me las arreglé para resolver este problema y, me parece, con bastante elegancia:


  1. Primero, se genera un certificado ficticio e inválido, cuyo único propósito es permitir que Nginx se inicie.
  2. Nginx y certbot reciben conjuntamente un nuevo certificado ahora válido.
  3. Tan pronto como se recibe el certificado, certbot entra en "modo de espera", despertando cada 12 horas para verificar si necesita actualizarse, de acuerdo con las recomendaciones de Let's Encrypt.
  4. Cuando llegue el momento y el certificado se haya renovado, certbot le indicará a Nginx que se reinicie.

Dificultad No. 4: algo debe ser preservado


Sospecho firmemente que desea guardar los comentarios de los usuarios después de un reinicio o una actualización del sistema.


Además, para que Let's Encrypt no lo prohíba debido a solicitudes demasiado frecuentes, sería bueno conservar los certificados recibidos durante toda la fecha de vencimiento.


Ambos puntos se resolvieron en la configuración propuesta utilizando los volúmenes de Docker, creados automáticamente por systemd cuando se lanzó Commento por primera vez. Los volúmenes se marcan como external , por lo que Docker los omite al eliminar contenedores con docker-compose down -v .


Reúne todo


Ahora puedes ver cómo funciona todo junto.


La siguiente figura muestra la interacción y el tráfico entre los cuatro contenedores:



depends_on opción depends_on Docker Compose depends_on para asegurar que los contenedores comiencen en el orden correcto.


Si solo desea iniciar su propio servidor Commento, puede omitir el resto del artículo e ir directamente al código en GitHub .


Bueno, hablaré más sobre esta implementación con más detalle más adelante.


Como funciona todo


Redactar archivo


Como puede ver en la imagen de arriba, mi "composición" consta de cuatro servicios:


  1. certbot - utilidad certbot de EFF
  2. nginx : proxy inverso que implementa la descarga SSL
  3. app - servidor Commento
  4. postgres - base de datos PostgreSQL

El docker-compose.yml contiene declaraciones de su propia red Docker, llamada commento_network , y tres volúmenes, de los cuales dos son externos (es decir, deben crearse fuera de Compose):


  • commento_postgres_volume almacena datos del servidor PostgreSQL para Commento: usuarios, moderadores, comentarios, etc.
  • certbot_etc_volume contiene los certificados recibidos por certbot .

Nginx


El contenedor Nginx se basa en una imagen oficial ligera basada en Alpine y utiliza el siguiente script para ejecutarse:


 #!/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;' 

  • Línea 3 ( ARRGHHH, Habr no admite la visualización de números de línea en el código - aprox. Transl. ) Se registra un controlador de interrupciones para que Nginx y el proceso de monitoreo en segundo plano completen con éxito el trabajo cuando el contenedor se detenga.
  • La línea 27 llama a la función de espera, que detiene el proceso de inicio de Nginx hasta que certbot archivos de configuración SSL creados por el contenedor certbot . Sin esto, Nginx se negaría a comenzar.
  • La línea 30 crea un proceso en segundo plano que regularmente, cada diez segundos, verifica la presencia de un archivo indicador llamado .nginx-reload , y tan pronto como lo detecta, le indica a Nginx que vuelva a cargar la configuración. Este archivo también crea certbot cuando se actualiza el certificado.
  • La línea 34 inicia Nginx en modo normal. En este caso, exec significa que el proceso de shell actual se reemplaza por el proceso Nginx.

Otro archivo importante en esta imagen es la configuración del servidor virtual Commento, que obliga a Nginx a reenviar solicitudes HTTPS al contenedor de 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; } } 

El primer bloque de servidor (líneas 1-21 ) describe cómo trabajar con HTTPS y la regla de reenvío. Aquí es donde se mencionan los archivos de certificado Let's Encrypt (o se usan stubs en su lugar).


El dominio servido por el servidor se pasa como argumento al construir la imagen; reemplaza la línea __DOMAIN__ en la configuración del servidor.


El segundo bloque (líneas 23-38 ) es la configuración del servidor HTTP, que es utilizado por el certbot para confirmar la propiedad del dominio (el llamado "desafío ACME"). Todas las demás solicitudes provocan un redireccionamiento a la dirección correspondiente a través de HTTPS.


certbot


Nuestra imagen certbot se basa en la compilación oficial con el siguiente script:


 #!/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 

Un breve recorrido por sus líneas:


  • La línea 3 , como en la secuencia de comandos anterior, es necesaria para completar el contenedor regularmente.
  • Las líneas 17-19 verifican las variables requeridas.
  • Y en las líneas 22-25 , que los directorios necesarios para que certbot funcione están montados correctamente.
  • El tenedor sigue:
    • Las líneas 30-50 se ejecutan solo en el primer inicio del contenedor:
      • Se copia un certificado ficticio, lo que permite que Nginx se inicie normalmente.
      • Nginx, mientras tanto, está esperando el final de este proceso, luego de lo cual continúa descargándose.
      • Una vez que Nginx ha comenzado, certbot inicia el proceso de obtención de un certificado válido de Let's Encrypt.
      • Y finalmente, tan pronto como se recibe el certificado, se crea el archivo .nginx-reload , insinuando a Nginx que es hora de volver a cargar la configuración.
    • La línea 54 espera a que Nginx se inicie, en el caso de que un certificado completo ya esté disponible.
  • Después de todo esto (líneas 58-63 ), continúa con el ciclo, una vez cada 12 horas, verificando la necesidad de renovar el certificado y señalando a Nginx para reiniciar.

Commento y PostgreSQL


La app y los contenedores de postgres usan las imágenes originales proporcionadas por los desarrolladores sin ningún cambio.


Servicio Systemd


La última pieza de este rompecabezas es el archivo de unidad systemd commento.service , en el que debe crear un enlace simbólico en /etc/systemd/system/commento.service para que se inicie en un buen momento cuando se inicia el sistema:


 [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 

Filas:


  • La línea 6 implica que el código del proyecto está clonado en el directorio /opt/commento , esto es mucho más simple.
  • Las líneas 7-8 crean volúmenes externos, si aún no lo están.
  • En la línea 9 , se eliminan los posibles restos de los contenedores anteriores. Los volúmenes externos se conservan.
  • La línea 10 marca el lanzamiento real de Docker Compose. El --abort-on-container-exit toda la bandada de contenedores cuando alguno de ellos se --abort-on-container-exit . Gracias a esto, systemd al menos sabrá que el servicio está detenido.
  • La línea 11 nuevamente está limpiando y eliminando contenedores, redes y volúmenes.

Código fuente


Una implementación completamente funcional, que requiere solo la configuración de variables en docker-compose.yml , está disponible en GitHub . Solo necesita seguir cuidadosamente los pasos descritos en README .


El código está sujeto a la licencia MIT .


Gracias por leer este lugar, ¡los comentarios son frenéticamente bienvenidos!

Source: https://habr.com/ru/post/464105/


All Articles