2010年9月

搭建MongoDB Sharding集群

从1.6版本起,MongoDB开始正式支持Sharding

同时,MongoDB也推出了Replica Sets,用以替代之前版本的Replica Pairs

通过把Sharding和Replica Sets相结合,我们可以搭建一个分布式的,高可用性,自动水平扩展的集群

一个典型的集群结构如下:

集群由以下3个服务组成:

  1. Shards Server: 每个shard由一个或多个mongod进程组成,用于存储数据
  2. Config  Server: 用于存储集群的Metadata信息,包括每个Shard的信息和chunks信息
  3. Route   Server: 用于提供路由服务,由Client连接,使整个Cluster看起来像单个DB服务器

另外,Chunks是指MongoDB中一段连续的数据块,默认大小是200M,一个Chunk位于其中一台Shard服务器上

下面,搭建一个Cluster,它由4台服务器组成,包括2个Shard,3个Config,1个Route

其中每个Shard由一个Replica Set组成,每个Replica Set由2个Mongod节点,1个vote节点组成

以下是搭建配置的过程:

1. 四台服务器分别启动相应的Mongod进程:

192.168.x.216
/usr/local/mongodb/bin/mongod --fork --shardsvr --port 10000 --replSet set1 --dbpath /pvdata/mongodb_data  --logpath /pvdata/mongodb_log/mongod.log
/usr/local/mongodb/bin/mongod --fork --shardsvr --port 10001 --replSet set2 --dbpath /pvdata/mongodb_data1  --logpath /pvdata/mongodb_log/mongod1.log

192.168.x.217
/usr/local/mongodb/bin/mongod --fork --shardsvr --port 10000 --replSet set1 --dbpath /pvdata/mongodb_data  --logpath /pvdata/mongodb_log/mongod.log

192.168.x.218
/usr/local/mongodb/bin/mongod --fork --shardsvr --port 10000 --replSet set2 --dbpath /pvdata/mongodb_data  --logpath /pvdata/mongodb_log/mongod.log
/usr/local/mongodb/bin/mongod --fork --shardsvr --port 10001 --replSet set1 --dbpath /pvdata/mongodb_data1  --logpath /pvdata/mongodb_log/mongod1.log

192.168.x.137
/usr/local/mongodb/bin/mongod --fork --shardsvr --port 10000 --replSet set2 --dbpath /opt/mongodb_data  --logpath /opt/mongodb_log/mongod.log

2. 分别配置2组Replica Sets:

192.168.x.216
mongo --port 10000
    config = {_id: 'set1', members: [
        {_id: 0, host: '192.168.x.216:10000'},
        {_id: 1, host: '192.168.x.217:10000'},
        {_id: 1, host: '192.168.x.218:10001', arbiterOnly: true}
    ]}
    rs.initiate(config)
    rs.status()


192.168.x.218
mongo --port 10000
    config = {_id: 'set2', members: [
        {_id: 0, host: '192.168.x.218:10000'},
        {_id: 1, host: '192.168.x.137:10000'},
        {_id: 1, host: '192.168.x.216:10001', arbiterOnly: true}
    ]}
    rs.initiate(config)
    rs.status()

注意:2台Server上的10001对应的Mongod,它们只负责在某个node down掉后,进行vote选举新的master,它们本身并不存储数据备份

3.配置3台Config Servers:

mongod --configsvr --fork --logpath /pvdata/mongodb_log/config.log --dbpath /pvdata/mongodb_config_data --port 20000

4.配置1台Route Server:

192.168.x.216
/usr/local/mongodb/bin/mongos --fork --chunkSize 1 --configdb "192.168.x.216:20000,192.168.x.217:20000,192.168.x.218:20000" --logpath /pvdata/mongodb_log/mongos.log

chunkSize参数用来设置chunk块的大小,这里为了测试,设置成1M

5..配置2组Shard:

