06月26, 2016

网站部署多服务器之完整篇

自从网站更换了博客系统和增加了几台服务器以来,为了能实现服务器的文件和数据库统一,并能在文件修改后快速部署更新,我对服务器折腾就没有停过。因为我的博客系统不是纯静态,为了提升访问速度,我放弃了 CDN 加速,而采用了海内外多服务器用户直连的方式。而在前几篇的博客文章已经有提到里其中所用到的方法,这篇文章是对整个实现过程的细节补充和完善。当然了,实现的方法有多种多样,我的只是其中之一,仅供参考。因为脚本都是自己写的,写的有点挫,请多多指教。

本文更新说明:

  • 新增网站状态监测脚本 [2016.06.26]
  • 优化 post-receive 里数据库端口的修改方式 [2016.06.27]

首先,大概说说我现在服务器的情况:数据库是通过主从复制来实现同步;网站文件和 SSL 证书则放在一个 Git 里面,并通过 Git hook 来实现所有服务器的文件更新和服务的 reload 。

这里需要说明一下的是,我之前已经在本地搭建了一个 Ubuntu 作为调试环境,并搭建好了网页的运行环境,使用的是本地的调试数据库。我把 SSL 证书的生成工具放在了本地的 Ubuntu 系统里面,并且用 ln -s 软连接到了 Mac OS 下。

配置的过程可以参考我之前的文章,只是系统从 Centos 换成了 Ubuntu 而已:

https://g.32ph.com/post/perfect-dev-environment-on-mac.html

也就是我把所有的文件都放在了 Mac OS 的一个文件夹里,并且他还是个 Git 的私有仓库,通过软连接到 Ubuntu 里。Ubuntu 里面没有实际文件,都是软连接。那么我就能在本地测试网站的运行。而且在网站文件更新或 SSL 证书被自动更新时,Source Tree (一个 Git 客户端)能检测到文件变更。所有东西在本地环境测试无误后,只要点击 “推送” ,即可推送到远程仓库和所有的服务器上,配合 Git hook 从而实现一键更新所有服务器的文件和配置。

但这样做的前提是,图片不放在网站目录里。

为了方便大家理解,在此贴出文件目录:

|---- steven   
    |--master (主服务器,空文件)
    |--full_chained.pem
    |--session_ticket.key
    |--dhparams.pem
    |--check.sh (网站状态监测小脚本)
    |--git-hook.git
        |--hooks
            |--post-receive
    |--stevensclub (仓库)
        |--files
            |--网页文件
        |--ssl
            |--https 证书们
        |--steven_nginx.conf

Nginx 和 SSL 配置

Nginx 的配置参考 imququ 的博客,采用的是最新版的 Nginx ,不支持 SPDY:

https://imququ.com/post/my-nginx-conf.html

SSL 证书用的是 Let's Encrypt ,使用 acme.sh 这个小工具来生成的 ECC 证书,并且推荐大家用 DNS mode 来签发证书,会比较方便。

https://github.com/Neilpang/acme.sh

由于使用的是 acme.sh 来签发证书,和 imququ 文章里用到的工具不一样,所以证书的文件后缀也有点不一样。为了方便大家,我把证书部分的配置列出来,供大家参考。

当证书签发成功后,acme.sh 会有通过 crontab 来定时更新证书文件,所有不能删除 DNS 的 TXT 。

# 中间证书 + 站点证书
ssl_certificate   /steven/stevensclub/ssl/cer/fullchain.cer;

# 创建 CSR 文件时用的密钥
ssl_certificate_key   /steven/stevensclub/ssl/cer/g.32ph.com.key;

ssl_dhparam   /steven/dhparams.pem;

ssl_session_ticket_key   /steven/session_ticket.key;

ssl_trusted_certificate   /steven/full_chained.pem;

也就是我们只需要用到 acme.sh 里的 fullchain.cerxxx.com.key,其他的文件跟 imququ 博客里面的一样。

MySQL 主从复制

https://g.32ph.com/post/deployment-multi-server-mysql.html

MySQL 读写分离

https://g.32ph.com/post/mysql-mysql-read-write-split.html

Git hook 配置

https://g.32ph.com/post/use-git-hooks-to-upload-website.html

post-receive 如下配置,请根据实际情况进行修改。

#!/bin/sh

unset GIT_DIR

cd /steven/stevensclub/
git checkout . && git clean -xdf
git pull origin master

# 检测网站目录是否存在
if [ ! -d "/wwwroot/g.32ph.com/" ];then
    mkdir /wwwroot/g.32ph.com/
fi

