本页内容

  • 准备工作
  • 第 1 步 - 配置防火墙
  • 第 2 步 - 安装 Docker 和 Docker Compose
  • 第 3 步 - 安装 Nginx
  • 第 4 步 - 安装 SSL
  • 第 5 步 - 配置 SELinux
  • 第 6 步 - 为安装准备并配置 Zulip
  • 第 7 步 - 安装 Zulip
  • 第 8 步 - 配置 Nginx
  • 第 9 步 - 访问 Zulip 界面
  • 第 10 步 - Zulip 服务器命令
  • 第 11 步 - 测试外发邮件
  • 第 12 步 - 升级 Zulip
  • 总结

Zulip 是一款开源聊天服务器,类似于 Microsoft Teams、Rocket Chat 或 Slack。它由 Python 编写,使用 Django、PostgreSQL 和 JavaScript。它集成了超过 90 种第三方插件,包括 Github、Jira、Stripe、Zendesk、Sentry 等。你可以通过将其与 Zapier 和 IFTTT 连接来扩展集成能力。它具备私信、群聊、话题式对话、自定义频道、视频通话、拖拽文件上传、自定义表情、Giphy 集成、图片和推文预览等众多功能。Zulip 提供适用于各平台的桌面端和移动端应用,使其不受平台限制。

本教程将教你如何在 Rocky Linux 9 服务器上安装并配置 Zulip 聊天服务器。

准备工作

  • 一台运行 Rocky Linux 9 的服务器。
  • 如果用户少于 100 人,至少需要 2GB 内存;若用户超过 100 人,则需要一台 4GB 内存、2 CPU 的服务器。
  • 一个具有 sudo 权限的非 root 用户。
  • 一个已配置指向该服务器的域名,例如 zulip.example.com。
  • 所有软件包均已更新。$ sudo dnf update
  • 系统所需的一些软件包。$ sudo dnf install wget curl nano unzip yum-utils policycoreutils-python-utils -y 其中部分软件包可能已安装在你的系统中。

一台运行 Rocky Linux 9 的服务器。

如果用户少于 100 人,至少需要 2GB 内存;若用户超过 100 人,则需要一台 4GB 内存、2 CPU 的服务器。

一个具有 sudo 权限的非 root 用户。

一个已配置指向该服务器的域名,例如 zulip.example.com。

所有软件包均已更新。

$ sudo dnf update

系统所需的一些软件包。

$ sudo dnf install wget curl nano unzip yum-utils policycoreutils-python-utils -y

其中部分软件包可能已安装在你的系统中。

第 1 步 - 配置防火墙

在安装任何软件包之前,第一步是配置防火墙以开放 HTTP 和 HTTPS 端口。Rocky Linux 使用 Firewalld 防火墙。请检查防火墙的状态。

$ sudo firewall-cmd --state
running

防火墙由不同的区域(zone)组成,我们将使用默认的公共区域。列出防火墙当前激活的所有服务和端口。

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

它应该显示如下输出。

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:

在防火墙中开放 HTTP 和 HTTPS 端口。

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

重新检查防火墙的状态。

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

你应该会看到类似的输出。

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:

将所做的更改设为永久生效,并重新加载防火墙以使更改生效。

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

第 2 步 - 安装 Docker 和 Docker Compose

安装官方 Docker 仓库。

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

安装 Docker。

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

启用并运行 Docker 守护进程。

$ sudo systemctl enable docker --now

检查 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

将你的系统用户加入 Docker 组,以免每次运行 Docker 命令都要使用 sudo。

$ sudo usermod -aG docker $(whoami)

注销后重新登录服务器以使更改生效。

第 3 步 - 安装 Nginx

对于生产环境,建议使用 Nginx 代理来运行 Synapse 服务器。

Rocky Linux 9 自带较旧版本的 Nginx。你需要使用官方 Nginx 仓库来安装最新版本。

创建并打开文件 /etc/yum.repos.d/nginx.repo 进行编辑。

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

在其中粘贴以下代码。