192.168.x.216
mongo
    use admin
    db.runCommand({addshard:'set1/192.168.x.216:10000,192.168.x.217:10000'})
    db.runCommand({addshard:'set2/192.168.x.218:10000,192.168.x.137:10000'})
    db.runCommand({enablesharding:'test'})
    db.runCommand({listshards:1})
    printShardingStatus()
    db.runCommand({shardcollection:'test.test', key:{_id:1}, unique : true})

这样整个配置就完成了,下面可以用pymongo来进行测试:

con = pymongo.Connection("192.168.x.216", 27017)
db = con.test
collection = db.test
for i in xrange(10000):
    name = ''.join(random.choice(string.letters) for i in xrange(10))
    collection.save({'_id':name})

然后,进入mongo的命令行,可以在2组的shard中分别查看count值,会发现collection记录被平均的分布到了2组shard server上了

下面,我们再来测试一下automated failover:

将x.218的mongod进程kill -2杀掉后,在x.137的log中,可以看到:

Wed Sep 29 10:51:04 [ReplSetHealthPollTask] replSet info 192.168.x.218:10000 is now down (or slow to respond)
Wed Sep 29 10:51:04 [rs Manager] replSet info electSelf 1
Wed Sep 29 10:51:04 [rs Manager] replSet PRIMARY

说明,新的vote已经完成,x.137变成了新的primary master了

此时,我们再往db中继续写入数据,然后启动x.218,会发现:

Wed Sep 29 10:52:56 [ReplSetHealthPollTask] replSet 192.168.x.218:10000 SECONDARY

说明,x.218此时又作为secondary来运行了

同时,在218 down掉时,137上写入的数据也会继续同步到218上

整个配置过程还是比较简单的,测试也挺正常

但整个Cluster的稳定性,还有待于应用上线后的观察...

Apache的Alias和Rewrite

Apache2中通过配置Alias,可以方便的把某个URL给重定向到某个目录下

如下述配置:

<Directory "e:/ysz">
        Options Indexes FollowSymLinks
        AllowOverride None
        Order allow,deny
        Allow from all
</Directory>
Alias /flash e:/ysz/
ProxyPass /flash !
ProxyPass /   http://10.10.66.71/

注意上面最后有一个反向代理,把所有的请求都反向代理到某台机器上

所以,需要设置对于/flash开头的url就不在反向代理了

Rewrite的功能就更强大了,一个简单的例子:

RewriteEngine on
RewriteRule ^/post/([0-9]+)/  http://www.tech126.com/?p=$1 [R=301,L]

搭建syslog-ng服务器

1. 从syslog-ng官网下载自己合适的版本,可以是src源文件,也可以是rpm包

2. 安装rpm包,安装后的位置默认为/opt/syslog-ng下

rpm -i syslog-ng-3.1.2-1.rhel5.x86_64.rpm

3. 修改配置文件,vi /opt/syslog-ng/etc/syslog-ng.conf

@version: 3.0

options {
    create_dirs(yes);
    ts_format(bsd);
};

# sources
source s_local {
    # message generated by Syslog-NG
    internal();
    # standard Linux log source (this is the default place for the syslog() function to send logs to)
    unix-stream("/dev/log");
    # messages from the kernel
    file("/proc/kmsg" program_override("kernel: "));
    udp(ip(0.0.0.0) port(514));
};


# destinations
destination d_messages { file("/var/log/messages"); };

log {
    source(s_local);
    destination(d_messages);
};

filter f_login    { program("sso");};
log { source(s_local); filter (f_login); destination(d_login); };
destination d_login { file("/maildata/orilog/login/$YEAR$MONTH$DAY$HOUR$MIN.log" create_dirs(yes) template("$UNIXTIME|$HOST|$LEVEL|$MSG\n") );};

filter f_register { program("register");};
log { source(s_local); filter (f_register); destination(d_register); };
destination d_register { file("/maildata/orilog/register/$YEAR$MONTH$DAY$HOUR$MIN.log" create_dirs(yes));};

4.启动syslog-ng服务

/etc/init.d/syslog-ng start

