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 验证会受到影响。
大功告成~