Cómo instalar Zulip Chat Server usando Docker en Rocky Linux 9

Zulip es un servidor de chat de código abierto similar a Microsoft Teams, Rocket Chat o Slack. Está escrito en Python y utiliza Django, PostgreSQL y JavaScript. Se integra con más de 90 plugins de terceros, como Github, Jira, Stripe, Zendesk, Sentry, etc. Puedes ampliar las integraciones conectándolas con Zapier e IFTTT. Incluye funciones como mensajería privada, chats de grupo, conversaciones hilvanadas, canales personalizados, videollamadas, carga de archivos mediante arrastrar y soltar, emojis personalizados, integración con Giphy, previsualización de imágenes y tweets, y muchas más. Zulip viene con aplicaciones de escritorio y móviles para todas las plataformas, por lo que es agnóstico en cuanto a plataformas.

Este tutorial te enseña a instalar y configurar Zulip Chat en un servidor Rocky Linux 9.

Requisitos previos

  • Un servidor con Rocky Linux 9.
  • Al menos 2 GB de RAM si esperas menos de 100 usuarios. Para más de 100 usuarios, consigue un servidor de 4 GB de RAM y 2 CPU.
  • Un usuario no root con privilegios sudo.
  • Un nombre de dominio configurado para apuntar al servidor, zulip.example.com.
  • Todo actualizado.
    $ sudo dnf update
    
  • Pocos paquetes que necesite tu sistema.
    $ sudo dnf install wget curl nano unzip yum-utils policycoreutils-python-utils -y
    

    Puede que algunos de estos paquetes ya estén instalados en tu sistema.

Paso 1 – Configurar el cortafuegos

Antes de instalar ningún paquete, el primer paso es configurar el cortafuegos para abrir puertos para HTTP, y HTTPS. Rocky Linux utiliza el cortafuegos Firewalld. Comprueba el estado del cortafuegos.

$ sudo firewall-cmd --state
running

El cortafuegos funciona con diferentes zonas, y la zona pública es la que utilizaremos por defecto. Enumera todos los servicios y puertos activos en el cortafuegos.

$ sudo firewall-cmd --zone=public --list-all

Debería mostrar la siguiente salida.

public
  target: default
  icmp-block-inversion: no
  interfaces: enp1s0
  sources: 
  services: cockpit dhcpv6-client ssh
  ports: 
  protocols: 
  forward: yes
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

Abre los puertos HTTP y HTTPS en el cortafuegos.

$ sudo firewall-cmd --zone=public --add-service=http
$ sudo firewall-cmd --zone=public --add-service=https

Vuelve a comprobar el estado del cortafuegos.

$ sudo firewall-cmd --zone=public --list-all

Deberías ver un resultado similar.

public
  target: default
  icmp-block-inversion: no
  interfaces: enp1s0
  sources: 
  services: cockpit dhcpv6-client http https ssh
  ports: 
  protocols: 
  forward: yes
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

Haz permanentes todos los cambios y vuelve a cargar el cortafuegos para activar los cambios.

$ sudo firewall-cmd --runtime-to-permanent
$ sudo firewall-cmd --reload

Paso 2 – Instala Docker y Docker Compose

Instala el repositorio oficial de Docker.

$ sudo dnf install yum-utils
$ sudo dnf config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

Instala Docker.

$ sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Habilita y ejecuta el demonio Docker.

$ sudo systemctl enable docker --now

Comprueba el estado del servicio Docker.

$ sudo systemctl status docker
? docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: disabled)
     Active: active (running) since Tue 2024-02-06 07:17:33 UTC; 5s ago
TriggeredBy: ? docker.socket
       Docs: https://docs.docker.com
   Main PID: 22302 (dockerd)
      Tasks: 10
     Memory: 31.3M
        CPU: 198ms
     CGroup: /system.slice/docker.service
             ??22302 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

Añade tu usuario de sistema al grupo Docker para evitar utilizar sudo para ejecutar comandos Docker.

$ sudo usermod -aG docker $(whoami)

Vuelve a acceder a tu servidor después de cerrar la sesión para activar el cambio.

Paso 3 – Instalar Nginx

Para el entorno de producción, se recomienda ejecutar el servidor Synapse utilizando un proxy Nginx.

Rocky Linux 9 viene con una versión antigua de Nginx. Necesitas utilizar el repositorio oficial de Nginx para instalar la última versión.

Crea y abre el archivo /etc/yum.repos.d/nginx.repo para editarlo.

$ sudo nano /etc/yum.repos.d/nginx.repo

Pega en él el siguiente código.

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

Cuando hayas terminado, guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida.

Vamos a instalar la versión principal de Nginx, así que habilita el paquete correspondiente.

$ sudo dnf config-manager --enable nginx-mainline

Instala Nginx.

$ sudo dnf install nginx -y

Verifica la instalación.

$ nginx -v
nginx version: nginx/1.25.3

Habilita e inicia el servicio del servidor Nginx.

$ sudo systemctl enable nginx --now

Comprueba el estado del servicio.

$ sudo systemctl status nginx
? nginx.service - nginx - high performance web server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: disabled)
     Active: active (running) since Tue 2024-02-06 07:18:56 UTC; 5s ago
       Docs: http://nginx.org/en/docs/
    Process: 22762 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
   Main PID: 22763 (nginx)
      Tasks: 5 (limit: 50339)
     Memory: 4.9M
        CPU: 16ms
     CGroup: /system.slice/nginx.service
             ??22763 "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"
             ??22764 "nginx: worker process"
             ??22765 "nginx: worker process"
             ??22766 "nginx: worker process"
             ??22767 "nginx: worker process"