5. 一些配置项说明

  • source中定义udp(ip(0.0.0.0) port(514)),是监听514的UDP端口,为了接收其它服务器远程打来的syslog
  • program是根据msg的前缀来进行过滤,比如program("sso"),将匹配以sso:开头的msg,注意sso后面必须要有:
  • match是根据msg消息的内容进行过滤,支持正则,比如:match("aaa[|]bbb")
  • template定义自己的syslog日志的输出模板,替换默认的模板,如:template("$UNIXTIME|$HOST|$LEVEL|$MSG\n")
  • destination中还可以将msg转发到其它的syslog服务器上,如:destination d_sso {file("/opt/log/sso/$YEAR$MONTH$DAY.log" );udp("192.168.x.x" port(514));};

手贱

昨天,去西三旗桥那边买了一个都市风的电动车

原本打算要买锂电池的,拿着方便一点

但后来听说更换锂电池特别贵,就放弃它了

最后还是买了铅酸电池的,总共1300

后来,又在黄土店买了一把链锁,40元

据说是能防盗,防锯...

晚上又找到以前我买的那个链锁

发现,这2把锁的钥匙是一种类型的

当时,想都没想就把新钥匙给插到旧锁中

顺势扭了一下,竟然还能拧动一点

结果,杯具出现了

我想继续拧,无法拧动

我想往回拧,无法拧动

我想拔掉它,无法拔掉

折腾了好久,把新钥匙都弄折了,也没能取出

浪费了我的一把链锁和一把新钥匙(更杯具的是,那把新链锁只有2把钥匙,这样就只剩一把可用的了)

真是手贱...

hadoop中mapred.tasktracker.map.tasks.maximum的设置

目前,我们邮件的一部分log已经迁移到Hadoop集群上

并由Hive来执行相关的查询

hadoop中默认的mapred.tasktracker.map.tasks.maximum设置是2

也即:每一个tasktracker同时运行的map任务数为2

照此默认设置,查询80天某用户的操作日志,耗时5mins, 45sec

经过测试,发现将mapred.tasktracker.map.tasks.maximum设置为节点的cpu cores数目或者数目减1比较合适

此时的运行效率最高,大概花费3mins, 25sec

我们现在的机器都是8核的,所以最终配置如下:

<property>
    <name>mapred.tasktracker.map.tasks.maximum</name>
    <value>8</value>
    <description>The maximum number of map tasks that will be run
    simultaneously by a task tracker.
    </description>
</property>

而对于mapred.map.tasks(每个job的map任务数)值,hadoop默认值也为2

可以在执行hive前,通过set mapred.map.tasks=24来设定

但由于使用hive,会操作多个input文件,所以hive默认会把map的任务数设置成输入的文件数目

即使你通过set设置了数目,也不起作用...

从集群外操作Hadoop和Hive

如果在集群里面,可以直接使用hadoop的fs shell来操作集群

往上面put/get文件,或者执行一些hive查询

但如果要在hadoop集群外面去操作hadoop和hive,那么可以有2个办法:

1.  写Java程序,调用hadoop提供的一些API去完成

2. 在需要操作的服务器上搭建一个hadoop的环境

目前,我们采用的是第2种办法,这种方式比较简单

只需要将hadoop master上的hadoop相关目录打个包

部署到对应的服务器上,并修改相应的环境变量和hosts就可以了

打包时,可以把一些无用的配置信息和脚本给去掉

但需要保留core-site.xml和hdfs-site.xm和mapred-site.xml这3个配置文件

vi /etc/profile

export JAVA_HOME=/opt/java/jdk

export HADOOP_CONF_DIR=/opt/sohuhadoop/conf

export HADOOP_HOME=/opt/sohuhadoop/hadoop

export HIVE_HOME=/opt/sohuhadoop/hive

vi /etc/hosts

10.10.1.1 hadoop-master. hadoop-master

Hadoop和Hive的权限问题

如果你的Hadoop集群,是公用的,可能有很多其它部门的文件都存放在上面

那么,就一定要考虑权限问题,给各种数据以不同的权限

Hadoop目前的权限,实现的比较简单,类似于Shell的权限,是通过操作用户名来控制的

