Discuz! X 容器化改造指南
自2021年,我开始运维一个日均PV 10万+的 Discuz! X论坛站点。接手后陆续发现一些问题难以通过传统方法解决,遂决定对其进行容器化改造。
1. 为什么要对 Discuz! X 进行容器化改造#
1.1. 原部署方案为单机部署#
Discuz! X 论坛的默认部署方式是单机部署,自然会出现一些单机部署会发生的问题:
- 单点故障:如果这台机器出现任何问题,会导致整个论坛处于不可用状态。包括 Nginx、PHP 进程出现问题,机器关机、重启等。
- 扩容困难:遇到搞活动时期,扩容需重启(使用的虚拟机为腾讯云CVM,可动态修改虚拟机配置,但需要重启,重启时间为数分钟)。而且一旦容量预估失败,活动期间调整容量几乎不可能。
1.2. PHP环境部署复杂#
- 扩展安装困难:开发以来的很多扩展安装比较复杂,新增一个扩展往往要倒腾半天。例如 grpc 扩展,开发人员本地安装成功但我按照开发人员的方法无法安装成功;更让人崩溃的是在测试环境安装调试OK后再生产环境安装依然出现了问题。pcel 执行也有失败的概率。
- 扩展安装时间久:新增 PHP 扩展往往需要停服更新,往往安装方法既受限于网速,又受限于源码编译构建的速度。万一出现意料之外的情况需要增加停机时间。
1.3. 支持滚动升级#
实现版本更新用户无感知
2. 各项关键问题的解决方案#
2.1. PHP 环境安装使用 Docker 镜像#
这边使用的基础镜像为:php:7.2-fpm,为 PHP 官方发布的镜像。该镜像也能非常方便的安装 PHP 扩展。该项目也依赖了 composer,在 PHP 镜像中编译添加 composer 比较困难,比较简单的方法是,使用 COPY 命令从 composer 镜像中复制 composer 的二进制文件。示例的 Dockerfile 如下:
| 1 | FROM php:7.2-fpm | 
2.2. NFS 解决 Discuz! X 负载均衡问题#
Discuz! X 多机部署的一个问题就在于 Discuz! X 使用了本地缓存目录和本地存储目录。我这边的解决方案是:
- 本地存储目录(用户上传的图片等资源):存储至腾讯云COS,使用此方案需要部署 cos-ftp-server ,其 Dockerfile 如下: - 1 
 2
 3
 4
 5
 6
 7- FROM python:2.7 
 COPY ./cos-ftp-server-V5-master.zip /
 RUN mkdir /tmp/cos && pip install -i https://mirrors.tencent.com/pypi/simple/ cos-python-sdk-v5 pyftpdlib psutil && unzip cos-ftp-server-V5-master.zip && rm cos-ftp-server-V5-master.zip && cd cos-ftp-server-V5-master && python setup.py install
 CMD cd cos-ftp-server-V5-master && python ftp_server.py- 需要注意的是,示例的 Dockerfile 中没有修改 cos-ftp-server 的配置文件 - vsftpd.conf,推荐在 k8s 中使用 configmap 替换此文件。
- 缓存目录:使用腾讯云文件存储,其支持使用 NFS 协议挂载到容器。假设网站的根目录为 - /www/src,我们将在 yaml 中挂载 NFS 到- /nfs/src。- pv - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15- apiVersion: v1 
 kind: PersistentVolume
 metadata:
 name: {{ .Release.Name }}
 labels:
 pv: {{ .Release.Name }}-pv
 spec:
 capacity:
 storage: 150Gi
 accessModes:
 - ReadWriteMany
 persistentVolumeReclaimPolicy: Retain
 nfs:
 path: /
 server: {{ .Values.nfs }}- pvc - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14- kind: PersistentVolumeClaim 
 apiVersion: v1
 metadata:
 name: {{ .Release.Name }}
 spec:
 accessModes:
 - ReadWriteMany
 storageClassName: ""
 resources:
 requests:
 storage: 150Gi
 selector:
 matchLabels:
 pv: {{ .Release.Name }}-pv
- Deployment - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11- ... 
 volumeMounts:
 - mountPath: "/nfs"
 name: {{ .Release.Name }}
 ...
 volumes:
 - name: {{ .Release.Name }}
 persistentVolumeClaim:
 claimName: {{ .Release.Name }}
 readOnly: false
 ...
 - 在 Dockerfile 中,软链接缓存目录(缓存目录众多,如下仅为示例)。 - 1 
 2
 3
 4
 5- ... 
 ADD ./www/ /www/src
 RUN ln -s /nfs/src/data /www/src/
 ...
 
3. 整体架构参考#
graph TB
  client("用户")
  cos(腾讯云COS)
     mysql(MySQL)
     nfs(NFS)
  clb(腾讯云CLB)
  subgraph k8s
        subgraph bbs_pod
            nginx(Nginx)
            php(PHP)
            cos-ftp(cos-ftp)
        end
        subgraph redis_pod
            redis(Redis)
        end
  end
    client --> clb --> nginx --> php --> cos-ftp --> cos
    php --> redis
    php ---> mysql
    nginx --> nfs
    php ---> nfs
4. 交付部署流程优化#
使用了 Docker k8s 部署 Discuz! X 后,可以借助腾讯蓝鲸蓝盾优化整个部署流程,实现全流程零运维。
graph TB
    a1(Git 触发流水线构建) --> a2(构建 Docker 镜像) --> a3(通过 helm 滚动更新测试环境) --> a4(通过 helm 滚动更新生产环境)