Paso 4 – Instalar SSL

Necesitamos instalar Certbot para generar el certificado SSL. Para ello utilizaremos el instalador de paquetes Snapd. Como Rocky Linux no lo incluye, instala el instalador Snapd. Necesita el repositorio EPEL (Extra Packages for Enterprise Linux) para funcionar.

Instala el repositorio EPEL.

$ sudo dnf install epel-release -y

Instala el paquete Snapd.

$ sudo dnf install snapd -y

Activa e inicia el servicio Snap.

$ sudo systemctl enable snapd --now

Instala el paquete Snap core, y asegúrate de que tu versión de Snapd está actualizada.

$ sudo snap install core && sudo snap refresh core

Crea los enlaces necesarios para que Snapd funcione.

$ sudo ln -s /var/lib/snapd/snap /snap
$ echo 'export PATH=$PATH:/var/lib/snapd/snap/bin' | sudo tee -a /etc/profile.d/snapd.sh

Instala Certbot.

$ sudo snap install --classic certbot

Asegúrate de que se puede ejecutar el comando Certbot creando un enlace simbólico al directorio /usr/bin.

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Comprueba la versión de Certbot.

$ certbot --version
certbot 2.8.0

Ejecuta el siguiente comando para generar un certificado SSL.

$ sudo certbot certonly --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m [email protected] -d zulip.example.com

El comando anterior descargará un certificado en el directorio /etc/letsencrypt/live/zulip.example.com de tu servidor.

Genera un certificado de grupo Diffie-Hellman.

$ sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096

Comprueba el servicio programador de renovación de Certbot.

$ systemctl list-timers

Encontrarás snap.certbot.renew.service como uno de los servicios programados para ejecutarse.

NEXT                        LEFT          LAST                        PASSED      UNIT                        ACTIVATES                   ----------------------------------------------------------------------------------------------------------------------------------  
Tue 2024-02-06 07:30:00 UTC 6min left     Tue 2024-02-06 07:20:11 UTC 3min 41s ago sysstat-collect.timer      sysstat-collect.service
Tue 2024-02-06 08:22:43 UTC 58min left    Tue 2024-02-06 07:22:39 UTC 1min 13s ago dnf-makecache.timer        dnf-makecache.service
Tue 2024-02-06 09:36:00 UTC 2h 12min left -                           -            snap.certbot.renew.timer   snap.certbot.renew.service

Realiza una ejecución en seco del proceso para comprobar si la renovación SSL funciona correctamente.

$ sudo certbot renew --dry-run

Si no ves ningún error, ya está todo listo. Tu certificado se renovará automáticamente.

Paso 5 – Configurar SELinux

Aplica la política para permitir conexiones a hosts externos.

$ sudo setsebool -P httpd_can_network_connect 1

Aplica la política para permitir que Nginx dé acceso a PostgreSQL.

$ sudo setsebool -P httpd_can_network_connect_db 1

Paso 6 – Preparar y configurar Zulip para la instalación

En primer lugar, crea una clave secreta para Zulip. Guarda la clave generada porque la necesitaremos más adelante.

$ openssl rand -base64 32
sLIeucGPMCNbR0LwcRhyXafXmputmtse6+EYU04+9JY=

Crea un directorio para el archivo Zulip Docker Compose y pasa a él.

$ mkdir ~/docker-zulip
$ cd ~/docker-zulip

Crea y abre el archivo de entorno para editarlo.

$ nano .env

Pega en él el siguiente código.

[email protected]
EXTERNAL_HOST=zulip.example.com
ZULIP_AUTH_BACKENDS=EmailAuthBackend
ZULIP_PUSH_NOTIFICATION_BOUNCER_URL=https://push.zulipchat.com
DISABLE_HTTPS=true
SSL_CERTIFICATE_GENERATION=self-signed

EMAIL_HOST=email-smtp.us-west-2.amazonaws.com
EMAIL_HOST_USER=AMAZONSESUSERNAME
EMAIL_PASSWORD=AMAZONSESPASSWORD
EMAIL_PORT=465
EMAIL_USE_SSL=True
EMAIL_USE_TLS=False
[email protected]

ZULIP_GIT_URL=https://github.com/zulip/zulip.git
ZULIP_GIT_REF=8.2

SECRET_KEY=sLIeucGPMCNbR0LwcRhyXafXmputmtse6+EYU04+9JY=
POSTGRES_USER=zulip
POSTGRES_DB=zulip
POSTGRES_PASSWORD=REPLACE_WITH_SECURE_POSTGRES_PASSWORD
REDIS_PASSWORD=REPLACE_WITH_SECURE_REDIS_PASSWORD
MEMCACHED_PASSWORD=REPLACE_WITH_SECURE_MEMCACHED_PASSWORD
RABBITMQ_DEFAULT_USER=zulip
RABBITMQ_DEFAULT_PASS=REPLACE_WITH_SECURE_RABBITMQ_PASSWORD

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida una vez hayas terminado.