[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

完成后,按 Ctrl + X 并在提示时输入 Y 来保存文件。

我们将安装 Nginx 主线版(mainline),因此需要启用对应的软件包。

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

安装 Nginx。

$ sudo dnf install nginx -y

验证安装。

$ nginx -v
nginx version: nginx/1.25.3

启用并启动 Nginx 服务器服务。

$ sudo systemctl enable nginx --now

检查服务的状态。

$ 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"

第 4 步 - 安装 SSL

我们需要安装 Certbot 来生成 SSL 证书。为此我们将使用 Snapd 软件包安装器。由于 Rocky Linux 默认未附带,请安装 Snapd 安装器。它需要 EPEL(Extra Packages for Enterprise Linux,企业版 Linux 附加软件包)仓库才能正常工作。

安装 EPEL 仓库。

$ sudo dnf install epel-release -y

安装 Snapd 软件包。

$ sudo dnf install snapd -y

启用并启动 Snap 服务。

$ sudo systemctl enable snapd --now

安装 Snap core 软件包,并确保你的 Snapd 版本为最新。

$ sudo snap install core && sudo snap refresh core

为 Snapd 创建必要的链接。

$ 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

安装 Certbot。

$ sudo snap install --classic certbot

使用以下命令,通过创建指向 /usr/bin 目录的符号链接,确保 Certbot 命令可以运行。

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

检查 Certbot 的版本。

$ certbot --version
certbot 2.8.0

运行以下命令生成 SSL 证书。

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

上述命令会将证书下载到服务器上的 /etc/letsencrypt/live/zulip.example.com 目录。

生成 Diffie-Hellman 组证书。

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

检查 Certbot 续期调度服务。

$ systemctl list-timers

你会发现 snap.certbot.renew.service 是被安排运行的服务之一。

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

对该过程进行演练(dry run),以检查 SSL 续期是否正常工作。

$ sudo certbot renew --dry-run

如果没有看到错误,说明一切就绪。你的证书将自动续期。

第 5 步 - 配置 SELinux

应用策略以允许连接到外部主机。

$ sudo setsebool -P httpd_can_network_connect 1

应用策略以允许 Nginx 访问 PostgreSQL。

$ sudo setsebool -P httpd_can_network_connect_db 1

第 6 步 - 为安装准备并配置 Zulip

首先,为 Zulip 创建一个密钥。请保存生成的密钥,因为我们稍后会用到它。

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

为 Zulip 的 Docker Compose 文件创建一个目录并切换到该目录。

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

创建并打开环境文件进行编辑。

$ nano .env

在其中粘贴以下代码。

[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

完成后,按 Ctrl + X 并在提示时输入 Y 来保存文件。

让我们逐一了解我们所定义的所有变量。

  • ZULIP_ADMINISTRATOR - 安装过程中创建的管理员账户的主电子邮件地址。
  • EXTERNAL_HOST - 我们用于 Zulip 安装的域名。
  • ZULIP_AUTH_BACKENDS - 定义登录方式。我们使用 EmailAuthBackend,即 Zulip 将要求输入电子邮件地址和密码。还有其他使用 Google、GitHub 和 Apple 凭据登录的认证方式。你可以在 Zulip 文档和默认的 settings.py 文件中找到更多信息。
  • ZULIP_PUSH_NOTIFICATION_BOUNCER_URL - Zulip 用于发送移动推送通知的 URL。对于 10 人或更少用户的组织的 Zulip 社区版,推送通知是免费的。根据需求不同,你的组织可能仍有资格免费使用无限推送通知。详情请查阅 Zulip 计费文档。
  • DISABLE_HTTPS - 我们将其设为 true 以禁用 Zulip 的 SSL,因为我们将在容器外部处理它。
  • SSL_CERTIFICATE_GENERATION - 由于我们已禁用 Zulip 的 SSL,需要将其设为生成自签名证书。
  • EMAIL_HOST - 启用电子邮件通知所需的 SMTP 邮件主机。在本教程中我们使用 Amazon SES 邮件服务。
  • EMAIL_HOST_USER - SMTP 邮件用户名。
  • EMAIL_PASSWORD - SMTP 邮件密码。
  • EMAIL_PORT - SMTP 邮件端口。
  • EMAIL_USE_SSL - SMTP 主机是否支持 SSL 认证。
  • EMAIL_USE_TLS - SMTP 主机是否支持 TLS 认证。
  • EMAIL_NOREPLY_ADD - 用于发送通知邮件的 no-reply 电子邮件地址。
  • ZULIP_GIT_URL - 我们使用的是 Zulip docker 镜像,但如果你想构建自定义镜像,需要输入 Zulip Git 仓库的 URL。
  • ZULIP_GIT_REF - 从 Git 仓库中用于构建 docker 镜像的 Zulip 版本。
  • SECRET_KEY - Zulip 认证所需的高强度密钥。请输入我们之前创建的密钥。
  • POSTGRES_USER - 为你的 PostgreSQL 用户选择一个用户名。
  • POSTGRES_DB - 为你的 PostgreSQL 数据库选择一个数据库名称。
  • POSTGRES_PASSWORD - 为 PostgreSQL 数据库选择一个高强度密码。
  • REDIS_PASSWORD - 为 Redis 数据库选择一个高强度密码。
  • MEMCACHED_PASSWORD - 为 Memcached 服务选择一个高强度密码。
  • RABBITMQ_DEFAULT_USER - 为 RabbitMQ 服务选择一个用户名。
  • RABBITMQ_DEFAULT_PASS - 为 RabbitMQ 服务选择一个高强度密码。

创建并打开 Docker compose 文件进行编辑。

$ nano docker-compose.yml

添加以下代码以定义数据库服务。

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

数据库服务包含以下选项:

  • image - 这告诉 Docker 拉取 zulip-postgresql:14 镜像。尽管最新版 Zulip 支持 PostgreSQL 16,但对应的 docker 镜像仅支持 PostgreSQL 14。
  • container_name - 为容器指定一个名称。
  • restart - 定义容器的重启策略。我们已将容器设为在未被手动停止的情况下自动重启。
  • environment - 你需要为容器设置环境变量。我们设置了三个变量来定义 PostgreSQL 数据库、用户名和密码。
  • volumes - 这里我们创建了一个名为 postgresql-14 的命名卷,指向容器内的 PostgreSQL 数据目录。
  • networks - 我们使用它来为容器分配一个静态 IP 地址,以便从其他容器访问它。

接下来,在数据库服务部分下方,添加 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

memcached 服务包含以下专属定义:

  • image - 这里我们将使用 memcached:alpine 镜像作为容器。alpine 镜像仅包含最基本的组件,因此可保持较小的镜像体积。
  • command - 这里我们将运行一些在容器创建后执行的命令,用于配置 Memcached 并设置 Zulip 凭据。这些命令还会覆盖镜像声明的默认命令。
  • environment - 这里设置 Memcached 配置文件的位置、密码数据库文件以及密码。
  • network - 这里为 docker 容器设置一个唯一的私有 IP 地址。

接下来,在 memcached 部分下方添加 rabbitmq 服务的定义。

  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

rabbitmq 服务包含以下专属定义:

  • image - 与之前类似,我们将使用 alpine 镜像作为 RabbitMQ 容器。
  • environment - 我们在 environment 部分设置 RabbitMQ 的默认用户名和密码。
  • volumes - 这里我们创建一个名为 rabbitmq 的命名卷,指向容器内的 RabbitMQ 目录。
  • network - 与之前相同,我们为容器设置一个唯一的 IP 地址。

接下来,在 rabbitmq 部分下方添加 redis 服务的定义。

  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

redis 服务包含以下专属定义:

  • image - 这里我们将使用 alpine 镜像作为 Redis 容器。
  • command - 我们将使用这些命令来配置 Redis 密码并以其启动服务器。
  • volumes - 这里我们创建一个名为 redis 的命名卷,指向容器内的数据目录。
  • network - 与之前相同,我们为 Redis 容器设置一个唯一的 IP 地址。

最后,在 redis 部分下方添加 zulip 服务的定义。

  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

zulip 服务包含以下专属定义:

  • ports - Zulip 容器在运行过程中会启动 nginx。这里我们将容器的 80 端口映射到宿主机服务器的 8080 端口。稍后我们将用它来在容器外部将 Nginx 配置为代理。
  • environment - 这里我们通过环境变量来配置 Zulip。大部分环境变量取自我们之前讲解过的 .env 文件。数据库主机设置为数据库容器服务,端口为默认的 PostgreSQL 端口 5432。LOADBALANCER_IPS 变量设置为 docker 网络子网。这是让 Zulip 在反向代理服务器后正常工作的必要设置。MEMCACHED_LOCATION 设置为 memcached 服务及其默认端口 11211。同理,我们将 RabbitMQ 和 Redis 主机分别指向它们各自的容器服务。
  • volumes - 这里我们设置一个命名卷,并将其以读写模式挂载到 /data 目录。
  • ulimits - 这里我们为容器设置用户资源使用的软限制和硬限制。
  • networks - 与之前相同,我们为 Zulip 容器设置一个唯一的 IP 地址。

最后,粘贴以下代码。

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

这里定义我们之前为容器创建的命名卷。接下来,我们定义桥接网络以连接所有容器,并提供子网和网关 IP 地址。

完成后,按 Ctrl + X 并在提示时输入 Y 来保存文件。

完成后的 Docker compose 文件如下所示。

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

第 7 步 - 安装 Zulip

使用以下命令启动 Zulip 容器。

$ docker compose up -d

使用以下命令检查容器的状态。

$ docker ps

你应该会看到类似的输出。

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

你也可以使用以下命令达到同样效果。

$ docker compose ps

在这种情况下,你的输出将如下所示。

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

Zulip 容器需要一些时间才能启动运行。你可以使用以下命令跟踪进度。

$ docker logs zulip --follow

你会看到一长串为搭建容器而运行的命令。一旦看到如下输出,就表示 Zulip 已安装完成。

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)

按 Ctrl + C 退出界面。Zulip 已安装。不过,我们仍需配置 Nginx 来提供 Zulip 服务。

第 8 步 - 配置 Nginx

打开文件 /etc/nginx/nginx.conf 进行编辑。

$ sudo nano /etc/nginx/nginx.conf

在 include /etc/nginx/conf.d/*.conf; 这一行之前添加以下行。

server_names_hash_bucket_size 64;

按 Ctrl + X 并在提示时输入 Y 来保存文件。

创建并打开文件 /etc/nginx/conf.d/zulip.conf 进行编辑。

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

在其中粘贴以下代码。

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;
    }
}

完成后,按 Ctrl + X 并在提示时输入 Y 来保存文件。

验证 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

重启 Nginx 服务。

$ sudo systemctl restart nginx

第 9 步 - 访问 Zulip 界面

在浏览器中访问 https://zulip.example.com/,将会出现以下界面。

在 Rocky Linux 9 上使用 Docker 安装 Zulip 聊天服务器

点击顶部的“新建组织”(New Organization)链接将带你进入以下页面。

在 Rocky Linux 9 上使用 Docker 安装 Zulip 聊天服务器

如你所见,Zulip 不允许从前端创建组织。请回到终端并运行以下命令来创建新组织的页面 URL。我们将在下一节详细讲解如何使用 Zulip 命令。

$ 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

在浏览器中打开 URL https://zulip.example.com/new/cjjivovgcillw44z7gsnobkv,你将被带到以下页面。

在 Rocky Linux 9 上使用 Docker 安装 Zulip 聊天服务器

输入你的组织名称、组织类型、语言和电子邮件 ID 以开始创建组织。点击“创建组织”(Create organization)按钮继续。

在下一个界面中,系统会要求你创建一个账户。

在 Rocky Linux 9 上使用 Docker 安装 Zulip 聊天服务器

输入你的姓名,选择一个登录密码,然后点击“注册”(Sign up)按钮继续。

完成后,Zulip 仪表盘将会打开,你就可以开始使用了。

在 Rocky Linux 9 上使用 Docker 安装 Zulip 聊天服务器

第 10 步 - Zulip 服务器命令

有时,要运行 Zulip 服务器命令,你需要访问容器的 shell。要以 zulip 用户身份访问 shell,请使用以下命令。

$ docker compose exec -u zulip zulip bash

或者你可以使用以下命令。

$ docker exec -itu zulip zulip bash

要在容器内运行命令,你可以使用如下单条命令来运行。

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

不过,有一种更简单的方法。我们可以创建一个用于运行命令的 shell 脚本。创建并打开 zulip_manage.sh 进行编辑。

$ nano zulip_manage.sh

在其中粘贴以下代码。

#!/bin/sh

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

按 Ctrl + X 并在提示时输入 Y 来保存文件。

使脚本可执行。

$ chmod +x zulip_manage.sh

要停止 Zulip 服务器,请使用以下命令。

$ 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!

要再次启动服务器,请使用以下命令。

$ 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!

以类似方式重启服务器。

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

你可以使用 Zulip 自带的 manage.py 脚本完成许多管理任务。

你可以使用以下命令运行该脚本。我们将使用 help 子命令来列出所有可以执行的操作。

$ ./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

Docker 的 entrypoint.sh 文件还提供了一些其他选项。运行以下命令查看它们。

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

你应该会看到如下输出。

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

第 11 步 - 测试外发邮件

要测试你的外发邮件配置,可以使用以下命令发送一封测试邮件。

$ ~/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]

第 12 步 - 升级 Zulip

升级 Zulip 的第一步是停止现有的容器。

$ cd ~/docker-zulip
$ docker compose stop

打开 docker-compose.yml 文件进行编辑。

$ nano docker-compose.yml

在 Zulip 的 DockerHub Tags 页面查看最新可用版本,并在以下部分修改版本。

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

根据需要进行其他修改。你需要参考 Zulip 的更新日志(changelog)和 Zulip 的 Docker GitHub 仓库。

完成后,按 Ctrl + X 并在提示时输入 Y 来保存文件。

再次启动容器。Zulip 容器将应用这些更改重新创建。

$ docker compose up -d

删除所有旧的容器。

$ docker compose rm

总结

至此,关于在 Rocky Linux 9 服务器上安装和配置 Zulip 聊天服务器的教程就结束了。你可以参阅 Zulip 官方文档深入了解。如果你有任何问题,请在下方评论区留言。