敏捷开发

维基百科

个体和互动:高于 流程和工具。
工作的软件:高于 详尽的文档。
客户合作:高于 合同谈判。
响应变化:高于 遵循计划。

是的,我不是要安利敏捷开发。而是:

TMD 什么时候说不要流程和工具了?
TMD 什么时候说不要文档了?
TMD 什么时候说不要合同谈判了?
TMD 什么时候说不要计划了?

网站被爆之后-开网站基本常识

早上点进自己做的小网站,想看看比分,说数据库挂了。当下的反应,是 TMD mongo 真不稳定。
晚上回来一看, mongo 进程果然不在,重启,网站还是各种报错。
进 rockmongo 一看,自己的表不见了,各种奇怪的字符。
去 mongo 的 data 目录下一看
string:{var_dump(md5(812812))}
肯定被黑了。
看看 mongo 和 nginx 的 log,搞明白怎么回事。
前天来一个哥们,通过 ip 爬 /pma /admin 的接口,一直没结果,昨晚估计这哥们想起来手动直接访问了 ip,竟然出来 rockmongo 的页面,而且我还没加密..

教训:

  1. nginx 的配置里,默认 444;
  2. rockmongo 这种,至少基础验证,基本的账号密码要来一个,如果真的太懒,至少限制 IP 访问;
  3. log 是好东西,自己 SB 了之后能弄明白是怎么回事。

最后,安利一下 tt-nba.com,欢迎来搞。

使用 rsyslog 将 nginx log 同步到一台机器

nginx 每天会产生大量日志,对系统和业务分析很有用。 生产环境一般有多台 web 服务器,分担压力和避免单点故障,这种情况下,很难快速定位到需要的信息在哪些机器上。
当然,有高大上的方法,比如 elasticsearch + kibana,但这些方法比较吃机器性能,有没有简单粗暴的办法呢。
如果不需要将查 log 玩出花,归并 log 到一台机器是个好办法,况且现在硬盘也很便宜。
rsyslog 是一个简单同步的工具,支持 UDP TCP,内网下很好。和 nginx log 集成的方式,有 2 种,一种是直接在 nginx 配置种写 syslog 的协议,但需要 1.7 以上版本支持;另一种,可以根据已有 log 文件的变化,做增量同步,对 nginx 版本没有限制,理论上可以同步所有 log。这次介绍的是后者。

以 ubuntu 为例,先看服务端的配置。

修改 /etc/rsyslog.conf

$ModLoad immark # 去掉前面的注视符号
$ModLoad imtcp # 去掉前面的注视符号,表示接受指定端口来的 tcp 信息
$InputTCPServerRun 10514 # 设定端口号,默认的 514 打开不了,我换成了 10514

在 /etc/rsyslog.d/ 下添加一个配置文件 40-nginx.conf

$template filename, "/path/to/log/%PROGRAMNAME%_%$year%_%$month%_%$day%.log"
$template format, "%FROMHOST% -%msg%\n"
:syslogtag,startswith,"nginx" ?filename;format
:syslogtag,startswith,"nginx" ~

第一行定义了文件名格式。PROGRAMNAME 是 tag 名称,后面会由客户端指定。
第二行定义里 log 格式,为了区分来自哪台机器,我加了 FROMHOST。要注意的是,必须在 /ets/hosts 中事说明对应 host 的名称,否则 log 中不会出现机器名称。
第三行说明 tag 以 nginx 开头的 log,以上面定义的 format 格式,存到 filename 格式的文件中。
第四行防止将 log 存放到 /var/log/syslog 中
保存后重启一下

sudo service rsyslog restart

客户端配置
在 /etc/rsyslog.d/ 下添加一个配置文件 40-nginx.conf

$ModLoad imfile

$InputFileName /path/to/nginx/access/log
$InputFileTag nginx_access_log:
$InputFileStateFile stat-nginx-access
$InputFileSeverity info
$InputFileFacility local7
$InputRunFileMonitor

$InputFilePollInterval 5

:syslogtag,startswith,"nginx" @@host_ip:host_port
:syslogtag,startswith,"nginx" ~

第一行表示从文件同步。
第三行说明需要同步的文件。
第四行说明传到服务端的 tag。
第五行是 rsyslog 用来标记文件同步位置的文件,每台客户端唯一。
InputFilePollInterval 说明同步间隔,单位是秒。
最后第二行和服务端类似,只是说明 log 上传服务端的 ip 和端口。两个 @ 符号,表示使用 tcp 协议。
保存后重启客户端 rsyslog。

这样就完成了整个配置。