Repasemos todas las variables que hemos definido.

  • ZULIP_ADMINISTRATOR – es la dirección de correo electrónico principal de la cuenta de administrador creada durante la instalación.
  • EXTERNAL_HOST – es el nombre de dominio que utilizaremos para nuestra instalación de Zulip.
  • ZULIP_AUTH_BACKENDS – define el método para iniciar sesión. Nosotros utilizamos EmailAuthBackend, lo que significa que Zulip te pedirá una dirección de correo electrónico y una contraseña. Existen otros métodos de autenticación que utilizan credenciales de Google, GitHub y Apple para iniciar sesión. Puedes encontrar más información en la documentación de Zulip y en el archivo por defecto settings.py por defecto.
  • ZULIP_PUSH_NOTIFICATION_BOUNCER_URL – es la URL que Zulip utilizará para enviar notificaciones push móviles. Para la versión comunitaria de Zulip para una organización con 10 o menos usuarios, las notificaciones push son gratuitas. Tu organización aún puede optar a notificaciones push ilimitadas gratuitas en función de los requisitos. Consulta la documentación de facturación de Z ulip para más detalles.
  • DISABLE_HTTPS – Lo hemos puesto a true para desactivar SSL para Zulip porque lo gestionaremos fuera del contenedor.
  • SSL_CERTIFICATE_GENERATION – Como hemos deshabilitado SSL para Zulip, necesitamos configurarlo para generar certificados autofirmados.
  • EMAIL_HOST – El host de correo SMTP que necesitamos para activar las notificaciones por correo electrónico. Aquí estamos utilizando el servicio de correo electrónico Amazon SES para nuestro tutorial.
  • EMAIL_HOST_USER – El nombre de usuario del correo SMTP.
  • EMAIL_PASSWORD – La contraseña del correo electrónico SMTP.
  • EMAIL_PORT – El puerto de correo SMTP.
  • EMAIL_USE_SSL – Si el host SMTP admite autenticación SSL.
  • EMAIL_USE_TLS – Si el host SMTP admite autenticación TLS.
  • EMAIL_NOREPLY_ADD – La dirección de correo electrónico sin respuesta utilizada para enviar correos electrónicos de notificación.
  • ZULIP_GIT_URL – Estamos utilizando la imagen docker de Zulip, pero si quieres crear una imagen personalizada, debes introducir la URL del repositorio Git de Zulip.
  • ZULIP_GIT_REF – La versión de Zulip a utilizar del repositorio Git para construir la imagen docker.
  • SECRET_KEY – Una clave secreta fuerte que Zulip necesita para la autenticación. Introduce la clave que hemos creado antes.
  • POSTGRES_USER – Elige un nombre de usuario para tu usuario PostgreSQL.
  • POSTGRES_DB – Elige el nombre de la base de datos para tu base de datos PostgreSQL.
  • POSTGRES_PASSWORD – Elige una contraseña segura para la base de datos PostgreSQL.
  • REDIS_PASSWORD – Elige una contraseña segura para la base de datos Redis.
  • MEMCACHED_PASSWORD – Elige una contraseña segura para el servicio Memcached.
  • RABBITMQ_DEFAULT_USER – Elige un nombre de usuario para el servicio RabbitMQ.
  • RABBITMQ_DEFAULT_PASS – Elige una contraseña segura para el servicio RabbitMQ.

Crea y abre el archivo Docker compose para editarlo.

$ nano docker-compose.yml

Añade el siguiente código para definir el servicio database.

services:
  database:
    image: "zulip/zulip-postgresql:14"
    container_name: zulip-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - "postgresql-14:/var/lib/postgresql/data:rw"
    networks:
      network:
        ipv4_address: 10.5.0.2

El servicio database contiene las siguientes opciones:

  • image – Indica a Docker que extraiga la imagen zulip-postgresql:14. Aunque la última versión de Zulip admite PostgreSQL 16, la imagen docker correspondiente sólo admite PostgreSQL 14.
  • nombre_contenedor – especifica un nombre para el contenedor.
  • reinicio – define la política de reinicio del contenedor. Hemos configurado el contenedor para que se reinicie automáticamente a menos que se detenga manualmente.
  • environment – Debes establecer variables de entorno para el contenedor. Hemos establecido tres variables que definen la base de datos PostgreSQL, el nombre de usuario y la contraseña.
  • volúmenes – Aquí creamos un volumen con nombre llamado postgresql-14 que apunta al directorio de datos PostgreSQL en el contenedor.
  • redes – utilizamos esto para dar al contenedor una dirección IP estática que pueda utilizarse para acceder a él desde otros contenedores.

A continuación, debajo de la sección del servicio database, añade la definición del servicio memcached.

  memcached:
    image: "memcached:alpine"
    restart: unless-stopped
    container_name: zulip-memcached
    command:
      - "sh"
      - "-euc"
      - |
        echo 'mech_list: plain' > "$$SASL_CONF_PATH"
        echo "zulip@$$HOSTNAME:$$MEMCACHED_PASSWORD" > "$$MEMCACHED_SASL_PWDB"
        echo "zulip@localhost:$$MEMCACHED_PASSWORD" >> "$$MEMCACHED_SASL_PWDB"
        exec memcached -S
    environment:
      SASL_CONF_PATH: "/home/memcache/memcached.conf"
      MEMCACHED_SASL_PWDB: "/home/memcache/memcached-sasl-db"
      MEMCACHED_PASSWORD: ${MEMCACHED_PASSWORD}
    networks:
      network:
        ipv4_address: 10.5.0.3