它默认的超级用户就是你启动Hadoop时的用户

一般,我们所有的服务器都默认是用root来登录的

因为,安装Hadoop时一定要新建一个用户来安装,不要安装在root下

然后,对于不同的log,再新建不同的用户目录来存放,如:

hadoop fs -mkdir /user/test

hadoop fs -chmod -R 700 /user/test

hadoop fs -chown -R test:test /user/test

这样,只有test这个用户才能操作/user/test目录,其它用户都无权操作

如果,你su pplog,然后执行

hadoop fs -ls /user/test

你将会看到一个错误提示:

ls: could not get get listing for 'hdfs://zw-hadoop-master:9000/user/test' : org.apache.hadoop.security.AccessControlException: Permission denied: user=pplog, access=READ_EXECUTE, inode="test":test:test:rwx------

对于Hive来说,可以这么来控制权限访问

  1. 为不同日志的MetaStore在Mysql建立不同的数据库
  2. 为不同的用户建立单独的conf目录,如用户test的hive conf目录位于/opt/sohuhadoop/hive/conf/test下
  3. 在单独的test目录下,修改hive-default.xml文件,配置相应的db
  4. 启动单独的hiveserver实例,并监听不同的端口:HIVE_PORT=10020 nohup hive --config $HIVE_HOME/conf/test --service hiveserver &
  5. 在JDBC中连接自己对应的端口,如10020

上面的权限控制虽然有一定作用,但却是还很弱,如果其它人知道了你的用户名或者端口号

一样可以去删除你的文件,据说,将来Hadoop会对权限认证做一定改进,期待......

使用Mysql来存储Hive的Meta信息

Hive默认是采用Derby来存储其Meta信息的,如下:

<property>

  <name>javax.jdo.option.ConnectionURL</name>

  <value>jdbc:derby://zw-hadoop-master:1527/metastore_db;create=true</value>

  <description>JDBC connect string for a JDBC metastore</description>

</property>


<property>

  <name>javax.jdo.option.ConnectionDriverName</name>

  <value>org.apache.derby.jdbc.ClientDriver</value>

  <description>Driver class name for a JDBC metastore</description>

</property>

我们可以修改一下配置,让Mysql来存储其Meta信息

首先,在Mysql服务器上建立相应的库,并赋权限

create database hivedb;

grant all privileges on hivedb.* to hiveuser@'%' identified by 'hiveuser';

然后,把mysql-connector-java-5.1.12.jar拷贝到/opt/sohuhadoop/hive/lib下

再修改hive-default.xml配置

<property>

  <name>javax.jdo.option.ConnectionURL</name>

  <value>jdbc:mysql://192.168.x.x:3306/hivedb?createDatabaseIfNotExist=true</value>

  <description>JDBC connect string for a JDBC metastore</description>

</property>

<property>

  <name>javax.jdo.option.ConnectionDriverName</name>

  <value>com.mysql.jdbc.Driver</value>

  <description>Driver class name for a JDBC metastore</description>

</property>

<property>

  <name>javax.jdo.option.ConnectionUserName</name>

  <value>hiveuser</value>

  <description>username to use against metastore database</description>

</property>

<property>

  <name>javax.jdo.option.ConnectionPassword</name>

  <value>hiveuser</value>

  <description>password to use against metastore database</description>

</property>

修改后,进入hive后,可能会报错:

FAILED: Error in metadata: javax.jdo.JDOException: Couldnt obtain a new sequence (unique id) : Binary logging not possible. Message: Transaction level 'READ-COMMITTED' in InnoDB is not safe for binlog mode 'STATEMENT'

因为,READ-COMMITTED需要把bin-log以mixed方式来记录,用以下命令来修改:

set global binlog_format='MIXED';

Windows下编译Apache2的Passport模块

虽然,线上运行的服务都是在Linux平台,直接在Linux上编译对应的passport模块就可以了

但是,我们的很多开发人员仍然是在Windows下工作,所以需要passport模块的windows版本

最近,passport模块又进行了升级,可以支持多账号绑定功能了

