grep 动态文件输出

tail -f access.log | grep "www.firefoxbug.net" 在因为access.log是动态变化的,随时都可能增加,因为管道的作用是在输入端完成或者buffer满了才会到输出端过滤,所以grep出来的结果是无的。

加 --line-buffered 参数可以动态查看文件。
tail -f access.log | grep --line-buffered "www.firefoxbug.net"

git入门二(新建分支)

这一节讲下git的分支功能,分支功能是什么呢?其实这才是真正的软件版本管理核心,比如你的项目想添加一个的功能,但是这个版本是测试的,不一定上线,再或者想开发一个2.0版本专门用于某个场合。传统的做法就是把原来的项目代码,全部都复制一份。然后还傻乎乎地为了区别,把文件夹的名字分成1.0版本和2.0版本,别笑华哥,我之前开发OpenCDN就是这样的。到后来以至于我根本搞不清楚各个版本了,而且还浪费磁盘空间。总之看起来就是很不爽,但是用git可以解决版本控制的问题。

git通过增加分支的方法来实现,git默认的主分支就是master,可以用下面的命令查看。

git branch #查看分支
git branch new_branch #创建一个新的分支
git checkout new_branch #切换到新的分支
git checkout -d new_branch #删除分支

- 阅读剩余部分 -

git入门一(一次commit)

网上git的资料已经很多了,可git这个东西一开始还是比较难理解,以至于我周围的朋友都说这东西太难用了。我自己也玩了两年的linux,但是接触git这个东西一开始还是挺难理解的。其实主要是不像其他工具那样,git还是得静下心来好好看看的。自己对git也不是特别熟悉,所以不能深入解释里面的工作细节,只解释一些常用到的。
在一个项目下面初始化一个git仓库,主要会分成三个部分。一部分是你的工作目录(working),说白了就是你的项目代码。一部分是索引文件(staged file),这里就是你项目的tmp文件。还有一部分是仓库(History),这里才是git真正的数据库,存放你的项目记录的地方。举个例子,我们通用的vim编辑一个文件,一旦开始编辑,目录下就会产生一个.swp文件,然后你编辑完成后按下wq退出,才会真正存到编辑的文件里面。

git1
图片from http://blog.jobbole.com/tag/git/page/2/

git的工作流程也是这样,你初始化一个git仓库后,git监控要指定的文件,你修改了文件之后会暂时存到staged里面去,也就是tmp的部分。当你commit的时候就会真正把tmp里面的文件提交到git仓库里面,这才是真正的一次操作。

- 阅读剩余部分 -

Python日志模块

日志的重要性不言而喻,之前写各种程序最纠结的就是这部分了,每次都自己定义日志格式,以后还是用标准的python日志模块吧。Python的日志类型也是Nginx和Syslog一样,是分等级的。 NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL 如果把looger的级别设置为INFO, 那么小于INFO级别的日志都不输出, 大于等于INFO级别的日志都输出

- 阅读剩余部分 -

Python watchdog

想监控本地文件系统下的某个目录,原本是想用pyinotify的,谁知道mac的最新系统不支持,于是找了watchdog。根据官网的API做了一份和pyinotify的demo。

<pre>
#!/usr/bin/python
# -*- coding: UTF-8 -*-

# author : firefoxbug
# E-Mail : wanghuafire@gmail.com
# Blog   : www.firefoxbug.net

import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class MyHandler(FileSystemEventHandler):
	def on_created(self,event):
		if event.is_directory:
			print event.event_type,event.src_path
		else :
			print event.event_type,event.src_path

	def on_deleted(self,event):
		if event.is_directory:
			print event.event_type,event.src_path
		else :
			print event.event_type,event.src_path

	def on_modified(self,event):
		if not event.is_directory:
			print event.event_type,event.src_path

	def on_moved(self,event):
		print "move",event.src_path,event.dest_path

if __name__ == "__main__":
	event_handler = MyHandler()
	observer = Observer()
	observer.schedule(event_handler, path='.', recursive=True)
	observer.start()

	try:
		print "started myWatch"
		while True:
			time.sleep(1)
	except KeyboardInterrupt:
		observer.stop()
	observer.join()
</pre>

详细的可以参考 http://pythonhosted.org/watchdog/

OS X下能访问网页但ping不通域名

昨晚碰到一个奇葩的问题,所有网页的浏览都正常,QQ也正常,明明可以打开网站,但是ping那个网站就是显示不通,无法解析。一看就知道本地的DNS查找有问题,修改了reslov.conf加了8.8.8.8还是没用,这我就纳闷了。还有奇怪的,QQ音乐和AppStore也打不开,都是无法连接网络。但是我用nslookup都是可以的,直接pingIP也是没问题。这真是诡异了,今天无意中发现了mDNSResponder
这个东西,这个东西估计是MAC OS X自定义的一个DNS解析,linux下是没这个的,直接就是根据hosts和resolv.conf来解析的。我ps aux了下没发现有这个程序在跑,我就找到/System/Library/LaunchDaemons/com.apple.mDNSResponder.plist,发现里面是启动/usr/sbin/mDNSResponder的设置,我手动运行了还是不对。然后查看了下系统日志,/var/log/system.log

