本页内容
- 准备工作
- 第 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/,将会出现以下界面。

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

如你所见,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,你将被带到以下页面。

输入你的组织名称、组织类型、语言和电子邮件 ID 以开始创建组织。点击“创建组织”(Create organization)按钮继续。
在下一个界面中,系统会要求你创建一个账户。

输入你的姓名,选择一个登录密码,然后点击“注册”(Sign up)按钮继续。
完成后,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 官方文档深入了解。如果你有任何问题,请在下方评论区留言。