El servicio memcached contiene las siguientes definiciones específicas para él:

  • imagen – Aquí utilizaremos la imagen memcached:alpine para el contenedor. Una imagen alpina reduce el tamaño de la imagen, ya que sólo incluye lo esencial.
  • comando – Aquí ejecutaremos algunos comandos que se ejecutan tras la creación del contenedor para configurar Memcached y establecer las credenciales de Zulip. Estos comandos también anulan los comandos por defecto declarados por la imagen.
  • entorno – Aquí establecemos la ubicación del archivo de configuración de Memcached, el archivo de la base de datos de contraseñas y la contraseña.
  • red – Aquí establecemos una dirección IP privada única para el contenedor docker.

A continuación, añade la definición del servicio rabbitmq debajo de la sección memcached.

  rabbitmq:
    image: "rabbitmq:alpine"
    restart: unless-stopped
    container_name: zulip-rabbitmq
    environment:
      RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER}
      RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
    volumes:
      - "rabbitmq:/var/lib/rabbitmq:rw"
    networks:
      network:
        ipv4_address: 10.5.0.4

El servicio rabbitmq contiene las siguientes definiciones específicas para él:

  • image – Al igual que antes, utilizaremos la imagen alpine para el contenedor RabbitMQ.
  • entorno – Usamos la sección entorno para establecer el nombre de usuario y la contraseña por defecto de RabbitMQ.
  • volúmenes – Aquí creamos un volumen con nombre llamado rabbitmq que apunta al directorio RabbitMQ en el contenedor.
  • red – Igual que antes, establecemos una dirección IP única para el contenedor.

A continuación, añade la definición del servicio redis debajo de la sección rabbitmq.

  redis:
    image: "redis:alpine"
    restart: unless-stopped
    container_name: zulip-redis
    command:
      - "sh"
      - "-euc"
      - |
        echo "requirepass '$$REDIS_PASSWORD'" > /etc/redis.conf
        exec redis-server /etc/redis.conf
    environment:
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    volumes:
      - "redis:/data:rw"
    networks:
      network:
        ipv4_address: 10.5.0.5

El servicio redis contiene las siguientes definiciones específicas para él:

  • imagen – Aquí utilizaremos la imagen alpina para el contenedor Redis.
  • comando – Utilizaremos los comandos para configurar la contraseña de Redis e iniciar el servidor con ella.
  • volúmenes – Aquí creamos un volumen con nombre llamado redis que apunta al directorio de datos del contenedor.
  • red – Igual que antes, establecemos una dirección IP única para el contenedor Redis.

Por último, añade la definición del servicio zulip debajo de la sección redis.

  zulip:
    image: "zulip/docker-zulip:8.2-0"
    restart: unless-stopped
    container_name: zulip
    ports:
      - "8080:80"
    environment:
      DB_HOST: "database"
      DB_HOST_PORT: "5432"
      DB_USER: ${POSTGRES_USER}
      DISABLE_HTTPS: ${DISABLE_HTTPS}
      SSL_CERTIFICATE_GENERATION: ${SSL_CERTIFICATE_GENERATION}
      LOADBALANCER_IPS: 10.5.0.0/16
      SETTING_MEMCACHED_LOCATION: "memcached:11211"
      SETTING_RABBITMQ_HOST: "rabbitmq"
      SETTING_REDIS_HOST: "redis"
      SECRETS_email_password: ${EMAIL_PASSWORD}
      SECRETS_rabbitmq_password: ${RABBITMQ_DEFAULT_PASS}
      SECRETS_postgres_password: ${POSTGRES_PASSWORD}
      SECRETS_memcached_password: ${MEMCACHED_PASSWORD}
      SECRETS_redis_password: ${REDIS_PASSWORD}
      SECRETS_secret_key: ${SECRET_KEY}
      SETTING_EXTERNAL_HOST: ${EXTERNAL_HOST}
      SETTING_ZULIP_ADMINISTRATOR: ${ZULIP_ADMINISTRATOR}
      SETTING_EMAIL_HOST: ${EMAIL_HOST}
      SETTING_EMAIL_HOST_USER: ${EMAIL_HOST_USER}
      SETTING_EMAIL_PORT: ${EMAIL_PORT}
      SETTING_EMAIL_USE_SSL: ${EMAIL_USE_SSL}
      SETTING_EMAIL_USE_TLS: ${EMAIL_USE_TLS}
      ZULIP_AUTH_BACKENDS: ${ZULIP_AUTH_BACKENDS}
      SETTING_NOREPLY_EMAIL_ADDRESS: ${EMAIL_NOREPLY_ADD}
      # Uncomment this when configuring the mobile push notifications service
      # SETTING_PUSH_NOTIFICATION_BOUNCER_URL: ${ZULIP_PUSH_NOTIFICATION_BOUNCER_URL}
    volumes:
      - "zulip:/data:rw"
    ulimits:
      nofile:
        soft: 1000000
        hard: 1048576
    networks:
      network:
        ipv4_address: 10.5.0.6

