因为一些内容审查方面的原因,一个运行了很久的论坛被要求限期迁移出现有机房。管制很严,要求在1-2天内完成。论坛的文件(主要是图片及附件)100多G,数据库有80G。我评估了一下,导出数据,部署新环境,导入数据,调试到能正常访问,1-2天可能有点紧张,万一迁移不顺利,还得花费更多的时间。但没办法拒绝,先答应下来再说。


如果不想看文字,猛戳此处看视频,临场感更强。


跟其它技术人员讨论了一下,先临时采取投机取巧方式,应付检查,其措施如下:

1、在其它不受内容审核的地区,部署一个代理,用nginx就行。

2、修改现论坛web的监听端口(由80改成8989),暂时先不改,配好nginx反向代理以后,绑定本地hosts,域名指向新部署服务器的ip地址,访问确认没问题,再修改到8989端口。同时代理nginx转发端口也对应改成8989。

3、修改dns,把域名解析到代理web服务器的ip。

这样处理,避开了审查,为迁移赢得了时间。



安排异地机房的技术给我们部署系统,因为经验以及其它方面的原因,花了将近一天的时间才交付过来(幸亏采取了前边的临时措施)。为了加快进度,我们自己做了分工,一人负责新系统部署php、nginx和mysql运行环境,而我负责导出数据。导出数据分两部分:论坛数据本身压缩打包、数据库数据导出再压缩打包。


开始,我习惯性的用mysqldump --all-databases 全库导出,过程很顺利,导出后打包再复制到别的地方,大概2个小时。新系统那边,环境已经部署好了,我试着进行导入,进行了几十分钟,报错,因为中文字符的问题,尝试了几种方式,还是一样,只得放弃。重新回源服务器,换成innobackup导出。悲催的是,这个服务器的系统太老了,为centos 5.8,安装xtrabackup有依赖问题,需要使用yum来处理一些依赖,但系统自带的yum源没有了,只好重新构造了一个Centos-Base.repo,其内容如下:

[base]

name=CentOS-$releasever - Base

#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os

#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/

baseurl=http://vault.centos.org/5.11/os/$basearch/

gpgcheck=1

gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5


#released updates

[updates]

name=CentOS-$releasever - Updates

#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates

#baseurl=http://mirror.centos.org/centos/$releasever/updates/$basearch/

baseurl=http://vault.centos.org/5.11/updates/$basearch/

gpgcheck=1

gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5


#additional packages that may be useful

[extras]

name=CentOS-$releasever - Extras

#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras

#baseurl=http://mirror.centos.org/centos/$releasever/extras/$basearch/

baseurl=http://vault.centos.org/5.11/extras/$basearch/

gpgcheck=1

gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5


#additional packages that extend functionality of existing packages

[centosplus]

name=CentOS-$releasever - Plus

#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus

#baseurl=http://mirror.centos.org/centos/$releasever/centosplus/$basearch/

baseurl=http://vault.centos.org/5.11/centosplus/$basearch/

gpgcheck=1

enabled=0

gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5


#contrib - packages by Centos Users

[contrib]

name=CentOS-$releasever - Contrib

#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=contrib

#baseurl=http://mirror.centos.org/centos/$releasever/contrib/$basearch/

baseurl=http://vault.centos.org/5.11/contrib/$basearch/

gpgcheck=1

enabled=0

gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

再测试 yum install lrzsz 正常。


从网上下载备份的安装包percona-xtrabackup-2.3.10.tar.gz,用yum 安装libaio-devel、gcc,vim-comm等几个依赖包。另外,在编译该软件是,会提示cmake的版本过低,需要替换一下。

下载cmake-3.9.6.tar.gz,tar开以后,执行 ./configure --prefix=/usr/local/cmake ;make ;make install 把新版本限定到目录/usr/local/cmake,这样就不用处理旧版本,也不会与其产生冲突。编译安装时,带上cmake的全路径:

/usr/local/cmake/bin/cmake -DBUILD_CONFIG=xtrabackup_release && make -j4 

如果出现“CMake Error at cmake/libev.cmake:23 (MESSAGE)”报错信息,手动安装包libev-3.7.tar.gz(yum不到的),步骤为:

tar zxvf libev-3.7.tar.gz

cd libev-3.7

./configure

make;make install

这个不需要指定安装路径,方便cmake执行的时候找得到。

费了好大一通时间,才把这个备份软件安装好,虽然描述得有些啰嗦,但可能对其它人排错会有帮助。


现在,开始切入正题.

执行备份

1、确保源数据库处于启动状态

2、执行指令

innobackupex --user=root --password=MaGiCdB1 --defaults-file=/etc/my.cnf /data1/dumpdir

备份目录是任意足够大的分区

3、检查备份目录,是否产生数据

4、执行 innobackupex --apply-log  /data1/dumpdir/2018-03-30_11-10-53


压缩和传输文件(论坛数据文件与此步骤相同,不再说明)


tar czvf 2018-03-30_11-10-53.tgz  2018-03-30_11-10-53

rsync -avz -e "ssh -p 2222 " 2018-03-30_11-10-53.tgz  120.189.55.109:/data1/

特别值得注意的是,由于数据量比较大,避免shell中断导致任务终止,尽量在screen下进行操作,就算shell退出,也不会受到影响。

到此为止,源服务器的操作暂告一段落。


数据量相对来说比较大,因此传输过程花了好长一段时间。在这同时,在目的服务器,把安装好的环境配置成与源站基本一致(操作系统版本、php、ngnix、mysql做了升级):相同的目录结构、相同的内网ip、相同的用户帐号。这样就可以直接把源站nginx配置、mysql配置、php原样同步过来。目前因为没有数据库数据导入及论坛文件,我先试着在nginx配置的站点根文档写一个php测试脚本test.php,内容为:<? phpinfo(); ?>.把nginx及php服务都启动起来,浏览器访问该url,检查一下php都有哪些模块被加载,与源站是不是差不多的情形。测试完记得删掉它,这个是安全隐患哟!


nginx的主配置文件nginx.conf如下(节录):

user  www www;

worker_processes  8;


#dso {

#     load ngx_http_cache_purge_module.so;

#}


worker_rlimit_nofile 51200;


events {

 use epoll;

 #use kqueue;   #FreeBSD system

 worker_connections 51200;

}


http {

 include       mime.types;

....................此处省略.............................

map $request_method $limit {

        default         "";

        POST            $binary_remote_addr;

    }

limit_conn_zone $binary_remote_addr zone=one_limit:10m;

limit_req_zone $limit zone=zone_limit_post:10m rate=10r/m;

log_format post     '$remote_addr - $remote_user [$time_local]  '

                        '"$request" $status $body_bytes_sent '

                        '"$http_referer" "$http_user_agent" '

                        '$host $request_time $proxy_add_x_forwarded_for 

$request_body';


access_log /data/logs/http.a.log;

error_log  /data/logs/http.e.log;


#include vhosts/test.conf;

include vhosts/default.conf;

include vhosts/bbs.formyz.net.conf;

include vhosts/file.formyz.net.conf;

}

某个站点的配置文件 bbs.formyz.net.conf