- 阅读剩余部分 -

Hadoop HDFS和MapReduce测试

Hadoop的MapReduce应该是属于核心的编程框架,到目前为止我还不是特别懂,只能按照网上的步骤一点实践,就和学一门新语言第一个程序是HelloWorld一样。前文已经介绍了Hadoop的安装,这里就HDFS和MapReduce的测试。

HDFS测试


在HDFS中新建一个文件夹,并把本地文件copy到HDFS新建的文件夹
cd hadoop路径
bin/hadoop dfs -mkdir /tmp/firefoxbug
bin/hadoop dfs -put /opt/run.log /tmp/firefoxbug
bin/hadoop dfs -ls /tmp/firefoxbug

- 阅读剩余部分 -

Elasticsearch集群搭建

之前对于CDN的日志处理模型是从
logstash agent==>>redis==>>logstash index==>>elasticsearch==>>kibana3,对于elasticsearch集群搭建,可以把索引进行分片存储,一个索引可以分成若干个片,分别存储到集群里面,而对于集群里面的负载均衡,副本分配,索引动态均衡(根据节点的增加或者减少)都是elasticsearch自己内部完成的,一有情况就会重新进行分配。
下面先是介绍几个关于elasticsearch的几个名词

- 阅读剩余部分 -

Hadoop安装

听了这么久的Hadoop,终于要开始玩了,好幸福的说。不水了,Hadoop到底能怎么用我还没研究,只是安装了下。这里大概介绍下Hadoop,它是一组项目,在下面有好多个开源的牛逼组件。HDFS是下面的一个分布式存储项目,主要是文件存储,它设计就是来存储大文件,整个HDFS就是一套文件系统,但是它的Block特别大,默认是64M,HDFS会有自己的Backup。HDFS主要分成两部分,一部分是管理中心,叫做NameData,这类似linux文件系统的super blocks,用来管理所有的DataNode,DataNode就是真正用来存储实际的数据内容的。而NameData会管理好文件的索引,存储,还有backup。

- 阅读剩余部分 -

logstash+elasticsearch+kibana3+redis分布式搭建

之前的logstash都是单机模式,把几个组件都安装在一台机器上,这一次把每个组件都安装到独立的server上,下面是架构图。

logstash
日志通过syslog-ng传输到logstash的agent端,agent主要就是负责接收日志,然后把日志放到redis,redis在这里的作用就是一个队列,主要是日志的缓存,redis是以内存做缓存的,一段时间存到磁盘上。在redis另一端是logstash index,这端主要是从redis取出日志,然后进行filter和output,filter就是对日志进行切割,匹配,过滤,logstash index这里可以是集群的,都从redis拿就行。取出来之后output可以到elasticsearch,elasticsearch也可以是集群,专门做索引。在最前端就是kibana3,再js里面配置从elasticsearch的端口读取数据,呈现到前端。

- 阅读剩余部分 -

logstash kibana3显示坐标

按照之前logstash+kibana3 GeoIP地理位置那样配置出来的geo地理位置里有经度和纬度,但是是独立开来的。在官网的demo上可以有coordinates的字段,只要是以数组的形式显示经纬度。下面是logstash的配置,在kibana3上呈现坐标形式。