El servicio zulip contiene las siguientes definiciones específicas para él:

  • puertos – El contenedor Zulip ejecuta nginx como parte del proceso. Aquí hemos asignado el puerto 80 del contenedor al puerto 8080 del servidor anfitrión. Lo utilizaremos más adelante para configurar Nginx como gestor proxy fuera del contenedor.
  • entorno – Aquí configuramos variables de entorno para configurar Zulip. La mayoría de las variables de entorno se recogen del archivo .env que hemos explicado antes. El host de la base de datos se establece en el servicio de contenedor database y el puerto en el puerto predeterminado PostgreSQL 5432. La variable LOADBALANCER_IPS se establece en la subred de red de Docker. Esto es necesario para que Zulip funcione desde detrás de un servidor proxy inverso. La variable MEMCACHED_LOCATION se establece en el servicio memcached con su puerto por defecto 11211. Del mismo modo, hemos configurado los hosts RabbitMQ y Redis para que apunten a sus respectivos servicios de contenedor.
  • volúmenes – Aquí establecemos un volumen con nombre y lo fijamos en el directorio /data en modo lectura-escritura.
  • ulimits – Aquí establecemos los límites blandos y duros para el uso de recursos del usuario para el contenedor.
  • networks – Igual que antes, establecemos una dirección IP única para el contenedor Zulip.

Y por último, pega el siguiente código.

volumes:
  zulip:
  postgresql-14:
  rabbitmq:
  redis:
networks:
  network:
    driver: bridge
    ipam:
      config:
        - subnet: 10.5.0.0/16
          gateway: 10.5.0.1

Aquí definimos los volúmenes con nombre que creamos anteriormente para nuestros contenedores. A continuación, definimos la red puente para conectar todos los contenedores y proporcionamos las direcciones IP de subred y puerta de enlace.

Una vez terminado, guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida una vez terminado.

El archivo Docker compose terminado tendrá el siguiente aspecto.

services:
  database:
    image: "zulip/zulip-postgresql:14"
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - "postgresql-14:/var/lib/postgresql/data:rw"
    networks:
      network:
        ipv4_address: 10.5.0.2
  memcached:
    image: "memcached:alpine"
    restart: unless-stopped
    command:
      - "sh"
      - "-euc"
      - |
        echo 'mech_list: plain' > "$$SASL_CONF_PATH"
        echo "zulip@$$HOSTNAME:$$MEMCACHED_PASSWORD" > "$$MEMCACHED_SASL_PWDB"
        echo "zulip@localhost:$$MEMCACHED_PASSWORD" >> "$$MEMCACHED_SASL_PWDB"
        exec memcached -S
    environment:
      SASL_CONF_PATH: "/home/memcache/memcached.conf"
      MEMCACHED_SASL_PWDB: "/home/memcache/memcached-sasl-db"
      MEMCACHED_PASSWORD: ${MEMCACHED_PASSWORD}
    networks:
      network:
        ipv4_address: 10.5.0.3
  rabbitmq:
    image: "rabbitmq:alpine"
    restart: unless-stopped
    environment:
      RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER}
      RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
    volumes:
      - "rabbitmq:/var/lib/rabbitmq:rw"
    networks:
      network:
        ipv4_address: 10.5.0.4
  redis:
    image: "redis:alpine"
    restart: unless-stopped
    command:
      - "sh"
      - "-euc"
      - |
        echo "requirepass '$$REDIS_PASSWORD'" > /etc/redis.conf
        exec redis-server /etc/redis.conf
    environment:
      REDIS_PASSWORD: ${REDIS_PASSWORD}
    volumes:
      - "redis:/data:rw"
    networks:
      network:
        ipv4_address: 10.5.0.5
  zulip:
    image: "zulip/docker-zulip:8.1-0"
    restart: unless-stopped
    #build:
    #  context: .
    #  args:

    #    ZULIP_GIT_URL: ${ZULIP_GIT_URL}
    #    ZULIP_GIT_REF: ${ZULIP_GIT_REF}
        # Set this up if you plan to use your own CA certificate bundle for building
        # CUSTOM_CA_CERTIFICATES: ${ZULIP_CUSTOM_CA_CERTIFICATES}
    ports:
      - "8080:80"
    environment:
      DB_HOST: 10.5.0.2
      DB_HOST_PORT: "5432"
      DB_USER: ${POSTGRES_USER}
      DISABLE_HTTPS: ${DISABLE_HTTPS}
      LOADBALANCER_IPS: 10.5.0.6
      SSL_CERTIFICATE_GENERATION: ${SSL_CERTIFICATE_GENERATION}
      SETTING_MEMCACHED_LOCATION: "memcached:11211"
      SETTING_RABBITMQ_HOST: "rabbitmq"
      SETTING_REDIS_HOST: "redis"
      SECRETS_email_password: ${EMAIL_PASSWORD}
      SECRETS_rabbitmq_password: ${RABBITMQ_DEFAULT_PASS}
      SECRETS_postgres_password: ${POSTGRES_PASSWORD}
      SECRETS_memcached_password: ${MEMCACHED_PASSWORD}
      SECRETS_redis_password: ${REDIS_PASSWORD}
      SECRETS_secret_key: ${SECRET_KEY}
      SETTING_EXTERNAL_HOST: ${EXTERNAL_HOST}
      SETTING_ZULIP_ADMINISTRATOR: ${ZULIP_ADMINISTRATOR}
      SETTING_EMAIL_HOST: ${EMAIL_HOST}
      SETTING_EMAIL_HOST_USER: ${EMAIL_HOST_USER}
      SETTING_EMAIL_PORT: ${EMAIL_PORT}
      SETTING_EMAIL_USE_SSL: ${EMAIL_USE_SSL}
      SETTING_EMAIL_USE_TLS: ${EMAIL_USE_TLS}
      ZULIP_AUTH_BACKENDS: ${ZULIP_AUTH_BACKENDS}
      SETTING_NOREPLY_EMAIL_ADDRESS: ${EMAIL_NOREPLY_ADD}
      # Uncomment this when configuring the mobile push notifications service
      # SETTING_PUSH_NOTIFICATION_BOUNCER_URL: ${ZULIP_PUSH_NOTIFICATION_BOUNCER_URL}
    volumes:
      - "zulip:/data:rw"
    ulimits:
      nofile:
        soft: 1000000
        hard: 1048576
    networks:
      network:
        ipv4_address: 10.5.0.6
