Docker 部署禅道及 Nginx 反向代理设置

简单介绍

禅道是国产的开源项目管理软件,专注研发项目管理,内置需求管理、任务管理、bug管理、缺陷管理、用例管理、计划发布等功能,可以实现软件的完整生命周期管理,我个人还是觉得很不错的。

禅道提供了多样化的一键安装包,可以减少很多工作。我之前因为有其他的项目共存,并没有使用一键安装包,而选择的是源码部署。虽然配置也是简单方便,但还是本着能懒就懒得原则,我在升级禅道的时候选择使用 docker 的形式部署,便于之后管理。

Docker 使用 Go 语言编写,并利用 Linux 内核的几个特性来提供其功能。Docker 使用容器技术来提供沙箱环境,运行多个容器时,各个容器之间不受影响,安全性较高。可适用于自动化测试、打包、持续继承和发布应用程序等多种场景。

Docker 版的 禅道内置了数据库、 Apache 和 PHP 等运行环境,可以拿来就能用。这次也是趁着这个机会,记录部署的经过,以便自己或看这篇文章的你有用到的时候能够提供一点点帮助。

获取禅道镜像

此步骤可以省略

如果你的电脑或者服务器上已经安装好了 Docker,可以直接使用命令行拉取禅道的 Docker 镜像,

格式为 docker pull easysoft/zentao:[标签]

例如拉取开源15.7.1版:

docker pull easysoft/zentao:15.7.1

标签对应的就是禅道的版本号,具体和可以查看禅道的官网,或者 Docker Hub ( hub.docker.com/r/easysoft/zentao/tags) 所罗列出来标签。

docker run 设置镜像和标签后,会先找本地是否有对应的镜像和标签,如果找不到会自动到 hub.docker.com 获取镜像回来,所以这个步骤可以省略,在直接看 运行禅道 中的命令即可。

运行禅道镜像

等待下载完成就可以运行镜像,也可以直接运行下面的命令:(请根据自身实际情况修改)

docker run --name [容器名] -p [主机端口]:80 -v [主机禅道目录]:/www/zentaopms \
       -v [主机mysql目录]:/var/lib/mysql \
       -e MYSQL_ROOT_PASSWORD=[数据库密码] -d easysoft/zentao:[镜像标签]
  • 容器名:启动的容器名字,可随意指定;
  • 主机端口:主机端口为web访问端口;
  • 主机禅道目录:可以不指定,但是为了方便禅道代码、附件等数据的持久化,强烈建议指定。非升级情况需指定空目录;
  • 主机mysql目录:可以不指定,但是为了方便禅道数据持久化,强烈建议指定。非升级情况需指定空目录;
  • 数据库密码: 容器内置 MySQL 用户名为 root,默认密码 123456,如果不修改可以不指定该变量,如果想更改密码可以设置 MYSQL_ROOT_PASSWORD 变量来更改密码;
  • 镜像标签:禅道版本。

例如:

docker run --name pm -p 10086:80 -v /home/wenhsing/pm:/www/zentaopms \
       -v /home/wenhsing/db:/var/lib/mysql \
       -e MYSQL_ROOT_PASSWORD=password -d easysoft/zentao:15.7.1

等待镜像完成运行,会提示一串哈希值,表示镜像已经成功运行,这个时候其实就可以访问禅道了。

如果没有反向代理的需求或者只是在本地部署测试之类的话,可以选择仅 Docker 的方式运行,跳过 Nginx 的相关配置。

仅 Docker 的方式运行可以通过 IP 加端口号的形式访问,IP 即为 Docker 所在的电脑或者服务器的 IP,端口号即为运行镜像时设置的主机端口。

要是只部署一个禅道项目在服务器上,可以直接将主机的80端口绑定容器上,这样可以直接使用禅道容器中已经安装好的 Apache 提供网页服务。

如果想要通过域名的形式访问,先在域名商处配置域名指向 Docker 所在的主机公网 IP,接着在服务器上运行禅道镜像时将 主机端口 设置为 80 端口,最后进入容器配置一下 Apache 的域名,应用设置后就可以通过域名的形式访问了。

配置 Nginx

因为我自己的还有其他项目在服务器上运行,所以我继续使用了 Nginx 的反向代理,有一说一是真的方便(_)。

这里提供简单的 Nginx 配置示例,将数据转发到 docker 对应的端口上,这里指定的是 10086,实际情况根据你自己设置的修改。

注意要设置转发的请求头信息,否则禅道无法获取这些信息,无法进行安装,无限重复跳转到第一步安装上。

