
wechat 搜索【数据与算法联盟】,专注于云计算和算法,目前就职于京东
<img src="http://img.blog.csdn.net/20171231111930492?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvR2FtZXJfZ3l0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast"> <br> 打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer推荐系统一直以来都是电商网站必不可少的一项,在提升用户转化,增加GMV方面可谓功不可没,那么一个好的推荐算法必然会创造更大的价值,刚好最近听了一个关于推荐算法的讲座,写出来一些思考吧,算是分享一下。 学术界的推荐系统 其实在大学期间也看过一些推荐的算法,还帮别人实现过关于推荐系统的毕设,但终究都是停留在协同过滤的层面,顶多是加了一些热门推荐来防止冷启动。不得不说,协同过滤打开了我对推荐系统认知的大门,当然在真实环境中这是远远不够的。 传统的推荐系统无非就是评分和排序两种方案,评分即计算出用户对item的可能评分,根据评分的高低进行排序,排序则不关心具体的评分是多少,只是为了得到一个顺序(其实这一点和推荐系统即为相似)。传统的推荐算法典型的有协同过滤和基于内容的过滤。如果你不明白什么是协同过滤算法可以参考:https://blog.csdn.net/gamer_gyt/article/details/51346159 ,如果你不知道协同过滤与基于内容的过滤的区别可参考:https://www.zhihu.com/question/19971859。 大学数学术界关于推荐算法的论文都是对协同过滤的改进,而最终得到一个相对于原先的算法有很大的提升的结果,但是这些都过于理想化了,真实的环境远比实验要复杂的多,网上最有名的推荐系统数据集莫过于那个电影评分数据了,里边只有用户对电影的评分,和电影的一些信息数据。建立在这些数据上的推荐算法其实有点理想化了,他并不能模拟出真实的电商环境,数据的缺乏也是导致协同过滤算法大行其道的原因。 工业界的推荐系统 工业界的推荐系统,需要的是明确的价值走向,比如说电商网站的推荐系统是为了增加交易总额,那么在进行推荐的时候是不是应该适当过滤一些极其廉价的商品,是否应该根据用户对不同价格段的需求进行不同价格段商品的推送;如果电商的推荐系统是为了增加用户停留时间,提交CTR,那么推荐系统就不应该考虑过多别的因素,只找到用户最感兴趣的商品或者评论等,当然如何找到用户最感兴趣的也是一个问题,但是有一点不可否认的是这些如果用传统的协同过滤来做是很难满足需求的。这时候就需要开发出新的推荐架构,来适应不同的需求。 目前工业界用的最多的算法莫过于GBDT,LR,DNN等,但所有的推荐算法都会面临一个海量数据的情况,这个时候的做法便是对数据集进行数据召回,得到用户比较感兴趣的一些数据,然后再根据我们的推荐模型进行素材偏好度排序,过滤掉用户已经购买过的类别数据,继而推送给用户。 那么如何进行数据召回呢?这就需要一些基础的模型进行数据准备,比如说用户肖像,用户的价格段偏好,用户购买力水平等。根绝已有的用户数据和特征进行数据召回,适度拉取一些新的数据,保证召回结果的多样性,得到召回池数据之后,便是对模型的训练,其实模型本身难度不大,难度大的是如何选取有效的特征来作为模型的输入数据。你组合得到的有效特征越多,对于模型的训练结果就越准确。 推荐系统的多样性 如何保证推荐结果的多样性呢,首先我们要先认识到推荐的可能性,比如说电商网页首页的推荐,商品详细页面的推荐,不同年龄下的推荐,推荐的结果和被评估的指标都是不一样的。这个时候不能单一对所有情况下使用同一种算法或者特征,而是要找到能够区分出不同位置,不同年龄的推荐结果的特征,进行模型训练。
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:https://yq.aliyun.com/u/thinkgamer博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 在使用Spark时,有时候主函数入口参数过多的时候,会特别复杂,这个时候我们可以将相应的参数写在xml文件中,然后只要将xml文件的路径传进去即可,这里的xml路径可以是本地的,也可以是hdfs上的。 scala提供了类似于Xpath的语法来解析xml文件,其中很重要的两个操作符是""和 "\" :根据搜索条件得到下一个节点 \ :根据条件获取所有的节点 <configure> <input> <name>app_feature_goods</name> <hdfs>/user/path/to/goods</hdfs> </input> <input> <name>app_feature_user</name> <hdfs>/user/path/to/user</hdfs> </input> </configure> val input = args(0) val xml = XML.load(input) // 找到所有的一级节点 input val input_list = xml\"input" input_list.foreach(println) // 遍历每个一级节点,得到具体的值 for(one <- input_list){ println(one\"name") println((one\"name").text) println(one\"hdfs") println((one\"hdfs").text) } // 得到所有的name val name_list = xml\\"name" name_list.map(one => one.text).foreach(println) // 获取所有hdfs val hdfs_list = xml\\"hdfs" hdfs_list.map(one => one.text).foreach(println) // 获取具有class的值 println(xml\"input"\"name"\\"@class") // 打印出具有class属性的name值和hdfs值 println((xml\\"name").filter(_.attribute("class").exists(_.text.equals("test"))).text) println((xml\\"hdfs").filter(_.attribute("class").exists(_.text.equals("test"))).text) 打印出的信息为: <input> <name>app_feature_goods</name> <hdfs>/user/path/to/goods</hdfs> </input> <input> <name>app_feature_user</name> <hdfs>/user/path/to/user</hdfs> </input> <input> <name class="test">app_feature_user_test</name> <hdfs class="test">/user/path/to/user_test</hdfs> </input> ------------- <name>app_feature_goods</name> app_feature_goods <hdfs>/user/path/to/goods</hdfs> /user/path/to/goods <name>app_feature_user</name> app_feature_user <hdfs>/user/path/to/user</hdfs> /user/path/to/user <name class="test">app_feature_user_test</name> app_feature_user_test <hdfs class="test">/user/path/to/user_test</hdfs> /user/path/to/user_test ------------- app_feature_goods app_feature_user app_feature_user_test ------------- /user/path/to/goods /user/path/to/user /user/path/to/user_test ------------- test ------------- app_feature_user_test /user/path/to/user_test ------------- Process finished with exit code 0
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:https://yq.aliyun.com/u/thinkgamer博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer Spark对于统计量中的最大值,最小值,平均值和方差(均值)的计算都提供了封装,这里小编知道两种计算方法,整理一下分享给大家 DataFrame形式 加载Json数据源 example.json文件格式如下 {"name":"thinkgamer","age":23,"math":78,"chinese":78,"english":95} {"name":"think","age":25,"math":95,"chinese":88,"english":93} {"name":"gamer","age":24,"math":93,"chinese":68,"english":88} // persist(StorageLevel.MEMORY_AND_DISK) 当内存不够时cache到磁盘里 val df = spark.read.json("/path/to/example.json").persist(StorageLevel.MEMORY_AND_DISK) df.show() df.describe() 我们便可以看到如下的形式 +---+-------+-------+----+----------+ |age|chinese|english|math| name| +---+-------+-------+----+----------+ | 23| 78| 95| 78|thinkgamer| | 25| 88| 93| 95| think| | 24| 68| 88| 93| gamer| +---+-------+-------+----+----------+ +-------+----+-------+-----------------+-----------------+----------+ |summary| age|chinese| english| math| name| +-------+----+-------+-----------------+-----------------+----------+ | count| 3| 3| 3| 3| 3| | mean|24.0| 78.0| 92.0|88.66666666666667| null| | stddev| 1.0| 10.0|3.605551275463989| 9.29157324317757| null| | min| 23| 68| 88| 78| gamer| | max| 25| 88| 95| 95|thinkgamer| +-------+----+-------+-----------------+-----------------+----------+ 如果是想看某列的通知值的话,可以用下面的方式 df.select("age").describe().show() +-------+----+ |summary| age| +-------+----+ | count| 3| | mean|24.0| | stddev| 1.0| | min| 23| | max| 25| +-------+----+ RDD形式 假设同样还是上边的数据,只不过现在变成按t分割的普通文本 thinkgamer 23 78 78 95 think 25 95 88 93 gamer 24 93 68 88 这里可以将rdd转换成dataframe洗形式,也可以使用rdd计算,转化为df的样例如下 val new_data = data_txt .map(_.split("\\s+")) .map(one => Person(one(0),one(1).toInt,one(2).toDouble,one(3).toDouble,one(4).toDouble)) .toDF() 接下来就是进行和上边df一样的操作了。 那么对于rdd形式的文件如何操作: import org.apache.spark.mllib.linalg.Vectors import org.apache.spark.mllib.stat.{MultivariateStatisticalSummary, Statistics} val data_txt = SparkSC.spark.sparkContext.textFile(input_txt).persist(StorageLevel.MEMORY_AND_DISK) val new_data = data_txt .map(_.split("\\s+")) .map(one => Vectors.dense(one(1).toInt,one(2).toDouble,one(3).toDouble,one(4).toDouble)) val summary: MultivariateStatisticalSummary = Statistics.colStats(new_data) println("Max:"+summary.max) println("Min:"+summary.min) println("Count:"+summary.count) println("Variance:"+summary.variance) println("Mean:"+summary.mean) println("NormL1:"+summary.normL1) println("Norml2:"+summary.normL2) 输出结果为: Max:[25.0,95.0,88.0,95.0] Min:[23.0,78.0,68.0,88.0] Count:3 Variance:[1.0,86.33333333333331,100.0,13.0] Mean:[24.0,88.66666666666667,78.0,92.0] NormL1:[72.0,266.0,234.0,276.0] Norml2:[41.593268686170845,154.1363033162532,135.83813897429545,159.43023552638942] 这里可以得到相关的统计信息,主要区别在于dataframe得到的是标准差,而使用mllib得到的统计值中是方差,但这并不矛盾,两者可以相互转化得到。 当然如果要求四分位数,可以转化成df,使用sql语句进行查询 Select PERCENTILE(col,<0.25,0.75>) from tableName; 本地碰见的一个错误 1:错误1 scala.Predef$.refArrayOps([Ljava/lang/Object;)Lscala/collection/mutable/Array 原因是Spark中spark-sql_2.11-2.2.1 ,是用scala 2.11版本上编译的,而我的本地的scala版本为2.12.4,所以就错了,可以在里边把相应的scala版本就行修改就行了 2:错误2 java.lang.NoSuchMethodError: scala.Product.$init$(Lscala/Product;)V 原因也是因为我下载安装的scala2.12版本,换成scala2.11版本就可以了
我的博客即将入住“云栖社区”,诚邀技术同仁一同入驻。
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 在实际应用场景中,我们对于Spark往往有各式各样的需求,比如说想MR中的二次排序,Top N,多路劲输出等。那么这篇文章我们就来看下这几个问题。 二次排序 假设我们的数据是这样的: 1 2 1 3 1 1 1 6 1 4 2 5 2 8 2 3 我们想要实现第一列按降序排列,当第一列相同时,第二列按降序排列 定义一个SecondSortKey类: class SecondSortKey(val first: Int, val second: Int) extends Ordered[SecondSortKey] with Serializable { override def compare(that: SecondSortKey): Int = { if (this.first - that.first == 0) { this.second - that.second } else { this.first - that.first } } } 然后这样去使用 val lines = sc.textFile("test.txt") val pairs = lines.map { x => (new SecondSortKey(x.split("\\s+")(0).toInt, x.split("\\s+")(1).toInt), x) } val sortedPairs = pairs.sortByKey(false); sortedPairs.map(_._2).foreach(println) 当然这里如果想按第一列升序,当第一列相同时,第二列升序的顺序排列,只需要对SecondSoryKey做如下修改即可 class SecondSortKey(val first: Int, val second: Int) extends Ordered[SecondSortKey] with Serializable { override def compare(that: SecondSortKey): Int = { if (this.first - that.first !== 0) { this.second - that.second } else { this.first - that.first } } } 当时使用的使用去掉 pairs.sortByKey(false) 中的false Top N 同样还是上边的数据,假设我们要得到第一列中的前五位 val lines = sc.textFile("test.txt") val rdd = lines .map(x => x.split("\\s+")) .map(x => (x(0),x(1))) .sortByKey() rdd.take(N).foreach(println) 多路径输出 自己在使用的过程中,通过搜索发现了两种方法 1:调用saveAsHadoopFile函数并自定义一个OutputFormat类 自定义RDDMultipleTextOutputFormat类 RDDMultipleTextOutputFormat类中的generateFileNameForKeyValue函数有三个参数,key和value就是我们RDD的Key和Value,而name参数是每个Reduce的编号。本例中没有使用该参数,而是直接将同一个Key的数据输出到同一个文件中。 import org.apache.hadoop.mapred.lib.MultipleTextOutputFormat class RDDMultipleTextOutputFormat extends MultipleTextOutputFormat[Any, Any] { override def generateFileNameForKeyValue(key: Any, value: Any, name: String): String = key.asInstanceOf[String] } 调用 sc.parallelize(List(("w", "www"), ("b", "blog"), ("c", "com"), ("w", "bt"))) .map(value => (value._1, value._2 + "Test")) .partitionBy(new HashPartitioner(3)) .saveAsHadoopFile("/iteblog", classOf[String],classOf[String],classOf[RDDMultipleTextOutputFormat]) 这里的 new HashPartitioner(3) 中的3是有key的种类决定的,当然在实际应用场景中,我们可能并不知道有多少k,这个时候就可以通过一个rdd 的 distinct操作来得到唯一key的数目。 2:使用dataframe people_rdd = sc.parallelize([(1, "alice"), (1, "bob"), (2,"charlie")]) people_df = people_rdd.toDF(["number", "name"]) people_df.write.partitionBy("number").format("text").save(path ) 当然这两种方法都有一个缺陷,就是当数据量特别大的时候,数据在repartition的过程中特别耗费资源,也会容易出现任务failed的情况,小编采用的解决办法是,适当的对原rdd进行split,然后遍历每个rdd,进行multioutput操作 形似如下: val rdd = sc.textFile(input) var split_rdd = rdd.randomSplit(Array(1.0,1.0,1.0,1.0)) for (one <- Array(1,2,3,4)) { split_rdd(one)XXXX } 参考: Spark学习笔记——二次排序,TopN,TopNByGroup Spark多文件输出(MultipleOutputFormat) scala - Write to multiple outputs by key Spark - one Spark job Write to multiple outputs by key Spark - one Spark job
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 最近在搞一个价格分类模型,虽说是分类,用的是kmeans算法,求出聚类中心,对每个价格进行级别定级。虽然说起来简单,但做起来却是并没有那么容易,不只是因为数据量大,在执行任务时要不是效率问题就是shuffle报错等。但在这整个过程中对scala编程,Spark rdd 机制,以及海量数据背景下对算法的认知都有很大的提升,这一篇文章主要是总结一些Spark在shell 终端提交jar包任务的时候的相关知识,在后续文章会具体涉及到相关的”实战经历“。 对Spark的认识 由于之前接触过Hadoop,对Spark也是了解一些皮毛,但中间隔了好久才重新使用spark,期间也产生过一些错误的认识。 之前觉得MapReduce耗费时间,写一个同等效果的Spark程序很快就能执行完,很长一段时间自己都是在本地的单机环境进行测试学习,所以这种错误的认知就会更加深刻,但事实却并非如此,MR之所以慢是因为每一次操作数据都写在了磁盘上,大量的IO造成了时间和资源的浪费,但是Spark是基于内存的计算引擎,相比MR,减少的是大量的IO,但并不是说给一个Spark程序足够的资源,就可以为所欲为了,在提交一个spark程序时,不仅要考虑所在资源队列的总体情况,还要考虑代码本身的高效性,要尽量避免大量的shuffle操作和action操作,尽量使用同一个rdd。 会用spark,会调api和能用好spark是两回事,在进行开发的过程中,不仅要了解运行原理,还要了解业务,将合适的方法和业务场景合适的结合在一起,才能发挥最大的价值。 spark-submit 进入spark的home目录,执行以下命令查看帮助 bin/spark-submit --help spark提交任务常见的两种模式 1:local/local[K] 本地使用一个worker线程运行spark程序 本地使用K个worker线程运行spark程序 此种模式下适合小批量数据在本地调试代码 2:yarn-client/yarn-cluster yarn-client:以client方式连接到YARN集群,集群的定位由环境变量HADOOP_CONF_DIR定义,该方式driver在client运行。 yarn-cluster:以cluster方式连接到YARN集群,集群的定位由环境变量HADOOP_CONF_DIR定义,该方式driver也在集群中运行。 注意:若使用的是本地文件需要在file路径前加:file:// 在提交任务时的几个重要参数 executor-cores —— 每个executor使用的内核数,默认为1 num-executors —— 启动executors的数量,默认为2 executor-memory —— executor内存大小,默认1G driver-cores —— driver使用内核数,默认为1 driver-memory —— driver内存大小,默认512M 下边给一个提交任务的样式 spark-submit \ --master local[5] \ --driver-cores 2 \ --driver-memory 8g \ --executor-cores 4 \ --num-executors 10 \ --executor-memory 8g \ --class PackageName.ClassName XXXX.jar \ --name "Spark Job Name" \ InputPath \ OutputPath 如果这里通过--queue 指定了队列,那么可以免去写--master 以上就是通过spark-submit来提交一个任务 几个参数的常规设置 executor_cores*num_executors 表示的是能够并行执行Task的数目 不宜太小或太大!一般不超过总队列 cores 的 25%,比如队列总 cores 400,最大不要超过100,最小不建议低于 40,除非日志量很小。 executor_cores 不宜为1!否则 work 进程中线程数过少,一般 2~4 为宜。 executor_memory 一般 6~10g 为宜,最大不超过20G,否则会导致GC代价过高,或资源浪费严重。 driver-memory driver 不做任何计算和存储,只是下发任务与yarn资源管理器和task交互,除非你是 spark-shell,否则一般 1-2g 增加每个executor的内存量,增加了内存量以后,对性能的提升,有三点: 1、如果需要对RDD进行cache,那么更多的内存,就可以缓存更多的数据,将更少的数据写入磁盘, 甚至不写入磁盘。减少了磁盘IO。 2、对于shuffle操作,reduce端,会需要内存来存放拉取的数据并进行聚合。如果内存不够,也会写入磁盘。如果给executor分配更多内存以后,就有更少的数据,需要写入磁盘,甚至不需要写入磁盘。减少了磁盘IO,提升了性能。 3、对于task的执行,可能会创建很多对象。如果内存比较小,可能会频繁导致JVM堆内存满了,然后频繁GC,垃圾回收,minor GC和full GC。(速度很慢)。内存加大以后,带来更少的GC,垃圾回收,避免了速度变慢,性能提升。 常规注意事项 预处理数据,丢掉一些不必要的数据 增加Task的数量 过滤掉一些容易导致发生倾斜的key 避免创建重复的RDD 尽可能复用一个RDD 对多次使用的RDD进行持久化 尽量避免使用shuffle算子 在要使用groupByKey算子的时候,尽量用reduceByKey或者aggregateByKey算子替代.因为调用groupByKey时候,按照相同的key进行分组,形成RDD[key,Iterable[value]]的形式,此时所有的键值对都将被重新洗牌,移动,对网络数据传输造成理论上的最大影响. 使用高性能的算子 参考: 1:http://www.cnblogs.com/haozhengfei/p/e570f24c43fa15f23ebb97929a1b7fe6.html 2:https://www.jianshu.com/p/4c584a3bac7d
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 在机器学习领域,体梯度下降算法分为三种 批量梯度下降算法(BGD,Batch gradient descent algorithm) 随机梯度下降算法(SGD,Stochastic gradient descent algorithm) 小批量梯度下降算法(MBGD,Mini-batch gradient descent algorithm) 批量梯度下降算法 BGD是最原始的梯度下降算法,每一次迭代使用全部的样本,即权重的迭代公式中(公式中用θ代替θi), ȷ(θ0,θ1,...,θn)=∑i=0m(hθ(x0,x1,...,xn)−yi)2 θi=θi−α∂ȷ(θ1,θ2,...,θn)∂θi 公式(1) 这里的m代表所有的样本,表示从第一个样本遍历到最后一个样本。 特点: 能达到全局最优解,易于并行实现 当样本数目很多时,训练过程缓慢 随机梯度下降算法 SGD的思想是更新每一个参数时都使用一个样本来进行更新,即公式(1)中m为1。每次更新参数都只使用一个样本,进行多次更新。这样在样本量很大的情况下,可能只用到其中的一部分样本就能得到最优解了。 但是,SGD伴随的一个问题是噪音较BGD要多,使得SGD并不是每次迭代都向着整体最优化方向。 特点: - 训练速度快 - 准确度下降,并不是最优解,不易于并行实现 小批量梯度下降算法 MBGD的算法思想就是在更新每一参数时都使用一部分样本来进行更新,也就是公式(1)中的m的值大于1小于所有样本的数量。 相对于随机梯度下降,Mini-batch梯度下降降低了收敛波动性,即降低了参数更新的方差,使得更新更加稳定。相对于批量梯度下降,其提高了每次学习的速度。并且其不用担心内存瓶颈从而可以利用矩阵运算进行高效计算。一般而言每次更新随机选择[50,256]个样本进行学习,但是也要根据具体问题而选择,实践中可以进行多次试验,选择一个更新速度与更次次数都较适合的样本数。mini-batch梯度下降可以保证收敛性,常用于神经网络中。 补充 在样本量较小的情况下,可以使用批量梯度下降算法,样本量较大的情况或者线上,可以使用随机梯度下降算法或者小批量梯度下降算法。 在机器学习中的无约束优化算法,除了梯度下降以外,还有前面提到的最小二乘法,此外还有牛顿法和拟牛顿法。 梯度下降法和最小二乘法相比,梯度下降法需要选择步长,而最小二乘法不需要。梯度下降法是迭代求解,最小二乘法是计算解析解。如果样本量不算很大,且存在解析解,最小二乘法比起梯度下降法要有优势,计算速度很快。但是如果样本量很大,用最小二乘法由于需要求一个超级大的逆矩阵,这时就很难或者很慢才能求解解析解了,使用迭代的梯度下降法比较有优势。 梯度下降法和牛顿法/拟牛顿法相比,两者都是迭代求解,不过梯度下降法是梯度求解,而牛顿法/拟牛顿法是用二阶的海森矩阵的逆矩阵或伪逆矩阵求解。相对而言,使用牛顿法/拟牛顿法收敛更快。但是每次迭代的时间比梯度下降法长。 sklearn中的SGD sklearn官网上查了一下,并没有找到BGD和MBGD的相关文档,只是看到可SGD的,感兴趣的可以直接去官网看英文文档,点击SGD查看:SGD,这也有一个中文的 SGD In [1]: from sklearn.linear_model import SGDClassifier In [2]: X = [[0., 0.], [1., 1.]] In [3]: y = [0, 1] In [4]: clf = SGDClassifier(loss="hinge", penalty="l2") In [5]: clf.fit(X, y) Out[5]: SGDClassifier(alpha=0.0001, average=False, class_weight=None, epsilon=0.1, eta0=0.0, fit_intercept=True, l1_ratio=0.15, learning_rate='optimal', loss='hinge', n_iter=5, n_jobs=1, penalty='l2', power_t=0.5, random_state=None, shuffle=True, verbose=0, warm_start=False) In [6]: clf.predict([[2., 2.]]) Out[6]: array([1]) In [7]: clf.coef_ Out[7]: array([[ 9.91080278, 9.91080278]]) In [8]: clf.intercept_ Out[8]: array([-9.97004991]) 参考: https://www.cnblogs.com/pinard/p/5970503.html http://blog.csdn.net/uestc_c2_403/article/details/74910107
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 指数平滑法是一种特殊的加权平均法,加权的特点是对离预测值较近的历史数据给予较大的权数,对离预测期较远的历史数据给予较小的权数,权数由近到远按指数规律递减,所以,这种预测方法被称为指数平滑法。它可分为一次指数平滑法、二次指数平滑法及更高次指数平滑法。 关于指数平滑的得相关资料: ES API接口: https://github.com/IBBD/IBBD.github.io/blob/master/elk/aggregations-pipeline.md https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-pipeline-movavg-aggregation.html 理论概念 http://blog.sina.com.cn/s/blog_4b9acb5201016nkd.html ES移动平均聚合:Moving Average的四种模型 simple 就是使用窗口内的值的和除于窗口值,通常窗口值越大,最后的结果越平滑: (a1 + a2 + … + an) / n curl -XPOST 'localhost:9200/_search?pretty' -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg":{ "moving_avg":{ "buckets_path": "the_sum", "window" : 30, "model" : "simple" } } } } } } ' 线性模型:Linear 对窗口内的值先做线性变换处理,再求平均:(a1 * 1 + a2 * 2 + … + an * n) / (1 + 2 + … + n) curl -XPOST 'localhost:9200/_search?pretty' -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_avg":{ "buckets_path": "the_sum", "window" : 30, "model" : "linear" } } } } } } ' 指数平滑模型 指数模型:EWMA (Exponentially Weighted) 即: 一次指数平滑模型 EWMA模型通常也成为单指数模型(single-exponential), 和线性模型的思路类似,离当前点越远的点,重要性越低,具体化为数值的指数下降,对应的参数是alpha。 alpha值越小,下降越慢。(估计是用1 - alpha去计算的)默认的alpha=0.3 计算模型:s2 = α * x2 + (1 - α) * s1 其中α是平滑系数,si是之前i个数据的平滑值,α取值为[0,1],越接近1,平滑后的值越接近当前时间的数据值,数据越不平滑,α越接近0,平滑后的值越接近前i个数据的平滑值,数据越平滑,α的值通常可以多尝试几次以达到最佳效果。 一次指数平滑算法进行预测的公式为:xi+h=si,其中i为当前最后的一个数据记录的坐标,亦即预测的时间序列为一条直线,不能反映时间序列的趋势和季节性。 curl -XPOST 'localhost:9200/_search?pretty' -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_avg":{ "buckets_path": "the_sum", "window" : 30, "model" : "ewma", "settings" : { "alpha" : 0.5 } } } } } } } ' 二次指数平滑模型: Holt-Linear 计算模型: s2 = α * x2 + (1 - α) * (s1 + t1) t2 = ß * (s2 - s1) + (1 - ß) * t1 默认alpha = 0.3 and beta = 0.1 二次指数平滑保留了趋势的信息,使得预测的时间序列可以包含之前数据的趋势。二次指数平滑的预测公式为 xi+h=si+hti 二次指数平滑的预测结果是一条斜的直线。 curl -XPOST 'localhost:9200/_search?pretty' -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_avg":{ "buckets_path": "the_sum", "window" : 30, "model" : "holt", "settings" : { "alpha" : 0.5, "beta" : 0.5 } } } } } } } ' 三次指数平滑模型:Holt-Winters无季节模型 三次指数平滑在二次指数平滑的基础上保留了季节性的信息,使得其可以预测带有季节性的时间序列。三次指数平滑添加了一个新的参数p来表示平滑后的趋势。 1: Additive Holt-Winters:Holt-Winters加法模型 下面是累加的三次指数平滑 si=α(xi-pi-k)+(1-α)(si-1+ti-1) ti=ß(si-si-1)+(1-ß)ti-1 pi=γ(xi-si)+(1-γ)pi-k 其中k为周期 累加三次指数平滑的预测公式为: xi+h=si+hti+pi-k+(h mod k) curl -XPOST 'localhost:9200/_search?pretty' -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_avg":{ "buckets_path": "the_sum", "window" : 30, "model" : "holt_winters", "settings" : { "type" : "add", "alpha" : 0.5, "beta" : 0.5, "gamma" : 0.5, "period" : 7 } } } } } } } ' 2: Multiplicative Holt-Winters:Holt-Winters乘法模型 下式为累乘的三次指数平滑: si=αxi/pi-k+(1-α)(si-1+ti-1) ti=ß(si-si-1)+(1-ß)ti-1 pi=γxi/si+(1-γ)pi-k 其中k为周期 累乘三次指数平滑的预测公式为: xi+h=(si+hti)pi-k+(h mod k) α,ß,γ的值都位于[0,1]之间,可以多试验几次以达到最佳效果。 s,t,p初始值的选取对于算法整体的影响不是特别大,通常的取值为s0=x0,t0=x1-x0,累加时p=0,累乘时p=1. curl -XPOST 'localhost:9200/_search?pretty' -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_avg":{ "buckets_path": "the_sum", "window" : 30, "model" : "holt_winters", "settings" : { "type" : "mult", "alpha" : 0.5, "beta" : 0.5, "gamma" : 0.5, "period" : 7, "pad" : true } } } } } } } ' 预测模型:Prediction 使用当前值减去前一个值,其实就是环比增长 curl -XPOST 'localhost:9200/_search?pretty' -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "my_date_histo":{ "date_histogram":{ "field":"date", "interval":"1M" }, "aggs":{ "the_sum":{ "sum":{ "field": "price" } }, "the_movavg": { "moving_avg":{ "buckets_path": "the_sum", "window" : 30, "model" : "simple", "predict" : 10 } } } } } } ' 最小化:Minimization 某些模型(EWMA,Holt-Linear,Holt-Winters)需要配置一个或多个参数。参数选择可能会非常棘手,有时不直观。此外,这些参数的小偏差有时会对输出移动平均线产生剧烈的影响。 出于这个原因,三个“可调”模型可以在算法上最小化。最小化是一个参数调整的过程,直到模型生成的预测与输出数据紧密匹配为止。最小化并不是完全防护的,并且可能容易过度配合,但是它往往比手动调整有更好的结果。 ewma和holt_linear默认情况下禁用最小化,而holt_winters默认启用最小化。 Holt-Winters最小化是最有用的,因为它有助于提高预测的准确性。 EWMA和Holt-Linear不是很好的预测指标,主要用于平滑数据,所以最小化对于这些模型来说不太有用。 通过最小化参数启用/禁用最小化:”minimize” : true 原始数据 数据为SSH login数据其中 IP/user已处理 { "_index": "logstash-sshlogin-others-success-2017-10", "_type": "sshlogin", "_id": "AV-weLF8c2nHCDojUbat", "_version": 2, "_score": 1, "_source": { "srcip": "222.221.238.162", "dstport": "", "pid": "20604", "program": "sshd", "message": "dwasw-ibb01:Oct 19 23:38:02 176.231.228.130 sshd[20604]: Accepted publickey for nmuser from 222.221.238.162 port 49484 ssh2", "type": "zsh-sshlogin", "ssh_type": "ssh_successful_login", "forwarded": "false", "manufacturer": "others", "IndexTime": "2017-10", "path": "/home/logstash/log/logstash_data/aaa/sshlogin/11.txt", "number": 1, "hostname": "116.241.218.120", "protocol": "ssh2", "@timestamp": "2017-10-19T15:38:02.000Z", "ssh_method": "publickey", "_hostname": "wq-ibb01", "@version": "1", "host": "localhost", "srcport": "49484", "dstip": "", "category": "sshlogin", "user": "nmuwser" } } 利用ES API接口去调用查询数据 “interval”: “hour”: hour为单位,这里可以是分钟,小时,天,周,月 “format”: “yyyy-MM-dd HH”: 聚合结果得日期格式 "the_sum": { "sum": { "field": "number" } } number为要聚合得字段 curl -POST 'localhost:9200/logstash-sshlogin-others-success-2017-10/sshlogin/_search?pretty' -H 'Content-Type: application/json' -d' { "size": 0, "query": { "term": { "ssh_type": "ssh_successful_login" } }, "aggs": { "hour_sum": { "date_histogram": { "field": "@timestamp", "interval": "hour", "format": "yyyy-MM-dd HH" }, "aggs": { "the_sum": { "sum": { "field": "number" } }, "the_movavg": { "moving_avg": { "buckets_path": "the_sum", "window": 30, "model": "holt", "settings": { "alpha": 0.5, "beta": 0.7 } } } } } } }' 得到的结果形式为: { "took" : 35, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "failed" : 0 }, "hits" : { "total" : 206821, "max_score" : 0.0, "hits" : [ ] }, "aggregations" : { "hour_sum" : { "buckets" : [ { "key_as_string" : "2017-09-30 16", "key" : 1506787200000, "doc_count" : 227, "the_sum" : { "value" : 227.0 } }, { "key_as_string" : "2017-09-30 17", "key" : 1506790800000, "doc_count" : 210, "the_sum" : { "value" : 210.0 }, "the_movavg" : { "value" : 113.5 } }, { "key_as_string" : "2017-09-30 18", "key" : 1506794400000, "doc_count" : 365, "the_sum" : { "value" : 365.0 }, "the_movavg" : { "value" : 210.0 } }, ... } } 对应得python代码(查询数据到画图) # coding: utf-8 from elasticsearch import Elasticsearch import matplotlib.pyplot as plt from matplotlib.font_manager import FontManager, FontProperties class Smooth: def __init__(self,index): self.es = Elasticsearch(['localhost:9200']) self.index = index # 处理mac中文编码错误 def getChineseFont(self): return FontProperties(fname='/System/Library/Fonts/PingFang.ttc') # 对index进行聚合 def agg(self): # "format": "yyyy-MM-dd HH:mm:SS" dsl = ''' { "size": 0, "query": { "term": { "ssh_type": "ssh_successful_login" } }, "aggs": { "hour_sum": { "date_histogram": { "field": "@timestamp", "interval": "day", "format": "dd" }, "aggs": { "the_sum": { "sum": { "field": "number" } }, "the_movavg": { "moving_avg": { "buckets_path": "the_sum", "window": 30, "model": "holt_winters", "settings": { "alpha": 0.5, "beta": 0.7 } } } } } } } ''' res = self.es.search(index=self.index, body=dsl) return res['aggregations']['hour_sum']['buckets'] # 画图 def draw(self): x,y_true,y_pred = [],[],[] for one in self.agg(): x.append(one['key_as_string']) y_true.append(one['the_sum']['value']) if 'the_movavg' in one.keys(): # 前几条数据没有 the_movavg 字段,故将真实值赋值给pred值 y_pred.append(one['the_movavg']['value']) else: y_pred.append(one['the_sum']['value']) x_line = range(len(x)) plt.figure(figsize=(10,5)) plt.plot(x_line,y_true,color="r") plt.plot(x_line,y_pred,color="g") plt.xlabel(u"每单位时间",fontproperties=self.getChineseFont()) #X轴标签 plt.xticks(range(len(x)), x) plt.ylabel(u"聚合结果",fontproperties=self.getChineseFont()) #Y轴标签 plt.title(u"10月份 SSH 主机登录成功聚合图",fontproperties=self.getChineseFont()) # 标题 plt.legend([u"True value",u"Predict value"]) plt.show() smooth = Smooth("logstash-sshlogin-others-success-2017-10") print smooth.draw() 结果图示为:
标签(空格分隔): 回归分析 sklearn pandas 交叉验证 打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 参考原文:http://www.cnblogs.com/pinard/p/6016029.html 这里进行了手动实现,增强记忆。 1:数据集介绍 使用的数据是UCI大学公开的机器学习数据 数据的介绍在这: http://archive.ics.uci.edu/ml/datasets/Combined+Cycle+Power+Plant 数据的下载地址在这:http://archive.ics.uci.edu/ml/machine-learning-databases/00294/ 里面是一个循环发电场的数据,共有9568个样本数据,每个数据有5列,分别是:AT(温度), V(压力), AP(湿度), RH(压强), PE(输出电力)。我们不用纠结于每项具体的意思。 我们的问题是得到一个线性的关系,对应PE是样本输出,而AT/V/AP/RH这4个是样本特征, 机器学习的目的就是得到一个线性回归模型,即: PE=θ0+θ0∗AT+θ0∗V+θ0∗AP+θ0∗RH 而需要学习的,就是θ0,θ1,θ2,θ3,θ4这5个参数。 2:准备数据 下载源数据之后,解压会得到一个xlsx的文件,打开另存为csv文件,数据已经整理好,没有非法数据,但是数据并没有进行归一化,不过这里我们可以使用sklearn来帮我处理 sklearn的归一化处理参考:http://blog.csdn.net/gamer_gyt/article/details/77761884 3:使用pandas来进行数据的读取 import pandas as pd # pandas 读取数据 data = pd.read_csv("Folds5x2_pp.csv") data.head() 然后会看到如下结果,说明数据读取成功: AT V AP RH PE 0 8.34 40.77 1010.84 90.01 480.48 1 23.64 58.49 1011.40 74.20 445.75 2 29.74 56.90 1007.15 41.91 438.76 3 19.07 49.69 1007.22 76.79 453.09 4 11.80 40.66 1017.13 97.20 464.43 4:准备运行算法的数据 X = data[["AT","V","AP","RH"]] print X.shape y = data[["PE"]] print y.shape (9568, 4) (9568, 1) 说明有9658条数据,其中”AT”,”V”,”AP”,”RH” 四列作为样本特征,”PE”列作为样本输出。 5:划分训练集和测试集 from sklearn.cross_validation import train_test_split # 划分训练集和测试集 X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=1) print X_train.shape print y_train.shape print X_test.shape print y_test.shape (7176, 4) (7176, 1) (2392, 4) (2392, 1) 75%的数据被划分为训练集,25的数据划分为测试集。 6:运行sklearn 线性模型 from sklearn.linear_model import LinearRegression linreg = LinearRegression() linreg.fit(X_train,y_train) # 训练模型完毕,查看结果 print linreg.intercept_ print linreg.coef_ [ 447.06297099] [[-1.97376045 -0.23229086 0.0693515 -0.15806957]] 即我们得到的模型结果为: PE=447.06297099−1.97376045∗AT−0.23229086∗V+0.0693515∗AP−0.15806957∗RH 7:模型评价 我们需要评价模型的好坏,通常对于线性回归来讲,我么一般使用均方差(MSE,Mean Squared Error)或者均方根差(RMSE,Root Mean Squared Error)来评价模型的好坏 y_pred = linreg.predict(X_test) from sklearn import metrics # 使用sklearn来计算mse和Rmse print "MSE:",metrics.mean_squared_error(y_test, y_pred) print "RMSE:",np.sqrt(metrics.mean_squared_error(y_test, y_pred)) MSE: 20.0804012021 RMSE: 4.48111606657 得到了MSE或者RMSE,如果我们用其他方法得到了不同的系数,需要选择模型时,就用MSE小的时候对应的参数。 8:交叉验证 我们可以通过交叉验证来持续优化模型,代码如下,我们采用10折交叉验证,即cross_val_predict中的cv参数为10: # 交叉验证 from sklearn.model_selection import cross_val_predict predicted = cross_val_predict(linreg,X,y,cv=10) print "MSE:",metrics.mean_squared_error(y, predicted) print "RMSE:",np.sqrt(metrics.mean_squared_error(y, predicted)) MSE: 20.7955974619 RMSE: 4.56021901469 可以看出,采用交叉验证模型的MSE比第6节的大,主要原因是我们这里是对所有折的样本做测试集对应的预测值的MSE,而第6节仅仅对25%的测试集做了MSE。两者的先决条件并不同。 9:画图查看结果 # 画图查看结果 import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.scatter(y, predicted) ax.plot([y.min(), y.max()], [y.min(), y.max()], 'k--', lw=4) ax.set_xlabel('Measured') ax.set_ylabel('Predicted') plt.show()
标签(空格分隔): 回归分析 二元线性回归 多元线性回归 打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 在上一篇文章中我们介绍了 回归分析之理论篇,在其中我们有聊到线性回归和非线性回归,包括广义线性回归,这一篇文章我们来聊下回归分析中的线性回归。 一元线性回归 预测房价: 输入编号 平方米 价格 1 150 6450 2 200 7450 3 250 8450 4 300 9450 5 350 11450 6 400 15450 7 600 18450 针对上边这种一元数据来讲,我们可以构建的一元线性回归函数为 H(x)=k∗x+b 其中H(x)为平方米价格表,k是一元回归系数,b为常数。最小二乘法的公式: k=∑n1(xi−x¯)(yi−y¯)∑n1(xi−x¯)2 自己使用python代码实现为: def leastsq(x,y): """ x,y分别是要拟合的数据的自变量列表和因变量列表 """ meanX = sum(x) * 1.0 / len(x) # 求x的平均值 meanY = sum(y) * 1.0 / len(y) # 求y的平均值 xSum = 0.0 ySum = 0.0 for i in range(len(x)): xSum += (x[i] - meanX) * (y[i] - meanY) ySum += (x[i] - meanX) ** 2 k = ySum/xSum b = ySum - k * meanX return k,b 使用python的scipy包进行计算: leastsq(func, x0, args=(), Dfun=None, full_output=0, col_deriv=0, ftol=1.49012e-08, xtol=1.49012e-08, gtol=0.0, maxfev=0, epsfcn=None, factor=100, diag=None) from scipy.optimize import leastsq import numpy as np def fun(p, x): """ 定义想要拟合的函数 """ k,b = p # 从参数p获得拟合的参数 return k*x + b def err(p, x, y): return fun(p,x) - y #定义起始的参数 即从 y = 1*x+1 开始,其实这个值可以随便设,只不过会影响到找到最优解的时间 p0 = [1,1] #将list类型转换为 numpy.ndarray 类型,最初我直接使用 #list 类型,结果 leastsq函数报错,后来在别的blog上看到了,原来要将类型转 #换为numpy的类型 x1 = np.array([150,200,250,300,350,400,600]) y1 = np.array([6450,7450,8450,9450,11450,15450,18450]) xishu = leastsq(err, p0, args=(x1,y1)) print xishu[0] 当然python的leastsq函数不仅仅局限于一元一次的应用,也可以应用到一元二次,二元二次,多元多次等,具体可以看下这篇博客:http://www.cnblogs.com/NanShan2016/p/5493429.html 多元线性回归 总之:我们可以用python leastsq函数解决几乎所有的线性回归的问题了,比如说 y=a∗x2+b∗x+c y=a∗x21+b∗x1+c∗x2+d y=a∗x31+b∗x21+c∗x1+d 在使用时只需把参数列表和 fun 函数中的return 换一下,拿以下函数举例 y=a∗x21+b∗x1+c∗x2+d 对应的python 代码是: from scipy.optimize import leastsq import numpy as np def fun(p, x1, x2): """ 定义想要拟合的函数 """ a,b,c,d = p # 从参数p获得拟合的参数 return a * (x1**2) + b * x1 + c * x2 + d def err(p, x1, x2, y): return fun(p,x1,x2) - y #定义起始的参数 即从 y = 1*x+1 开始,其实这个值可以随便设,只不过会影响到找到最优解的时间 p0 = [1,1,1,1] #将list类型转换为 numpy.ndarray 类型,最初我直接使用 #list 类型,结果 leastsq函数报错,后来在别的blog上看到了,原来要将类型转 #换为numpy的类型 x1 = np.array([150,200,250,300,350,400,600]) # 面积 x2 = np.array([4,2,7,9,12,14,15]) # 楼层 y1 = np.array([6450,7450,8450,9450,11450,15450,18450]) # 价格/平方米 xishu = leastsq(err, p0, args=(x1,x2,y1)) print xishu[0] sklearn中的线性回归应用 普通最小二乘回归 这里我们使用的是sklearn中的linear_model来模拟 y=a∗x1+b∗x2+c In [1]: from sklearn.linear_model import LinearRegression In [2]: linreg = LinearRegression() In [3]: linreg.fit([[0, 0], [1, 1], [2, 2]], [0, 1, 2]) In [4]: linreg.coef_ Out[4]: array([ 0.5, 0.5]) In [5]: linreg.intercept_ Out[5]: 1.1102230246251565e-16 In [6]: linreg.predict([4,4]) Out[6]: array([ 4.]) In [7]: zip(["x1","x2"], linreg.coef_) Out[7]: [('x1', 0.5), ('x2', 0.49999999999999989)] 所以可得 y=0.5∗x1+0.5∗x2+1.11e−16 linreg.coef_ 为系数 a,b linreg.intercept_ 为截距 c 缺点:因为系数矩阵x与它的转置矩阵相乘得到的矩阵不能求逆,导致最小二乘法得到的回归系数不稳定,方差很大。 多项式回归:基函数扩展线性模型 机器学习中一种常见的模式是使用线性模型训练数据的非线性函数。这种方法保持了一般快速的线性方法的性能,同时允许它们适应更广泛的数据范围。 例如,可以通过构造系数的多项式特征来扩展一个简单的线性回归。在标准线性回归的情况下,你可能有一个类似于二维数据的模型: y(w,x)=w0+w1x1+w2x2 如果我们想把抛物面拟合成数据而不是平面,我们可以结合二阶多项式的特征,使模型看起来像这样: y(w,x)=w0+w1x1+w2x2+w3x1x2+w4x21+w5x22 我们发现,这仍然是一个线性模型,想象着创建一个新变量: z=[x1,x2,x1x2,x21,x22] 可以把线性回归模型写成下边这种形式: y(w,x)=w0+w1z1+w2z2+w3z3+w4z4+w5z5 我们看到,所得的多项式回归与我们上面所考虑的线性模型相同(即模型在W中是线性的),可以用同样的方法来求解。通过考虑在用这些基函数建立的高维空间中的线性拟合,该模型具有灵活性,可以适应更广泛的数据范围。 使用如下代码,将二维数据进行二元转换,转换规则为: [x1,x2]=>[1,x1,x2,x21,x1x2,x22] In [15]: from sklearn.preprocessing import PolynomialFeatures In [16]: import numpy as np In [17]: X = np.arange(6).reshape(3,2) In [18]: X Out[18]: array([[0, 1], [2, 3], [4, 5]]) In [19]: poly = PolynomialFeatures(degree=2) In [20]: poly.fit_transform(X) Out[20]: array([[ 1., 0., 1., 0., 0., 1.], [ 1., 2., 3., 4., 6., 9.], [ 1., 4., 5., 16., 20., 25.]]) 验证: In [38]: from sklearn.preprocessing import PolynomialFeatures In [39]: from sklearn.linear_model import LinearRegression In [40]: from sklearn.pipeline import Pipeline In [41]: import numpy as np In [42]: In [42]: model = Pipeline( [ ("poly",PolynomialFeatures(degree=3)),("linear",LinearRegression(fit_intercept=False)) ] ) In [43]: model Out[43]: Pipeline(steps=[('poly', PolynomialFeatures(degree=3, include_bias=True, interaction_only=False)), ('linear', LinearRegression(copy_X=True, fit_intercept=False, n_jobs=1, normalize=False))]) In [44]: x = np.arange(5) In [45]: y = 3 - 2 * x + x ** 2 - x ** 3 In [46]: y Out[46]: array([ 3, 1, -5, -21, -53]) In [47]: model = model.fit(x[:,np.newaxis],y) In [48]: model.named_steps['linear'].coef_ Out[48]: array([ 3., -2., 1., -1.]) 我们可以看出最后求出的参数和一元三次方程是一致的。 这里如果把degree改为2,y的方程也换一下,结果也是一致的 In [51]: from sklearn.linear_model import LinearRegression In [52]: from sklearn.preprocessing import PolynomialFeatures In [53]: from sklearn.pipeline import Pipeline In [54]: import numpy as np In [55]: model = Pipeline( [ ("poly",PolynomialFeatures(degree=2)),("linear",LinearRegression(fit_intercept=False)) ] ) In [56]: x = np.arange(5) In [57]: y = 3 + 2 * x + x ** 2 In [58]: model = model.fit(x[:, np.newaxis], y) In [59]: model.named_steps['linear'].coef_ Out[59]: array([ 3., 2., 1.]) 线性回归的评测 在上一篇文章中我们聊到了回归模型的评测方法,解下来我们详细聊聊如何来评价一个回归模型的好坏。 这里我们定义预测值和真实值分别为: true = [10, 5, 3, 2] pred = [9, 5, 5, 3] 1: 平均绝对误差(Mean Absolute Error, MAE) 1N(∑1n|yi−y¯|) 2: 均方误差(Mean Squared Error, MSE) 1N∑1n(yi−y¯)2 3: 均方根误差(Root Mean Squared Error, RMSE) 1N∑1n(yi−y¯)2‾‾‾‾‾‾‾‾‾‾‾⎷ In [80]: from sklearn import metrics In [81]: import numpy as np In [82]: true = [10, 5, 3, 2] In [83]: pred = [9, 5, 5, 3] In [84]: print("MAE: ", metrics.mean_absolute_error(true,pred)) ('MAE: ', 1.0) In [85]: print("MAE By Hand: ", (1+0+2+1)/4.) ('MAE By Hand: ', 1.0) In [86]: print("MSE: ", metrics.mean_squared_error(true,pred)) ('MSE: ', 1.5) In [87]: print("MSE By Hand: ", (1 ** 2 + 0 ** 2 + 2 ** 2 + 1 ** 2 ) / 4.) ('MSE By Hand: ', 1.5) In [88]: print("RMSE: ", np.sqrt(metrics.mean_squared_error(true,pred))) ('RMSE: ', 1.2247448713915889) In [89]: print("RMSE By Hand: ", np.sqrt((1 ** 2 + 0 ** 2 + 2 ** 2 + 1 ** 2 ) / 4.)) ('RMSE By Hand: ', 1.2247448713915889) 总结 线性回归在现实中还是可以解决很多问题的,但是并不是万能的,后续我会继续整理逻辑回归,岭回归等相关回归的知识,如果你感觉有用,欢迎分享!
标签: 数据挖掘/曼哈顿距离/欧几里得距离/皮尔逊相关系数/余弦相似度 打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 本文涉及以下几种距离计算公式的分析,参考资料为《面向程序员的数据挖掘指南》 曼哈顿距离 欧几里得距离 闵可夫斯基距离 皮尔逊相关系数 余弦相似度 之前整理过一篇关于距离相关的文章:机器学习算法中的距离和相似性计算公式,分析以及python实现 闵可夫斯基距离 两个n维变量a(x11,x12,…,x1n)与 b(x21,x22,…,x2n)间的闵可夫斯基距离定义为: ∑k=1n∣∣x1k−x2k∣∣p‾‾‾‾‾‾‾‾‾‾‾‾‾‾⎷p 其中p是一个变参数。 当p=1时,就是曼哈顿距离 当p=2时,就是欧氏距离 当p→∞时,就是切比雪夫距离 根据变参数的不同,闵氏距离可以表示一类的距离。 p值越大,单个维度的差值大小会对整体距离有更大的影响 曼哈顿距离/欧几里得距离的瑕疵 在《面向程序员的数据挖掘指南》中给出了这样一组样例数据, 下图为一个在线音乐网站的的用户评分情况,用户可以用1-5星来评价一个乐队,下边是8位用户对8个乐队的评价: 表中的横线表示用户没有对乐队进行评价,我们在计算两个用户的距离时,只采用他们都评价过的乐队。 现在来求Angelica和Bill的距离,因为他们共同评分过的乐队有5个,所以使用其对该5个乐队的评分进行曼哈顿距离的计算为: Dis_1 = |3.5-2| + |2-3.5| + |5-2| + |1.5-3.5| + |2-3| = 9 同样使用欧式距离计算为: Dis_2 = sqrt( (3.5-2)^2 + (2-3.5)^2 + (5-2)^2 + (1.5-3.5)^2 + (2-3)^2 ) = 4.3 当对Angelica和Bill,Bill和Chan进行距离对比时,由于两者的共同评分过的乐队均为5,数据都在一个5维空间里,是公平的,如果现在要计算Angelica和Hailey与Bill的距离时,会发现,Angelica与Bill共同评分的有5个乐队,Hailey与Bill共同评分的有3个乐队,也就是说两者数据一个在5维空间里,一个在三维空间里,这样明显是不公平的。这将会对我们进行计算时产生不好的影响,所以曼哈顿距离和欧几里得距离在数据完整的情况下效果最好。 用户问题/皮尔逊相关系数/分数膨胀 现象——用户问题 仔细观察用户对乐队的评分数据,可以发现每个用户的评分标准不同: Bill没有打出极端的分数,都在2-4分之间 Jordyn似乎喜欢所有的乐队,打分都在4-5之间 Hailey是一个有趣的人,他的评分不是1就是4 那么如何比较这些用户呢?比如说Hailey的4分是相当于Jordyn的4分还是5分呢?我觉得更接近5分,这样一来,就影响推荐系统的准确性了! 解决该现象 解决该现象的办法之一就是 使用皮尔逊相关系数,例如下边这样的数据样例(Clara和Robert对五个乐队的评分): 这种现象在数据挖掘领域被称为“分数膨胀“。我们将其评分画成图,如下: 一条直线-完全吻合,代表着Clara和Robert的喜好完全一致。 皮尔逊相关系数用于衡量两个变量之间的相关性,他的值在-1~1,1代表完全一致,-1代表完全相悖。所以我们可以利用皮尔逊相关系数来找到相似的用户。 皮尔逊相关系数的计算公式为: 该公式除了看起来比较复杂,另外需要对数据进行两次遍历,第一次遍历求出 x平均值和y平均值,第二次遍历才能出现结果,这里提供另外一个计算公式,能够计算皮尔逊相关系数的近似值: 余弦相似度/稀疏数据 假设这样一个数据集,一个在线音乐网站,有10000w首音乐(这里不考虑音乐类型,年代等因素),每个用户常听的也就其中的几十首,这种情况下使用曼哈顿或者欧几里得或者皮尔逊相关系数进行计算用户之间相似性,计算相似值会非常小,因为用户之间的交集本来就很少,这样对于计算结果来讲是很不准确的,这个时候就需要余弦相似度了,余弦相似度进行计算时会自动略过这些非零值。 总结 这里只是简答的介绍了这几种相似性距离度量的方法和场景,但是在实际环境中远比这个复杂许多。这里总结下: 如果数据存在“分数膨胀“问题,就使用皮尔逊相关系数 如果数据比较密集,变量之间基本都存在共有值,且这些距离数据都是非常重要的,那就使用欧几里得或者曼哈顿距离 如果数据是稀疏的,就使用余弦相似度
标签: 回归分析 / 正态分布 / T检验 / 关系 打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 2015年的机器学习博客其实都是看《机器学习实战》这本书时学到的,说实话当时也是知其然,不知其所以然,以至于对其理解不深刻,好多细节和理论知识都搞的是乱七八糟,自从工作之后再去看一个算法,思考的比之前多了点,查看资料也比之前多了点,生怕理解错误,影响其他人,当然在理解的程度上还是不够深刻,这也是一个学习的过程吧,记录一下,欢迎指正。 一:一些名词定义 1)指数分布族 指数分布族是指可以表示为指数形式的概率分布。 fX(x∣θ)=h(x)exp(η(θ)⋅T(x)−A(θ)) 其中,η为自然参数(nature parameter),T(x)是充分统计量(sufficient statistic)。当参数A,h,T都固定以后,就定义了一个以η为参数的函数族。 伯努利分布与高斯分布是两个典型的指数分布族 伯努利分布 又名两点分布或者0-1分布,是一个离散型概率分布。假设1的概率为p,0的概率为q,则 其概率质量函数为: fX(x)=px(1−p)1−x={pq if x=1,if x=0. 其期望值为: E[X]=∑i=01xifX(x)=0+p=p 其方差为: var[X]=∑i=01(xi−E[X])2fX(x)=(0−p)2(1−p)+(1−p)2p=p(1−p)=pq 正态分布(高斯分布) 若随机变量X服从一个位置参数为 μ 、尺度参数为 σ 的概率分布,记为: X∼N(μ,σ2), 其概率密度函数为: f(x)=1σ2π‾‾‾√e−(x−μ)22σ2 正态分布的数学期望值或期望值μ 等于位置参数,决定了分布的位置;其方差 σ2 的开平方或标准差σ 等于尺度参数,决定了分布的幅度。 标准正态分布: 如果μ=0 并且 σ=1 则这个正态分布称为标准正态分布。简化为: f(x)=12π‾‾‾√exp(−x22) 如下图所示: 正态分布中一些值得注意的量: 密度函数关于平均值对称 平均值与它的众数(statistical mode)以及中位数(median)同一数值。 函数曲线下68.268949%的面积在平均数左右的一个标准差范围内。 95.449974%的面积在平均数左右两个标准差 2σ 的范围内。 99.730020%的面积在平均数左右三个标准差3σ 的范围内。 99.993666%的面积在平均数左右四个标准差4σ 的范围内。 函数曲线的反曲点(inflection point)为离平均数一个标准差距离的位置。 2)多重共线性和完全共线性 多重共线性:指线性回归模型中的解释变量之间由于存在精确相关关系或高度相关关系而使模型估计失真或难以估计准确。一般来说,由于经济数据的限制使得模型设计不当,导致设计矩阵中解释变量间存在普遍的相关关系。通俗点理解就是自变量里边有一些是打酱油的,可以由另外一些变量推导出来,当变量中存在大量的多重共线性变量就会导致模型误差很大,这个时候就需要从自变量中将“打酱油”的变量给剔除掉。 完全共线性:在多元回归中,一个自变量是一个或多个其他自变量的线性函数。 两者在某种特殊情况下是有交集的。 3)T检验 T检验又叫student T 检验,主要用于样本含量小,总标准差 σ 未知的正太分布数据。T检验是用于小样本的两个平均值差异程度的检查方法,他是用T分布理论值来推断事件发生的概率,从而判断两个平均数的差异是否显著。 参考: http://blog.csdn.net/shulixu/article/details/53354206 4)关系 函数关系 确定性关系,y=3+2x 相关关系 非确定性关系,比如说高中时数学成绩好的人,一般物理成绩也好,这是因为它们背后使用的都是数学逻辑,这种酒叫做非确定性关系。 5)虚拟变量 定义: 又称虚设变量、名义变量或哑变量,用以反映质的属性的一个人工变量,是量化了的自变量,通常取值为0或1。(通常为离散变量,因子变量) 作用: 引入哑变量可使线形回归模型变得更复杂,但对问题描述更简明,一个方程能达到两个方程的作用,而且接近现实。 设置: 例如:体重(w)和身高(h),性别(s)的关系,但这里性别并非连续的或者数字可以表示的变量,你并不能拿 1表示男,2表示女,这里的性别是离散变量,只能为男或者女,所以这里就需要引入哑变量来处理。 性别(s) =》 isman(男1,非男0),iswoman (因为只有两种可能,所以这里只需要引入一个哑变量即可),同理假设这里有另外一个变量肤色(有黑,白,黄三种可能),那么这里只需引入两个哑变量即可(isblack,iswhite),因为不是这两种的话那肯定是黄色皮肤了。 例子: 针对上边所说的体重和身高,性别的关系。 构建模型: 1)加法模型 w = a + b * h + c * isman 针对数据样本而言,性别是确定的,所以 c * isman 的结果不是c就是0,所以在加法模型下,影响的是模型在y轴上的截距。这说明的是针对不同的性别而言,回归方程是平衡的,只不过是截距不一样。 2)乘法模型 w = a + b * h + c * isman * h + d * iswoman * h 同样针对数据样本而言,性别也是确定的,假设一个男性,isman 为1,iswoman 为0,则上述模型变成了 w = a + b*h + c * h =a + (b+c) * h,这个时候就是在y轴上的截距一样,而斜率不一致。 3)混合模型 w = a + b * h + c * isman + d * iswoman + e * isman * h + f * iswoman * h 假设一个针对一个性别为男的样本数据,该模型变可以变成 w = a + b*h + c + e * h = a +c + (b+e)*h,这个时候斜率和截距都是不一样的。 二:什么是回归(分析) 回归就是利用样本(已知数据),产生拟合方程,从而(对未知数据)进行预测。比如说我有一组随机变量X(X1,X2,X3…)和另外一组随机变量Y(Y1,Y2,Y3…),那么研究变量X与Y之间的统计学方法就叫做回归分析。当然这里X和Y是单一对应的,所以这里是一元线性回归。 回归分为线性回归和非线性回归,其中一些非线性回归可以用线性回归的方法来进行分析的叫做==广义线性回归==,接下来我们来了解下每一种回归: 1)线性回归 线性回归可以分为一元线性回归和多元线性回归。当然线性回归中自变量的指数都是1,这里的线性并非真的是指用一条线将数据连起来,也可以是一个二维平面,三维平面等。 一元线性回归:自变量只有一个的回归,比如说北京二环的房子面积(Area)和房子总价(Money)的关系,随着面积(Area)的增大,房屋价格也是不断增长。这里的自变量只有面积,所以这里是一元线性回归。 多元线性回归:自变量大于等于两个,比如说北京二环的房子面积(Area),楼层(floor)和房屋价格(Money)的关系,这里自变量是两个,所以是二元线性回归,三元,多元同理。 2)非线性回归 有一类模型,其回归参数不是线性的,也不能通过转换的方法将其变为线性的参数,这类模型称为非线性回归模型。非线性回归可以分为一元回归和多元回归。非线性回归中至少有一个自变量的指数不为1。回归分析中,当研究的因果关系只涉及因变量和一个自变量时,叫做一元回归分析;当研究的因果关系涉及因变量和两个或两个以上自变量时,叫做多元回归分析。 3)广义线性回归 一些非线性回归可以用线性回归的方法来进行分析叫做广义线性回归。 典型的代表是Logistic回归。 4)如何衡量相关关系既判断适不适合使用线性回归模型? 使用相关系数(-1,1),绝对值越接近于1,相关系数越高,越适合使用线性回归模型(Rxy>0,代表正相关,Rxy<0,代表负相关) rXY=∑(Xi−X¯)(Yi−Y¯)∑(Xi−X¯)2)∑(Yi−Y¯)2)‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾√ 三:回归中困难点 1)选定变量 假设自变量特别多,有一些是和因变量相关的,有一些是和因变量不相关的,这里我们就需要筛选出有用的变量,如果筛选后变量还特别多的话,可以采用降维的方式进行变量缩减(可以参考之前的PCA降维的文章:http://blog.csdn.net/gamer_gyt/article/details/51418069 ,基本是整理《机器学习实战》这本书的笔记) 2)发现多重共线性 (1).方差扩大因子法( VIF) 一般认为如果最大的VIF超过10,常常表示存在多重共线性。 (2).容差容忍定法 如果容差(tolerance)<=0.1,常常表示存在多重共线性。 (3). 条件索引 条件索引(condition index)>10,可以说明存在比较严重的共线性 3)过拟合与欠拟合问题 过拟合和欠拟合其实对每一个模型来讲都是存在的,过拟合就是模型过于符合训练数据的趋势,欠拟合就是模型对于训练数据和测试数据都表现出不好的情况。针对于欠拟合来讲,是很容易发现的,通常不被讨论。 在进行模型训练的时候,算法要进行不断的学习,模型在训练数据和测试数据上的错误都在不断下降,但是,如果学习的时间过长的话,模型在训练数据集上的表现将会继续下降,这是因为模型已经过拟合,并且学习到了训练数据集中不恰当的细节和噪音,同时,测试集上的错误率开始上升,也是模型泛化能力在下降。 这个完美的临界点就在于测试集中的错误率在上升时,此时训练集和测试集上都有良好的表现。通常有两种手段可以帮助你找到这个完美的临界点:重采样方法和验证集方法。 如何限制过拟合? 过拟合和欠拟合可以导致很差的模型表现。但是到目前为止大部分机器学习实际应用时的问题都是过拟合。 过拟合是个问题因为训练数据上的机器学习算法的评价方法与我们最关心的实际上的评价方法,也就是算法在位置数据上的表现是不一样的。 当评价机器学习算法时我们有两者重要的技巧来限制过拟合 使用重采样来评价模型效能 保留一个验证数据集 最流行的重采样技术是k折交叉验证。指的是在训练数据的子集上训练和测试模型k次,同时建立对于机器学习模型在未知数据上表现的评估。 验证集只是训练数据的子集,你把它保留到你进行机器学习算法的最后才使用。在训练数据上选择和调谐机器学习算法之后,我们在验证集上在对于模型进行评估,以便得到一些关于模型在未知数据上的表现的认知。 4)检验模型是否合理 验证目前主要采用如下三类办法: 1、拟合优度检验 主要有R^2,t检验,f检验等等 这三种检验为常规验证,只要在95%的置信度内满足即可说明拟合效果良好。 2、预测值和真实值比较 主要是差值和比值,一般差值和比值都不超过5%。 3、另外的办法 GEH方法最为常用。GEH是Geoffrey E. Havers于1970年左右提出的一种模型验证方法,其巧妙的运用一个拟定的公式和标准界定模型的拟合优劣。 GEH=(2(M-C)^2/(M+C))^(1/2) 其中M是预测值,C是实际观测值 如果GEH小于5,认为模型拟合效果良好,如果GEH在5-10之间,必须对数据不可靠需要进行检查,如果GEH大于10,说明数据存在问题的几率很高。 http://blog.sina.com.cn/s/blog_66188c300100hl45.html 5)线性回归的模型评判 误差平方和(残差平方和) 例如二维平面上的一点(x1,y1),经过线性回归模型预测其值为 y_1,那么预测模型的好与坏就是计算预测结果到直线的距离的大小,由于是一组数据,那么便是这一组数据的和。 点到直线的距离公式为: ∣∣Ax0+By0+C∣∣A2+B2‾‾‾‾‾‾‾‾√ 由于涉及到开方,在计算过程中十分不方便,所以这里转换为纵轴上的差值,即利用预测值与真实值的差进行累加求和,最小时即为最佳的线性回归模型,但是这里涉及到预测值与真实值的差可能为负数,所以这里用平方,所以最终的误差平方和为: RSS=∑i=1n(yi−yi^)2=∑i=1n[yi−(α+βxi)]2 AIC准则(赤池信息准则) AIC=n ln (RSSp/n)+2p n为变量总个数,p为选出的变量个数,AIC越小越好
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 一:数据归一化 数据归一化(标准化)处理是数据挖掘的一项基础工作,不同评价指标往往具有不同的量纲和量纲单位,这样的情况会影响到数据分析的结果,为了消除指标之间的量纲影响,需要进行数据标准化处理,以解决数据指标之间的可比性。原始数据经过数据标准化处理后,各指标处于同一数量级,适合进行综合对比评价。 归一化方法有两种形式,一种是把数变为(0,1)之间的小数,一种是把有量纲表达式变为无量纲表达式。在机器学习中我们更关注的把数据变到0~1之间,接下来我们讨论的也是第一种形式。 1)min-max标准化 min-max标准化也叫做离差标准化,是对原始数据的线性变换,使结果落到[0,1]区间,其对应的数学公式如下: Xscale=x−minmax−min 对应的python实现为 # x为数据 比如说 [1,2,1,3,2,4,1] def Normalization(x): return [(float(i)-min(x))/float(max(x)-min(x)) for i in x] 如果要将数据转换到[-1,1]之间,可以修改其数学公式为: Xscale=x−xmeanmax−min x_mean 表示平均值。 对应的python实现为 import numpy as np # x为数据 比如说 [1,2,1,3,2,4,1] def Normalization(x): return [(float(i)-np.mean(x))/float(max(x)-min(x)) for i in x] 其中max为样本数据的最大值,min为样本数据的最小值。这种方法有个缺陷就是当有新数据加入时,可能导致max和min的变化,需要重新定义。 该标准化方法有一个缺点就是,如果数据中有一些偏离正常数据的异常点,就会导致标准化结果的不准确性。比如说一个公司员工(A,B,C,D)的薪水为6k,8k,7k,10w,这种情况下进行归一化对每个员工来讲都是不合理的。 当然还有一些其他的办法也能实现数据的标准化。 2)z-score标准化 z-score标准化也叫标准差标准化,代表的是分值偏离均值的程度,经过处理的数据符合标准正态分布,即均值为0,标准差为1。其转化函数为 Xscale=x−μσ 其中μ为所有样本数据的均值,σ为所有样本数据的标准差。 其对应的python实现为: import numpy as np #x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] def z_score(x): return (x - np.mean(x) )/np.std(x, ddof = 1) z-score标准化方法同样对于离群异常值的影响。接下来看一种改进的z-score标准化方法。 3)改进的z-score标准化 将标准分公式中的均值改为中位数,将标准差改为绝对偏差。 Xscale=x−xcenterσ1 中位数是指将所有数据进行排序,取中间的那个值,如数据量是偶数,则取中间两个数据的平均值。 σ1为所有样本数据的绝对偏差,其计算公式为: 1N∑1n|xi−xcenter| 二:sklearn中的归一化 sklearn.preprocessing 提供了一些实用的函数 用来处理数据的维度,以供算法使用。 1)均值-标准差缩放 即我们上边对应的z-score标准化。 在sklearn的学习中,数据集的标准化是很多机器学习模型算法的常见要求。如果个别特征看起来不是很符合正态分布,那么他们可能为表现不好。 实际上,我们经常忽略分布的形状,只是通过减去整组数据的平均值,使之更靠近数据中心分布,然后通过将非连续数特征除以其标准偏差进行分类。 例如,用于学习算法(例如支持向量机的RBF内核或线性模型的l1和l2正则化器)的目标函数中使用的许多元素假设所有特征都以零为中心并且具有相同顺序的方差。如果特征的方差大于其他数量级,则可能主导目标函数,使估计器无法按预期正确地学习其他特征。 例子: >>> from sklearn import preprocessing >>> import numpy as np >>> X_train = np.array([[ 1., -1., 2.], ... [ 2., 0., 0.], ... [ 0., 1., -1.]]) >>> X_scaled = preprocessing.scale(X_train) >>> X_scaled array([[ 0. , -1.22474487, 1.33630621], [ 1.22474487, 0. , -0.26726124], [-1.22474487, 1.22474487, -1.06904497]]) 标准化后的数据符合标准正太分布 >>> X_scaled.mean(axis=0) array([ 0., 0., 0.]) >>> X_scaled.std(axis=0) array([ 1., 1., 1.]) 预处理模块还提供了一个实用程序级StandardScaler,它实现了Transformer API来计算训练集上的平均值和标准偏差,以便能够稍后在测试集上重新应用相同的变换。 >>> scaler = preprocessing.StandardScaler().fit(X_train) >>> scaler StandardScaler(copy=True, with_mean=True, with_std=True) >>> scaler.mean_ array([ 1. , 0. , 0.33333333]) >>> scaler.scale_ array([ 0.81649658, 0.81649658, 1.24721913]) >>> scaler.transform(X_train) array([[ 0. , -1.22474487, 1.33630621], [ 1.22474487, 0. , -0.26726124], [-1.22474487, 1.22474487, -1.06904497]]) 使用转换器可以对新数据进行转换 >>> X_test = [[-1., 1., 0.]] >>> scaler.transform(X_test) array([[-2.44948974, 1.22474487, -0.26726124]]) 2)min-max标准化 X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0)) >>> X_train = np.array([[ 1., -1., 2.], ... [ 2., 0., 0.], ... [ 0., 1., -1.]]) >>> min_max_scaler = preprocessing.MinMaxScaler() >>> X_train_minmax = min_max_scaler.fit_transform(X_train) >>> X_train_minmax array([[ 0.5 , 0. , 1. ], [ 1. , 0.5 , 0.33333333], [ 0. , 1. , 0. ]]) 上边我们创建的min_max_scaler 同样适用于新的测试数据 >>> X_test = np.array([[ -3., -1., 4.]]) >>> X_test_minmax = min_max_scaler.transform(X_test) >>> X_test_minmax array([[-1.5 , 0. , 1.66666667]]) 可以通过scale_和min方法查看标准差和最小值 >>> min_max_scaler.scale_ array([ 0.5 , 0.5 , 0.33333333]) >>> min_max_scaler.min_ array([ 0. , 0.5 , 0.33333333]) 3)最大值标准化 对于每个数值/每个维度的最大值 >>> X_train array([[ 1., -1., 2.], [ 2., 0., 0.], [ 0., 1., -1.]]) >>> max_abs_scaler = preprocessing.MaxAbsScaler() >>> X_train_maxabs = max_abs_scaler.fit_transform(X_train) >>> X_train_maxabs array([[ 0.5, -1. , 1. ], [ 1. , 0. , 0. ], [ 0. , 1. , -0.5]]) >>> X_test = np.array([[ -3., -1., 4.]]) >>> X_test_maxabs = max_abs_scaler.transform(X_test) >>> X_test_maxabs array([[-1.5, -1. , 2. ]]) >>> max_abs_scaler.scale_ array([ 2., 1., 2.]) 4)规范化 规范化是文本分类和聚类中向量空间模型的基础 >>> X = [[ 1., -1., 2.], ... [ 2., 0., 0.], ... [ 0., 1., -1.]] >>> X_normalized = preprocessing.normalize(X, norm='l2') >>> X_normalized array([[ 0.40824829, -0.40824829, 0.81649658], [ 1. , 0. , 0. ], [ 0. , 0.70710678, -0.70710678]]) 解释:norm 该参数是可选的,默认值是l2(向量各元素的平方和然后求平方根),用来规范化每个非零向量,如果axis参数设置为0,则表示的是规范化每个非零的特征维度。 机器学习中的范数规则:点击阅读 其他对应参数:点击查看 preprocessing模块提供了训练种子的功能,我们可通过以下方式得到一个新的种子,并对新数据进行规范化处理。 >>> normalizer = preprocessing.Normalizer().fit(X) >>> normalizer Normalizer(copy=True, norm='l2') >>> normalizer.transform(X) array([[ 0.40824829, -0.40824829, 0.81649658], [ 1. , 0. , 0. ], [ 0. , 0.70710678, -0.70710678]]) >>> normalizer.transform([[-1,1,0]]) array([[-0.70710678, 0.70710678, 0. ]]) 5)二值化 将数据转换到0-1 之间 >>> X [[1.0, -1.0, 2.0], [2.0, 0.0, 0.0], [0.0, 1.0, -1.0]] >>> binarizer = preprocessing.Binarizer().fit(X) >>> binarizer Binarizer(copy=True, threshold=0.0) >>> binarizer.transform(X) array([[ 1., 0., 1.], [ 1., 0., 0.], [ 0., 1., 0.]]) 可以调整二值化的门阀 >>> binarizer = preprocessing.Binarizer(threshold=1.1) >>> binarizer.transform(X) array([[ 0., 0., 1.], [ 1., 0., 0.], [ 0., 0., 0.]]) 6)编码的分类特征 通常情况下,特征不是作为连续值给定的。例如一个人可以有 ["male", "female"], ["from Europe", "from US", "from Asia"], ["uses Firefox", "uses Chrome", "uses Safari", "uses Internet Explorer"] 这些特征可以被有效的编码为整数,例如 ["male", "from US", "uses Internet Explorer"] => [0, 1, 3] ["female", "from Asia", "uses Chrome"] would be [1, 2, 1]. 这样的整数不应该直接应用到scikit的算法中,可以通过one-of-k或者独热编码(OneHotEncorder),该种处理方式会把每个分类特征的m中可能值转换成m个二进制值。 >>> enc = preprocessing.OneHotEncoder() >>> enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]]) OneHotEncoder(categorical_features='all', dtype=<class 'numpy.float64'>, handle_unknown='error', n_values='auto', sparse=True) >>> enc.transform([[0,1,3]]).toarray() array([[ 1., 0., 0., 1., 0., 0., 0., 0., 1.]]) 默认情况下,从数据集中自动推断出每个特征可以带多少个值。可以明确指定使用的参数n_values。在我们的数据集中有两种性别,三种可能的大陆和四种Web浏览器。然后,我们拟合估计量,并转换一个数据点。在结果中,前两个数字编码性别,下一组三个数字的大陆和最后四个Web浏览器。 >>> enc = preprocessing.OneHotEncoder(n_values=[2,3,4]) >>> enc.fit([[1,2,3],[0,2,0]]) OneHotEncoder(categorical_features='all', dtype=<class 'numpy.float64'>, handle_unknown='error', n_values=[2, 3, 4], sparse=True) >>> enc.transform([[1,0,0]]).toarray() array([[ 0., 1., 1., 0., 0., 1., 0., 0., 0.]]) 7)填补缺失值 由于各种原因,真实数据中存在大量的空白值,这样的数据集,显然是不符合scikit的要求的,那么preprocessing模块提供这样一个功能,利用已知的数据来填补这些空白。 >>> import numpy as np >>> from sklearn.preprocessing import Imputer >>> imp = Imputer(missing_values='NaN',strategy='mean',verbose=0) >>> imp.fit([[1, 2], [np.nan, 3], [7, 6]]) Imputer(axis=0, copy=True, missing_values='NaN', strategy='mean', verbose=0) >>> X = [[np.nan, 2], [6, np.nan], [7, 6]] >>> print(imp.transform(X)) [[ 4. 2. ] [ 6. 3.66666667] [ 7. 6. ]] Imputer同样支持稀疏矩阵 >>> import scipy.sparse as sp >>> X = sp.csc_matrix([[1,2],[0,3],[7,6]]) >>> imp = Imputer(missing_values=0,strategy='mean',axis=0) >>> imp.fit(X) Imputer(axis=0, copy=True, missing_values=0, strategy='mean', verbose=0) >>> X_test = sp.csc sp.csc sp.csc_matrix( >>> X_test = sp.csc_matrix([[0,2],[6,0],[7,6]]) >>> print(imp.transform(X_test)) [[ 4. 2. ] [ 6. 3.66666667] [ 7. 6. ]] 8)生成多项式特征 通常,通过考虑输入数据的非线性特征来增加模型的复杂度是很有用的。一个简单而常用的方法是多项式特征,它可以得到特征的高阶和相互作用项。 其遵循的原则是 (X1,X2)−>(1,X1,X2,X21,X1X2,X22) >>> import numpy as np >>> from sklearn.preprocessing import PolynomialFeatures >>> X = np.arange(6).reshape(3, 2) >>> X array([[0, 1], [2, 3], [4, 5]]) >>> poly = PolynomialFeatures(2) >>> poly.fit_transform(X) array([[ 1., 0., 1., 0., 0., 1.], [ 1., 2., 3., 4., 6., 9.], [ 1., 4., 5., 16., 20., 25.]]) 有些情况下,有相互关系的标签才是必须的,这个时候可以通过设置 interaction_only=True 来进行多项式特征的生成 >>> X = np.arange(9).reshape(3, 3) >>> X array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) >>> poly = PolynomialFeatures(degree=3, interaction_only=True) >>> poly.fit_transform(X) array([[ 1., 0., 1., 2., 0., 0., 2., 0.], [ 1., 3., 4., 5., 12., 15., 20., 60.], [ 1., 6., 7., 8., 42., 48., 56., 336.]]) 其遵循的规则是: (X1,X2,X3)−>(1,X1,X2,X3,X1X2,X1X3,X2X3,X1X2X3) 对应的scikit-learn资料为: http://scikit-learn.org/stable/modules/preprocessing.html
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 一:异常检测概述 1)引用维基百科 在数据挖掘中,异常检测(英语:anomaly detection)对不匹配预期模式或数据集中其他项目的项目、事件或观测值的识别。通常异常项目会转变成银行欺诈、结构缺陷、医疗问题、文本错误等类型的问题。异常也被称为离群值、新奇、噪声、偏差和例外。 特别是在检测滥用与网络入侵时,有趣性对象往往不是罕见对象,但却是超出预料的突发活动。这种模式不遵循通常统计定义中把异常点看作是罕见对象,于是许多异常检测方法(特别是无监督的方法)将对此类数据失效,除非进行了合适的聚集。相反,聚类分析算法可能可以检测出这些模式形成的微聚类。 2)有三大类异常检测算法 在假设数据集中大多数实例都是正常的前提下 - 无监督异常检测方法能通过寻找与其他数据最不匹配的实例来检测出未标记测试数据的异常。 - 监督式异常检测方法需要一个已经被标记“正常”与“异常”的数据集,并涉及到训练分类器(与许多其他的统计分类问题的关键区别是异常检测的内在不均衡性)。 - 半监督式异常检测方法根据一个给定的正常训练数据集创建一个表示正常行为的模型,然后检测由学习模型生成的测试实例的可能性。 3)应用领域 异常检测技术用于各种领域,如入侵检测、欺诈检测、故障检测、系统健康监测、传感器网络事件检测和生态系统干扰检测等。它通常用于在预处理中删除从数据集的异常数据。在监督式学习中,去除异常数据的数据集往往会在统计上显著提升准确性。 二:正态分布 1)正态分布介绍 正态分布又名高斯分布,是一个在数学,物理以及工程等领域都非常重要的概率分布。由于这个分布函数有很多漂亮的性质,使得其在诸多设计统计科学离散科学等许多领域都有着重大的影响力。 若随机变量X服从一个位置参数为 μ 尺度参数为 σ 的概率分布,记为: X∼N(μ,σ2) 则其概率密度函数为 f(x)=1σ2π‾‾‾√e−(x−μ)22σ2 正态分布的数学期望值或期望值 μ 等于位置参数,决定了分布的位置;其方差 σ2 的开平方或标准差 σ 等于尺度参数,决定了分布的幅度。 2) 标准正态分布 若 μ = 0, σ=1 ,这个分布被称为标准正态分布,这个分布可以简化为: f(x)=12π‾‾‾√exp(−x22) 不同参数的正态分布图: 3)正态分布中一些值得注意的量: 密度函数关于平均值对称 平均值与它的众数(statistical mode)以及中位数(median)同一数值。 函数曲线下68.268949%的面积在平均数左右的一个标准差范围内。 95.449974%的面积在平均数左右两个标准差 2σ 的范围内。 99.730020%的面积在平均数左右三个标准差 3σ 的范围内。 99.993666%的面积在平均数左右四个标准差 4σ 的范围内。 函数曲线的反曲点(inflection point)为离平均数一个标准差距离的位置。 更多基础资料参考WIKI :点击查看 三:异常点检测介绍 异常点检测(又称为离群点检测)是找出其行为很不同于预期对象的一个检测过程。这些对象被称为异常点或者离群点。异常点检测在很多实际的生产生活中都有着具体的应用,比如信用卡欺诈,工业损毁检测,图像检测等。 异常点(outlier)是一个数据对象,它明显不同于其他的数据对象,就好像它是被不同的机制产生的一样。例如下图红色的点,就明显区别于蓝色的点。相对于蓝色的点而言,红色的点就是异常点。 一般来说,进行异常点检测的方法有很多,最常见的就是基于统计学的方法。 1)基于正态分布的一元离群点检测方法 假设有 n 个点 (x1,...,xn),那么可以计算出这 n 个点的均值μ 和方差 σ。均值和方差分别被定义为: μ=∑i=1nxi/n σ2=∑i=1n(xi−μ)2/n. 在正态分布的假设下,区域 μ±3σ 包含了99.7% 的数据,如果某个值距离分布的均值μ 超过了3σ,那么这个值就可以被简单的标记为一个异常点(outlier)。 2)多元离群点的检测方法 涉及两个或者两个以上变量的数据称为多元数据,很多一元离群点的检测方法都可以扩展到高维空间中,从而处理多元数据。 (1) 基于一元正态分布的离群点检测方法 假设 n 维的数据集合形如x⃗ i=(xi,1,...,xi,n),i∈{1,...,m},那么可以计算每个维度的均值和方差μj,σj,j∈{1,...,n}. 具体来说,对于j∈{1,...,n},可以计算 μj=∑i=1mxi,j/m σ2j=∑i=1m(xi,j−μj)2/m 在正态分布的假设下,如果有一个新的数据 x⃗ ,可以计算概率 p(x⃗ ) 如下: p(x⃗ )=∏j=1np(xj;μj,σ2j)=∏j=1n12π‾‾‾√σjexp(−(xj−μj)22σ2j) 根据概率值的大小就可以判断 x 是否属于异常值。运用该方法检测到的异常点如图,红色标记为异常点,蓝色表示原始的数据点. (2)多元高斯分布的异常点检测 假设 n 维的数据集合x⃗ =(x1,...,xn) , 可以计算 n 维的均值向量 μ⃗ =(E(x1),...,E(xn)) 和 n×n 的协方差矩阵: Σ=[Cov(xi,xj)],i,j∈{1,...,n} 如果有一个新的数据 x⃗ ,可以计算 p(x⃗ )=1(2π)n2|Σ|12exp(−12(x⃗ −μ⃗ )TΣ−1(x⃗ −μ⃗ )) 根据概率值的大小就可以判断x⃗ 是否属于异常值。 (3)使用马氏 (Mahalanobis) 距离检测多元离群点 对于一个多维的数据集合 D,假设a⎯⎯是均值向量,那么对于数据集 D 中的其他对象 a,从 a 到a⎯⎯的 Mahalanobis 距离是 MDist(a,a⎯⎯)=(a−a⎯⎯)TS−1(a−a⎯⎯)‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾√, 其中 S 是协方差矩阵。 在这里,MDist(a,a⎯⎯) 是数值,可以对这个数值进行排序,如果数值过大,那么就可以认为点 a 是离群点。或者对一元实数集合{MDist(a,a⎯⎯)|a∈D} 进行离群点检测,如果MDist(a,a⎯⎯) 被检测为异常点,那么就认为 a 在多维的数据集合 D 中就是离群点。 运用 Mahalanobis 距离方法检测到的异常点如图,红色标记为异常点,蓝色表示原始的数据点。 (4)使用卡方(χ2) 统计量检测多元离群点 在正态分布的假设下,χ2 统计量可以用来检测多元离群点。对于某个对象 a,χ2 统计量是 χ2=∑i=1n(ai−Ei)2/Ei. 其中,ai 是a 在第 i 维上的取值,Ei 是所有对象在第 i 维的均值,n 是维度。如果对象 a 的 χ2 统计量很大,那么该对象就可以认为是离群点。 运用χ2统计量检测到的异常点如图,红色标记为异常点,蓝色表示原始的数据点。 参考:https://zr9558.com/2016/06/13/outlierdetectionone/
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 前言 工作之后,发现对算法和技术的理解和上学时学习是不一样的,当时也整理了几篇关于k-means聚类的文章,但是现在看起来比较苍白和空洞,于是打算带着重新学习的态度对以往学习过或者见过的一些机器学习算法进行温习和总结,写的不足之处还望路过大神指点一二。 《机器学习实战》kMeans算法(K均值聚类算法) 《机器学习实战》二分-kMeans算法(二分K均值聚类) scikit-learn学习之K-means聚类算法与 Mini Batch K-Means算法 聚类分析 聚类分析(Cluster Analysisi)也被成为集群分析,基于生活中物以类聚的思想,是对某个样本或者指标进行分类多元统计分析的方法,他需要一组单独的属性特征或特性的代表变量,称为聚类变量。根据个人的样品或松紧之间的联系进行分类,一般分类的变量由研究者指定。 聚类分析的方法要求: 1. 聚类分析要简单,便于人直观的理解 2. 聚类分析主要是对未知事务的类别相似性探索,可能会有多个分析结果 3. 聚类分析一般情况必须是收敛的,无论现实中是否存在都能够得出客观的解 4. 聚类分析中的聚类属性选择是客观的,可以选择一个属性,也可以选择几个属性 5. 聚类分析的解完全依赖于研究者所选择的聚类变量,增加或者删除一些变量对最终的解都可能产生实质性的影响 什么是kmeans 其基本思想是根据随机给取的k个初始簇类中心,按照“距离最近”的原则将每条数据划分到最近的簇类中心,第一次迭代之后更新各个簇类中心,进行第二次的迭代,依旧按照“距离最近”原则进行数据归类,直到簇类不再改变,停止迭代。 具体的执行步骤如下: 输入:用户需要输入分类簇的数目K以及包含n个数据对象的数据集合。 输出:k个聚类完成的簇 步骤1:在输入的数据对象集合中随机初始化k个点作为k-means算法样本; 步骤2:计算给定的数据集合分别到初始化聚类中心的几何距离 步骤3:按照距离最短原则将没一点数据分配到最邻近的簇中 步骤4:使用每个簇中的样本数据几何中心作为新分类的聚类中心; 步骤5:反复迭代算法中步骤2、步骤3和步骤4直到算法收敛为止 步骤6:算法结束,得到输出结果。 那么这里就会引现出几个问题, 1: 初始簇类中心的选择? 2: K值的选择 3: “距离最近”原则具体指什么? 4: 怎么更新簇类中心? 5: 判断簇类收敛到不再改变的条件是什么? 下面我们就来一一解释这些问题 K-means 聚类中初始簇心的选择 选择初始类簇中心点对于聚类效果的好坏有很大的影响,那么我们该如何去确定簇类中心呢? 随机选取 随机选取是最简单的方法,但是也是有技巧的,我们通过对数据的预估来进行观察,从而确定初始的K值,比如说二维平面上的点,我们可以通过将其可视化到二维平面进行肉眼的判断,从而确定k值;比如说对于一些利用特征值进行聚类的数据,我们依旧可以将其进行量化到二维或者三维空间中,当然对于高维数据,首先可以进行降维操作,继而进行可视化。 随机选择法,假设有M行数据,我们可以用使用python的random模块来随机选取K行作为初始的聚类中心。 初始聚类 选用层次聚类或者Canopy算法进行初始聚类,然后利用这些类簇的中心点作为KMeans算法初始类簇中心点。 常用的层次聚类算法有BIRCH,ROCK,Canopy算法。 层次聚类的思想是: 一层一层地进行聚类,可以从下而上地把小的cluster合并聚集,也可以从上而下地将大的cluster进行分割。似乎一般用得比较多的是从下而上地聚集,这里我们说下自下而上的聚类。 所谓从下而上地合并cluster,具体而言,就是每次找到距离最短的两个cluster,然后进 行合并成一个大的cluster,直到全部合并为一个cluster。整个过程就是建立一个树结构,类似于下图。 Canopy算法的主要思想: 首先定义两个距离T1和T2,T1>T2.从初始的点的集合S中随机移除一个点P,然后对于还在S中的每个点I,计算该点I与点P的距离,如果距离小于T1,则将点I加入到点P所代表的Canopy中,如果距离小于T2,则将点I从集合S中移除,并将点I加入到点P所代表的Canopy中。迭代完一次之后,重新从集合S中随机选择一个点作为新的点P,然后重复执行以上步骤。 Canopy算法执行完毕后会得到很多Canopy,可以认为每个Canopy都是一个Cluster,与KMeans等硬划分算法不同,Canopy的聚类结果中每个点有可能属于多个Canopy。我们可以选择距离每个Canopy的中心点最近的那个数据点,或者直接选择每个Canopy的中心点作为KMeans的初始K个类簇中心点。 平均质心距离的加权平均值 首先随机选出一个点,然后选取离这个点距离最大的点作为第二个点,再选出离这两个点距离最小值最大的点作为第三个点,以此类推选出K个点 K值的确定 这里我们需要调试K值对于结果进行评测,从而判断最优K值,并将其应用到实际的模型中。 给定一个合适的类簇指标,比如平均半径或直径,只要我们假设的类簇的数目等于或者高于真实的类簇的数目时,该指标上升会很缓慢,而一旦试图得到少于真实数目的类簇时,该指标会急剧上升。 类簇的直径是指类簇内任意两点之间的最大距离,类簇的半径是指类簇内所有点到类簇中心距离的最大值 “距离最近”原则具体指什么? 即两两数据之间的相似度,可参考我的这篇博客 点击阅读 怎么更新簇类中心? 求平均值,需要说明一点的是这里的平均值并不一定是实实在在存在的数据,很大程度上就是虚拟的一个数据,比如说我们对二维平面上的点进行聚类时,更新簇类中心就不是原数据中的某个点,同样对于用户对item进行评分时的数据聚类,更新簇类中心后的item也并不是一个真正的item,而是虚拟出的一个。 判断簇类收敛到不再改变的条件是什么? 一般情况下判断聚类stop的条件是 聚类结果不再发生变化,但是对于始终无法停止的聚类的数据,就要额外的添加一些约束条件来迫使停止聚类,比如说更新簇类中心前后的新旧簇类中心相似度大于90%,距离小于10%等。 如何对用户评分数据集进行合理的分类 数据集说明 http://www.letiantian.me/2014-11-20-introduce-movielens-dataset/ 算法说明 这里采用的是k-means进行聚类,初始聚类中心为7个,当然可以自己设定,选取k个初始簇类中心的原则是,评分最多的k个item,“距离最近”原则采用的计算方法是余弦值法。 代码如下 # coding: utf-8 from numpy import * from math import * import cPickle as pickle import os class Kmeans: def __init__(self, filepath, n_clusters, user_num,item_num): self.filepath = filepath # 评分数据的路径 self.n_clusters = n_clusters # 聚类中心的数目 self.user_num = user_num # 用户数目 self.item_num = item_num # item 数目 self.dataSet = self.loadData() # 评分矩阵 self.CC = self.randCent() # 初始化随机产生的聚类中心 self.CC_new = {} self.C = {} # 簇类集 簇集item id self.C_new = {} # 簇类集 打分 # 构建评分矩阵 def loadData(self): dataSet = zeros((self.item_num,self.user_num)) with open(self.filepath, 'r') as fr: for line in fr.readlines(): curLine = line.strip().split("\t") dataSet[ int(curLine[1])-1, int(curLine[0])-1 ] = int(curLine[2]) return dataSet # 选取初始聚类中心 # 以评分数量最多的k个item作为初始的聚类中心 def randCent(self): CC_item_dic = {} CC = {} for i in range(self.item_num): CC_item_dic[ i ] = len(nonzero(self.dataSet[i,:])[0]) CC_item_dic = sorted(CC_item_dic.iteritems(), key = lambda one:one[1], reverse=True) for item in CC_item_dic[:self.n_clusters]: CC[item[0]] = self.dataSet[item[0],:] return CC # 计算相似度 def distances(self,item_id,cc_id): sum_fenzi = 0.0 sum_fenmu_1, sum_fenmu_2 = 0,0 for i in range(len(self.dataSet[item_id])): sum_fenzi += self.dataSet[item_id][i]*self.CC[cc_id][i] sum_fenmu_1 += self.dataSet[item_id][i]**2 sum_fenmu_2 += self.CC[cc_id][i]**2 return sum_fenzi/( sqrt(sum_fenmu_1) * sqrt(sum_fenmu_2) ) def kmeans(self): clusterChanged = True # 标志变量,若为true,则继续迭代 while clusterChanged: for item_id in range(self.item_num): max_cos = -inf # inf 为无穷大 clusterIndex = -1 # 创建索引,用来记录归属到哪个簇集 for cc_id in self.CC.keys(): cos = self.distances(item_id,cc_id) if cos > max_cos: max_cos = cos clusterIndex = cc_id self.C.setdefault(clusterIndex,[]).append(item_id) self.C_new.setdefault(clusterIndex,[]).append(self.dataSet[item_id]) # 更新簇类 for item_cid in self.C_new.keys(): for col in range(self.user_num): self.CC_new.setdefault(item_cid,[]).append( sum( mat( self.C_new[item_cid] ).T[col] )/len( mat( self.C_new[item_cid] ).T[col] ) ) for item_cid_new in self.CC.keys(): if (self.CC[item_cid_new] - self.CC_new[item_cid_new]).all(): # false 相等 self.CC = self.CC_new break else: clusterChanged = False return 1 def save(self): if os.path.exists("file/dataMatrix.dat"): os.remove("file/dataMatrix.dat") pickle.dump(self.dataSet,open("file/dataMatrix.dat","wb"),True) # pickle.load(open("file/dataMatrix.dat","rb")) if os.path.exists("file/clusterResult.dat"): os.remove("file/clusterResult.dat") pickle.dump(self.C,open("file/clusterResult.dat","wb"),True) # pickle.load(open("file/clusterResult.dat","rb")) if __name__ == '__main__': k_means = Kmeans('file/u.data', n_clusters =7,user_num = 943, item_num = 1682) k_means.kmeans() k_means.save() print 'cluster over!' 聚类结果的评价 这篇文章总结的还算简单清楚: http://blog.csdn.net/u012102306/article/details/52423074
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 前言 写这篇文章的目的不是说摘抄网上其他人的总结,刚才最近在看这方面的东西,为了让自己能够实际的去感受下每种求距离公式的差别,然后用python进行具体实现。 在机器学习中,经常要用到距离和相似性的计算公式,我么要常计算个体之间的差异大小,继而评价个人之间的差异性和相似性,最常见的就是数据分析中的相关分析,数据挖掘中的分类和聚类算法。如利用k-means进行聚类时,判断个体所属的类别,要利用距离计算公式计算个体到簇心的距离,如利用KNN进行分类时,计算个体与已知类别之间的相似性,从而判断个体所属的类别等。 文章编辑的过程中或许存在一个错误或者不合理的地方,欢迎指正。 参考:http://www.cnblogs.com/heaad/archive/2011/03/08/1977733.html 推荐:https://my.oschina.net/hunglish/blog/787596 欧氏距离 也称欧几里得距离,是指在m维空间中两个点之间的真实距离。欧式距离在ML中使用的范围比较广,也比较通用,就比如说利用k-Means对二维平面内的数据点进行聚类,对魔都房价的聚类分析(price/m^2 与平均房价)等。 二维空间的欧氏距离 二维平面上两点a(x1,y1)与b(x2,y2)间的欧氏距离 d12=(x1−x2)2+(y1−y2)2‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾√ python 实现为: # coding: utf-8 from numpy import * def twoPointDistance(a,b): d = sqrt( (a[0]-b[0])**2 + (a[1]-b[1])**2 ) return d print 'a,b 二维距离为:',twoPointDistance((1,1),(2,2)) 三维空间的欧氏距离 三维空间两点a(x1,y1,z1)与b(x2,y2,z2)间的欧氏距离 d12=(x1−x2)2+(y1−y2)2+(z1−z2)2‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾√ python 实现为: def threePointDistance(a,b): d = sqrt( (a[0]-b[0])**2 + (a[1]-b[1])**2 + (a[2]-b[2])**2 ) return d print 'a,b 三维距离为:',threePointDistance((1,1,1),(2,2,2)) 多维空间的欧氏距离 两个n维向量a(x11,x12,…,x1n)与 b(x21,x22,…,x2n)间的欧氏距离 ∑nk=1(x1k−x2k)2‾‾‾‾‾‾‾‾‾‾‾‾‾⎷ python 实现为: def distance(a,b): sum = 0 for i in range(len(a)): sum += (a[i]-b[i])**2 return sqrt(sum) print 'a,b 多维距离为:',distance((1,1,2,2),(2,2,4,4)) 这里传入的参数可以是任意维的,该公式也适应上边的二维和三维 标准欧氏距离 标准化欧氏距离是针对简单欧氏距离的缺点而作的一种改进方案。标准欧氏距离的思路:既然数据各维分量的分布不一样,好吧!那我先将各个分量都“标准化”到均值、方差相等吧。均值和方差标准化到多少呢?这里先复习点统计学知识吧,假设样本集X的均值(mean)为m,标准差(standard deviation)为s,那么X的“标准化变量”表示为: 而且标准化变量的数学期望为0,方差为1。因此样本集的标准化过程(standardization)用公式描述就是: X∗=X−ms 标准化后的值 = ( 标准化前的值 - 分量的均值 ) /分量的标准差 经过简单的推导就可以得到两个n维向量a(x11,x12,…,x1n)与 b(x21,x22,…,x2n)间的标准化欧氏距离的公式: d12=∑k=1n(x1k−x2ksk)2‾‾‾‾‾‾‾‾‾‾‾‾‾‾⎷ 如果将方差的倒数看成是一个权重,这个公式可以看成是一种加权欧氏距离(Weighted Euclidean distance)。 python 实现为 def moreBZOSdis(a,b): sumnum = 0 for i in range(len(a)): # 计算si 分量标准差 avg = (a[i]-b[i])/2 si = sqrt( (a[i] - avg) ** 2 + (b[i] - avg) ** 2 ) sumnum += ((a[i]-b[i])/si ) ** 2 return sqrt(sumnum) print 'a,b 标准欧式距离:',moreBZOSdis((1,2,1,2),(3,3,3,4)) 曼哈顿距离 又称为城市街区距离(City Block distance), 想象你在曼哈顿要从一个十字路口开车到另外一个十字路口,驾驶距离是两点间的直线距离吗?显然不是,除非你能穿越大楼。实际驾驶距离就是这个“曼哈顿距离”。而这也是曼哈顿距离名称的来源。同样曼哈顿距离也分为二维,三维和多维。 在计程车几何学中,一个圆是由从圆心向各个固定曼哈顿距离标示出来的点围成的区域,因此这种圆其实就是旋转了45度的正方形。如果有一群圆,且任两圆皆相交,则整群圆必在某点相交;因此曼哈顿距离会形成一个超凸度量空间。 这里有一篇人脸表情分类的论文采用的曼哈顿距离进行计算的,一种人脸表情分类的新方法——Manhattan距离 二维曼哈顿距离 二维平面两点a(x1,y1)与b(x2,y2)间的曼哈顿距离 d12=∣∣x1−x2∣∣+∣∣y1−y2∣∣ python实现为 def twoMHDdis(a,b): return abs(a[0]-b[0])+abs(a[1]-b[1]) print 'a,b 二维曼哈顿距离为:', twoMHDdis((1,1),(2,2)) 三维曼哈顿距离 三维平面两点a(x1,y1,z1)与b(x2,y2,z2)间的曼哈顿距离 d12=∣∣x1−x2∣∣+∣∣y1−y2∣∣+∣∣z1−z2∣∣ python实现为 def threeMHDdis(a,b): return abs(a[0]-b[0])+abs(a[1]-b[1]) + abs(a[2]-b[2]) print 'a,b 三维曼哈顿距离为:', threeMHDdis((1,1,1),(2,2,2)) 多维曼哈顿距离 多维平面两点a(x1,y1)与b(x2,y2)间的曼哈顿距离 d12=∑k=1n∣∣x1k−x2k∣∣ python实现为 def moreMHDdis(a,b): sum = 0 for i in range(len(a)): sum += abs(a[i]-b[i]) return sum print 'a,b 多维曼哈顿距离为:', moreMHDdis((1,1,1,1),(2,2,2,2)) 由于维距离计算是比较灵活的,所以也同样适合二维和三维。 切比雪夫距离 切比雪夫距离(Chebyshev Distance)的定义为:max( | x2-x1 | , |y2-y1 | , … ), 切比雪夫距离用的时候数据的维度必须是三个以上,这篇文章中曼哈顿距离,欧式距离,明式距离,切比雪夫距离区别 给了一个很形象的解释如下: 比如,有同样两个人,在纽约准备到北京参拜天安门,同一个地点出发的话,按照欧式距离来计算,是完全一样的。 但是按照切比雪夫距离,这是完全不同的概念了。 譬如,其中一个人是土豪,另一个人是中产阶级,第一个人就能当晚直接头等舱走人,而第二个人可能就要等机票什么时候打折再去,或者选择坐船什么的。 这样来看的话,距离是不是就不一样了呢? 或者还是不清楚,我再说的详细点。 同样是这两个人,欧式距离是直接算最短距离的,而切比雪夫距离可能还得加上财力,比如第一个人财富值100,第二个只有30,虽然物理距离一样,但是所包含的内容却是不同的。 二维切比雪夫距离 二维平面两点a(x1,y1)与b(x2,y2)间的切比雪夫距离 d12=max(∣∣x1−x2∣∣,∣∣y1−y2∣∣) python 实现为 def twoQBXFdis(a,b): return max( abs(a[0]-b[0]), abs(a[1]-b[1])) print 'a,b二维切比雪夫距离:' , twoQBXFdis((1,2),(3,4)) 多维切比雪夫距离 两个n维向量a(x11,x12,…,x1n)与 b(x21,x22,…,x2n)间的切比雪夫距离 d12=maxiϵn(∣∣x1i−x2i∣∣) python 实现为 def moreQBXFdis(a,b): maxnum = 0 for i in range(len(a)): if abs(a[i]-b[i]) > maxnum: maxnum = abs(a[i]-b[i]) return maxnum print 'a,b多维切比雪夫距离:' , moreQBXFdis((1,1,1,1),(3,4,3,4)) 马氏距离 有M个样本向量X1~Xm,协方差矩阵记为S,均值记为向量μ,则其中样本向量X到u的马氏距离表示为 D(x)=(X−μ)TS−1(X−μ)‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾√ 而其中向量Xi与Xj之间的马氏距离定义为 D(Xi,Xj)=(Xi−Xj)TS−1(Xi−Xj)‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾√ 若协方差矩阵是单位矩阵(各个样本向量之间独立同分布),则公式就成了: D(Xi,Xj)=(Xi−Xj)T(Xi−Xj)‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾√ 也就是欧氏距离了。 若协方差矩阵是对角矩阵,公式变成了标准化欧氏距离。 马氏距离的优缺点:量纲无关,排除变量之间的相关性的干扰。 夹角余弦 几何中夹角余弦可用来衡量两个向量方向的差异,机器学习中借用这一概念来衡量样本向量之间的差异。 二维空间向量的夹角余弦相似度 在二维空间中向量A(x1,y1)与向量B(x2,y2)的夹角余弦公式: cosθ=x1x2+y1y2x21+x22‾‾‾‾‾‾‾√y21+y22‾‾‾‾‾‾‾√ python 实现为 def twoCos(a,b): cos = (a[0]*b[0]+a[1]*b[1]) / (sqrt(a[0]**2 + b[0]**2) * sqrt(a[1]**2 + b[1]**2) ) return cos print 'a,b 二维夹角余弦距离:',twoCos((1,1),(2,2)) 多维空间向量的夹角余弦相似度 两个n维样本点a(x11,x12,…,x1n)和b(x21,x22,…,x2n)的夹角余弦 类似的,对于两个n维样本点a(x11,x12,…,x1n)和b(x21,x22,…,x2n),可以使用类似于夹角余弦的概念来衡量它们间的相似程度。 cosθ=a⋅b|a|∣∣b∣∣ 即: cosθ=∑nk=1x1kx2k∑nk=1x21k‾‾‾‾‾‾‾‾√∑nk=1x22k‾‾‾‾‾‾‾‾√ python实现为 def moreCos(a,b): sum_fenzi = 0.0 sum_fenmu_1,sum_fenmu_2 = 0,0 for i in range(len(a)): sum_fenzi += a[i]*b[i] sum_fenmu_1 += a[i]**2 sum_fenmu_2 += b[i]**2 return sum_fenzi/( sqrt(sum_fenmu_1) * sqrt(sum_fenmu_2) ) print 'a,b 多维夹角余弦距离:',moreCos((1,1,1,1),(2,2,2,2)) 夹角余弦取值范围为[-1,1]。夹角余弦越大表示两个向量的夹角越小,夹角余弦越小表示两向量的夹角越大。当两个向量的方向重合时夹角余弦取最大值1,当两个向量的方向完全相反夹角余弦取最小值-1。 闵可夫斯基距离 闵氏距离不是一种距离,而是一组距离的定义 定义 两个n维变量a(x11,x12,…,x1n)与 b(x21,x22,…,x2n)间的闵可夫斯基距离定义为: ∑k=1n∣∣x1k−x2k∣∣p‾‾‾‾‾‾‾‾‾‾‾‾‾‾⎷p 其中p是一个变参数。 当p=1时,就是曼哈顿距离 当p=2时,就是欧氏距离 当p→∞时,就是切比雪夫距离 根据变参数的不同,闵氏距离可以表示一类的距离。 闵氏距离的缺点 闵氏距离,包括曼哈顿距离、欧氏距离和切比雪夫距离都存在明显的缺点。 举个例子:二维样本(身高,体重),其中身高范围是150 ~ 190,体重范围是50 ~ 60,有三个样本:a(180,50),b(190,50),c(180,60)。那么a与b之间的闵氏距离(无论是曼哈顿距离、欧氏距离或切比雪夫距离)等于a与c之间的闵氏距离,但是身高的10cm真的等价于体重的10kg么?因此用闵氏距离来衡量这些样本间的相似度很有问题。 简单说来,闵氏距离的缺点主要有两个:(1)将各个分量的量纲(scale),也就是“单位”当作相同的看待了。(2)没有考虑各个分量的分布(期望,方差等)可能是不同的。 汉明距离 定义 两个等长字符串s1与s2之间的汉明距离定义为将其中一个变为另外一个所需要作的最小替换次数。例如字符串“1111”与“1001”之间的汉明距离为2。 应用:信息编码(为了增强容错性,应使得编码间的最小汉明距离尽可能大)。 python 实现 def hanmingDis(a,b): sumnum = 0 for i in range(len(a)): if a[i]!=b[i]: sumnum += 1 return sumnum print 'a,b 汉明距离:',hanmingDis((1,1,2,3),(2,2,1,3)) 杰卡德距离 & 杰卡德相似系数 杰卡德距离 与杰卡德相似系数相反的概念是杰卡德距离(Jaccard distance)。杰卡德距离可用如下公式表示: Jδ(A,B)=|A⋃B|−|A⋂B||A⋃B| 杰卡德距离用两个集合中不同元素占所有元素的比例来衡量两个集合的区分度。 python 实现 def jiekadeDis(a,b): set_a = set(a) set_b = set(b) dis = float(len( (set_a | set_b) - (set_a & set_b) ) )/ len(set_a | set_b) return dis print 'a,b 杰卡德距离:', jiekadeDis((1,2,3),(2,3,4)) 杰卡德相似系数 两个集合A和B的交集元素在A,B的并集中所占的比例,称为两个集合的杰卡德相似系数,用符号J(A,B)表示。 J(A,B)=|A⋂B||A⋃B| 杰卡德相似系数是衡量两个集合的相似度一种指标。 python 实现 def jiekadeXSDis(a,b): set_a = set(a) set_b = set(b) dis = float(len(set_a & set_b) )/ len(set_a | set_b) return dis print 'a,b 杰卡德相似系数:', jiekadeXSDis((1,2,3),(2,3,4)) 杰卡德相似系数与杰卡德距离的应用 可将杰卡德相似系数用在衡量样本的相似度上。 样本A与样本B是两个n维向量,而且所有维度的取值都是0或1。例如:A(0111)和B(1011)。我们将样本看成是一个集合,1表示集合包含该元素,0表示集合不包含该元素。 p :样本A与B都是1的维度的个数 q :样本A是1,样本B是0的维度的个数 r :样本A是0,样本B是1的维度的个数 s :样本A与B都是0的维度的个数 那么样本A与B的杰卡德相似系数可以表示为: 这里p+q+r可理解为A与B的并集的元素个数,而p是A与B的交集的元素个数。 而样本A与B的杰卡德距离表示为: J=pp+q+r 相关系数 & 相关距离 相关系数 ρXY=Cov(X,Y)D(X)‾‾‾‾‾√D(Y)‾‾‾‾‾√=E((X−EX)(Y−EY))D(X)‾‾‾‾‾√D(Y)‾‾‾‾‾√ 相关系数是衡量随机变量X与Y相关程度的一种方法,相关系数的取值范围是[-1,1]。相关系数的绝对值越大,则表明X与Y相关度越高。当X与Y线性相关时,相关系数取值为1(正线性相关)或-1(负线性相关)。 python 实现 相关系数可以利用numpy库中的corrcoef函数来计算 例如 对于矩阵a,numpy.corrcoef(a)可计算行与行之间的相关系数,numpy.corrcoef(a,rowvar=0)用于计算各列之间的相关系数,输出为相关系数矩阵。 from numpy import * a = array([[1, 1, 2, 2, 3], [2, 2, 3, 3, 5], [1, 4, 2, 2, 3]]) print corrcoef(a) >>array([[ 1. , 0.97590007, 0.10482848], [ 0.97590007, 1. , 0.17902872], [ 0.10482848, 0.17902872, 1. ]]) print corrcoef(a,rowvar=0) >>array([[ 1. , -0.18898224, 1. , 1. , 1. ], [-0.18898224, 1. , -0.18898224, -0.18898224, -0.18898224], [ 1. , -0.18898224, 1. , 1. , 1. ], [ 1. , -0.18898224, 1. , 1. , 1. ], [ 1. , -0.18898224, 1. , 1. , 1. ]]) 相关距离 Dxy=1−ρXY python 实现(基于相关系数) 同样针对矩阵a # 行之间的相关距离 ones(shape(corrcoef(a)),int) - corrcoef(a) >>array([[ 0. , 0.02409993, 0.89517152], [ 0.02409993, 0. , 0.82097128], [ 0.89517152, 0.82097128, 0. ]]) # 列之间的相关距离 ones(shape(corrcoef(a,rowvar = 0)),int) - corrcoef(a,rowvar = 0) >>array([[ 0. , 1.18898224, 0. , 0. , 0. ], [ 1.18898224, 0. , 1.18898224, 1.18898224, 1.18898224], [ 0. , 1.18898224, 0. , 0. , 0. ], [ 0. , 1.18898224, 0. , 0. , 0. ], [ 0. , 1.18898224, 0. , 0. , 0. ]]) 信息熵 信息熵并不属于一种相似性度量,是衡量分布的混乱程度或分散程度的一种度量。分布越分散(或者说分布越平均),信息熵就越大。分布越有序(或者说分布越集中),信息熵就越小。 计算给定的样本集X的信息熵的公式: Entropy(X)=∑i=1n−pilog2pi 参数的含义: n:样本集X的分类数 pi:X中第i类元素出现的概率 信息熵越大表明样本集S分类越分散,信息熵越小则表明样本集X分类越集中。。当S中n个分类出现的概率一样大时(都是1/n),信息熵取最大值log2(n)。当X只有一个分类时,信息熵取最小值0 python进行计算和实现可参考: http://blog.csdn.net/autoliuweijie/article/details/52244246
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 前言 该篇文章将是Scrapy爬虫系列的开篇,随后会不定时更新该框架方面的内容和知识,在scrapy之前写爬虫主要用的BeautifulSoup, request 和urllib,但随着使用程度的加深,慢慢意识到功能和效率都是不够的,那么便重新接触了Scrapy框架,并尝试着写出一些有趣的东西。 什么是Scrapy 它是一个应用程序框架,用来抓取并提取网站中的结构化数据,在数据挖掘,信息处理等方面十分有用。 安装 pip install Scrapy 它是一个python包,依赖于以下几个python包 lxml : XML和HTML解析 parsel :基于lxml的HTML/XML解析器 w3lib :处理网页编码和URL解析器 twisted :一个异步网络框架 cryptography and pyOpenSSL :处理各种网络级安全需求 创建一个简单的项目 scrapy startproject tiebaSpider 目录结构为 tiebaSpider/ scrapy.cfg # 配置文件 tiebaSpider/ # py 模块 __init__.py items.py # 定义一些数据模型文件 pipelines.py # 管道,用来进行数据的保存 settings.py # 关于项目的一些配置 spiders/ # 该目录下编写自己的代码逻辑 __init__.py 在soiders/下新建一个tibe.py文件,内容为: # coding: utf-8 import scrapy class TiebaSpider(scrapy.Spider): name = "tiebaSpider" def start_requests(self): urls = [ 'https://tieba.baidu.com/f?kw=%E6%88%92%E8%B5%8C&ie=utf-8&pn=0' ] for url in urls: yield scrapy.Request(url=url, callback=self.parse) def parse(self,response): page = response.url.split("pn=")[1] filename = "tieba-%s.html" % page with open(filename, 'wb') as f: f.write(response.body) self.log('Saved file %s' % filename) name: 爬虫名字 start_requests: 返回一个可迭代的请求地址 parse: 解析网页的函数 yield:python 生成器 运行爬虫: scrapy crawl tiebaSpider 这里我们也可以这样写 # coding: utf-8 import scrapy class TiebaSpider(scrapy.Spider): name = "tiebaSpider" start_urls = [ 'https://tieba.baidu.com/f?kw=%E6%88%92%E8%B5%8C&ie=utf-8&pn=0', ] def parse(self,response): page = response.url.split("pn=")[1] filename = "tieba-%s.html" % page with open(filename, 'wb') as f: f.write(response.body) self.log('Saved file %s' % filename) 因为parse是scrapy的默认回调函数,当然如果你要写好几自己定义的函数的话,就必须要指定自己的回调函数。 爬取贴吧用户 背景 爬取贴吧的尽可能多的用户名,作为推广使用(暂时使用以下的方法来获取用户名,后续会更新一种新的方法:对于给定的贴吧名,爬取其前三页所有帖子的发帖人和回帖人,这里只保存用户名) 分析 这里的实现思路是search 出来指定的贴吧,然后获取所有贴吧前三页的帖子 href,然后保存到文件中,再启用另外一个爬虫逐个帖子去爬取解析用户名 代码结构 tieba data tieba spiders __init__.py tieba1.py # 获取所有的帖子的链接 tieba2.py # 获取发帖人和回帖人的用户名 __init__.py items.py middlewares.py pipelines.py settings.py name.txt #存放贴吧名 scrapy.cfg 代码实现 tieba1.py # coding: utf-8 import scrapy import urllib import time class TiebaSpider(scrapy.Spider): name = 'tieba' def __init__(self): self.urls = [] # 加载贴吧名 fr = open("name.txt", "r") for one in fr.readlines(): for i in range(0, 3): self.urls.append('https://tieba.baidu.com/f?kw=' + urllib.quote(one.strip()) + '&ie=utf-8&pn=' + str(i * 50)) fr.close() def start_requests(self): urls = self.urls for url in urls: yield scrapy.Request(url=url, callback=self.parse) def parse(self, response): sel = scrapy.Selector(response) ahref_list = sel.xpath( '//a[re:test(@class, "j_th_tit ")]//@href').extract() fw = open("data/%s_all_href.txt" % time.strftime('%Y%m%d'), "a") for ahref in ahref_list: href = "https://tieba.baidu.com" + ahref fw.write(href + "\n") fw.close() tieba2.py # coding: utf-8 import scrapy import time from scrapy.http.request import Request from scrapy.http import HtmlResponse class TiebaSpider2(scrapy.Spider): name = 'tieba2' def __init__(self): self.urls = [] # 加载贴吧名 fr = open("data/%s_all_href.txt" % time.strftime('%Y%m%d'), "r") for one in fr.readlines(): self.urls.append(one.strip()) fr.close() def start_requests(self): urls = self.urls for one in urls: yield scrapy.Request(url=one, callback=self.parse) def parse_uname(self, response): # response = HtmlResponse(url=page_url.url) sel = scrapy.Selector(response) name_list = sel.xpath('//li[re:test(@class, "d_name")]//a/text()').extract() # print respons fw = open("data/%s_all_name.txt" % time.strftime('%Y%m%d'), "a") for name in list(set(name_list)): fw.write(name.encode("utf-8")) fw.write("\n") fw.close() def parse(self, response): sel = scrapy.Selector(response) # 可能有些帖子被删除 try: # 得到每个帖子有多少页 num = int(sel.xpath('//span[re:test(@class,"red")]//text()').extract()[1]) # 遍历每页获得用户名 for page_num in range(1, num + 1): one_url = response.url + "?pn=" + str(page_num) yield Request(url=one_url, callback=self.parse_uname) except Exception as e: pass 代码的github地址为:点击查看 总结 相比BeautifulSoup ,urllib,request来讲,Scrapy更加的快捷,高效和灵活,而且使用lxml,parsel 来进行网页内容的解析比BS也更快
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 前言 之前看过一段时间协同过滤的推荐算法,当时理解并不深刻,对于其浅显的理解是从海量数据中挖掘出小部分与你品味相同的用户,协同过滤分为基于用户的和基于物品的。 个性化推荐和非个性化推荐 聊个性化推荐之前,我们先理解下什么是非个性化推荐,我对其的理解为从比较单一的维度加上某个选项构成了推荐,比如说某个视频网站中娱乐类节目的观看量排名,某个图书网站中某段时间内的购买量排名等等,但是其有个缺点就是导致访问量高的物品,访问量会越来越高,因为当用户进入到推荐界面时看到的永远都是热销的,热卖的,访问量高的物品,那些排名靠后的物品只会越来越靠后。 为了解决这个问题,同时在海量数据中实现对用户的精准推荐便产生了个性化推荐,所谓的个性化推荐就是根据用户产生的行为或者再加上分析行为对象的内容属性向用户推荐其相似用户喜欢的,其喜欢物品相似的,其同一类别下的用户喜欢的物品。 基于物品和基于内容的推荐的区别 说到基于物品的推荐(Based-ItemCF,Based Item CollaborationFilte)我们很容易想到和他属于同一类别的Based-UserCF(基于用户的协同过滤推荐,Based User CollaborationFilte),Based-UserCF就是给某个用户推荐和他相似用户所喜欢的物品,其考虑的是用户之间的相似度和用户评分物品在用户喜欢物品中所占的权重,Based-UserF的计算公式为 其中,p(u,i)表示用户u对物品i的感兴趣程度,S(u,k)表示和用户u兴趣最接近的K个用户,N(i)表示对物品i有过行为的用户集合,Wuv表示用户u和用户v的兴趣相似度,Rvi表示用户v对物品i的兴趣(不过在大多数情况下,用户喜欢物品的权重都默认设置为1,即所有的Rvi都等于1,不过我们可以根据用户的浏览记录,评分高低来进行权重的计算)。 举个简单的例子,用户A对a,b,c,d,e的评分分别为1,2,3,4,5分,那么A用户对a产生的权重可以计算出来为:1/(1+2+3+4+5)=1/15,同理可得到用户A对b,c,d,e的产生的权重分别为2/15,3/15,4/15,5/15。 所有的推荐系统都是根据分析用户的行为来进行推荐的,这里的基于item进行的协同过滤推荐只不过是通过用户对物品的行为,来计算物品之间的相似度,继而求出用户对物品的兴趣度(这里说的兴趣度是用户对推荐给他的物品的可能喜欢程度),而基于内容的推荐是分析每个item 内容之间的相似程度,来计算物品的之间的相似度,比如说现在有A,B两部电影,A电影打的tag是,爱情,青春,积极,向上,B电影打的tag是爱情,青春,乐观,积极,那么这两部电影在很大程度上是相似的。 简单粗暴的Slope One 对于协同过滤或者基于内容的推荐算法而言,存在的问题是当数据量过大的时候,计算用户或者物品的相似度是很浪费时间的,当用户的评分矩阵是一个稀疏矩阵时,计算用户的相似度或者物品的相似度意义也不大,所以这时候Slope One就要发挥其伟大的价值了。 slope one 原理 其原理可以概括为一句话为:用平均值来代替某两个未知个体之间的打分差异 用户 对事物A打分 对事物B打分 对事物C打分 X 3 4 6 Y 2 4 7 Z 4 ? ? 例如:用户X,Y,Z三个用户对A,B物品的评分如上表,那么Z用户对B的评分可能为多少呢? 根据平均值原理计算得Z对B的评分为:((3-4)+(2-4))/2=-1.5, 也就是说对B的打分要比A高1.5分,所以Z对B的打分为 4+1.5 = 5.5分。 同理可计算出用户Z对C的打分为:4+((7-2)+(6-3))/2 = 8分 加权算法 但通常情况下,数据量和数据规整程度往往是不一的,比如说有n个人对事物A和事物B打分了,R(A->B)表示这n个人对A和对B打分的平均差(A-B),有m个人对事物B和事物C打分了,R(B->C)表示这m个人对B和对C打分的平均差(B-C),注意都是平均差而不是平方差,现在某个用户对A的打分是Ra,对C的打分是Rc,那么A对B的打分可能是: Rb = (n * (Ra - R(A->B)) + m * (Rc - R(B->C)))/(m+n) 问题 数据稀疏性 简单填值 用一个固定的数值填充系统中所有的未评过分的项目,从而解决稀疏性问题一种方法,常用固定值的选取方法有两种: 1:缺省值可以设为评分的平均值,或者对前两者进行某种合成。 该方法在一定程度上能缓解数据稀疏性问题,但在用户和项目数量很大的情况下填充所有缺省值,完成推荐的计算量也比较大,因此适合于小规模数据库。另外用户对未评过分的项目评分情况会有一些差异,该方法采用统一的数值进行填充,没有考虑到用户的兴趣差异,抹杀了用户的个性。 2:众数法。 众数法就是将目标用户所有评分的众数作为新项目预测评分的方法。从统计学的角度来说,采用众数法这种预测方法的准确率会比较高,但是在实际生活中,采用众数法预测用户对项目的评分可能会是错误的。另外,用户对项目的评分可能会存在多个众数或者没有众数的情况,因此众数法的应用局限性比较大。 聚类 该方法根据用户兴趣之间的差异,利用某种聚类算法将系统中的所有用户划分为不同的群体;系统把用户所在群体的中心值作为用户对未评项目的评分预测值,进行用户项目评分矩阵的填充。主要的聚类方法有k-means聚类和遗传聚类等。聚类方法针对的对象可以是用户,也可以是项目,还可以对用户和项目均进行聚类。其中,对用户进行聚类时首先要对用户-项目评分数据库进行聚类,然后选择目标用户所属类的用户作为最近邻集合;对项目进行聚类时根据用户对项目评分的相似性对项目进行k-means聚类生成相应聚类中心,在此基础上计算目标项目与聚类中心的相似性,从而只需在与目标项目最相似的若干个聚类中就能寻找到目标项目的最近邻,并能够产生推荐列表;对用户和项目均进行聚类的方法主要有层次聚类、biclustering聚类和co-clustering聚类等。聚类的方法利用相似群体的评分信息提高了预测的准确度,但不能体现用户间的爱好区别,因此推荐结果的准确率并没有得到显著提高。 降维 用户-项目评分矩阵出现数据稀疏的情况是由项目的高维数据引起的,因此可以考虑采取一定措施降低项目数据的维度,进而达到约减数据的目的。目前常见的降维技术主要有简单降维方法、矩阵分解和主成分分析(PCA)三类。 1:简单降维方法 简单的降维方法就是通过设置限制条件删除一些用户和项目,从而降低用户-评分矩阵的维度。被删除的往往是没有参加过评分活动或者是评分次数很少的用户,或者是没有被用户评价过或者是被评价的次数很少的项目。利用该方法可以在一定程度上降低评分矩阵的维度,但是无法对被删除的用户或者项目进行推荐,这就导致了用户流失和信息隐藏的问题。 2:矩阵分解 矩阵分解的最简单方法是单值分解算法。用该方法分解用户-项目评分矩阵,可以约减评分矩阵中的数据。但是该算法在分解矩阵的过程中会造成数据遗失,影响准确率。 奇异值分解(SVD)是一种矩阵分解的有效方式,该技术在计算机科学、统计学等领域有着广泛应用。SVD可以将高度相关且在一起出现的内容作为单独因子,把通常很大的矩阵向量拆解成更小阶的近似矩阵。奇异值分解能够应用于协同过滤算法解决数据稀疏性问题,主要原因是协同过滤中用户对项目评分是因为用户对这些项目的隐含特性比较感兴趣,而这些项目之间也存在着一些共同的特征。用户喜欢某一项目的表现为用户对这些项目的评分比较高,所以通过将用户的评分用线性代数方法分解为一些特征,可以根据用户对这些特征的喜好程度来预测用户对他所没有评过分的项目的喜好。 3:主成分分析 主成分分析是基于矩阵特征值分解计算的标准统计分析方法。该方法式将原来的变量重新组合成一组新的互相无关的综合变量,同时根据实际需要可以从中取出几个能够尽可能多地反映原来信息综合变量作为新的参考信息。经过PCA处理后,原始评分数据被投射到最相关的主特征向量上,从而能够约减数据集。 降维技术虽然在一定程度上能够降低用户-项目评分矩阵的规模和稀疏程度,但采用该技术也流失了一部分用户对项目的评分数据。C C Aggarwak指出降维技术产生的效果与数据集密切相关,在项目空间维度很高的情况下进行降维,效果往往难以得到保证。 结合内容的过滤 协同过滤利用的信息只是用户评分数据,基于内容的过滤可以具体显示用户的描述信息,因此将这两种方式融合在一起可以增加可利用的数据量。具体的融合方式有以下四种 综合考虑协同过滤和基于内容的过滤的推荐结果,采用一定方式将两种结果融合在一起。 将协同过滤和基于内容的过滤集成到一个统一的模型。 将协同过滤的部分功能集成到基于内容的过滤。 将基于内容过滤的部分功能集成到协同过滤。 冷启动 引导用户将自己的兴趣点表达出来 比如说当我们在注册一个文字阅读类APP时,他会让你选择自己喜欢的标签,这样当你第一次进入系统时,便可以根据你选择的标签进行推荐 利用其他平台的数据进行冷启动 将平台的注册路径改为用新浪/QQ/微信等社交平台登录,一方面可以降低用户注册成本提高转化率,一方面可以同时获得用户的社交信息,从而获得推荐系统的冷启动数据。举个大家都应该知道的产品——“今日头条”,号称5秒钟知道你的兴趣偏好,其实也是在用户登录新浪等社交平台后,获取用户的关注列表,以及爬取用户最近参与互动的feed(转发/评论/赞)进行语义分析,从而获取用户的偏好。 热门推荐 这个是最笨的方法 附录文档 基于用户兴趣分类的协同过滤推荐 基于用户和Item的协同过滤算法的分析与实现 基于标签的用户推荐系统 基于图的推荐算法
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 数据在远程传输过程中,可能被劫持和获取,继而造成的后果是数据信息泄露,那么如何有效的进行数据传输呢,在ELK Stack中filebeat 提供了这样的一个解决方案,其不仅可以监听指定文件夹的数据,还可以对数据进行TLS 双向认证加密,从而保证数据传输过程中的可靠性。 环境说明 Ubuntu 16.04 ELK 5.2.1 (elk在192.168.1.127上) Filebeat 5.2.1 (fb在192.168.1.128上) Filebeat区别不同的日志源 filebeat.yaml 文件配置如下: filebeat.prospectors: # Each - is a prospector. Most options can be set at the prospector level, so # you can use different prospectors for various configurations. - input_type: log paths: - /home/thinkgamer/test/*.log document_type: test_log - input_type: log paths: - /var/log/*.log document_type: auth_log 这里得document_type 就是对应filter中对应的log的type,在logstash的解析文件中可以这样写 input { beats { port => 5044 } } filter { if [type] == 'auth_log'{ } else if [type] == 'test_log'{ } } output { elasticsearch { hosts => ["http://localhost:9200"] index =>"%{type}-%{+YYYY.MM.dd}" } } Filebeat传输数据加密 上边基本环境里介绍到logstash和filebeat的ip地址分别为127和128 上,分别在两个机器上生成证书 logstash(127 机器): cd ~ mkdir -p pki/tls/certs mkdir -p pki/tls/private openssl req -subj '/CN=192.168.1.127/' -x509 -days $((100 * 365)) -batch -nodes -newkey rsa:2048 -keyout pki/tls/private/logstash.key -out pki/tls/certs/logstash.crt 配置logstash的input为如下形式: input { beats { port => 5044 ssl => true ssl_certificate_authorities => ["/home/thinkgamer/pki/tls/certs/filebeat.crt"] ssl_certificate => "/home/thinkgamer/pki/tls/certs/logstash.crt" ssl_key => "/home/thinkgamer/pki/tls/private/logstash.key" ssl_verify_mode => "force_peer" } } ssl:true 表示开启 Logstash 的 SSL/TLS 安全通信功能; ssl_certificate_authorities:配置 Logstash 使其信任所有由该 CA 证书发行的证书; ssl_certificate & ssl_key:Logstash server 证书和 key 文件。Logstash 使用它们向 Filebeat client 证明自己的可信身份; ssl_verify_mode:表明 Logstash server 是否验证 Filebeat client 证书。有效值有 peer 或 force_peer。如果是 force_peer,表示如果 Filebeat 没有提供证书,Logstash server 就会关闭连接。 Filebeat(128机器): cd ~ mkdir -p pki/tls/certs mkdir -p pki/tls/private openssl req -subj '/CN=192.168.1.128/' -x509 -days $((100 * 365)) -batch -nodes -newkey rsa:2048 -keyout pki/tls/private/filebeat.key -out pki/tls/certs/filebeat.crt filebeat.yaml 配置文件中修改output logstash: output.logstash: # The Logstash hosts hosts: ["192.168.1.127:5044"] # Optional SSL. By default is off. # List of root certificates for HTTPS server verifications ssl.certificate_authorities: ["/home/master/pki/tls/certs/logstash.crt"] # Certificate for SSL client authentication ssl.certificate: "/home/master/pki/tls/certs/filebeat.crt" # Client Certificate Key ssl.key: "/home/master/pki/tls/private/filebeat.key" certificate_authorities:CA 证书,即用来签署证书的证书。这里表示配置 Filebeat 使其信任所有由该 CA 证书发行的证书。因为自签名证书的发行者和证书主体相同,所以这里直接使用 Logstash 证书使 Filebeat 信任使用该证书的 Logstash server; certificate & certificate_key:Filebeat 证书和 key 文件。Filebeat 将使用它们向 Logstash server 证明自己的可信身份。 至此配置over了,在两个机器上分别启动logstash和filebeat,报错如下: cannot validate certificate for 10.10.11.109 because it doesn't contain any IP SANs 解决办法参考下边《遇到的问题和解决办法》 遇到的问题和解决办法 在配置好证书之后启动filebeat和logstash,filebeat并不能将数据传输给logstash,报出了下边的错误 cannot validate certificate for 10.10.11.109 because it doesn't contain any IP SANs 解决办法是: 删除之前产生的证书,对openssl进行重新配置 sudo vim /etc/ssl/openssl.cnf 找到 [ v3_ca ] 加入下边一行: subjectAltName = IP:这里写ip地址 比如我的两个机器便为: subjectAltName = IP:192.168.1.127 subjectAltName = IP:192.168.1.128 重新生成证书,进行copy 配置即可 加密多个日志源问题 在生产环境中有多个日志源这是个很常见的问题,那么如果我们需要对多个日志源的数据进行加密传输时该怎么办呢? 答案是肯定得,这里有多种解决办法 1:每个filebeat分别发送到logstash监听的不同端口,比如说Filebeat A 发送到logstash的 5044端口,Filebeat B发送到logstash的5045 端口,这样做肯定是没有任何问题的 input 的输入是类似于下面这样的 input { beats { port => 5044 } beats { port => 5044 } } 2:每个filebeat所在的机器都发送到logstash 的5044 端口,这样做在没有加密保护的情况下也是可以的(已经尝试过了),那如果是在加密的情况下该怎么做呢?是否能成功呢? 回答:当然是可以的,用同样的方法在另外一台安装filebeat的机器上生成证书,和logstash所在的机器进行证书的互相copy和配置,logstash的机器的input差不多应该是下面的这种 input { beats { port => 5044 ssl => true ssl_certificate_authorities => ["/home/thinkgamer/pki/tls/certs/filebeat.crt","/home/thinkgamer/pki/tls/certs/filebeat1.crt"] ssl_certificate => "/home/thinkgamer/pki/tls/certs/logstash.crt" ssl_key => "/home/thinkgamer/pki/tls/private/logstash.key" ssl_verify_mode => "force_peer" } } 引深应用 以上仅仅是对数据从发源地到logstash收集过程进行解释,但是filebeat进行数据传输加密也可以应用别的方面,比如说A公司的两个数据点之间(B,C)要进行数据的传递,这个时候便可以使用filebeat了,在B和C地点分别生成证书和配置,在C地点可以使用logstash的output将传输过来的数据输出到指定文件夹中即可。
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 前言 这个问题是我在做这个项目【点击查看】时遇到的,主要是因为以前在使用django的models时,在models的str(self) 函数时,默认返回的字段都是CharField类型的,而在这次返回了一个IntegerField类型导致出现了题目中的错误。 python的Model._unicode_()和Model._str_() __unicode__() unicode() 方法在每当你对一个对象调用unicode() 时调用。Django 在许多地方都使用unicode(obj)(或者相关的函数 str(obj))。最明显的是在Django 的Admin 站点显示一个对象和在模板中插入对象的值的时候。所以,你应该始终让unicode() 方法返回模型的一个友好的、人类可读的形式。 from django.db import models class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) def __unicode__(self): return u'%s %s' % (self.first_name, self.last_name) 如果你定义了模型的unicode() 方法且没有定义str() 方法,Django 将自动提供一个 str(),它调用unicode() 并转换结果为一个UTF-8 编码的字符串。下面是一个建议的开发实践:只定义unicode() 并让Django 在需要时负责字符串的转换。 __str__() str() 方法在每当你对一个对象调用str() 时调用。在Python 3 中,Django 在许多地方使用str(obj)。 最明显的是在Django 的Admin 站点显示一个对象和在模板中插入对象的值的时候。 所以,你应该始终让str() 方法返回模型的一个友好的、人类可读的形式。 例如: from django.db import models class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) def __str__(self): return '%s %s' % (self.first_name, self.last_name) 在Python 2 中,Django 内部对str 的直接使用主要在随处可见的模型的repr() 输出中(例如,调试时的输出)。如果已经有合适的unicode() 方法就不需要str() 了。 前面unicode() 的示例可以使用str() 这样类似地编写: from django.db import models from django.utils.encoding import force_bytes class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) def __str__(self): # Note use of django.utils.encoding.force_bytes() here because # first_name and last_name will be unicode strings. return force_bytes('%s %s' % (self.first_name, self.last_name)) 发现问题和解决问题 遇到这个问题是因为在__str__() return 了 IntegerField 类型的数据 TypeError: __str__ returned non-string (type int) 解决办法: return 的时候 加一个 str() 进行强制类型转换即可 Over !
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 grok 作为 Logstash 最广为人知的插件,在性能和资源损耗方面同样也广为诟病。为了应对这个情况,同时也考虑到大多数时候,日志格式并没有那么复杂,Logstash 开发团队在 5.0 版新添加了另一个解析字段的插件:dissect。当日志格式有比较简明的分隔标志位,而且重复性较大的时候,我们可以使用 dissect 插件更快的完成解析工作。 安装和example 安装 使用之前首先检查自己的logstash plugin中是否包含,如果未包含,请先安装 检查所有插件 /your/logstash/path/bin/logstash-plugin list 安装logstash-filter-dissect /your/logstash/path/bin/logstash-plugin install logstash-filter-dissect example filter { dissect { mapping => { "message" => "%{ts} %{+ts} %{+ts} %{src} %{} %{prog}[%{pid}]: %{msg}" } convert_datatype => { pid => "int" } } } 语法解释: 我们看到上面使用了和 Grok 很类似的 %{} 语法来表示字段,这显然是基于习惯延续的考虑。不过示例中 %{+ts} 的加号就不一般了。dissect 除了字段外面的字符串定位功能以外,还通过几个特殊符号来处理字段提取的规则: ● %{+key} 这个 + 表示,前面已经捕获到一个 key 字段了,而这次捕获的内容,自动添补到之前 key 字段内容的后面。 ● %{+key/2} 这个 /2 表示,在有多次捕获内容都填到 key 字段里的时候,拼接字符串的顺序谁前谁后。/2 表示排第 2 位。 ● %{?string} 这个 ? 表示,这块只是一个占位,并不会实际生成捕获字段存到 Event 里面。 ● %{?string} %{&string} 当同样捕获名称都是 string,但是一个 ? 一个 & 的时候,表示这是一个键值对。 比如对 http://rizhiyi.com/index.do?id=123 写这么一段配置: http://%{domain}/%{?url}?%{?arg1}=%{&arg1} 则最终生成的 Event 内容是这样的: { domain => "rizhiyi.com", id => "123" } 该插件的一些配置 该插件支持下边这几种配置,所有的配置都包括在 dissect{ }中。 Setting Input Type Required Default Value add_field hash No {} add_tag array No [] convert_datatype hash No {} enable_metric boolean No true id string No mapping hash No {} periodic_flush boolean No false remove_field array No [] remove_tag array No [] tag_on_failure arrray No [“_dissectfailure”] 1:add_field 如果此过滤器解析成功,便可以为该事件添加任何字段,字段名称可以是动态的,并使用%{field}包括事件部分 eg: input{ stdin{ type => "dissect" } } filter { dissect { add_field => { "from_%{host}" => "Hello world, now is %{@timestamp}" "new_field" => "new_static_value" } } } output{ stdout{ codec => rubydebug } elasticsearch{ hosts => ["http://localhost:9200"] index => "logstash-%{type}" } } 执行 sudo /usr/share/logstash/bin/logstash -f test.conf 输入hello test 显示: 17:08:26.925 [Api Webserver] INFO logstash.agent - Successfully started Logstash API endpoint {:port=>9600} hello test { "@timestamp" => 2017-03-28T09:08:29.814Z, "new_field" => "new_static_value", "@version" => "1", "host" => "thinkgamer", "message" => "hello test", "type" => "dissect", "from_thinkgamer" => "Hello world, now is 2017-03-28T09:08:29.814Z" } 所以说这里的%{somefield} 就是已经存在的字段名称 2:add_tag 替换上边程序的filter为: filter { dissect { add_tag => ["foo_%{host}","taggedy_tag" ] } } 运行输入 hello test 显示: 17:12:42.307 [Api Webserver] INFO logstash.agent - Successfully started Logstash API endpoint {:port=>9600} hello test { "@timestamp" => 2017-03-28T09:13:05.728Z, "@version" => "1", "host" => "thinkgamer", "message" => "hello test", "type" => "dissect", "tags" => [ [0] "foo_thinkgamer", [1] "taggedy_tag" ] } 3:convert_datatype 使用此设置,可以指定int和float数据类型转换,如果在elasticsearch中没有使用mapping的话,这将是很有用的 filter { dissect { convert_datatype{ cpu => "float" code => "int" } } } 4:enable_metric 默认情况下我们可以记录所有的指标,但是你可以通过该项配置来禁止或者启动是否记录这些指标 5:id 向插件实例添加唯一ID,此ID用于跟踪插件特定配置的信息。 6:mapping field=>value 可以对当前解析的值进行以后的解析 eg: filter { dissect { mapping => { "message" => "%{field1} %{field2} %{description}" "description" => "%{field3} %{field4} %{field5}" } } } 7:periodic_flush 定期调用过滤器刷新方法,可选 8:remove_field eg: filter { dissect { add_field => { "from_%{host}" => "Hello world, now is %{@timestamp}" "new_field" => "new_static_value" } remove_field => ["from_%{host}"] } } 输入hello test 显示: 18:04:09.028 [Api Webserver] INFO logstash.agent - Successfully started Logstash API endpoint {:port=>9600} hello test { "@timestamp" => 2017-03-28T10:04:36.910Z, "new_field" => "new_static_value", "@version" => "1", "host" => "thinkgamer", "message" => "hello test", "type" => "dissect" } 9:remove_tag 删除标签,可以是动态的 eg: filter { dissect { add_tag => ["foo_%{host}","taggedy_tag" ] remove_tag => ["foo_%{host}"] } } 输入hello test 显示: 18:06:10.097 [Api Webserver] INFO logstash.agent - Successfully started Logstash API endpoint {:port=>9600} hello test { "@timestamp" => 2017-03-28T10:06:35.259Z, "@version" => "1", "host" => "thinkgamer", "message" => "hello test", "type" => "dissect", "tags" => [ [0] "taggedy_tag" ] } 10:tag_on_failure 当解析失败时,将值附加到标签字段 Over !
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 Logstash在ELK这个技术栈中占据着重要的位置,所有的数据都要经过logstash的解析,才能格式化的存入ES中,那么对于Logstash的学习也是十分重要的,今天这篇文章我们将看一下logstash的基本知识,后续会有更多细节性的文章和使用案例文章,请持续关注博主和ELK Stack 从入门到放弃专栏。 环境准备 以下在ubuntu16.04上进行 1:Java sudo apt install openjdk-8-jdk 2:logstash安装 直接在官网下载最新的安装包:https://www.elastic.co/downloads/logstash deb包安装(ubuntu):sudo dpkg -i logstash-5.2.2.deb rpm包(Centos):sudo rpm -ivh logstash-5.2.2.deb tar.gz包后者zip包:解压到指定的目录即可 Hello World 我的环境是Ubuntu16.04 / java 1.8 ELK版本均为5.2.1 sudo /usr/share/logstash/bin/logstash -e ‘input{stdin{}}output{stdout{codec=>rubydebug}}’ 启动之后随便输入字符串即可看到: thinkgamer@thinkgamer:/usr/share/logstash$ sudo /usr/share/logstash/bin/logstash -e 'input{stdin{}}output{stdout{codec=>rubydebug}}' [sudo] thinkgamer 的密码: WARNING: Could not find logstash.yml which is typically located in $LS_HOME/config or /etc/logstash. You can specify the path using --path.settings. Continuing using the defaults Could not find log4j2 configuration at path /usr/share/logstash/config/log4j2.properties. Using default config which logs to console 17:34:42.881 [LogStash::Runner] INFO logstash.agent - No persistent UUID file found. Generating new UUID {:uuid=>"04b030e9-d4ba-4414-b2ad-42df3a714a81", :path=>"/usr/share/logstash/data/uuid"} The stdin plugin is now waiting for input: 17:34:43.775 [[main]-pipeline-manager] INFO logstash.pipeline - Starting pipeline {"id"=>"main", "pipeline.workers"=>4, "pipeline.batch.size"=>125, "pipeline.batch.delay"=>5, "pipeline.max_inflight"=>500} 17:34:43.864 [[main]-pipeline-manager] INFO logstash.pipeline - Pipeline main started 17:34:44.040 [Api Webserver] INFO logstash.agent - Successfully started Logstash API endpoint {:port=>9600} hello world { "@timestamp" => 2017-03-27T09:35:46.959Z, "@version" => "1", "host" => "thinkgamer", "message" => "hello world" } Logstash语法简介 logstash设计了自己的DSL,包括区域,注释,数据类型,条件判断和字段引用 区域(section): Logstash 用 {} 来定义区域。区域内可以包括插件区域定义,你可以在一个区域内定义多个插件。插件区域内则可以定义键值对设置。示例如下: input { stdin {} syslog {} } 数据类型: logstasj支持少量的数据类型 bool :debug => true string: host => “hostname” number: port => 5044 array: match => [“datetime” , “unitime”] hash: options => { key1 => “value1”, key2 => “value2” } 字段引用(field reference): logstash字段引用语法。要在 Logstash 配置中使用字段的值,只需要把字段的名字写在中括号 [] 里就行了,这就叫字段引用。还需注意字段层次。如果引用的是一个顶级字段,可以省略[],直接指定字段名。要引用嵌套的字段,需要指定完整的路径,如[top-level field][nested field]。 下面有五个顶级字段(agent, ip, request, response, ua) 和三个嵌套字段 (status, bytes, os)。 { "agent": "Mozilla/5.0 (compatible; MSIE 9.0)", "ip": "192.168.24.44", "request": "/index.html" "response": { "status": 200, "bytes": 52353 }, "ua": { "os": "<a href="http://www.ttlsa.com/windows/" title="windows"target="_blank">Windows</a> 7" } } 为了引用os字段,需指定[ua][os]。引用顶级字段如request,可以简单指定request即可。 字段引用格式也可以用于logstash调用sprintf格式。这种格式可以从其他字符串中引用字段值。如: output { statsd { increment => "apache.%{[response][status]}" } } 也可以格式化时间,如 output { file { path => "/var/log/%{type}.%{+yyyy.MM.dd.HH}" } } 条件判断 表达式支持下面这些操作符: ● ==(等于), !=(不等于), <(小于), >(大于), <=(小于等于), >=(大于等于) ● =~(匹配正则), !~(不匹配正则) ● in(包含), not in(不包含) ● and(与), or(或), nand(非与), xor(非或) ● ()(复合表达式), !()(对复合表达式结果取反) 命令行参数 -e : 即我们看到的hello world的测试 -f/–config : 意即文件。真实运用中,我们会写很长的配置,甚至可能超过 shell 所能支持的 1024 个字符长度。所以我们必把配置固化到文件里,然后通过 bin/logstash -f agent.conf 这样的形式来运行。 此外,logstash 还提供一个方便我们规划和书写配置的小功能。你可以直接用 bin/logstash -f /etc/logstash.d/ 来运行。logstash 会自动读取 /etc/logstash.d/ 目录下所有 *.conf 的文本文件,然后在自己内存里拼接成一个完整的大配置文件,再去执行。 注意: logstash 列出目录下所有文件时,是字母排序的。而 logstash 配置段的 filter 和 output 都是顺序执行,所以顺序非常重要。采用多文件管理的用户,推荐采用数字编号方式命名配置文件,同时在配置中,严谨采用 -t/–configtest : 测试,用来测试logstash读取的conf文件是否能够正确的解析 -l/–log : 默认输出日志到标准错误,在生产环境中你可以通过 >bin/logstash -l logs/logstash.log 命令来统一存储日志 -w/–pipeline-workers : 允许filter和output的pipeline线程数量,默认是CPU核数 -b/–pipeline-batch-size : 每个 Logstash pipeline 线程,在执行具体的 filter 和 output 函数之前,最多能累积的日志条数。默认是 125 条。越大性能越好,同样也会消耗越多的 JVM 内存。 -u/–pipeline-batch-delay : 每个 Logstash pipeline 线程,在打包批量日志的时候,最多等待几毫秒。默认是 5 ms。 -P/–pluginpath : 可以写自己的插件,然后用 bin/logstash –pluginpath /path/to/own/plugins 加载它们 –verbose : 输出一定的调试日志 –debug: 输出更多的调试日志,在logstash 5.0 开始,提供了logstash.yaml配置文件,可以将命令行的所有参数都通过YAML文件方式设置 input/filter/output 在hello world中已经见识了默认的logstash运行流程和配置的基础语法,配置文件中主要包括三部分,input,filter,output,下边将会分别来看这三部分 输入插件input: Logstash 使用一个名叫 FileWatch 的 Ruby Gem 库来监听文件变化。这个库支持 glob 展开文件路径,而且会记录一个叫 .sincedb 的数据库文件来跟踪被监听的日志文件的当前读取位置。所以,不要担心 logstash 会漏过你的数据。sincedb 文件中记录了每个被监听的文件的 inode, major number, minor number 和 pos。 读取文件(File) input { file { path => ["/var/log/*.log", "/var/log/message"] type => "system" start_position => "beginning" } } 这里有一些配置: 有一些比较有用的配置项,可以用来指定 FileWatch 库的行为: ● discover_interval logstash 每隔多久去检查一次被监听的 path 下是否有新文件。默认值是 15 秒。 ● exclude 不想被监听的文件可以排除出去,这里跟 path 一样支持 glob 展开。 ● close_older 一个已经监听中的文件,如果超过这个值的时间内没有更新内容,就关闭监听它的文件句柄。默认是 3600 秒,即一小时。 ● ignore_older 在每次检查文件列表的时候,如果一个文件的最后修改时间超过这个值,就忽略这个文件。默认是 86400 秒,即一天。 ● sincedb_path 如果你不想用默认的 $HOME/.sincedb(Windows 平台上在 C:\Windows\System32\config\systemprofile.sincedb),可以通过这个配置定义 sincedb 文件到其他位置。 ● sincedb_write_interval logstash 每隔多久写一次 sincedb 文件,默认是 15 秒。 ● stat_interval logstash 每隔多久检查一次被监听文件状态(是否有更新),默认是 1 秒。 ● start_position logstash 从什么位置开始读取文件数据,默认是结束位置,也就是说 logstash 进程会以类似 tail -F 的形式运行。如果你是要导入原有数据,把这个设定改成 “beginning”,logstash 进程就从头开始读取,类似 less +F 的形式运行。 标准输入(stdin) stdin应该是logstash最简单最基础的插件了 eg: input { stdin { add_field => {"key" => "value"} codec => "plain" tags => ["add"] type => "std" } } 解释: type 和 tags 是 logstash 事件中两个特殊的字段。通常来说我们会在输入区段中通过 type 来标记事件类型 —— 我们肯定是提前能知道这个事件属于什么类型的。而 tags 则是在数据处理过程中,由具体的插件来添加或者删除的。 最常见的用户应该是这样的 input { stdin { type => "web" tags => ["add1","add2"] } } filter { if [type] == "web" { } } output { if "_grokparsefailure" in [tags] { } else { } } 读取Syslog数据 syslog会帮助你很容易的手收集设备上的数据,而且配置也很简单,http://blog.csdn.net/gamer_gyt/article/details/54025857 这篇文章中对linux的syslog配置介绍的比较清晰,那么如何在logstash中配置呢? eg: input { syslog { port => "514" } } Logstash 是用 UDPSocket, TCPServer 和 LogStash::Filters::Grok 来实现 LogStash::Inputs::Syslog的。所以你其实可以直接用 logstash 配置实现一样的效果 input { tcp { port => "8514" } udp { port => "8515" } } 建议在使用 LogStash::Inputs::Syslog 的时候走 TCP 协议来传输数据,因为具体实现中,UDP 监听器只用了一个线程,而 TCP 监听器会在接收每个连接的时候都启动新的线程来处理后续步骤。 注意:虽然 LogStash::Inputs::TCP 用 Ruby 的 Socket 和 OpenSSL 库实现了高级的 SSL 功能,但 Logstash 本身只能在 SizedQueue 中缓存 20 个事件。所以建议在生产环境中换用其他消息队列。 编码插件codec介绍 Logstash 不只是一个input | filter | output 的数据流,而是一个 input | decode | filter | encode | output 的数据流!codec 就是用来 decode、encode 事件的。codec 的引入,使得 logstash 可以更好更方便的与其他有自定义数据格式的运维产品共存,比如 graphite、fluent、netflow、collectd,以及使用 msgpack、json、edn 等通用数据格式的其他产品等。 json:用来解析输入数据是json格式的 multiline:用来解析一个事件可能是多行的内容 过滤器插件 filter filter主要使用来解析message的,在filter中所有的DSL是按顺序执行的,这里边的东西太多,主要包括grok 解析,数据修改等,这个在稍后的文章中会做详细解释 输出插件 output 1:输出到elasticsearch eg: output { elasticsearch { hosts => ["192.168.0.2:9200"] index => "logstash-%{type}-%{+YYYY.MM.dd}" document_type => "%{type}" flush_size => 20000 idle_flush_time => 10 sniffing => true template_overwrite => true } } 解释: 批量发送 在过去的版本中,主要由本插件的 flush_size 和 idle_flush_time 两个参数共同控制 Logstash 向 Elasticsearch 发送批量数据的行为。以上面示例来说:Logstash 会努力攒到 20000 条数据一次性发送出去,但是如果 10 秒钟内也没攒够 20000 条,Logstash 还是会以当前攒到的数据量发一次。默认情况下,flush_size 是 500 条,idle_flush_time 是 1 秒。这也是很多人改大了 flush_size 也没能提高写入 ES 性能的原因——Logstash 还是 1 秒钟发送一次。从 5.0 开始,这个行为有了另一个前提:flush_size 的大小不能超过 Logstash 运行时的命令行参数设置的 batch_size,否则将以 batch_size 为批量发送的大小。 索引名 写入的 ES 索引的名称,这里可以使用变量。为了更贴合日志场景,Logstash 提供了 %{+YYYY.MM.dd} 这种写法。在语法解析的时候,看到以 + 号开头的,就会自动认为后面是时间格式,尝试用时间格式来解析后续字符串。所以,之前处理过程中不要给自定义字段取个加号开头的名字…… 此外,注意索引名中不能有大写字母,否则 ES 在日志中会报 InvalidIndexNameException,但是 Logstash 不会报错,这个错误比较隐晦,也容易掉进这个坑中。 轮询 Logstash 1.4.2 在 transport 和 http 协议的情况下是固定连接指定 host 发送数据。从 1.5.0 开始,host 可以设置数组,它会从节点列表中选取不同的节点发送数据,达到 Round-Robin 负载均衡的效果。 2:stdout 输出 eg: output { stdout { codec => rubydebug workers => 2 } } 解释: 输出插件统一具有一个参数是 workers。Logstash 为输出做了多线程的准备。 其次是 codec 设置。codec 的作用在之前已经讲过。可能除了 codecs/multiline ,其他 codec 插件本身并没有太多的设置项。所以一般省略掉后面的配置区段。换句话说。上面配置示例的完全写法应该是: output { stdout { codec => rubydebug { } workers => 2 } } 单就 outputs/stdout 插件来说,其最重要和常见的用途就是调试。所以在不太有效的时候,加上命令行参数 -vv 运行,查看更多详细调试信息。 3:输出到hdfs 具体可参照这个 https://github.com/dstore-dbap/logstash-output-webhdfs-discontinued 4:输出到文件中 通过日志收集系统将分散在数百台服务器上的数据集中存储在某中心服务器上,这是运维最原始的需求。早年的 scribed ,甚至直接就把输出的语法命名为 。Logstash 当然也能做到这点。 和 LogStash::Inputs::File 不同, LogStash::Outputs::File 里可以使用 sprintf format 格式来自动定义输出到带日期命名的路径。 eg: output { file { path => "/path/to/%{+yyyy}/%{+MM}/%{+dd}/%{host}.log.gz" message_format => "%{message}" gzip => true } } 解释: 使用 output/file 插件首先需要注意的就是 message_format 参数。插件默认是输出整个 event 的 JSON 形式数据的。这可能跟大多数情况下使用者的期望不符。大家可能只是希望按照日志的原始格式保存就好了。所以需要定义为 %{message},当然,前提是在之前的 filter 插件中,你没有使用 remove_field 或者 update 等参数删除或修改 %{message} 字段的内容。 另一个非常有用的参数是 gzip。gzip 格式是一个非常奇特而友好的格式。其格式包括有: ● 10字节的头,包含幻数、版本号以及时间戳 ● 可选的扩展头,如原文件名 ● 文件体,包括DEFLATE压缩的数据 ● 8字节的尾注,包括CRC-32校验和以及未压缩的原始数据长度 Over!
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 一路走来,DJango也用了挺久了,自己也做了一些基于Django的小项目,具体可看github,但是Django默认的admin后台编辑文本框实在是太丑了,而且单一,其实在很久之前就想写这篇文章了,但是由于种种原因拖延到了现在,终于下定了决心来写,现在时间是00:17。 KindEditor介绍 官网地址是:http://kindeditor.net 目前最新版本是4.1.1,我现在做这个东西是用python3.5 ,django 1.10.4 ,三者之间完全兼容 看一张效果图吧: 使用过程 1:下载该文件,解压至相应的js文件目录 例如我的目录是: 这里我删除了多余的文件,因为是python项目,其他的都用不到,所以选择删除,节省空间 2:settings.py和urls.py配置 在项目的settings.py 中设置MEDIA_ROOT 目录 eg: #文件上传配置 MEDIA_ROOT = os.path.join(BASE_DIR,’uploads’) 项目的根urls.py 配置 eg: url(r'^admin/uploads/(?P<dir_name>[^/]+)$', upload_image, name='upload_image'), url(r"^uploads/(?P<path>.*)$", views.static.serve, {"document_root": settings.MEDIA_ROOT, }), 3:编写upload.py文件 该文件存放于项目的根目录同名文件夹下,eg: upload.py:主要是对上传的图片做一些限制 # -*- coding: utf-8 -*- from django.http import HttpResponse from django.conf import settings from django.views.decorators.csrf import csrf_exempt import os import uuid import json import datetime as dt @csrf_exempt def upload_image(request, dir_name): ################## # kindeditor图片上传返回数据格式说明: # {"error": 1, "message": "出错信息"} # {"error": 0, "url": "图片地址"} ################## result = {"error": 1, "message": "上传出错"} files = request.FILES.get("imgFile", None) if files: result =image_upload(files, dir_name) return HttpResponse(json.dumps(result), content_type="application/json") #目录创建 def upload_generation_dir(dir_name): today = dt.datetime.today() dir_name = dir_name + '/%d/%d/' %(today.year,today.month) if not os.path.exists(settings.MEDIA_ROOT): os.makedirs(settings.MEDIA_ROOT) return dir_name # 图片上传 def image_upload(files, dir_name): #允许上传文件类型 allow_suffix =['jpg', 'png', 'jpeg', 'gif', 'bmp'] file_suffix = files.name.split(".")[-1] if file_suffix not in allow_suffix: return {"error": 1, "message": "图片格式不正确"} relative_path_file = upload_generation_dir(dir_name) path=os.path.join(settings.MEDIA_ROOT, relative_path_file) if not os.path.exists(path): #如果目录不存在创建目录 os.makedirs(path) file_name=str(uuid.uuid1())+"."+file_suffix path_file=os.path.join(path, file_name) file_url = settings.MEDIA_URL + relative_path_file + file_name open(path_file, 'wb').write(files.file.read()) return {"error": 0, "url": file_url} 4:config.js 配置 该配置文件主要是对django admin后台作用的,比如说我们现在有一个news的app,我们需要对该模块下的 news类的content加上富文本编辑器,这里需要做两步 第一:在news 的admin.py中加入 class Media: js = ( '/static/js/kindeditor-4.1.10/kindeditor-min.js', '/static/js/kindeditor-4.1.10/lang/zh_CN.js', '/static/js/kindeditor-4.1.10/config.js', ) 第二:config.js 中配置 上边说了我们是要对news的content加上富文本编辑器,那么我们首先要定位到该文本框的name属性,鼠标右键查看源代码,如下红线标示: config.js 中加入: //news KindEditor.ready(function(K) { K.create('textarea[name="new_content"]', { width : "800px", height : "500px", uploadJson: '/admin/uploads/kindeditor', }); }); 至此,已经完成,如果你还有什么不懂的,可以加我关注我的微信公众号,进行提问,谢谢! Over!
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 ES5.2.1 集群部署参考:http://blog.csdn.net/gamer_gyt/article/details/59077189 对于集群的监控和优化是很重要的一部分,如果想持久维护集群,单单靠增加物理内存,cpu,硬盘是不够的,必须通过一些方法来进行优化。 集群结点角色分配 在之前得文章中我们介绍到es集群中得三个角色 master ,data,client master结点:node.master: true node.data: false 该node服务器只作为一个主节点,但不存储任何索引数据,该node服务器将使用自身空闲得资源,来协调各种创建索引请求或者查询请求,将这些请求合理分发到相关得node服务器上。 data结点:node.master: false node.data: true 该node服务器只作为一个数据节点,只用于存储索引数据。使该node服务器功能 单一,只用于数据存储和数据查询,降低其资源消耗率。 client结点(负载均衡结点):node.master: false node.data: false 该node服务器即不会被选作主节点,也不会存储任何索引数据。该服务器主要用 于查询负载均衡。在查询的时候,通常会涉及到从多个node服务器上查询数据,并请 求分发到多个指定的node服务器,并对各个node服务器返回的结果进行一个汇总处理, 最终返回给客户端。 1:关闭data结点得http功能 针对ElasticSearch集群中的所有数据节点,不用开启http服务。将其中的配置 参数这样设置:http.enabled: false,同时也不要安装head, bigdesk, marvel等监控 插件,这样保证data节点服务器只需处理创建/更新/删除/查询索引数据等操作。 http功能可以在非数据节点服务器上开启,上述相关的监控插件也安装到这些服 务器上,用于监控ElasticSearch集群状态等数据信息。这样做一来出于数据安全考虑,二来出于服务性能考虑。 2:一台服务器上最好只部署一个node 一台物理服务器上可以启动多个Node服务器节点(通过设置不同的启动port),但一台服务器上的CPU,内存,硬盘等资源毕竟有限,从服务器性能考虑,不建议一台服务器上启动多个node节点。 在大规模局点,比如100个点,可以专门配备3个Master,可使用3台具有内存的刀片即可,即参数配置为node.master: true,node.data: false;可以按比例配备数据汇聚节点,比如10个,即参数配置为node.master: false ,node.data: false;小规模节点,可以不用如此设置,当然如果依然有性能问题,也是一个优化的措施 集群得机器内存设置 Elasticsearch 默认采用的是Lucene,至于为什么es采用这个,原因可能是因为Lucene是一个成熟的、高性能的、可扩展的、轻量级的,而且功能强大的搜索引擎包。Lucene的核心jar包只有一个文件,而且不依赖任何第三方jar包。更重要的是,它提供的索引数据和检索数据的功能开箱即用。当然,Lucene也提供了多语言支持,具有拼写检查、高亮等功能。当然es使用Lucene作为分词搜索包,势必会造成很大程度上的内存消耗。 1:预留一半内存给Lucene使用 一个常见的问题是配置堆太大。你有一个64 GB的机器,觉得JVM内存越大越好,想给Elasticsearch所有64 GB的内存。 当然,内存对于Elasticsearch来说绝对是重要的,用于更多的内存数据提供更快的操作。而且还有一个内存消耗大户-Lucene Lucene的设计目的是把底层OS里的数据缓存到内存中。Lucene的段是分别存储到单个文件中的,这些文件都是不会变化的,所以很利于缓存,同时操作系统也会把这些段文件缓存起来,以便更快的访问。 Lucene的性能取决于和OS的交互,如果你把所有的内存都分配给Elasticsearch,不留一点给Lucene,那你的全文检索性能会很差的。 最后标准的建议是把50%的内存给elasticsearch,剩下的50%也不会没有用处的,Lucene会很快吞噬剩下的这部分内存。 2:32GB限制 在java中,所有的对象都分配在堆上,然后有一个指针引用它。指向这些对象的指针大小通常是CPU的字长的大小,不是32bit就是64bit,这取决于你的处理器,指针指向了你的值的精确位置。 对于32位系统,你的内存最大可使用4G。对于64系统可以使用更大的内存。但是64位的指针意味着更大的浪费,因为你的指针本身大了。浪费内存不算,更糟糕的是,更大的指针在主内存和缓存器(例如LLC, L1等)之间移动数据的时候,会占用更多的带宽。 java 使用一个叫内存指针压缩的技术来解决这个问题。它的指针不再表示对象在内存中的精确位置,而是表示偏移量。这意味着32位的指针可以引用40亿个对象,而不是40亿个字节。最终,也就是说堆内存长到32G的物理内存,也可以用32bit的指针表示。 一旦你越过那个神奇的30-32G的边界,指针就会切回普通对象的指针,每个对象的指针都变长了,就会使用更多的CPU内存带宽,也就是说你实际上失去了更多的内存。事实上当内存到达40-50GB的时候,有效内存才相当于使用内存对象指针压缩技术时候的32G内存。 这段描述的意思就是说:即便你有足够的内存,也尽量不要超过32G,因为它浪费了内存,降低了CPU的性能,还要让GC应对大内存。 3:机器内存大于64GB 你可以考虑一台机器上创建两个或者更多ES节点,而不要部署一个使用32+GB内存的节点。仍然要 坚持50%原则,假设 你有个机器有128G内存,你可以创建两个node,使用32G内存。也就是说64G内存给ES的堆内存,剩下的64G给Lucene。 如果你选择第二种,你需要配置 cluster.routing.allocation.same_shard.host:true 这会防止同一个shard的主副本存在同一个物理机上(因为如果存在一个机器上,副本的高可用性就没有了) 4:ES集群的heap参数优化 所谓的heap即数据缓存的内存大小,ES集群中消耗内存的有以下几个: 1):segment Memory Lucene 把每次生成的倒排索引,叫做一个段(segment)。然后另外使用一个 commit 文件,记录索引内所有的 segment。而生成 segment 的数据来源,则是内存中的 buffer。由于词典的size会很大,全部装载到heap里不现实,因此Lucene为词典做了一层前缀索引(Term Index),这个索引在Lucene4.0以后采用的数据结构是FST (Finite State Transducer)。这种数据结构占用空间很小,Lucene打开索引的时候将其全量装载到内存中,加快磁盘上词典查询速度的同时减少随机磁盘访问次数。所以ES的data node存储数据并非只是耗费磁盘空间的,为了加速数据的访问,每个segment都有会一些索引数据驻留在heap里。因此segment越多,瓜分掉的heap也越多,并且这部分heap是无法被GC掉的! 理解这点对于监控和管理集群容量很重要,当一个node的segment memory占用过多的时候,就需要考虑删除、归档数据,或者扩容了。 2):Filter Cache Filter cache是用来缓存使用过的filter的结果集的,需要注意的是这个缓存也是常驻heap,无法GC的。默认的10% heap size设置工作得够好了,如果实际使用中heap没什么压力的情况下,才考虑加大这个设置。 3):Field Data cache 对搜索结果做排序或者聚合操作,需要将倒排索引里的数据进行解析,然后进行一次倒排。在有大量排序、数据聚合的应用场景,可以说field data cache是性能和稳定性的杀手。这个过程非常耗费时间,因此ES2.0以前的版本主要依赖这个cache缓存已经计算过的数据,提升性能。但是由于heap空间有限,当遇到用户对海量数据做计算的时候,就很容易导致heap吃紧,集群频繁GC,根本无法完成计算过程。ES2.0以后,正式默认启用Doc Values特性(1.x需要手动更改mapping开启),将field data在indexing time构建在磁盘上,经过一系列优化,可以达到比之前采用field data cache机制更好的性能。因此需要限制对field data cache的使用,最好是完全不用,可以极大释放heap压力。这里需要注意的是,排序、聚合字段必须为not analyzed。设想如果有一个字段是analyzed过的,排序的实际对象其实是词典,在数据量很大情况下这种情况非常致命。 4):Bulk Queue Bulk Queue是做什么用的?当所有的bulk thread都在忙,无法响应新的bulk request的时候,将request在内存里排列起来,然后慢慢清掉。一般来说,Bulk queue不会消耗很多的heap,但是见过一些用户为了提高bulk的速度,客户端设置了很大的并发量,并且将bulk Queue设置到不可思议的大,比如好几千。这在应对短暂的请求爆发的时候有用,但是如果集群本身索引速度一直跟不上,设置的好几千的queue都满了会是什么状况呢? 取决于一个bulk的数据量大小,乘上queue的大小,heap很有可能就不够用,内存溢出了。一般来说官方默认的thread pool设置已经能很好的工作了,建议不要随意去“调优”相关的设置,很多时候都是适得其反的效果。 5):Indexing Buffer Indexing Buffer是用来缓存新数据,当其满了或者refresh/flush interval到了,就会以segment file的形式写入到磁盘。这个参数的默认值是10% heap size。根据经验,这个默认值也能够很好的工作,应对很大的索引吞吐量。但有些用户认为这个buffer越大吞吐量越高,因此见过有用户将其设置为40%的。到了极端的情况,写入速度很高的时候,40%都被占用,导致OOM。 6):Cluster State Buffer ES被设计成每个Node都可以响应用户的api请求,因此每个Node的内存里都包含有一份集群状态的拷贝。这个Cluster state包含诸如集群有多少个Node,多少个index,每个index的mapping是什么?有少shard,每个shard的分配情况等等(ES有各类stats api获取这类数据)。在一个规模很大的集群,这个状态信息可能会非常大的,耗用的内存空间就不可忽视了。并且在ES2.0之前的版本,state的更新是由Master Node做完以后全量散播到其他结点的。频繁的状态更新都有可能给heap带来压力。在超大规模集群的情况下,可以考虑分集群并通过tribe node连接做到对用户api的透明,这样可以保证每个集群里的state信息不会膨胀得过大。 7):超大搜索聚合结果集的fetch ES是分布式搜索引擎,搜索和聚合计算除了在各个data node并行计算以外,还需要将结果返回给汇总节点进行汇总和排序后再返回。无论是搜索,还是聚合,如果返回结果的size设置过大,都会给heap造成很大的压力,特别是数据汇聚节点。 5:优化建议: 一般分配主机1/4-1/2的内存 编辑:elasticsearch/bin/ elasticsearch 加上(10g换成你自己设置的内存数): ES_MIN_MEM=10g ES_MAX_MEM=10g ES_HEAP_NEWSIZE=1g 集群的硬盘和CPU设置 1:硬盘选型: 硬盘对集群非常重要,特别是建索引多的情况。磁盘是一个服务器最慢的系统,对于写比较重的集群,磁盘很容易成为集群的瓶颈。如果可以承担的器SSD盘,最好使用SSD盘。如果使用SSD,最好调整I/O调度算法。RAID0是加快速度的不错方法。 2:自动调整存储带宽 在2.0.0之前,elasticsearch会限制合并速度(merges),默认为20MB/sec。但是这个速率经常是显得太小,导致合并速度落后于索引速度,进而限制了索引速度。 现在Elasticsearch2.0.0之后,使用了自动调整合并IO速度方式:如果合并落于索引速度,合并IO速度会逐渐增大,并且随着合并的持续进行会减小。在索引吞吐量小的时候,即使突然来了一个大的合并任务,这种情况也不会吞噬整个节点可用的IO,极小化的降低对正在进行的查询和索引的影响。 但是对索引请求大的情况下,允许的合并速度会自动调整到跟上索引的速度。有了2.0.0这个特性,意味着我们不需要管任何的限制值了,只要用默认的就好了。 3:多个path.data 路径 如果磁盘空间和IO性能是Elasticsearch的瓶颈的话,使用多个IO设备(通过设置多个path.data路径)存储shards,能够增加总的存储空间和提升IO性能。 在Elasticsearch2.0之前的版本,也是配置多个path.data路径,但是其相当于RAID 0,每个shards的数据会分布在所有的磁盘上。当一个节点上有一块盘坏了的情况下,该节点上所有的shards都会损坏了。需要恢复该节点上的所有shards。 在2.0.0版本,把这个实现改成了:每个shards所有的数据只会在一块磁盘上面。这样即使一个节点的一块磁盘损坏了,也只是损失了该磁盘上的shards,其它磁盘上的shards安然无事。只需要恢复该块盘上的shards即可。 升级到2.0.0版本时,旧版本一个shard分布到所有磁盘上的数据,会拷贝到一块盘上。 对应这个改变,在设计shards时,如果一个节点有10块磁盘,共3个节点,则shards至少30个,才能分布在30块盘上(即最大限度使用磁盘空间)。 集群的分片和副本配置 分片(Shard) 一个索引会分成多个分片存储,分片数量在索引建立后不可更改 分片数是与检索速度非常相关的的指标,如果分片数过少或过多都会导致检索比较慢。分片数过多会导致检索时打开比较多的文件别外也会导致多台服务器之间通讯。而分片数过少会导致单个分片索引过大,所以检索速度慢。基于索引分片数=数据总量/单分片数的计算公式,在确定分片数之前需要进行单服务单索引单分片的测试,目前我们测试的结果单个分片的内容为10G。 副本(replicas) 每个索引的数据备份数量。 ElasticSearch在创建索引数据时,最好指定相关的shards数量和replicas, 否则会使用服务器中的默认配置参数shards=5,replicas=1。 因为这两个属性的设置直接影响集群中索引和搜索操作的执行。假设你有足够的机器来持有碎片和副本,那么可以按如下规则设置这两个值: 1) 拥有更多的碎片可以提升索引执行能力,并允许通过机器分发一个大型的索引; 2) 拥有更多的副本能够提升搜索执行能力以及集群能力。 对于一个索引来说,number_of_shards只能设置一次,而number_of_replicas可以使用索引更新设置API在任何时候被增加或者减少。 这两个配置参数在配置文件的配置如下: index.number_of_shards: 5 number_of_replicas: 1 Elastic官方文档建议:一个Node中一个索引最好不要多于三个shards.配置total_shards_per_node参数,限制每个index每个节点最多分配多少个发片. 集群的优化总结 1:java jdk版本尽量高一点,否则容易出现bug 2:es集群结点规划好,master,client,data node 分开,关闭data node 的http功能 3:合理利用内存 4:根据机器数,磁盘数,索引大小等硬件环境,根据测试结果,设置最优的分片数和备份数,单个分片最好不超过10GB,定期删除不用的索引,做好冷数据的迁移。 5:保守配置内存限制参数,尽量使用doc value存储以减少内存消耗,查询时限制size、from参数。 6:结合实际场景,做好集群监控
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 一直以来对Django的用户权限登录保护模棱两可,最近由于在做一个django的项目,其中涉及到用户的权限登录保护,所以算是有些清楚了,总结下来,给还在模棱两可的你阅读。 附上我最近一直在commit的github地址:https://github.com/Thinkgamer/CSMarket 环境说明 Django:1.10 Python:3.5 用户登录验证 1:前提说明 以下所谈到的用户登录验证给予扩展Django 内置的User模型,以AbstractUser方式扩展User模型,Django中的用户扩展的两种方式可参考:http://blog.csdn.net/gamer_gyt/article/details/50499653 以下代码为上边给的github地址中的实例,抽取其中用户验证为例。 2:以AbstractUser方式扩展内置User models.py: from django.db import models from django.contrib.auth.models import AbstractUser # Create your models here. class User(AbstractUser): # 手机号 user_phone = models.CharField(blank=True, verbose_name='电话', max_length=11) #判断是否是认证通过的用户 user_isValid=models.BooleanField(blank=True,default=False) def __unicode__(self): return self.user.username admin.py: from django.contrib import admin from logre.models import User admin.site.register(User) 3:authenticate 进行用户验证 提供了用户认证,即验证用户名以及密码是否正确。一般需要username password两个关键字参数。 如果认证信息有效,会返回一个 User 对象。authenticate()会在User 对象上设置一个属性标识那种认证后端认证了该用户,且该信息在后面的登录过程中是需要的。当我们试图登陆一个从数据库中直接取出来不经过authenticate()的User对象会报错的!! user = authentica(username=’someone’,password=’somepassword’) 那么用户登录函数该怎么写呢? views.py #用户登录 @csrf_exempt def login(request): if request.method=='POST': uname=request.POST.get('username') pwd=request.POST.get('passwd') user = authenticate(username=uname,password=pwd) if user is not None: auth_login(request, user) # 更新最后登录时间 now_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) user.last_login=now_time user.save() return HttpResponseRedirect(referer) else: return render_to_response('login.html',{ 'error': '邮箱或者密码不正确', 'user_name': uname, 'user_pwd': pwd, }) else: return render_to_response('login.html',{}) 注意事项:这里的authentica函数只能接受username和password,因为我在测试email的时候不好使(如果恰巧你看到了这里,并且验证可以用email和password验证的话,请留言) AbstractUser扩展的User模型,在数据库中密码存储形式是经过转化的,所以我们并不能直接操作密码,幸好django提供了专门的函数来修改密码。 set_password 函数 eg:(由于我项目还没更新到这一步,所以这里就举这样一个简单的例子) >>> from django.contrib.auth.models import User >>> u = User.objects.get(username='john') >>> u.set_password('new password') >>> u.save() 4:Permission Django的auth系统提供了模型级的权限控制, 即可以检查用户是否对某个数据表拥有增(add), 改(change), 删(delete)权限。 auth系统无法提供对象级的权限控制, 即检查用户是否对数据表中某条记录拥有增改删的权限。如果需要对象级权限控制可以使用django-guardian。 假设在博客系统中有一张article数据表管理博文, auth可以检查某个用户是否拥有对所有博文的管理权限, 但无法检查用户对某一篇博文是否拥有管理权限 user.has_perm方法用于检查用户是否拥有操作某个模型的权限 eg: user.has_perm('blog.add_article') user.has_perm('blog.change_article') user.has_perm('blog.delete_article') 如果要添加权限的话,我们可以在后台管理中自己添加,当然也可以使用以下方法添加 user.user_permissions.add() 删除权限: user.user_permissions.delete() 清空权限 user.user_permissions.clear() 用户拥有他所在用户组的权限, 使用用户组管理权限是一个更方便的方法。Group中包含多对多字段permissions, 在数据库中由auth_group_permissions数据表维护。 添加权限 group.permissions.add(permission) 删除权限 group.permissions.delete(permission) 清空权限 group.permissions.clear() 当然我们也可以自定义权限,可参考之前文章中的内容:http://blog.csdn.net/gamer_gyt/article/details/51023560 用户登录和注销 1:Http请求和回应 参考:http://djangobook.py3k.cn/appendixH/‘>HTTP请求(Request)和回应(Response)对象 2:用户登录 该函数接受一个HttpRequest对象,以及一个认证了的User对象 此函数使用django的session框架给某个已认证的用户附加上session id等信息。 其实在我们上边的views.py函数中也有看到就是 auth_login(request,user) 前提条件是扩展django的User用户模型,使用时需要导入django的login from django.contrib.auth import login as auth_login PS:我这里as为auth_login是因为我的views函数为login,否则会重名,会出现错误 使用: uname=request.POST.get('username') pwd=request.POST.get('passwd') user = authenticate(username=uname,password=pwd) if user is not None: auth_login(request, user) 3:用户注销 logout:该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错 from django.contrib.auth import logout as auth_logout def logout(request): auth_logout(request) return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) 4:登录之后保持会话状态 在其登录之后会将用户信息保存在request中,那么我们我进行一个views 函数前,可以先判断下request.user是否存在,如存在将数据传递给返回前端,如果不存在我们也可以添加一些其他的逻辑。 def all(request): if request.user.is_authenticated: user_name=request.user else: user_name='' return render(request, 'news.html', { 'user_name': user_name, }) 5:如何记录登录前的来源页面 这里我们使用的是: request.META[‘HTTP_REFERER’] 大概实现的思路是:在login函数内声明一个全局变量referer,在不是发生post请求的时候记录下来源页面赋值给referer,在是发生post请求后返回到原页面。代码如下: #用户登录 @csrf_exempt def login(request): global referer if request.method=='POST': ... return HttpResponseRedirect(referer) else: try: referer = request.META['HTTP_REFERER'] # 获取网页访问来源 except: pass finally: return render_to_response('login.html',{}) 总结 其实Django本身的用户验证系统和登录注销功能是十分强大的,在开发过程中如果能利用好这些技术点,必然会节约很多我们的开发时间。 附本人的Django专栏:Django从零开始到项目优化
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前面的话 Express 是一种保持最低程度规模的灵活 Node.js Web 应用程序框架,为 Web 和移动应用程序提供一组强大的功能。 环境说明 Ubuntu 16.04 安装express 假设已经安装了nodejs mkdir myapp cd myapp 实用npm init 命令为应用程序创建package.json文件 关于package.json的更多信息参考:https://docs.npmjs.com/files/package.json npm init 输入相应的信息 安装express框架,如果要将express保存到依赖库中后面跟–save,否则不跟 npm install express –save 这里解释下这个 –save,就是把安装的express 和对应的版本号写进packages.json文件中,如果事先在package.json中写入了依赖,那么直接执行npm install即可 在此基础上我们来创建我们的HelloWorld 创建app.js,添加以下内容 var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Hello World!'); }); app.listen(3000, function () { console.log('Example app listening on port 3000!'); }); 执行node app.js 报错 Cannot find module ‘express’ 分析:在安装express时已经指定了 –save,我也尝试了 -g ,但是依旧还是这个问题,后来查资料发现说是 -g安装的位置(用户目录)与node安装路径的位置不在同一个目录导致的,需要配置以下NODE_PATH,ok,我们来配置下环境变量 vim ~/.bashrc 加入(/home/master/.nvm/versions/node/v6.9.5/bin 是我node的位置) #nodejs export NODE_PATH=/home/master/.nvm/versions/node/v6.9.5/bin export PATH=$PATH:$NODE_PATH 此时再执行node app.js ,浏览器访问 localhost:3000 express应用程序生成器 除了手动创建程序的各个文件,我们也可以使用express应用程序生成器生成整个框架 1 . 安装 npm install express-generator -g 可以使用-h来查看帮助 master@ubuntu:~/Desktop$ express -h Usage: express [options] [dir] Options: -h, --help output usage information --version output the version number -e, --ejs add ejs engine support --pug add pug engine support --hbs add handlebars engine support -H, --hogan add hogan.js engine support -v, --view <engine> add view <engine> support (ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade) -c, --css <engine> add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css) --git add .gitignore -f, --force force on non-empty directory 2 . 创建myapp的应用程序 express –view=pug myapp master@ubuntu:~/Desktop$ express --view=pug myapp create : myapp create : myapp/package.json create : myapp/app.js create : myapp/public create : myapp/public/javascripts create : myapp/public/images create : myapp/public/stylesheets create : myapp/public/stylesheets/style.css create : myapp/routes create : myapp/routes/index.js create : myapp/routes/users.js create : myapp/views create : myapp/views/index.pug create : myapp/views/layout.pug create : myapp/views/error.pug create : myapp/bin create : myapp/bin/www install dependencies: $ cd myapp && npm install run the app: $ DEBUG=myapp:* npm start 3 . 安装依赖 cd myapp npm install 4 . 启动服务 npm start 浏览器访问 http://localhost:3000/ Express 的基本路由 路由由于确定应用程序如何响应对特定端点的请求机会,包含一个URL(或路径)和一个特定的请求方法(GET,POST等)。每个路由都有一个或多个处理程序函数,这些函数在路由匹配时执行。 路由定义采用以下结构: app.METHOD(PATH, HANDLER) 其中: app 是espress的实例 METHOD是HTTP请求方法 PATH是服务器上的路径 HANDLER是在路由匹配时执行的函数 路由方法派生自HTTP方法之一,以下代码是为访问应用程序根目录的GET和POST方法定义的路由示例 // GET method route app.get('/', function (req, res) { res.send('GET request to the homepage'); }); // POST method route app.post('/', function (req, res) { res.send('POST request to the homepage'); }); Express 支持对英语HTTP方法的以下路由方法:get,post,put,head,delete,options,trace,copy,lock,mkcol,move,purge,propfind,proppatch,unlock,report,mkactivity,ceckout,merge,msearch,notify,subscribe,unsubscribe,patch,connect。 有一种特殊的路由方法:app.all() ,他并非派生自HTTP方法,该方法用于在所有请求方法的路径中装入中间件函数。在以下示例中,无论使用GET。POST,PUT,DELETE还是http模块中支持的其他任何HTTP请求方法,都将为针对 “/secret” 的请求执行处理程序。 app.all('/',function(req,res,next){ console.log('Accessing the secret section...'); next(); //pass control to the next handle }); 路由路径 路由路径与请求方法相结合,用于定义可以在其中提出请求的端点。路由路径可以是字符串,字符串模式或正则表达式。以下是基于字符串路由的一些示例: // 此路由路径将请求与根路由 / 匹配。 app.get('/',function(req,res){ res.send('root'); }); //此路由路径将请求与 /about 匹配。 app.get('about',function(req,res){ res.send('about'); }); //此路由路径将请求与 /random.text 匹配。 app.get('/random.text',function(req,res){ res.send('random.text'); }); //以下是基于字符串模式的路由路径的一些示例。 //此路由路径将匹配 acd 和 abcd。 app.get('/ab?cd',function(req,res){ res.send('ab?cd') }); //此路由路径将匹配 abcd、abbcd、abbbcd 等。 app.get('/ab+cd',function(req,res){ res.send('ab+cd'); }); //此路由路径将匹配 abcd、abxcd、abRABDOMcd、ab123cd 等。 app.get('ab*cd',function(req,res){ res.send('ab*cd'); }); //此路由路径将匹配 /abe 和 /abcde。 app.get('/ab(cd)?e',function(req,res){ res.send('/a/'); }); //基于正则表达式的路由路径的示例: //此路由路径将匹配名称中具有“a”的所有路由。 app.get(/a/,function(req,res){ res.send('/a/'); }); // 此路由路径将匹配 butterfly 和 dragonfly,但是不匹配 butterflyman、dragonfly man 等。 app.get(/.*fly$/,function(req,res){ res.send('/.*fly$/'); }); 路由处理程序 你可以提供多个回调函数,以类似与中间件的行为方式来处理请求,唯一例外是这些回调函数可能调用 next(‘route’) 来绕过省区的路由回调,你可以使用此机制对路由施加先决条件,在灭有路由继续执行当前路由的情况下,可将控制权传递给后续路由。路由处理程序的形式可以是一个函数,一组函数,或者两者的结合,如下示例所示: //单个回调函数可以处理一个路由。例如: app.get('/example/a',function(req,res){ res.send('Hello from A'); }); //多个回调函数可以处理一个路由(确保您指定 next 对象)。例如: app.get('/example/b', function(req,res,next){ console.log('the respnse will be sent by the next function'); next(); },function(req,res){ res.send('Hello from B'); }); //一组回调函数可以处理一个路由。例如: var cb0 = function(req,res,next){ console.log('CB0'); next(); }; var cb1 = function(req,res,next){ console.log('CB1'); next(); }; var cb2 = function(res,res){ res.send('Hello from C'); }; app.get('/example/c',[cb0,cb1,cb2]); //独立函数与一组函数的组合可以处理一个路由。例如: var ab1 = function(req,res,next){ console.log('AB1'); next(); }; var ab2 = function(req,res,next){ console.log('AB2'); next(); } app.get('/example/d',[ab1,ab2],function(req,res,next){ console.log('the response will be sent by the next function ...'); next(); },function(req,res){ res.send('Hello from D'); }); 响应方法 下表中响应独享(res)的方法可以向客户机发送响应,并终止请求/响应循环。如果没有从路由处理程序调用其中任何方法,客户机请求将保持挂起状态。 方法 描述 res.download() 提示将要下载文件。 res.end() 结束响应进程。 res.json() 发送 JSON 响应。 res.jsonp() 在 JSONP 的支持下发送 JSON 响应。 res.redirect() 重定向请求。 res.render() 呈现视图模板。 res.send() 发送各种类型的响应。 res.sendFile 以八位元流形式发送文件。 res.sendStatus() 设置响应状态码并以响应主体形式发送其字符串表示 app.route() 你可以使用app.route() 为路由路径创建可链接的路由处理程序,因为在单一位置指定路径,所以可以减少冗余和输入错误,有关路由的更多信息,请参阅:http://expressjs.com/zh-cn/4x/api.html#router //以下是使用 app.route() 定义的链式路由处理程序的示例。 app.route('/book') .get(function(req,res){ res.send('Get a random book'); }) .post(function(req,res){ res.send('add a book'); }) .put(function(req,res){ res.send('update the book'); }); express.Router 使用express.Router类来创建可安装的模块化路由处理程序,Router实例是完整的中间件和路由系统,因此,常常将其称为” 微型应用程序”。以下示例将路由器创建为模块,在其中装入中间件,定义一些路由,然后安装在主应用程序的路径中。 在应用程序中创建birds.js文件,写入以下内容: var express = require('express') var router = express.Router(); // middleware that is specific to this router router.use(function(req,res,next){ console.log('Time:' ,Date.now()); next(); }); // define the home page route router.get('/',function(req,res){ res.send('Birds home page'); }); // define the about route router.get('/about',function(req,res){ res.send('About birds'); }) module.exports = router 在app.js中引用 var birds = require('./birds'); var express = require('express'); var app = express(); app.listen(3000, function () { console.log('Example app listening on port 3000!'); }); app.use('/birds',birds); 此应用程序现在可针对 /birds 和 /birds/about 的请求,调用特定于此路由的 timeLog 中间价函数。 在Express中提供静态文件 为了提供诸如图像、CSS 文件和 JavaScript 文件之类的静态文件,请使用 Express 中的 express.static 内置中间件函数。 将包含静态资源的目录的名称传递给 express.static 中间件函数,以便开始直接提供这些文件。例如,使用以下代码在名为 public 的目录中提供图像、CSS 文件和 JavaScript 文件: app.use(express.static(‘public’)); 现在便可以引用public目录下的文件 http://localhost:3000/images/kitten.jpg http://localhost:3000/css/style.css http://localhost:3000/js/app.js http://localhost:3000/images/bg.png http://localhost:3000/hello.html Express 相对于静态目录查找文件,因此静态目录的名称不是此 URL 的一部分。 要使用多个静态资源目录,请多次调用 express.static 中间件函数: app.use(express.static(‘public’)); app.use(express.static(‘files’)); Express 以您使用 express.static 中间件函数设置静态目录的顺序来查找文件。 要为 express.static 函数提供的文件创建虚拟路径前缀(路径并不实际存在于文件系统中),请为静态目录指定安装路径,如下所示: app.use(‘/static’, express.static(‘public’)); 现在,可以装入具有 /static 路径前缀的 public 目录中的文件。 http://localhost:3000/static/images/kitten.jpg http://localhost:3000/static/css/style.css http://localhost:3000/static/js/app.js http://localhost:3000/static/images/bg.png http://localhost:3000/static/hello.html 然而,向 express.static 函数提供的路径相对于您在其中启动 node 进程的目录。如果从另一个目录运行 Express 应用程序,那么对于提供资源的目录使用绝对路径会更安全: app.use(‘/static’, express.static(__dirname + ‘/public’)); Express框架的入门篇基本就是上边这些内容了,截止到这里我们并没有演示一个完整的例子,那么下面我们来看一个表单提交的例子,在以上代码的基础上。 使用GET方式提交表单 1:创建静态文件夹 在项目myapp 目录下创建public (存放img,js,css文件)和templates(存放html文件) 2:app.js 如下 var express = require('express'); // var birds = require('./birds'); var app = express(); app.use(express.static('public')); //static file app.use(express.static('templates')); //static file // // app.listen(3000, function () { // console.log('Example app listening on port 3000!'); // }); app.get('/index.html', function (req, res) { res.sendFile( __dirname + "/" + "index.html" ); }) app.get('/form_get', function (req, res) { // 输出 JSON 格式 response = { username:req.query.username, email:req.query.email, }; console.log(response); res.end(JSON.stringify(response)); }) var server = app.listen(3000, function () { var host = server.address().address var port = server.address().port console.log("应用实例,访问地址为 http://%s:%s", host, port) }) 3:index.html 如下(位于 根目录的templates文件夹下) <html> <head> <meta charset="UTF-8"> <title>test</title> <link rel="stylesheet" href="http://127.0.0.1:3000/css/index.css"> </head> <div class="login-main"> <h3 align="center">------------Login-------------</h3> <form role="form" method="get" action="http://127.0.0.1:3000/form_get"> <input class="incon" name="username" type="text" placeholder="username"> <input class="incon" name="email" type="text" placeholder="email"> <input class="btnincon" type="submit" value="登录"> <hr style="height:1px;border:none;border-top:1px solid darkgray;" /> </form> </table> </div> </body> </html> 4:index.css /*登录注册样式 */ hr{margin-bottom: 5px;} .loginbody{ background-repeat: no-repeat; background-size:cover;} .login-main{width:400px; height: 300px;margin: auto; overflow: hidden; margin-top:8%; border-radius: 4px; border: 1px solid darkgray; box-shadow: -10px 10px 20px gray;} .login-main h3{ text-indent: 0.5em;} .incon{ width: 360px; font-size: 16px; margin-left:20px; margin-top:20px; height: 38px; border-radius: 2px; text-indent: 1em;} .btnincon{ width: 200px; font-size: 16px; margin-left:100px; margin-top:20px; height: 38px; border-radius: 2px; text-indent: 1em;} 5:查看效果 输入相应内容 使用POST方式提交表单 1:修改html文件 把html文件中的method改为 post,action中的form_get 改成form_post 2:在app.js中添加 var bodyParser = require('body-parser'); // 创建 application/x-www-form-urlencoded 编码解析 var urlencodedParser = bodyParser.urlencoded({ extended: false }) app.post('/form_post', urlencodedParser, function (req, res) { // 输出 JSON 格式 response = { username:req.bod.username, email:req.body.email, }; console.log(response); res.end(JSON.stringify(response)); }) 3:执行 npm install body-parser –save 重新测试会看到同样的效果 文件上传功能 1:index.html <html> <head> <title>文件上传表单</title> </head> <body> <h3>文件上传:</h3> 选择一个文件上传: <br /> <form action="/file_upload" method="post" enctype="multipart/form-data"> <input type="file" name="image" size="50" /> <br /> <input type="submit" value="上传文件" /> </form> </body> </html> 2:app.js var express = require('express'); var app = express(); var fs = require("fs"); var bodyParser = require('body-parser'); var multer = require('multer'); app.use(express.static('public')); app.use(express.static('templates')); app.use(bodyParser.urlencoded({ extended: false })); app.use(multer({ dest: '/tmp/'}).array('image')); app.get('/index.html', function (req, res) { res.sendFile( __dirname + "/" + "index.html" ); }) app.post('/file_upload', function (req, res) { console.log(req.files[0]); // 上传的文件信息 var des_file = __dirname + "/" + req.files[0].originalname; fs.readFile( req.files[0].path, function (err, data) { fs.writeFile(des_file, data, function (err) { if( err ){ console.log( err ); }else{ response = { message:'File uploaded successfully', filename:req.files[0].originalname }; } console.log( response ); res.end( JSON.stringify( response ) ); }); }); }) var server = app.listen(3000, function () { var host = server.address().address var port = server.address().port console.log("应用实例,访问地址为 http://%s:%s", host, port) }) 执行 npm install fs –save npm install multer –save 效果图 上一篇:Node.js历险记之剑未配好,已出江湖
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt/ 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 其实我个人对于接受新知识是有点抵触的,当然这里的新知识指的是除了大数据范畴之前的,就比如说nodejs,但是由于种种原因,我决定还是学习下吧,顺便在这个过程中把javascript也好好学习一番,至于我为什么抵触大数据之外的东西,是因为我觉得人应该有棱角,知识和技术也一样,如果你什么都会,但是什么都不精那就没意思了。 其实这一段时间一直有看elk的技术栈,在kibana的二次开发中,如果涉及到后台服务,那么nodejs就逃避不了了,当然还有难搞的js,反正我是很头疼。 Node.js是什么? js是脚本语言,脚本语言都需要一个解析器才能运行。对于写在HTML页面里的JS,浏览器充当了解析器的角色。而对于需要独立运行的JS,NodeJS就是一个解析器。 每一种解析器都是一个运行环境,不但允许JS定义各种数据结构,进行各种计算,还允许js使用运行环境提供的内置对象和方法做一些事情。例如运行在浏览器中的js的用途是操作DOM,浏览器就提供了document之类的内置对象。而运行在Node.js中的JS的用途是操作磁盘文件或搭建HTTP服务器,Node.js就相应提供了fs、http等内置对象。 Node.js部署和简单使用 1:安装nvm nvm是node.js版本管理工具,其全程是Node Version Manager,其实现是通过shell脚本,安装起来也比较方便 方法1: curl https://raw.github.com/creationix/nvm/v0.4.0/install.sh | sh 方法2: wget -qO- https://raw.github.com/creationix/nvm/v0.4.0/install.sh | sh 以上脚本会把nvm库clone到~/.nvm,然后会在~/.bash_profile, ~/.zshrc或~/.profile末尾添加source,安装完成之后,你可以用nvm –help 来查看其使用方法 2:安装nodejs 目前node官网上得最新版本为7.6.0 安装: nvm install v7.6.0 当然这里得版本号是你可以选择得,你也可以安装多个版本得node,然后通过nvm use 来选择你要使用得node版本,如果你不知道你现在使用得node版本,你可以通过以下命令来查看 $node -v v7.6.0 3:npm介绍使用 npm是随同node.js一起安装得包管理工具,能解决Node.js代码部署上得很多问题,常用得使用场景有: 允许用户从NPM服务器上下载别人编写的第三方包到本地使用 允许用户从NPM服务器下载并安装别人编写得命令杭程序到本地使用 允许用户将自己编写的包或者命令程序上传到npm服务器供别人使用 由于新版得node.js已经集成了npm,在使用nvm install node-version之后, 可以使用npm -v来测试是否安装成功 $ npm -v 4.1.2 当然这里如果你使用的不是新版的npm,你可以使用下面的命令来升级npm sudo npm install npm -g npm install,这个时候就可以使用cnpm来替代npm,cnpm安装方式是: npm install cnpm -g –registry=https://registry.npm.taobao.org npm安装package分两种方式:本地安装和全局安装,区别是是否有 -g 标示,比如说: npm install express //本地安装 npm install express -g //全局安装 其区别是: 本地安装将安装包放在./node_moudles下,如果没有该目录, 会生成一个目录。 全局安装是将安装包放在 /usr/local 下或者 你的node目录,你可以直接在命令行里调用。 你可以通过以下命令来查看你安装了那些全局包 npm ls -g npm卸载,更新,搜索模块 npm uninstall express npm update express npm search express npm 发布模块 (1):执行npm init ,按要求填写相应的信息,之后会在当前目录生成一个package.js的文件 (2):在npm 资源库中注册用户(使用邮箱注册) $ npm adduser Username: Password: Email: (this IS public) thinkgamer@163.com (3):发布模块 npm publish (4):如果你以上步骤都对,你就可以使用npm install 来安装你上传的模块 4:nodejs创建一个简单的项目 vim server.js,加入以下内容,然后执行node server.js var http = require('http') http.createServer(function (request, response){ response.writeHead(200,{'Content-Type': 'text/plain'}); response.end('Hello Thinkgamer,My blog is <a href="http://blog.csdn.net/gamer_gyt">Blog</a>\n') }).listen(8888); console.log('server as http://127.0.0.0.1:8888'); 浏览器可看到: 分析代码: 第一行:引入node.js自带的http模块,并且赋值给http变量 接下来:调用createServer函数,返回一个对象,该对象有listen方法,指定服务器监听得端口号 5:Node.js REPL交互式解释器 通过node 进入 ~$ node > 常用的REPL命令: ctrl + c - 退出当前终端。 ctrl + c 按下两次 - 退出 Node REPL。 ctrl + d - 退出 Node REPL. 向上/向下 键 - 查看输入的历史命令 tab 键 - 列出当前命令 .help - 列出使用命令 .break - 退出多行表达式 .clear - 退出多行表达式 .save filename - 保存当前的 Node REPL 会话到指定文件 .load filename - 载入当前 Node REPL 会话的文件内容。 Node.js的模块化 编写稍微大一点的程序时都会将代码模块化,在Nodejs中,一般将代码合理的拆分到不同的JS文件中,每一个文件就是一个模块,而文件路径就是模块名,在编写每个模块时,都有require,export,module三个预先定义好的变量可供使用。 1:require require函数用于在当前模块中加载和使用别的模块,传入一个模块名,返回一个模块导出对象,模块名可使用相对路径(./开头),或者绝对路径(/ 或者C:之类的盘符开头),另外模块名中的.js扩展名可以省略。例如 var foo1 = require(‘./hello’) var foo2 = require(‘./hello.js’) 两者保存的是同一个模块导出的对象 2:exports exports是当前模块的导出对象,用于导出模块共有方法和属性,别的模块通过require函数使用当前模块时得到的就是当前模块的导出对象,例如test.js exports.hello=function(){ console.log("hello, this is exports"); } 以上代码中,模块导出对象被替换为一个函数。 index.js内容如下: var H = require('./test.js'); H.hello(); 调用: >node index.js hello, this is exports 3:module 通过nodule对象可以访问到 当前模块的一些相关信息,但最多的用途是替换当前模块的导出对象。例如模块导出对象默认是一个普通对象,如果想改成一个函数的话,可以使用以下方式 test.js module.exports=function(){ var name; this.setName=function(new_name){ name=new_name; }; this.showName=function(){ console.log(name); }; } 当然这个地方还可以这样写(test.js): function Hello(){ var name; this.setName=function(new_name){ name=new_name; }; this.showName=function(){ console.log(name); }; }; module.exports=Hello; index.js: var Per = require('./test.js'); var p = new Per(); p.setName('thinkgamer'); p.showName(); 调用: >node index.js thinkgamer 4:模块初始化 一个模块中的JS代码仅在模块第一次使用时执行一次,并在执行过程中初始化模块的导出对象,之后,缓存起来的导出对象被重复利用。 5:主模块 通过命令行参数传递给Nodejs以启动程序的模块被称为主模块,主模块负责调度组成整个程序的其他模块完成工作,例如通过以下命令来启动程序时,main.js就是主模块 $node main.js Nodejs的代码组织 1:模块路径解析规则 (1) 内置模块 如果传递给require函数的是NodeJS内置模块名称,不做路径解析,直接返回内部模块的导出对象,例如require(‘fs’)。 (2) node_modules目录 NodeJS定义了一个特殊的node_modules目录用于存放模块。例如某个模块的绝对路径是/home/user/hello.js,在该模块中使用require(‘foo/bar’)方式加载模块时,则NodeJS依次尝试使用以下路径。 /home/user/node_modules/foo/bar /home/node_modules/foo/bar /node_modules/foo/bar (3)NODE_PATH环境变量 与PATH环境变量类似,NodeJS允许通过NODE_PATH环境变量来指定额外的模块搜索路径。NODE_PATH环境变量中包含一到多个目录路径,路径之间在Linux下使用:分隔,在Windows下使用;分隔。例如定义了以下NODE_PATH环境变量: NODE_PATH=/home/user/lib:/home/lib 当使用require(‘foo/bar’)的方式加载模块时,则NodeJS依次尝试以下路径。 /home/user/lib/foo/bar /home/lib/foo/bar 2:包(package) 我们知道js模块的基本文件是单个js文件,但复杂些的模块往往由多个子模块组成,为了便于管理和使用,我们把由多个子模块组成的大模块称作包,并把所有子模块放在同一个目录下。 在组成一个包的所有子模块中,需要有一个入口模块,入口模块的导出对象被作为包的导出对象。例如有以下目录结构。 - /home/user/lib/ - cat/ head.js body.js main.js 其中cat目录定义了一个包,其中包含了3个子模块。main.js作为入口模块,其内容如下: var head = require('./head'); var body = require('./body'); exports.create = function (name) { return { name: name, head: head.create(), body: body.create() }; }; 在其它模块里使用包的时候,需要加载包的入口模块。接着上例,使用require(‘/home/user/lib/cat/main’)能达到目的,但是入口模块名称出现在路径里看上去不是个好主意。因此我们需要做点额外的工作,让包使用起来更像是单个模块。 index.js 当模块的文件名是index.js,加载模块时可以使用模块所在目录的路径代替模块文件路径,因此接着上例,以下两条语句等价。 var cat = require(‘/home/user/lib/cat’); var cat = require(‘/home/user/lib/cat/index’); 这样处理后,就只需要把包目录路径传递给require函数,感觉上整个目录被当作单个模块使用,更有整体感。 package.json 如果想自定义入口模块的文件名和存放位置,就需要在包目录下包含一个package.json文件,并在其中指定入口模块的路径。上例中的cat模块可以重构如下。 - /home/user/lib/ - cat/ + doc/ - lib/ head.js body.js main.js + tests/ package.json 其中package.json内容如下。 { "name": "cat", "main": "./lib/main.js" } 如此一来,就同样可以使用require(‘/home/user/lib/cat’)的方式加载模块。NodeJS会根据包目录下的package.json找到入口模块所在位置。 3:工程目录 以编写一个命令行程序为例,一般我们会同时提供命令行模式和API模式两种使用方式,并且我们会借助三方包来编写代码。除了代码外,一个完整的程序也应该有自己的文档和测试用例。因此,一个标准的工程目录都看起来像下边这样。 - /home/user/workspace/node-echo/ # 工程目录 - bin/ # 存放命令行相关代码 node-echo + doc/ # 存放文档 - lib/ # 存放API相关代码 echo.js - node_modules/ # 存放三方包 + argv/ + tests/ # 存放测试用例 package.json # 元数据文件 README.md # 说明文件 其中部分文件内容如下: /* bin/node-echo */ var argv = require('argv'), echo = require('../lib/echo'); console.log(echo(argv.join(' '))); /* lib/echo.js */ module.exports = function (message) { return message; }; /* package.json */ { "name": "node-echo", "main": "./lib/echo.js" } 以上例子中分类存放了不同类型的文件,并通过node_moudles目录直接使用三方包名加载模块。此外,定义了package.json之后,node-echo目录也可被当作一个包来使用。 下一篇: Node.js历险记之express框架入门篇
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前面的话 之前写过一篇文章是: 如何使用一个IP搭建ES集群——Docker如你所愿,在该篇文章中说明了Elasticsearch集群的单播和多播的概念和差别,以及在生产环境中的利与弊。其实在里边也写了怎么搭建集群,但是整个流程走下来是有很多bug的,那么这篇文章就好好聊一下如何搭建一个完整的Elasticsearch集群,并灵活添加节点。 环境准备 1:Elasticsearch 5.2.1 ZIP包下载:点击下载 2:Ubuntu 16.04 3:Java 1.8 4:解压包到/opt/elk/目录下,生成两个Elasticsearch文件夹,如下 >ls /opt/elk elasticsearch-5.2.1_1 elasticsearch-5.2.1_2 5:赋予权限,否则在启动的过程中会报错(可选,根据自己的情况而定) sudo chown -R master;master elasticsearch-5.2.1_* 配置说明 注意:以下配置过程中可能会出现权限错误,由于我是在/opt/elk目录下进行的,所以有权限问题 1:Elasticsearch集群中的三种角色 master node:master几点主要用于元数据(metadata)的处理,比如索引的新增、删除、分片分配等。 data node:data 节点上保存了数据分片。它负责数据相关操作,比如分片的 CRUD,以及搜索和整合操作。这些操作都比较消耗 CPU、内存和 I/O 资源; client node:client 节点起到路由请求的作用,实际上可以看做负载均衡器。 其对应的高性能集群拓扑结构模式为: # 配置文件中给出了三种配置高性能集群拓扑结构的模式,如下: # 1. 如果你想让节点从不选举为主节点,只用来存储数据,可作为负载器 # node.master: false # node.data: true # 2. 如果想让节点成为主节点,且不存储任何数据,并保有空闲资源,可作为协调器 # node.master: true # node.data: false # 3. 如果想让节点既不称为主节点,又不成为数据节点,那么可将他作为搜索器,从节点中获取数据,生成搜索结果等 # node.master: false # node.data: false 2:config/elasticsearch.ymal中配置项说明 cluster_name 集群名称,默认为elasticsearch,这里我们设置为es5.2.1Cluster node.name配置节点名,用来区分节点 network.host 是配置可以访问本节点的路由地址 http.port 路由地址端口 transport.tcp.port TCP协议转发地址端口 node.master 是否作为集群的主结点 ,值为true或true node.data 是否存储数据,值为true或true discovery.zen.ping.unicast.hosts 用来配置所有用来组建集群的机器的IP地址,由于5.2.1新版本是不支持多播的,因此这个值需要提前设定好,当集群需要扩展的时候,该值都要做改变,增加新机器的IP地址,如果是在一个ip上,要把TCP协议转发端口写上 discovery.zen.minimum_master_nodes 用来配置主节点数量的最少值,如果主节点数量低于该值,闭包范围内的集群将会停止服务,之所以加粗体,是因为暂时尚未认证,下面配置为1方便集群更容易形成,即使只有一个主节点,也可以构建集群 gateway.* 网关的相关配置 script.* indices.* 根据需求添加的配置(可选) 3:elasticsearch-5.2.1_1中的yaml文件 该结点作为master-node运行 cluster.name: es5 node.name: node-1 network.host: 0.0.0.0 http.port: 9200 transport.tcp.port: 9300 node.master: true node.data: true discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301", "0.0.0.0:9302"] discovery.zen.minimum_master_nodes: 2 gateway.recover_after_nodes: 2 gateway.recover_after_time: 5m gateway.expected_nodes: 1 script.engine.groovy.inline.search: on script.engine.groovy.inline.aggs: on indices.recovery.max_bytes_per_sec: 20mb 4:elasticsearch-5.2.1_2中的yaml文件 该结点作为data-node运行 cluster.name: es5 node.name: node-2 network.host: 0.0.0.0 http.port: 9201 transport.tcp.port: 9301 node.master: false node.data: true discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301", "0.0.0.0:9302"] discovery.zen.minimum_master_nodes: 2 gateway.recover_after_nodes: 2 gateway.recover_after_time: 5m gateway.expected_nodes: 1 script.engine.groovy.inline.search: on script.engine.groovy.inline.aggs: on indices.recovery.max_bytes_per_sec: 20mb 5:elasticsearch-5.2.1_3中的yaml文件 该结点作为client-node运行 cluster.name: es5 node.name: node-3 network.host: 0.0.0.0 http.port: 9202 transport.tcp.port: 9302 node.master: false node.data: false discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301", "0.0.0.0:9302"] discovery.zen.minimum_master_nodes: 2 gateway.recover_after_nodes: 2 gateway.recover_after_time: 5m gateway.expected_nodes: 1 script.engine.groovy.inline.search: on script.engine.groovy.inline.aggs: on indices.recovery.max_bytes_per_sec: 20mb 启动三个结点,打开http://localhost:9200/ 查看 http://localhost:9200/_cluster/health?pretty=true 出现错误: no known master node, scheduling a retry 原因是:我们设置的主节点只有一个而 discovery.zen.minimum_master_nodes: 2 所以这里改为1即可,然后此时还可能会遇到一个问题就是node2和node3不能加入到集群,报的错如下: [node-2] failed to send join request to master [{node-1}{WbcP0pC_T32jWpYvu5is1A}{2_LCVHx1QEaBZYZ7XQEkMg}{10.10.11.200}{10.10.11.200:9300}], reason [RemoteTransportException[[node-1][10.10.11.200:9300][internal:discovery/zen/join]]; nested: IllegalArgumentException[can't add node {node-2}{WbcP0pC_T32jWpYvu5is1A}{p-HCgFLvSFaTynjKSeqXyA}{10.10.11.200}{10.10.11.200:9301}, found existing node {node-1}{WbcP0pC_T32jWpYvu5is1A}{2_LCVHx1QEaBZYZ7XQEkMg}{10.10.11.200}{10.10.11.200:9300} with the same id but is a different node instance]; ] 原因是:是因为复制的elasticsearch文件夹下包含了data文件中示例一的节点数据,需要把示例二data文件下的文件清空。 然后在查看集群状态: { "cluster_name" : "es5", "status" : "yellow", "timed_out" : false, "number_of_nodes" : 3, "number_of_data_nodes" : 2, "active_primary_shards" : 22, "active_shards" : 22, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 21, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 51.162790697674424 } 6:配置head插件 克隆到本地: git clone git://github.com/mobz/elasticsearch-head.git 进入到文件夹,并安装 cd elasticsearch-head npm install 在elasticsearch.ymal文件中添加: http.cors.enabled: true http.cors.allow-origin: "*" 运行 npm install -g grunt grunt server 查看http://localhost:9100/ 配置logstash解析rsyslog文件 配置rsyslog参考:http://blog.csdn.net/gamer_gyt/article/details/54025857 编写相应的jx_syslog.conf 解析文件 input { tcp{ port => 5000 type => syslog } udp{ port => 5000 type => syslog } } filter { if [type] == 'syslog'{ grok { match => { 'message' => '%{SYSLOGTIMESTAMP:syslog_timestamp} %{HOSTNAME:hostname} %{WORD:program}%{GREEDYDATA:msgsplit}' } } date { match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ] target => "syslog_timestamp" timezone => "UTC" } # processing repeated messages if [msgsplit] =~ "message repeated " { grok { match => [ "msgsplit", "\[%{BASE10NUM:pid}\]: message repeated %{BASE10NUM:ntimes} times: \[ Failed password for %{NOTSPACE:user} from %{IP:src_ip} port %{BASE10NUM:src_port} %{WORD:protocol}\]" ] tag_on_failure => ["parsefailure", "ssh_failed_login", "ssh_repeat_message" ] add_tag => [ "ssh_repeat_message", "grokked", "ssh_failed_login" ] } } # SSH successful login else if [msgsplit] =~ "Accepted password for" { mutate { replace => { type => "success_syslog" } } grok { match => [ "msgsplit", "\[%{BASE10NUM:pid}\]: Accepted password for %{NOTSPACE:user} from %{IP:src_ip} port %{BASE10NUM:src_port} %{WORD:protocol}" ] tag_on_failure => ["parsefailure", "ssh_successful_login" ] add_tag => [ "ssh_successful_login", "grokked" ] } } # SSH Brute force attemp else if [msgsplit] =~ "Failed password for invalid user" { mutate { replace => { type => "brute_syslog" } } grok { match => [ "msgsplit", "\[%{BASE10NUM:pid}\]: Failed password for invalid user %{NOTSPACE:user} from %{IP:src_ip} port %{BASE10NUM:src_port} %{WORD:protocol}" ] add_tag => [ "ssh_brute_force", "grokked" ] tag_on_failure => ["parsefailure", "ssh_brute_force" ] } } # SSH failed login else if [msgsplit] =~ "Failed password for" { mutate { replace => { type => "fail_syslog" } } grok { match => [ "msgsplit", "\[%{BASE10NUM:pid}\]: Failed password for %{NOTSPACE:user} from %{IP:src_ip} port %{BASE10NUM:src_port} %{WORD:protocol}" ] add_tag => [ "ssh_failed_login", "grokked" ] tag_on_failure => ["parsefailure", "ssh_failed_login" ] } } else { drop { } } } } output { if [type] in ['success_syslog', 'brute_syslog', 'fail_syslog'] { elasticsearch { hosts => ["http://localhost:9200"] #index => "ssh_login-%{+YYYY.MM.dd}" index => "%{type}-%{+YYYY.MM.dd}" } } } 运行logstash: bin/logstash -f conf/jx_syslog.conf ssh 模拟登录 查看es集群 node-1是master结点,node-2是data结点,node-3不存储数据,作为负载均衡使用 踩过的坑 1:我是在虚拟机中进行的,由于硬盘内存不足,在es集群正常启动之后,logstash往es集群写数据时不能正常写入 2:复制elasticsearch文件夹时,如果原来的es文件夹下存在node数据,那么es集群也不能正常启动 3:配置master结点个数,由于我是三台机器,一个master node,一个data node,一个client node,然后设置 discovery.zen.minimum_master_nodes: 2,es集群也不能正常启动,建议这里设置为1 elasticsearch.ymal配置文件说明 上边已经对我配置es集群设置的参数有了简单的说明,但是其实还有许多参数没有设置和说明 修改配置 /etc/elasticsearch/elasticsearch.yml 以下对相关字段以注释方式进行解析. ##################### Elasticsearch Configuration Example ##################### # 我只是挑些重要的配置选项进行注释,其实自带的已经有非常细致的英文注释了.有理解偏差的地方请以英文原版解释为准. ################################### Cluster ################################### # 代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的. # es的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看es集群,在逻辑上是个整体,你与任何一个节点的通信和与整个es集群通信是等价的。 # cluster.name可以确定你的集群名称,当你的elasticsearch集群在同一个网段中elasticsearch会自动的找到具有相同cluster.name的elasticsearch服务. # 所以当同一个网段具有多个elasticsearch集群时cluster.name就成为同一个集群的标识. #cluster.name: elasticsearch #################################### Node ##################################### # 节点名称同理,可自动生成也可手动配置. #node.name: "Franz Kafka" # 允许一个节点是否可以成为一个master节点,es是默认集群中的第一台机器为master,如果这台机器停止就会重新选举master. #node.master: true # 允许该节点存储数据(默认开启) #node.data: true # 配置文件中给出了三种配置高性能集群拓扑结构的模式,如下: # 1. 如果你想让节点从不选举为主节点,只用来存储数据,可作为负载器 # node.master: false # node.data: true # # 2. 如果想让节点成为主节点,且不存储任何数据,并保有空闲资源,可作为协调器 # node.master: true # node.data: false # # 3. 如果想让节点既不称为主节点,又不成为数据节点,那么可将他作为搜索器,从节点中获取数据,生成搜索结果等 # node.master: false # node.data: false # 监控集群状态有一下插件和API可以使用: # Use the Cluster Health API [http://localhost:9200/_cluster/health], the # Node Info API [http://localhost:9200/_nodes] or GUI tools # such as <http://www.elasticsearch.org/overview/marvel/>, # <http://github.com/karmi/elasticsearch-paramedic>, # <http://github.com/lukas-vlcek/bigdesk> and # <http://mobz.github.com/elasticsearch-head> to inspect the cluster state. # A node can have generic attributes associated with it, which can later be used # for customized shard allocation filtering, or allocation awareness. An attribute # is a simple key value pair, similar to node.key: value, here is an example: # #node.rack: rack314 # By default, multiple nodes are allowed to start from the same installation location # to disable it, set the following: #node.max_local_storage_nodes: 1 #################################### Index #################################### # 设置索引的分片数,默认为5 #index.number_of_shards: 5 # 设置索引的副本数,默认为1: #index.number_of_replicas: 1 # 配置文件中提到的最佳实践是,如果服务器够多,可以将分片提高,尽量将数据平均分布到大集群中去 # 同时,如果增加副本数量可以有效的提高搜索性能 # 需要注意的是,"number_of_shards" 是索引创建后一次生成的,后续不可更改设置 # "number_of_replicas" 是可以通过API去实时修改设置的 #################################### Paths #################################### # 配置文件存储位置 #path.conf: /path/to/conf # 数据存储位置(单个目录设置) #path.data: /path/to/data # 多个数据存储位置,有利于性能提升 #path.data: /path/to/data1,/path/to/data2 # 临时文件的路径 #path.work: /path/to/work # 日志文件的路径 #path.logs: /path/to/logs # 插件安装路径 #path.plugins: /path/to/plugins #################################### Plugin ################################### # 设置插件作为启动条件,如果一下插件没有安装,则该节点服务不会启动 #plugin.mandatory: mapper-attachments,lang-groovy ################################### Memory #################################### # 当JVM开始写入交换空间时(swapping)ElasticSearch性能会低下,你应该保证它不会写入交换空间 # 设置这个属性为true来锁定内存,同时也要允许elasticsearch的进程可以锁住内存,linux下可以通过 `ulimit -l unlimited` 命令 #bootstrap.mlockall: true # 确保 ES_MIN_MEM 和 ES_MAX_MEM 环境变量设置为相同的值,以及机器有足够的内存分配给Elasticsearch # 注意:内存也不是越大越好,一般64位机器,最大分配内存别才超过32G ############################## Network And HTTP ############################### # 设置绑定的ip地址,可以是ipv4或ipv6的,默认为0.0.0.0 #network.bind_host: 192.168.0.1 # 设置其它节点和该节点交互的ip地址,如果不设置它会自动设置,值必须是个真实的ip地址 #network.publish_host: 192.168.0.1 # 同时设置bind_host和publish_host上面两个参数 #network.host: 192.168.0.1 # 设置节点间交互的tcp端口,默认是9300 #transport.tcp.port: 9300 # 设置是否压缩tcp传输时的数据,默认为false,不压缩 #transport.tcp.compress: true # 设置对外服务的http端口,默认为9200 #http.port: 9200 # 设置请求内容的最大容量,默认100mb #http.max_content_length: 100mb # 使用http协议对外提供服务,默认为true,开启 #http.enabled: false ################################### Gateway ################################### # gateway的类型,默认为local即为本地文件系统,可以设置为本地文件系统 #gateway.type: local # 下面的配置控制怎样以及何时启动一整个集群重启的初始化恢复过程 # (当使用shard gateway时,是为了尽可能的重用local data(本地数据)) # 一个集群中的N个节点启动后,才允许进行恢复处理 #gateway.recover_after_nodes: 1 # 设置初始化恢复过程的超时时间,超时时间从上一个配置中配置的N个节点启动后算起 #gateway.recover_after_time: 5m # 设置这个集群中期望有多少个节点.一旦这N个节点启动(并且recover_after_nodes也符合), # 立即开始恢复过程(不等待recover_after_time超时) #gateway.expected_nodes: 2 ############################# Recovery Throttling ############################# # 下面这些配置允许在初始化恢复,副本分配,再平衡,或者添加和删除节点时控制节点间的分片分配 # 设置一个节点的并行恢复数 # 1.初始化数据恢复时,并发恢复线程的个数,默认为4 #cluster.routing.allocation.node_initial_primaries_recoveries: 4 # # 2.添加删除节点或负载均衡时并发恢复线程的个数,默认为2 #cluster.routing.allocation.node_concurrent_recoveries: 2 # 设置恢复时的吞吐量(例如:100mb,默认为0无限制.如果机器还有其他业务在跑的话还是限制一下的好) #indices.recovery.max_bytes_per_sec: 20mb # 设置来限制从其它分片恢复数据时最大同时打开并发流的个数,默认为5 #indices.recovery.concurrent_streams: 5 # 注意: 合理的设置以上参数能有效的提高集群节点的数据恢复以及初始化速度 ################################## Discovery ################################## # 设置这个参数来保证集群中的节点可以知道其它N个有master资格的节点.默认为1,对于大的集群来说,可以设置大一点的值(2-4) #discovery.zen.minimum_master_nodes: 1 # 探查的超时时间,默认3秒,提高一点以应对网络不好的时候,防止脑裂 #discovery.zen.ping.timeout: 3s # For more information, see # <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-zen.html> # 设置是否打开多播发现节点.默认是true. # 当多播不可用或者集群跨网段的时候集群通信还是用单播吧 #discovery.zen.ping.multicast.enabled: false # 这是一个集群中的主节点的初始列表,当节点(主节点或者数据节点)启动时使用这个列表进行探测 #discovery.zen.ping.unicast.hosts: ["host1", "host2:port"] # Slow Log部分与GC log部分略,不过可以通过相关日志优化搜索查询速度 ############## Memory(重点需要调优的部分) ################ # Cache部分: # es有很多种方式来缓存其内部与索引有关的数据.其中包括filter cache # filter cache部分: # filter cache是用来缓存filters的结果的.默认的cache type是node type.node type的机制是所有的索引内部的分片共享filter cache.node type采用的方式是LRU方式.即:当缓存达到了某个临界值之后,es会将最近没有使用的数据清除出filter cache.使让新的数据进入es. # 这个临界值的设置方法如下:indices.cache.filter.size 值类型:eg.:512mb 20%。默认的值是10%。 # out of memory错误避免过于频繁的查询时集群假死 # 1.设置es的缓存类型为Soft Reference,它的主要特点是据有较强的引用功能.只有当内存不够的时候,才进行回收这类内存,因此在内存足够的时候,它们通常不被回收.另外,这些引用对象还能保证在Java抛出OutOfMemory异常之前,被设置为null.它可以用于实现一些常用图片的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory.在es的配置文件加上index.cache.field.type: soft即可. # 2.设置es最大缓存数据条数和缓存失效时间,通过设置index.cache.field.max_size: 50000来把缓存field的最大值设置为50000,设置index.cache.field.expire: 10m把过期时间设置成10分钟. #index.cache.field.max_size: 50000 #index.cache.field.expire: 10m #index.cache.field.type: soft # field data部分&&circuit breaker部分: # 用于field data 缓存的内存数量,主要用于当使用排序,faceting操作时,elasticsearch会将一些热点数据加载到内存中来提供给客户端访问,但是这种缓存是比较珍贵的,所以对它进行合理的设置. # 可以使用值:eg:50mb 或者 30%(节点 node heap内存量),默认是:unbounded #indices.fielddata.cache.size: unbounded # field的超时时间.默认是-1,可以设置的值类型: 5m #indices.fielddata.cache.expire: -1 # circuit breaker部分: # 断路器是elasticsearch为了防止内存溢出的一种操作,每一种circuit breaker都可以指定一个内存界限触发此操作,这种circuit breaker的设定有一个最高级别的设定:indices.breaker.total.limit 默认值是JVM heap的70%.当内存达到这个数量的时候会触发内存回收 # 另外还有两组子设置: #indices.breaker.fielddata.limit:当系统发现fielddata的数量达到一定数量时会触发内存回收.默认值是JVM heap的70% #indices.breaker.fielddata.overhead:在系统要加载fielddata时会进行预先估计,当系统发现要加载进内存的值超过limit * overhead时会进行进行内存回收.默认是1.03 #indices.breaker.request.limit:这种断路器是elasticsearch为了防止OOM(内存溢出),在每次请求数据时设定了一个固定的内存数量.默认值是40% #indices.breaker.request.overhead:同上,也是elasticsearch在发送请求时设定的一个预估系数,用来防止内存溢出.默认值是1 # Translog部分: # 每一个分片(shard)都有一个transaction log或者是与它有关的预写日志,(write log),在es进行索引(index)或者删除(delete)操作时会将没有提交的数据记录在translog之中,当进行flush 操作的时候会将tranlog中的数据发送给Lucene进行相关的操作.一次flush操作的发生基于如下的几个配置 #index.translog.flush_threshold_ops:当发生多少次操作时进行一次flush.默认是 unlimited #index.translog.flush_threshold_size:当translog的大小达到此值时会进行一次flush操作.默认是512mb #index.translog.flush_threshold_period:在指定的时间间隔内如果没有进行flush操作,会进行一次强制flush操作.默认是30m #index.translog.interval:多少时间间隔内会检查一次translog,来进行一次flush操作.es会随机的在这个值到这个值的2倍大小之间进行一次操作,默认是5s #index.gateway.local.sync:多少时间进行一次的写磁盘操作,默认是5s # 以上的translog配置都可以通过API进行动态的设置 如何动态的加入结点 上边我们已经部署了三个结点的es集群,加入现在我们要另外加入一个data node,我们该怎么办? 1:copy 一个elasticsearch文件夹,作为第四个结点 sudo cp -r elasticsearch-5.2.1_2 elasticsearch-5.2.1_4 2:修改es4 中的yaml文件 cluster.name: es5 node.name: node-4 network.host: 0.0.0.0 http.port: 9203 transport.tcp.port: 9303 node.master: false node.data: true discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301", "0.0.0.0:9302", "0.0.0.0:9302"] discovery.zen.minimum_master_nodes: 1 gateway.recover_after_nodes: 2 gateway.recover_after_time: 5m gateway.expected_nodes: 1 script.engine.groovy.inline.search: on script.engine.groovy.inline.aggs: on indices.recovery.max_bytes_per_sec: 20mb 3:修改另外三个结点的yaml文件 修改discovery.zen.ping.unicast.hosts: 配置项为: discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301", "0.0.0.0:9302", "0.0.0.0:9302"] 4:重启es集群 前三个结点启动完毕,启动第四个结点时报错如下: Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x000000008a660000, 1973026816, 0) failed; error='Cannot allocate memory' (errno=12) # # There is insufficient memory for the Java Runtime Environment to continue. # Native memory allocation (mmap) failed to map 1973026816 bytes for committing reserved memory. # An error report file with more information is saved as: # /tmp/hs_err_pid9963.log 这个错误的意思是JVM运行内存不足,解决办法是增加虚拟机内存,同时删除es4目录下data目录下的数据 然后重启elasticsearch集群,重启logstash: 欧了,到这里灵活的添加结点我们也完成了 ES集群的监控 网上查看资料相应的插件有 bigdesk:https://github.com/hlstudio/bigdesk paramedic:https://github.com/karmi/elasticsearch-paramedic kopf:https://github.com/lmenezes/elasticsearch-kopf 由于大部分插件只支持es2.x,所以这里采用bigdesk 1:下载 git clone https://github.com/hlstudio/bigdesk.git 2:进入该目录,在浏览器中打开index.html 效果图如下,可以进行刷新时间设置,查看不同结点情况 结束 如果你觉得我写的还可以,请关注我的微信公众号,谢谢!
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前面的话 或许我们经常会遇到这样一个场景,就是当我们的某种日志数据积累到一定程度的时候,我们需要大数据平台来进行存储,包括hdfs,hive等,这个时候Sqoop就发挥他的巨大价值了。 Sqoop简介 1. Sqoop简单介绍 Sqoop是一款开源的工具,主要用于在Hadoop和传统的数据库(mysql、postgresql等)进行数据的传递,可以将一个关系型数据库(例如:MySQL、Oracle、Postgres等)中的数据导进到Hadoop的HDFS中,也可以将HDFS的数据导进到关系型数据库中。 Sqoop中一大亮点就是可以通过hadoop的mapreduce把数据从关系型数据库中导入数据到HDFS。 Sqoop目前版本已经到了1.99.7,我们可以在其官网上看到所有的版本,Sqoop1.99.7是属于sqoop2,Sqoop1的最高版本为1.4.6,版本号划分区别,Apache:1.4.x,1.99.x~ 2. Sqoop一代和二代对比 版本号对比 两代之间是两个完全不同的版本,不兼容 sqoop1:1.4.x sqoop2:1.99. sqoop2比sqoop1的改进 (1) 引入sqoop server,集中化管理connector等 (2) 多种访问方式:CLI,Web UI,REST API (3) 引入基于角色 的安全机制 sqoop2和sqoop1的功能性对比 功能 Sqoop 1 Sqoop 2 用于所有主要 RDBMS 的连接器 支持 不支持解决办法: 使用已在以下数据库上执行测试的通用 JDBC 连接器: Microsoft SQL Server 、 PostgreSQL 、 MySQL 和 Oracle 。 此连接器应在任何其它符合 JDBC 要求的数据库上运行。但是,性能可能无法与 Sqoop 中的专用连接器相比 Kerberos 安全集成 支持 不支持 数据从 RDBMS 传输至 Hive 或 HBase 支持 不支持 解决办法: 按照此两步方法操作。 将数据从 RDBMS 导入 HDFS 在 Hive 中使用相应的工具和命令(例如 LOAD DATA 语句),手动将数据载入 Hive 或 HBase 数据从 Hive 或 HBase 传输至 RDBMS 不支持 解决办法: 按照此两步方法操作。 从 Hive 或 HBase 将数据提取至 HDFS (作为文本或 Avro 文件) 使用 Sqoop 将上一步的输出导出至 RDBMS 不支持 按照与 Sqoop 1 相同的解决方法操作 sqoop1和sqoop2的架构对比 (1) : sqoop1的架构图 版本号为1.4.x为sqoop1 在架构上:sqoop1使用sqoop客户端直接提交的方式 访问方式:CLI控制台方式进行访问 安全性:命令或脚本中指定用户数据库名及密码 (2) : sqoop2的架构图 版本号为1.99x为sqoop2 在架构上:sqoop2引入了sqoop server,对connector实现了集中的管理 访问方式:REST API、 JAVA API、 WEB UI以及CLI控制台方式进行访问 CLI方式访问,会通过交互过程界面,输入的密码信息丌被看到,同时Sqoop2引入基亍角色的安全机制,Sqoop2比Sqoop多了一个Server端。 (3) : 优缺点 sqoop1与sqoop2的优缺点如下: sqoop1的架构,仅仅使用一个sqoop客户端,sqoop2的架构,引入了sqoop server集中化管理connector,以及rest api,web,UI,并引入权限安全机制。 sqoop1与sqoop2优缺点比较 : sqoop1优点架构部署简单 sqoop1的缺点命令行方式容易出错,格式紧耦合,无法支持所有数据类型,安全机制不够完善,例如密码暴漏, 安装需要root权限,connector必须符合JDBC模型 sqoop2的优点多种交互方式,命令行,web UI,rest API,conncetor集中化管理,所有的链接安装在sqoop server上,完善权限管理机制,connector规范化,仅仅负责数据的读写。 sqoop2的缺点,架构稍复杂,配置部署更繁琐。 Sqoop的部署 1. Sqoop1的部署 sqoop1的部署相对比较简单,以1.4.6为例 (1) 下载:点击链接到下载页 (2) 解压到指定目录 sudo tar -zxvf /home/thinkgamer/下载/sqoop-1.4.6.tar.gz -C sudo mv sqoop-1.4.6/ sqoop (3) 配置环境变量 sudo vim ~/.bashrc 添加以下两行 export SQOOP_HOME=/opt/sqoop export PATH=$PATH:$SQOOP_HOME/bin 保存即可 source ~/.bashrc (4) 复制Mysql-jdbc 包到sqoop/lib目录下 sudo cp /home/thinkgamer/下载/MySQL-connector-Java-5.1.39-bin.jar /opt/bigdata/sqoop/lib/ (5) 修改bin/configure-sqoop文件 此时如果没有启用hbase,zookeeper等组件,将相应的信息注释,如果启用了,就pass,直接进入下一步 (6) 执行sqoop help查看帮助 2. Sqoop2的部署 以下部分大部分来自官方安装教程:https://sqoop.apache.org/docs/1.99.7/admin/Installation.html sqoop的部署相对比较麻烦,因为sqoop2即包含了client端,又包含了server端,官方给出的提示是: 服务器您需要在集群中的单个节点上安装服务器。此节点将用作所有Sqoop客户端的入口点。 客户端客户端可以安装在任意数量的计算机上。 下载文件解压到指定目录: sudo tar -zxvf /home/thinkgamer/下载/package/hadoop-family/sqoop-1.99.7.tar.gz /opt/bigdata (1) 目录说明 bin:可执行脚本,一般使用sqoop都是通过这个目录中的工具调用,是一些shell或batch脚本。 conf:存放配置文件 docs:目前不清楚具体是什么,可能是帮助文档,不过一般使用sqoop不会用到。 server:里面只有一个lib目录,存了很多jar文件,是sqoop2 的server包。 shell:同理,sqoop2的shell包。 tools:同理,sqoop2的工具包。 (2) 服务器端安装 2.1 环境变量设置 sqoop的安装依赖于hadoop的环境变量,$HADOOP_COMMON_HOME,$HADOOP_HDFS_HOME, $HADOOP_MAPRED_HOME 和 $HADOOP_YARN_HOME,请你确定这些环境变量被定义和指向了hadoop的安装目录,如果这些没有被正确配置,sqoop server端将不会被正常启动。 如果换将变量里已经配置了$HADOOP_HOME,那么sqoop将会在以下这几个路径中找寻$HADOOP_COMMON_HOME,$HADOOP_HDFS_HOME, $HADOOP_MAPRED_HOME 和 $HADOOP_YARN_HOME $HADOOP_HOME/share/hadoop/common $HADOOP_HOME/share/hadoop/hdfs $HADOOP_HOME/share/hadoop/mapreduce $HADOOP_HOME/share/hadoop/yarn 若$HADOOP_HOME已经配置了,最好不要再配置下面的变量,可能会有些莫名错误。 2.2 hadoop配置 Sqoop服务器将需要模拟用户访问集群内外的HDFS和其他资源,作为开始给定作业的用户,而不是运行服务器的用户。 您需要配置Hadoop以通过所谓的proxyuser系统显式地允许此模拟。 您需要在core-site.xml文件 - hadoop.proxyuser。$SERVER_USER.hosts和hadoop.proxyuser。$ SERVER_USER.groups中创建两个属性,其中$ SERVER_USER是将运行Sqoop 2服务器的用户。 在大多数情况下,配置*是足够的。 当服务器在sqoop2 user下运行时,需要在core-site.xml文件中配置如下: <property> <name>hadoop.proxyuser.sqoop2.hosts</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.sqoop2.groups</name> <value>*</value> </property> 我是用thinkgamer用户运行hadoop,所以这里将sqoop2换成thinkgamer 2.3 配置第三方jar包引用路径 一般我们使用的数据库驱动包都没有随着Sqoop一起释出,多半是因版权问题,所以将这些包放置在这个第三方组件下。再在配置一个SQOOP_SERVER_EXTRA_LIB系统变量即可,本例指定路径为$SQOOP_HOME/extra sudo vim ~/.bashrc 加入 export SQOOP_HOME=/opt/bigdata/sqoop export SQOOP_SERVER_EXTRA_LIB=$SQOOP_HOME/extra export PATH=$PATH:$SQOOP_HOME/bin 最后把mysql的驱动jar文件复制到这个目录下。 2.4 服务器配置 主要是配置conf目录下的sqoop.properties和sqoop_bootstrap.properties两个文件 sqoop_bootstrap.properties文件配置config支持类,这里一般使用默认值即可: sqoop.config.provider=org.apache.sqoop.core.PropertiesConfigurationProvider sqoop.properties文件配置比较多,这里按需要配置,我写下我配置的项,其他都使用默认值: org.apache.sqoop.submission.engine.mapreduce.configuration.directory=/opt/bigdata/hadoop/etc/hadoop org.apache.sqoop.security.authentication.type=SIMPLE org.apache.sqoop.security.authentication.handler=org.apache.sqoop.security.authentication.SimpleAuthenticationHandler org.apache.sqoop.security.authentication.anonymous=true 注意:官方文档上只说了配置上面第一项,mapreduce的配置文件路径,但后来运行出现authentication异常,找到sqoop文档描述security部分,发现sqoop2支持hadoop的simple和kerberos两种验证机制。所以配置了一个simple验证,这个异常才消除。 2.5 初始化 元数据存储库需要在第一次启动Sqoop 2服务器之前进行初始化。 使用升级工具初始化存储库: sqoop sqoop2-tool upgrade Setting conf dir: /opt/bigdata/sqoop/bin/../conf Sqoop home directory: /opt/bigdata/sqoop Sqoop tool executor: Version: 1.99.7 Revision: 435d5e61b922a32d7bce567fe5fb1a9c0d9b1bbb Compiled on Tue Jul 19 16:08:27 PDT 2016 by abefine Running tool: class org.apache.sqoop.tools.tool.UpgradeTool 0 [main] INFO org.apache.sqoop.core.PropertiesConfigurationProvider - Starting config file poller thread Tool class org.apache.sqoop.tools.tool.UpgradeTool has finished correctly. 您可以使用验证工具验证是否已正确配置一切: sqoop2-tool verify 此时,我在运行的时候报了一个错误:Tool class org.apache.sqoop.tools.tool.VerifyTool has failed. 在LOGDIR目录下,可以从sqoop里看到错误的日志说是权限的问题, 2017-02-16 01:41:34,373 ERROR [org.apache.sqoop.core.SqoopServer.initialize(SqoopServer.java:67)] Failure in server initialization org.apache.sqoop.common.SqoopException: MAPREDUCE_0002:Failure on submission engine initialization - Invalid Hadoop configuration directory (not a directory or permission issues): /opt/bigdata/hadoop/etc/hadoop at org.apache.sqoop.submission.mapreduce.MapreduceSubmissionEngine.initialize(MapreduceSubmissionEngine.java:97) at org.apache.sqoop.driver.JobManager.initialize(JobManager.java:257) at org.apache.sqoop.core.SqoopServer.initialize(SqoopServer.java:64) at org.apache.sqoop.tools.tool.VerifyTool.runTool(VerifyTool.java:36) at org.apache.sqoop.tools.ToolRunner.main(ToolRunner.java:72) 解决办法: 我这里是把hadoop的配置文件权限设为775,然后再测试 sqoop sqoop2-tool verify Setting conf dir: /opt/bigdata/sqoop/bin/../conf Sqoop home directory: /opt/bigdata/sqoop Sqoop tool executor: Version: 1.99.7 Revision: 435d5e61b922a32d7bce567fe5fb1a9c0d9b1bbb Compiled on Tue Jul 19 16:08:27 PDT 2016 by abefine Running tool: class org.apache.sqoop.tools.tool.VerifyTool 0 [main] INFO org.apache.sqoop.core.SqoopServer - Initializing Sqoop server. 9 [main] INFO org.apache.sqoop.core.PropertiesConfigurationProvider - Starting config file poller thread SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/opt/bigdata/hadoop/share/hadoop/common/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/opt/bigdata/hive/lib/log4j-slf4j-impl-2.4.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. Verification was successful. Tool class org.apache.sqoop.tools.tool.VerifyTool has finished correctly. 2.6 开启服务器 sqoop2的运行模式不再是sqoop1的一个小工具,而加入了服务器,这样只要能访问到mapreduce配置文件及其开发包,sqoop服务器部署在哪里都无所谓,而客户端shell是不需要任何配置的。直接用即可。 开启服务器: bin/sqoop2-server start 这时可以通过JDK中的jps工具查看是否已经正确启动起来,正常情况下会有个SqoopJettyServer的进程,这也可以想象,Sqoop server是基于jetty实现的。 注意:请确保Sqoop2服务器已经启动,并确保Hadoop启动。其中Hadoop不仅要启动hdfs(NameNode、DataNode),还要启动yarn(NodeManager、ResourceManager),当然,一般还会有一个SecondaryNameNode,用于原始NameNode的备援进程。 (2) 客户端配置使用 到这里基本sqoop1.99.7已经配置完毕了,至于客户端就是启动,进行使用 sqoop2-shell 会进入sqoop的交互终端,输入help或者\h可以查看帮助 For information about Sqoop, visit: http://sqoop.apache.org/ Available commands: :exit (:x ) Exit the shell :history (:H ) Display, manage and recall edit-line history help (\h ) Display this help message set (\st ) Configure various client options and settings show (\sh ) Display various objects and configuration options create (\cr ) Create new object in Sqoop repository delete (\d ) Delete existing object in Sqoop repository update (\up ) Update objects in Sqoop repository clone (\cl ) Create new object based on existing one start (\sta) Start job stop (\stp) Stop job status (\stu) Display status of a job enable (\en ) Enable object in Sqoop repository disable (\di ) Disable object in Sqoop repository grant (\g ) Grant access to roles and assign privileges revoke (\r ) Revoke access from roles and remove privileges For help on a specific command type: help command sqoop2的使用 1:sqoop1的使用 对于sqoop1的使用没有专门写过文章,最主要的原因还是使用的少,发现的问题也就相对少了 至于sqoop1的使用可以参考这位网友的example:http://blog.csdn.net/gdmzlhj1/article/details/50483171 2:sqoop2的使用介绍 sqoop2的启动说明 sqoop2客户端支持两种模式运行,shell终端交互模式和批处理模式 终端模式为:sqoop2-shell 批处理模式:sqoop2-shell /path/to/your/script.sqoop Sqoop客户端脚本应包含有效的Sqoop客户端命令,空行和以#开头的表示注释行的行。 忽略注释和空行,解释所有其他行。 示例脚本: # Specify company server set server --host sqoop2.company.net # Executing given job start job --name 1 sqoop2客户端具有类似于其他命令行工具加载资源文件的能力,在执行开始时,Sqoop客户端将检查当前记录的用户的主目录中是否存在文件.sqoop2rc。如果此类文件存在,sqoop2客户端启动的时候将会被加载和解释,他可以用于执行任何批处理兼容命令。例如: # Configure our Sqoop 2 server automatically set server --host sqoop2.company.net # Run in verbose mode by default set option --name verbose --value true Sqoop2的核心概念 由于sqoop2是C-S架构,Sqoop的用户都必须通过sqoop-client类来与服务器交互,sqoop-client提供给用户的有: 连接服务器 搜索connectors 创建Link 创建Job 提交Job 返回Job运行信息等功能 这些基本功能包含了用户在数据迁移的过程中所用到的所有信息。 sqoop2中将数据迁移任务中的相关概念进行细分。将数据迁移任务中的数据源, 数据传输配置, 数据传输任务进行提取抽象。经过抽象分别得到核心概念Connector, Link, Job, Driver。 (1)connector sqoop2中预定一了各种里链接,这些链接是一些配置模板,比如最基本的generic-jdbc-connector,还有hdfs-connector,通过这些模板,可以创建出对应数据源的link,比如我们链接mysql,就是使用JDBC的方式进行链接,这时候就从这个generic-jdbc-connector模板继承出一个link,可以这么理解。 (2)link Connector是和数据源(类型)相关的。对于Link是和具体的任务Job相关的。 针对具体的Job, 例如从MySQL->HDFS 的数据迁移Job。就需要针对该Job创建和数据源MySQL的Link1,和数据目的地MySQL的Link2. Link是和Job相关的, 针对特定的数据源,配置信息。 Link定义了从某一个数据源读出和写入时的配置信息。 (3)job Link定义了从某一个数据源的进行读出和写入时的配置信息。Job是从一个数据源读出, 写入到另外的一个数据源的过程。 所以Job需要由Link(From), Link(To),以及Driver的信息组成。 (4)Dirver 提供了对于Job任务运行的其他信息。比如对Map/Reduce任务的配置。 终端使用介绍 set 函数 说明 server 设置服务器连接 option 设置各种客户端选项 (1) set server 参数 默认值 描述 -h ,–host localhost sqoop server 运行的服务器地址 -p, –port 12000 TCP 端口 -w, –webapp sqoop jetty服务器名称 -u, –url url格式的sqoop服务器 example: sqoop:000> set server --host localhost --port 12000 --weapp sqoop Invalid command invocation: Unknown option encountered: --weapp sqoop:000> set server --host localhost --port 12000 --webapp sqoop Server is set successfully sqoop:000> show version --all client version: Sqoop 1.99.7 source revision 435d5e61b922a32d7bce567fe5fb1a9c0d9b1bbb Compiled by abefine on Tue Jul 19 16:08:27 PDT 2016 0 [main] WARN org.apache.hadoop.util.NativeCodeLoader - Unable to load native-hadoop library for your platform... using builtin-java classes where applicable server version: Sqoop 1.99.7 source revision 435d5e61b922a32d7bce567fe5fb1a9c0d9b1bbb Compiled by abefine on Tue Jul 19 16:08:27 PDT 2016 API versions: [v1] 执行show version –all 之后会正确显示server的版本信息,说明连接OK (2) set option 配置Sqoop客户端相关选项。 此函数具有两个必需的参数名称和值。 Name表示内部属性名称,值保存应设置的新值。 可用选项名称列表如下: 选项名称 默认值 描述 verbose false 如果启用详细模式,客户端将打印附加信息 poll-timeout 10000 服务器轮询超时(以毫秒为单位) example: set option --name verbose --value true set option --name poll-timeout --value 20000 show (1) show server Argument Description -a, –all Show all connection related information (host, port, webapp) -h, –host Show host -p, –port Show port -w, –webapp Show web application name example: sqoop:000> show server -all Server host: localhost Server port: 12000 Server webapp: sqoop (2) show option Argument Description -n, –name Show client option value with given name example: sqoop:000> show option --name verbose Verbose = true sqoop:000> show option --name poll-timeout Poll-timeout = 20000 (3) show version Argument Description -a, –all Show all versions (server, client, api) -c, –client Show client build version -s, –server Show server build version -p, –api Show supported api versions example: sqoop:000> show version -all client version: Sqoop 1.99.7 source revision 435d5e61b922a32d7bce567fe5fb1a9c0d9b1bbb Compiled by abefine on Tue Jul 19 16:08:27 PDT 2016 server version: Sqoop 1.99.7 source revision 435d5e61b922a32d7bce567fe5fb1a9c0d9b1bbb Compiled by abefine on Tue Jul 19 16:08:27 PDT 2016 API versions: [v1] (4) show connector Argument Description -a, –all Show information for all connectors -c, –cid Show information for connector with id example: sqoop:000> show connector +------------------------+---------+------------------------------------------------------------+----------------------+ | Name | Version | Class | Supported Directions | +------------------------+---------+------------------------------------------------------------+----------------------+ | generic-jdbc-connector | 1.99.7 | org.apache.sqoop.connector.jdbc.GenericJdbcConnector | FROM/TO | | kite-connector | 1.99.7 | org.apache.sqoop.connector.kite.KiteConnector | FROM/TO | | oracle-jdbc-connector | 1.99.7 | org.apache.sqoop.connector.jdbc.oracle.OracleJdbcConnector | FROM/TO | | ftp-connector | 1.99.7 | org.apache.sqoop.connector.ftp.FtpConnector | TO | | hdfs-connector | 1.99.7 | org.apache.sqoop.connector.hdfs.HdfsConnector | FROM/TO | | kafka-connector | 1.99.7 | org.apache.sqoop.connector.kafka.KafkaConnector | TO | | sftp-connector | 1.99.7 | org.apache.sqoop.connector.sftp.SftpConnector | TO | +------------------------+---------+------------------------------------------------------------+----------------------+ show connector -all 会输出更详细的信息 (5) show driver sqoop:000> show driver Driver specific options: Persistent id: 8 Job config 1: Name: throttlingConfig Label: Throttling resources Help: Set throttling boundaries to not overload your systems Input 1: Name: throttlingConfig.numExtractors Label: Extractors Help: Number of extractors that Sqoop will use Type: INTEGER Sensitive: false Editable By: ANY Overrides: Input 2: Name: throttlingConfig.numLoaders Label: Loaders Help: Number of loaders that Sqoop will use Type: INTEGER Sensitive: false Editable By: ANY Overrides: Job config 2: Name: jarConfig Label: Classpath configuration Help: Classpath configuration specific to the driver Input 1: Name: jarConfig.extraJars Label: Extra mapper jars Help: A list of the FQDNs of additional jars that are needed to execute the job Type: LIST Sensitive: false Editable By: ANY Overrides: (6) show link Argument Description -a, –all Show all available links -n, –name Show link with name sqoop:000> show link --all or show link --name linkName 0 link(s) to show: (7) show job function Argument Description -a, –all Show all available jobs -n, –name Show job with name example: show job --all or show job --name jobName (8) show submission function Argument Description -j, –job Show available submissions for given job name -d, –detail Show job submissions in full details example: show submission show submission --j jobName show submission --job jobName --detail create 创建新的链接和作业对象。 此命令仅在交互模式下受支持。 当分别创建链接和作业对象时,将要求用户输入来自/到的驱动程序的链接配置和作业配置。 Function Description link Create new link object job Create new job object (1) create link Argument Description -c, –connector Create new link object for connector with name example: create link –connector connectorName or create link -c connectorName sqoop:000> create link --connector hdfs-connector Creating link for connector with name hdfs-connector Please fill following values to create new link object Name: hdfsCN HDFS cluster URI: localhost:9200 Conf directory: /opt/bigdata/hadoop/ Additional configs:: There are currently 0 values in the map: entry# New link was successfully created with validation status OK and name hdfsCN sqoop:000> show link +--------+----------------+---------+ | Name | Connector Name | Enabled | +--------+----------------+---------+ | hdfsCN | hdfs-connector | true | +--------+----------------+---------+ (2) create job Argument Description -f, –from Create new job object with a FROM link with name -t, –to Create new job object with a TO link with name example: create job –from fromLinkName –to toLinkName or create job –f fromLinkName –t toLinkName update 更新命令允许您编辑链接和作业对象。 此命令仅在交互模式下受支持。 (1) udpate link Argument Description -n, –name Update existing link with name example: update link –name linkName (2) update job Argument Description -n, –name Update existing job object with name example: update job –name jobName delete 删除与Sqoop server的连接或作业 (1) delete link Argument Description -n, –name Delete link object with name example: delete link –name linkNam (2) delete job Argument Description -n, –name Delete job object with name example: delete job –name jobName clone Clone命令将从Sqoop服务器加载现有链接或作业对象,并允许用户进行适当的更新,这将导致创建新的链接或作业对象。 批处理模式不支持此命令。 (1) clone link Argument Description -n, –name Clone link object with name example: clone link –name linkName (2) clone job Argument Description -n, –name Clone job object with name example: clone job –name jobName start 启动命令将开始执行现有的Sqoop作业。 开始作业(提交新提交)。 启动已在运行的作业被视为无效操作。 Argument Description -n, –name Start job with name -s, –synchronous Synchoronous job execution example: start job –name jobName start job –name jobName –synchronous stop 停止命令将中断作业执行。 停止正在运行的作业 Argument Description -n, –name Interrupt running job with name example: stop job –name jobName status 状态命令将检索作业的最后状态。 检索给定作业的最后状态。 Argument Description -n, –name Retrieve status for job with name example: status job –name jobName
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 参考资料 1:容器网络那些事儿 2:使用 Docker 容器网络 3:Docker 1.9的新网络特性,以及Overlay详解(1) 4:容器互联 5:docker的网络-Container network interface(CNI)与Container network model(CNM) 写在前边的话 突然发现好久没有更新博客了,像我这种频繁发表博客的人竟然也会放慢了更新的速度,其实不是说自己不去写,不去更新,只是不愿意去将就,去发表一些让别人看了没有多大帮助的文章,作为2017年的开篇博客,我想和你们一起学习下docker容器网络的知识,首先声明,以下内容大部分都是来源网络,按照我对docker网络的理解,整理的一篇文章,一起学习。 docker容器网络概述 1:默认网络 在默认情况下会看到三个网络,它们是Docker Deamon进程创建的。它们实际上分别对应了Docker过去的三种『网络模式』,可以使用docker network ls来查看 master@ubuntu:~$ sudo docker network ls NETWORK ID NAME DRIVER SCOPE 18d934794c74 bridge bridge local f7a7b763f013 host host local 697354257ae3 none null local 这 3 个网络包含在 Docker 实现中。运行一个容器时,可以使用 the –net标志指定您希望在哪个网络上运行该容器。您仍然可以使用这 3 个网络。 bridge 网络表示所有 Docker 安装中都存在的 docker0 网络。除非使用 docker run –net=选项另行指定,否则 Docker 守护进程默认情况下会将容器连接到此网络。在主机上使用 ifconfig命令,可以看到此网桥是主机的网络堆栈的一部分。 none 网络在一个特定于容器的网络堆栈上添加了一个容器。该容器缺少网络接口。 host 网络在主机网络堆栈上添加一个容器。您可以发现,容器中的网络配置与主机相同。 2:自定义网络 当然你也可以自定义网络来更好的隔离容器,Docker 提供了一些默认网络驱动程序来创建这些网络。您可以创建一个新 bridge 网络或覆盖一个网络。也可以创建一个网络插件或远程网络并写入您自己的规范中。您可以创建多个网络。可以将容器添加到多个网络。容器仅能在网络内通信,不能跨网络进行通信。一个连接到两个网络的容器可与每个网络中的成员容器进行通信。当一个容器连接到多个网络时,外部连接通过第一个(按词典顺序)非内部网络提供。 (1):docker network 命令 执行 sudo docker network –help master@ubuntu:~$ sudo docker network --help Usage: docker network COMMAND Manage Docker networks Options: --help Print usage Commands: connect Connect a container to a network create Create a network disconnect Disconnect a container from a network inspect Display detailed information on one or more networks ls List networks rm Remove one or more networks Run 'docker network COMMAND --help' for more information on a command. (2):创建test-network网络 执行命令: sudo docker network create test-network 查看: sudo docker network ls master@ubuntu:~$ sudo docker network ls NETWORK ID NAME DRIVER SCOPE 18d934794c74 bridge bridge local f7a7b763f013 host host local 697354257ae3 none null local c4f6d347c8b4 test-network bridge local 查看自己创建的网络的信息 master@ubuntu:~$ sudo docker network inspect test-network [ { "Name": "test-network", "Id": "c4f6d347c8b47471b97e1b5621dd2e90aff303bb7db632db86b0bbec6ffb91d4", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1/16" } ] }, "Internal": false, "Containers": {}, "Options": {}, "Labels": {} } ] 另外,还可以采用其他一些选项,比如 –subnet、–gateway和 –ip-range。 (3):启动容器连接到test-network sudo docker run -itd –name=test –net=test-network lt:1.0 /bin/bash master@ubuntu:~$ sudo docker run -itd --name=test --net=test-network lt:1.0 /bin/bash 09a9d7a9c37d691e0fc0f7cfdf3c9470b77f410592f9bf624fe90bff2b17e315 再次查看信息,可以看到挂载的容器 master@ubuntu:~$ sudo docker network inspect test-network [ { "Name": "test-network", "Id": "c4f6d347c8b47471b97e1b5621dd2e90aff303bb7db632db86b0bbec6ffb91d4", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1/16" } ] }, "Internal": false, "Containers": { "09a9d7a9c37d691e0fc0f7cfdf3c9470b77f410592f9bf624fe90bff2b17e315": { "Name": "test", "EndpointID": "7d1d57418a8ebde2d5404f37e27de68be41a917979fd74f394bc5fccc2601e08", "MacAddress": "02:42:ac:12:00:02", "IPv4Address": "172.18.0.2/16", "IPv6Address": "" } }, "Options": {}, "Labels": {} } ] 当然也可以动态的将容器挂载到某个网络上 master@ubuntu:~$ sudo docker run -itd --name=test1 lt:1.0 /bin/bash b2aba703c5180819542d26e7bda784774ca87b896e8df612dd1b727218dde334 master@ubuntu:~$ sudo docker network connect test-network test1 master@ubuntu:~$ sudo docker network inspect test-network [ { "Name": "test-network", "Id": "c4f6d347c8b47471b97e1b5621dd2e90aff303bb7db632db86b0bbec6ffb91d4", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1/16" } ] }, "Internal": false, "Containers": { "09a9d7a9c37d691e0fc0f7cfdf3c9470b77f410592f9bf624fe90bff2b17e315": { "Name": "test", "EndpointID": "7d1d57418a8ebde2d5404f37e27de68be41a917979fd74f394bc5fccc2601e08", "MacAddress": "02:42:ac:12:00:02", "IPv4Address": "172.18.0.2/16", "IPv6Address": "" }, "b2aba703c5180819542d26e7bda784774ca87b896e8df612dd1b727218dde334": { "Name": "test1", "EndpointID": "5d18b876b62312698d4ce253e33b52552c9a3b1237bed90ec565d4cbd9f5710d", "MacAddress": "02:42:ac:12:00:03", "IPv4Address": "172.18.0.3/16", "IPv6Address": "" } }, "Options": {}, "Labels": {} } ] “石器时代”的容器网络模型 目前对于刚起步接触docker容器的筒子们(当然也包括我),大部分使用网络的方式应该是这样的,把需要暴漏的端口做端口映射(docker run -itd -p 81:80 …),例如一个主机内有很多Apache容器,每一个Apache要往外抛80的端口,那我怎么办?我需要针对第一个容器和主机80端口做映射,第二个和主机81端口做映射,依此类推,到最后发现非常混乱,没办法管理。这样的容器网络模型对于企业来说是基本没办法被采用。 这是石器时代网络模型,它是Docker1.9之前的容器网络,实现方式是只针对单台主机进行IPAM管理,所有主机上的容器都会连接到主机内部的一个Linux Bridge,叫Docker0,主机的IP它默认会分配172.17网段中的一个IP,因为有Docker0,所以在一个主机上的容器可以实现互联互通。但是因为IP分配的范围是基于单主机的,所以你会发现在其他主机上,也会出现完全相同的IP地址。很明显,这两个地址肯定没办法直接通信。为了解决这个问题,在石器时代我们会用端口映射,实际上就是NAT的方法。比如说我有一个应用,它有Web和Mysql,分别在不同的主机上,Web需要去访问Mysql,我们会把这个Mysql的3306端口映射到主机上的3306这个端口,然后这个服务实际上是去访问主机IP 10.10.10.3 的3306端口,这是过去的石器时代的一个做法。 总结一下它的典型技术特征:基于单主机的IPAM;主机之内容器通讯实际上通过一个docker0的Linux Bridge;如果服务想要暴露到外部的话需要做NAT,会导致端口争抢非常严重;当然它有一个好处,对大网IP消耗比较少。 docker容器的单主机通信 docker容器的单主机通信使用的是容器互联技术,即 –link,映射网络端口不是吧container彼此连接起来的唯一方法。Docker的linking系统允许你吧多个 container连接起来, 让他们彼此交互信息。Docker的linking会创建一种父子级别的关系。 父container可以看到他的子container提供的信息。 创建一个数据库容器: sudo docker run -d –name db training/postgres 然后创建一个新的 web 容器,并将它连接到 db 容器 sudo docker run -d -P –name web –link db:db training/webapp python app.py 此时,db 容器和 web 容器建立互联关系。 –link 参数的格式为 –link name:alias,其中 name 是要链接的容器的名称,alias 是这个连接的别名。 docker容器的跨主机通信 早期大家的跨主机通信方案主要有以下几种: 容器使用host模式:容器直接使用宿主机的网络,这样天生就可以支持跨主机通信。虽然可以解决跨主机通信问题,但这种方式应用场景很有限,容易出现端口冲突,也无法做到隔离网络环境,一个容器崩溃很可能引起整个宿主机的崩溃。 端口绑定:通过绑定容器端口到宿主机端口,跨主机通信时,使用主机IP+端口的方式访问容器中的服务。显而易见,这种方式仅能支持网络栈的四层及以上的应用,并且容器与宿主机紧耦合,很难灵活的处理,可扩展性不佳。 docker外定制容器网络:在容器通过docker创建完成后,然后再通过修改容器的网络命名空间来定义容器网络。典型的就是很久以前的pipework,容器以none模式创建,pipework通过进入容器的网络命名空间为容器重新配置网络,这样容器网络可以是静态IP、vxlan网络等各种方式,非常灵活,容器启动的一段时间内会没有IP,明显无法在大规模场景下使用,只能在实验室中测试使用。 第三方SDN定义容器网络:使用Open vSwitch或Flannel等第三方SDN工具,为容器构建可以跨主机通信的网络环境。这些方案一般要求各个主机上的docker0网桥的cidr不同,以避免出现IP冲突的问题,限制了容器在宿主机上的可获取IP范围。并且在容器需要对集群外提供服务时,需要比较复杂的配置,对部署实施人员的网络技能要求比较高。 上面这些方案有各种各样的缺陷,同时也因为跨主机通信的迫切需求,docker 1.9版本时,官方提出了基于vxlan的overlay网络实现,原生支持容器的跨主机通信。同时,还支持通过libnetwork的plugin机制扩展各种第三方实现,从而以不同的方式实现跨主机通信。就目前社区比较流行的方案来说,跨主机通信的基本实现方案有以下几种: 基于隧道的overlay网络:按隧道类型来说,不同的公司或者组织有不同的实现方案。docker原生的overlay网络就是基于vxlan隧道实现的。ovn则需要通过geneve或者stt隧道来实现的。flannel最新版本也开始默认基于vxlan实现overlay网络。 基于包封装的overlay网络:基于UDP封装等数据包包装方式,在docker集群上实现跨主机网络。典型实现方案有weave、flannel的早期版本。 基于三层实现SDN网络:基于三层协议和路由,直接在三层上实现跨主机网络,并且通过iptables实现网络的安全隔离。典型的方案为Project Calico。同时对不支持三层路由的环境,Project Calico还提供了基于IPIP封装的跨主机网络实现。 docker容器的CNI模型和CNM模型 目前围绕着docker的网络,目前有两种比较主流的声音,docker主导的Container network model(CNM)和社区主导的Container network interface(CNI)。 1:CNI (1) 概述 Container Networking Interface(CNI)提供了一种linux的应用容器的插件化网络解决方案。最初是由rkt Networking Proposal发展而来。也就是说,CNI本身并不完全针对docker的容器,而是提供一种普适的容器网络解决方案。因此他的模型只涉及两个概念: 容器(container) : 容器是拥有独立linux网络命名空间的独立单元。比如rkt/docker创建出来的容器。 这里很关键的是容器需要拥有自己的linux网络命名空间。这也是加入网络的必要条件。 网络(network): 网络指代了可以相互联系的一组实体。这些实体拥有各自独立唯一的ip。这些实体可以是容器,是物理机,或者其他网络设备(比如路由器)等。 (2) 接口及实现 CNI的接口设计的非常简洁,只有两个接口ADD/DELETE。 以 ADD接口为例 Add container to network 参数主要包括: Version. CNI版本号 Container ID. 这是一个可选的参数,提供容器的id Network namespace path. 容器的命名空间的路径,比如 /proc/[pid]/ns/net。 Network configuration. 这是一个json的文档,具体可以参看network-configuration Extra arguments. 其他参数 Name of the interface inside the container. 容器内的网卡名 返回值: IPs assigned to the interface. ipv4或者ipv6地址 DNS information. DNS相关信息 2:CNM 相较于CNI,CNM是docker公司力推的网络模型。其主要模型如下图: Sandbox Sandbox包含了一个容器的网络栈。包括了管理容器的网卡,路由表以及DNS设置。一种Sandbox的实现是通过linux的网络命名空间,一个FreeBSD Jail 或者其他类似的概念。一个Sandbox可以包含多个endpoints。 Endpoint 一个endpoint将Sandbox连接到network上。一个endpoint的实现可以通过veth pair,Open vSwitch internal port 或者其他的方式。一个endpoint只能属于一个network,也只能属于一个sandbox。 Network 一个network是一组可以相互通信的endpoints组成。一个network的实现可以是linux bridge,vlan或者其他方式。一个网络中可以包含很多个endpoints。 接口 CNM的接口相较于CNI模型,较为复杂。其提供了remote plugin的方式,进行插件化开发。remote plugin相较与CNI的命令行,更加友好一些,是通过http请求进行的。remote plugin监听一个指定的端口,docker daemon直接通过这个端口与remote plugin进行交互。 鉴于CNM的接口较多,这里就不一一展开解释了。这里主要介绍下在进行docker的操作中,docker daemon是如何同CNM插件繁盛交互。 调用过程 Create Network 这一系列调用发生在使用docker network create的过程中。 /IpamDriver.RequestPool: 创建subnetpool用于分配IP /IpamDriver.RequestAddress: 为gateway获取IP /NetworkDriver.CreateNetwork: 创建neutron network和subnet Create Container 这一系列调用发生在使用docker run,创建一个contain的过程中。当然,也可以通过docker network connect触发。 /IpamDriver.RequestAddress: 为容器获取IP /NetworkDriver.CreateEndpoint: 创建neutron port /NetworkDriver.Join: 为容器和port绑定 /NetworkDriver.ProgramExternalConnectivity: /NetworkDriver.EndpointOperInfo Delete Container 这一系列调用发生在使用docker delete,删除一个contain的过程中。当然,也可以通过docker network disconnect触发。 /NetworkDriver.RevokeExternalConnectivity /NetworkDriver.Leave: 容器和port解绑 /NetworkDriver.DeleteEndpoint /IpamDriver.ReleaseAddress: 删除port并释放IP Delete Network 这一系列调用发生在使用docker network delete的过程中。 /NetworkDriver.DeleteNetwork: 删除network /IpamDriver.ReleaseAddress: 释放gateway的IP /IpamDriver.ReleasePool: 删除subnetpool 3:CNI与CNM的转化 CNI和CNM并非是完全不可调和的两个模型。二者可以进行转化。比如calico项目就是直接支持两种接口模型。 从模型中来看,CNI中的container应与CNM的sandbox概念一致,CNI中的network与CNM中的network一致。在CNI中,CNM中的endpoint被隐含在了ADD/DELETE的操作中。CNI接口更加简洁,把更多的工作托管给了容器的管理者和网络的管理者。从这个角度来说,CNI的ADD/DELETE接口其实只是实现了docker network connect和docker network disconnect两个命令。 kubernetes/contrib项目提供了一种从CNI向CNM转化的过程。其中原理很简单,就是直接通过shell脚本执行了docker network connect和docker network disconnect命令,来实现从CNI到CNM的转化。
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 玩linux的人都知道他有个syslog这个东西,新版的linux操作系统中升级为了rsyslog,具体的rsyslog这里就不多说了,感兴趣的可以参考之前的一篇文章:http://www.voidcn.com/blog/gamer_gyt/article/p-6225856.html,今天我们聊一聊怎么自定义rsyslog的日志输出格式 场景介绍 在做日志分析的过程中,我们往往要记录每条日志产生的时间,当然这个对于绝大部分的软件或者系统都是支持的,对于linux的rsyslog也不例外,但是Linux的rsyslog产生的日志这是携带了月-天 时:分:秒,并没有年份,假设在2017年,给你一份16年或者15年的log日志,让你去分析,你怎么知道是哪个年份呢?因此,接下来我们要讨论的就是这个问题 环境说明 操作系统 :ubuntu 16.04 ELK:elasticsearch 2.4.1 / logstash 2.4.0 / kibana 4.6.1 以下介绍以ubuntu16.04的auth.log为例,不同的操作系统或者版本会有稍微的差异 文件说明 /etc/rsyslog.conf :主配置文件 /etc/rsyslog.d/*.conf :辅助配置文件 /lib64/rsyslog/*.so : rsyslog的功能扩展模块位置,I开始的文件表示,从那收集, O开始的文件表示,将日志记录到那 配置说明 其实单纯的在rsyslog中添加年份这个熟悉很简单,但是为了便于我们的logstash进行解析,所以这里进行了自定义template文件,后边会结合不考虑logstash的解析做出说明。 方法1:修改原有的rsyslog文件 自定义template控制输出的日志格式,先是修改/etc/rsyslog.d/50-default.conf文件,控制写入auth.log的日志格式 $template myFormat,"%timegenerated:1:19:date-rfc3339%%timegenerated:27:32:date-rfc3339% %HOSTNAME% %syslogtag%%msg%\n" $ActionFileDefaultTemplate myFormat 同时修改 auth,authpriv.* /var/log/auth.log 为 auth,authpriv.* /var/log/auth.log;myFormat date-rfc3339 :一种日期格式 >>> import time >>> timestamp = time.time() >>> print timestamp 1483719436.8 >>> import datetime >>> d = datetime.datetime.fromtimestamp(timestamp) >>> print d 2017-01-07 00:17:16.799617 >>> from rfc3339 import rfc3339 >>> rfc3339(timestamp) '2017-01-07T00:17:16+08:00' >>> rfc3339(d) '2017-01-07T00:17:16+08:00' >>> rfc3339(d, utc=True) '2017-01-07T08:17:16Z' >>> rfc3339(d, utc=True, use_system_timezone=False) '2017-01-07T00:17:16Z' :1:19 :进行切片,类似于python中的分片 >>> str_a="thinkgamer" >>> str_a[0:5] 'think' 这个时候我们便可以进行测试查看auth.log的日志格式了,首先重启rsyslog服务 sudo service rsyslog restart ok,执行ssh localhost进行测试,产生的日志如下图 上边我们说了50-default.conf是控制输出到auth.log中的日志格式,那么如何将修改好的log格式也应用到rsyslog服务上呢,这个时候就需要对rsyslog.conf做配置了 vim /etc/rsyslog.conf 加入 $template myFormat,"%timegenerated:1:19:date-rfc3339%%timegenerated:27:32:date-rfc3339% %HOSTNAME% %syslogtag%%msg%\n" $ActionFileDefaultTemplate myFormat 该文件中有一行为: $ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat 其意思是使用RSYSLOG_TraditionalFileFormat这个模板格式的日志输出,这里先忽略掉,下面会进行说明,这里传输的几种方式 1:简单点的 *.* @localhost:5000 *.* @@localhost:5000 这种情况要修改 上边那行中的RSYSLOG_TraditionalFileFormat为我们自定义的template name :myFormat 这种情况是对应所有的日志都采用此template格式 2:在配置向外传输时携带上template 这种情况夏,不需要改动 $ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat 配置方式为: *.* @localhost:5000;myFormat *.* @@localhost:5000;myFormat 3:自定义使用的文件 *.*;syslog;auth action( type="omfwd" Target="localhost" Port="5000" Protocol="tcp" template="mytem" ) type:linux自带的 Target:ip,本地的话就是localhost port:端口 Protocol:使用的协议方式,tcp或者udp template:自己定义的template_name 这个时候便可以结合logstash进行解析 es中的监听为: input { tcp{ port => 5000 type => syslog } udp{ port => 5000 type => syslog } } logstash的解析写法为: filter{ if [type] == 'syslog' { grok { match => { 'message' => '%{SYSLOGTIMESTAMP:syslog_timestamp} %{HOSTNAME:hostname} %{WORD:program}%{GREEDYDATA:msgsplit}' } } date { match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ] target => "syslog_timestamp" timezone => "UTC" } } 方法2:将需要修改的写进一个conf文件 我们也可以自己另外写一个rsyslog.conf,比如my_rsyslog.conf ,然后将其放进 /etc/rsyslog.d中,重启rsyslog服务即可,因为在rsyslog.conf有个include $IncludeConfig /etc/rsyslog.d/*.conf 比如说我们将需要修改的写进一个my_syslog.conf $template myFormat,"%timegenerated:1:19:date-rfc3339%%timegenerated:27:32:date-rfc3339% %HOSTNAME% %syslogtag%%msg%\n" $ActionFileDefaultTemplate myFormat *.*;syslog;auth action( type="omfwd" Target="localhost" Port="5000" Protocol="tcp" template="mytem" ) 放在/etc/rsyslog.d/ 目录下,然后重启rsyslog即可 看了上边有哪点不懂的,可以留言或者加我微信。
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 前言 由于业务关系,最近一段时间一直在关注入侵检测技术方面的知识,经过了最近一天的学习与调研,在大体上还是有了一定的了解与研究,下面就分享一下我得学习成果,当然大部分知识都是从网上进行收集和整理的,当然加上了自己的一些想法 本文永久地址:http://blog.csdn.net/gamer_gyt/article/details/53876659 什么是入侵检测 入侵检测(Intrusion Detection )是对入侵行为的检测。它通过收集和分析计算机网络或计算机系统中若于关键点的信息,检查网络或系统中是否存在违反安全策略的行为和被攻击的迹象。入侵检测作为一种积极主动的安全防护技术,提供了对内部攻击、外部攻击和误操作的实时保护,在网络系统受到危害之前拦截和响应入侵入侵检测技术虽然也能够对网络攻击进行识另拼作出反应,但其侧重点还是在于发现,而不能代替防火墙系统执行整个网络的访问控制策略。 入侵检测系统(intrusion Detection System ,IDS)是对计算机和网络资源的恶意使用为进行识别的系统;它的目的是监测和发现可能存在的攻击行为,包括来自系统外部的入侵行为和来自内部用户的非授权行为,并且采取相应的防护手段。 入侵检测系统的分类 一:按检测分析方法分类 1:异常检测 基于异常的入侵检测方法主要来源于这样的思想,任何人的正常行为都是有一定的规律的,并且可以通过分析这些行为的日志信息型总结出这些规律,而入侵和滥用行为规则通常和正常的行为存在严重的差异,通过检查这些差异就可以判断是否为入侵。总之,一场检测基于这样的假设和前提:用户活动是有规律的,而且这种规律是可以通过数据有效的描述和反映;入侵时异常活动的子集和用户的正常活动有着可以描述的明显的区别。 异常监测系统首先经过一个学习阶段,总结正常的行为的轮廓成为自己的先验知识,系统运行时将信息采集子系统获得并预处理后的数据与正常行为模式比较,如果差异不超出预设阀值,则认为是正常的,出现较大差异即超过阀值则判定为入侵。 异常检测系统有如下特点: 1):检测的效率取决于用户轮过的完备性和监控的频率,因为不需要对每种入侵行为进行定义,而能有效检测未知的入侵,因此也称为一个研究热点 2):系统能针对用户行为的改变进行自我调整和优化,但随着检测模型的逐步精确,异常检测会消耗更多的系统资源 2:误用检测 又称为基于特征的检测,基于误用的入侵检测系统通过使用某种模式或者信号标示表示攻击,进而发现同类型的攻击,其实现过程是:先收集非正常操作的行为特征,系统就认为这种行为是入侵,系统处理过程如同: 这种方法可以检测到许多甚至是全部已知的攻击行为,如果入侵特征与正常的用户行为能匹配,则系统会发生误报,如果没有特征能与某种新的攻击行为匹配,则系统会发生漏报。 特点:采用特征匹配模式能明显降低错报率,但漏报率随之增加,攻击特征的细微变化,会使得滥用检测无能为力。 二:按数据源分为 1:基于主机的入侵检测 基于主机的入侵检测是入侵检测的最初期形式,这种入侵检测系统通常运行在被检测的主机或者服务器上,实时检测检测系统的运行,通常从主机的审计记录和日志文件中获得所需的主要数据源,并辅之以主机上的其他信息,在此基础上完成检测攻击行为的任务。特别的,从主机入侵检测技术中还可以单独分离出基于应用的入侵检测模型,这是特别针对于某个特定任务的应用程序而设计的入侵检测技术,采用的输入数据源是应用程序的日志信息。 基于主机的入侵检测悉尼型来源主要包括: 1):系统信息,几乎所有的操作系统都提供一组命令,获得本机当前激活的进程的状态信息,他们直接检查内核程序的内存信息。 2):记账,通常指由操作系统或操作员所执行的特定操作,记录计算机资源的使用情况,例如CPU占用时间,内存,硬盘,网络使用情况。在计算机未普及之前,记账是为了向用户收费的。 3):系统日志,可分为操作系统日志和应用程序日志两部分。操作系统日志从不同方面记录了系统中发生的事情,对于入侵检测而言,具备重要的价值,当一个进程终止时,系统内核为每个进程在进程日志文件中写入一条记录。 4):C2安全审计,记录所有可能与安全性有关的发生在系统上的事情。 基于主机的入侵检测能够较为准确的检测到发生在主机系统高层的复杂攻击行为,其中,许多发生在应用进程级别的攻击行为是无法依靠基于网络的入侵检测来完成的,基于主机的入侵检测系统巨头检测效率高,分析代价小,分析速度快的特点,能够迅速并准确的定为入侵者,并可以结合操作系统和应用程序的行为特征对入侵进行进一步的分析,响应。比如,一旦检测到有入侵行为,我们可以立即使该用户的账号失效,用户的进程中断。他可以帮助发现基于网络的入侵检测无法检测的加密攻击。基于主机的入侵检测系统尤其对于独立的服务器及应用构造简单,易于理解,也只有这种检测方式能检测出通过控制台的入侵活动。目前许多是基于主机日志分析的。 同时,基于主机的入侵检测系统也有若干显而易见的缺点,由于他一定程度上依赖于特定的操作系统平台,管理困难,必须按照每一台机器的环境配置管理。同时主机的日志提供的信息有限,有的入侵手段和途径不会在日志中有所反映,日志系统对网络层的入侵行为无能为力。在数据提取的实时性,充分性,可靠性方面基于主机日志的入侵检测系统不如基于网络的入侵检测系统。他通常无法对网络环境下发生的大量攻击行为作出及时的反应,他在所保护主机上运行,这也会影响宿主机的运行性能。 2:基于网络的入侵检测系统 通过监听网络中的数据包,既抓包技术来获取必要的数据来源,并通过协议分析,特征匹配,统计分析等手段当前发生的攻击行为。 基于网络的入侵检测的优点是:一个安装在网络合适位置NIDS系统可以监视一个很大范围的网络,他的运行丝毫不影响主机或者服务器的运行效率,因为基于网络的入侵检测系统通常采取独立主机和被动监听的工作模式,他对网络的性能影响也很小。NIDS能够实时监控网络中的数据流量,并发现潜在的攻击行为和作为迅速的响应,而使攻击者难以发现自己已被监视,另外,他的分析对象是网络协议,一般没有移植性的问题。 同事基于网络的入侵检测系统的主要问题是监视数据量过于庞大并且他不结合操作系统特征来对网络行为进行准确的判断,在网络通讯的高峰时刻,难以检查所有数据包;如果网络数据被加密,NIDS就不能扫描协议或内容NIDS不能判断一个攻击是否已经成功,对于渐进式,合作式的攻击难以防范。 常用的入侵检测技术 1:基于统计分析技术的入侵检测 他试图建立一个对应”正常活动”的特征原型,然后把与所建立的特征原型中差别”很大”的所有行为都标志为异常。显而易见,当入侵集合与异常活动集合不完全相等时,一定会存在漏 报或者误报的问题,为了使漏报和误报的概率较为符合实际需要,必须选择一个区分异常事件的阀值,而调整和更新某些系统特征度量值的方法非常复杂,开销巨大,在实际情况下,试图用逻辑方法明确划分正常行为和异常行为两个集合非常困难,统计手段的主要优点是可以自适应学习用户的行为,主要问题是其可能被入侵者逐渐训练以致最终将入侵事件误认为是正常,并且阀值设置不会当导致大比例的误报与漏报,此外,由于统计量度对事件顺序的不敏感性,事件间的关系会漏掉。 2:基于模式预测异常检测 基于模式预测异常检测方法的假设条件是:事件序列不是随机的,而是遵循可辨别的模式,这种检测方法的特点是考虑了事件的序列和相互关系。而基于时间的推理方法则利用时间规则识别用户行为正常模式的特征,通过归纳学习产生这些规则集,能动态的修改系统中的规则,使之具有高的预测性,准确性和可信度。如果规则大部分时间是正确的,并能够成功的运用预测所观察到的数据,那么规则就具有高的可信度,根据观察到用户的行为,归纳产生出一套规则集来构建用户的轮廓框架,如果观测到的事件序列匹配规则的左边,而后续事件显著的背离根据规则预测到的事件,那么系统就可以检测出这种偏离,这就表明用户操作是异常。如果能预测出不着呢刚才的后继事件的片段,则一定程度上断定用户行为的异常性,这种方法的主要优点是: 1):能较好地处理变化多样的用户行为,具有很强的时序模式。 2):能够集中考察少数几个相关的安全事件,而不是关注可疑的整个登录会话过程 3):对发现检测系统遭受攻击,具有良好的灵敏度,因为根据规则的蕴含语义,在系统学习阶段,能够更容易的辨别出欺骗者训练系统的企图 预测模式生成技术的问题在于未被这些规则描述的入侵会被漏检 3:基于神经网络技术的入侵检测 神经网络用给定的n个动作训练神经网络去预测用户的下一步行为。训练结束之后,神经网络使用已出现在网中的用户特征匹配实际的用户行为,标志统计差异较大的事件为异常或者非法。使用神经网络的优点是可以很好的处理噪声数据,因为他只与用户行为相关,而不依赖于任何底层数据特性的统计,但同样有入侵者能够在其徐诶阶段训练网络的问题。 4:基于机器学习异常检测 这种异常检测方法通过机器学习实现入侵检测,其主要的方法有死记硬背式、监督、学习、归纳学习、类比学习等。 5:基于数据挖掘异常检测 数据挖掘,也称知识发现,通常记录系统运行日志得数据库都非常大,如何从大量数据中“浓缩”出一个值或者一组值来表示对象得概貌,并以此进行行为的异常分析和检测,这就是数据挖掘技术在入侵检测系统的应用,数据挖掘中一般会用到数据聚类技术。 6:专家系统 用专家系统对入侵进行检测,经常时针对具有明显特征的入侵行为,即所谓的规则,即时知识,专家系统的建立依赖于知识库的完备性,知识库的完备性又取决于审计记录的完备性和实时性。 基于专家系统无用入侵检测方法是通过将安全专家的知识表示城IF-THEN规则形成专家知识库,然后,运用推理算法进行入侵检测,编码规则说明攻击的必需条件作为IF的组成部分,当规则的左边的全部条件都满足时,规则的右边的动作才会执行,入侵检测专家系统应用的实际问题时要处理大量的数据和依赖于审计跟踪的次序,其推理方式主要又以下两种: 1):根据给定的数据,应用符号推理出入侵的发生情况,需要解决的主要问题时处理序列数据和知识库的维护,不足之处就是只能检测已知。 2):根绝其他的入侵证据,进行不确定性推理,这种推理的局限性就是推理证据的不精确和专家知识的不精确。 入侵检测的技术关键 入侵检测技术对于网络安全方面来说是一项重要的技术而提高入侵检查的一项根本方法就是提高模式匹配效率,提高模式匹配的效率也就是等于提升了网络安全。 一:模式匹配技术 1:模式匹配 入侵检测系统对重要的网段进行监控,对网段中没个数据包进行模式匹配和分析。如果数据包内容和入侵检测系统规律相符,就会发出警报,并切断网络,由此可见模式匹配是影响入侵检测的关键技术。 模式匹配定义为:设有给定的连哥哥串T和P,则在T中寻找P的子串的经过成为模式匹配。T称为正文,P称为模式,通常T的长度远远大于P的长度,若在T中找到等于P的子串,则匹配成功,否则匹配失败。 2:模式匹配的原理 在入侵检测中,模式匹配可以理解为:给定入侵规则库中的一个特定的模式字符串P,在网络数据包T中进行查找,确定P是否在T中出现。 3:模式匹配的规则 网络入侵检测以网络中采集的数据包为数据源,使用模式匹配方法对数据包进行检测从而发现网络中可能存在的入侵事件,其中对数据包的检测就是要在网络数据包中检测是否存在可以代表入侵行为或者入侵企图的一些字符串,即查找出某些入侵规则中规则选项中所标识的字符串,由于规则数较多,模式匹配过程是入侵检测系统中时间小号最大的环节之一。如果没有高效的模式匹配算法作为保障,检测过程中就会产生超时溢出错误,此时为了保证正常工作状态,系统将主动的丢弃一些数据包,形成漏检。 所以,一个好的高效的模式皮匹配算法对入侵检测效率的提升至关重要。 二:模式匹配算法 参考:http://dsqiu.iteye.com/blog/1700312 案例说明 1:介绍 以企业入侵日志为例,假设攻击者要攻击某个企业,那么必需使用进入到该企业的网络,已知该企业的网络分为内部用户和访客,每次登陆都会产生一系列的日志,那么如何根据这些wlan的访问日志来进行登陆用户的异常行为检测呢? 2:分析 根据访问日志,我们并不能确定使用网络的用户哪个是进行入侵的,即我们没有一个明确的结果来判断入侵者和非入侵者,那么这里我们可以采用的入侵检测技术便可以是:基于统计的入侵检测技术 3:实现 首先我们可以对访问者进行比例划分,例如9:1,利用90%的数据进行构建用户肖像,统计分析出用户肖像数据,继而利用10%的数据进行数据测试,主要依据的便是90%数据构建的用户模型。统计处测试用户产生的异常值。并可以根据实际情况设置一个阀值,来判定是否是入侵用户。 其次,如果我们能明确知道哪些是入侵者,即数据产生方已经积累了一定的入侵用户,那么我们可以针对入侵者的入侵手段进行建模,比如说,这些入侵者都喜欢在凌晨3点,进行网络认证,且他们在线的时间较短,那么我们便可以收集这两个特征作为入侵者的特征,继而针对网络认证用户进行判断。 当然我们也可以结合其他的一些辅助手段进行异常用户的检测,比如说黑名单,可以根据入侵者的行为模式构建哥规则列表,即符合该规则库中的任意一条规则几位入侵者。 文章推荐:以企业入侵检测日志分析为场景谈大数据安全 个人微信公众号,欢迎关注
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 前言 这几天突然萌生了一个想法:自己写个python程序,定时查询一下自己的CSDN博客的一些信息,并以邮件的形式推送给自己,那么废话不多说,现在已经把基本功能给实现了,每天可以定时发送访问量,积分,排名,同时会比较前一天的数据,形成对比,呈现给自己,这样就能一目了然的看到自己每天博客的PV增加数目了,如果你感兴趣,拿去玩吧 本文永久地址:http://blog.csdn.net/gamer_gyt/article/details/53823842 内容包括 由于是第一版,实现的功能比较简单,只是发送自己博客的PV,积分,排名,原创文章数,转载文章数,翻译文章数,评论条数和同比前一天的增长数量 后续计划:封装成web的形式,融合自己之前做的一个博客统计分析系统,使之前的产品更加完善,而且会逐步封装成谷歌浏览的插件形式,供大家使用方便 博客统计分析系统git地址:https://github.com/Thinkgamer/BlogAnalysic 代码结构 说明: 这里并没有采用数据库存储,而是直接写进csv文件,后续会更新 这里对每篇文章的数据也做了解析,只不过暂时注释了,没有进行推送,因为还没想好合适的方式 涉及的技术 目前涉及: python 爬虫 python 发送email 主要代码 别的代码都比较烂,这里主要说一下python发送email示例 #-*-coding:utf-8-*- import smtplib from email.mime.text import MIMEText from email.header import Header def send_email(): message = "test" # 第三方 SMTP 服务,我这里设置的是网易163邮箱 mail_host = "smtp.163.com" # 设置服务器 mail_user = "thinkgamer@163.com" # 用户名 mail_pass = "xxxxxxxxxxx" # 密码 sender = "thinkgamer@163.com" receive="thinkgamer@163.com" # 三个参数:第一个为文本内容,第二个 plain 设置文本格式,第三个 utf-8 设置编码 message = MIMEText(message, 'plain', 'utf-8') message['From'] = Header(sender, 'utf-8') message['To'] = Header(receive, 'utf-8') subject = 'CSDN博客访问信息统计' message['Subject'] = Header(subject, 'utf-8') try: smtpObj = smtplib.SMTP() smtpObj.connect(mail_host, 25) # 25 为 SMTP 端口号 smtpObj.login(mail_user,mail_pass) smtpObj.sendmail(sender, receive, message.as_string()) print("邮件发送成功") except smtplib.SMTPException as e: print("Error: 无法发送邮件") 说明:我这里的发送邮箱和接收邮箱都是我的163邮箱,原因是我刚开始测试的是QQ邮箱接受,但是万恶的网易把这个邮件当成垃圾邮件,发送不出去,会提示: (554, b'DT:SPM 163 smtp9,DcCowABXdex2kFtYogSVEQ.... 提示的url只指向:http://help.163.com/09/1224/17/5RAJ4LMH00753VB8.html 谷歌了好久,也没有找到解决办法,于是乎就换成163邮箱来接受了,当然如果你看了这篇文章,你也解决了这个办法,那么请留言告诉我,谢谢 效果图 来一张我邮箱接受到的邮件的效果图吧 如果你对这个代码感兴趣的话,或者你也想接受这个信息推送的话,留下的163邮箱吧,因为别的我怕发不出去(代码地址:点击查看)~~~~ 补充内容:python发送短信 使用互亿无线的短信接口发送短信,只不过他是收费的,但是有30条的测试 注册一个账号,将代码中的用户名和密码换成你自己的即可,另外我这里使用的是python3.4,官网上给的python样例代码时2.7,直接运行不好使,需要修改一些小地方,代码如下: #-*- coding:utf-8 -*- import http.client import urllib.request host = "106.ihuyi.com" sms_send_uri = "/webservice/sms.php?method=Submit" #用户名是登录ihuyi.com账号名(例如:cf_demo123) account = "xxxxx" #密码 查看密码请登录用户中心->验证码、通知短信->帐户及签名设置->APIKEY password = "xxxxxx" def send_sms(text, mobile): params = urllib.parse.urlencode({'account': account, 'password' : password, 'content': text, 'mobile':mobile,'format':'json' }) headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} conn = http.client.HTTPConnection(host, port=80, timeout=30) conn.request("POST", sms_send_uri, params, headers) response = conn.getresponse() response_str = response.read() conn.close() return response_str if __name__ == '__main__': mobile = "171xxxx1234" text = "您的验证码是:121254。请不要把验证码泄露给其他人。" print(send_sms(text, mobile)) 手机收到的短信如下: 个人微信公众号,欢迎关注
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 如果说岁月是年轮,我们便是推行者,如果说成长是一场华丽的蜕变,我们便是领舞者。一路走来,太多不易,告别青春的年少轻狂,我们成了岁月里被磨平的棱角,静静的守在属于自己的一亩三分地。 2016-时间是长了脚的妖怪,跑的飞快 四年时光,匆匆而过,沈阳占据了我23岁之前的太多第一次,第一次一个人一包行李,第一次21个小时的硬座,第一次坐地铁,第一次谈恋爱,第一次分手,第一次旅行,第一次坐摩天轮,第一次吃棉花糖,第一次看电影,第一次接触电脑,第一次…… 时间是长了脚的妖怪,跑的飞快,只是好像后来我们都离开,各自生活在喧嚣未来,当时的遗憾在回忆肆虐的某些时段,重新打开,又好象我们同时都在。 有人说,谈过恋爱,分过手,挂过科,拿过奖学金,当过学生干部的大学才是完美的,那么我想我还是比较幸运的,回顾我的大学生活,除了两件我极力想做的事情没有完成之外,经历了太多,谈过恋爱,分过手,当过学生干部,拿过奖学金,参加过各种志愿活动,也做过校园代理,被坑过,被骗过,也干过兼职,做过外包,送过外卖,发过传单,在这整个过程中,认识了不少人,见过不少事,看明白了不少的社会道理,看清了多少人的虚情假意,感谢那一路让我经历成长的人。 大一是我大学生活里最快乐的一年,那时的我们很单纯。只是后来,大家都变了。 2016-剑未配好,已出江湖,来一场说走就走的北漂 该到来的还是会到来,虽然对于工作我是做好了准备,但是还是有点措手不及。 七月,别了流年 那是七月,我的心情迫切的像火辣辣的太阳,拉着行李,从大学的门前离开,没有回头,虽然这里有我牵挂的人,有我念着的事,但我还是把更多的希望寄托在充满魔性的首都,因为我相信这里是梦会是开始的地方,于是在朋友的帮助下,我开启了我的北漂生活。 广联达 我来北京的第一家公司是广联达,建筑行业国内算是龙头老大了,虽然在互联网行业不是太牛逼,但对于一个初出茅庐的我还是够我学习和经历了,而且凑巧的是公司是我一个八几年的校友创立的,只是这和我没有半毛钱关系,在那的三个月里,我连个人影都没有见过。 后来的后来我选择了离开,不是公司不好,不是带我的师父不优秀,不是同事不牛逼,只是我感觉那里不适合我。 我的师父是项目组组长吧,人有点娘娘腔,别人都叫他梅梅,但是对我们特别好,他技术也十分厉害,离开的时候和师父聊天,他说在公司是P3和P4的技术双认证,是一个技术架构师,自己带着团队几个月为公司写了一个云测试平台,现在更到3.x版本了吧。我个人是十分佩服我师父的,为人低调,技术够强,还有好人缘。 在公司的那段时候里,我主要做的是一个以课题形式展示的数据分析平台,用到的技术无非就是大学里学的那些,那个时候和另外一个同事还吹牛逼说咱也是架构师了,这仅仅是因为自己画了个水的一逼的图 哈哈,如果这幅图出自架构师之手,就是系统架构了,可是出自我们这等毛小子之手就是闹着玩了吧。就好比以国家的名义去挖墓,就是考古,以个人名义去挖墓就是盗墓了。 昌平线,煎熬 西二旗是中国最堵得一个地铁站了,大家都说后村厂路堵车十分钟,中国互联网经济停滞2小时。 昌平线是北漂人的聚集地了,不是因为别的,是因为这条线路上的房租便宜 ,那个时候我就盘踞在沙河高教园旁边的东沙屯村里,每月800元的房租还是负担的起的,除了交通不便之外,一切还都可以接受,毕竟你是在北漂。 2016-别了流年,是现在的我 九月末我面试了现在所在的公司,离开了广联达,不是因为它不优秀,它不好,只是因为那里现在还不适合我,在我的棱角被磨平之前,我想出去闯一闯。 可能是我所在部门的原因,我觉得特别懒散,感觉大家都是在混日子,每天改那么点bug,每天更新一点小功能,或者这就是大公司的尴尬,或者说转型之中的公司的短板吧,大家都沉浸在以前的辉煌之中,没有创造力,没有新奇的想法,没有交流的冲动,没有那种干劲。于是我选择了离开,我想先让我去经历一番我想要的工作与生活,等我累了,说不定我就会想念这种状态了。 现在所在的是一个创业公司,像我想象中一样,大家窝在一个不大的办公司,交流与合作,为了梦想一起努力着,很开心。 在这里我接触到了更多知识,技术的,做人的,交流的,至今我脑海中还清晰的记着那天赵总的一句话:读书要有收获,至少要涨气场。 新的环境里我接触学习了Docker,ELK,重新学习了一些机器学习的算法知识。于是在我的CSDN博客中创建了两个技术专栏,由于刚刚接触,写的也不够深入,不过我会努力的。 Docker江湖:http://blog.csdn.net/column/details/13159.html ELK从入门到放弃:http://blog.csdn.net/column/details/13079.html 认认真真经历才能好好成长。 2016-我在CSDN的收获 鲍大神 开始在CSDN上写博客是大一的时候,是一个牛逼的学长带我走上了这条”不归路”,谢谢鲍大神这一路的指导与传授,一直以来,他都是我的榜样。我也努力赶上他,只可惜看到的永远都是背影。 梦姐姐 八月份的时候偶然的机会认识梦姐姐,做了博乐,后来也申请并通过了CSDN博客专家。 结识技术爱好者 其实相比这些更重要的是通过CSDN所认识的每一个技术爱好者,可以说CSDN是国内的程序员的社交平台了。感觉那些给我留言提问我的人,可能有些疑问还是没有帮你们解决,只是我个人能力有限,不像郭神,鸿洋大神技术功底深厚。在这个平台之上,我也认识到了自己的许多不足和技术缺点,在阅读博客的过程中,也学到了不少东西。 谢谢你一路陪我成长,你若不离,我定不弃。 2016-开始commit我的github 有人说开源垃圾,有人说开源缩减了开发的成本和时间,不管怎样,开源是一种趋势,而且势头不会减弱,很荣幸我也投入了开源的大军,即使现在我还是一个蝼蚁。 我的github:https://github.com/thinkgamer 2016-杂乱无章 这一年,从一个初出茅庐的蝼蚁一步步成长,一个个经历,我给交了一份70分的答卷,我没有让我的父母和亲人失望,我没有让我的老师失望,我没有让我喜欢的人失望,我也没有让曾经看不起我的人失望,只是我让自己失望了。 有些东西我没有去争取,有些机会我没有把握,有些冲动我失了控。但正是这些完美的不完美的,才让你有更大的劲头去前进。 2017-下一个自己 时间不会因为你的遗憾而停留,我们能做的就是把每一天都当成最后一天来过。 2017,我要完成: 一个安卓APP和对应的Web 小说《这夏未眠》 发表社区划分论文 深入学习Scala和Spark 掌握一个深度学习框架(eg:Caffe) 跟进研究Hadoop家族的最近版本,并形成文档 换一台Mackbook Pro 攒够100K+ 感谢这一路有你,加油! 个人微信公众号,欢迎关注
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 随着业务的发展,elasticsearch部署在一台机子上显然会不够用,那么我们该如何处理呢,幸运的elasticsearch支持横向扩展,即集群模式,这样无论数据量增长多大,我们只需要扩展我们的es集群即可。 本文永久地址 http://blog.csdn.net/gamer_gyt/article/details/53648983 背景说明 Ubuntu16.04 Docker 1.9 Elasticsearch 2.4 假设现在就一台服务器,我们要用这台服务器来部署一个ES的集群,那么最好的解决办法便是Docker了,我们可以利用Docker启动两个容器,在两个容器内各部署一个ElasticSearch,总而组成一个2个节点的ES集群 linux下Docker的部署:http://blog.csdn.net/gamer_gyt/article/details/52769294 终端安装Docker:http://bbs.csdn.net/topics/392063910 我的Docker专栏:http://blog.csdn.net/column/details/13159.html Docker网络模式解释 1:host模式 众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。 例如,我们在10.10.101.105/24的机器上用host模式启动一个含有web应用的Docker容器,监听tcp80端口。当我们在容器中执行任何类似ifconfig命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用10.10.101.105:80即可,不用任何NAT转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。 2:container模式 在理解了host模式后,这个模式也就好理解了。这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。 3:none模式 这个模式和前两个不同。在这种模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。 4:bridge模式 bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上 两个容器以组播方式创建ES集群 组播配置示例如下,单播配置在最下边,使用时只需替换加几行配置即可 1:下载Ubuntu最近版镜像 sudo docker pull ubuntu 2:配置基本环境 启动容器,进入容器内安装基本环境vim,java,和elasticsearch sudo docker run -it -d –name esc1 ubuntu:latest sudo docker exec -it esc1 /bin/bash apt-get install vim apt install openjdk-8-jre 然后安装es,下载安装包 点击下载 将其拷贝到docker内,安装 dpkg -i elasticsearch-2.4.0.deb 至此基本环境已经准备的差不多了,然后便是退出容器,stop 容器,然后进行commit 保存成两个容器 sudo docker commit esc1 es1:1.0 sudo docker commit esc1 es2:1.0 最后查看镜像如图所示: 3:es环境配置 分别启动两个容器 sudo docker run -it -d -p 9201:9201 -p 9301:9301 -p 5001:5001 -p 5602:5602 –name esc1 es1:1.0 sudo docker run -it -d -p 9200:9200 -p 9300:9300 -p 5000:5000 -p 5601:5601 –name esc2 es2:1.0 进入两个容器进行配置elasticsearch.ymal文件 esc1: cluster.name: xdstar node.name: node-1 node.master: false node.data: true network.host: 0.0.0.0 http.port: 9201 transport.tcp.port: 9301 esc2: cluster.name: xdstar node.name: node-2 node.master: true node.data: true network.host: 0.0.0.0 http.port: 9200 transport.tcp.port: 9300 cluster.name:集群名字,配置必须一样 node.name:每个节点的名字,不能一样 node.master: true 代表可以被选举为主节点,false代表不能被选举为主节点(这里我们设置esc2为主节点) ndoe.data:代表是否可以存储数据 network.host:表示访问的ip,0.0.0.0表示可以以IP访问或者localhost,127.0.0.1 network.port:访问的端口 启动Elasticsearch service elasticsearch start 细心的朋友会发现容器的启动端口转发端口不一致,这是因为,这里启动Docker容器默认采用的桥接,和主机共享端口了,所以两者端口不能一致,要不然会重复 启动完es集群,浏览器输入 https:192.168.1.250:9200/_plugin/head (这里我安装了head插件,插件安装,参考之前的一篇文章,里边有讲到:http://blog.csdn.net/gamer_gyt/article/details/52654263) 4:安装logstash配置rsyslog解析文件 logstash 2.4版本 点击下载 更多版本:https://www.elastic.co/downloads/past-releases dpkg -i logstash-all-plugins-2.4.0_all.deb 然后进入配置文件目录 cd /etc/logstash/conf.d vim rsyscon.conf 添加以下内容 input { syslog{ port => 5000 type => syslog } } output { stdout { codec=> rubydebug } elasticsearch { hosts => ["localhost:9200"] } } 启动logstash service logstash start 5:本地配置,产生syslog日志 编辑rsyslog的配置文件: vim /etc/rsyslog.conf 最后添加 *.* @localhost:5000 *.* @@localhost:5000 重启rsyslog服务 service rsyslog restart ssh 本地,产生日志 ssh localhost 这个时候观察elasticsearch界面: ES集群的多播与单播 以上的集群是采用组播的方式来构建的,组播就是通过在你的网络中发送UDP的ping请求以发现节点,其它Elasticsearch会收到这些ping请求并且进行响应,这样随后就会形成一个集群。 多播对于开发环境是很好的,你不需要做什么事情,打开一些节点,他们自然的会发现对方形成一个集群。 正是因为这种易用性,你在生产环境中必须禁掉它。否在你得到的结果就是一个节点意外的加入到了你的生产环境,因为他们收到了一个错误的组播信号。对于组播本身并没有错。组播会导致一些愚蠢的问题,并且导致集群变的脆弱(例如:一个网络工程师正在捣鼓网络,而没有告诉你,你会发现所有的节点突然发现不了对方了)。 在生产环境中,建议使用单播代替组播,也就是说为Elasticsearch提供一些它应该去尝试连接的节点列表。一旦这个节点联系到组播列表中的一员,它就会得到整个集群所有节点的状态,然后它会联系master节点,并加入集群。 这意味着你的单播列表不需要包含你的集群中的所有节点,它只需要包含足够一个新节点联系上其中一个并且说上话就ok了。 ES 是一个 P2P 类型(使用 gossip 协议)的分布式系统,除了集群状态管理以外,其他所有的请求都可以发送到集群内任意一台节点上,这个节点可以自己找到需要转发给哪些节点,并且直接跟这些节点通信。 所以,从网络架构及服务配置上来说,构建集群所需要的配置极其简单。在 Elasticsearch 2.0 之前,无阻碍的网络下,所有配置了相同 cluster.name 的节点都自动归属到一个集群中。 2.0 版本之后,基于安全的考虑,Elasticsearch 稍作了调整,避免开发环境过于随便造成的麻烦。 ES 从 2.0 版本开始,默认的自动发现方式改为了单播(unicast)方式。配置里提供几台节点的地址,ES 将其视作 gossip router 角色,借以完成集群的发现。由于这只是 ES 内一个很小的功能,所以 gossip router 角色并不需要单独配置,每个 ES 节点都可以担任。所以,采用单播方式的集群,各节点都配置相同的几个节点列表作为 router 即可。 此外,考虑到节点有时候因为高负载,慢 GC 等原因可能会有偶尔没及时响应 ping 包的可能,一般建议稍微加大 Fault Detection 的超时时间。 同样基于安全考虑做的变更还有监听的主机名。现在默认只监听本地 lo 网卡上。所以正式环境上需要修改配置为监听具体的网卡。 network.host: "0.0.0.0" discovery.zen.minimum_master_nodes: 3 discovery.zen.ping.timeout: 100s discovery.zen.fd.ping_timeout: 100s discovery.zen.ping.unicast.hosts: ["10.19.0.97","10.19.0.98","10.19.0.99","10.19.0.100"] 上面的配置中,两个 timeout 可能会让人有所迷惑。这里的 fd 是 fault detection 的缩写。也就是说: discovery.zen.ping.timeout 参数仅在加入或者选举 master 主节点的时候才起作用; discovery.zen.fd.ping_timeout 参数则在稳定运行的集群中,master 检测所有节点,以及节点检测 master 是否畅通时长期有用。 既然是长期有用,自然还有运行间隔和重试的配置,也可以根据实际情况调整: discovery.zen.fd.ping_interval: 10s discovery.zen.fd.ping_retries: 10 单播配置 以上展示为组播方式,单播配置相对来说只需要在配置文件里加几行即可 esc1: discovery.zen.ping.unicast.hosts: ["localhost:9300","localhost:9301"] esc2: discovery.zen.ping.unicast.hosts: ["localhost:9300","localhost:9301"] PS:我这里尝试了很多次,增加了 discovery.zen.minimum_master_nodes: 3 discovery.zen.ping.timeout: 100s discovery.zen.fd.ping_timeout: 100s 这三个属性之后集群并不能启动,所以这里只设置了discovery.zen.ping.unicast.hosts属性 OK!!!
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 由于最近在玩ELK数据分析,慢慢的对JSON格式的数据有了一些了解,之前只知道JSON是Javascript Object Notation编码格式的数据,且在web开发中的前后端数据传输时会大部分用的都是json格式的数据,那么接下来我们就好好看看python 对于 json格式数据的操作吧 本文永久地址:【python3.5】 读写JSON格式的数据 Json快速入门 json模块提供了一种很简单的方式来编码和解码json格式的数据,其中两个主要的函数是json.dumps()和json.loads(),当然与之对应的还要json.dump()和json.load()函数,下边先看几个例子帮你快速理解json 将一个Python数据结构转化为json: import json data ={ 'name': 'thinkgamer', 'age': 23, 'sex': 'men' } json_str = json.dumps(data) type(json_str) #<class 'str'> type(data) #<class 'dict'> 将一个JSON编码的字符串转换回一个Python数据结构: new_data = json.loads(json_str) type(new_data) #<class 'dict'> 上边演示的是json.dumps()函数和json.loads()函数,但是如果你的操作对象是文件而不仅是python的数据结构呢,这个时候就要用到json.dump()和json.load()了。 >>> new_data = json.loads(json_str) >>> type(new_data) <class 'dict'> >>> with open("data.json","w") as fw: ... json.dump(data,fw) ... >>> import os >>> os.system("cat data.json") {"age": 23, "name": "thinkgamer", "sex": "men"}0 >>> with open("data.json","r") as fr: ... data = json.load(fr) ... print(data) ... print(type(data)) ... {'age': 23, 'name': 'thinkgamer', 'sex': 'men'} <class 'dict'> 四个函数解释 json.dump() json.dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, encoding="utf-8", default=None, sort_keys=False, **kw) 将obj序列化为JSON格式流到fp 如果skipkeys为True (默认:False),然后并不是一种基本的类型 (str, unicode, int, long, float, bool, None) 的字典键将被跳过而不是抛出TypeError异常。 如果ensure_ascii为True(默认值),则输出中的所有非ASCII字符都将使用uXXXX序列进行转义,并且结果是仅由ASCII字符组成的str实例。 如果ensure_ascii为False,写入fp的一些块可能是unicode实例。 这通常发生,因为输入包含unicode字符串或使用encoding参数。 除非fp.write()明确理解unicode(如codecs.getwriter()),这很可能导致错误。 如果check_circuler设置为false(默认为true)对容器类型的循环引用检查将被跳过,然后循环的引用将导致OverflowError。 若allow_nan为False(默认:True),那么它将是ValueError范围的浮点型值-inf inf nan) 从序列化 JSON 的规范,而不是使用 JavaScript 等价物严格按照 (NaN,无穷大, -无限)。 cls:如果缩进是一个非负整数,那么 JSON 数组元素和对象成员将漂亮印有该缩进级别。缩进级别为 0,或负面的只能将插入换行符。没有一个(默认值) 选择的最紧凑的表示形式。 indent:如果分隔符是(item_separator, dict_separator)元组),那么它将代替默认的(‘, ‘, ‘: ‘)分隔符。 (‘,’, ‘: ‘)是最紧凑的 JSON 表示 encoding:编码的字符编码为 str 实例,默认为 utf-8 default(obj)是 obj 的一个函数,它应该返回一个可序列化的版本或提高TypeError。默认值只是引发TypeError。 如果sort_keys为True (默认:False),然后将按关键字排序字典的输出。 json.dumps() json.dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, encoding="utf-8", default=None, sort_keys=False, **kw) 将Python的对象数据或者是str序列化为JSON格式,具体的参数含义如dump()函数 json.load() json.load(fp[, encoding[, cls[, object_hook[, parse_float[, parse_int[, parse_constant[, object_pairs_hook[, **kw]]]]]]]]) 反序列化fp ( .read()-支持文件类似对象包含一个 JSON 文档) 到 Python 对象 object_hook:可选函数,将调用任何对象文本解码的结果 object_pairs_hook:可选函数,将调用任何的结果对象文本解码成对有序列表 parse_float:如果指定,将使用每个 JSON float要解码的字符串调用。默认情况下,这是相当于float(num_str)。这可以用来使用另一种数据类型或解析 JSON float parse_int:如果指定,将使用每个 JSON int 要解码的字符串调用。默认情况下,这是相当于int(num_str)。这可以用来使用另一种数据类型或解析 JSON 整数 parse_constant,如果指定,将调用以下字符串之一: ‘-无穷大 ‘, ‘无穷大’、 ‘null’。如果遇到无效的 JSON 格式时引发异常。 json.loads() 将包含json格式的文档序列化成Python的对象,其余参数同load()函数 几个小案例 一般来讲,JSON解码会根据提供的数据创建dicts或lists。 如果你想要创建其他类型的对象,可以给 json.loads() 传递object_pairs_hook或object_hook参数。 例如,下面是演示如何解码JSON数据并在一个OrderedDict中保留其顺序的例子 >>> import os >>> s= '{"name":"Think","share":50, "price":12}' >>> from collections import OrderedDict >>> data = json.loads(s, object_pairs_hook=OrderedDict) >>> data OrderedDict([('name', 'Think'), ('share', 50), ('price', 12)]) >>> type(data) <class 'collections.OrderedDict'> 将一个JSON字典转换为一个Python对象例子 >>> class json_object: ... def __init__(self,d): ... self.__dict__ = d ... >>> data = json.loads(s, object_hook = json_object) >>> data.name 'Think' >>> data.share 50 >>> data.price 12 在这个例子中,JSON解码后的字典作为一个单个参数传递给 init() 。 然后,你就可以随心所欲的使用它了,比如作为一个实例字典来直接使用它。 在编码JSON的时候,还有一些选项很有用。 如果你想获得漂亮的格式化字符串后输出,可以使用 json.dumps() 的indent参数。 比如: >>> print(json.dumps(data)) {"name": "Think", "shares": 50, "price": 12} >>> print(json.dumps(data,indent=4)) { "name": "Think", "shares": 50, "price": 12 } 然而实例通常并不是可序列化的,例如 >>> class Point: ... def __init__(self,x,y): ... self.x = x ... self.y = y ... >>> p = Point(2,3) >>> json.dumps(p) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python3.5/json/__init__.py", line 230, in dumps return _default_encoder.encode(obj) File "/usr/lib/python3.5/json/encoder.py", line 198, in encode chunks = self.iterencode(o, _one_shot=True) File "/usr/lib/python3.5/json/encoder.py", line 256, in iterencode return _iterencode(o, 0) File "/usr/lib/python3.5/json/encoder.py", line 179, in default raise TypeError(repr(o) + " is not JSON serializable") TypeError: <__main__.Point object at 0x7f2757f2f278> is not JSON serializable 如果你想序列化对象实例,你可以提供一个函数,它的输入是一个实例,返回一个可序列化的字典。例如: >>> def serialize_instance(obj): ... d = { '__classname__' : type(obj).__name__} ... d.update(vars(obj)) ... return d ... >>> p = Point(2,3) >>> json.dumps(p,default =serialize_instance) '{"x": 2, "y": 3, "__classname__": "Point"}' 资料参考 http://python.usyiyi.cn/python_278/library/json.html http://python3-cookbook.readthedocs.io/zh_CN/latest/c06/p02_read-write_json_data.html
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 Elastalert是由python2.6写的一个告警框架,针对ELK日志分析系统来讲具有很大的作用,在 Elastalert-基于Elasticsearch层面的监控告警框架中,我们了解了Elastalert框架和配置使用了frequence的告警规则,这篇文章我们来看下Spike告警规则,相对来讲,spike rule不是很好理解,希望读者们能够细心阅读,有什么不懂的问题,可以留言讨论 该文永久地址:http://blog.csdn.net/Gamer_gyt/article/details/53381279 环境说明 ubuntu:16.04 Elastalert :0.14 Elasticsearch 2.4.1 Logstash 2.4.0 Kibana 4.6.1 其中的发邮件配置部分参考: Elastalert-基于Elasticsearch层面的监控告警框架 配置文件解读 # 可选的,elasticsearch 所在主机的ip地址 # es_host: elasticsearch.example.com # 可选的,elasticsearch的服务端口 # 这里的elasticsearch之所以说是可选是因为在config.ymal中已经配置过了 # es_port: 14900 # 可选,是否使用SSL加密传送数据,这是因为elasticsearch如果被search-guard或者x-pack/shield保护的话,要启用 #use_ssl: True # 可选,对应访问elasticsearch的账号和密码 #es_username: someusername #es_password: somepassword # 必选的,rule的唯一名字 name: Event spike # 必选的,rule的类型 type: spike # 必选的,监控的索引 index: logstash-* # 二选一,cur:当前窗口,ref:参照窗口 后边会着重讲述 threshold_cur: 5 #threshold_ref: 5 # 必选的,窗口参照时间 timeframe: hours: 2 # 必选的,衡论是否进行报警的一个警戒线 spike_height: 3 # 必选的,spike_type有三种类型,up,down,both spike_type: "up" # 必须的,进行匹配过滤的条件 # 更多过滤条件设置参考: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html filter: - query: query_string: query: "field: value" - type: value: "some_doc_type" # 必须的,报警方式 alert: - "email" # 必选的,邮件的接受地址 email: - "elastalert@example.com" 重要参数解释 spike_type: up:意味着只会匹配超过当事件发生的次数超过spike_height down:意味着只会匹配超过当事件发生的次数低于spike_height both:都会进行匹配 spike_height:最后一个时间帧中的事件数与上一个命中时触发警报的时间帧的比率 threshold_ref:要触发的警报在参考窗口中必须存在的事件的最小数量。例如,如果spike_height:3和threshold_ref:10,则“引用”窗口必须包含至少10个事件,“当前”窗口至少为触发警报的三倍。 threshold_cur:当前窗口中必须存在的触发警报的最小事件数。例如,如果spike_height:3和threshold_cur:60,则当前窗口具有多于60个事件并且参考窗口少于三分之一时,将发生警报。 (当前窗口和参照窗口,而这个窗口就是一个时间段(timeframe)) spike的报警规则 1、如果设置了threshold_ref或者threshold_cur,那么比较对应的ref或cur是否大于threshold_ref或者threshold_cur,如果大于,接着看2,否则不警报; 2、是否满足公式 ref * spike_height < cur ,如果是则警报,否则不警报。 例如: spike_type:up 12:00-14:00发生事件的次数为5,14:00-16:00发生事件的次数为20 设置threshold_cur:6,spike_height:3 由于4<6所以不进行报警 如果我们设置的threshold_cur:4 由于5>4,那么我们看是否满足公式 ref * spike_height < cur ,5*3 > 6不报警 但是如果我们设置的spike_height:1,那么5*1<6,就会进行报警 (这里不是太好理解,大家多读几遍) 实例 前提是ELK和Elastalert都配置OK logstash的配置文件rsyslog_test.conf input { tcp{ port => 5000 type => syslog } udp{ port => 5000 type => syslog } } output { stdout { codec=> rubydebug } elasticsearch { hosts => ["127.0.0.1:9200"] } } elastalert 的example_spike.ymal es_host: 127.0.0.1 es_port: 9200 name: spike rule type: spike index: logstash-* threshold_ref: 3 timeframe: minutes: 1 spike_height: 1 spike_type: "up" filter: - query: query_string: query: "host: 127.0.0.1" - term: _type: "syslog" use_count_query: true doc_type: syslog #SMTP协议的邮件服务器相关配置(我这里是163 mial) smtp_host: smtp.163.com smtp_port: 25 #用户认证文件,需要user和password两个属性 smtp_auth_file: smtp_auth_file.yaml email_reply_to: thinkgamer@163.com from_addr: thinkgamer@163.com alert: - "email" email: - "1923361654@qq.com" 邮件配置部分参考: Elastalert-基于Elasticsearch层面的监控告警框架 系统的/etc/rsyslog.conf,最后加入 *.* @@localhost:5000 *.* @localhost:5000 启动elasticsearch和logstash的rsyslog_test.conf 启动elastalert的example_spike.ymal python -m elastalert.elastalert –verbose –rule example_rules/example_spike.yaml 适当的控制系统的登录日志,最终spike窗口显示如下: master@ubuntu:/opt/elk/elastalert$ python -m elastalert.elastalert --verbose --rule rules/spike.yaml INFO:elastalert:Starting up INFO:elastalert:Queried rule spike rule from 2016-11-28 01:26 PST to 2016-11-28 01:27 PST: 0 hits INFO:elastalert:Ran spike rule from 2016-11-28 01:26 PST to 2016-11-28 01:27 PST: 0 query hits, 0 matches, 0 alerts sent INFO:elastalert:Sleeping for 59 seconds INFO:elastalert:Queried rule spike rule from 2016-11-28 01:27 PST to 2016-11-28 01:28 PST: 22 hits INFO:elastalert:Ran spike rule from 2016-11-28 01:27 PST to 2016-11-28 01:28 PST: 22 query hits, 0 matches, 0 alerts sent INFO:elastalert:Sleeping for 59 seconds INFO:elastalert:Queried rule spike rule from 2016-11-28 01:28 PST to 2016-11-28 01:29 PST: 98 hits INFO:elastalert:Ran spike rule from 2016-11-28 01:28 PST to 2016-11-28 01:29 PST: 98 query hits, 0 matches, 0 alerts sent INFO:elastalert:Sleeping for 59 seconds INFO:elastalert:Queried rule spike rule from 2016-11-28 01:29 PST to 2016-11-28 01:30 PST: 110 hits INFO:elastalert:Sent email to ['1923361654@qq.com'] INFO:elastalert:Ran spike rule from 2016-11-28 01:29 PST to 2016-11-28 01:30 PST: 110 query hits, 1 matches, 1 alerts sent INFO:elastalert:Sleeping for 59 seconds 这个时候邮箱收到邮件内容: OVER !
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 前言 其实一直以来都没有太关注elsticsearch的健康状态,只是单纯的部署完成,然后es能正常工作就OK了,然而事实却并非如此,elasticsearch得索引状态和集群状态得不同传达着不同得意思,下面我们就来看看 集群状态 Rest API: root@c2dbf1f9da5d:/# curl 'http://localhost:9200/_cat/health?v' epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent 1479536615 06:23:35 elasticsearch yellow 1 1 931 931 0 0 930 0 - 50.0% 我当前得elasticsearch时单机版得,由于加载得索引数据比较多索引显示为yellow,正常情况下,集群得健康状态分为三种: green 最健康得状态,说明所有的分片包括备份都可用 yellow 基本的分片可用,但是备份不可用(或者是没有备份) red 部分的分片可用,表明分片有一部分损坏。此时执行查询部分数据仍然可以查到,遇到这种情况,还是赶快解决比较好 从上边查看的集群状态中还包括一些其他信息: 集群名字时elasticsearch,集群的节点个数为1,分片数量为930,活动分片百分比为 50% 查看各个索引状态 Rest API: root@c2dbf1f9da5d:/# curl -k -u admin:admin 'https://localhost:9200/_cat/indices' | grep raoxing-* % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 11033 100 11033 0 0 43611 0 --:--:-- --:--:-- --:--:-- 43608 yellow open raoxing-2016.01.03 5 1 1 0 7.1kb 7.1kb yellow open raoxing-2016.01.02 5 1 1 0 7kb 7kb yellow open raoxing-2015.11.09 5 1 4 0 20.4kb 20.4kb yellow open raoxing-2015.11.05 5 1 1 0 7kb 7kb yellow open raoxing-2015.11.03 5 1 1 0 7kb 7kb 我这里过滤了raoxing-*的索引数据,可以看到raoxing-* 的健康状态全部为yellow,所以我的集群(单机版称为空集群)的健康状态也为yellow 索引的健康状态也有三种,yellow、green、red与集群的健康状态解释是一样的 状态为yellow的解决办法 分片与副本分片 分片用于Elasticsearch在你的集群中分配数据。想象把分片当作数据的容器。文档存储在分片中,然后分片分配给你集群中的节点上。 当你的集群扩容或缩小,Elasticsearch将会自动在你的节点间迁移分片,以使集群保持平衡。 一个分片(shard)是一个最小级别的“工作单元(worker unit)”,它只是保存索引中所有数据的一小片.我们的文档存储和被索引在分片中,但是我们的程序不知道如何直接与它们通信。取而代之的是,他们直接与索引通信.Elasticsearch中的分片分为主分片和副本分片,复制分片只是主分片的一个副本,它用于提供数据的冗余副本,在硬件故障之后提供数据保护,同时服务于像搜索和检索等只读请求,主分片的数量和复制分片的数量都可以通过配置文件配置。但是主切片的数量只能在创建索引时定义且不能修改.相同的分片不会放在同一个节点上。 分片算法 shard = hash(routing) % number_of_primary_shards routing值是一个任意字符串,它默认是_id但也可以自定义,这个routing字符串通过哈希函数生成一个数字,然后除以主切片的数量得到一个余数(remainder),余数的范围永远是0到number_of_primary_shards - 1,这个数字就是特定文档所在的分片。 这也解释了为什么主切片的数量只能在创建索引时定义且不能修改:如果主切片的数量在未来改变了,所有先前的路由值就失效了,文档也就永远找不到了。 所有的文档API(get、index、delete、bulk、update、mget)都接收一个routing参数,它用来自定义文档到分片的映射。自定义路由值可以确保所有相关文档.比如用户的文章,按照用户账号路由,就可以实现属于同一用户的文档被保存在同一分片上。 分片与副本交互 新建、索引和删除请求都是写(write)操作,它们必须在主分片上成功完成才能复制到相关的复制分片上,下面我们罗列在主分片和复制分片上成功新建、索引或删除一个文档必要的顺序步骤: 1、客户端给Node 1发送新建、索引或删除请求。 2、节点使用文档的_id确定文档属于分片0。它转发请求到Node 3,分片0位于这个节点上。 3、Node 3在主分片上执行请求,如果成功,它转发请求到相应的位于Node 1和Node 2的复制节点上。当所有的复制节点报告成功,Node 3报告成功到请求的节点,请求的节点再报告给客户端。 客户端接收到成功响应的时候,文档的修改已经被应用于主分片和所有的复制分片。你的修改生效了 查看分片状态 root@c2dbf1f9da5d:/# curl -X GET 'http://localhost:9200/_cluster/health?pretty' { "cluster_name" : "elasticsearch", "status" : "yellow", "timed_out" : false, "number_of_nodes" : 1, "number_of_data_nodes" : 1, "active_primary_shards" : 931, "active_shards" : 931, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 930, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 50.02686727565825 } 这里解释一下为什么集群状态为yellow 由于我们是单节点部署elasticsearch,而默认的分片副本数目配置为1,而相同的分片不能在一个节点上,所以就存在副本分片指定不明确的问题,所以显示为yellow,我们可以通过在elasticsearch集群上添加一个节点来解决问题,如果你不想这么做,你可以删除那些指定不明确的副本分片(当然这不是一个好办法)但是作为测试和解决办法还是可以尝试的,下面我们试一下删除副本分片的办法 解决办法 root@c2dbf1f9da5d:/# curl -XPUT "http://localhost:9200/_settings" -d' { "number_of_replicas" : 0 } ' {"acknowledged":true} 这个时候再次查看集群的状态状态变成了green root@c2dbf1f9da5d:/# curl -X GET 'http://localhost:9200/_cluster/health?pretty' { "cluster_name" : "elasticsearch", "status" : "green", "timed_out" : false, "number_of_nodes" : 1, "number_of_data_nodes" : 1, "active_primary_shards" : 931, "active_shards" : 931, "relocating_shards" : 0, "initializing_shards" : 0, "unassigned_shards" : 0, "delayed_unassigned_shards" : 0, "number_of_pending_tasks" : 0, "number_of_in_flight_fetch" : 0, "task_max_waiting_in_queue_millis" : 0, "active_shards_percent_as_number" : 100.0 } elasticsearch集群的一些概念 集群与节点 节点(node)是你运行的Elasticsearch实例。一个集群(cluster)是一组具有相同cluster.name的节点集合,他们协同工作,共享数据并提供故障转移和扩展功能,当有新的节点加入或者删除节点,集群就会感知到并平衡数据。集群中一个节点会被选举为主节点(master),它用来管理集群中的一些变更,例如新建或删除索引、增加或移除节点等;当然一个节点也可以组成一个集群。 节点通信 我们能够与集群中的任何节点通信,包括主节点。任何一个节点互相知道文档存在于哪个节点上,它们可以转发请求到我们需要数据所在的节点上。我们通信的节点负责收集各节点返回的数据,最后一起返回给客户端。这一切都由Elasticsearch透明的管理。 集群生态 1.同集群中节点之间可以扩容缩容, 2.主分片的数量会在其索引创建完成后修正,但是副本分片的数量会随时变化。 3.相同的分片不会放在同一个节点上. 索引的unssigned问题 这里的unssigned就是未分配副本分片的问题,在我们执行删除副本分片是这个问题就没了 root@c2dbf1f9da5d:/# curl -XPUT "http://localhost:9200/_settings" -d' { "number_of_replicas" : 0 } ' {"acknowledged":true}
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 前言 无意间在网上看到一篇不错的文章拿来分享 原文链接:http://dockone.io/article/783 英文原文链接:http://merrigrove.blogspot.sg/2015/10/visualizing-docker-containers-and-images.html 编者的话 本文用图文并茂的方式介绍了容器、镜像的区别和Docker每个命令后面的技术细节,能够很好的帮助读者深入理解Docker。 这篇文章希望能够帮助读者深入理解Docker的命令,还有容器(container)和镜像(image)之间的区别,并深入探讨容器和运行中的容器之间的区别。 当我对Docker技术还是一知半解的时候,我发现理解Docker的命令非常困难。于是,我花了几周的时间来学习Docker的工作原理,更确切地说,是关于Docker统一文件系统(the union file system)的知识,然后回过头来再看Docker的命令,一切变得顺理成章,简单极了。 题外话 就我个人而言,掌握一门技术并合理使用它的最好办法就是深入理解这项技术背后的工作原理。通常情况下,一项新技术的诞生常常会伴随着媒体的大肆宣传和炒作,这使得用户很难看清技术的本质。更确切地说,新技术总是会发明一些新的术语或者隐喻词来帮助宣传,这在初期是非常有帮助的,但是这给技术的原理蒙上了一层砂纸,不利于用户在后期掌握技术的真谛。 Git就是一个很好的例子。我之前不能够很好的使用Git,于是我花了一段时间去学习Git的原理,直到这时,我才真正明白了Git的用法。我坚信只有真正理解Git内部原理的人才能够掌握这个工具。 Image Definition 镜像(Image)就是一堆只读层(read-only layer)的统一视角,也许这个定义有些难以理解,下面的这张图能够帮助读者理解镜像的定义。 从左边我们看到了多个只读层,它们重叠在一起。除了最下面一层,其它层都会有一个指针指向下一层。这些层是Docker内部的实现细节,并且能够在主机(译者注:运行Docker的机器)的文件系统上访问到。统一文件系统(union file system)技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。我们可以在图片的右边看到这个视角的形式。 你可以在你的主机文件系统上找到有关这些层的文件。需要注意的是,在一个运行中的容器内部,这些层是不可见的。在我的主机上,我发现它们存在于/var/lib/docker/aufs目录下。 sudo tree -L 1 /var/lib/docker/ /var/lib/docker/ ├── aufs ├── containers ├── graph ├── init ├── linkgraph.db ├── repositories-aufs ├── tmp ├── trust └── volumes 7 directories, 2 files Container Definition 容器(container)的定义和镜像(image)几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。 细心的读者可能会发现,容器的定义并没有提及容器是否在运行,没错,这是故意的。正是这个发现帮助我理解了很多困惑。 要点:容器 = 镜像 + 读写层。并且容器的定义并没有提及是否要运行容器。 接下来,我们将会讨论运行态容器。 Running Container Definition 一个运行态容器(running container)被定义为一个可读写的统一文件系统加上隔离的进程空间和包含其中的进程。下面这张图片展示了一个运行中的容器。 正是文件系统隔离技术使得Docker成为了一个前途无量的技术。一个容器中的进程可能会对文件进行修改、删除、创建,这些改变都将作用于可读写层(read-write layer)。下面这张图展示了这个行为。 我们可以通过运行以下命令来验证我们上面所说的: docker run ubuntu touch happiness.txt 即便是这个ubuntu容器不再运行,我们依旧能够在主机的文件系统上找到这个新文件。 find / -name happiness.txt /var/lib/docker/aufs/diff/860a7b…889/happiness.txt Image Layer Definition 为了将零星的数据整合起来,我们提出了镜像层(image layer)这个概念。下面的这张图描述了一个镜像层,通过图片我们能够发现一个层并不仅仅包含文件系统的改变,它还能包含了其他重要信息。 元数据(metadata)就是关于这个层的额外信息,它不仅能够让Docker获取运行和构建时的信息,还包括父层的层次信息。需要注意,只读层和读写层都包含元数据。 除此之外,每一层都包括了一个指向父层的指针。如果一个层没有这个指针,说明它处于最底层。 Metadata Location: 我发现在我自己的主机上,镜像层(image layer)的元数据被保存在名为”json”的文件中,比如说: /var/lib/docker/graph/e809f156dc985…/json e809f156dc985…就是这层的id 一个容器的元数据好像是被分成了很多文件,但或多或少能够在/var/lib/docker/containers/< id>目录下找到,< id>就是一个可读层的id。这个目录下的文件大多是运行时的数据,比如说网络,日志等等。 全局理解(Tying It All Together) 现在,让我们结合上面提到的实现细节来理解Docker的命令。 docker create < image-id> Input (if applicable) Output (if applicable) docker create 命令为指定的镜像(image)添加了一个可读写层,构成了一个新的容器。注意,这个容器并没有运行。 docker start < container-id> Input (if applicable) Output (if applicable) Docker start命令为容器文件系统创建了一个进程隔离空间。注意,每一个容器只能够有一个进程隔离空间。 docker run < image-id> 看到这个命令,读者通常会有一个疑问:docker start 和 docker run命令有什么区别。 从图片可以看出,docker run 命令先是利用镜像创建了一个容器,然后运行这个容器。这个命令非常的方便,并且隐藏了两个命令的细节,但从另一方面来看,这容易让用户产生误解。 题外话:继续我们之前有关于Git的话题,我认为docker run命令类似于git pull命令。git pull命令就是git fetch 和 git merge两个命令的组合,同样的,docker run就是docker create和docker start两个命令的组合。 docker ps docker ps 命令会列出所有运行中的容器。这隐藏了非运行态容器的存在,如果想要找出这些容器,我们需要使用下面这个命令。 docker ps –a docker ps –a命令会列出所有的容器,不管是运行的,还是停止的。 docker images docker images命令会列出了所有顶层(top-level)镜像。实际上,在这里我们没有办法区分一个镜像和一个只读层,所以我们提出了top-level镜像。只有创建容器时使用的镜像或者是直接pull下来的镜像能被称为顶层(top-level)镜像,并且每一个顶层镜像下面都隐藏了多个镜像层。 docker images –a docker images –a命令列出了所有的镜像,也可以说是列出了所有的可读层。如果你想要查看某一个image-id下的所有层,可以使用docker history来查看。 docker stop < container-id> docker stop命令会向运行中的容器发送一个SIGTERM的信号,然后停止所有的进程。 docker kill < container-id> docker kill 命令向所有运行在容器中的进程发送了一个不友好的SIGKILL信号。 docker pause < container-id> docker stop和docker kill命令会发送UNIX的信号给运行中的进程,docker pause命令则不一样,它利用了cgroups的特性将运行中的进程空间暂停。具体的内部原理你可以在这里找到:https://www.kernel.org/doc/Doc … m.txt,但是这种方式的不足之处在于发送一个SIGTSTP信号对于进程来说不够简单易懂,以至于不能够让所有进程暂停。 docker rm < container-id> docker rm命令会移除构成容器的可读写层。注意,这个命令只能对非运行态容器执行。 docker rmi < image-id> docker rmi 命令会移除构成镜像的一个只读层。你只能够使用docker rmi来移除最顶层(top level layer)(也可以说是镜像),你也可以使用-f参数来强制删除中间的只读层。 docker commit < container-id> docker commit命令将容器的可读写层转换为一个只读层,这样就把一个容器转换成了不可变的镜像。 docker build docker build命令非常有趣,它会反复的执行多个命令。 我们从上图可以看到,build命令根据Dockerfile文件中的FROM指令获取到镜像,然后重复地1)run(create和start)、2)修改、3)commit。在循环中的每一步都会生成一个新的层,因此许多新的层会被创建。 docker exec < running-container-id> docker exec 命令会在运行中的容器执行一个新进程。 docker inspect < container-id> or < image-id> docker inspect命令会提取出容器或者镜像最顶层的元数据。 docker save < image-id> docker save命令会创建一个镜像的压缩文件,这个文件能够在另外一个主机的Docker上使用。和export命令不同,这个命令为每一个层都保存了它们的元数据。这个命令只能对镜像生效。 docker export < container-id> docker export命令创建一个tar文件,并且移除了元数据和不必要的层,将多个层整合成了一个层,只保存了当前统一视角看到的内容(译者注:expoxt后的容器再import到Docker中,通过docker images –tree命令只能看到一个镜像;而save后的镜像则不同,它能够看到这个镜像的历史镜像)。 docker history < image-id> docker history命令递归地输出指定镜像的历史镜像。 结论 我希望你们能喜欢这篇文章。还有其他许多的命令(pull,search,restart,attach等)我没有提及,但是我相信通过阅读这篇文章,大部分的Docker命令都能够被很好理解。我仅仅学习了Docker两个星期,因此,如果我有什么地方说的不好,欢迎大家指出。
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 环境介绍 docker 1.12.1 elasticsearch 2.4.1 logstash 2.4.0 kibana 4.6.1 docker容器中运行elk服务,并用logstash 解析日志,出现错误 查看日志发现是字符格式解析失败,如下信息所示 Received an event that has a different character encoding than you configured. {:text=>"2015/11/3 8:33,\\\"\\xB7\\xA2\\xCF\\xD6\\xD3?\\xA7\\xC8?\\xFDUSP,?\\xD3\\xC3\\xD5?\\xC5bj,\\xB4\\xD3su\\xB5\\xC7??\\xB1\\xEA\\xD6\\xF7\\xBB\\xFA132.77.138.21\\xA3\\xAC\\xD0\\xE8?\\xB9\\xD8?\\xA1\\xA3\\\"\\r", :expected_charset=>"UTF-8", :level=>:warn} Error parsing csv {:field=>"message", :source=>"2015/11/3 8:33,\\\"\\xB7\\xA2\\xCF\\xD6\\xD3?\\xA7\\xC8?\\xFDUSP,?\\xD3\\xC3\\xD5?\\xC5bj,\\xB4\\xD3su\\xB5\\xC7??\\xB1\\xEA\\xD6\\xF7\\xBB\\xFA132.77.138.21\\xA3\\xAC\\xD0\\xE8?\\xB9\\xD8?\\xA1\\xA3\\\"\\r", :exception=>#<CSV::MalformedCSVError: Illegal quoting in line 1.>, :level=>:warn} to_s at /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-core-event-2.2.4-java/lib/logstash/event.rb:105 encode at /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-codec-line-2.1.2/lib/logstash/codecs/line.rb:56 receive at /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-output-stdout-2.0.6/lib/logstash/outputs/stdout.rb:55 multi_receive at /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-core-2.2.4-java/lib/logstash/outputs/base.rb:83 each at org/jruby/RubyArray.java:1613 multi_receive at /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-core-2.2.4-java/lib/logstash/outputs/base.rb:83 worker_multi_receive at /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-core-2.2.4-java/lib/logstash/output_delegator.rb:130 multi_receive at /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-core-2.2.4-java/lib/logstash/output_delegator.rb:114 output_batch at /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-core-2.2.4-java/lib/logstash/pipeline.rb:293 each at org/jruby/RubyHash.java:1342 output_batch at /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-core-2.2.4-java/lib/logstash/pipeline.rb:293 worker_loop at /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-core-2.2.4-java/lib/logstash/pipeline.rb:224 start_workers at /opt/logstash/vendor/bundle/jruby/1.9/gems/logstash-core-2.2.4-java/lib/logstash/pipeline.rb:193 解决办法 想了好久也没找到解决办法,网上也是各种百度谷歌,最后突发奇想是不是docker安装时是最小化安装,然后好多package loss了,结果还真是 总结一下原因 :docker是最小化安装,logstash对文本数据进行解析时,如果没有指定codec,默认采用得是utf-8,而docker由于是最小化安装,缺少对应得package,所以解析时会报错 查看docker容器当前的系统运行语言环境 root@0f2baa6ee463:/var/log/logstash# locale LANG= LANGUAGE= LC_CTYPE="POSIX" LC_NUMERIC="POSIX" LC_TIME="POSIX" LC_COLLATE="POSIX" LC_MONETARY="POSIX" LC_MESSAGES="POSIX" LC_PAPER="POSIX" LC_NAME="POSIX" LC_ADDRESS="POSIX" LC_TELEPHONE="POSIX" LC_MEASUREMENT="POSIX" LC_IDENTIFICATION="POSIX" LC_ALL= 查看所支持的语言 root@0f2baa6ee463:/home# locale -a C C.UTF-8 POSIX 开始解决问题 /* 生成 en_US.UTF-8 locale文件 CentOS没有locale-gen命令*/ $localedef -v -c -i en_US -f UTF-8 en_US.UTF-8 $export LC_ALL="en_US.UTF-8" bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8): No such file or directory 最后在查看新系统的语言环境 root@0f2baa6ee463:/home# locale LANG= LANGUAGE= LC_CTYPE="en_US.UTF-8" LC_NUMERIC="en_US.UTF-8" LC_TIME="en_US.UTF-8" LC_COLLATE="en_US.UTF-8" LC_MONETARY="en_US.UTF-8" LC_MESSAGES="en_US.UTF-8" LC_PAPER="en_US.UTF-8" LC_NAME="en_US.UTF-8" LC_ADDRESS="en_US.UTF-8" LC_TELEPHONE="en_US.UTF-8" LC_MEASUREMENT="en_US.UTF-8" LC_IDENTIFICATION="en_US.UTF-8" LC_ALL=en_US.UTF-8 root@0f2baa6ee463:/home# locale -a C C.UTF-8 en_US.utf8 POSIX 然后重新加载解析文件,发现解决问题了 End 总之也是走了很多弯路,但是幸运的是解决了问题,晚安
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 背景 最近对自己大学做的一个网站进行了升级简化和美观优化 github地址:https://github.com/Thinkgamer/CyanScikit 然后之前做的时候用的是Python 2.7 + Django 1.8.6,现在这个版本对python和django都进行了升级,使用的是python 3.4+Django 1.10,然后再使用到views的函数的时候便遇到了问题 环境介绍 Django 1.10 Python 3.4 产生问题的场景是:对django的admin后台融合富文本编辑器,用的还是之前django1.8的语法例子,然后就报出了 File "F:\github\CyanScikit\CyanScikit\urls.py", line 22, in <module> url(r"^uploads/(?P<path>.*)$","django.views.static.serve",{"document_root":s ettings.MEDIA_ROOT,}), File "D:\Program Files\python\lib\site-packages\django\conf\urls\__init__.py", line 85, in url raise TypeError('view must be a callable or a list/tuple in the case of incl ude().') TypeError: view must be a callable or a list/tuple in the case of include(). 思考过程 这个问题是写这篇文章的前一天晚上遇到的,但是由于好久没有碰django了,所以一时半会还不知道问题出在哪了,当时一直在想是不是python版本的关系,因为之前遇到过一个问题是,在定义django的models的时候有一个方法 def __unicode__(self): return self.xxx 但这个是在python2.7中的写法,在python3.X中是这样写的 def __str__(self): return self.xxx 所以把误区一直停留在了python版本问题上,导致走路很多弯路也没有解决这个问题 然后上网寻找答案,在这里得到了解决的办法:http://www.imooc.com/qadetail/98920 意思是在django1.10版本中views视图函数的语法出现了变化,需要先导入views视图函数,然后再引用其内部的函数,同时去掉双引号 解决办法 我的使用kindeditor富文本编辑器,使用以下语法出现错误: from django.conf import settings from CyanScikit.upload import upload_image urlpatterns = [ url(r'^admin/uploads/(?P<dir_name>[^/]+)$', upload_image, name='upload_image'), url(r"^uploads/(?P<path>.*)$",views.static.serve,{"document_root":settings.MEDIA_ROOT,}), ] 对应的解决办法是修改以上部分为: from django import views from CyanScikit.upload import upload_image urlpatterns = [ url(r'^admin/uploads/(?P<dir_name>[^/]+)$', upload_image, name='upload_image'), url(r"^uploads/(?P<path>.*)$",views.static.serve,{"document_root":settings.MEDIA_ROOT,}), ] OK,解决问题 注意:版本变化之后带来的语法差异 反思 在做项目的时候,思想往往会被固化在以后的某一个时间点,因为我们相信,我们对于该项技术是熟知的,导致我们不再去官网查看文档,然后就会出现一些语法上的错误,同时对于错误的问题分析上要敢于猜想错误出现的原因,并进行迅速的验证和分析,来解决问题,而不是一拖再拖 总而言之,自己还有很多不足的地方需要改善了学习,come on! 最后附两张截图吧,感兴趣的朋友可以 github
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 前言 elasticsearch,logstash随着kibana的命名升级直接从2.4跳跃到了5.0,5.x版本的elk在版本对应上要求相对较高,不再支持5.x和2.x的混搭,同时elastic做了一个package,对原本的watch,alert做了一个封装,形成了x-pack,也就是下面我们要讨论的内容 简介 x-pack是elasticsearch的一个扩展包,将安全,警告,监视,图形和报告功能捆绑在一个易于安装的软件包中,虽然x-pack被设计为一个无缝的工作,但是你可以轻松的启用或者关闭一些功能 安装 Elasticsearh:bin/elasticsearch-plugin install x-pack (es如果是一个集群,在每一个节点上安装) Kibana:bin/kibana-plugin install x-pack 用户管理 x-pack安装之后有一个超级用户elastic ,其默认的密码是changeme,拥有对所有索引和数据的控制权,可以使用该用户创建和修改其他用户,当然这里可以通过kibana的web界面进行用户和用户组的管理 也可以使用shell 终端进行管理: 修改elastic用户的密码: curl -XPUT -u elastic 'localhost:9200/_xpack/security/user/elastic/_password' -d '{ "password" : "123456" }' 修改kibana用户的密码: curl -XPUT -u elastic 'localhost:9200/_xpack/security/user/kibana/_password' -d '{ "password" : "123456" }' 创建用户组和角色,创建所属用户 eg:创建beats_admin用户组,该用户组对filebeat*有all权限,对.kibana*有manage,read,index权限 curl -XPOST -u elastic 'localhost:9200/_xpack/security/role/beats_admin' -d '{ "indices" : [ { "names" : [ "filebeat*" ], "privileges" : [ "all" ] }, { "names" : [ ".kibana*" ], "privileges" : [ "manage", "read", "index" ] } ] }' 创建jockbeat用户,密码是jockbeat curl -XPOST -u elastic 'localhost:9200/_xpack/security/user/jockbeat' -d '{ "password" : "jockbeat", "full_name" : "jock beat", "email" : "john.doe@anony.mous", "roles" : [ "beats_admin" ] }' 保护级别 X-pack提供以下几个级别保护elastic集群 用户验证 授权和基于角色的访问控制 节点/客户端认证和信道加密 审计 1:启动消息身份验证,验证消息在传输的过程中未被篡改或者修改 bin/x-pack/syskeygen 如果es是一个集群,请将生成的密钥复制到集群的其他节点 2:启动审核以跟踪与您的elasticsearch集群的尝试与成功的交互 vim config/elasticsearch.yml xpack.security.audit.enabled: true x-pack的权限保护 安装完x-pack,登录elasticsearch和kibana会让你输入密码 X-pack的elk之间的数据传递保护 安装完x-pack之后,我们就可以用我们所创建的用户来保护elk之间的数据传递 1:kibana<——>elasticsearch 在kibana.yml文件中配置: elasticsearch.username: “elastic” elasticsearch.password: “changeme” 2:logstash<——>elasticsearch 在自己写的配置文件中定义 input { stdin{} beats{ port => 5044 } } output { elasticsearch { hosts => ["http://localhost:9200"] user => elastic password => changeme } stdout{ codec=>rubydebug } } 这里如果不进行相关配置的话,elk之间的数据传递就会出现问题 x-pack的监控功能 取消X-Pack监控组件使您能够通过Kibana轻松监控Elasticsearch。 您可以实时查看集群运行状况和性能,以及分析过去的集群,索引和节点指标。 此外,您可以监控Kibana本身的性能。 在群集上安装X-Pack时,监视代理会在每个节点上运行,以从Elasticsearch收集索引指标。 通过在Kibana中安装X-Pack,您可以通过一组专用仪表板查看监视数据 elasticsearch: kibana: 配置监控的索引: 编辑elasticsearch.yml,假设监控logstash-*,index1,test2索引 xpack.monitoring.collection.indices: logstash-*, index1, test2 您可以在前面加上+或 - 来显式包含或排除索引名称或模式。 例如,要包括以test开头的所有索引(test3除外),可以指定+ text *, - test3。 设置单独的集群监控: https://www.elastic.co/guide/en/x-pack/current/monitoring-cluster.html x-pack的alert 总体上来将和之前的单独产品watch并没有什么太大的区别,这里我们只是简单的看下x-pack的alert功能,更多详细内容参考官网 https://www.elastic.co/guide/en/x-pack/current/xpack-alerting.html 当watch被触发的时候,数据将会被加载到执行的context中,watch支持四种输入: Simple:加载静态的数据到execution context中 Search:加载搜索的结果到execution context中 Http:将Http请求的结果加载到execution context中 chain:使用一系列输入将数据加载到execution context中 每个watch必须有一个触发器触发watch的执行开始,watch旨在支持不同类型的的触发器,但只有基于时间戳的计划触发器目前可用,watch提供了以下几种类型的时间过滤器 Hourly Daily Weekly Monthly Yearly Cron Interval 例如创建一个每小时的watch监控(每半个小时启动一次) { "trigger" : { "schedule" : { "hourly" : { "minute" : 30 } } } } 配置一个每小时多时间的watch(每15分钟启动一次) { "trigger" : { "schedule" : { "hourly" : { "minute" : [ 0, 15, 30, 45 ] } } } } x-pack的Graph https://www.elastic.co/guide/en/x-pack/current/graph-getting-started.html
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 前言 hive 2.x版本出来已经有一段时间了,目前的2.x中的稳定版本为2.1.0 github地址:https://github.com/apache/hive/tree/master 官方下载地址为:https://mirrors.tuna.tsinghua.edu.cn/apache/hive/ 工作之余,我们就来看看hive2.1.0这个版本相对于1.2来说的change 部署 之前的一篇hive1.2的mysql部署文章: http://blog.csdn.net/gamer_gyt/article/details/52032579 hive2.1.0相对1.2来讲部署上并没有什么变化,但就算是温习吧,我们依旧走一遍这个过程,看看有哪些坑等着我们去踩。 1:下载文件 2:解压至指定目录 我这里hive的存放目录是/opt/bigdata/hive,并重命名为hive tar -zxvf /home/thinkgamer/下载/apache-hive-2.1.0-bin.tar.gz -C /opt/bigdata/ mv apache-hive-2.1.0-bin/ hive 3:Mysql创建hive21用户 创建hive21用户,赋予权限,清除缓存 CREATE USER 'hive21' IDENTIFIED BY 'hive21'; grant all privileges on *.* to 'hive21' with grant option; flush privileges; 4:拷贝msyql jar包到hive/lib cp /path/to/mysql-connector-java-5.1.38-bin.jar hive/lib 5:修改配置文件 (1):javax.jdo.option.ConnectionURL <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:mysql://localhost:3306/hive21?createDatabaseIfNotExist=true&amp;useUnicode=true&amp;characterEncoding=UTF-8</value> (2):javax.jdo.option.ConnectionDriverName <name>javax.jdo.option.ConnectionDriverName</name> <value>com.mysql.jdbc.Driver</value> (3):javax.jdo.option.ConnectionUserName <name>javax.jdo.option.ConnectionUserName</name> <value>hive21</value> (4):javax.jdo.option.ConnectionPassword <name>javax.jdo.option.ConnectionPassword</name> <value>hive21</value> 6:启动/测试 bin/hive hive> show databases; OK default Time taken: 1.123 seconds, Fetched: 1 row(s) hive> create table table_name ( > id int, > dtDontQuery string, > name string > ); OK Time taken: 0.983 seconds hive> show tables; OK table_name Time taken: 0.094 seconds, Fetched: 1 row(s) 这个时候进入mysql数据库有一个hive21的数据库 mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | hive | | hive21 | | mysql | | performance_schema | +--------------------+ 7:踩过的坑 (1):没有初始化hive元数据库 报错如下: Caused by: MetaException(message:Hive metastore database is not initialized. Please use schematool (e.g. ./schematool -initSchema -dbType ...) to create the schema. If needed, don't forget to include the option to auto-create the underlying database in your JDBC connection string (e.g. ?createDatabaseIfNotExist=true for mysql)) 解决办法: bin/schematool -initSchema -dbType mysql --verbose 此问题解决时在网上查阅资料有人说这里要初始化derby数据库,个人认为这是不正确的,因为我们已经配置使用了mysql作为元数据库 (2):未配置日志和缓存目录 报错如下: Logging initialized using configuration in file:/opt/bigdata/hive/conf/hive-log4j2.properties Async: true Exception in thread "main" java.lang.IllegalArgumentException: java.net.URISyntaxException: Relative path in absolute URI: ${system:java.io.tmpdir%7D/$%7Bsystem:user.name%7D at org.apache.hadoop.fs.Path.initialize(Path.java:205) at org.apache.hadoop.fs.Path.<init>(Path.java:171) at org.apache.hadoop.hive.ql.session.SessionState.createSessionDirs(SessionState.java:631) at org.apache.hadoop.hive.ql.session.SessionState.start(SessionState.java:550) at org.apache.hadoop.hive.ql.session.SessionState.beginStart(SessionState.java:518) at org.apache.hadoop.hive.cli.CliDriver.run(CliDriver.java:705) at org.apache.hadoop.hive.cli.CliDriver.main(CliDriver.java:641) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.hadoop.util.RunJar.run(RunJar.java:221) at org.apache.hadoop.util.RunJar.main(RunJar.java:136) Caused by: java.net.URISyntaxException: Relative path in absolute URI: ${system:java.io.tmpdir%7D/$%7Bsystem:user.name%7D at java.net.URI.checkPath(URI.java:1823) at java.net.URI.<init>(URI.java:745) at org.apache.hadoop.fs.Path.initialize(Path.java:202) ... 12 more 解决办法: 修改 hive-site.xml 替换${system:java.io.tmpdir} 和 ${system:user.name}为/opt/bigdata/hive/tmp hive2.1 和hive1.2的简单比较 hive1.2 [master@master1 hive]$ bin/hive --service help Usage ./hive <parameters> --service serviceName <service parameters> Service List: beeline cli help hiveburninclient hiveserver2 hiveserver hwi jar lineage metastore metatool orcfiledump rcfilecat schemaTool version Parameters parsed: --auxpath : Auxillary jars --config : Hive configuration directory --service : Starts specific service/component. cli is default Parameters used: HADOOP_HOME or HADOOP_PREFIX : Hadoop install directory HIVE_OPT : Hive options For help on a particular service: ./hive --service serviceName --help Debug help: ./hive --debug --help hive2.1 root@thinkgamer-pc:/opt/bigdata/hive# bin/hive --service help Usage ./hive <parameters> --service serviceName <service parameters> Service List: beeline cleardanglingscratchdir cli hbaseimport hbaseschematool help hiveburninclient hiveserver2 hplsql hwi jar lineage llapdump llap llapstatus metastore metatool orcfiledump rcfilecat schemaTool version Parameters parsed: --auxpath : Auxillary jars --config : Hive configuration directory --service : Starts specific service/component. cli is default Parameters used: HADOOP_HOME or HADOOP_PREFIX : Hadoop install directory HIVE_OPT : Hive options For help on a particular service: ./hive --service serviceName --help Debug help: ./hive --debug --help 我们可以看到在hive2.1中增加了对hbase的支持,同时还增加了hplsql等等,这些都是hive2.1的新特性,这里介绍几个常用的 beeline:和hive1.2中beeline使用方法应该是一样的,至于性能方面的提升肯定是有的,beeline的使用,参考 http://blog.csdn.net/gamer_gyt/article/details/52062460 cleardanglingscratchdir:scratch directory(清楚缓存) 使用方法: bin/hive –service cleardanglingscratchdir hbaseimport/hbaseschematool:与Hbase进行交互 hiveserver2:提供一个JDBC接口,供外部程序操作hive hplsql:一个工具,实现sql在Apache hive,sparkSql,以及其他基于hadoop的sql,Nosql和关系数据库的使用 官方解释: HPL/SQL (previously known as PL/HQL) is an open source tool (Apache License 2.0) that implements procedural SQL language for Apache Hive, SparkSQL as well as any other SQL-on-Hadoop implementations, NoSQL and RDBMS. HPL/SQL language is compatible to a large extent with Oracle PL/SQL, ANSI/ISO SQL/PSM (IBM DB2, MySQL, Teradata i.e), PostgreSQL PL/pgSQL (Netezza), Transact-SQL (Microsoft SQL Server and Sybase) that allows you leveraging existing SQL/DWH skills and familiar approach to implement data warehouse solutions on Hadoop. It also facilitates migration of existing business logic to Hadoop. HPL/SQL is an efficient way to implement ETL processes in Hadoop. LLAP:也是hive2.1引入的新特性,大概就是提升hive2.1的执行时间,具体可参考: http://zh.hortonworks.com/blog/llap-enables-sub-second-sql-hadoop/ 下面附一张从网上看到的图片:
打开微信扫一扫,关注微信公众号【数据与算法联盟】 http://blog.csdn.net/gamer_gyt/article/details/52976232转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer Docker江湖 【Docker江湖】之Docker部署与理解 【Docker江湖】之hub上镜像的使用,Dockerfile语法解读和数据管理 【Docker江湖】之创建带有SSH服务的镜像 【Docker江湖】之创建Web应用服务镜像——Apache/Nginx 写在前边的话 Web应用服务器的价格是真的贵,而且在一个web服务器上创建多个web应用也不是很方便,那么有没有好办法来解决这个问题,答案是肯定的,有了Docker那就可以在在一个服务器上创建多个docker容器了,每个容器启动一个web服务,这要管理起来也相对方便 Start Apache 1:创建一个apache-ubuntu的工作目录 mkdir apache-ubuntu cd apache-ubuntu/ vim Dockerfile 在Docker中添加以下内容(thinkgamer/sshd_dockerfile:latest是在上一篇博客中创建的sshd镜像) FROM thinkgamer/sshd_dockerfile:latest #设置继承自我们创建的sshd镜像 #设置环境变量,所有操作都是非交互的 ENV DEBIAN_FRONTEND noninteractive #安装 RUN apt-get -yq install apache2 && rm -rf /var/lib/apt/lists/* RUN echo "Asia/Shanghai" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata #注意这里要更改系统的时区,因为在web应用中经常会用到时区这个系统变量,默认的ubuntu会让你的应用程序发生不可思议的效果 #添加我们的脚本,并设置权限,这会覆盖之前放在这个位置的脚本 ADD run.sh /run.sh RUN chmod 755 /*.sh #添加一个示例的web站点,删除默认安装在apache文件夹下面的文件,并将我们添加的示例用软连接链接到/var/www/html目录下面 RUN mkdir -p /var/lock/apache2 RUN rm -fr /var/www/html/* COPY sample/* /var/www/html/ #设置Apache相关的一些变量,在容器启动的时候可以使用-e参数替代 ENV APACHE_RUN_USER www-data ENV APACHE_RUN_GROUP www-data ENV APACHE_LOG_DIR /var/log/apache2 ENV APACHE_PID_FILE /var/run/apache2.pid ENV APACHE_RUN_DIR /var/run/apache2 ENV APACHE_LOCK_DIR /var/lock/apache2 ENV APACHE_SERVERADMIN admin@localhost ENV APACHE_SERVERNAME localhost ENV APACHE_SERVERALIAS docker.localhost ENV APACHE_DOCUMENTROOT /var/www EXPOSE 80 WORKDIR / CMD ["/run.sh"] 这里我们只做测试,所以sample的站点内容相对简单 在apache-ubunt目录下,创建sample目录 mkdir sample cd sample vim index.html index.html内容为 <!DOCTYPE html> <html> <body> <p> hello docker</p> </body> </html> 在apache-ubuntu目录下创建执行 vim run.sh 添加以下内容 $cat run.sh #!/bin/bash exec apache2 -D FOREGROUND 2:build镜像 在apache-ubuntu目录下执行 sudo docker build -t apache:ubuntu . 最终会输出类似下面这样的信息 Removing intermediate container 0933c4f11b73 Successfully built f244e1db152e 查看我们build的镜像 sudo docker images [redhat@localhost apache-ubuntu]$ sudo docker images [sudo] password for redhat: REPOSITORY TAG IMAGE ID CREATED SIZE apache ubuntu 821f09bee815 8 minutes ago 310.3 MB thinkgamer/sshd_dockerfile latest f530a7a43fd7 33 hours ago 220.6 MB thinkgamer/sshd_ubuntu latest cc0c1d242d82 34 hours ago 255 MB ubuntu latest f753707788c5 2 weeks ago 127.1 MB 3:测试镜像 运行镜像,并使用-p参数映射需要开放的端口(22和80端口) [redhat@localhost apache-ubuntu]$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8059064935c5 apache:ubuntu "/bin/bash" 5 minutes ago Up 5 minutes 0.0.0.0:80->80/tcp, 0.0.0.0:10022->22/tcp clever_yalow [redhat@localhost apache-ubuntu]$ sudo docker exec -it 80 /bin/bash root@8059064935c5:/# ./run.sh ./run.sh: line 1: run.sh: command not found AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message 在宿主机用curl抓取网页来验证刚才创建的sample站点 curl localhost <!DOCTYPE html> <html> <body> <p> hello docker</p> </body> </html> 4:Docker创建的镜像拥有继承的特性 需要说明的是在apache镜像的dockerfile中只用EXPOSE定义了对外开放的80端口,而在查看容器的时候,是映射两个端口:22和80 但是实际上,当尝试使用SSH登录到容器时,会发现无法登录,这是因为在run.sh中并未启动ssh服务,这说明在使用Dockerfile创建镜像时,会继承父镜像的开放端口,但却不会继承启动命令,因此,需要在run.sh脚本中添加启动sshd的服务的命令: $ cat run.sh #!/bin/bash /usr/sbin/sshd & exec apache2 -D FOREGROUND 再次创建镜像 sudo docker build -t apache:ubuntu . 这次创建的镜像将会同时启动ssh和Apache服务 5:映射本地目录 可以通过映射本地目录的方式来指定容器内Apache服务相应的内容,这个时候只需要在启动命令中加入 -v参数即可 Start Nginx 1:创建nginx-ubuntu目录 mkdir nginx-ubuntu vim Dockerfile #设置继承自己创建的sshd镜像 FROM thinkgamer/sshd_dockerfile:latest #创建者的基本信息 MAINTAINER http://blog.csdn.net/gamer_gyt #安装nginx,设置nginx以非daemon启动 RUN apt-get install -y nginx && rm -rf /var/lib/apt/lists/* RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf && chown -R www-data:www-data /var/lib/nginx RUN echo "Asia/Shanghai" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata #注意这里要更改系统的时区,因为在web应用中会常用到时区这个系统变量,默认的ubuntu会让你的应用程序发送不可思议的效果 #添加我们的脚本,并设置权限,这回覆盖之前放在这个位置的脚本 ADD run.sh /run.sh RUN chmod 755 /*.sh #定义可以被挂载的目录,分别是虚拟主机的挂载目录,证书目录,配置目录和日志目录 VOLUME ["/etc/nginx/sites-enabled","/etc/nginx/certs","/etc/nginx/conf.d","/var/log/nginx"] #定义工作目录 WORKDIR /etc/nginx #定义输出命令 CMD ["/run.sh"] #定义输出端口 EXPOSE 80 EXPOSE 443 创建run.sh $cat run.sh #!/bin/bash /usr/sbin/sshd & /usr/sbin/nginx 2:build镜像 sudo docker build -t thinkgamer/nginx:a2_u16 3:测试 启动并进入容器 [redhat@bogon nginx-ubuntu]$ sudo docker run -it -d -p 10022:22 -p 80:80 thinkgamer/nginx:a2_u16 /bin/bash 074466a9a455ee581fcdd21f6195a4575eb7dbb9ee50aa341dd093ca369b2246 [redhat@bogon nginx-ubuntu]$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 074466a9a455 thinkgamer/nginx:a2_u16 "/bin/bash" 3 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp, 443/tcp, 0.0.0.0:10022->22/tcp silly_bartik [redhat@bogon nginx-ubuntu]$ sudo docker exec -it 07 /bin/bash 执行run.sh /run.sh web访问ip
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer Docker江湖 【Docker江湖】之Docker部署与理解 【Docker江湖】之hub上镜像的使用,Dockerfile语法解读和数据管理 【Docker江湖】之创建带有SSH服务的镜像 写在前边的话 一般情况下,linux操作系统的管理员通过SSH服务来管理操作系统,但是Docker的很多镜像都是不带SSH服务的,接下来我们就来看一下如何创建一个带有SSH服务的镜像 基于Commit命令创建 1:准备一个ubuntu的镜像 sudo docker pull ubuntu 默认安装最新版,查看镜像 sudo docker images 这个时候便可以看到我们pull的ubuntu镜像了 2:启动镜像,进入容器 [redhat@localhost ~]$ sudo docker run -it -d ubuntu:latest /bin/bash [sudo] password for redhat: e968f75ffc88881377ac0b5b74bd273c3c516544ff7d6270a1683aa676da3d6c [redhat@localhost ~]$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e968f75ffc88 ubuntu:latest "/bin/bash" 18 seconds ago Up 16 seconds cranky_stallman [redhat@localhost ~]$ sudo docker exec -it e9 /bin/bash 尝试使用sshd命令,会发现容器中并没有安装此命令 root@e968f75ffc88:/# sshd bash: sshd: command not found 尝试安装openssh-server root@e968f75ffc88:/# apt-get install openssh-server Reading package lists... Done Building dependency tree Reading state information... Done E: Unable to locate package openssh-server 更新软件源 apt-get update 3:安装和配置ssh服务 apt-get install openssh-server 要正常启动ssh服务,需要目录/var/run/sshd存在,手动创建他,并启动服务 mkdir -p /var/run/sshd /usr/sbin/sshd -D & 此时查看容器的22端口(SSH服务默认监听的端口),已经处于监听状态 apt-get install net-tools netstat -tunlp root@e968f75ffc88:/# netstat -tunlp Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 3551/sshd tcp6 0 0 :::22 :::* LISTEN 3551/sshd 修改SSH服务的安全登录配置,取消pam登陆限制 sed -ri ‘s/session required pam_loginuid.so/#session required pam_loginuid.so/g’ /etc/pam.d/sshd 在root用户目录下创建.ssh目录,并复制需要登录的公钥信息(一般为本地主机用户目录下的.ssh/id_rsd.pub文件,可由ssh-keygen -t rsa命令生成)到authorized_keys文件中 mkdir root/.ssh apt-get install vim vim /root/.ssh/authorized_keys 创建自动启动SSH服务的可执行文件run.sh,并添加可执行权限: vim /run.sh chmod +x run.sh run.sh的内容如下: #!/bin/bash /usr/sbin/sshd -D 最后退出容器: exit 4:保存镜像 sudo docker commit fcl sshd:ubuntu 查看本地镜像,就会看到新生成的镜像 ``` [redhat@localhost ~]$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e968f75ffc88 ubuntu:latest "/bin/bash" About an hour ago Exited (0) About a minute ago cranky_stallman [redhat@localhost ~]$ sudo docker commit e96 sshd:ubuntu sha256:f52e07fa7accf437f52cb39cd36cdab9229ef88b2280129ff4d2c272fbb73aad [redhat@localhost ~]$ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE sshd ubuntu f52e07fa7acc About a minute ago 255 MB ubuntu latest f753707788c5 2 weeks ago 127.1 MB 使用镜像,并添加端口映射(10022–>22),其中10022是宿主主机的端口,22是容器SSH服务监听端口 sudo docker run -it -d -p 10022:22 sshd:ubuntu_new /run.sh SSH测试登录 ssh you_ip -p 10022 [root@localhost .ssh]# ssh 192.168.10.179 -p 10022 The authenticity of host '[192.168.10.179]:10022 ([192.168.10.179]:10022)' can't be established. ECDSA key fingerprint is 0b:ae:62:09:a2:18:4e:ef:16:e3:3f:b9:2d:15:fb:7a. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '[192.168.10.179]:10022' (ECDSA) to the list of known hosts. Welcome to Ubuntu 16.04.1 LTS (GNU/Linux 3.10.0-229.el7.x86_64 x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. root@e498e20668d9:~# exit logout Connection to 192.168.10.179 closed. 使用Dockerfile创建 1:创建工作目录 首先创建一个sshd_ubuntu目录 mkdir ssh_ubuntu 在其中,创建Dockerfile和run.sh文件 [root@localhost mydockerfile]# cd ssh_ubuntu/ [root@localhost ssh_ubuntu]# touch Dockerfile run.sh [root@localhost ssh_ubuntu]# ls Dockerfile run.sh [root@localhost ssh_ubuntu]# 2:编写run.sh脚本和authorized_keys文件 脚本文件run.sh的内容与上面的内容一致: #!/bin/bash /usr/sbin/sshd -D 在宿主主机上生成SSh密钥对,并创建authorized_keys文件: cat ~/.ssh/id_rsa.pub > authorized_keys 3:编写Dockerfile 下面是Dockerfile内容及各部分注释,和上边commit镜像的步骤操作是一致的 #设置继承镜像 FROM ubuntu:latest #运行命令 RUN apt-get update #安装ssh RUN apt-get install -y openssh-server RUN mkdir -p /var/run/sshd RUN mkdir -p /root/.ssh #取消pam限制 RUN sed -ri 's/session required pam_loginuid.so/#session required pam_loginuid.so/g' /etc/pam.d/sshd #复制配置文件到相应位置,并赋予脚本可执行权限 ADD authorized_keys /root/.ssh/authorized_keys ADD run.sh /run.sh RUN chmod 755 /run.sh #开放端口 EXPOSE 22 #设置自启动命令 CMD ["/run.sh"] 4:创建镜像 在sshd_ubuntu 目录下,使用docker build 命令来创建镜像,注意一下,在最后还有一个“.” ,表示使用当前目录中的Dockerfile cd sshd_ubuntu sudo docker build -t sshd:dockerfile . 这里有一点需要注意的是使用Dockerfile创建自定义镜像,docker会自动删除中间临时创建的层,还需要注意每一步的操作和编写的dockerfile中命令的对应关系 执行docker build命令的输出参考结果如下: 命令执行完毕后,如果可见 “successfully build XXX”字样,则说明镜像创建成功,可以看到,以上命令生成的镜像ID是 Step 11 : CMD /run.sh ---> Running in 69e4227186fb ---> f530a7a43fd7 Removing intermediate container 69e4227186fb Successfully built f530a7a43fd7 在本地查看sshd:dokcerfile镜像已存在: [root@localhost ssh_ubuntu]# sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE sshd dockerfile f530a7a43fd7 About a minute ago 220.6 MB sshd ubuntu cc0c1d242d82 38 minutes ago 255 MB 5:测试镜像,运行容器 使用刚才创建的sshd:dockerfile镜像来运行一个容器,直接启动镜像,映射容器的22端口到本地10122端口: sudo docker run -d -p 10122:22 sshd:dockerfile ssh 192.168.10.179 -p 10122 显示: [root@localhost .ssh]# ssh 192.168.10.179 -p 10122 The authenticity of host '[192.168.10.179]:10122 ([192.168.10.179]:10022)' can't be established. ECDSA key fingerprint is 0b:ae:62:09:a2:18:4e:ef:16:e3:3f:b9:2d:15:fb:7a. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '[192.168.10.179]:10022' (ECDSA) to the list of known hosts. Welcome to Ubuntu 16.04.1 LTS (GNU/Linux 3.10.0-229.el7.x86_64 x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. root@e498e20668d9:~# exit logout Connection to 192.168.10.179 closed. Over!
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 elasticsearch的alpha版本早已经在github上了,但是beta版本却是最近才正式发布,浏览其http://elastic.co/ 官网,我们可以发现整个style都变了,在尝试安装elkb 5.0的过程中,出现了一点小差错,记录下来,并附上解决办法,以便更多的人能够受益。 错误记录 max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144] max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536] 解决办法 先解决第一条错误 max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144] 原因分析: 操作系统的vm.max_map_count参数设置太小导致的 解决办法: 这里我们可以直接修改docker本身的参数,当然也可以直接修改宿主机本身的配置文件,需要注意的是在docker 容器中执行 sysctl -w vm.max_map_count=655360 会提示错误: sysctl: setting key “vm.max_map_count”: Read-only file system 这是因为Docker的base image做的很精简,甚至都没有init进程,原本在OS启动时执行生效系统变量的过程(sysctl -p)也给省略了,导致这些系统变量依旧保留着kernel默认值,这时候需要我们在容器启动时加入 –privileged 来获取修改系统参数的权限 这里我选择的是修改宿主机本身的配置文件,然后重新启动镜像,也能解决问题,退出容器,返回到宿主机 修改vm.max_map_count 可以通过命令行修改,但是在机器重启时会失效,所以通过修改配置文件来解决问题 命令行修改办法: sudo sysctl -w vm.max_map_count=655360 并用以下命令查看是否修改成功 sysctl -a | grep “vm.max_map_count” 修改配置文件: sudo vim /etc/sysctl.conf 加入: vm.max_map_count=262144 保存即可 接下来解决 max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536] 这个问题 sudo vim /etc/security/limits.conf 加入以下两行: redhat hard nofile 65536 redhat soft nofile 65536 redhat这里为用户名 然后重启启动elasticsearch即可 附一张kibana 5.0的图片
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 在之前的一篇文章中介绍了Shield在Elk Stack中的权限保护,但由于Shield是收费的,所以就有人给出了免费的解决方案——Search-guard 简单说明 search-guard是elastcisearch的一款插件,提供加密,身份验证和授权,基于search guard SSL,另外提供可插入的身份验证/授权模块,search-guard是shield的替代品,可免费提供所有的基本安全功能,其功能特性: 基于用户和角色的权限控制 支持SSL和TLS方式安全认证 支持LDAP认证 环境说明 Ubuntu 16.04 Elasticsearch 2.4.1 Logstash 2.4.0 Kibana 4.6.1 部署 Elasticsearch配置search-guard 进入elasticsearch的根目录 安装search-guard-ssl bin/plugin install -b com.floragunn/search-guard-ssl/2.4.1.16 这里我们需要配置密钥和证书 依旧是在es 的根目录 git clone https://github.com/floragunncom/search-guard-ssl.git cd searchguard_ssl/example-pki-scripts 执行 ./example.sh 会默认生成证书 当然这里我们可以执行clean.sh删除安装的东西 拷贝相应的文件到指定目录,后续会有需要 cp node-0-keystore.jks ../../config cp truststore.jks ../../config 编辑ec根目录下的config/elasticsearch.yml,加入 searchguard.ssl.transport.keystore_filepath: node-0-keystore.jks searchguard.ssl.transport.keystore_password: changeit searchguard.ssl.transport.truststore_filepath: truststore.jks searchguard.ssl.transport.truststore_password: changeit searchguard.ssl.transport.enforce_hostname_verification: false web访问:http://192.168.1.193:9200/ 会看到如下信息 { "name" : "M-Twins", "cluster_name" : "elasticsearch", "cluster_uuid" : "X6U0RRiaQ4ucBXokFj30Yw", "version" : { "number" : "2.4.1", "build_hash" : "c67dc32e24162035d18d6fe1e952c4cbcbe79d16", "build_timestamp" : "2016-09-27T18:57:55Z", "build_snapshot" : false, "lucene_version" : "5.5.2" }, "tagline" : "You Know, for Search" } web访问 http://192.168.1.193:9200/_searchguard/sslinfo?pretty { "principal" : null, "peer_certificates" : "0", "ssl_protocol" : null, "ssl_cipher" : null, "ssl_openssl_available" : false, "ssl_openssl_version" : -1, "ssl_openssl_version_string" : null, "ssl_openssl_non_available_cause" : "java.lang.ClassNotFoundException: org.apache.tomcat.jni.SSL", "ssl_provider_http" : null, "ssl_provider_transport_server" : "JDK", "ssl_provider_transport_client" : "JDK" } 在config/elasticsearch.yml配置文件中加入 #configure https searchguard.ssl.http.enabled: true searchguard.ssl.http.keystore_filepath: node-0-keystore.jks searchguard.ssl.http.keystore_password: changeit searchguard.ssl.http.truststore_filepath: truststore.jks searchguard.ssl.http.truststore_password: changeit https://192.168.1.193:9200/ (会提示证书错误) { "name" : "Zero", "cluster_name" : "elasticsearch", "cluster_uuid" : "X6U0RRiaQ4ucBXokFj30Yw", "version" : { "number" : "2.4.1", "build_hash" : "c67dc32e24162035d18d6fe1e952c4cbcbe79d16", "build_timestamp" : "2016-09-27T18:57:55Z", "build_snapshot" : false, "lucene_version" : "5.5.2" }, "tagline" : "You Know, for Search" } https://192.168.1.193:9200/_searchguard/sslinfo?pretty (会提示证书错误) { "principal" : null, "peer_certificates" : "0", "ssl_protocol" : "TLSv1.2", "ssl_cipher" : "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "ssl_openssl_available" : false, "ssl_openssl_version" : -1, "ssl_openssl_version_string" : null, "ssl_openssl_non_available_cause" : "java.lang.ClassNotFoundException: org.apache.tomcat.jni.SSL", "ssl_provider_http" : "JDK", "ssl_provider_transport_server" : "JDK", "ssl_provider_transport_client" : "JDK" } 配置客户认证 searchguard.ssl.http.clientauth_mode: REQUIRE 出现web不能访问的情况,由于该配置是可选的,所以暂时不配置 这里需要解释一下: 默认执行的./example.sh 是以kiri为用户名的,这个我们可以通过vim example.sh可以看的出来,在elasticsearch.yml中的**.passwordd可以自己设置 安装search-guard bin/plugin install -b com.floragunn/search-guard-2/2.4.1.7 安装之后不做任何设置再次重启elasticsearch,web访问会出现以下状况: Search Guard not initialized (SG11) 这是提示我们没有进行初始化 编辑config/elasticsearch.yml,加入以下两行 searchguard.authcz.admin_dn: - CN=kirk,OU=client,O=client,L=test, C=DE #由于我们是采用的默认的example.sh进行密钥生成的 - cn=admin,ou=Test,ou=ou,dc=company,dc=com 此时需要重新启动elasticsearch,因为需要把我们新更改的elasticsearch.yml加载进来,否则在初始化的时候会报错 复制 kirk-keystore.jks和truststore.jks到 plugins/search-guard-2/tools目录下,然后执行初始化命令 ./sgadmin.sh -ts truststore.jks -ks kirk-keystore.jks -cd ../sgconfig -icl 然后重启启动elasticsearch,web访问会提示你输入账号和密码 Logstash配置search-guard 依旧采用rsyslog的例子,机器配置rsyslog.conf,最后两行加入 *.* @@localhost:5000 *.* @localhost:5000 重启rsyslog服务 在logstash目录下编辑一个新文件rsyslog.conf,内容如下: input { tcp{ port => 5000 type => syslog } udp{ port => 5000 type => syslog } } output { stdout { codec=> rubydebug } elasticsearch { hosts => ["localhost:9200"] ssl => true ssl_certificate_verification => true truststore => "/opt/elk/elasticsearch-2.4.1/config/truststore.jks" truststore_password => changeit user => logstash password => logstash } } 这里的logstash用户在 elasticsearch安装目录下的plugin/seach-guard-2/sgconfig/sg_roles.yml中 这里我们说一下sgconfig这几个文件: Search-guard的动态配置 sg_config.yml:配置验证器和授权后端 sg_roles.yml:定义角色和相关的权限 sg_roles_mapping.yml:将后端角色,主机和用户映射到角色 sg_internal_users.yml:用户和散列密码(使用hasher.sh哈希) sg_action_groups.yml:组权限在一起 接着我们设置logstash用户的权限,我们可以在sg_roles.yml中方看到logstash用户具有的权限 sg_logstash: cluster: - indices:admin/template/get - indices:admin/template/put indices: 'logstash-*': '*': - CRUD - CREATE_INDEX '*beat*': '*': - CRUD - CREATE_INDEX 启动配置文件,测试登录,elasticsearch便可以接收到rsyslog发送过来的日志 Kibana配置 编辑kibana.yml server.port: 5601 server.host: "0.0.0.0" elasticsearch.url: "https://localhost:9200" elasticsearch.username: "kibanaserver" elasticsearch.password: "kibanaserver" elasticsearch.ssl.ca: /opt/elk/elasticsearch-2.4.1/search-guard-ssl/example-pki-scripts/kirk-signed.pem elasticsearch.ssl.verify: false 启动kibana服务,web访问,便会提示你输入密码 验证器配置: 验证器配置: Vim plugins/search-guard-2/sgconfig/sg_config.yml searchguard: dynamic: http: ... authc: kibana_auth_domain: enabled: true order: 1 http_authenticator: type: basic challenge: true authentication_backend: type: internal authz: ... 其他的自定义角色和角色对应的权限请参考官网资料对sgconfig文件夹的几个文件进行配置即可
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 Elastalert是Yelp公司用python2.6写的一个报警框架,github地址为 https://github.com/Yelp/elastalert 环境介绍 Ubuntu16.04 Elasticsearch 2.4.1 Logstash 2.4.0 Kibana 4.6.1 Elastalert的安装 Elastalert的安装相对比较简单,只需要按照步骤走就没有问题 git clone https://github.com/Yelp/elastalert.git 进入elastalert的目录,执行 Pip install -r requirements.txt Python setup.py install OK 安装完事 安装之后会自带三个命令 elastalert-create-index:ElastAlert会把执行记录存放到一个ES 索引中,该命令就是用来 创建这个索引的,默认情况下,索引名叫elastalert_status。其中有4个 _type,都有 自己的@timestamp字段,所以同样也可以用kibana,来查看这个索引的日志记录情况。 elastalert-rule-from-kibana:从Kibana3已保存的仪表盘中读取Filtering设置,帮助生成config.yaml里的配置。不过注意,它只会读取filtering,不包括queries。 elastalert-test-rule:测试自定义配置中的rule设置。 Elastalert支持的警告类型 Command Email JIRA OpsGenie SNS HipChat Slack Telegram Debug 至于每种是干什么的,参考官网资料吧,不过多解释 http://elastalert.readthedocs.io/en/latest/ config.ymal中的配置项 Rules_folder:用来加载下一阶段rule的设置,默认是example_rules Run_every:用来设置定时向elasticsearch发送请求 Buffer_time:用来设置请求里时间字段的范围,默认是45分钟 Es_host:elasticsearch的host地址 Es_port:elasticsearch 对应的端口号 Use_ssl:可选的,选择是否用SSL连接es,true或者false Verify_certs:可选的,是否验证TLS证书,设置为true或者false,默认为true Es_username:es认证的username Es_password:es认证的password Es_url_prefix:可选的,es的url前缀(我的理解是https或者http) Es_send_get_body_as:可选的,查询es的方式,默认的是GET Writeback_index:elastalert产生的日志在elasticsearch中的创建的索引 Alert_time_limit:失败重试的时间限制 Elastalert的rule规则 name:配置,每个rule需要有自己独立的name,一旦重复,进程将无法启动。 type:配置,选择某一种数据验证方式。 index:配置,从某类索引里读取数据,目前已经支持Ymd格式,需要先设置 use_strftime_index:true,然后匹配索引,配置形如:index: logstash-es-test%Y.%m.%d,表示匹配logstash-es-test名称开头,以年月日作为索引后缀的index。 filter:配置,设置向ES请求的过滤条件。 timeframe:配置,累积触发报警的时长。 alert:配置,设置触发报警时执行哪些报警手段。不同的type还有自己独特的配置选项。目前ElastAlert 有以下几种自带ruletype: any:只要有匹配就报警; blacklist:compare_key字段的内容匹配上 blacklist数组里任意内容; whitelist:compare_key字段的内容一个都没能匹配上whitelist数组里内容; change:在相同query_key条件下,compare_key字段的内容,在 timeframe范围内 发送变化; frequency:在相同 query_key条件下,timeframe 范围内有num_events个被过滤出 来的异常; spike:在相同query_key条件下,前后两个timeframe范围内数据量相差比例超过spike_height。其中可以通过spike_type设置具体涨跌方向是up,down,both 。还可以通过threshold_ref设置要求上一个周期数据量的下限,threshold_cur设置要求当前周期数据量的下限,如果数据量不到下限,也不触发; flatline:timeframe 范围内,数据量小于threshold 阈值; new_term:fields字段新出现之前terms_window_size(默认30天)范围内最多的terms_size (默认50)个结果以外的数据; cardinality:在相同 query_key条件下,timeframe范围内cardinality_field的值超过 max_cardinality 或者低于min_cardinality 一个小demo 这里我们还是测试rrsyslog发送日志的这个例子,就是启动ELK服务,rsyslog通过logstash把日志发送给elasticsearch,然后传送给kibana,logstash编写rsyslog_test.conf文件,内容为 input { tcp{ port => 5000 type => syslog } udp{ port => 5000 type => syslog } } output { stdout { codec=> rubydebug } elasticsearch { hosts => ["192.168.1.198:9200"] } } 然后启动ELK服务和rsyslog_test.conf文件 编辑elastalert下的configure.yaml文件 rules_folder: example_rules run_every: minutes: 1 buffer_time: minutes: 15 es_host: localhost es_port: 9200 writeback_index: elastalert_status alert_time_limit: days: 2 编辑example_rules/example_frequency.yaml 文件内容如下: es_host: localhost es_port: 9200 name: Example rule use_strftine_index: true type: frequency index: logstash-* num_events: 5 timeframe: hours: 1 filter: - term: _type: "syslog" alert: - "email" email: - "elastalert@example.com" 上边我们设置的事件次数是5,也就是说elaticseach记录的事件条件超过即发送email文件,这里的email文件我并没有配置,原因是测试没有成功,需要提供密码验证,没有解决,以后解决的话会在评论中给出,不过这里用debug替换下列verbose即可将邮件内容打印在窗口 启动elastalert服务,监听elasticsearch python -m elastalert.elastalert –debug –rule example_rules/example_frequency.yaml 这个时候我们的ELK已经启动了,再次启动终端,执行ssh localhost进行测试,只要在一分钟内连续输入几次的错误密码,这个时候就会在elastalert的终端看到类似下面这样的提示: INFO:elastalert:Queried rule Example rule from 2016-10-24 08:20 PDT to 2016-10-24 08:35 PDT: 34 / 34 hits INFO:elastalert:Skipping writing to ES: {'rule_name': 'Example rule', '@timestamp': '2016-10-24T15:35:00.120937Z', 'exponent': 0, 'until': '2016-10-24T15:36:00.120924Z'} INFO:elastalert:Alert for Example rule at 2016-10-24T15:34:24.422Z: INFO:elastalert:Example rule At least 5 events occurred between 2016-10-24 07:34 PDT and 2016-10-24 08:34 PDT @timestamp: 2016-10-24T15:34:24.422Z @version: 1 _id: AVf3VBTs1Cgl8RD3WDw9 _index: logstash-2016.10.24 _type: syslog host: 127.0.0.1 message: <38>Oct 24 08:34:24 ubuntu sshd[3097]: Connection closed by 127.0.0.1 port 52434 [preauth] type: syslog INFO:elastalert:Skipping writing to ES: {'hits': 34, 'matches': 1, '@timestamp': '2016-10-24T15:35:00.123939Z', 'rule_name': 'Example rule', 'starttime': '2016-10-24T15:20:00.031300Z', 'endtime': '2016-10-24T15:35:00.031300Z', 'time_taken': 0.09257292747497559} 说明整个告警体系已经OK了 邮件告警配置 上次在配置邮件告警时并没有取得成功,后来又尝试了几次,ok了 以下进行得操作基于上边得demo步骤 首先添加发送方: 在elastalert目录下编辑smtp_auth_file.yaml文件,加入你要使用发送邮件的账号和密码 这里我使用的是网易的163邮箱,这里的密码是你开启邮箱的POP3的客户端登陆密码,不是网页登陆邮箱的密码 user: "thinkgamer@163.com" password: "xxxxxxxx" 然后编辑你的配置文件,我这里使用 example_rules/example_frequency.yaml 别的配置正常配置,在alert之前加入 #SMTP协议的邮件服务器相关配置(我这里是腾讯企业邮箱) #smtp.163.com是网易163邮箱的smtp服务器 smtp_host: smtp.163.com smtp_port: 25 #用户认证文件,需要user和password两个属性 # smtp_auth_file.yaml,为刚才编辑的配置文件 smtp_auth_file: smtp_auth_file.yaml email_reply_to: thinkgamer@163.com from_addr: thinkgamer@163.com 修改email为你要接受邮件的邮箱 然后启动配置文件 python -m elastalert.elastalert --verbose --rule example_rules/example_frequency.yaml 测试: 查看邮件: Over!
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 在上一篇博客中ELK Stack之Shield介绍介绍了Shiel的简单部署和一些工作机制,本篇博客就结合实例演示一下Shield在elk stack中的权限保护 Shield是什么 Shield作为插件安装到elasticsearch中,一旦安装,插件会拦截入栈API调用,以强制执行身份验证和授权,插件还可以使用Secure Sockets Layer/Transport Layer Security (SSL/TLS)为来自网络和elasticsearch的网络流量提供加密,该插件还是用API拦截层,该层使身份验证和授权能够提供审计日志记录功能。 为什么要使用Shield ELK是一个开源的日志分析平台,可以对各类日志进行分析和研究,但是有一个缺陷就是无法对用户身份进行验证,造成的直接后果就是任何人都可以访问和查看数据,从而我们需要这样一个插件来对elk的访问做一个权限控制,elastic官方给出的是使用shield,当然也有开源的产品替代search-guard,下边我们就先来看下elastic官方给出的shield在elk中的使用。 环境说明 Ubuntu:16.04 Elasticsearch:2.4.0 Logstash:2.4.0 Kibana:4.6.1 Java:1.8.0_101 安装Shield到Elasticsearch 这里我们采用在线安装 bin/plugin install license bin/plugin install shield 测试安装是否成功 Curl -X GET http://loclahost:9200 如果报错类似于下面这样,说明安装成功 {"error":{"root_cause":[{"type":"security_exception","reason":"missing authentication token for REST request [/]","header":{"WWW-Authenticate":"Basic realm=\"shield\" charset=\"UTF-8\""}}],"type":"security_exception","reason":"missing authentication token for REST request [/]","header":{"WWW-Authenticate":"Basic realm=\"shield\" charset=\"UTF-8\""}},"status":401} 创建一个es_admin用户,用来登录elasticsearch,按提示输入密码 bin/shield/esusers useradd es_admin -r admin 用新建的用户名进行测试 curl -u es_admin:your_password -X GET http://localhost:9200 提示如下 { "name" : "Mauvais", "cluster_name" : "elasticsearch", "version" : { "number" : "2.4.0", "build_hash" : "ce9f0c7394dee074091dd1bc4e9469251181fc55", "build_timestamp" : "2016-08-29T09:14:17Z", "build_snapshot" : false, "lucene_version" : "5.5.2" }, "tagline" : "You Know, for Search" } OK Kibana添加用户权限 为了给kibana加用户访问权限,同时为让kibana可以访问elasticsearch的数据,需要给kibana添加kibana server密码 创建一个用户用来访问elasticsearch master@ubuntu:/opt/elk/elasticsearch-2.4.0$ bin/shield/esusers useradd kibana4-server -r kibana4_server Enter new password: Retype new password: 接下来是比较重要的一个环节 1:利用openssl创建 server.key ,server.crt ,serverpem 切换到kibana的目录,创建server_ssl目录,进入该目录创建所需的文件 Key生成: openssl genrsa -des3 -out server.key 2048 这样是生成rsa私钥,des3算法,openssl格式,2048位强度。server.key是密钥文件名。为了生成这样的密钥,需要一个至少四位的密码。 利用以下命令创建没有密码的key,这里需要用到上边生成的server.key openssl rsa -in server.key -out server.key 这里会提示你输入密码,密码就是上一条命令你输入的密码 这里的server.key就是没有密码的版本 生成CA的Crt: openssl req -new -x509 -key server.key -out ca.crt -days 3650 会提示你输入相应的信息,除了Common Name (e.g. server FQDN or YOUR name) []:这条之外,别的都可以使用.代替,common name是用来进行https申请的,我这里写的是kibana的ip地址即本机的ip地址,因为我的elk在同一台机器上 Csr生成: openssl req -new -key server.key -out server.csr 提示信息如crt的生成 Crt生成: openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey server.key -CAcreateserial -out server.crt 输入key的密钥后,完成证书生成。-CA选项指明用于被签名的csr证书,-CAkey选项指明用于签名的密钥,-CAserial指明序列号文件,而-CAcreateserial指明文件不存在时自动生成。 最后生成了私用密钥:server.key和自己认证的SSL证书:server.crt 证书合并: cat server.key server.crt > server.pem 最后在server_sll目录下有这些文件 master@ubuntu:/opt/elk/kibana-4.6.1-Linux-x86_64/server_ssl$ ls ca.crt ca.srl server.crt server.csr server.key server.pem 2:配置Kibana 进入kibana的安装目录,执行:vim config/kibana.yml (1):指定访问elasticsearch的用户和密码 elasticsearch.username: "kibana4-server" elasticsearch.password: "kibana" (2):设置server.ssl.key /server.ssl.crt/serve.pem server.ssl.cert: /opt/elk/kibana-4.6.1-linux-x86_64/server_ssl/server.crt server.ssl.key: /opt/elk/kibana-4.6.1-linux-x86_64/server_ssl/server.key elasticsearch.ssl.ca: /opt/elk/kibana-4.6.1-linux-x86_64/server_ssl/server.pem (3):设置elasticsearch.url elasticsearch.url: "http://192.168.10.110:9200" (4):设置shield.encryptionKey shield.encryptionKey: "something_at_least_32_characters" (5):设置shield.sessionTimeout shield.sessionTimeout: 600000 3:在kibana中安装Shield bin/kibana plugin –install kibana/shield/latest Roles.yml文件的配置 roles.yml的位置 /opt/elk/elasticsearch-2.4.0/config/shield/roles.yml 我们拿其中一个举例说一下 #The required permissions for the kibana 4 server kibana4_server: #用户组 cluster: - monitor indices: #权限 - names: '.kibana*' #索引名称 privileges: #用户可对该索引执行的操作 - all #这里是给隶属于kibana4_server的用户所有的执行权限 - names: '.reporting-*' privileges: - all 编辑roles.yml,添加用户组 my_kibana_user 用户组,这里只给了read的权限 my_kibana_user: cluster: - monitor indices: - names: 'logstash-*' privileges: - view_index_metadata - read - names: '.kibana*' privileges: - manage - read - index 接下来创建一个kibana属于my_kibana_user用户组 master@ubuntu:/opt/elk/elasticsearch-2.4.0/config/shield$ /opt/elk/elasticsearch-2.4.0/bin/shield/esusers useradd kibana -r my_kibana_user Enter new password: Retype new password: master@ubuntu:/opt/elk/elasticsearch-2.4.0/config/shield$ /opt/elk/elasticsearch-2.4.0/bin/shield/esusers list logstash : logstash kibana4-server : kibana4_server es_admin : admin kibana : my_kibana_user logstash配置Shield 创建一个logstash用户,用来连接elasticsearch master@ubuntu:/opt/elk/elasticsearch-2.4.0$ bin/shield/esusers useradd logstash -r logstash Enter new password: Retype new password: 接着我们依旧测试Rsyslog发送日志 在logstash目录下便捷rsyslog.conf,加入 input { tcp{ port => 5000 type => syslog } udp{ port => 5000 type => syslog } } output { stdout { codec=> rubydebug } elasticsearch { hosts => ["192.168.10.110:9200"] user => "logstash" #logstash与elasticsearch交互的用户名 password => "logstash" #这个是logsatsh对应的密码 } } 配置rsyslog的配置文件 vim /etc/rsyslog.conf 加入以下两行,用于发送日志 *.* @@localhost:5000 *.* @localhost:5000 重启rsyslog服务 sudo /bin/systemctl restart rsyslog.service 启动ELK,进行测试 至此我们的整体环境已经编辑OK,使用es_admin登录,该用户所属用户组为admin,对elk有所有的执行权限 输入 https://192.168.10.110:5601 输入账号和密码,在shell 执行 ssh localhost进行测试 然后我们删除一下索引进行测试 可以删除 然后logout,使用kibana用户登录,该用户隶属于my_kibana_user用户组,该用户组只有read权限 提示错误,没有权限 over !
打开微信扫一扫,关注微信公众号【数据与算法联盟】 转载请注明出处:http://blog.csdn.net/gamer_gyt 博主微博:http://weibo.com/234654758 Github:https://github.com/thinkgamer 写在前边的话 elk是日志分析的利器,但是就elk本身而言室友一个弊端的那就是任何人都可以访问和操作,权限的管理方面似乎是一个空白,但是幸好elastic公司对其有辅助的产品Shield,Shield是一个收费的项目,但幸好有一个月的试用期,那么接下来我们就来玩玩这个shield Shield简介 Shield是Elastic公司官方发布的权限管理产品。其主要特性包括: 提供集群节点身份验证和集群数据访问身份验证 提供基于身份角色的细粒度资源和行为访问控制,细到索引级别的读写控制 提供节点间数据传输通道加密保护输出传输安全 提供审计功能 以插件的形式发布 基本部署 1:Elasticsearch安装shield 2.4.0插件,最基本的用户验证 先决条件: Java version >= java 7 Elasticsearch 2.4 Elasticsearch liscense 2.4.0 plugin 在线安装: 终端进入elasticsearch的根目录,此时要注意 以elasticsearch所属用户进行安装,同时注意elastic所属的用户组,若是属于root用户组,在安装时会报错提示没有权限 bin/plugin install license bin/plugin install shield 这里默认安装的是最新版本 离线安装: 下载需要的licese和shield包 https://download.elastic.co/elasticsearch/release/org/elasticsearch/plugin/license/2.4.0/license-2.4.0.zip(sha1) https://download.elastic.co/elasticsearch/release/org/elasticsearch/plugin/shield/2.4.0/shield-2.4.0.zip(sha1) 放在一个path下(eg:/opt/shield/),不要把zip包放在elasticsearch的plugins目录 然后执行安装命令(网友的说法是:这里的目录是绝对路径,不能写成相对路径): bin/plugin install /opt/shield/license-2.4.0.zip bin/plugin install /opt/shield/shield-2.4.0.zip 版本升级: 版本升级相对比较简单,只需要把原先的版本给remove掉,然后install就ok,因为卸载时shield会保留其配置 bin/plugin remove shield bin/plugin install shield 卸载: bin/plugin remove shield 之后重启elasticsearch即可 2:shield在elasticsearch上的验证 当安装完shield后,启动elasticsearch 执行 curl -X GET http://localhost:9200 会爆出以下error {"error":{"root_cause":[{"type":"security_exception","reason":"missing authentication token for REST request [/]","header":{"WWW-Authenticate":"Basic realm=\"shield\" charset=\"UTF-8\""}}],"type":"security_exception","reason":"missing authentication token for REST request [/]","header":{"WWW-Authenticate":"Basic realm=\"shie 因为shield已经起作用了,当然你也可以通过web访问http://localhost:920 我的是win10 的Edge跳出弹出框: 创建用户: 我们可以在shell终端中创建一个es_admin的用户(在es的根目录下执行) bin/shield/esusers useradd es_admin -r admin es_admin 为用户名,-r 表示指定的角色为admin PS:我们可以使用以下命令查看帮助 bin/shield/esusers -h 登录验证: 我们可以在web弹出的输入框输入账号和密码进行登录查看数据,也可以使用下面的这条命令进行验证 es_admin 是用户名,elasticsearch是我es_admin对应的密码 master@ubuntu:/opt/elk/elasticsearch-2.4.0$ curl -u es_admin:elasticsearch -X GET http://localhost:9200 { "name" : "Mauvais", "cluster_name" : "elasticsearch", "version" : { "number" : "2.4.0", "build_hash" : "ce9f0c7394dee074091dd1bc4e9469251181fc55", "build_timestamp" : "2016-08-29T09:14:17Z", "build_snapshot" : false, "lucene_version" : "5.5.2" }, "tagline" : "You Know, for Search" } 至此shield在elasticsearch上的简单部署已经完成 3:shield在Logstash中的应用 首先我们创建一个logstash用户,用于logstash访问elasticsearch 同样在elasticsearch的根目录下执行创建命令: master@ubuntu:/opt/elk/elasticsearch-2.4.0$ bin/shield/esusers useradd logstash -r logstash Enter new password: Retype new password: 然后当我们编写**.conf文件时,在output中加入 output{ ... elasticsearch{ .... user => "logstash" password => "logstash" } } 4:shield在kibana中的应用 kibana的配置相对比较麻烦,我会在下一篇博客中集合具体的实例讲解: http://blog.csdn.net/gamer_gyt/article/details/52896522 Shield的工作原理 Shield作为插件安装到elasticsearch中,一旦安装,插件会拦截入栈API调用,以强制执行身份验证和授权,插件还可以使用Secure Sockets Layer/Transport Layer Security (SSL/TLS)为来自网络和elasticsearch的网络流量提供加密,该插件还是用API拦截层,该层使身份验证和授权能够提供审计日志记录功能。 1:用户验证 Shield已经定义了一组用户,以便对发出请求的用户进行身份验证,这组用户集合通过一个称为realm来定义,realm是配置为使用shield插件的用户数据库,native, file, LDAP, Active Directory, PKI支持 realm 在native realm中,用户将与Elasticsearch集群一起存储和分发。对于native realm,管理员使用API管理用户进行用户管理,所有用户操作都发生在Elasticsearch集群中。用户使用用户名和密码对进行身份验证。 在 file realm中,用户保存与存储在Elasticsearch集群的每个节点上的文件中。使用file realm,管理员使用Elasticsearch提供的工具管理用户,所有用户操作都发生在Elasticsearch集群中。用户使用用户名和密码对进行身份验证。 在LDAP realm中,管理员使用LDAP供应商提供的工具管理用户。Elasticsearch通过访问配置的LDAP服务器来验证用户。用户使用用户名和密码对进行身份验证。Shield还支持将LDAP组映射到Shield中的角色。 在Active realm中,管理员在Active Directory中管理用户。Elasticsearch使用LDAP协议向Active Directory验证用户。用户使用用户名和密码对进行身份验证。Shield还支持将Active Directory安全组映射到Shield中的角色。 在PKI realm,管理员使用X.509证书管理工具管理用户。 Elasticsearch通过验证用户的X.509证书已由可信的授权方签名来验证用户。用户通过在TLS通信期间显示其PKI证书进行身份验证,Shield会将主题映射到适当的角色 您的应用程序可以是Shield领域中的用户。 Elasticsearch客户端通过为每个请求提供用户名和密码对(a.k.a身份验证令牌)来对集群进行身份验证。 shield定义的用户规则的路径为 ES_HOME/config/shield/roles.yml # All cluster rights # All operations on all indices admin: cluster: - all indices: - names: '*' privileges: - all # monitoring cluster privileges # All operations on all indices power_user: cluster: - monitor indices: - names: '*' privileges: - all # Read-only operations on indices user: indices: - names: '*' privileges: - read # Defines the required permissions for transport clients transport_client: cluster: - transport_client # The required permissions for the kibana 4 server kibana4_server: cluster: - monitor indices: - names: '.kibana*' privileges: - all - names: '.reporting-*' privileges: - all # The required role for logstash users logstash: cluster: - manage_index_templates indices: - names: 'logstash-*' privileges: - write - read - create_index # Marvel user role. Assign to marvel users. marvel_user: indices: - names: '.marvel-es-*' privileges: [ "read" ] - names: '.kibana' privileges: - view_index_metadata - read # Marvel remote agent role. Assign to the agent user on the remote marvel cluster # to which the marvel agent will export all its data remote_marvel_agent: cluster: [ "manage_index_templates" ] indices: - names: '.marvel-es-*' privileges: [ "all" ] 2:授权规则 Shield的行动授权数据包含以下元素: Secured Resource :定义资源的安全特权,包括集群索引/别名,或一组指标/集群中的别名 Privilege:用户可以针对安全资源执行的一个或多个动作。 这包括命名的动作组(例如读取)或设置特定动作(例如,索引:/ data / read / percolate) Permissions:针对安全资源的一个或者多个特权(eg:在索引“products”上的read) Role:命名的特权集 Users:可以分配零个或多个角色的实体,授权他们对其角色并集中描述的安全资源执行操作 安全的Elasticsearch集群通过角色管理用户的权限。 角色具有唯一的名称,并标识一组权限,这些权限转换为资源上的权限。 用户可以具有任意数量的角色。 有两种类型的权限:集群和索引。 用户具有的总权限集由所有角色中的权限的并集来定义。根据使用的领域,Shield提供了向用户分配角色的适当方法。 3:节点认证与信道加密