volumes:
  zulip:
  postgresql-14:
  rabbitmq:
  redis:
networks:
  network:
    driver: bridge
    ipam:
      config:
        - subnet: 10.5.0.0/16
          gateway: 10.5.0.1

Paso 7 – Instalar Zulip

Inicia el contenedor Zulip utilizando el siguiente comando.

$ docker compose up -d

Comprueba el estado de los contenedores utilizando los siguientes comandos.

$ docker ps

Deberías ver una salida similar.

CONTAINER ID   IMAGE                       COMMAND                  CREATED         STATUS         PORTS                                                 NAMES
fea5d02f53d7   rabbitmq:alpine             "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   4369/tcp, 5671-5672/tcp, 15691-15692/tcp, 25672/tcp   zulip-rabbitmq
01cb77f16c1a   zulip/zulip-postgresql:14   "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   5432/tcp                                              zulip-db
f5b6523a3a8c   zulip/docker-zulip:8.2-0    "/sbin/entrypoint.sh…"   2 minutes ago   Up 2 minutes   443/tcp, 0.0.0.0:8080->80/tcp, :::8080->80/tcp        zulip
c0a358209b09   redis:alpine                "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   6379/tcp                                              zulip-redis
27be352a0a35   memcached:alpine            "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   11211/tcp                                             zulip-memcached

También puedes utilizar el siguiente comando para lo mismo.

$ docker compose ps

En este caso, tu salida sería como la siguiente.

NAME              IMAGE                       COMMAND                  SERVICE     CREATED              STATUS              PORTS
zulip             zulip/docker-zulip:8.2-0    "/sbin/entrypoint.sh…"   zulip       About a minute ago   Up About a minute   443/tcp, 0.0.0.0:8080->80/tcp, :::8080->80/tcp
zulip-db          zulip/zulip-postgresql:14   "docker-entrypoint.s…"   database    About a minute ago   Up About a minute   5432/tcp
zulip-memcached   memcached:alpine            "docker-entrypoint.s…"   memcached   About a minute ago   Up About a minute   11211/tcp
zulip-rabbitmq    rabbitmq:alpine             "docker-entrypoint.s…"   rabbitmq    About a minute ago   Up About a minute   4369/tcp, 5671-5672/tcp, 15691-15692/tcp, 25672/tcp
zulip-redis       redis:alpine                "docker-entrypoint.s…"   redis       About a minute ago   Up About a minute   6379/tcp

El contenedor Zulip tardará algún tiempo en empezar a funcionar. Puedes seguir el progreso utilizando el siguiente comando.

$ docker logs zulip --follow

Verás una larga lista de comandos que se ejecutan para configurar el contenedor. Una vez que veas la siguiente salida, significa que Zulip está instalado.

2024-02-21 09:02:55,310 INFO success: go-camo entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,311 INFO success: smokescreen entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,311 INFO success: zulip-django entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,311 INFO success: zulip-tornado entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,311 INFO success: zulip_deliver_scheduled_emails entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,311 INFO success: zulip_deliver_scheduled_messages entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,311 INFO success: process-fts-updates entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,311 INFO success: cron entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,311 INFO success: nginx entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,311 INFO success: zulip_events_deferred_work entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,311 INFO success: zulip_events_digest_emails entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,311 INFO success: zulip_events_email_mirror entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,312 INFO success: zulip_events_embed_links entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,312 INFO success: zulip_events_embedded_bots entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,312 INFO success: zulip_events_invites entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,312 INFO success: zulip_events_email_senders entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,312 INFO success: zulip_events_missedmessage_emails entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,312 INFO success: zulip_events_missedmessage_mobile_notifications entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,312 INFO success: zulip_events_outgoing_webhooks entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,312 INFO success: zulip_events_user_activity entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,312 INFO success: zulip_events_user_activity_interval entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2024-02-21 09:02:55,312 INFO success: zulip_events_user_presence entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

Pulsa Ctrl + C para salir de la pantalla. Zulip está instalado. Sin embargo, aún tenemos que configurar Nginx para que sirva a Zulip.

Paso 8 – Configurar Nginx

Abre el archivo /etc/nginx/nginx.conf para editarlo.

$ sudo nano /etc/nginx/nginx.conf