于是,又需要编译一个Windows下的新版本,原来曾经编译过,但编译的环境没有了

上午又捣鼓了半天,终于编出来dll了,记录一下过程

  1. 安装Apache2.2,选择“自定义安装”,需要把一些headers和libs给装上
  2. 安装Win32Openssl,我装的版本是Win32OpenSSL-0_9_8e
  3. 安装Visual Studio express 2008中的Visual C++,它已经包含SDK工具包了
  4. 参考vsvars32.bat,写了一个bat文件:
@set PATH=C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE;C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN;C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools;C:\WINDOWS\Microsoft.NET\Framework\v3.5;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 9.0\VC\VCPackages;c:\Program Files\Microsoft SDKs\Windows\v6.0A\bin;%PATH%
@set INCLUDE=C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE;c:\Program Files\Microsoft SDKs\Windows\v6.0A\Include;c:\Program Files\Apache Software Foundation\Apache2.2\include;c:\Program Files\OpenSSL\include;%INCLUDE%
@set LIB=C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;c:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib;c:\Program Files\Apache Software Foundation\Apache2.2\lib;c:\Program Files\OpenSSL\lib\VC;%LIB%
@set LIBPATH=C:\WINDOWS\Microsoft.NET\Framework\v3.5;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;%LIBPATH%

cl /MD /D "WIN32" /c mod_passport.c
link /DLL mod_passport.obj libhttpd.lib libapr-1.lib libeay32MD.lib
mt -manifest mod_passport.dll.manifest -outputresource:mod_passport.dll;#2

在编译过程中,报了一个错:

 mod_passport.c(896) : error C2275: “BIO”: 将此类型用作表达式非法
 c:\Program Files\OpenSSL\include\openssl/bio.h(199) : 参见“BIO”的声明

在宋老师的指点下,把bio的声明给提前到所在函数的第一行就可以了

博客大迁移

这2天,对博客进行了大迁移

首先,域名从www.51yu.cn迁移到了www.tech126.com上

然后,博客空间从虚拟主机迁移到了VPS上

另外,博客也从bo-blog迁移到了wordpres上

做这些迁移,其实是很无奈的

现在,国内的环境确实很糟糕

cn域名和国内的虚机受制于政策

造成很不稳定,经常被河蟹......

于是,干脆在国外搞个VPS

买的这个VPS很便宜,使用优惠码后,年付才$21.60

购买的是https://www.123systems.net/billing/cart.php?gid=19

详细信息如下:

Linux-256-SolusVM-Special

Instant Setup!

Guaranteed RAM: 256MB

Burstable RAM: 512MB

Disk Space: 10GB

Bandwidth: 500GB/Month

2 CPU Cores

1 IP Address

SolusVM Control Panel

现在感觉速度还是不错的,不知道以后会怎么样

VPS确实很爽,有独立IP,还有root权限,自己想装什么就装什么

经过一番折腾后,在上面编译了nginx,安装了php,mysql等

借用了别人写的一个php脚本,把bo-blog很顺利的转到了wordpress

为了防止vps出意外,我又起了一个rsync服务

每天晚上自动压缩数据,同步到本地的某台服务器上做备份

推荐大家用VPS吧,抛弃国内万恶的cn域名和虚机...

最新文章

最近回复

  • feifei435:这两个URI实际是不一样的
  • zsy: git push origin 分支 -f 给力!
  • 冼敏兵:简单易懂,good fit
  • Jack:无需改配置文件,看着累! # gluster volume se...
  • Mr.j:按照你的方法凑效了,折腾死了。。。。
  • zheyemaster:补充一句:我的网站路径:D:\wamp\www ~~菜鸟站长, ...
  • zheyemaster:wamp2.5(apache2.4.9)下局域网访问403错误的...
  • Git中pull对比fetch和merge | 炼似春秋:[…] 首先,我搜索了git pull和git fe...
  • higkoo:总结一下吧, 性能调优示例: gluster volume s...
  • knowaeap:请问一下博主,你维护的openyoudao支持opensuse吗

分类

归档

其它