Configuración de despliegue de Discourse con opiniones de MKJ

He propuesto una arquitectura llamada “limpia” para el nuevo servicio. Pero “limpio” no siempre significa “simple”.up/backup-config

restic --cache-dir=/var/restic
backup
/etc
/root
/var/discourse
/opt/backup
–exclude /var/discourse/shared/data/postgres_data

restic --cache-dir=/var/restic forget
–prune --keep-hourly 24 --keep-daily 7 --keep-monthly 3
EOF

chmod +x backup


Estos detalles variarán según el destino de restic. No todos utilizan variables de entorno de AWS, así que lee la documentación de restic.

```shell
# cat > backup-config <<EOF
### Estos detalles dependen del destino de restic que configures, así que cámbialos
export AWS_ACCESS_KEY_ID=tu-clave-aquí
export AWS_SECRET_ACCESS_KEY=igual
export RESTIC_PASSWORD_FILE=/root/restic-password
export RESTIC_REPOSITORY=consulta-la-documentación-de-restic
EOF
# {$EDITOR:-nano} /root/restic-password

Necesitarás guardar una copia de lo que pongas en /root/restic-password, ¡o no podrás leer las copias de seguridad! Usa tu bóveda de contraseñas.

Por último, crea algunos servicios, inicializa el repositorio de restic y establece un temporizador para que comiencen las copias de seguridad.

# cat > /etc/systemd/system/backup.service <<EOF
[Unit]
Description=Realizar copia de seguridad en destino remoto
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
StandardOutput=file:/var/log/backup.out
StandardError=file:/var/log/backup.err
WorkingDirectory=/var/discourse
ExecStart=/opt/backup/backup

[Install]
WantedBy=multi-user.target
EOF
# cat > /etc/systemd/system/backup.timer <<EOF
[Unit]
Description=Copias de seguridad del sistema periódicas

[Timer]
Persistent=true
OnCalendar=00/4:30:00
Unit=backup.service

[Install]
WantedBy=timers.target
EOF

# systemctl daemon-reload
# . /opt/backup/backup-config
# restic init
# /opt/backup/backup
# systemctl enable backup.timer

Después de este proceso, deberías poder ver que has creado tu copia de seguridad inicial.

# restic snapshots

He utilizado con éxito copias de seguridad de restic realizadas de esta manera para mover un servidor Discourse de un sistema a otro utilizando el comando restic restore.

Copias de seguridad de imágenes minio en streaming

Como alternativa a Restic, mientras que las copias de seguridad de la base de datos se pueden almacenar en S3, no hay una copia de seguridad de imágenes S3 separada de la entrega de imágenes desde S3. Una alternativa es usar minio-client para copiar imágenes a cualquier almacenamiento similar a S3. Esto puede ser muchos destinos similares a S3, incluyendo S3 y minio, pero no DigitalOcean Spaces porque está construido sobre el sistema de archivos Ceph, que no implementa la API ListObjectsV2 de la misma manera que S3.

En S3, crea un bucket que bloquee el acceso público (PermisosBloquear acceso público es la forma fácil de hacerlo correctamente en AWS).

Instala minio-client (mc) de alguna manera. Aquí hay una forma.

curl https://dl.min.io/client/mc/release/linux-amd64/mc > /usr/local/bin/mc && chmod +x /usr/local/bin/mc

Configura minio-client con un alias llamado backup usando un comando como este:

# mc alias set backup https://s3.amazonaws.com ACCESSKEY SECRETKEY --api S3v4
# mc mirror /var/discourse/shared/standalone/uploads backup/UPLOADS-BACKUP-BUCKET

Luego crea un servicio /etc/systemd/system/backup-uploads.service como este

[Unit]
Description=Sincronización de copia de seguridad remota casi en tiempo real de cargas de Discourse
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=600
User=root
ExecStart=/usr/local/bin/mc mirror --overwrite -a --watch /var/discourse/shared/app/uploads backup/UPLOADS-BACKUP-BUCKET

[Install]
WantedBy=multi-user.target

Ten en cuenta que UPLOADS-BACKUP-BUCKET aquí debe ser un bucket diferente al s3_backup_bucket en el que configuras Discourse para cargar copias de seguridad de la base de datos. Además, ten en cuenta que la ruta será /var/discourse/shared/web_only/uploads si usas el despliegue estándar de múltiples contenedores.

# systemctl enable backup-uploads
# systemctl start backup-uploads
# journalctl -fu backup-uploads

Sube una imagen de prueba y asegúrate de ver líneas para la copia de seguridad exitosa de las imágenes originales y optimizadas. Control-C saldrá del modo de seguimiento en journalctl.

Recuperación

Hasta la fecha, nunca he tenido que probar este plan. Este resumen podría omitir algo.

  • Restaurar todos los archivos respaldados en general
  • Iniciar nginx (ahora se mostrará tu página de mantenimiento)
  • Realizar un despliegue normal de Discourse usando los archivos restaurados en /var/discourse/containers
  • Instalar minio-client en /usr/local/bin/mc si no lo restauraste desde las copias de seguridad
  • Si no respaldaste /root/mc, configura el alias de copia de seguridad # mc alias set backup https://s3.amazonaws.com ACCESSKEY SECRETKEY --api S3v4
  • # mc cp backup/UPLOADS-BACKUP-BUCKET /var/discourse/shared/app/uploads
  • Restaurar la copia de seguridad de la base de datos más reciente; recomiendo que consultes Restore a backup from the command line
  • Solo después de confirmar que el sitio está operativo, reconfigura la copia de seguridad de las cargas en S3 como se documenta arriba.

Copias de seguridad de postgresql en streaming

En el futuro, puedo crear, probar y proporcionar una configuración para habilitar el uso de archivado WAL continuo para transmitir copias de seguridad de Postgres casi instantáneas con minio-client usando el archive-command en postgresql, similar a las copias de seguridad de cargas en streaming.

Monitoreo de rendimiento

Hay al menos dos enfoques para el monitoreo de rendimiento.

Contenedor Prometheus

Configura prometheus, poniendo los registros de prometheus en /var/discourse/shared/prometheus si lo ejecutas en el mismo sistema. Los archivos de Prometheus pueden crecer mucho, y no quieres que llenen el sistema de archivos raíz; también probablemente quieras llevarlos contigo si te mudas a un sistema de host más nuevo (ya sea actualizando a una VM más grande o a una VM con una instalación de sistema operativo más nueva).

Si despliegas prometheus en el sistema de Discourse (o en cualquier otro lugar en internet público), configura la seguridad delante de él. Instalado de esa manera, una opción sería la configuración de nginx como esta:

  location /prometheus/ {
    auth_basic "Prometheus";
    auth_basic_user_file /etc/nginx/prometheus-htpasswd;
    proxy_pass http://localhost:9090/;
  }

Sysstat

Si Prometheus es demasiado, considera usar sysstat en su lugar.

  • dnf install sysstat (o apt install sysstat en debian y derivados)
  • systemctl enable --now sysstat
  • systemctl enable --now sysstat-collect.timer
  • systemctl enable --now sysstat-summary.timer
  • systemctl edit sysstat-collect.timer y cambia OnCalendar=*:00/10 a OnCalendar=*:00/2
  • Si existe /etc/default/sysstat, cambia false a true

Después de esto, el comando sar puede decirte si te estás quedando sin recursos de vez en cuando.

Otros Recursos

Aquí hay una discusión complementaria (y más compacta) sobre el uso de Discourse internamente como forma principal de comunicación interna.

54 Me gusta

Hola, gracias por esta guía.

¿Cuáles son tu CPU (núcleos) y RAM actuales?
¿Cuáles son tus configuraciones actuales:

  db_shared_buffers: "xGB"
  db_work_mem: "xMB"
  UNICORN_WORKERS:

2 vCPU (Xeon L5640), 4 GB de RAM, pero gracias a la importación masiva, tenemos más contenido por usuario simultáneo que el típico Discourse construido de forma totalmente orgánica. Rara vez tenemos más de 5 usuarios simultáneos.

Actualmente no he configurado db_work_mem en data.yml, pero parece que está establecido en 10 MB en /etc/postgresql/13/main/postgresql.conf. En mi data.yml he configurado db_shared_buffers: "768MB", pero ahora veo que en /etc/postgresql/13/main/postgresql.conf dentro de mi contenedor de datos dice shared_buffers = 512MB, lo cual me sorprende. Aparentemente no reconstruí mi contenedor de datos después de mi último cambio. :roll_eyes: Realicé el cambio de configuración antes de agregar Prometheus (que usa más memoria), así que antes de modificar eso, probablemente moveré Prometheus fuera de esa instancia del servidor.

En la aplicación, he configurado UNICORN_WORKERS: 4.

1 me gusta

gracias @mcdanlj por todas estas cosas. ¿Algún consejo sobre mantenimiento periódico/programado? ¿como reinicios semanales/mensuales? ¿o monitoreo de subida/bajada con reinicio automático? ¿Algún otro mantenimiento periódico manual o automatizado?

1 me gusta

@jaffadog Vigila la etiqueta release-notes (es la campana en la esquina superior derecha; yo uso “Vigilar la primera publicación”) y/o añade https://meta.discourse.org/tag/release-notes.rss a tu feed RSS para saber cuándo hay lanzamientos. Esto es típicamente mensual para la aplicación, lo que cubre el reinicio mensual. No reinicio con más frecuencia según un horario. Además:

Si usas letsencrypt y mail_receiver, probablemente deberías configurarlo para reiniciar mail_receiver después de obtener un nuevo certificado.

Eso es todo lo que se me ocurre por el momento. Gracias por preguntar, e incorporé cómo vigilar release-notes y más detalles sobre el reinicio en el texto principal. Creo que ahora está todo cubierto en la publicación original.

He aumentado las instrucciones de actualización para extraer las actualizaciones en /var/discourse con el fin de cambiar la versión de la imagen base, porque al ver Update base image for polkit vulnerability me di cuenta de que no ser explícito sobre este paso podría ser engañoso. Anteriormente lo consideraba una de esas cosas que forman parte de la documentación básica. El script launcher contiene una referencia específica a la versión de la imagen base, y hasta que no hagas git pull estarás construyendo sobre una imagen base más antigua, y no estarás ejecutando lo que ha sido probado. (Busca image= cerca de la parte superior del archivo).

1 me gusta

Es peor que eso: está configurado por defecto para eliminar cuentas inactivas de nivel 0 después de un período. En mi caso, ¡realmente no era lo que quería! Comprueba “limpiar usuarios inactivos después de días” y configúralo a cero, o a un número realmente grande.

2 Me gusta

Lo miré, pero hasta donde entendí significa que nunca respondieron al flujo de “confirma tu dirección de correo electrónico” y, por lo tanto, no recibirían correos electrónicos de todos modos. No creo que “inactivo” aquí signifique “no iniciar sesión en el sitio”, pero me gustaría saber si me equivoco.

No, “inactivo” no significa active=false aquí.

Significa usuarios a los que se aplican todas las siguientes condiciones:

  • nivel de confianza 0
  • sin publicaciones
  • no es administrador, no es moderador
  • visto por última vez hace más de X días.

Y sí, esa redacción es realmente confusa, aunque la configuración lo explica un poco (“nivel de confianza 0 sin ninguna publicación”).

8 Me gusta

En realidad, estaba confundiendo diferentes configuraciones y tenía en mente “limpiar usuarios en staging no utilizados después de días”. Ya había establecido “limpiar usuarios inactivos después de días” a cero en Maker Forums hace mucho tiempo, y no lo mencioné aquí; debo habérmelo perdido en mi auditoría de la configuración del sitio modificada cuando buscaba cosas que pudieran ser de interés general. ¡Gracias a ambos, @Ed_S y @RGJ! He actualizado la publicación con otro párrafo sobre cómo habilitar el acecho. :smiling_face:

4 Me gusta

Hola @mcdanlj, gracias por tu excelente perspectiva que compartes aquí. Con respecto a una instalación de 2 contenedores, estoy confundido sobre cómo eso da una ventaja para reducir el tiempo de inactividad cuando se realizan las actualizaciones. En mi instalación de prueba, una actualización a través de la interfaz gráfica de usuario /admin/upgrade#/upgrade/all tarda varios minutos como describes, pero el sitio permanece operativo para los usuarios durante todo el proceso.

Cuando tenga que reconstruir desde la línea de comandos, la instalación de dos contenedores le permite iniciar la nueva imagen mientras la antigua sigue ejecutándose.

2 Me gusta

Como siempre, @pfaffman es más rápido que yo y sabe lo que hace. :smiley:

Nunca hago actualizaciones desde la GUI. De esa manera, cada vez que actualizo, incluyo cualquier actualización de seguridad del sistema en los contenedores subyacentes sobre los que se ejecuta Discourse. Eso no significa que sea malo usar la GUI, ni es una recomendación para todos los demás. Las actualizaciones de la GUI tienen tiempos de inactividad ligeramente menores (reiniciar el contenedor web interrumpe el servicio momentáneamente, lo cual es una de varias razones para considerar esto junto con nginx externo), por lo que es un compromiso, y he tomado el camino menos transitado.

Una instalación de un solo contenedor tiene tiempos de inactividad más largos con más frecuencia, si actualiza regularmente para obtener actualizaciones de seguridad, así como para las actualizaciones ocasionales de la versión de la base de datos, ya que Discourse aprovecha las nuevas características de Postgresql. Sin revisar datos reales, mi instinto me dice que hay una razón para reconstruir eso 3-4 veces al año. Si esa cantidad de tiempo de inactividad está bien desde su perspectiva, no hay muchas razones para asumir la complejidad de la implementación de 2 contenedores.

4 Me gusta

Eso es muy amable, pero se trata de tus opiniones. :wink:

Yo tampoco. Excepto con mi panel de control, que tiene un montón de cosas adicionales en el contenedor (como Ansible, y no recuerdo exactamente qué más), y si alguien está ejecutando una actualización usando dashboard.literatecomputing.com y luego destruyo ese contenedor, su reconstrucción se termina, lo que es potencialmente problemático. Así que he estado haciendo algunas actualizaciones más de docker_manager últimamente, y son bastante elegantes.

En realidad no. Es al menos mayormente el caso que si hay una nueva imagen base, docker_manager te obligará a obtenerla (al menos, lo intentará).

Eso es más o menos correcto. A título informativo, y no lo recomiendo, pero he interactuado con un montón de gente que no hizo ninguna actualización durante años sin ningún problema.

1 me gusta

Sí, no hay duda de que las actualizaciones dentro del contenedor están bien implementadas.

Sí, eso es lo que quise decir. :tada:

2 Me gusta

Hola de nuevo, gracias a ambos por sus ideas. Así que todavía estoy tratando de decidir qué voy a hacer para mi configuración de producción. Entiendo en teoría por qué una instalación de 2 contenedores conduciría a menos tiempo de inactividad. Pero todavía veo muy poco tiempo de inactividad con el mecanismo de actualización de la GUI. Acabo de cronometrarlo, tuve una actualización de docker-manager y Discourse estaba 22 commits atrás. Todo el proceso tomó menos de 5 minutos y durante todo el proceso el foro fue completamente operable. Es cierto que esta vez no hubo actualizaciones de PostgreSQL, pero si fuera necesario actualizar eso también, estaría observando la cantidad máxima de tiempo de inactividad incluso con el método de 2 contenedores, ¿correcto? Entonces, si entiendo correctamente, ¿el método de 2 contenedores solo reduce el tiempo de inactividad cuando es necesario un inicio de sesión SSH y una reconstrucción del contenedor de la aplicación debido a un cambio de configuración (agregar/eliminar complementos, etc.)? No preveo cambios frecuentes en mi configuración de producción, por lo que no veo ninguna reducción potencial en el tiempo de inactividad en mi caso. ¿O también necesito iniciar sesión por SSH y reconstruir los contenedores para aplicar ciertos tipos de actualizaciones de funciones/seguridad?

Está bien.

Sí, tienes el tiempo de inactividad completo cada vez que actualizas postgresql (cada año o dos, supongo, además de las actualizaciones de seguridad de PostgreSQL en sí, no es que hayan sido tan frecuentes).

Antes de cambiar, tuve un par de fallos al hacer las actualizaciones de la GUI, pero ya no recuerdo los detalles; historia antigua.

Las actualizaciones de la GUI no actualizan la imagen base, por lo que todas las actualizaciones de seguridad en el software proporcionado, como el procesamiento de imágenes, no se aplicarán allí.

Me gusta reconstruir el contenedor de la aplicación rápidamente como mi modo normal para consumir actualizaciones de seguridad del contenedor de la aplicación cuando estén disponibles, de modo que nunca pospongo eso por 10 minutos de inactividad para reconstruir todo. Es por eso que git pull en el lanzador es parte de la primera parte de mi aplicación de cada actualización; para que si la imagen base con cosas como programas de procesamiento de imágenes se ha actualizado, se apliquen sin que yo tenga que pensar en preguntar si hay actualizaciones de seguridad que aplicar. :smiling_face:

Pero, en última instancia, personalmente encuentro que el enfoque de dos contenedores es más simple para mí, y absolutamente no estoy tratando de convencer a otras personas. Si no te parece más simple según tu conocimiento y experiencia, no lo hagas solo porque mi guía personal y opinionada lo identifica como útil en un contexto determinado. :grin:

2 Me gusta

¡Entendido! :wink: Yo también tiendo a tener opiniones firmes sobre los detalles de implementación tecnológica, pero aprecio tu perspectiva adicional.

Hmm, ¿así que esto sigue siendo así?

En cualquier caso, por mi experiencia con foros tradicionales instalados sin contenerización en una pila LAMP/LEMP, la vulnerabilidad / vector de ataque típico que lleva a sitios web comprometidos en el mundo real casi siempre está en el código de la aplicación web o en uno de sus frameworks de desarrollo web. Por lo tanto, tiendo a tener un mayor sentido de urgencia hacia las actualizaciones de la base de código de Discourse, que parecen ser manejadas por la GUI, así que supongo que me inclino hacia esa dirección.


Por cierto, sobre el tema del tiempo de inactividad, una rápida mención a Clear Linux: comencé a probarlo debido a sus optimizaciones de bajo nivel en el procesamiento puro de números para intentar reducir unas horas de mi enorme proceso de importación de foros. Puede que haya algunas mejoras de velocidad en ese caso, pero en general, santo cielo, es increíblemente rápido al reiniciar, especialmente como invitado de KVM. En el nivel más barato de VPS, puedo reiniciar e iniciar sesión de nuevo en SSH en apenas 5 segundos. Así que estoy deseando usarlo cuando haya actualizaciones importantes del sistema operativo anfitrión.

Sí. Pero unas pocas veces al año debes hacer una reconstrucción desde la línea de comandos porque algún componente en el contenedor ha sido actualizado. Y entonces tendrás de 5 a 15 minutos de inactividad. Yo hago las actualizaciones casi exclusivamente desde la línea de comandos (excepto en mi panel de control, donde podría actualizar solo el plugin del panel varias veces al día), y tengo una gran cantidad de clientes para quienes hago esas actualizaciones desde la línea de comandos cuando son necesarias (presumiblemente, ellos hacen las actualizaciones a través de la interfaz web, pero a menudo no lo hacen).

2 Me gusta

¿El panel de control de Discourse notifica específicamente sobre ese tipo de actualización requerida? ¿O debo estar atento a los PSA de Debian?