Añade la siguiente línea antes de la línea include /etc/nginx/conf.d/*.conf;.

server_names_hash_bucket_size 64;

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite.

Crea y abre el archivo /etc/nginx/conf.d/zulip.conf para editarlo.

$ sudo nano /etc/nginx/conf.d/zulip.conf

Pega en él el siguiente código.

server {
    listen 80;
    listen [::]:80;
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name zulip.example.com;

    ssl_certificate /etc/letsencrypt/live/zulip.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/zulip.example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/zulip.example.com/chain.pem;

    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;
    ssl_session_tickets off;
    ssl_prefer_server_ciphers on;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001] 8.8.8.8 8.8.4.4 [2001:4860:4860::8888] [2001:4860:4860::8844] valid=60s;
    resolver_timeout 2s;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

    tcp_nopush on;
    gzip on;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_buffering off;
        proxy_read_timeout 20m;
        proxy_pass http://127.0.0.1:8080;
    }
}

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te solicite una vez hayas terminado.

Comprueba la sintaxis del archivo de configuración de Nginx.

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Reinicia el servicio Nginx.

$ sudo systemctl restart nginx

Paso 9 – Acceder a la interfaz de Zulip

Visita https://zulip.example.com/ en tu navegador, y aparecerá la siguiente pantalla.

Zulip Home - La organización no existe

Utilizando el enlace Nueva Organización de la parte superior accederás a la siguiente página.

Zulip Nueva Organización Página de Fallos

Como puedes ver, Zulip no te permite crear una organización desde el front-end. Vuelve al terminal y ejecuta el siguiente comando para crear la URL de la página de la nueva organización. Hablaremos más sobre cómo utilizar los comandos de Zulip en la siguiente sección.

$ docker compose exec -u zulip zulip /home/zulip/deployments/current/manage.py generate_realm_creation_link
Please visit the following secure single-use link to register your 
new Zulip organization:

    https://zulip.example.com/new/svvss33z4qmwewmanbbcalen

Abre la URL https://zulip.example.com/new/cjjivovgcillw44z7gsnobkv en tu navegador y accederás a la siguiente página.

Zulip Nueva Página de Organización

Introduce el nombre de tu organización, el tipo de organización, el idioma y el ID de correo electrónico para empezar a crear tu organización. Haz clic en el botón Crear organización para continuar.

En la siguiente pantalla se te pedirá que configures una cuenta.

Página de creación de cuenta Zulip

Introduce tu nombre, elige una contraseña para iniciar sesión y haz clic en el botón Registrarse para continuar.

Una vez terminado, se abrirá el panel de control de Zulip y podrás empezar a utilizarlo.

Panel de control de Zulip

Paso 10 – Comandos del Servidor Zulip

De vez en cuando, para ejecutar comandos de Zulip Server, necesitarás acceder al shell del contenedor. Para acceder al shell como usuario zulip, utiliza el siguiente comando.

$ docker compose exec -u zulip zulip bash

O puedes utilizar el siguiente comando.

$ docker exec -itu zulip zulip bash

Para ejecutar un comando dentro de un contenedor, puedes ejecutarlo utilizando un único comando, como se indica a continuación.

$ docker compose exec -u zulip zulip \
    /home/zulip/deployments/current/manage.py help <subcommand>

Sin embargo, hay un método más sencillo de hacerlo. Podemos crear un script de shell para ejecutar comandos. Crea y abre zulip_manage.sh para editarlo.

$ nano zulip_manage.sh

Pega en él el siguiente código.

#!/bin/sh

docker compose exec -u zulip zulip /home/zulip/deployments/current/manage.py "$@"

Guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida.

Haz que el script sea ejecutable.

$ chmod +x zulip_manage.sh

Para detener el servidor Zulip, utiliza el siguiente comando.

$ docker exec -u zulip zulip /home/zulip/deployments/current/scripts/stop-server
process-fts-updates: stopped
zulip-django: stopped
zulip-tornado: stopped
zulip-workers:zulip_events_deferred_work: stopped
zulip-workers:zulip_events_digest_emails: stopped
zulip-workers:zulip_events_email_mirror: stopped
zulip-workers:zulip_events_email_senders: stopped
zulip-workers:zulip_events_embed_links: stopped
zulip-workers:zulip_events_embedded_bots: stopped
zulip-workers:zulip_events_invites: stopped
zulip-workers:zulip_events_missedmessage_emails: stopped
zulip-workers:zulip_events_missedmessage_mobile_notifications: stopped
zulip-workers:zulip_events_outgoing_webhooks: stopped
zulip-workers:zulip_events_user_activity: stopped
zulip-workers:zulip_events_user_activity_interval: stopped
zulip-workers:zulip_events_user_presence: stopped
zulip_deliver_scheduled_emails: stopped
zulip_deliver_scheduled_messages: stopped

Zulip stopped successfully!

Para volver a iniciar el servidor, utiliza el siguiente comando.

$ docker exec -u zulip zulip /home/zulip/deployments/current/scripts/start-server
2024-02-21 09:18:52,932 start-server: Running syntax and database checks
System check identified no issues (28 silenced).
2024-02-21 09:18:54,944 start-server: Starting Tornado process
zulip-tornado: started
2024-02-21 09:18:56,089 start-server: Starting django server
zulip-django: started
2024-02-21 09:18:58,133 start-server: Starting workers
process-fts-updates: started
zulip-workers:zulip_events_deferred_work: started
zulip-workers:zulip_events_digest_emails: started
zulip-workers:zulip_events_email_mirror: started
zulip-workers:zulip_events_email_senders: started
zulip-workers:zulip_events_embed_links: started
zulip-workers:zulip_events_embedded_bots: started
zulip-workers:zulip_events_invites: started
zulip-workers:zulip_events_missedmessage_emails: started
zulip-workers:zulip_events_missedmessage_mobile_notifications: started
zulip-workers:zulip_events_outgoing_webhooks: started
zulip-workers:zulip_events_user_activity: started
zulip-workers:zulip_events_user_activity_interval: started
zulip-workers:zulip_events_user_presence: started
zulip_deliver_scheduled_emails: started
zulip_deliver_scheduled_messages: started
2024-02-21 09:19:19,571 start-server: Done!
Zulip started successfully!

Reinicia el servidor de forma similar.

$ docker exec -u zulip zulip /home/zulip/deployments/current/scripts/restart-server

Hay muchas tareas de gestión que puedes realizar utilizando el script manage.py incluido con Zulip.

Puedes ejecutar el script utilizando el siguiente comando. Utilizaremos el subcomando help para enumerar todas las operaciones posibles que se pueden realizar.

$ ./zulip_manage.sh help
Type 'manage.py help <subcommand>' for help on a specific subcommand.

Available subcommands:

[analytics]
    check_analytics_state
    clear_analytics_tables
    clear_single_stat
    populate_analytics_db
    update_analytics_counts

[django]
    dbshell
    makemigrations
    migrate
    shell
    showmigrations

[zerver]
    add_users_to_streams
    archive_messages
    audit_fts_indexes
    backup
    bulk_change_user_name
    change_password
    change_realm_subdomain
    change_user_email
    change_user_role
    check_redis
    checkconfig
    compilemessages
    convert_gitter_data
    convert_mattermost_data
    convert_rocketchat_data
    convert_slack_data
    create_default_stream_groups
    create_realm
    create_realm_internal_bots
    create_stream
    create_user
    deactivate_realm
    deactivate_user
    delete_old_unclaimed_attachments
    delete_realm
    delete_user
    deliver_scheduled_emails
    deliver_scheduled_messages
    edit_linkifiers
    email_mirror
    enqueue_digest_emails
    enqueue_file
    export
    export_search
    export_single_user
    export_usermessage_batch
    fetch_tor_exit_nodes
    fill_memcached_caches
    generate_realm_creation_link
    get_migration_status
    import
    list_realms
    logout_all_users
    makemessages
    merge_streams
    process_queue
    promote_new_full_members
    purge_queue
    query_ldap
    rate_limit
    reactivate_realm
    realm_domain
    register_server
    remove_users_from_stream
    reset_authentication_attempt_count
    restore_messages
    runtornado
    scrub_realm
    send_custom_email
    send_password_reset_email
    send_realm_reactivation_email
    send_test_email
    send_to_email_mirror
    send_webhook_fixture_message
    send_welcome_bot_message
    show_admins
    soft_deactivate_users
    sync_ldap_user_data
    transfer_uploads_to_s3
    unarchive_stream

El archivo entrypoint.sh de Docker también proporciona algunas otras opciones. Ejecuta el siguiente comando para verlas.

$ docker exec -it zulip bash /sbin/entrypoint.sh app:help

Deberías ver el siguiente resultado.

Available commands:
> app:help     - Show this help menu and exit
> app:version  - Container Zulip server version
> app:managepy - Run Zulip's manage.py script (defaults to "shell")
> app:backup   - Create backups of Zulip instances
> app:restore  - Restore backups of Zulip instances
> app:certs    - Create self-signed certificates
> app:run      - Run the Zulip server
> [COMMAND]    - Run given command with arguments in shell

Paso 11 – Prueba del correo electrónico saliente

Para probar tu configuración de correo saliente, puedes enviar un correo de prueba utilizando el siguiente comando.

$ ~/docker-zulip/zulip_manage.sh send_test_email [email protected]
If you run into any trouble, read:

  https://zulip.readthedocs.io/en/latest/production/email.html#troubleshooting

The most common error is not setting `ADD_TOKENS_TO_NOREPLY_ADDRESS=False` when
using an email provider that doesn't support that feature.

Sending 2 test emails from:
  * [email protected]
  * [email protected]

Successfully sent 2 emails to [email protected]

Paso 12 – Actualizar Zulip

El primer paso para actualizar Zulip es detener los contenedores existentes.

$ cd ~/docker-zulip
$ docker compose stop

Abre el archivo docker-compose.yml para editarlo.

$ nano docker-compose.yml

Comprueba la última versión disponible en la página de etiquetas DockerHub de Zulip. Modifica la versión en la sección siguiente.

.....
zulip:
    image: "zulip/docker-zulip:8.2-0"
    restart: unless-stopped
    container_name: zulip
....

Realiza cualquier otro cambio que necesites. Tendrás que consultar el registro de cambios de Zulip y el repositorio Docker GitHub de Zulip para ello.

Cuando hayas terminado, guarda el archivo pulsando Ctrl + X e introduciendo Y cuando se te pida.

Inicia de nuevo los contenedores. El contenedor Zulip se volverá a crear con los cambios.

$ docker compose up -d

Elimina los contenedores antiguos.

$ docker compose rm

Conclusión

Con esto concluye nuestro tutorial sobre la instalación y configuración del servidor de Chat Zulip en un servidor Rocky Linux 9. Puedes seguir la documentación oficial de Z ulip para explorar en detalle. Si tienes alguna pregunta, publícala en los comentarios a continuación.

También te podría gustar...