# 仓库文件复制并替换文件到网站目录
cp -frp /steven/stevensclub/files/* /wwwroot/g.32ph.com/

# 设置权限
chown -R www.www /wwwroot/
find /wwwroot/ -type d -exec chmod 755 {} \;
find /wwwroot/ -type f -exec chmod 644 {} \;

# 根据 master 文件是否存在,判断是否修改数据库端口,从 6666 改成 8888
if [ ! -f "/steven/master" ];then
        perl -p -i -e "s/6666/8888/g" /wwwroot/g.32ph.com/app/common/config/db.js
fi

# 博客重启
cd /wwwroot/g.32ph.com/
touch .installed
pm2 restart pm2.json

# 复制并替换 Nginx 配置文件
cd /steven/stevensclub/
cp -fp steven_nginx.conf /vhost/

service nginx reload

echo All Done!

exit 0

这样下来,无论我是网页文件更新、SSL 证书更新、甚至是 Nginx 配置文件更新,都能一键推送到所有的服务器上。

小 Tips :

为了在推送的时候好看点,大家还可以在 post-receive 你喜欢的地方加上 echo 来显示进度。那么在推送的时候,也能看到哦,就好像我点击推送后:

Pushing to lon:/steven/git-hook.git
remote: From /steven/git-hook        
remote:  * branch            master     -> FETCH_HEAD        
remote:    8df7ce2..6dc05cd  master     -> origin/master        
remote: Updating 8df7ce2..6dc05cd        
remote: Fast-forward        
remote:  files/app/common/config/db.js | 17 +++++++++++++++++        
remote:  steven_nginx.conf             |  2 --        
remote:  2 files changed, 17 insertions(+), 2 deletions(-)        
remote:  create mode 100644 files/app/common/config/db.js        
remote: copying new files        
remote: setting files chmod        
remote: slave server,link db.js successful        
remote: restarting stevensclub        
remote: [PM2] Applying action restartProcessId on app [firekylin](ids: 0)        
remote: [PM2] [firekylin](0) ✓        
remote: ┌───────────┬────┬──────┬───────┬────────┬─────────┬────────┬────────────┬──────────┐        
remote: │ App name  │ id │ mode │ pid   │ status │ restart │ uptime │ memory     │ watching │        
remote: ├───────────┼────┼──────┼───────┼────────┼─────────┼────────┼────────────┼──────────┤        
remote: │ firekylin │ 0  │ fork │ 88888 │ online │ 88      │ 0s     │ 9.656 MB   │ disabled │        
remote: └───────────┴────┴──────┴───────┴────────┴─────────┴────────┴────────────┴──────────┘        
remote:  Use `pm2 show <id|name>` to get more details about an app        
remote: update nginx conf successful        
remote: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok        
remote: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful        
remote:  * Reloading (via HUP) Nginx Server...        
remote: Hangup        
remote: nginx reloaded        
remote: All Done!        
Completed successfully

本地 Git 环境配置

配置完 Git hook 以后,我们只需要在本地的仓库添加远程仓库的地址,即可实现推送时推送到多个地方。

但这里有个不足的地方就是,他会按照 pushurl 的顺序进行逐个推送的,这样就会有个生效时间延迟的问题。而且首次推送耗时较长,特别是在连接较远的服务器的时候。

像 coding 这样的网站已经推出了 web hook ,应该能很好的解决以上问题,目前还在研究如何使用中。

打开 config 新增 pushurl 即可:

[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true
[branch "master"]
    remote = github
    merge = refs/heads/master
[remote "github"]
    url = git@xxxx.com:xxx/xxx.git
    fetch = +refs/heads/*:refs/remotes/github/*
    pushurl = git@xxx.com:xxx/xxx.git
    pushurl = sh:/steven/git-hook.git
    pushurl = lon:/steven/git-hook.git
    pushurl = gz:/steven/git-hook.git
    pushurl = us:/steven/git-hook.git

网站状态监测脚本

由于 Firekylin 不能开机启动,当我们重启了服务器或者网站进程意外退出时,网页就会变得不可访问。特别是在多服务器的情况下,某台服务器出了问题很难感知到。(试过上海服务器挂了 3 天,浑然不知 ... )曾试过用监控宝来监测,但发现他的指定主机 IP 监测网站的功能并不 work ,指定了 IP 但还是根据 DNS 返回的结果来监控的。想了想还是自己写了个小脚本好了,与其写脚本来监控,倒不如让脚本直接把它修好。当然了,这个脚本是要在你文件没有问题的前提下运行才有效,不然怎样也 start 不起来。

#!/bin/bash 

URL=https://g.32ph.com/

HTTP_CODE=`curl -o /dev/null -s -w "%{http_code}" "${URL}"`

if [ $HTTP_CODE != 200 ];then 
    echo $HTTP_CODE
    echo website is not runing,fixing...
    cd /wwwroot/g.32ph.com/
    pm2 restart pm2.json
    service nginx restart
else
    echo website is running
fi

exit 0

由于我是用了 CloudXNS 的 “X优化” ,因此在服务器运行这个脚本,DNS 返回的结果肯定就是本机 IP ,所以 URL 直接写域名就好了。

文件保存好后,运行 chmod +x check.sh 修改权限,

运行 crontab -e 编辑计划任务,

加入 */3 * * * * /steven/check.sh >/dev/null 2>&1 代表每三分钟运行一次,大家可以自行修改时间。

MySQL 主数据库自动打包备份到七牛

由于该部分在使用过程中发现有 BUG ,正在努力调试中,暂不贴出。

(未完,待续)

本文链接:https://g.32ph.com/post/deployment-multi-server-final.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。