好处如上所说,可以在同一个地方查看日志,现在发现一个缺点,多个服务器日志的顺序,会有错乱,原因和客户端不是及时上传 log 有关(节省网络开销),如果 nginx 写入 log 时有 buffer,也会加剧这个问题。但错位问题不严重,目前的分析中,也可以容忍错位,因此不解决了。

MongoDB shard 开启

为啥要 shard:个别 collection 数据量越来越大,又没法 archive,索引大,读取和写入性能极差。shard 可以分割数据和读写

MongoDB 的 shard,可以指定需要 shard 的 collection,这样不用所有 collection 都折腾;已经 shard 的 collection 可以取消 shard,数据会从别的 shard 传回 primary shard;可以从 replica 平滑升级到 shard。这些可以保证平滑开启。

相对于 repica,shard 多了 2 种服务:config,用于管理 shard 信息,也就是数据如何映射,非常重要,生产环境中,必须布置 3 个,有一个坏了,数据还可以正常读写,但是数据的合并和迁移就不会发生,如果所有 config 都坏了,或者 config 数据坏了,数据的读写就挂了;mongos,用于读写 shard 数据的接口服务,可以有多个,本身不保留任何状态信息,实际只有 log。

shard keys 是做 shard 前必须要决定的事情,它影响几个方面:查询,如果查询条件包含了 shard keys(必须排在前列,例如查询包含了 userId,(userId, _id) 这个 shardKey 有用, (_id, userId) 这个就没用了),则查询可以只涉及相关的 replica,否则所有 replica 都要查询;数据分割,好的 shard key 可以让数据分割的均匀,典型的例如 _id,因为每个 document 的 _id 都不一样,所以数据可以分割的非常均匀,而用户表里的性别就是一个非常差的 shard key,因为所有的数据只能分成三块(男,女,未知);存储压力分配,好的 shard key 可以将存储压力平均的分配到所有 replica 上,刚才那个 _id 在这里就是一个很差的 shard key,因为所有新数据都集中在某个分片,也就是某台机器上,这台机器压力会很大。shard key 的选择和存储的数据用法很有关系,没有一个万能的选择方案,不过任何一个介绍 shard 的书或正式文档都会讲到 shard key 的选择,可以参考。

shard 过程比较简单,首先搭起新的 mongos, config, replica 机器,连接 mongos,指定 config,首先添加原来的 replica,这样它就成为 primary shard,然后添加其他 replica 作为 shard。之后指明某个 db 可以 shard,指明某个 collection 可以 shard,指明 shard key,之后 mongo 会自动开始挪动数据,挪动过程中数据可以正常读写。

MongoDB 升级

为啥要升级?

更好的性能,和更少的 bug。

2.6 相对于 2.4,得益于引擎的升级,存储时的锁从 db 降为 collection;

3.0 相对于 2.6,如果用了 wiredtiger,存储时的锁可以从 collection 降为 document!

实际试下来发现,性能有极大的优化,锁住的比例降低很多,吞吐率和时间表现都好很多;而且,如果使用 wiredtiger,会发现磁盘占用量少很多(至少节省 1/2)。对升级的结果非常满意。

如何升级?

首先,不能跳跃升级,也就是 2.4 无法直接升级到 3.0,必须 2.4 => 2.6,2.6 => 3.0。稍稍有些麻烦。

基本过程比较简单,都是停止 mongod 进程,用新的 mongod 程序使用原来的配置文件和原来的存储空间启动。

对没有使用 replica 的 mongod,升级过程会导致无法服务,不过,生产环境用 standalone 的情况,很少吧。

对 replica,要一台一台升级,集群内的 mongod 可以是不同的版本,升级过程不会中断服务。为了防止停止一台 mongod 时,无法 vote 出来 primary,停止前要检查一下集群内可用 mongod 数量,必要时需要添加 arbiter 用作投票,整个集群升级完毕后再干掉它。

对 shard,集群内的 replica 按照上面的方式升级;mongos 升级很简单,用新的 mongos 启动即可,但是要注意重启的过程防治有请求打到挂掉的 mongos 上。config 升级前,要停掉 balancer,然后逐个升级。整个升级过程也不会中断服务。

升级后,用户监控或备份的 mms,也要升级,否则可能无法正常工作。 此外,开发语言使用的驱动,也需要做相应升级,否则可能无法正常使用。

切换存储引擎到 wiredtiger,这个非常推荐,一个是能极大减少磁盘占用量,存储锁机制的修改,对性能的提升有极大的好处。stand alone 的机器就别升级了。replica 的机器,需要逐个升级,需要用新的地方数据库存储文件或干掉原来的存储文件,如果使用了旧版的 config 格式,也需要升级到新版,启动 mongod 等待数据传输号即可。

最后,2.4 升级到 3.0,小心操作的话,实际过程很顺利,带来的性能提升非常明显,很棒!