filter {
geoip {
   source => "source_ip"
   type => "linux-syslog"
   add_tag => [ "geoip" ]
   add_field => [ "[geoip][coordinates]", "%{[geoip][longitude]}" ]
   add_field => [ "[geoip][coordinates]", "%{[geoip][latitude]}"  ]
}

mutate {
    convert => [ "[geoip][coordinates]", "float" ]
}

下面是显示后的效果

- 阅读剩余部分 -

Python redis操作

首先安装python redis扩展模块 yum install python-redis

#!/usr/bin/env python2.6
#-*- coding: utf-8 -*-

# Author : firefoxbug
# E-Mail : wanghuafire@gmail.com
# Blog   : www.firefoxbug.net
# Function : put or get log dictionary from redis-server

import redis  

global redis_q

class RedisQueue(object):
    """Simple Queue with Redis Backend"""
    def __init__(self,**redis_kwargs):
        """The default connection parameters are: host='localhost', port=6379, db=0"""
        self.__db= redis.Redis(**redis_kwargs)
#       self.key = '%s:%s' %(namespace, name)  

    def qsize(self,key):
        """Return the approximate size of the queue."""
        return self.__db.llen(key)

    def empty(self):
        """Return True if the queue is empty, False otherwise."""
        return self.qsize() == 0  

    def put(self,key,item):
        """Put item into the queue."""
        self.__db.rpush(key, item)  

    def get(self,key,block=True, timeout=None):
        """Remove and return an item from the queue.  

        If optional args block is true and timeout is None (the default), block
        if necessary until an item is available."""
        if block:
            item = self.__db.blpop(key, timeout=timeout)
        else:
            item = self.__db.lpop(key)  

        if item:
            item = item[1]
        return item  

    def get_nowait(self):
        """Equivalent to get(False)."""
        return self.get(False) 

def connect2redis(host='127.0.0.1'):
    global redis_q
    redis_q = RedisQueue()

if __name__ == '__main__':
    redis_q = RedisQueue(host='127.0.0.1')
    i = 0
    while True:
                redis_q.put('test2','hello world')
        print redis_q.get('test2')

redis-2.6安装

在使用redis2.4和redis2.6过程中,发现一个问题,在redis-cli里输入 keys * 参看键值的时候,2.4版本会卡住,应该是阻塞的,2.6就不是,留个笔记吧。。。

#!/bin/bash

wget http://download.redis.io/releases/redis-2.6.16.tar.gz
tar zxvf redis-2.6.16.tar.gz
cur_dir=$(pwd)
cd redis-2.6.16
make
cp src/redis-server /usr/sbin/
cp src/redis-cli /usr/bin/
cp utils/redis_init_script /etc/init.d/redis
curl -L https://bitbucket.org/ptylr/public-stuff/raw/41d5c8e87ce6adb34aa16cd571c3f04fb4d5e7ac/etc/init.d/redis > /etc/init.d/redis
mkdir -p /etc/redis
mkdir -p /var/redis
mkdir -p /var/lib/redis
mv ${cur_dir}/redis.conf /etc/
redis-server /etc/redis.conf

redis.conf配置文件在附件redis

Nginx反向代理和php解析配置

Nginx纯反向代理配置

upstream  firefoxbug{
        ip_hash; 
        server 10.161.171.87:80;
        server 10.160.55.81;
        server 10.160.55.93;
}

server {
    server_name         www.firefoxbug.net firefoxbug.net;
    listen              80;
    gzip                on;


    error_log           /home/logs/www_firefoxbug_net_error.log error;
    access_log          /home/logs/www_firefoxbug_net_access.log;

    ## Bypass For All
    location / {
        proxy_redirect      off;
        proxy_pass          http://firefoxbug; 
        proxy_set_header Host   $host;
        proxy_set_header    X-Real-IP       $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

- 阅读剩余部分 -

OpenCDN流量采集bug修复

OpenCDN节点端Nginx把节点流量信息以下面这种格式输出到管道bandwidth.pipe。

nginx.conf
log_format  bandwidth   '[$time_local] $host $body_bytes_sent $upstream_cache_status';

vhost配置
access_log /usr/local/opencdn/pipe/bandwidth.pipe bandwidth;

在管道的另一段读取流量信息,然后输出到下面的文件里(shell守护)
/usr/local/opencdn/stream/域名/
-| year_month_day_hour_minute
比如
/usr/local/opencdn/stream/www_firefoxbug_net
2013_Sep_30_12_10

管控端采集流量就是通过守护,以http://%s:%s/ocdn/stream?token=%s&domain=%s来抓取流量,处理后导入数据库。

之前OpenCDN产生一个小需求,使用CDN加速的小伙伴们添加了一个域名比如是aaa.com,但是他还加了别名,xxx.aaa.com(可以是二级域名)。OpenCDN节点端会在Nginx的vhost配置server_name加一个aliasname。但是小伙伴访问都是用这个aliasname访问的。而根据上面的逻辑管控端就没有办法采集到aliasname的流量。
怎么办?在节点端加对域名的流量路径加个软链接就可以,那么aliasname访问的流量也会记录到原站点的流量下。

中间还遇到一个小问题

$ mkdir test1
$ ln -s test1 test2
$ [ -L test2 ] ; echo $?
0
$ [ -L test2/ ] ; echo $?
1

为什么测试目录链接文件加/和没加/有区别呢?得@smallfish指点,大概明白了。软链接是新建一个inode节点的,这个inode节点对应的block记录的是原目录的路径,从软链接到原目录路径其实是比正常访问多一层的。我想linux的设计就是加/来区别吧,没加/那么就是读取普通的目录文件一样,加了/就是change到原路径里去了。

Python QQ纯真库解析

QQWry.Dat不是普通的txt文件,所以python解析要特定的程序。下面是一个Python类

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# author : firefoxbug
# E-Mail : wanghuafire@gmail.com
# Blog   : www.firefoxbug.net

import sys
import socket
from struct import pack, unpack

class IPInfo(object):
    '''QQWry.Dat数据库查询功能集合
    '''
    def __init__(self, dbname):

        self.dbname = dbname
        f = file(dbname, 'r')
        self.img = f.read()
        f.close()

        (self.firstIndex, self.lastIndex) = unpack('II', self.img[:8])
        self.indexCount = (self.lastIndex - self.firstIndex) / 7 + 1

    def getString(self, offset = 0):
        o2 = self.img.find('\0', offset)
        gb2312_str = self.img[offset:o2]
        try:
            utf8_str = unicode(gb2312_str,'gb2312').encode('utf-8')
        except:
            return '未知'
        return utf8_str

    def getLong3(self, offset = 0):
        s = self.img[offset: offset + 3]
        s += '\0'
        return unpack('I', s)[0]

    def getAreaAddr(self, offset = 0):
        ''' 通过给出偏移值,取得区域信息字符串,'''

        byte = ord(self.img[offset])
        if byte == 1 or byte == 2:
            p = self.getLong3(offset + 1)
            return self.getAreaAddr(p)
        else:
            return self.getString(offset)

    def getAddr(self, offset, ip = 0):
        img = self.img
        o = offset
        byte = ord(img[o])

        if byte == 1:
            return self.getAddr(self.getLong3(o + 1))

        if byte == 2:
            cArea = self.getAreaAddr(self.getLong3(o + 1))
            o += 4
            aArea = self.getAreaAddr(o)
            return (cArea, aArea)

        if byte != 1 and byte != 2:

            cArea = self.getString(o)
            o = self.img.find('\0',o) + 1
            aArea = self.getString(o)
            return (cArea, aArea)

    def find(self, ip, l, r):
        ''' 使用二分法查找网络字节编码的IP地址的索引记录'''
        if r - l <= 1:
            return l

        m = (l + r) / 2
        o = self.firstIndex + m * 7
        new_ip = unpack('I', self.img[o: o+4])[0]
        if ip <= new_ip:
            return self.find(ip, l, m)
        else:
            return self.find(ip, m, r)

    def getIPAddr(self, ip):
        ip = unpack('!I', socket.inet_aton(ip))[0]
        i = self.find(ip, 0, self.indexCount - 1)
        o = self.firstIndex + i * 7

        o2 = self.getLong3(o + 4)

        (c, a) = self.getAddr(o2 + 4)
        return (c, a)

    def output(self, first, last):
        for i in range(first, last):
            o = self.firstIndex +  i * 7
            ip = socket.inet_ntoa(pack('!I', unpack('I', self.img[o:o+4])[0]))
            offset = self.getLong3(o + 4)
            (c, a) = self.getAddr(offset + 4)
            print "%s %d %s/%s" % (ip, offset, c, a)

'''search ip and its'localtion '''
def search_ip(ip_instance,ipaddr):
    if ipaddr :
        try :
            (c, a) = ip_instance.getIPAddr(ipaddr)
            print '%s/%s'%(c,a)
        except :
            print 'Unknow/Unknow'

if __name__ == '__main__':
    ip_instance = IPInfo('./QQWry.Dat')
    search_ip(ip_instance,"8.8.8.8")

纯真库优势就是可以精确到公司,街道,网吧,但是有麻烦的一点就是统计起来不方便,因为都是一个字符串,需要再做分词。

理解inode

转自 http://www.ruanyifeng.com/blog/2011/12/inode.html

inode是一个重要概念,是理解Unix/Linux文件系统和硬盘储存的基础。

我觉得,理解inode,不仅有助于提高系统操作水平,还有助于体会Unix设计哲学,即如何把底层的复杂性抽象成一个简单概念,从而大大简化用户接口。

下面就是我的inode学习笔记,尽量保持简单。

- 阅读剩余部分 -

logstash+kibana3 GeoIP地理位置

原日志从logstash输入,根据filter匹配或者修改,通过json输出到elasticsearch,然后到kibana3。所以要显示地理位置,要在logstash里面设置。logstash的jar包默认应该是有GeoIP的包的。下面是配置,pattern主要是针对Nginx的combined日志格式

log_format  access  '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" '   '"$http_user_agent"

filter {
grok {
        type => "linux-syslog"
        pattern => "%{IPORHOST:source_ip} - %{USERNAME:remote_user} \[%{HTTPDATE:time_local}\] %{QS:request} %{INT:status} %{INT:body_bytes_sent} %{QS:http_referer} %{QS:http_user_agent}
}
geoip {
   source => "source_ip"
   type => "linux-syslog"
   add_tag => [ "geoip" ]
#   database => "/var/geoip/GeoLiteCity.dat" 不是必须
}
}

kibana5

- 阅读剩余部分 -