server  {

    listen       80;

    server_name     bbs.formyz.net;

    error_page  404 http://bbs.formyz.net;

       

    access_log /data/logs/bbs.formyz.net-access;

    error_log  /data/logs/bbs.formyz.net-error;

    limit_conn one_limit 5;

    

    root  /data/html/bbs.formyz.net;

    index index.html index.htm  forum.php index.php;

    if ($host != 'bbs.formyz.net' ) {

        rewrite ^/(.*)$ http://bbs.formyz.net/$1 permanent;

    }


    if (-d $request_filename) {

        rewrite ^/(.*)([^/])$ http://$host:$server_port/$1$2/ permanent;

    }


    location ^~ /config {

        deny all;

    }


    location ^~ /.git

     {

        deny all;

      }


    location ~ /(data|static|template|images|uc_server/data)/.*\.(php|php5|html)?$ {

    deny all;

    location ~ .*\.(php|php5)$ {

        limit_req zone=zone_limit_post burst=5;

        if ($request_method = "POST") {

            access_log /data/logs/bbs.formyz.net-post post;

        }


        root    /data/html/bbs.formyz.net;

               fastcgi_pass  127.0.0.1:9000;

               fastcgi_index index.php;

               include fastcgi_params;

    }


    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {

         expires      30d;

    }


    location ~ .*\.(js|css)?$ {

        expires      1h;

    }


    location / {

        limit_conn one_limit 5;

        rewrite ^(.*)/topic-(.+)\.html$ $1/portal.php?mod=topic&topic=$2 last;

        rewrite ^(.*)/article-([0-9]+)\.html$ $1/portal.php?mod=article&articleid=$2 last;

        rewrite ^(.*)/forum-(\w+)-([0-9]+)\.html$ $1/forum.php?mod=forum display&fid=$2&page=$3 last;

        rewrite ^(.*)/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=viewthread&tid=$2&extra=page%3D$4&page=$3 last;

        rewrite ^(.*)/group-([0-9]+)-([0-9]+)\.html$ $1/forum.php?mod=group&fid=$2&page=$3 last;

        rewrite ^(.*)/space-(username|uid)-(.+)\.html$ $1/home.php?mod=space&$2=$3 last;

        rewrite ^(.*)/([a-z]+)-(.+)\.html$ $1/$2.php?rewrite=$3 last;

        break;

    }

}

这里是每一个站点,用一行include 显示的包含。偏偏有不少人,至少我碰到不少,喜欢用 include *.conf把所有的站点都包含进去,我不太赞同这种做法,配置的时候你是方便了,可是到了要维护的时候,就不是那么方便了。为什么这样说?假设现在做维护,我打开nginx.conf大概就可以看到有哪些站点(当然文件命名也要规划好,能望文生义);特别是在一个nginx下站点多的场景,对某个站点进行临时维护,只要在主配置文件nginx.conf注释掉与它关联的include行,重载nginx即可。


接下来给出php-fpm.conf文件的部分内容:

[global]

error_log = log/php-fpm.log

process.max = 5000

rlimit_files = 10240


[www]

user = www

group = www

listen = 127.0.0.1:9000


pm = dynamic

pm.max_children = 5000

pm.start_servers = 50

pm.min_spare_servers = 20

pm.max_spare_servers = 80

pm.max_requests = 5000


access.log = log/php.access.log

slowlog = log/php.log.slow

request_slowlog_timeout = 3

与nginx相对应,php也以普通用户www来运行,同时所有站点目录,都授权属主、属组为www,保证正确与合理的权限。一些经验不足的人,不注意程序权限与目录权限的对应关系,运行服务,只要提示权限不够,就立马执行 chmod -R 777 dir ,你这是在方便别人进入系统呢!另外,php开启了慢查询日志,在运行中排错或者排查性能问题会很有帮助,不过我自己不懂php,一般都是让开发人员自己看这个日志了,他懂的。


把论坛源站复制来的数据文件,tar解包后,放置在nginx指定的目录,然后用chown  -R www:www /data/html 及chmod -R 755 赋权,这个过程比较容易完成,不再赘述。


前边用mysqldump出来的文件,导入失败,这次换成innobackup,根据以往的经验,不应该有问题。注意,用此工具导入时,mysql不要初始化,也不要启动(这个是废话了,没初始化当然不能启动)。为方便大家了解全过程,顺便把mysql的选项文件my.cnf(不叫配置文件哟)贴出来,供大家参考:

[client]

port=3306

socket=/tmp/mysql.sock

[mysqld]

character-set-server=utf8

collation-server=utf8_general_ci


skip-external-locking

skip-name-resolve


user=mysql

port=3306

datadir=/data/mysql_db


open_files_limit=10240

back_log=600

max_connections=500

max_connect_errors=6000

wait_timeout=605800

#open_tables=600

#table_cache = 650

#opened_tables = 630


max_allowed_packet=32M

sort_buffer_size=4M

join_buffer_size=4M

thread_cache_size=300

query_cache_type=1

query_cache_size=256M

query_cache_limit=2M

query_cache_min_res_unit=16k


tmp_table_size=256M

max_heap_table_size=256M


key_buffer_size=256M

read_buffer_size=1M

read_rnd_buffer_size=16M

bulk_insert_buffer_size=64M


lower_case_table_names=1

default-storage-engine=INNODB


innodb_buffer_pool_size=2G

innodb_log_buffer_size=32M

innodb_log_file_size=128M

innodb_flush_method=O_DIRECT


thread_concurrency=32

long_query_time=2

slow-query-log=on

slow-query-log-file=/data/mysql_db/mysql-slow.log


[mysqldump]

quick

max_allowed_packet=32M


按my.cnf指定的"datadir=/data/mysql_db",创建好目录/data/mysql_db,并执行chown -R mysql:mysql /data/mysql 授权。这时,/data/mysql_db目录是空的。如果担心出错,坐好姿势,深呼吸,庄重地敲入screen,然后再执行如下命令行(2018-03-30_11-10-53为源数据库文件解包后的目录):

innobackupex  --defaults-file=/etc/my.cnf --copy-back /data/2018-03-30_11-10-53

这个过程同样比较耗时,没事的话,可以另外再开一个终端,进入目录/data/mysql_db,可以看到不断有目录和文件自动生成。执行完毕且没有报错,基本上就大功告成了。由于安全的原因,mysql对连接帐户有访问限制,迁移到新系统以后,也一样要遵循这个规则。前边已经启动了nginx 和php服务,这里把mysql服务也启动起来。在个人的电脑,绑定windows的hosts文件,把域名与服务器的ip地址临时关联起来,再在浏览器输入域名,提示网页无法访问。已经检查php等配置正确,那么问题就在连接数据库这个上边了。进入论坛根文档(nginx.conf里边root指定的那个),查看连数据库相关的脚本,有好几个呢,其中一个 config_global.php 部分内容为:

.............部分省略....................

$_config = array();


// ----------------------------  CONFIG DB  ----------------------------

- //

$_config['db']['1']['dbhost'] = '172.16.28.94';

$_config['db']['1']['dbuser'] = 'bbs_formyz_net';

$_config['db']['1']['dbpw'] = '5O333EvbY';

$_config['db']['1']['dbcharset'] = 'gbk';

$_config['db']['1']['pconnect'] = '0';

$_config['db']['1']['dbname'] = 'bbs_formyz_net';

$_config['db']['1']['tablepre'] = 'pre_';

$_config['db']['common']['slave_except_table'] = '';

$_config['db']['slave'] = '';

..................................余下省略........................

要解决问题,有两个办法:

(1)跟程序员协作,重新对每一个帐号授权(grant all ...);

(2)在系统上再配置一个 172.16.28.94的ip地址。

搞几天时间了,大家都很累,因此为了省事,我自己做主,在系统的另一个空闲网卡配置了上述私有ip地址。陪完重启网络服务,论坛可以打开,可以登录了。


经过多人多测测试,确认没问题以后,正式做域名解析到新的服务器ip。


还有些工作需要继续完善,包括同步这几天的新数据、开启新的数据备份等。论坛数据用rysnc -e 'ssh -p 20002' -avz src dist同步;数据库稍微麻烦一点,需要临时把论坛停一会,启用主从同步功能。由于源站用innobackup导出数据时,已经记录了主从同步需要的信息(xtrabackup_info),因此同步过程就不需要再执行费时的导出操作,只需按偏移量执行同步就行。

more xtrabackup_info 

uuid = 188600e0-33cc-11e8-986b-90b11c180978

name = 

tool_name = innobackupex

tool_command = --defaults-file=/etc/my.cnf --user=root --password=... /data/databk/db_back

tool_version = 2.3.10

ibbackup_version = 2.3.10

server_version = 5.5.29-log

start_time = 2018-03-30 11:10:53

end_time = 2018-03-30 11:40:31

lock_time = 0

binlog_pos = filename 'mysql-bin.001258', position '571444300'

innodb_from_lsn = 0

innodb_to_lsn = 79320170996

partial = N

incremental = N

format = file

compact = N

compressed = N

encrypted = N

数据库主从同步完成以后,把slave提升为主。似乎写得太长了,数据库备份部分,就不再写了。


如果不想看文字,猛戳此处看视频,临场感更强。