server {
    listen        80;
    server_name  localhost;

    location / {
        proxy_pass   http://127.0.0.1:10086;

        # Fixed can not be install
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

server {
    listen        443 ssl;
    server_name  localhost;

    ssl on;
    ssl_certificate "/cert.pem";
    ssl_certificate_key "/cert.key";
    ssl_session_timeout  5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass   http://127.0.0.1:10086;

        # Fixed can not be install
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Fixed login failed
        proxy_set_header REFERER $http_referer;
    }
}

完成禅道安装

很开心你能看到这里,说明你已经进入禅道安装的尾声了。如果你不是全新安装的话,建议查看如何升级禅道,跳过这段,节省时间。

首先通过浏览器访问禅道,如果你是仅 Docker 的方式运行禅道,就根据你自己的情况访问 IP加端口号(80 端口的话可以省略)或者域名。如果设置了 Nginx 反向代理,访问的地址根据 Nginx 设置的来。

禅道会检测是否安装,若没有安装会自动跳转安装页面。

同意协议以及通过系统检查之后,进入配置文件生成页面。填写数据库的相关配置,注意数据库的密码就是 Docker 运行的时候指定的数据库密码,其他可以根据自身情况填写。

确认配置信息后就到账号相关的设置了,填写公司名称、管理员账号密码等,保存之后提示安装成功,后面的不用我说你也知道可以登录系统了。

问题及解决方案

Q:无法安装,配置数据库信息后无法进入下一步,无限跳转到第一步安装步骤页面。

发现这个问题的时候,一开始是直接在容器中查看,发现没有日志、没有写入文件,导致无法排查问题。

后来在本地直接运行容器,居然一路畅通无阻的完成了安装,至此判断了问题是在 Nginx 的转发上。

从源码上看,是因为 $_POST 获取的参数为空,所以直接跳转了安装页,也证实了 Nginx 的配置需要调整。

找到问题的原因之后,开始解决问题。

解决方法很简单,在 Nginx 的端口转发配置里面设置下面三行设置,将请求的参数一并转发给 Docker 就好了。

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Q: 部署的禅道在 http 下可以正常登录,但 https 无法登录

我其他的项目都用了 Nginx 反向代理,通过 Nginx 提供 https 服务,所以我就 cp 了一份配置文件修改成禅道的反向代理设置,结果页面访问到了,但是却无法登录。

在排查的过程中发现才发现了 http 可以使用,但是 https 无法访问,放弃治疗的我只能先用 http 的形式登录禅道,这个问题也被我拖了一段时间,直到 N 天后的我看着域名不是 https 实在难受,于是又再次花时间看了这个问题。

经过半天的调试,查看了代码之后发现是 禅道的 CSRF 相关问题。

对应的文件是: framework/base/router.class.php
对应的方法是: public function setSuperVars() 设置超级变量

/* Change for CSRF. */
if($this->config->framework->filterCSRF)
{
    $httpType = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http';
    if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) and strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') $httpType = 'https';
    if(isset($_SERVER['REQUEST_SCHEME']) and strtolower($_SERVER['REQUEST_SCHEME']) == 'https') $httpType = 'https';

    $httpHost = zget($_SERVER, 'HTTP_HOST', '');
    $apiMode  = (defined('RUN_MODE') && RUN_MODE == 'api') || isset($_GET[$this->config->sessionVar]);
    if(!$apiMode && (empty($httpHost) or strpos($this->server->http_referer, "$httpType://$httpHost") !== 0)) $_FILES = $_POST = array();
}

可以看到代码的最后最后面判断了 HTTP 请求的 referer,如果不符合会被重置为空。

解决方法:

方法一:设置 Nginx 转发配置

在 Nginx 转发设置 referer 字段,将浏览器的 referer 传递给 Docker。

proxy_set_header REFERER $http_referer;

这个方法不用修改源代码,设置好后重启 Nginx即可,简单方便快捷。

方法二:关闭禅道 CSRF 验证

以交互式命令进入 Docker 容器:

docker exec -it pm /bin/bash

切换到禅道应用目录下:

cd /var/www/zentaopms/

在 config/my.php 文件中添加:

$config->framework->filterCSRF      = false;

这个方法关闭了禅道的 CSRF 验证,相对应的会有风险,所以这个方案不是很推荐。

方法三:修改 Docker 中的禅道源代码

打开 framework/base/router.class.php 文件

找到 CSRF 相关代码,将最后一行注释

// if(!$apiMode && (empty($httpHost) or strpos($this->server->http_referer, "$httpType://$httpHost") !== 0)) $_FILES = $_POST = array();

这个方法改动了源代码,如果之后项目升级,需要重新修改,并且对应的 CSRF 验证会受到影响。


大功告成~

posted @ 2021-12-27 09:06  Wenhsing  阅读(4035)  评论(2编辑  收藏  举报