Ambari是一种基于Web的工具,支持Apache Hadoop集群的创建、管理和监控。
Ambari已支持大多数Hadoop组件,包括HDFS、MapReduce、Hive、Pig、Hbase、Zookeeper、Sqoop和Hcatalog等。Apache Ambari支持HDFS、MapReduce、Hive、Pig、Hbase、Zookeepr、Sqoop和Hcatalog等的集中管理。也是5个顶级hadoop管理工具之一。
Ambari自身也是一个分布式架构的软件,主要由两部分组成:Ambari Server和Ambari Agent。简单来说,用户通过Ambari Server通知Ambari Agent安装对应的软件;Agent会定时地发送各个机器每个软件模块的状态给Ambari Server,最终这些状态信息会呈现在Ambari的GUI,方便用户了解到集群的各种状态,并进行相应的维护。
不收费的Hadoop版本主要有三个(均是国外厂商),分别是:Apache(最原始的版本,所有发行版均基于这个版本进行改进)、Cloudera版本(Cloudera’s Distribution Including Apache Hadoop,简称CDH)、Hortonworks版本(Hortonworks Data Platform,简称HDP)。
HDP安装包主要包括如下内容:
HDP
HDP是hortonworks的软件栈,里面包含了hadoop生态系统的所有软件项目,比如HBase、Zookeeper、Hive、Pig等等。
HDP-UTILS
HDP-UTILS是工具类库。
HDP-GPL
新版hadoop使用了LZO数据压缩库,这个协议跟HDP栈的协议不相同,需要使用GPL。
官方指导:
https://docs.cloudera.com/HDPDocuments/Ambari-2.6.1.5/bk_ambari-installation/content/ch_Getting_Ready.html
版本对应查询:
https://supportmatrix.cloudera.com/#Hortonworks
使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等
),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够通用解决这些问题的中间件就应运而生了。
应用场景:
分布式协调:比如把多个服务提供者的信息放在某个节点上,服务的消费者就可以通过ZK调用
。zookeeper=文件系统+通知机制
① 文件系统
ZooKeeper的数据结构,跟Unix文件系统非常类似,可以看做是一颗树,每个节点叫做Znode。每一个Znode只能存1MB数据。数据只是配置信息。每一个节点可以通过路径来标识,结构图如下:
1)每个子目录项如NameService都被称作为znode,这个znode是被它所在的路径唯一标识,如Server1这个znode的标识为/NameService/Server1。
2)znode可以有子节点目录,并且每个znode可以存储数据,注意EPHEMERAL(临时的)类型的目录节点不能有子节点目录。
3)znode是有版本的(version),每个znode中存储的数据可以有多个版本,也就是一个访问路径中可以存储多份数据,version号自动增加。
4)znode的类型:
znode可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端
,这个是Zookeeper的核心特性,Zookeeper的很多功能都是基于这个特性实现的。角色 | 描述 | |
---|---|---|
领导者(leader) | 负责进行投票的发起和决议,更新系统状态 | |
学习者(learner) | 跟随者(follower) | 接受客户端请求并想客户端返回结果,在选主过程中参与投票 |
观察者(observer) | 接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度 | |
客户端(client) | 请求发起方 |
为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。
Leader选举算法采用了Paxos协议;
Paxos核心思想:当多数Server写成功,则任务数据写成功如果有3个Server,则两个写成功即可;如果有4或5个Server,则三个写成功即可。
Server数目一般为奇数(3、5、7)如果有3个Server,则最多允许1个Server挂掉;如果有4个Server,则同样最多允许1个Server挂掉。由此,我们看出3台服务器和4台服务器的的容灾能力是一样的,所以为了节省服务器资源,一般我们采用奇数个数,作为服务器部署个数。
假设现在ZooKeeper集群有五台服务器,它们myid分别是服务器1、2、3、4、5
zookeeper集群初始化阶段,服务器(myid=1-5)依次启动,开始zookeeper选举Leader
服务器1(myid=1)启动,当前只有一台服务器,无法完成Leader选举
服务器2(myid=2)启动,此时两台服务器能够相互通讯,开始进入Leader选举阶段
① NameNode:HDFS的管理节点,负责客户端的请求响应,存放元数据;
② SecondaryNameNode:(辅助节点)当编辑日志和映像文件需要合并时,在同一个NameNode上执行合并操作会耗费大量内存和计算能力,因此合并操作一般会在另一台机器上执行,即(SecondaryNamenode);
③ DataNode:HDFS的工作节点,受客户端和NameNode的调度,检索并存放数据块。没有NameNode,DataNode将无法使用;
① NameNode
NameNode主要是用来保存HDFS的元数据信息,比如命名空间信息,块信息等。当它运行的时候,这些信息是存在内存中的,但是这些信息也可以持久化到磁盘上。
Fsimage:是在NameNode启动时对整个文件系统的快照
edit logs:是在NameNode启动后,对文件系统的改动序列
只有在NameNode重启时,edit logs才会合并到fsimage文件中,从而得到一个文件系统的最新快照。
但是在生产集群中NameNode是很少重启的,这也意味着当NameNode运行了很长时间后,edit logs文件会变得很大。
② 2nn/Secondary NameNode
SecondaryNameNode就是来帮助解决上述问题的,它的职责是合并NameNode的edit logs到fsimage文件中。
首先,它定时到NameNode去获取edit logs,并更新到fsimage上(Secondary NameNode自己的fsimage)。
一旦它有了新的fsimage文件,它将其拷贝回NameNode中。NameNode在下次重启时会使用这个新的fsimage文件,从而减少重启的时间。
所以2nn并不是nn的备份,而是给nn提供了一个检查点
。
③ nn HA
在hadoop2.0之前,namenode只有一个,存在单点问题(虽然hadoop1.0有secondarynamenode,checkpointnode,buckcupnode这些,但是单点问题依然存在),在hadoop2.0引入了HA机制。
hadoop2.0的HA机制有两个namenode,一个是active namenode,状态是active;另外一个是standby namenode,状态是standby。
两者的状态是可以切换的,但不能同时两个都是active状态,最多只有1个是active状态。只有active namenode提供对外的服务,standby namenode是不对外服务的。
active namenode和standby namenode之间通过NFS或者JN(journalnode,QJM方式)来同步数据。
HDFS中的文件在物理上是分块存储(Block),块的大小可以通过配置参数 ( dfs.blocksize)来规定,默认大小在Hadoop2.x/3.x版本中是128M,1.x版本中是64M。
如果使用的是机械硬盘,可以设置块大小为128M;如果使用的是固态硬盘,则可以设置为256M
默认是这样的,数据的寻址时间要约等于传输时间的1%,即最佳状态,如果硬盘的传输速度较快,在寻址时间变化不大的条件下,我们传输的数据块可以大一点,所以设置为256M
如果设置块太小,会增加我们的寻址时间,程序会一直找数据块的位置
如果块设置的太大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。导致程序在处理这块数据时,会非常慢
总结:HDFS块的大小设置主要取决于磁盘传输速率。
①客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
②NameNode检查是否已存在文件、检查权限。若通过检查,直接先将操作写入EditLog,并返回输出流对象。
(注:WAL,write ahead log,先写Log,再写内存,因为EditLog记录的是最新的HDFS客户端执行所有的写操作。如果后续真实写操作失败了,由于在真实写操作之前,操作就被写入EditLog中了,故EditLog中仍会有记录)
③客户端按128MB的块切分文件,并请求第一个Block上传到哪几个DataNode服务器上。
④NameNode返回3个DataNode节点,分别为dn1、dn2、dn3。
⑤客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
⑥dn1、dn2、dn3逐级应答客户端。
⑦客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。
⑧当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
①客户端通过DistributedFileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
②挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
③DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
④客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。
NameNode被格式化之后,将在…/hadoop/hdfs/namenode/current/目录中产生如下文件
①Fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和文件inode的序列化信息
②Edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到Edits文件中
③seen_txid文件保存的是一个数字,就是最后一个edits_的数字
④每次NameNode启动的时候都会将Fsimage文件读入内存,加载Edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成NameNode启动的时候就将Fsimage和Edits文件进行了合并
从Hadoop2.x版本后,HDFS采用了一种全新的元数据共享机制,即通过Quorum Journal Node(JournalNode)集群或者network File System(NFS)进行数据共享。NFS是操作系统层面的,而JournalNode是Hadoop层面的,成熟可靠、使用简单方便,一般采用JournalNode集群进行元数据共享。
JournalNode集群以及与NameNode之间共享元数据如下图所示。
JournalNode集群可以几乎实时的去NameNode上拉取元数据,然后保存元数据到JournalNode集群
;同时,处于standby状态的NameNode也会实时的去JournalNode集群上同步JNS数据
,通过这种方式,就实现了两个NameNode之间的数据同步。
两个NameNode为了数据同步,会通过一组称作JournalNodes的独立进程进行相互通信。当Active状态的NameNode元数据有任何修改时,会告知大部分的JournalNodes进程。同时,Standby状态的NameNode也会读取JNs中的变更信息,并且一直监控EditLog(事务日志)的变化,并把变化应用于自己的命名空间。Standby可以确保在集群出错时,元数据状态已经完全同步了。
JN1、JN2、JN3等是JournalNode集群的节点,QJM(Quorom Journal Manager)的基本原理是用2N+1台JournalNode存储EditLog,每次写数据操作有N/2+1个节点返回成功,那么本次写操作才算成功,保证数据高可用。当然这个算法所能容忍的是最多有N台机器挂掉,如果多于N台挂掉,算法就会失效。
ZooKeeper也是作为分布式协调的组件,namenode HA用了JournalNode,而没有用ZooKeeper,原因是Zookeeper不适合存储,znode中可以存储的默认最大数据大小为1MB。
健康检测:zkfc会周期性的向它监控的namenode(只有namenode才有zkfc进程,并且每个namenode各一个)发生健康探测命令,从而鉴定某个namenode是否处于正常工作状态,如果机器宕机,心跳失败,那么zkfc就会标记它处于不健康的状态。
会话管理:如果namenode是健康的,zkfc机会保持在zookeeper中保持一个打开的会话,如果namenode是active状态的,那么zkfc还会在zookeeper中占有一个类型为短暂类型的znode,当这个namenode挂掉时,这个znode将会被删除,然后备用的namenode得到这把锁,升级为主的namenode,同时标记状态为active,当宕机的namenode,重新启动,他会再次注册zookeeper,发现已经有znode了,就自动变为standby状态,如此往复循环,保证高可靠性,但是目前仅支持最多配置两个namenode。
master选举:如上所述,通过在zookeeper中维持一个短暂类型的znode,来实现抢占式的锁机制,从而判断哪个namenode为active状态。
在安全模式下集群在进行恢复元数据,即在合并fsimage和edits log,并且接受datanode的心跳信息,恢复block的位置信息,将集群恢复到上次关机前的状态。
在MRv1中,JobTracker由资源管理(由TaskScheduler模块实现)和作业控制(由JobTracker中多个模块共同实现)两部分组成,由于Hadoop对JobTracker赋予的功能过多而造成负载过重。从设计角度上看,Hadoop未能够将资源管理相关的功能与应用程序相关的功能分开,造成Hadoop难以支持多种计算框架。
YARN的基本设计思想是将JobTracker的两个主要功能,即资源管理和作业控制(包括作业监控、容错等),分拆成两独立的进程,资源管理进程与具体应用程序无关,它负责整个集群的资源(内存、CPU、磁盘等)管理,而作业控制进程则是直接与应用程序相关的模块,且每个作业控制进程只负责管理一个作业。YARN的基本设计思想是将MRv1中的JobTracker拆分成了两个独立的服务:一个全局的资源管理器ResourceManager和每个应用程序特有的ApplicationMaster
。其中ResourceManager负责整个系统的资源管理和分配,而ApplicationMaster负责单个应用程序的管理。这样,通过将原有JobTracker中与应用程序相关和无关的模块分开,不仅减轻JobTracker负载,也使得Hadoop支持更多的计算框架。
ResourceManager(RM)
RM是一个全局的资源管理器,负责整个系统的资源管理和分配。它主要由调度器(Scheduler)和应用程序管理器(Applications Manager,ASM)。
调度器根据容量、队列等限制条件将系统中的资源分配给各个正在运行的应用程序。需要注意的是,调度器是一个“纯调度器”,它不再从事任何与具体应用程序相关的工作,比如不负责监控或者跟踪应用的执行状态等,也不负责重新启动因应用执行失败或者硬件故障而产生的失败任务,这些均交由应用程序相关的ApplicationMaster完成,调度器仅根据各个应用程序的资源需求进行资源分配。该调度器是一个可插拔的组件,用户可根据自己的需要设计新的调度器,YARN提供了多种直接可用的调度器,比如Fair Scheduler和Capacity Scheduler等。
应用程序管理器负责管理整个系统中所有应用程序,包括应用程序提交、与调度器协商资源以启动ApplicationMaster、监控ApplicationMaster运行状态并在失败时重新启动它等。
ApplicationMaster(AM)
用户提交的每个应用程序均包含一个AM,主要用来与RM调度器协商以获取资源,得到的任务进一步分配给内部的任务,与NM通信以启动/停止任务,监控所有任务运行状态,并在任务运行失败时重新为任务申请资源以重启任务。当前YARN自带了两个AM实现,一个是用于演示AM编写方法的实例程序distributedshell,它可以申请一定数目的Container以并行运行一个Shell命令或者Shell脚本,另一个是运行MapReduce应用程序的MRAppMaster。此外,一些其他的计算框架例如Spark也有对应的AM。
NodeManager(NM)
NM是每个节点上的资源和任务管理器,一方面,它会定时地向RM汇报本节点上的资源使用情况和各个Container的运行状态,另一方面,它接收并处理来自AM的Container启动/停止等各种请求。
Container
Container是YARN中的资源抽象,它封装了某个节点上的多维度资源,如内存、CPU、磁盘、网络等,当AM向RM申请资源时,RM为AM返回的资源便是用Container表示的。YARN会为每个任务分配一个Container,且该任务只能使用该Container中描述的资源。需要注意的是,Container不同于MRv1中的slot,它是一个动态资源划分单位,是根据应用程序的需求动态生成的。
当用户向YARN中提交一个应用程序后,YARN将分两个阶段运行该应用程序:第一个阶段是启动ApplicationMaster。第二个阶段是由ApplicationMaster创建应用程序,为它申请资源,并监控它的整个运行过程,直到运行完成。
①用户向YARN中提交应用程序,其中包括ApplicationMaster程序、启动ApplicationMaster的命令、用户程序等
②ResourceManager为该应用程序分配第一个Container,并与对应的NodeManager通信,要求它在这个Container中启动应用程序的ApplicationMaster。
③ApplicationMaster首先向ResourceManager注册,这样用户可以直接通过ResourceManager查看应用程序的运行状态,然后它将为各个任务申请资源,并监控它的运行状态,直到运行结束,即重复4~7。
④ApplicationMaster采用轮询的方式通过RPC协议向ResourceManager申请和领取资源。
⑤ApplicationMaster申请到资源后,便与对应的NodeManager通信,要求它启动任务。
⑥NodeManager为任务设置好运行环境(包括环境变量、JAR包、二进制程序等)后,将任务启动命令写到一个脚本中,并通过运行该脚本启动任务。
⑦各个任务通过某个RPC协议向ApplicationMaster汇报自己的状态和进度,以让ApplicationMaster随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务。在应用程序运行过程中,用户可随时通过RPC向ApplicationMaster查询应用程序的当前运行状态。
⑧应用程序运行完成后,ApplicationMaster向ResourceManager注销并关闭自己。
①JobHistory
YARN提供的一个查看已经完成的任务的历史日志记录的服务,可查询每个job运行完以后的历史日志信息,比如map个数、reduce个数等。
②Timeline Serivce
Hadoop 2.4.0以后出现的新特性,主要是为了监控运行在YARN平台上的所有任务
(例如MR、Storm、Spark、HBase等),JobHistoryServer只能查看mr任务的信息,不能查看spark、flink的信息,针对这个问题,spark、flink都要提供自己的server,为了统一,yarn提供了timelineserver,功能更强大,但不是替代jobhistory,两者是功能间的互补关系。
Collector将数据写入后端存储
Reader是与Collector分开的独立守护进程,专用于通过REST API提供查询
③Registry DNS
由Yarn服务注册表支持的Yarn DNS服务器,可通过其标准DNS在Yarn上查找服务
通过DNS向外提供已有的service-discovery信息,将YARN Service registry records转换为DNS记录,从而使用户可以通过标准的DNS客户端机制(例如DNS SRV记录,描述host:port)查询YARN Applciation信息
一个完整的 MapReduce 程序在分布式运行时有三类实例进程:
①MRAppMaster:负责整个程序的过程调度及状态协调
②MapTask:负责Map阶段的整个数据处理流程
③ReduceTask:负责Reduce阶段的整个数据处理流程
MapReduce运算程序一般需要分成2个阶段:Map阶段和Reduce阶段
①Map阶段的并发MapTask,完全并行运行,互不相干
②Reduce阶段的并发ReduceTask,完全互不相干,但是他们的数据依赖于上一个阶段的所有MapTask并发实例的输出
③MapReduce编程模型只能包含一个Map阶段和一个Reduce阶段,如果用户的业务逻辑非常复杂,那就只能多个MapReduce程序,串行运行
Spark计算框架在处理数据时,所有的中间数据都保存在内存中,从而减少磁盘读写操作,提高框架计算效率。同时Spark还兼容HDFS、Hive,可以很好地与Hadoop系统融合,从而弥补MapReduce高延迟的性能缺点。
【Spark Core】
Spark由Scala语言开发的,Spark Core中提供了Spark最基础与最核心的功能,Spark其他的功能如:Spark SQL,Spark Streaming,GraphX,MLlib都是在Spark Core的基础上进行扩展的。
SparkCore是Spark的基础,底层的最小数据单位是:RDD。主要是处理一些离线(可以通过结合Spark Streaming来处理实时的数据流)、非格式化数据。它与Hadoop的MapReduce的区别就是,spark core基于内存计算,在速度方面有优势,尤其是机器学习的迭代过程。
【Spark SQL】
Spark SQL是Spark用来操作结构化数据的组件。通过Spark SQL,用户可以使用SQL或者Apache Hive版本的SQL方言(HQL)来查询数据。
Spark SQL底层的数据处理单位是:DataSet。主要是通过执行标准SQL来处理一些离线(可以通过结合Spark Streaming来处理实时的数据流)、格式化数据。就是Spark生态系统中一个开源的数据仓库组件,可以认为是Hive在Spark的实现,用来存储历史数据,做OLAP、日志分析、数据挖掘、机器学习等等。可以在Spark SQL中执行SQL语句,数据既可以来自RDD,也可以是HIVE、HDFS、Cassandra等外部数据源,还可以是JSON格式的数据。
Hive是将SQL转为MapReduce
SparkSQL可以理解成是将SQL解析成RDD+优化再执行
【Spark Streaming】
Spark Streaming是Spark平台上针对实时数据进行流式计算的组件,提供了丰富的处理数据流的API。
Spark Streaming底层的数据处理单位是:DStream。主要是处理流式数据(数据一直不停的在向Spark程序发送),这里可以结合Spark Core和Spark SQL来处理数据,如果来源数据是非结构化的数据,那么我们这里就可以结合Spark Core来处理,如果数据为结构化的数据,那么可以结合Spark SQL来进行处理。
Spark SQL构建在Spark Core之上,专门用来处理结构化数据(不仅仅是SQL)。即Spark SQL是Spark Core封装而来的!Spark SQL在Spark Core的基础上针对结构化数据处理进行很多优化和改进。
Driver Program
相当于AppMaster,整个应用管理者,负责应用中所有Job的调度执行;
运行JVM Process,运行程序的MAIN函数,必须创建SparkContext上下文对象;
一个SparkApplication仅有一个;
Executors
相当于一个线程池,运行JVM Process,其中有很多线程,每个线程运行一个Task任务,一个Task运行需要1 Core CPU,所有可以认为Executor中线程数就等于CPU Core核数;
一个Spark Application可以有多个,可以设置个数和资源信息;
Driver Program是用户编写的数据处理逻辑,这个逻辑中包含用户创建的SparkContext。SparkContext是用户逻辑与Spark集群主要的交互接口,它会和Cluster Manager交互,包括向它申请计算资源等。**Cluster Manager负责集群的资源管理和调度,现在支持Standalone、Apache Mesos和Hadoop的YARN。**Worker Node是集群中可以执行计算任务的节点。**Executor是在一个Worker Node上为某应用启动的一个进程,该进程负责运行任务,并且负责将数据存在内存或者磁盘上。**Task是被送到某个Executor上的计算单元,每个应用都有各自独立的Executor,计算最终在计算节点的Executor中执行。
Spark可以跑在很多集群上,比如跑在local上,跑在Standalone上,跑在Apache Mesos上,跑在Hadoop YARN上等等。不管Spark跑在什么上面,它的代码都是一样的,区别只是master的时候不一样。其中Spark on YARN是工作中或生产上用的非常多的一种运行模式。
Spark可以和Yarn整合,将Application提交到Yarn上运行,Yarn有两种提交任务的方式。
- yarn-client提交任务方式
1.客户端提交一个Application,在客户端启动一个Driver进程
2.Driver进程会向RS(ResourceManager)发送请求,启动AM(ApplicationMaster)
3.RS收到请求,随机选择一台NM(NodeManager)启动AM。这里的NM相当于Standalone中的Worker节点
4.AM启动后,会向RS请求一批container资源,用于启动Executor
5.RS会找到一批NM返回给AM,用于启动Executor。AM会向NM发送命令启动Executor
6.Executor启动后,会反向注册给Driver,Driver发送task到Executor,执行情况和结果返回给Driver端
Yarn-client模式适用于测试,因为Driver运行在本地,Driver会与yarn集群中的Executor进行大量的通信,会造成客户机网卡流量的大量增加.
- yarn-cluster提交任务方式
1.客户机提交Application应用程序,发送请求到RS(ResourceManager),请求启动AM(ApplicationMaster)
2.RS收到请求后随机在一台NM(NodeManager)上启动AM(相当于Driver端)
3.AM启动,AM发送请求到RS,请求一批container用于启动Executor
3.RS返回一批NM节点给AM
4.AM连接到NM,发送请求到NM启动Executor
5.Executor反向注册到AM所在的节点的Driver,Driver发送task到Executor
Yarn-Cluster主要用于生产环境中,因为Driver运行在Yarn集群中某一台NodeManager中,每次提交任务的Driver所在的机器都是随机的,不会产生某一台机器网卡流量激增的现象,缺点是任务提交后不能看到日志。只能通过yarn查看日志。
DataFrame是一种以RDD为基础的带有Schema元信息的分布式数据集,类似于传统数据库的二维表格。
DataSet是保存了更多的描述信息,类型信息的分布式数据集。
与RDD相比,保存了更多的描述信息,概念上等同于关系型数据库中的二维表。
与DataFrame相比,保存了类型信息,是强类型的,提供了编译时类型检查,调用Dataset的方法先会生成逻辑计划,然后被spark的优化器进行优化,最终生成物理计划,然后提交到集群中运行!
DataSet包含了DataFrame的功能,Spark2.0中两者统一,DataFrame表示为DataSet[Row],即DataSet的子集。DataFrame其实就是Dateset[Row]。
RDD、DataFrame、DataSet的区别
RDD[Person]:以Person为类型参数,但不了解其内部结构
DataFrame:提供了详细的结构信息schema列的名称和类型,更像是一张表
DataSet[Person]:不光有schema信息,还有类型信息
Apache Flink是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算。Flink设计为在所有常见的集群环境中运行,以内存速度和任何规模执行计算。
任何类型的数据都是作为事件流产生的。信用卡交易,传感器测量,机器日志或网站或移动应用程序上的用户交互,所有这些数据都作为流生成。
数据可以作为无界或有界流处理。
无界流有一个开始但没有定义的结束。它们不会在生成时终止并提供数据。必须持续处理无界流,即必须在摄取事件后立即处理事件。无法等待所有输入数据到达,因为输入是无界的,并且在任何时间点都不会完成。处理无界数据通常要求以特定顺序(例如事件发生的顺序)摄取事件,以便能够推断结果完整性。
有界流具有定义的开始和结束。可以在执行任何计算之前通过摄取所有数据来处理有界流。处理有界流不需要有序摄取,因为可以始终对有界数据集进行排序。有界流的处理也称为批处理。
Apache Flink擅长处理无界和有界数据集。精确控制时间和状态使Flink的运行时能够在无界流上运行任何类型的应用程序。有界流由算法和数据结构内部处理,这些算法和数据结构专门针对固定大小的数据集而设计,从而产生出色的性能。
Apache Flink是一个分布式系统,需要计算资源才能执行应用程序。Flink与所有常见的集群资源管理器(如Hadoop YARN,Apache Mesos和Kubernetes)集成,但也可以设置为作为独立集群运行。
Flink旨在很好地适用于之前列出的每个资源管理器。这是通过特定于资源管理器的部署模式实现的,这些模式允许Flink以其惯用的方式与每个资源管理器进行交互。
部署Flink应用程序时,Flink会根据应用程序配置的并行性自动识别所需资源,并从资源管理器请求它们。如果发生故障,Flink会通过请求新资源来替换发生故障的容器。提交或控制应用程序的所有通信都通过REST调用进行。这简化了Flink在许多环境中的集成。
Flink旨在以任何规模运行有状态流应用程序。应用程序可以并行化为数千个在集群中分布和同时执行的任务。因此,应用程序可以利用几乎无限量的CPU,主内存,磁盘和网络IO。而且,Flink可以轻松维护非常大的应用程序状态。其异步和增量检查点算法确保对处理延迟的影响最小,同时保证一次性状态一致性。
有状态Flink应用程序针对本地状态访问进行了优化。任务状态始终保留在内存中,或者,如果状态大小超过可用内存,则保存在访问高效的磁盘上数据结构中。因此,任务通过访问本地(通常是内存中)状态来执行所有计算,从而产生非常低的处理延迟。Flink通过定期和异步检查本地状态到持久存储来保证在出现故障时的一次状态一致性。
用户通过DataStream API、DataSet API、SQL和Table API编写Flink任务,它会生成一个JobGraph。JobGraph是由source、map()、keyBy()/window()/apply()和Sink等算子组成的。当JobGraph提交给Flink集群后,能够以Local、Standalone、Yarn和Kubernetes四种模式运行。
Client:Flink Client用于与JobManger建立连接,进行Flink任务的提交。Client会将Flink任务组装为一个JobGraph并进行提交。一个JobGraph是一个flink dataflow,其中包含了一个Flink程序的:JobID、Job名称、配置信息、一组JobVertex等;
JobManger:Flink系统协调者,负责接收job任务并调度job的多个task执行。同时负责job信息的收集和管理TaskManger;
TaskManger:负责执行计算的Worker,同时进行所在节点的资源管理(包括内存、cup、网络),启动时向JobManger汇报资源信息。
JobManager的功能主要有:
flink yarn client负责与yarn RM进行通信及资源申请;
JobManger和TaskManger分别申请Container资源运行各自的进程;
JobManger和YARN AM属于同一个Container中,从而YARN AM可进行申请Container及调度TaskManger;
HDFS用于数据的存储,如checkpoints、savepoints等数据。
Flink与Yarn的关系与MapReduce和Yarn的关系是一样的。Flink通过Yarn的接口实现了自己的App Master。当在Yarn中部署了Flink,Yarn就会用自己的Container来启动Flink的JobManager(也就是AppMaster)和TaskManager。
启动新的Flink YARN会话时,客户端首先检查所请求的资源(容器和内存)是否可用。之后,它将包含Flink和配置的jar上传到HDFS(步骤1)。
客户端的下一步是请求(步骤2)YARN容器以启动ApplicationMaster(步骤3)。由于客户端将配置和jar文件注册为容器的资源,因此在该特定机器上运行的YARN的NodeManager将负责准备容器(例如,下载文件)。完成后,将启动ApplicationMaster(AM)。
该JobManager和AM在同一容器中运行。一旦它们成功启动,AM就知道JobManager(它自己的主机)的地址。它正在为TaskManagers生成一个新的Flink配置文件(以便它们可以连接到JobManager)。该文件也上传到HDFS。此外,AM容器还提供Flink的Web界面。YARN代码分配的所有端口都是临时端口。这允许用户并行执行多个Flink YARN会话。
之后,AM开始为Flink的TaskManagers分配容器,这将从HDFS下载jar文件和修改后的配置。完成这些步骤后,即可建立Flink并准备接受作业。
一个flink程序执行时,都会映射为一个Streaming Dataflow 进行处理,类似一个DAG图。从Source开始到Sink结束。
flink程序由一个或多个输入流Stream(Source)经过计算(Transformation), 最终输出到一个或多个输出流Stream中(Sink)。
支持的Source:kafka、hdfs、本地文件…
支持的Sink:kafka、mysql、hdfs、本地文件…
parallel Dataflow(并发原理)
flink程序天生就支持并行及分部署处理:
a、一个Stream支持分为多个Stream分区,一个Operate支持分成多个Operate Subtask,每个Subtask都执行在不同的线程中。
b、一个Operate并行度等于Operate Subtask个数,Stream并行度总等于Operate并行度。
parallel Dataflow示例:
Source并行度为2,Sink并行度为1。
上图展示了Operate与Stream之间存在的两种模式:
a、One-to-one模式:Source[1]–>Map[1]的数据流模式,该模式保持了Source的分区特性及数据处理的有序性。
b、Redistribution模式:map[1]–>apply()[2]的数据流模式,该模式会改变数据流的分区。其与选择的Operate操作有关。
Task & Operator Chain
flink在分布式环境中会将多个Operate Subtask串在一起作为一个Operate Chain的执行链。每个执行链在TaskManger上独立的线程中执行。多个Operate通过Stream进行连接。每个Operate对应一个task。
下图分别展示单个并发与多个并发的执行原理图。
时间窗口
flink支持基于时间和数据的时间窗口。Flink支持基于多种时间的窗口。
a、基于事件的创建时间
b、基于事件进入Dataflow的时间
c、基于某Operate对事件处理时的本地时间
各种时间所处的位置及含义:
flink是在Chandy–Lamport算法的基础上实现的一种分布式快照。通过不断的生成分布式Streaming数据流Snapshot,实现利用snapshot进行数据流的恢复处理。
checkpoint主要步骤
a、Checkpoint Coordinator向所有source节点trigger Checkpoint;
b、source节点向下游广播barrier(附带在数据流中,随DAG流动);
c、task收到barrier后,异步的执行快照并进行持久化处理;
d、sink完成快照后,表示本次checkpoint完成。并将所有快照数据进行整合持久化处理。
Barrier
a、Stream Barrier是Flink分布式Snapshotting中的核心元素,它会对数据流进行记录并插入到数据流中,对数据流进行分组,并沿着数据流的方向向前推进。
b、每个Barrier会携带一个Snapshot ID,属于该Snapshot的记录会被推向该Barrier的前方。Barrier非常轻量,不会中断数据流处理。
带有Barrier的数据流图:
Strean Aligning
当Operate具有多个数据输入流时,需在Snapshot Barrier中进行数据对齐处理。
具体处理过程:
a、Operator从一个incoming Stream接收到Snapshot Barrier n,然后暂停处理,直到其它的incoming Stream的Barrier n(否则属于2个Snapshot的记录就混在一起了)到达该Operator。
b、接收到Barrier n的Stream被临时搁置,来自这些Stream的记录不会被处理,而是被放在一个Buffer中
c、一旦最后一个Stream接收到Barrier n,Operator会emit所有暂存在Buffer中的记录,然后向Checkpoint Coordinator发送Snapshot n
d、继续处理来自多个Stream的记录
基于Stream Aligning操作能够实现Exactly Once语义,但是也会给流处理应用带来延迟,因为为了排列对齐Barrier,会暂时缓存一部分Stream的记录到Buffer中。通常以最迟对齐Barrier的一个Stream做为处理Buffer中缓存记录的时刻点。可通过开关,选择是否使用Stream Aligning,如果关掉则Exactly Once会变成At least once。
State Backend(数据持久化方案)
flink的State Backend是实现快照持久化的重要功能,flink将State Backend抽象成一种插件,支持三种State Backend。
a、MemoryStateBackend:基于内存实现,将数据存储在堆中。数据过大会导致OOM问题,不建议生产环境使用,默认存储的大小为4M。
b、FsStateBackend:将数据持久化到文件系统包括(本地,hdfs,Amazon,阿里云),通过地址进行指定。
c、RocksDBStateBackend:RocksDB是一种嵌入式Key-Value数据库,数据实际保存在本地磁盘上。比起FsStateBackend的本地状态存储在内存中,RocksDB利用了磁盘空间,所以可存储的本地状态更大。从RocksDB中读写数据都需要进行序列化和反序列化,读写成本更高。允许增量快照,每次快照时只对发生变化的数据增量写到分布式存储上,而不是将所有的本地状态都拷贝过去。
flink on yarn主要有两种运行模式。一种是内存集中管理模式(即Session-Cluster模式),另一种是内存job管理模式(即Per-Job-Cluster模式)。
- Session-cluster模式
在Yarn中初始化一个Flink集群,开辟指定的资源,资源申请到之后,资源永远保持不变,之后我们提交的Flink Jon都在这个Flink yarn-session中,也就是说不管提交多少个job,这些job都会共用开始时在yarn中申请的资源。这个Flink集群会常驻在Yarn集群中,除非手动停止。
如果资源满了,下一个作业就无法提交(阻塞),只能等到yarn中的其中一个作业执行完成后,释放了资源,下个作业才会正常提交。
适用场景:
适合规模小、执行时间短的作业。
比如适合小的有界流,不适合无界流,因为无界流的Job 7*24小时运行,始终占用资源,如果Job多了资源不够用,导致Job阻塞
- Per-Job-Cluster模式
先提交job,再启动flink集群。一个job对应一个flink集群,每个flink集群单独向yarn申请资源,因此每个job之间资源不共享、每个job之间互相独立,互不影响,方便管理,一个作业的失败与否并不会影响下一个作业的正常提交和运行。
只有当整个yarn集群资源不足,才会造成任务无法提交了。
job执行完成之后对应的集群也会消失。
由于每个job独享一个flink集群,每个job独享Dispatcher和ResourceManager,按需接受资源申请。
适用场景:规模大,时间长的任务,无界流任务可用。
轻量目录访问协议,Lightweight Directory Access Protocol,作为目录服务系统,实现了Hadoop的集中账号管理
LDAP目录是一种树型结构数据库,适用于一次写入多次查询的场景
域名dc(Domain Component):类似于关系型数据库的中的Database
组织单位ou(Organization Unit):类似于Database中的table的集合
用户uid(User ID):类似于table中的主键
对象名称cn(Common Name):类似于table中的单位数据的名称
LDAP在制作层级的时候,可以在people下创建user,然后在group下创建公司不同的组,然后把uid加到对应的组里面(通过LDAP脚本实现)
OpenLDAP高可用
当在主服务器上更新数据时,该更新通过更新日志记录,并将更新复制到从服务器上。当在从服务器上更新数据时,该更新请求将重定向给主服务器,然后主服务器将更新数据复制到从服务器。
HA共有5种模式
模式 | 特点 |
---|---|
Syncrepl | 从(slave)服务器到主(master)服务器以拉的模式同步目录树。当主服务器对某个条目或更多条目修改条目属性时,从服务器会把修改的整个条目进行同步,而不是单独地同步修改的属性值。 |
Delta-syncrepl | 当主服务器对目录树上的相关条目进行修改时,会产生一条日志信息,于是这时候,从服务器会通过复制协议,将主服务器记录的日志应用到从服务器本地,完成数据同步的过程。但每个消费者获取和处理完全改变的对象,都执行同步操作。 |
Syncrepl Proxy | 代理同步,它将主服务器隐藏起来,而代理主机上边通过syncrepl从主服务器上以拉的方式同步目录树数据,当代理主机数据发生改变时,代理服务器又以推的方式将数据更新到下属的从LDAP服务器上,且从LDAP服务器只有对代理LDAP服务器有读权限。 |
N-Way Mulit-Master | 主要用于多台主服务器之间进行LDAP目录树信息的同步,更好的提供了服务器的冗余性。 |
MirrorMode | 镜像模式,主服务器互相以推的方式实现目录树条目同步,最多只允许且两台机器为主服务器。如果要添加更多的节点,此时只能增加多台从服务器,而不能将添加的节点配置为主服务器。 当一台服务器出现故障时,另一台服务器立即对外提供验证服务。当异常服务器恢复正常时,会自动通过另一个节点所添加或修改的条目信息进行同步,并应用在本地。 |
Kerberos是一种身份认证协议,被广泛运用在大数据生态中,甚至可以说是大数据身份认证的事实标准。
KDC由AS和TGS组成,AS进行身份认证发放TGT(Ticket Granting Tickets),TGT是用来避免多次请求而需要重复认证的凭证;TGS发放ST,ST用来访问某个service时的凭证,ST相当于告诉service你的身份被KDC认证为合法的一个凭证。
Kerberos核心概念:
Principal:大致可以认为是Kerberos世界的用户名,用于标识身份。principal主要由三部分构成:primary,instance(可选)和realm。
Ranger Admin:用于管理安全策略、用户/组的UI门户并提供Rest Server。
Ranger UserSync:定期将Unix系统或LDAP或Active Directory中的用户/组同步到RangerAdmin中。也可用作RangerAdmin的身份验证服务器,以使用linux用户/密码登陆到RangerAdmin。
Ranger TagSync:将资源分类与访问授权分开,只要资源附加相同的标签,就可以有一个标签策略应用于多个组件。可以有助于减少Ranger所需的策略数量,需要Apache Atlas管理元数据(Hive DB/Tables、HDFS路径、Kafka主题和标签/分类等)
Agent Plugin:插件是嵌入到Hadoop各个组件的轻量级java程序。插件定期从AdminServer拉取策略,存储在本地文件中。当用户访问Hadoop组件时,插件会拦截请求根据策略进行安全评估,并且定期发送数据到审计服务器做记录。
Hive是一个SQL解析引擎,将SQL语句转译成MR Job,然后再Hadoop平台上运行,达到快速开发的目的。
Hive中的表是纯逻辑表,就只是表的定义等,即表的元数据。本质就是Hadoop的目录/文件,达到了元数据与数据存储分离的目的
Hive本身不存储数据,它完全依赖HDFS和MapReduce。
Hive的内容是读多写少,不支持对数据的改写和删除。
Interface:Hive提供三个主要的用户接口
Hive外部连接提供的是一个shell客户端,是直接启动了一个org.apache.hadoop.hive.cli.cliDriver的进程,这个进程主要包含了两块内容,一个是提供交互的cli,另外一个就是Driver驱动引擎,这种情况下如果有多个客户端的情况下,就需要多个Driver,但如果通过HiveServer2连接就可以共享Driver,一方面可以简化客户端的设计降低资源损耗,另外一方面还能降低对MetaStore的压力,减少连接的数量。
在生产环境中使用Hive,强烈建议使用HiveServer2来提供服务,好处很多:
MetaStore HA
Hive Metastore HA解决方案旨在处理Metastore服务失败。每当部署的Metastore服务关闭时,Metastore服务在相当长的时间内都会保持不可用状态,直到恢复服务为止。为避免此类停机,在HA模式下部署Metastore服务。
Hive Metastore客户端始终使用第一个URI连接Metastore服务器。如果Metastore服务器变得无法访问,则客户端从列表中随机选取一个URI并尝试与其连接。
HiveServer2 HA
如果只是使用一台服务来启动hiveserver2,那么如果hiveserver2挂掉便不能提供jdbc的支持。hive 支持hiveserver2 HA,用于进行负载均衡和高可用
Hive从0.14开始,使用Zookeeper实现了HiveServer2的HA功能,Client端可以通过指定一个nameSpace来连接HiveServer2,而不是指定某一个host和port。
a.用户提交查询任务给Driver;
b.Driver将查询任务给编译器,并请求返回一个plan;
c.编译器向MetaStore获取必要的元数据;
d.MetaStore返回编译器请求的元数据;
e.编译器生成多个stage的DAG作为plan,并将plan交给Driver。每个stage代表着一个map/reduce任务,或一个元数据操作,或一个HDFS操作。这相当于做了一套job的流水线。在Map stage中,plan包含了Map Operator Tree;在Reduce stage中,plan包含了Reduce Operator Tree;
f.Driver上交plan给执行器去执行,获取元数据信息,提交给JobTracker或者ResourceManager去处理该任务,任务会直接读取HDFS中文件进行相应的操作;
g.获取执行的结果;
h.取得并返回执行结果。
MapReduce实现基本SQL操作的原理
SELECT name, orderid FROM User u JOIN Order o ON u.uid=o.uid;
a.两种表进行了join命令,而join on所用的uid正是两个map任务中的key。同时,将表中的其他数据连同各个表的标识id放到value中;
b.通过这种key的选择方式,就可以用shuffle将相同key的不同表的数据聚合在一起;
c.通过shuffle操作后,相同key的数据被聚合到一起,接下来使用reduce把不同表的数据做整合就得到了最后的查询结果。
SELECT rank,level,count(*) as value FROM score GROUP BY rank,level;
a.sql进行了group by,对rank和level进行分组,也就是说将这两列组合起来当作key,得到像的格式;
b.value记录该数据的出现次数;
c.使用shuffle操作,将相同key的数据分到一起,实现了分组效果;
d.使用reduce操作,将数据进行聚合累加得到最后结果。
SQL转换为MR的具体流程:
a.在编译器中,用Antlr语言识别工具对HQL的输入进行词法和语法解析,将HQL语句转换成抽象语法树(AST Tree)的形式;
b.遍历抽象语法树,转化成QueryBlock查询块。因为AST结构复杂,不方便直接翻译成MR算法程序。其中QueryBlock是一条最基本的SQL语法组成单元,包括输入源、计算过程、和输入三个部分;
c.遍历QueryBlock,生成OperatorTree(操作树),OperatorTree由很多逻辑操作符组成,如TableScanOperator、SelectOperator、FilterOperator、JoinOperator、GroupByOperator和ReduceSinkOperator等。这些逻辑操作符可在Map、Reduce阶段完成某一特定操作;
d.Hive驱动模块中的逻辑优化器对OperatorTree进行优化,变换OperatorTree的形式,合并多余的操作符,减少MR任务数、以及Shuffle阶段的数据量;
e.遍历优化后的OperatorTree,根据OperatorTree中的逻辑操作符生成需要执行的MR任务;
f.启动Hive驱动模块中的物理优化器,对生成的MR任务进行优化,生成最终的MR任务执行计划;
g.最后,有Hive驱动模块中的执行器,对最终的MR任务执行输出。
Hive驱动模块中的执行器执行最终的MR任务时,Hive本身不会生成MR算法程序。它通过一个表示“Job执行计划”的XML文件,来驱动内置的、原生的Mapper和Reducer模块。Hive通过和JobTracker通信来初始化MR任务,而不需直接部署在JobTracker所在管理节点上执行。通常在大型集群中,会有专门的网关机来部署Hive工具,这些网关机的作用主要是远程操作和管理节点上的JobTracker通信来执行任务。Hive要处理的数据文件常存储在HDFS上,HDFS由名称节点(NameNode)来管理。
Hive引擎包括:默认MR、Tez、Spark,不更换引擎hive默认的是MR。
MR性能差,资源消耗大,如:Hive作业之间的数据不是直接流动的,而是借助HDFS作为共享数据存储系统,即一个作业将处理好的数据写入HDFS,下一个作业再从HDFS重新读取数据进行处理。很明显更高效的方式是,第一个作业直接将数据传递给下游作业。
用Hive直接编写MR程序,假设有四个有依赖关系的MR作业,上图中,绿色是Reduce Task,云状表示写屏蔽,需要将中间结果持久化写到HDFS。
Tez可以将多个有依赖的作业转换为一个作业,这样只需写一次HDFS,且中间节点较少,从而大大提升作业的计算性能。
Kafka是最初由Linkedin公司开发,是一个分布式、支持分区的(partition)、多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景:比如基于hadoop的批处理系统、低延迟的实时系统、storm/Spark流式处理引擎,web/nginx日志、访问日志,消息服务等等,用scala语言编写,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。
高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒
可扩展性:kafka集群支持热扩展
持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
高并发:支持数千个客户端同时读写
日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等
消息系统:解耦和生产者和消费者、缓存消息等
用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘
运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告
流式处理:比如spark streaming和storm
事件源
点对点模式
点对点模式通常是基于拉取或者轮询的消息传送模型,这个模型的特点是发送到队列的消息被一个且只有一个消费者进行处理。生产者将消息放入消息队列后,由消费者主动的去拉取消息进行消费。点对点模型的的优点是消费者拉取消息的频率可以由自己控制。但是消息队列是否有消息需要消费,在消费者端无法感知,所以在消费者端需要额外的线程去监控。
发布订阅模式
发布订阅模式是一个基于消息送的消息传送模型,改模型可以有多种不同的订阅者。生产者将消息放入消息队列后,队列会将消息推送给订阅过该类消息的消费者(类似微信公众号)。由于是消费者被动接收推送,所以无需感知消息队列是否有待消费的消息!但是consumer1、consumer2、consumer3由于机器性能不一样,所以处理消息的能力也会不一样,但消息队列却无法感知消费者消费的速度!所以推送的速度成了发布订阅模模式的一个问题!假设三个消费者处理速度分别是8M/s、5M/s、2M/s,如果队列推送的速度为5M/s,则consumer3无法承受!如果队列推送的速度为2M/s,则consumer1、consumer2会出现资源的极大浪费!
Producer:Producer即生产者,消息的产生者,是消息的入口。
Broker:Broker是kafka实例,每个服务器上有一个或多个kafka的实例,我们姑且认为每个broker对应一台服务器。每个kafka集群内的broker都有一个不重复的编号,如图中的broker-0、broker-1等……
Topic:消息的主题,可以理解为消息的分类,kafka的数据就保存在topic。在每个broker上都可以创建多个topic。
Partition:Topic的分区,每个topic可以有多个分区,分区的作用是做负载,提高kafka的吞吐量。同一个topic在不同的分区的数据是不重复的,partition的表现形式就是一个一个的文件夹!
Replication:每一个分区都有多个副本,副本的作用是做备胎。当主分区(Leader)故障的时候会选择一个备胎(Follower)上位,成为Leader。在kafka中默认副本的最大数量是10个,且副本的数量不能大于Broker的数量,follower和leader绝对是在不同的机器,同一机器对同一个分区也只可能存放一个副本(包括自己)。
Message:每一条发送的消息主体。
Consumer:消费者,即消息的消费方,是消息的出口。
Consumer Group:我们可以将多个消费组组成一个消费者组,在kafka的设计中同一个分区的数据只能被消费者组中的某一个消费者消费。同一个消费者组的消费者可以消费同一个topic的不同分区的数据,这也是为了提高kafka的吞吐量!
Zookeeper:kafka集群依赖zookeeper来保存集群的的元信息,来保证系统的可用性。
producer就是生产者,是数据的入口。注意看图中的红色箭头,Producer在写入数据的时候永远的找leader,不会直接将数据写入follower。
1、先从集群获取分区的leader
2、producer将消息发送给leader
3、leader将消息写入到本地文件
4、followers从leader pull消息
5、followers将消息写入本地后向leader发送ACK
6、leader收到所有副本的ACK后向producer发送ACK
消息写入leader后,follower是主动的去leader进行同步的!producer采用push模式将数据发布到broker,每条消息追加到分区中,顺序写入磁盘,所以保证同一分区内的数据是有序的!
分区的主要目的是:
方便扩展:因为一个topic可以有多个partition,所以我们可以通过扩展机器去轻松的应对日益增长的数据量。
提高并发:以partition为读写单位,可以多个消费者同时消费数据,提高了消息的处理效率。
在kafka中,如果某个topic有多个partition,producer又怎么知道该将数据发往哪个partition呢?kafka中有几个原则:
partition在写入的时候可以指定需要写入的partition,如果有指定,则写入对应的partition。
如果没有指定partition,但是设置了数据的key,则会根据key的值hash出一个partition。
如果既没指定partition,又没有设置key,则会轮询选出一个partition。
保证消息不丢失是一个消息队列中间件的基本保证,那producer在向kafka写入消息的时候,怎么保证消息不丢失呢?其实上面的写入流程图中有描述出来,那就是通过ACK应答机制!在生产者向队列写入数据的时候可以设置参数来确定是否确认kafka接收到数据,这个参数可设置的值为0、1、all。
0代表producer往集群发送数据不需要等到集群的返回,不确保消息发送成功。安全性最低但是效率最高;1代表producer往集群发送数据只要leader应答就可以发送下一条,只确保leader发送成功;all代表producer往集群发送数据需要所有的follower都完成从leader的同步才会发送下一条,确保leader发送成功和所有的副本都完成备份。安全性最高,但是效率最低。
最后要注意的是,如果往不存在的topic写数据,能不能写入成功呢?kafka会自动创建topic,分区和副本的数量根据默认配置都是1。
Producer将数据写入kafka后,集群就需要对数据进行保存了。kafka将数据保存在磁盘,可能在我们的一般的认知里,写入磁盘是比较耗时的操作,不适合这种高并发的组件。Kafka初始会单独开辟一块磁盘空间,顺序写入数据(效率比随机写入高)。
Partition结构
前面说过了每个topic都可以分为一个或多个partition,如果你觉得topic比较抽象,那partition就是比较具体的东西了。Partition在服务器上的表现形式就是一个一个的文件夹,每个partition的文件夹下面会有多组segment文件,每组segment文件又包含.index文件、.log文件、.timeindex文件(早期版本中没有)三个文件,log文件就实际是存储message的地方,而index和timeindex文件为索引文件,用于检索消息。
如上图,这个partition有三组segment文件,每个log文件的大小是一样的,但是存储的message数量是不一定相等的(每条的message大小不一致)。文件的命名是以该segment最小offset来命名的,如000.index存储offset为0~368795的消息,kafka就是利用分段+索引的方式来解决查找效率的问题。
Message结构
上面说到log文件就实际是存储message的地方,我们在producer往kafka写入的也是一条一条的message,那存储在log中的message是什么样子的呢?消息主要包含消息体、消息大小、offset、压缩类型……等等!重点需要知道的是下面三个:
消息存储在log文件后,消费者就可以进行消费了。Kafka采用的是发布订阅模式,消费者主动的去kafka集群拉取消息,与producer相同的是,消费者在拉取消息的时候也是找leader去拉取。
多个消费者可以组成一个消费者组(consumer group),每个消费者组都有一个组id。同一个消费组者的消费者可以消费同一topic下不同分区的数据,但是不会组内多个消费者消费同一分区的数据。
图示是消费者组内的消费者小于partition数量的情况,所以会出现某个消费者消费多个partition数据的情况,消费的速度也就不及只处理一个partition的消费者的处理速度!如果是消费者组的消费者多于partition的数量,多出来的消费者不消费任何partition的数据。所以在实际的应用中,建议消费者组的consumer的数量与partition的数量一致。
partition划分为多组segment,每个segment又包含.log、.index、.timeindex文件,存放的每条message包含offset、消息大小、消息体……我们多次提到segment和offset,查找消息的时候是怎么利用segment+offset配合查找的呢?假如现在需要查找一个offset为368801的message是什么样的过程呢?我们先看看下面的图:
先找到offset的368801message所在的segment文件(利用二分法查找),这里找到的就是在第二个segment文件。
打开找到的segment中的.index文件(也就是368796.index文件,该文件起始偏移量为368796+1,我们要查找的offset为368801的message在该index内的偏移量为368796+5=368801,所以这里要查找的相对offset为5)。由于该文件采用的是稀疏索引的方式存储着相对offset及对应message物理偏移量的关系,所以直接找相对offset为5的索引找不到,这里同样利用二分法查找相对offset小于或者等于指定的相对offset的索引条目中最大的那个相对offset,所以找到的是相对offset为4的这个索引。
根据找到的相对offset为4的索引确定message存储的物理偏移位置为256。打开数据文件,从位置为256的那个地方开始顺序扫描直到找到offset为368801的那条Message。
这套机制是建立在offset为有序的基础上,利用segment+有序offset+稀疏索引+二分查找+顺序查找等多种手段来高效的查找数据!至此,消费者就能拿到需要处理的数据进行处理了。那每个消费者又是怎么记录自己消费的位置呢?在早期的版本中,消费者将消费到的offset维护zookeeper中,consumer每间隔一段时间上报一次,这里容易导致重复消费,且性能不好!在新的版本中消费者消费到的offset已经直接维护在kafk集群的__consumer_offsets这个topic中。
在Kafka集群中,某个Broker将被选举出来担任一种特殊的角色,其用于管理和协调Kafka集群,即管理集群中的所有分区的状态并执行相应的管理操作。
每个Kafka集群任意时刻都只能有一个Controller。
当集群启动时,所有Broker都参与Controller的竞选,最终有一个胜出,一旦Controller在某个时刻崩溃,集群中的其他的Broker会收到通知,然后开启新一轮的Controller选举,新选举出来的Controller将承担起之前Controller的所有工作。
集群状态的维护是Controller保持运行状态一致性的基本要素,如果要持续稳定的对外提供服务,除了集群状态还有其他的工作需要Controller负责,如下:
Flume是一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统。Flume最主要是用在分布式系统中,例如读取服务器本地的磁盘数据,并将数据写入到HDFS中。
flume的核心是把数据从数据源(source)收集过来,在将收集到的数据送到指定的目的地(sink)。为了保证输送的过程一定成功,在送到目的地(sink)之前,会先缓存数据(channel),待数据真正到达目的地(sink)后,flume在删除自己缓存的数据。
Agent
flume中通过agent进行日志采集、聚合、传输。agent是一个JVM进程,它以事件的形式将数据从源头送至目的。Agent主要有3个部分组成,Source、Channel、Sink。
Source
Source作为Agent的输入口,负责接收各种类型、各种格式的日志数据,包括avro、thrift、exec、jms、spooling directory、netcat、taildir、sequence generator、syslog、http、legacy。
Sink
Sink作为Agent的输出口,负责不断轮询Channel中的事件并批量移除事件,根据配置文件的配置将事件写入到HDFS等存储系统,或者发到另一个Agent中。Sink组件目的地包括hdfs、logger、avro、thrift、ipc、file、HBase、solr、自定义。
Channel
Channel是位于Source和Sink之间的缓冲区。因此,Channel允许Source和Sink运作在不同的速率上。Channel是线程安全的,可以同时处理几个Source的写入操作和几个Sink的读取操作。
Flume自带两种Channel:Memory Channel 和File Channel。
Memory Channel是内存中的队列。Memory Channel在不需要关心数据丢失的情景下适用。如果需要关心数据丢失,那么Memory Channel就不应该使用,因为程序死亡、机器宕机或者重启都会导致数据丢失。File Channel将所有事件写到磁盘。因此在程序关闭或机器宕机的情况下不会丢失数据。
Event
event是flume的数据传输基本单元,event由两部分组成:Header和Body。
flume可以支持多级flume的agent,即flume可以前后相继,例如sink可以将数据写到下一个agent的source中,这样的话就可以连成串了,可以整体处理了。flume还支持扇入(fan-in)、扇出(fan-out)。所谓扇入就是source可以接受多个输入,所谓扇出就是sink可以将数据输出多个目的地destination中。
HBase是一种面向列存储的非关系型数据库,是在Hadoop之上的NoSQL的Key/value数据库,不支持join的、也不支持ACID、对事务支持有限,无schema(创建表的时候,无需去指定列、列类型)、原生就支持分布式存储的,所以可以用来存储海量数据,同时也兼顾了快速查询、写入的功能。是大数据领域中Key-Value数据结构存储最常用的数据库方案。
HBase作为NoSQL数据库的代表,属于三驾马车之一BigTable的对应实现,HBase的出现很好地弥补了大数据快速查询能力的空缺。
Hbase利用Hadoop的HDFS作为其文件存储系统,利用Hadoop的MapReduce来处理Hbase中的海量数据,利用zookeeper作为其协调工具。
HBase依赖于:ZooKeeper、HDFS,在启动HBase之前必须要启动ZK、HDFS。
HBase的核心架构由五部分组成,分别是HBase Client、HMaster、Region Server、ZooKeeper以及HDFS。
HBase Client
为用户提供了访问HBase的接口,可以通过元数据表来定位到目标数据的RegionServer,另外HBase Client还维护了对应的cache来加速Hbase的访问,比如缓存元数据的信息。
HMaster
HBase集群的主节点,负责整个集群的管理工作,主要工作职责如下:
Region Server寻址
A、HBase Client访问ZooKeeper;
B、获取写入Region所在的位置,即获取hbase:meta表位于哪个Region Server;
C、访问对应的Region Server;
D、获取hbase:meta表,并查询出目标数据位于哪个Region Server中的哪个Region中。并将该table的Region信息以及meta表的位置信息缓存在客户端的meta cache,方便下次访问;
写Hlog
E、HBase Client向Region Server发送写Hlog请求;
F、Region Server会通过顺序写入磁盘的方式,将Hlog存储在HDFS上;
写MemStore并返回结果
G、HBase Client向Region Server发送写MemStore请求;
只有当写Hlog和写MemStore的请求都成功完成之后,并将反馈给HBase Client,这时对于整个HBase Client写入流程已经完成。
MemStore刷盘
HBase会根据MemStore配置的刷盘策略定时将数据刷新到StoreFile 中,完成数据持久化存储。
为什么要把WAL加载到MemStore中,再刷写成HFile呢?
WAL(Write-Ahead-Log)预写日志是HBase的RegionServer在处理数据插入和删除过程中用来记录操作内容的一种日志。每次Put、Delete等一条记录时,首先将其数据写入到RegionServer对应的HLog文件中去。
而WAL是保存在HDFS上的持久化文件,数据到达Region时先写入WAL,然后被加载到MemStore中。这样就算Region宕机了,操作没来得及执行持久化,也可以再重启的时候从WAL加载操作并执行。
从写入流程中可以看出,数据进入HFile之前就已经被持久化到WAL了,而WAL就是在HDFS上的,MemStore是在内存中的,增加MemStore并不能提高写入性能,为什么还要从WAL加载到MemStore中,再刷写成HFile呢?
数据需要顺序写入,但HDFS是不支持对数据进行修改的;
WAL的持久化为了保证数据的安全性,是无序的;
Memstore在内存中维持数据按照row key顺序排列,从而顺序写入磁盘;
所以MemStore的意义在于维持数据按照RowKey的字典序排列,而不是做一个缓存提高写入效率。
Region Server寻址
HBase Client请求ZooKeeper获取元数据表所在的Region Server的地址。
Region寻址
HBase Client请求RegionServer获取需要访问的元数据,查询出目标数据位于哪个Region Server中的哪个Region中。并将该table的region信息以及meta表的位置信息缓存在客户端的meta cache,方便下次访问。
数据读取
HBase Client请求数据所在的Region Server,获取所需要的数据。Region首先在MemStore中查找,若命中则返回;如果在MemStore中找不到,则通过BloomFilter判断数据是否存在;如果存在,则在StoreFile中扫描并将结果返回客户端。
HBase的数据删除操作并不会立即将数据从磁盘上删除,因为HBase的数据通常被保存在HDFS中,而HDFS只允许新增或者追加数据文件,所以删除操作主要对要被删除的数据进行标记。
当执行删除操作时,HBase新插入一条相同的Key-Value数据,但是keyType=Delete,这便意味着数据被删除了,直到发生Major_compaction操作,数据才会真正地被从磁盘上删除。
HBase这种基于标记删除的方式是按顺序写磁盘的的,因此很容易实现海量数据的快速删除,有效避免了在海量数据中查找数据、执行删除及重建索引等复杂的流程。
行式存储的原理与特点
对于OLAP场景,大多都是对一整行记录进行增删改查操作的,那么行式存储采用以行的行式在磁盘上存储数据就是一个不错的选择。
当查询基于需求字段查询和返回结果时,由于这些字段都埋藏在各行数据中,就必须读取每一条完整的行记录,大量磁盘转动寻址的操作使得读取效率大大降低。
举个例子,下图为员工信息emp表。
数据在磁盘上是以行的形式存储在磁盘上,同一行的数据紧挨着存放在一起。
对于emp表,要查询部门dept为A的所有员工的名字。
select name from emp where dept=A
由于dept的值是离散地存储在磁盘中,在查询过程中,需要磁盘转动多次,才能完成数据的定位和返回结果。
列式存储的原理与特点
对于OLAP场景,一个典型的查询需要遍历整个表,进行分组、排序、聚合等操作,这样一来行式存储中把一整行记录存放在一起的优势就不复存在了。而且,分析型SQL常常不会用到所有的列,而仅仅对其中某些需要的的列做运算,那一行中无关的列也不得不参与扫描。
然而在列式存储中,由于同一列的数据被紧挨着存放在了一起。
那么基于需求字段查询和返回结果时,就不许对每一行数据进行扫描,按照列找到需要的数据,磁盘的转动次数少,性能也会提高。
还是上面例子中的查询,由于在列式存储中dept的值是按照顺序存储在磁盘上的,因此磁盘只需要顺序查询和返回结果即可。
列式存储不仅具有按需查询来提高效率的优势,由于同一列的数据属于同一种类型,如数值类型,字符串类型等,相似度很高,还可以选择使用合适的编码压缩可减少数据的存储空间,进而减少IO提高读取性能。
总的来说,行式存储和列式存储没有说谁比谁更优越,只能说谁更适合哪种应用场景。
主机名 | FQDN | IP | 服务器配置 |
---|---|---|---|
hdp01 | hdp01.hdp.com | 192.168.111.201/24 | 8c、8G、200GB+50GB |
hdp02 | hdp02.hdp.com | 192.168.111.202/24 | 8c、8G、200GB+50GB |
hdp03 | hdp03.hdp.com | 192.168.111.203/24 | 8c、8G、200GB+3*100GB |
hdp04 | hdp04.hdp.com | 192.168.111.204/24 | 8c、8G、200GB+3*100GB |
hdp05 | hdp05.hdp.com | 192.168.111.205/24 | 8c、8G、200GB+3*100GB |
主机 | 磁盘分布 |
---|---|
hdp01 hdp02 |
/dev/sda:系统盘 /dev/sdb:/data01,MySQL、zookeeper、NameNode、JournalNode |
hdp03 | /dev/sda:系统盘 /dev/sdb:/data01,zookeeper、DataNode、JournalNode /dev/sdc:/data02,DataNode /dev/sdd:/data03,DataNode |
hdp04 hdp05 |
/dev/sda:系统盘 /dev/sdb:/data01,DataNode /dev/sdc:/data02,DataNode /dev/sdd:/data03,DataNode |
组件 | 版本 | hdp01 | hdp02 | hdp03 | hdp04 | hdp05 | |
ansible | 2.9.27 | ★ | |||||
MySQL | 5.7.25 | ★ | ★ | ||||
Ambari-metrics | Metrics Collector | 0.2.0 | ★ | ||||
Metrics Monitor | ★ | ★ | ★ | ★ | ★ | ||
Grafana | ★ | ||||||
ZooKeeper | ZooKeeper Server | 3.4.6 | ★ | ★ | ★ | ||
ZooKeeper Client | ★ | ★ | |||||
HDFS | NameNode | 3.1.1.3.1 | ★ | ★ | |||
DataNode | ★ | ★ | ★ | ||||
JournalNode | ★ | ★ | ★ | ||||
zkfc | ★ | ★ | |||||
YARN | ResourceManager | 3.1.1 | ★ | ★ | |||
NodeManager | ★ | ★ | ★ | ||||
HistoryServer | ★ | ||||||
TimelineService | ★ | ||||||
Timeline Service Reader | ★ | ||||||
Registry DNS | ★ | ||||||
OpenLDAP | openldap-server | 2.4.44 | ★ | ★ | |||
openldap-client | ★ | ★ | ★ | ★ | ★ | ||
migrationtools | ★ | ★ | |||||
phpldapadmin | ★ | ★ | |||||
keepalived | v1.3.5 | ★ | ★ | ||||
HaProxy | 1.5.18 | ★ | ★ | ||||
kerberos | Krb5-server | 1.15.1 | ★ | ★ | |||
krb5-workstation | ★ | ★ | ★ | ★ | ★ | ||
Ranger | Ranger Admin | 1.2.0.3.1 | ★ | ★ | |||
UserSync | ★ | ||||||
Hive | HiveServer2 | 3.1.0 | ★ | ★ | |||
MetaStore | ★ | ★ | |||||
Tez | 0.9.1 | ★ | ★ | ★ | ★ | ★ | |
HBase | HMaster | 2.0.2 | ★ | ★ | |||
RegionServer | ★ | ★ | ★ | ||||
Spark | Spark2 History Server | 2.3.0 | ★ | ||||
Flink | client | 1.9.3 | ★ | ★ | ★ | ★ | ★ |
Kafka | broker | 2.0.0 | ★ | ★ | ★ | ||
kafka manager | 2.0.0.2 | ★ | |||||
Flume | flume | 1.9.0 | ★ | ★ | ★ |
在hdp01、hdp02上生成公钥,配置免密登录到其他节点
ssh-keygen -t rsa -f ~/.ssh/id_rsa -C username_root
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 22 [email protected]
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 22 [email protected]
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 22 [email protected]
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 22 [email protected]
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 22 [email protected]
配置ansible和rhel-system-roles,创建配置文件
mkdir /root/ansible
cd /root/ansible
cp /etc/ansible/ansible.cfg /root/ansible/
修改配置文件,/root/ansible/ansible.cfg
[defaults]
inventory = /root/ansible/inventory
ask_pass = false
remote_user = root
配置inventory文件,/root/ansible/inventory
[hdp:children]
nn
dn
[nn]
192.168.111.201 hostname=hdp01
192.168.111.202 hostname=hdp02
[dn]
192.168.111.203 hostname=hdp03
192.168.111.204 hostname=hdp04
192.168.111.205 hostname=hdp05
创建playbook,/root/ansible/hostname.yml
---
- name: modify hostname
hosts: all
tasks:
- name: modify hostname permanently
raw: "echo {{ hostname | quote }} > /etc/hostname"
- name: modify hostname temporarily
shell: hostname {{ hostname | quote }}
执行并确认
ansible-playbook /root/ansible/hostname.yml
ansible all -m shell -a 'hostname'
在nn01上修改主机列表,/etc/hosts
192.168.111.201 hdp01.hdp.com hdp01
192.168.111.202 hdp02.hdp.com hdp02
192.168.111.203 hdp03.hdp.com hdp03
192.168.111.204 hdp04.hdp.com hdp04
192.168.111.205 hdp05.hdp.com hdp05
分发至其他节点
ansible all -m template -a 'src=/etc/hosts dest=/etc/hosts'
安装vim等基础软件,/root/ansible/packages.yml
---
- hosts: all
tasks:
- name: install packages
yum:
name:
- pciutils
- bash-completion
- vim
- chrony
state: present
关闭firewall
ansible all -m service -a 'name=firewalld state=stopped enabled=no'
ansible all -m shell -a 'systemctl status firewalld | grep Active'
关闭SELinux
ansible all -m selinux -a 'policy=targeted state=disabled'
ansible all -m shell -a 'getenforce'
以hdp01为时钟源,其余节点从nn01进行时钟同步
服务端(hdp01)
修改配置文件/etc/chrony.conf
# 不指定外部NTP源
# 允许本网段其节点作为客户端访问
allow 192.168.111.0/24
# 如果时间服务可不用,则使用本地时间作为标准时间授权,层数为10
local stratum 10
重启服务
systemctl restart chronyd
客户端(hdp02-hdp05)
安装ntp时钟,/root/ansible/timesync.yml
---
- hosts: 192.168.111.202,dn
vars:
timesync_ntp_servers:
- hostname: 192.168.111.201
iburst: yes
roles:
- rhel-system-roles.timesync
执行
ansible-playbook /root/ansible/timesync.yml
确认时钟同步情况
ansible 192.168.111.202,dn -m shell -a 'chronyc sources -v'
创建分区parted.yml文件
---
- hosts: all
tasks:
- name: parted devices
parted:
device: "{{ item }}"
number: 1
label: gpt
state: present
loop:
- /dev/sdb
- /dev/sdc
- /dev/sdd
ignore_errors: yes
确认结果
ansible all -m shell -a 'lsblk -f'
创建文件系统mkfs.yml文件
---
- hosts: all
tasks:
- name: mkdir of nn
file:
path: /data01
state: directory
when: inventory_hostname in groups['nn']
- name: mkdir of dn
file:
path: "{{ item }}"
state: directory
loop:
- /data01
- /data02
- /data03
when: inventory_hostname in groups['dn']
- name: mkfs
filesystem:
fstype: xfs
dev: "{{ item }}"
loop:
- /dev/sdb1
- /dev/sdc1
- /dev/sdd1
ignore_errors: yes
- name: mount of nn
mount:
path: /data01
src: /dev/sdb1
fstype: xfs
state: mounted
when: inventory_hostname in groups['nn']
- name: mount of dn
mount:
path: "{{ item.p_dir }}"
src: "{{ item.s_dir }}"
fstype: xfs
state: mounted
loop:
- { p_dir: /data01, s_dir: /dev/sdb1 }
- { p_dir: /data02, s_dir: /dev/sdc1 }
- { p_dir: /data03, s_dir: /dev/sdd1 }
when: inventory_hostname in groups['dn']
【hdp01】配置CentOS镜像Yum源
mkdir /mnt/cdrom
mount /dev/cdrom /mnt/cdrom/
rm -f /etc/yum.repos.d/*
创建repo文件,/etc/yum.repos.d/local.repo
[centos]
name=centos
baseurl=file:///mnt/cdrom
gpgcheck=0
enabled=1
更新yum源
yum clean all
yum update
安装httpd服务
yum install -y httpd
systemctl enable --now httpd
配置http服务指向CentOS源
mkdir /var/www/html/centos
mount /dev/cdrom /var/www/html/centos/
删除原有repo文件
ansible all -m shell -a 'rm -f /etc/yum.repos.d/*.repo'
配置所有节点的系统Yum源
ansible all -m yum_repository -a 'name="centos" description="centos" baseurl="http://hdp01.hdp.com/centos" enabled=yes gpgcheck=no'
ansible all -m shell -a 'yum clean all'
ansible all -m shell -a 'yum update'
【hdp01】解压ambari压缩包
tar -zxvf /opt/hdp/ambari-2.7.5.0-centos7.tar.gz -C /var/www/html/
配置所有节点的ambari Yum源
ansible all -m yum_repository -a 'name="ambari" description="ambari" baseurl="http://hdp01.hdp.com/ambari/centos7/2.7.5.0-72" enabled=yes gpgcheck=yes gpgkey="http://hdp01.hdp.com/ambari/centos7/2.7.5.0-72/RPM-GPG-KEY/RPM-GPG-KEY-Jenkins"'
查看/etc/yum.repos.d/ambari.repo文件
[ambari]
baseurl = http://hdp01.hdp.com/ambari/centos7/2.7.5.0-72
enabled = 1
gpgcheck = 1
gpgkey = http://hdp01.hdp.com/ambari/centos7/2.7.5.0-72/RPM-GPG-KEY/RPM-GPG-KEY-Jenkins
name = ambari
【hdp01】解压HDP压缩包
tar -zxvf /opt/hdp/HDP-3.1.5.0-centos7-rpm.tar.gz -C /var/www/html/
配置所有节点的HDP Yum源
ansible all -m yum_repository -a 'name="HDP" description="HDP" baseurl="http://hdp01.hdp.com/HDP/centos7/3.1.5.0-152" enabled=yes gpgcheck=yes gpgkey="http://hdp01.hdp.com/HDP/centos7/3.1.5.0-152/RPM-GPG-KEY/RPM-GPG-KEY-Jenkins"'
查看/etc/yum.repos.d/HDP.repo文件
[HDP]
baseurl = http://hdp01.hdp.com/HDP/centos7/3.1.5.0-152
enabled = 1
gpgcheck = 1
gpgkey = http://hdp01.hdp.com/HDP/centos7/3.1.5.0-152/RPM-GPG-KEY/RPM-GPG-KEY-Jenkins
name = HDP
【hdp01】解压HDP-UTILS压缩包
tar -zxvf /opt/hdp/HDP-UTILS-1.1.0.22-centos7.tar.gz -C /var/www/html/
配置所有节点的HDP-UTILS Yum源
ansible all -m yum_repository -a 'name="HDP-UTILS" description="HDP-UTILS" baseurl="http://hdp01.hdp.com/HDP-UTILS/centos7/1.1.0.22" enabled=yes gpgcheck=yes gpgkey="http://hdp01.hdp.com/HDP-UTILS/centos7/1.1.0.22/RPM-GPG-KEY/RPM-GPG-KEY-Jenkins"'
查看/etc/yum.repos.d/HDP-UTILS.repo文件
[HDP-UTILS]
baseurl = http://hdp01.hdp.com/HDP-UTILS/centos7/1.1.0.22
enabled = 1
gpgcheck = 1
gpgkey = http://hdp01.hdp.com/HDP-UTILS/centos7/1.1.0.22/RPM-GPG-KEY/RPM-GPG-KEY-Jenkins
name = HDP-UTILS
【hdp01】解压HDP-GPL压缩包
tar -zxvf /opt/hdp/HDP-GPL-3.1.5.0-centos7-gpl.tar.gz -C /var/www/html/
配置所有节点的HDP-GPL Yum源
ansible all -m yum_repository -a 'name="HDP-GPL" description="HDP-GPL" baseurl="http://hdp01.hdp.com/HDP-GPL/centos7/3.1.5.0-152" enabled=yes gpgcheck=yes gpgkey="http://hdp01.hdp.com/HDP-GPL/centos7/3.1.5.0-152/RPM-GPG-KEY/RPM-GPG-KEY-Jenkins"'
查看/etc/yum.repos.d/HDP-GPL.repo文件
[HDP-GPL]
baseurl = http://hdp01.hdp.com/HDP-GPL/centos7/3.1.5.0-152
enabled = 1
gpgcheck = 1
gpgkey = http://hdp01.hdp.com/HDP-GPL/centos7/3.1.5.0-152/RPM-GPG-KEY/RPM-GPG-KEY-Jenkins
name = HDP-GPL
下载以下安装包,并放在/opt/openldap/下
安装包名称 | 说明 |
---|---|
openldap | openldap服务端和客户端必须用的库文件 |
openldap-servers | 用于启动服务和设置,包含单独的ldap后台守护程序 |
openldap-clients | 在LDAP服务端使用,用户增删改查的命令行环境 |
openldap-devel | devel包,可选择性安装 |
openldap-servers-sql | 支持sql模块,可选择性安装 |
migrationtools | 通过migrationtools实现OpenLDAP用户及用户组的添加,导入系统账户,可选择性安装 |
compat-openldap | openldap兼容性库 |
安装createrepo
yum install -y createrepo
根据安装的rpm文件,生成repodata
createrepo /opt/openldap/
将repodata文件夹、rpm文件复制到/var/www/html/openldap下
cp -R /opt/openldap/ /var/www/html/openldap/
配置hdp01-02节点的openldap yum源
ansible all -m yum_repository -a 'name="OpenLDAP" description="OpenLDAP" baseurl="http://hdp01.hdp.com/openldap" enabled=yes gpgcheck=no'
查看/etc/yum.repos.d/OpenLDAP.repo文件
[OpenLDAP]
baseurl = http://hdp01.hdp.com/openldap
enabled = 1
gpgcheck = 0
name = OpenLDAP
下载jdk安装文件,放到/opt中,将文件解压到/usr/local下后,修改/etc/profile文件中环境参数,实现java的安装。
创建jdk.yml文件
---
- hosts: all
tasks:
- name: copy and unzip jdk
unarchive:
src: "/opt/jdk-8u351-linux-x64.tar.gz"
dest: "/usr/local/"
- name: chmod bin
file:
dest: "/usr/local/jdk1.8.0_351/bin"
mode: 0755
recurse: yes
- name: set jdk env
lineinfile:
dest: /etc/profile
line: "{{ item }}"
state: present
loop:
- "export JAVA_HOME=/usr/local/jdk1.8.0_351"
- "export PATH=$JAVA_HOME/bin:$PATH"
执行安装
ansible-playbook /root/ansible/jdk.yml
确认java是否安装正常。
java -version
mysql主从复制的原理:
1)master将数据改变记录到二进制日志(binary log)中,也即是配置文件log-bin指定的文件(这些记录叫做二进制日志事件,binary log events);
2)slave将master的binary log events拷贝到它的中继日志(relay log);
3)slave重做中继日志中的事件,将改变反映它自己的数据(数据重演)。
主从配置注意点:
查询并删除原有的MariaDB
ansible nn -m shell -a 'rpm -qa | grep mariadb'
ansible nn -m shell -a 'rpm -e --nodeps mariadb-libs-5.5.64-1.el7.x86_64'
创建/root/ansible/mysql.yml文件
---
- hosts: nn
tasks:
- name: install mysql
unarchive:
src: "/opt/mysql-5.7.25-linux-glibc2.12-x86_64.tar.gz"
dest: "/usr/local/"
- name: create group
group:
name: mysql
state: present
- name: create user
user:
name: mysql
group: mysql
state: present
- name: create directory
file:
path: "/data01/mysql/mariadb"
state: directory
owner: mysql
group: mysql
recurse: yes
- name: create file of log
file:
path: "/data01/mysql/mariadb/mariadb.log"
state: touch
owner: mysql
group: mysql
执行安装
ansible-playbook mysql.yml
创建配置文件/etc/my.cnf,hdp01的server-id为100,hdp02的server-id为200
[mysqld]
user = mysql # mysqld程序在启动后将在给定UNIX/Linux账户下执行。mysqld必须从root账户启动才能在启动后切换到另一个账户下执行
basedir = /usr/local/mysql-5.7.25-linux-glibc2.12-x86_64/ # MySQL安装的绝对路径
datadir = /data01/mysql/data # MySQL数据存放的绝对路径
socket = /data01/mysql/mysql.sock # socket文件路径
port = 3306 # 服务端口号
server-id=100 # MySQL服务的唯一编号,每个MySQL服务的id需唯一
default_authentication_plugin = mysql_native_password
character-set-server = utf8mb4 # 数据库默认字符集, 主流字符集支持一些特殊表情符号(特殊表情符占用4个字节)
collation-server = utf8mb4_general_ci # 数据库字符集对应一些排序等规则,注意要和character-set-server对应
init_connect='SET NAMES utf8mb4' # 设置client连接mysql时的字符集,防止乱码
lower_case_table_names = 1 # 是否对 sql 语句大小写敏感,1 表示不敏感
key_buffer_size = 16M # 用于指定索引缓冲区的大小
max_allowed_packet = 1024M # 设置一次消息传输的最大值,如果有BLOB对象建议修改成1G
sql_mode = TRADITIONAL # 表示SQL模式的参数,通过这个参数可以设置检验SQL语句的严格程度
pid-file = /data01/mysql/data/mysql.pid # pid-file文件路径
log-bin=/data01/mysql/mysql-bin # 二进制文件存放路径,生产环境下建议将bin-log和data分磁盘存储,避免日志打满影响数据
max_connections = 1000 # 设置最大连接数
[mysqld_safe]
log-error = /data01/mysql/mariadb/mariadb.log
pid-file = /data01/mysql/mariadb/mariadb.pid
[client]
port = 3306
socket = /data01/mysql/mysql.sock
【hdp01】【hdp02】初始化基础信息,得到数据库的初始密码,在root账号下执行
cd /usr/local/mysql-5.7.25-linux-glibc2.12-x86_64/bin/
./mysqld --initialize
【hdp01】【hdp02】
配置mysqld系统服务文件/usr/lib/systemd/system/mysql.server.service
[Unit]
Description=Mysql
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/data01/mysql/data/mysql.pid
ExecStart=/usr/local/mysql-5.7.25-linux-glibc2.12-x86_64/support-files/mysql.server start
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=false
[Install]
WantedBy=multi-user.target
重新加载
systemctl daemon-reload
通过系统服务启动mysql.server并查看服务状态
systemctl start mysql.server
systemctl status mysql.server
【hdp01】【hdp02】做软链接,方便登录
ln -s /usr/local/mysql-5.7.25-linux-glibc2.12-x86_64/bin/mysql /usr/local/sbin/mysql
使用初始化获得的root密码登录一直报错,所以需要按照如下方法免密进入。
先停掉原有的系统服务mysql.server,手工携带参数–skip-grant-tables启动MySQL程序,用以免密进入MySQL修改密码(系统服务已经写好了启动参数,因此手工携带参数单次启动)
systemctl stop mysql.server
cd /usr/local/mysql-5.7.25-linux-glibc2.12-x86_64/support-files/
./mysql.server start --skip-grant-tables
先清空root密码及修改为远程登录
mysql -uroot -p
use mysql;
update user set authentication_string=‘’,host=‘%’ where user=‘root’;
flush privileges;
手工停掉免密登录的MySQL进程,以服务形式启动MySQL
./mysql.server stop
systemctl start mysql.server
再次登陆的时候是空密码登陆,然后修改密码
mysql -uroot -p
alter user ‘root’@‘%’ identified by ‘lnyd@LNsy115’;
flush privileges;
【hdp01】在mysql中,查看主节点信息
show master status;
create user 'master'@'192.168.111.202' identified with mysql_native_password by 'Aa@123456';
grant replication slave on *.* TO 'master'@'192.168.111.202';
flush privileges;
【hdp02】在mysql中,配置为slave模式
stop slave;
reset slave;
change master to master_host="192.168.111.201", master_user="master",master_password="Aa@123456", master_log_file="mysql-bin.000014",master_log_pos=605;
CHANGE MASTER TO MASTER_HOST:主服务器的IP地址
MASTER_USER:主服务器上用于同步账号
MASTER_PASSWORD:同步账号的密码
MASTER_LOG_FILE:bin日志的文件名,取自主节点SHOW MASTER STATUS结果
MASTER_LOG_POS:bin Eposition值,取自主节点SHOW MASTER STATUS结果
启用从节点
start slave;
show slave status\G;
观察Slave_IO_Running和Slave_SQL_Running如果为YES,说明运行正常
如果出现NO,可能是slave机器重起后,事务回滚造成的,在从节点上执行如下:
stop slave;
set GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
start slave;
在主节点上创建测试账号及测试库,查看从节点是否同步
【hdp01】创建测试账号及测试数据库、测试表
create user 'test_user_0212'@'localhost';
flush privileges;
create database test_db_0212;
create table test_db_0212.test_table_0212 (id integer);
【hdp02】查看是否存在测试账号、测试数据库及测试表
select user,host from mysql.user where user='test_user_0212';
show databases;
describe test_db_0212.test_table_0212;
【hdp01】创建Ambari数据库
create database ambari character set utf8;
create user 'ambari'@'%' identified by 'Ambari123456';
grant all privileges on ambari.* to 'ambari'@'%';
flush privileges;
【hdp01】yum安装ambari-server服务
yum install -y ambari-server
下载mysql-connector-java压缩包,创建/usr/share/java目录并将java-connector复制到该目录下
下载链接:
https://downloads.mysql.com/archives/get/p/3/file/mysql-connector-java-5.1.49.tar.gz
mkdir /usr/share/java
cp /opt/mysql-connector-java-5.1.49.jar /usr/share/java
自动化设置Ambari
ambari-server setup
密码:Ambari123456
登录Ambari数据库,导入Ambari相关表
mysql -uambari -pAmbari123456
use ambari;
source /var/lib/ambari-server/resources/Ambari-DDL-MySQL-CREATE.sql;
启动ambari-server服务
ambari-server start
ambari-server status
在hdp01-hdp05节点上部署Ambari-Agent
【hdp01】通过anbile完成安装并启动,/root/ansible/ambari-agent.yml
---
- hosts: all
tasks:
- name: install ambari-agent
yum:
name: ambari-agent
state: present
- name: start ambari-agent
service:
name: ambari-agent
state: started
执行
ansible-playbook ambari-agent.yml
确认状态
ansible all -m shell -a 'ambari-agent status'
登录页面
Ambari-server页面链接:http://192.168.111.201:8080/#/login
初始密码:admin/admin
修改admin用户的缺省密码为lnyd@LNsy115
Server端
在hdp01、hdp02上安装
ansible nn -m yum -a 'name=openldap-servers state=present'
ansible nn -m yum -a 'name=migrationtools state=present'
Client端
在hdp01-05上安装
ansible all -m yum -a 'name=openldap-clients state=present'
OpenLDAP的配置以树状结构存储在OpenLDAP数据库中,这些节点的配置文件以ldif格式存储在/etc/openldap/slapd.d目录下(从OpenLDAP2.4.23版本开始所有配置数据都保存在/etc/openldap/slapd.d/中,建议不再使用slapd.conf作为配置文件)
① cn=config
根节点用于全局性配置,它的配置文件是/etc/openldap/slapd.d/cn=config.ldif
② cn=module{0},cn=config
用于配置动态模块,它的配置文件位于/etc/openldap/slapd.d/cn=config目录下
③ cn=schema,cn=config
用于schema配置,它的配置文件位于/etc/openldap/slapd.d/cn=config目录下
④ olcDatabase={X},cn=config
用于配置数据库,它的配置文件位于/etc/openldap/slapd.d/cn=config目录下
以下所有内容在hdp01和hdp02上均进行操作
/etc/openldap/slapd.d/cn=config/olcDatabase={2}hdb.ldif
查看密码对应的md5值
slappasswd -h {md5} -s "lnyd@LNsy115"
修改olcDatabase={2}hdb.ldif文件,将管理员密码的md5值增加到此文件中
其中cn=admin中的admin表示OpenLDAP管理员的用户名,可将cn和dc按照实际情况修改,而olcRootPW表示OpenLDAP管理员的密码。
dn: olcDatabase={2}hdb
objectClass: olcDatabaseConfig
objectClass: olcHdbConfig
olcDatabase: {2}hdb
olcDbDirectory: /var/lib/ldap
olcSuffix: dc=hdp315,dc=com
olcRootDN: cn=admin,dc=hdp315,dc=com
olcRootPW: {MD5}WL8F9lXqbxOxR0xwaIG9Jg==
olcDbIndex: objectClass eq,pres
olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub
structuralObjectClass: olcHdbConfig
entryUUID: 22848b60-4563-103d-8500-c5b20ff267b6
creatorsName: cn=config
createTimestamp: 20230220120913Z
entryCSN: 20230220120913.754062Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20230220120913Z
/etc/openldap/slapd.d/cn=config/olcDatabase={1}monitor.ldif
将dn.base中OpenLDAP管理员的相关信息修改为{2}hdb.ldif中相同的内容
dn: olcDatabase={1}monitor
objectClass: olcDatabaseConfig
olcDatabase: {1}monitor
olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=extern
al,cn=auth" read by dn.base="cn=admin,dc=hdp315,dc=com" read by * none
structuralObjectClass: olcDatabaseConfig
entryUUID: 2284858e-4563-103d-84ff-c5b20ff267b6
creatorsName: cn=config
createTimestamp: 20230220120913Z
entryCSN: 20230220120913.753913Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20230220120913Z
/etc/openldap/slapd.d/cn=config/olcDatabase={0}config.ldif
增加olcRootDN和olcRootPW项
dn: olcDatabase={0}config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=extern
al,cn=auth" manage by * none
olcRootDN: cn=config
olcRootPW: {MD5}WL8F9lXqbxOxR0xwaIG9Jg==
structuralObjectClass: olcDatabaseConfig
entryUUID: 228480e8-4563-103d-84fe-c5b20ff267b6
creatorsName: cn=config
createTimestamp: 20230220120913Z
entryCSN: 20230220120913.753793Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20230220120913Z
对上述配置进行测试验证
slaptest -u
启动LDAP程序
systemctl start slapd
systemctl status slapd
LDAP默认监听端口为389,通过netstat进行验证
netstat -tunalp | grep 389
Schema是LDAP的一个重要组成部分,类似于数据库的模式定义,LDAP的Schema定义了LDAP目录所应遵循的结构和规则,被LDAP服务器识别。schema是一个标准,定义了ldap的对象和属性,也就是ldap能够存储什么数据,数据有什么属性等。
以下操作在hdp01和hdp02上均进行。
从/etc/openldap/schema/中导入3个基础的schema
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/cosine.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/nis.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/inetorgperson.ldif
migrate_common.ph文件主要是用于生成ldif文件使用
vim /usr/share/migrationtools/migrate_common.ph
修改内容如下
$DEFAULT_MAIL_DOMAIN = "hdp315.com";
$DEFAULT_BASE = "dc=hdp315,dc=com";
$EXTENDED_SCHEMA = 1;
本次配置采用MirrorMode方式。
在hdp01和hdp02上,创建mod_syncprov.ldif文件,ldap双主复制功能的实现依赖于syncprov模块,这个模块位于/usr/lib64/openldap目录下
/etc/openldap/syncprov_mod.ldif
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulePath: /usr/lib64/openldap
olcModuleLoad: syncprov.la
加载模块
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/syncprov_mod.ldif
在hdp01和hdp02上,创建syncprov.ldif文件,导入同步配置日志信息,/etc/openldap/syncprov_enable.ldif
dn: olcOverlay=syncprov,olcDatabase={2}hdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpCheckpoint: 1 1
olcSpSessionLog: 32767
olcSpCheckpoint:表示当修改1条或者1分钟时,以实现实时同步
olcSpSessionLog:session log会话日志条目的最大数量
加载模块
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/syncprov_enable.ldif
在hdp01上,创建master_node_1.ldif文件,/etc/openldap/master_node_1.ldif
(注:每个dn属于u一部分,上面需要加一个空行,否则会报错)
2个主节点属性olcServerID的值不能相同,provider指向对方
provider:同步来源,也就是主节点,可以包含多个主节点
binddn:主节点管理账户
credentials主节点管理账户密码
searchbase:根目录
dn: cn=config
changetype: modify
replace: olcServerID
olcServerID: 0
dn: olcDatabase={2}hdb,cn=config
changetype: modify
add: olcSyncRepl
olcSyncRepl: rid=001
provider=ldap://hdp02.hdp.com:389/
bindmethod=simple
binddn="cn=admin,dc=hdp315,dc=com"
credentials=lnyd@LNsy115
searchbase="dc=hdp315,dc=com"
scope=sub
schemachecking=on
type=refreshAndPersist
retry="30 5 300 3"
interval=00:00:05:00
-
add: olcMirrorMode
olcMirrorMode: TRUE
dn: olcOverlay=syncprov,olcDatabase={2}hdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
注:credentials为其他主节点的密码,只能用明文,不能用md5加密等格式
加载生效
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/master_node_1.ldif
在hdp02上,创建master_node_2.ldif文件,/etc/openldap/master_node_2.ldif
dn: cn=config
changetype: modify
replace: olcServerID
olcServerID: 1
dn: olcDatabase={2}hdb,cn=config
changetype: modify
add: olcSyncRepl
olcSyncRepl: rid=001
provider=ldap://hdp01.hdp.com:389/
bindmethod=simple
binddn="cn=admin,dc=hdp315,dc=com"
credentials=lnyd@LNsy115
searchbase="dc=hdp315,dc=com"
scope=sub
schemachecking=on
type=refreshAndPersist
retry="30 5 300 3"
interval=00:00:05:00
-
add: olcMirrorMode
olcMirrorMode: TRUE
dn: olcOverlay=syncprov,olcDatabase={2}hdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
加载生效
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/master_node_2.ldif
① 安装
在hdp04和hdp05安装keepalived和HAProxy服务
yum install -y haproxy keepalived
② 配置HAProxy
在hdp04和hdp05上,修改配置文件,/etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend tcp_389_openldap
bind *:389
mode tcp
stats uri /haproxy?stats
default_backend tcp_389_openldap
#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend tcp_389_openldap
mode tcp
balance roundrobin
server server1 hdp01.hdp.com:389 check weight 100
server server2 hdp02.hdp.com:389 check weight 1
启动keepalived服务
systemctl start haproxy
systemctl status haproxy
③ 配置KeepAlived
在hdp04上,修改配置文件,/etc/keepalived/keepalived.conf
global_defs {
notification_email {
root@localhost
}
notification_email_from root@localhost
smtp_server localhost
smtp_connect_timeout 30
router_id hdp04.hdp.com
script_user root
enable_script_security
}
vrrp_script chk_ha_port {
script "/etc/keepalived/chk_ha.sh"
interval 2
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state MASTER
interface ens192
virtual_router_id 128
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 12345678
}
virtual_ipaddress {
192.168.111.222
}
track_script {
chk_ha_port
}
}
在hdp05上,修改配置文件,/etc/keepalived/keepalived.conf
global_defs {
notification_email {
root@localhost
}
notification_email_from root@localhost
smtp_server localhost
smtp_connect_timeout 30
router_id hdp05.hdp.com
script_user root
enable_script_security
}
vrrp_script chk_ha_port {
script "/etc/keepalived/chk_ha.sh"
interval 2
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state BACKUP
interface ens192
virtual_router_id 128
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 12345678
}
virtual_ipaddress {
192.168.111.222
}
track_script {
chk_ha_port
}
}
监测脚本,/etc/keepalived/chk_ha.sh
#!/bin/bash
counter=$(ps -C haproxy --no-heading | wc -l)
if [ "${counter}" = "0" ]; then
systemctl start haproxy
sleep 1
counter=$(ps -C haproxy --no-heading | wc -l)
if [ "${counter}" = "0" ]; then
systemctl stop keepalived
fi
fi
启动keepalived服务
systemctl start keepalived
systemctl status keepalived
在hdp04上查看ens192端口上的IP增加了192.168.111.222
ip a
测试切换的时候,可以将chk_ha.sh中的启动haproxy注释掉,防止再次被拉起
默认情况下OpenLDAP是没有普通用户的,但是有一个管理员用户。
现在需要Linux系统中的用户tenant1,添加到OpenLDAP中。
useradd tenant1
echo "Aa123456" | passwd --stdin tenant1
提取tenant1的信息
grep -E "tenant1[^.]" /etc/passwd > /root/users
grep -E "tenant1[^.]" /etc/group > /root/groups
cat /root/users /root/groups
根据上述生成的用户和用户组属性,使用migrate_passwd.pl文件生成要添加用户和用户组的ldif(可以从/etc/passwd、/etc/shadow、/etc/groups中生成ldif更新ldap数据库,需要用到migrationtools工具)
/usr/share/migrationtools/migrate_passwd.pl /root/users > /root/template.ldif
/usr/share/migrationtools/migrate_group.pl /root/groups >> /root/template.ldif
cat /root/template.ldif
memberUid: uid=tenant1,ou=People,dc=hdp315,dc=com
后续如果要新加用户到OpenLDAP中的话,可以直接修改template.ldif文件即可。
注:增加LDAP用户,无需在OS层面添加用户,tenant1的添加只是为了生成ldif模板
在hdp01上,创建/etc/openldap/base.ldif文件,先创建hdp315这个organization(简称O),然后在其下面创建三个OU:
ou=People,dc=hdp315,dc=com用于存放用户账号
ou=services,dc=hdp315,dc=com用户存放服务账号
ou=Group,dc=hdp315,dc=com用户存放组账号
dn: dc=hdp315,dc=com
objectClass: top
objectClass: dcObject
objectclass: organization
o: hdp315
dn: cn=admin,dc=hdp315,dc=com
objectClass: organizationalRole
cn: admin
description: Directory Admin
dn: ou=People,dc=hdp315,dc=com
objectClass: organizationalUnit
ou: People
dn: ou=services,dc=hdp315,dc=com
objectclass: organizationalUnit
ou: services
dn: ou=Group,dc=hdp315,dc=com
objectClass: organizationalUnit
ou: Group
导入
ldapadd -x -D "cn=admin,dc=hdp315,dc=com" -w "lnyd@LNsy115" -f /etc/openldap/base.ldif
使用管理员账号admin添加普通用户tenant1到LDAP中,其中-D指定了管理员账号去执行,-w为管理员账号的口令
ldapadd -x -w "lnyd@LNsy115" -D "cn=admin,dc=hdp315,dc=com" -f /root/template.ldif
使用ldapsearch查询全部用户信息
ldapsearch -x -H ldap://hdp01.hdp.com:389 -b 'ou=People,dc=hdp315,dc=com'
在hdp01上创建的tenant1用户,在hdp02的LDAP上查询
ldapsearch -x -H ldap://hdp01.hdp.com:389 -b 'ou=People,dc=hdp315,dc=com' 'uid=tenant1'
ldapsearch -x -H ldap://hdp02.hdp.com:389 -b 'ou=People,dc=hdp315,dc=com' 'uid=tenant1'
ldapsearch -x -H ldap://192.168.111.222:389 -b 'ou=People,dc=hdp315,dc=com' 'uid=tenant1'
关闭hdp01上的slapd程序,测试是否负载至hdp02上
在hdp01上分别进行Yum安装phpldapadmin服务
yum install -y phpldapadmin
yum安装完phpldapadmin后会默认在/etc/httpd/conf.d加入,修改配置文件/etc/httpd/conf.d/phpldapadmin.conf
Alias /phpldapadmin /usr/share/phpldapadmin/htdocs
Alias /ldapadmin /usr/share/phpldapadmin/htdocs
<Directory /usr/share/phpldapadmin/htdocs>
<IfModule mod_authz_core.c>
# Apache 2.4
#Require local
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
Allow from ::1
</IfModule>
</Directory>
修改phpldapadmin的设置,首先备份配置文件
cp /etc/phpldapadmin/config.php /etc/phpldapadmin/config.php.bak.20230222
/etc/phpldapadmin/config.php,修改内容如下:
#注释掉以下行
#287行
#$servers->newServer('ldap_pla');
#291行
#$servers->setValue('server','name','Local LDAP Server');
#388行
#$servers->setValue('appearance','pla_password_hash','');
#398行
#$servers->setValue('login','attr','uid');
#取消注释并增加以下行,537行开始
$servers->newServer('ldap_pla');
$servers->setValue('server','name','hdp01 Server');
$servers->setValue('server','host','192.168.111.201');
$servers->setValue('server','port',389);
$servers->setValue('server','base',array('dc=hdp315,dc=com'));
$servers->setValue('login','auth_type','cookie');
$servers->setValue('login','bind_id','cn=admin,dc=hdp315,dc=com');
$servers->setValue('login','bind_pass','lnyd@LNsy115');
$servers->setValue('login','anon_bind',false); !此行为新增
$servers->setValue('server','tls',false);
$servers->newServer('ldap_pla');
$servers->setValue('server','name','hdp02 Server');
$servers->setValue('server','host','192.168.111.202');
$servers->setValue('server','port',389);
$servers->setValue('server','base',array('dc=hdp315,dc=com'));
$servers->setValue('login','auth_type','cookie');
$servers->setValue('login','bind_id','cn=admin,dc=hdp315,dc=com');
$servers->setValue('login','anon_bind',false);
$servers->setValue('login','bind_pass','lnyd@LNsy115');
重启httpd服务
systemctl restart httpd
systemctl status httpd
访问页面http://192.168.111.201/phpldapadmin,可以分别选择不同的OpenLDAP Server进行登录,用户名为在config.php中设置的内容
选择使用本地镜像仓库安装(Use Local Repository),将其他os部分删除
HDP-3.1:http://hdp01.hdp.com/HDP/centos7/3.1.5.0-152/
HDP-3.1-GPL:http://hdp01.hdp.com/HDP-GPL/centos7/3.1.5.0-152/
HDP-UTILS-1.1.0.22:http://hdp01.hdp.com/HDP-UTILS/centos7/1.1.0.22/
将hadoop集群中所有节点都加入(hdp01-05),并将hdp01的SSH私钥附上,查看私钥
hdp01.hdp.com
hdp02.hdp.com
hdp03.hdp.com
hdp04.hdp.com
hdp05.hdp.com
cat /root/.ssh/id_rsa
仅安装最基础的ZooKeeper、Ambari Metrics和smartsense
smartsense是hortonworks一个商业的组件功能,作用是监控集群并提供建议,建议是不使用。通常,此组件是安装ambari的时候的一个必选项,也就是说在安装ambari的时候它就强制绑定安装了。后面可以删除。
按照指示,client为zookeeper的,选择在hdp04、hdp05上安装即可
密码设置为lnyd@LNsy115
配置ZooKeeper路径
ZooKeeper directory:/data01/hadoop/zookeeper
ZooKeeper Log Dir:/var/log/zookeeper
ZooKeeper PID Dir:/var/run/zookeeper
配置Ambari Metrics路径
Aggregator checkpoint directory:/var/lib/ambari-metrics-collector/checkpoint
Metrics Grafana data dir:/var/lib/ambari-metrics-grafana
HBase Local directory:${hbase.tmp.dir}/local
HBase root directory:file:///var/lib/ambari-metrics-collector/hbase
HBase tmp directory:/var/lib/ambari-metrics-collector/hbase-tmp
HBase ZooKeeper Property DataDir:${hbase.tmp.dir}/zookeeper
Phoenix Spool directory:${hbase.tmp.dir}/phoenix-spool
Phoenix Spool directory:/tmp
Metrics Collector log dir:/var/log/ambari-metrics-collector
Metrics Monitor log dir:/var/log/ambari-metrics-monitor
Metrics Grafana log dir:/var/log/ambari-metrics-grafana
HBase Log Dir Prefix:/var/log/ambari-metrics-collector
Metrics Collector pid dir:/var/run/ambari-metrics-collector
Metrics Monitor pid dir:/var/run/ambari-metrics-monitor
Metrics Grafana pid dir:/var/run/ambari-metrics-grafana
HBase PID Dir:/var/run/ambari-metrics-collector/
设置各个服务的账号
Smoke User:ambari-qa
Hadoop Group:hadoop
Ambari Metrics User:ams
ZooKeeper User:zookeeper
待安装服务的所有配置
SMARTSENSE中的smartsense.id需要指定,不能为unspecified,可设置为1000
安装完成后,先stop然后delete掉SmartSense服务。
ZooKeeper的配置文件,/etc/zookeeper/conf/zoo.cfg
clientPort=2181
autopurge.purgeInterval=24
syncLimit=5
quorum.cnxn.threads.size=20
initLimit=10
dataDir=/data01/hadoop/zookeeper
tickTime=3000
autopurge.snapRetainCount=30
quorum.auth.enableSasl=false
server.1=hdp01.hdp.com:2888:3888
server.2=hdp02.hdp.com:2888:3888
server.3=hdp03.hdp.com:2888:3888
配置解析:
① clientPort:客户端连接端口
客户端连接Zookeeper服务器的端口,Zookeeper会监听这个端口,接受客户端的访问请求。
② autopurge.purgeInterval:日志自动清理频率
指定了清理频率,单位是小时,需要填写一个1或更大的整数,默认是0,表示不开启自己清理功能。
③ syncLimit:LF同步通信时限
集群中的follower服务器与leader服务器之间请求和应答之间能容忍的最多心跳数(tickTime的数量)。
④ quorum.cnxn.threads.size:设置可使用的最大线程池
⑤ initLimit:LF初始通信时限
集群中的follower服务器(F)与leader服务器(L)之间初始连接时能容忍的最多心跳数(tickTime的数量)。
⑥ dataDir:数据文件目录
Zookeeper保存数据的目录,默认情况下,Zookeeper将写数据的日志文件也保存在这个目录里。
⑦ tickTime:CS通信心跳时间
Zookeeper服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个tickTime 时间就会发送一个心跳。tickTime以毫秒为单位。
⑧ autopurge.snapRetainCount:sanp保留数量
内存中的数据作为snapshot保存下来,该参数指定了需要保留多少个snapshot,之前的全删除。默认是保留3个。
⑨ quorum.auth.enableSasl:Sasl开关
⑩ server.A= B:C:D :服务器名称与地址
(服务器编号,服务器地址,LF 通信端口,选举端口)
A是一个数字,表示这个是第几号服务器;
B是这个服务器的ip地址;
C表示的是这个服务器与集群中的Leader服务器交换信息的端口;
D表示的是万一集群中的Leader服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口。
/usr/hdp/3.1.5.0-152/zookeeper/bin/zkServer.sh status
在hdp01-03上分别查询可知,hdp03为leader,因为hdp03的myid最大
ansible nn,192.168.111.203 -m shell -a 'cat /data01/hadoop/zookeeper/myid'
/usr/hdp/3.1.5.0-152/zookeeper/bin/zkCli.sh -server hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181
ls /node1
create [-s] [-e] path data acl
创建一个znode节点,同时设置节点权限acl,-s表示创建有序节点,-e创建临时节点
znode需要按照层级去创建,如创建/node1/node2,需要先创建/node1,再创建/node1/node2
create /node1 test1
create /node1/node2 test2
ls2 path [watch]
列出znode的子节点,同时可以设置一个监听器,如:ls2 /,与ls的区别是ls2还可以获取到子节点个数等等状态信息
ls2 /node1
get path [watch]
获取znode节点的数据,同时可以注册一个监听器,如:get /mynode
get /node1
get /node1/node2
stat path [watch]
查看znode状态,如数据长度,时间戳等等,同时可以注册一个监听器
stat /node1
stat /node1/node2
set path data [version]
设置znode的数据,同时可以设置一个监听器,如:set /mynode “hello world”
set /node1 test3
设置完成后,mZxid(数据节点最后一次更新时的事务ID)会发生变化
delete path [version]
删除znode节点,注意路径为绝对路径,且不可删除拥有子节点的znode
delete /node1/node2/node3
rmr path
递归删除znode节点,与delete的区别是可以删除拥有子节点的znode
rmr /node1
setAcl /node1 ip:192.168.111.201:crwd
getAcl path
查看节点的权限
getAcl /node1
查看日志/var/log/ambari-metrics-collector/ambari-metrics-collector.log
需要把/var/lib/ambari-metrics-collector/下的checkpoint、hbase和hbase-tmp删除,如需要也可以先备份,然后在重新启动metrics-collector服务
rm -rf /var/lib/ambari-metrics-collector/*
报错信息
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry \'hdp04.hdp.com\' for key \'UQ_hosts_host_name\'\
Error Code: 1062\
Call: INSERT INTO hosts (host_id, cpu_count, cpu_info, discovery_status, host_attributes, host_name, ipv4, ipv6, last_registration_time, os_arch, os_info, os_type, ph_cpu_count, public_host_name, rack_info, total_mem) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\
\\tbind => [16 parameters bound]\
确认错误原因为在MySQL中的hosts表中INSERT数据时主键的值已经存在导致,登录到ambari的MySQL数据库中,首先关闭外键约束,然后删除对应的记录,再打开外键约束,
use ambari;
select host_name from hosts;
SET FOREIGN_KEY_CHECKS=0;
delete from hosts where host_name = 'hdp04.hdp.com';
select host_name from hosts;
SET FOREIGN_KEY_CHECKS=1;
ambari-server restart
服务端,在hdp01上安装
yum install -y krb5-server libkadm5
客户端,在hdp01-05上安装(可选,后面ambari启用kerberos的时候也会安装)
ansible all -m yum -a 'name=krb5-workstation state=present'
在hdp01上修改配置文件/etc/krb5.conf
includedir /etc/krb5.conf.d/
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log
[libdefaults]
dns_lookup_realm = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
rdns = false
pkinit_anchors = /etc/pki/tls/certs/ca-bundle.crt
default_realm = HDP315.COM
default_ccache_name = KEYRING:persistent:%{uid}
[realms]
HDP315.COM = {
kdc = hdp01.hdp.com
admin_server = hdp01.hdp.com
}
[domain_realm]
.hdp315.com = HDP315.COM
hdp315.com = HDP315.COM
3.修改kdc配置
修改/var/kerberos/krb5kdc/kdc.conf
[kdcdefaults]
kdc_ports = 88
kdc_tcp_ports = 88
[realms]
HDP315.COM = {
master_key_type = aes256-cts
acl_file = /var/kerberos/krb5kdc/kadm5.acl
dict_file = /usr/share/dict/words
admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab
supported_enctypes = aes256-cts:normal aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal camellia256-cts:normal camellia128-cts:normal des-cbc-md5:normal des-cbc-crc:normal
}
支持的加密方式务必要删掉des-hmac-sha1:normal,否则后续添加hdfs等服务会报WARNING,namenode启动和HA时都会出现问题
WARN security.UserGroupInformation: Not attempting to re-login since the last re-login was attempted less than 60 seconds before
server.AuthenticationFilter (AuthenticationFilter.java:doFilter(608)) - Authentication exception: GSSException: Failure unspecified at GSS-API level (Mechanism level: Encryption type DES3 CBC mode with SHA1-KD is not supported/enabled)
修改/var/kerberos/krb5kdc/kadm5.acl,给数据库管理员admin添加ACL权限,*代表全部
*/[email protected] *
创建数据库,其中HDP315.COM与/etc/krb5.conf对应,且需要设置管理员密码(lnyd@LNsy115),该命令会在/var/kerberos/krb5kdc/创建principal数据库。
kdb5_util create -r HDP315.COM
ll /var/kerberos/krb5kdc/
如果遇到数据库已经存在的提示,且需要重建数据库时,可以把/var/kerberos/krb5kdc/目录下的principal的相关文件都删除掉。
启用服务
systemctl start krb5kdc
systemctl start kadmin
systemctl status krb5kdc
systemctl status kadmin
创建管理员admin,以及keyta文件,/var/kerberos/krb5kdc/kadm5.keytab
kdc配置文件中指定了管理员的密码文件为kadmin5.keytab,因此新建账号admin密码存放在了kadmin5.keytab中,相当于获得了管理员身份,管理员的权限则由kadmin.acl中进行控制。账号需要为XXX/[email protected],对应到kadmin5.acl中的格式。
kadmin.local
addprinc -pw lnyd@LNsy115 admin/[email protected]
ktadd -k /var/kerberos/krb5kdc/kadm5.keytab -norandkey admin/[email protected]
确认是否可以登录,分别用密码和keytab两种方式登录
kinit admin/[email protected]
klist
kdestroy
kinit -kt /var/kerberos/krb5kdc/kadm5.keytab admin/[email protected]
klist
进一步验证是否有管理员权限,用kadmin登录,成功则表示权限无问题
kadmin -p admin/[email protected] -k -t /var/kerberos/krb5kdc/kadm5.keytab
对于Kerberos系统来说,默认使用的AES-256来进行加密。在集群启用Kerberos之前,必须在Ambari集群上的每个节点上都装有JCE。
如果使用的是Oracle JDK,则必须在群集中的所有主机上分发和安装JCE,包括Ambari Server,安装JCE后,需要重新启动Ambari Server;如果使用的是OpenJDK,OpenJDK的某些发行版会自动提供无限强度的JCE,因此不需要安装JCE。
JCE与JDK版本是对应的,需要根据JDK的版本来选择JCE版本,下载JCE的zip包并解压到$JAVA_HOME/jre/lib/security目录下。
unzip /opt/jce_policy-8.zip -d /opt/
cd /root/ansible/
ansible all -m copy -a 'src=/opt/UnlimitedJCEPolicyJDK8/local_policy.jar dest=/usr/local/jdk1.8.0_351/jre/lib/security/'
ansible all -m copy -a 'src=/opt/UnlimitedJCEPolicyJDK8/US_export_policy.jar dest=/usr/local/jdk1.8.0_351/jre/lib/security/'
重启ambari server
ambari-server restart
ambari-server status
选项 | 说明 |
---|---|
Using an existing MIT KDC | Ambari服务器和集群主机都可以通过网络访问KDC和KDC管理主机。手头有KDC管理凭证。 |
Install a new MIT KDC | 安装一个新的MIT KDC |
Using an existing IPA | 使用现有的IPA |
Using an existing AD | Ambari服务器和集群主机可以通过网络访问域控制器,并能够解析域控制器的DNS名称。已配置LDAP连接。服务主体的Active Directory用户容器已经创建并准备就绪。例如,“OU=Hadoop,OU=People,dc=apache,dc=org”在前面提到的用户容器上,Active Directory管理凭据具有“创建、删除和管理用户帐户”的委托控制。 |
Using manual Kerberos setup | 集群主机可以通过网络访问KDC。每个集群主机上都安装了Kerberos客户端实用程序(如kinit)。已经在Ambari服务器主机和集群中的所有主机上设置了Java加密扩展(JCE)。在完成向导之前,Service和Ambari主体将在KDC中手动创建。在完成向导之前,服务和Ambari主体的按键将手动创建并分发到集群主机。 |
配置Kerberos信息
【KDC信息】
KDC hosts:hdp01.hdp.com
Realm name:HDP315.COM
Domains:hdp315.com
【Kadmin】
Kadmin host:hdp01.hdp.com
Admin principal:admin/[email protected]
Admin password:lnyd@LNsy115
在集群启用Kerberos后,会自动创建出已安装服务(如zookeeper等)的principal,按照约定:服务账号是principal/instance@REALM格式,用户账号是principal@REALM;后续所有服务的操作(安装/卸载等),都需要用对应的principal来进行认证,以增强安全性,keytab文件存储在/etc/security/keytab/目录下
查看zk的server配置,/etc/zookeeper/conf/zookeeper_jaas.conf
Server {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
useTicketCache=false
keyTab="/etc/security/keytabs/zk.service.keytab"
principal="zookeeper/[email protected]";
};
配置中指定了连接ZooKeeper的principal和keytab要求,因此后面再用客户端zkCLi连接时,需要以此凭证连接
登录Kerberos server
kadmin.local 用户名
列出所有Kerberos用户
kadmin.local -q listprincs
添加Kerberos用户
kadmin.local -q "addprinc [options] principal"
options主要为
-randkey:随机生成一个值作为principal的key
-pw:设置密码,此选项一般用在脚本中
修改Principal用户信息
kadmin.local -q "modprinc [options] principal"
修改Principal密码
kadmin.local -q "change_password [-randkey] [-keepold] [-e keysaltlist] [-pw password] principal"
删除Principal信息
kadmin.local -q "delete_principal [-force] principal"
生成keytab认证文件
kadmin.local -q "ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] [-norandkey] [principal | -glob princ-exp]"
获取票据credentials
klist
klist -k /root/tenant5.keytab
更新Kerberos票据credentials信息,更新后可以看到Expires时间发生变化
kinit -R -kt keytab principal
kdestroy
HDFS依赖libtirpc-devel,因此需要先安装libtirpc-devel。
创建yml文件,/root/ansible/libtirpc.yml
---
- hosts: all
vars:
var_package:
- libtirpc-devel-0.2.4-0.16.el7.x86_64.rpm
tasks:
- name: copy install files
copy:
src: "/opt/{{ item }}"
dest: /root/
loop: "{{ var_package }}"
- name: install package
shell:
cmd: "yum localinstall -y /root/{{ item }}"
loop: "{{ var_package }}"
- name: delete install files
file:
path: "/root/{{ item }}"
state: absent
loop: "{{ var_package }}"
执行
ansible-playbook /root/ansible/libtirpc.yml
在Serivces->Add Service中添加HDFS服务
在ACTIONS->Enable NameNode HA中配置
修改JournalNode的路径为/data01/hadoop/hdfs/journal
按照提示在hdp01上创建checkpoint
sudo su hdfs -l -c 'hdfs dfsadmin -safemode enter'
sudo su hdfs -l -c 'hdfs dfsadmin -saveNamespace'
sudo su hdfs -l -c 'hdfs namenode -initializeSharedEdits'
sudo su hdfs -l -c 'hdfs zkfc -formatZK'
sudo su hdfs -l -c 'hdfs namenode -bootstrapStandby'
/etc/hadoop/conf/core-site.xml,是NameNode的核心配置文件,主要对NameNode的属性进行设置,也仅仅在NameNode节点生效。
nn和2nn时,fs.defaultFS为hdfs://hdp01.hdp.com:8020
改为nn HA后,fs.defaultFS为hdfs://hdp315,以高可用集群出现
参数 | 含义 | 配置值 |
---|---|---|
fs.defaultFS | 指定访问HDFS文件系统的URI,在HA集群中,此值必须和hdfs-site.xml中的dfs.nameservices配置值一致 | hdfs://hdp315 |
ha.zookeeper.quorum | ZooKeeper集群的地址和端口。注意,数量一定是奇数,且不少于三个节点 | hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 |
fs.trash.interval | 定义.trash目录下文件被永久删除前保留的时间。在文件从HDFS永久删除前,用户可以自由地把文件从该目录下移出来并立即还原。默认值0,说明垃圾回收站功能是关闭的,一般开启这个会比较好,以防错误删除重要文件,单位是分钟 | 360 |
正常情况下,kerberos对web页面也会进行认证,可取消掉;如果是对安全较高的场景下,需要在windows电脑上安装kerberos客户端,来实现身份认证,进而登录到web中。
如果未取消认证,会出现如下的界面
取消kerberos认证的配置
HDFS中CONFIGS->ADVANCED中,
Advanced core-site
hadoop.http.authentication.simple.anonymous.allowed:true
Custom core-site
hadoop.http.authentication.type:simple
重启hdfs服务后,namenode页面可以正常打开
/etc/hadoop/conf/hdfs-site.xml,是HDFS的核心配置文件,主要配置NameNode、DataNode的一些基于HDFS的属性信息、在NameNode和DataNode节点生效。
参数 | 含义 | 配置值 |
---|---|---|
dfs.nameservices | 指定一个逻辑上的HDFS集群服务名,该服务名是自定义的。当外界访问HDFS集群时,入口就是这个服务名 | HDP |
dfs.ha.namenodes.[nameservice ID] | 指定两个NameNode的唯一标识,名字随便起,相互不重复即可,在HDFS集群管理中会用到 | nn1,nn2 |
dfs.namenode.rpc-address.[nameservice ID].[name node ID] | 指定nn01、nn02的RPC地址 | hdp01.hdp.com:8020 hdp02.hdp.com:8020 |
dfs.namenode.http-address.[nameservice ID].[name node ID] | 指定nn01、nn02的http地址 | hdp01.hdp.com:50070 hdp02.hdp.com:50070 |
dfs.namenode.shared.edits.dir | 指定集群的两个NameNode共享edits文件目录时,使用JournalNode集群的信息 | qjournal://hdp01.hdp.com:8485;hdp02.hdp.com:8485;hdp03.hdp.com:8485/hdp315nn |
dfs.journalnode.edits.dir | 指定JournalNode集群在对NameNode的元数据目录进行共享时,数据在本地磁盘存储的路径 | /data01/hadoop/hdfs/journal |
dfs.replication | 指定DataNode存储数据块的副本数量。默认值是3个,现在有3个DataNode,该值不大于3即可 | 3 |
dfs.ha.fencing.methods | 配置隔离机制,一旦需要NameNode切换,使用shell方式进行操作 | shell(/bin/true) |
dfs.namenode.name.dir | 用于确定将HDFS文件系统的元信息保存在什么目录下。如果这个参数设置为多个目录,那么这些目录下都保存着元信息的镜像备份,推荐多个磁盘路径存放元数据 | /data01/hadoop/hdfs/namenode |
dfs.datanode.data.dir | 用于确定将HDFS文件系统的数据存储在本地磁盘哪个目录下。可以将这个参数设置为多个磁盘分区上的不同目录,即可将HDFS数据分布在多个不同磁盘分区上 | /data01/hadoop/hdfs/data,/data02/hadoop/hdfs/data,/data03/hadoop/hdfs/data |
dfs.permissions.enabled | 表示是否在HDFS中开启权限检查,true表示开启,false表示关闭,生产环境建议开启 | true |
NameNode的内存计算:
每个文件块大概占用150byte,hdp01-02的内存为8G,能存储的文件块为
810241024*1024/150Byte≈5700万
在ambari上配置后内存后,会同步更新到/etc/hadoop/conf/hadoop-env.sh
在SETTINGS中将内存设置为5G,然后通过ADVANCED下的Advanced hadoop-env中的参数进行传递
export HADOOP_NAMENODE_INIT_HEAPSIZE=“-Xms{{namenode_heapsize}}”
然后在配置文件hadoop-env.sh中,可以看到内存已经更改为5G
export HADOOP_NAMENODE_INIT_HEAPSIZE=“-Xms5120m”
NameNode不仅要应对客户端的请求,还需要对DataNode的心跳进行接收,这些均需要线程
具体在hdfs-site.xml中设置
<property>
<name>dfs.namenode.handler.count</name>
<value>21</value>
</property>
d f s . n a m e n o d e . h a n d l e r . c o u n t = 20 × log e C l u s t e r S i z e dfs.namenode.handler.count = 20\times\log_e^{Cluster Size} dfs.namenode.handler.count=20×logeClusterSize,比如集群规模(DataNode台数)为3台时,此参数设置为21。
NameNode的HA依赖于ZooKeeper,启用后在zk下会产生节点目录。因为已经开启了kerberos认证,因此在查看前应该先以服务principal登录到kdc上,然后才有权限查看到namenode的目录
kinit -kt /etc/security/keytabs/nn.service.keytab nn/[email protected]
/usr/hdp/3.1.5.0-152/zookeeper/bin/zkCli.sh -server hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181
ls /hadoop-ha/hdp315nn
因为开启了kerberos认证,所以需要先以这个principal登录,才能进行操作,否则会报错`
kinit -kt /etc/security/keytabs/nn.service.keytab nn/[email protected]
①mkdir:创建路径
hdfs dfs -mkdir /testhdfs
②ls: 显示目录信息
hdfs dfs -ls /
③cat:显示文件内容
hdfs dfs -cat /testhdfs/test0219.txt
④chmod、chown:更改权限及归属
hdfs dfs -chmod 777 /testhdfs/test0219.txt
hdfs dfs -chown hdfs:hadoop /testhdfs/test0219.txt
⑤cp:从HDFS的一个路径拷贝到HDFS的另一个路径
hdfs dfs -cp /testhdfs/test0219.txt /testhdfs/tmp/
⑥rm:删除文件或文件夹
hdfs dfs -rm /testhdfs/tmp/test0219.txt
⑦mv:在HDFS目录中移动文件
hdfs dfs -mv /testhdfs/test0219.txt /testhdfs/tmp/
⑧tail:显示一个文件的末尾1kb的数据
hdfs dfs -tail /testhdfs/tmp/test0219.txt
⑨rm -r:递归删除目录及目录里面内容
hdfs dfs -rm -r /testhdfs/tmp/
⑩du:统计文件夹的大小信息
第一列标示该目录下总文件大小
第二列标示该目录下所有文件在集群上的总存储大小和你的副本数相关,默认副本数是3,所以第二列的是第一列的三倍(第二列内容=文件大小*副本数)
hdfs dfs -du -s -h /testhdfs
hdfs dfs -du -s -h /testhdfs/test0219.txt
hdfs dfs -setrep 10 /testhdfs/test0219.txt
①moveFromLocal:把本地的文件剪切到HDFS上
hdfs dfs -moveFromLocal /home/hdfs/test0219.txt /testhdfs/
将hdfs家目录下的test0219.txt上传到HDFS根目录下的testhdfs文件夹
②copyFromLocal:将本地文件复制到HDFS上
hdfs dfs -copyFromLocal /home/hdfs/test0219-1.txt /testhdfs/
③put:等同于copyFromLocal,生产环境更习惯用put
hdfs dfs -put /home/hdfs/test0219-2.txt /testhdfs/
④AppendToFile:将一个本地文件的内容追加到一个HDFS文件末尾
hdfs dfs -appendToFile /home/hdfs/test0219-2.txt /testhdfs/test0219-1.txt
①copyToLocal:将HDFS上文件复制到本地目录上
hdfs dfs -copyToLocal /testhdfs/test0219.txt /home/hdfs/
②get:等同于copyToLocal,生产环境更习惯用get
hdfs dfs -get /testhdfs/test0219.txt /home/hdfs/
启用HA后重启namenode遇到报错:
resource_management.core.exceptions.ExecutionFailed: Execution of 'ambari-sudo.sh su hdfs -l -s /bin/bash -c 'ulimit -c unlimited ; /usr/hdp/3.1.5.0-152/hadoop/bin/hdfs --config /usr/hdp/3.1.5.0-152/hadoop/conf --daemon start namenode'' returned 1. namenode is running as process 15506. Stop it first.
查看日志/var/log/hadoop/hdfs/hadoop-hdfs-namenode-hdp01.log,确认为journalnode问题
查看journalnode日志,发现目录没有格式化
查看/data01/hadoop/hdfs/journal/下文件,发现为空,需要重新格式化
hdfs namenode -initializeSharedEdits
再次启动namenode后仍报错
再次查看日志/var/log/hadoop/hdfs/hadoop-hdfs-namenode-hdp01.log,
说明namenode元数据发生损坏,需要恢复元数据后,才能启动namenode。恢复过程中,遇到提示有错误的时候,按c继续恢复即可
/usr/hdp/3.1.5.0-152/hadoop/bin/
hadoop namenode -recover
ambari-server setup --jdbc-db=mysql --jdbc-driver=/opt/mysql-connector-java-5.1.49.jar
然后在ambari界面点击“TEST CONNECTION”进行测试
在CONFIGS->RANGER PLUGIN中,启用对应服务的Plugin
启用Plugin后,对应的服务(hdfs)重启
在OpenLDAP上新建测试账号tenant2
ldapadd -x -w "lnyd@LNsy115" -D "cn=admin,dc=hdp315,dc=com" -f /root/template.ldif
在ambari上重启UserSync服务,以重新同步(正常会周期性同步,为了即时同步,可重启该服务)
登录Ranger界面,http://192.168.111.201:6080,查看用户,确认OpenLDAP账号已经同步至Ranger上
以tenant1和tenant2为测试对象,设置tenant1在hdfs上的目录为/testhdfs/tenant1,tenant2在hdfs上的目录为/testhdfs/tenant2,在Ranger上未设置任何权限策略的情况下
先以hdfs管理员账号nn/[email protected]登录kdc,然后创建对应的目录和赋权
kinit -kt /etc/security/keytabs/nn.service.keytab nn/[email protected]
hdfs dfs -mkdir -p /testhdfs/tenant1
hdfs dfs -mkdir -p /testhdfs/tenant2
hdfs dfs -chmod 777 /testhdfs/tenant1
hdfs dfs -chmod 777 /testhdfs/tenant2
hdfs dfs -ls /testhdfs
LDAP上已经有tenant1和tenant2两个账号,还需要在kerberos中同步建立好对应的账号
kadmin.local
addprinc -randkey tenant1
addprinc -randkey tenant2
ktadd -kt /root/keytab/tenant1.keytab tenant1
ktadd -kt /root/keytab/tenant2.keytab tenant2
分别以tenant1和tenant2账号登录kdc后,上传测试文件到自身的目录下
kinit -kt /root/keytab/tenant1.keytab tenant1
hdfs dfs -put /root/file1 /testhdfs/tenant1/
hdfs dfs -ls /testhdfs/tenant1
kdestroy
kinit -kt /root/keytab/tenant2.keytab tenant2
hdfs dfs -put /root/file2 /testhdfs/tenant2/
hdfs dfs -ls /testhdfs/tenant2
分别用两个账号查看对方目录下的文件内容,目前是以hdfs自身的权限控制为准,即777权限,因此可以查看
kinit -kt /root/keytab/tenant1.keytab tenant1
hdfs dfs -cat /testhdfs/tenant2/file2
kdestroy
kinit -kt /root/keytab/tenant2.keytab tenant2
hdfs dfs -cat /testhdfs/tenant1/file1
在Ranger上添加策略
设置完成后,再次分别以tenant1和tenant2登录并查看权限情况
kinit -kt /root/keytab/tenant1.keytab tenant1
hdfs dfs -cat /testhdfs/tenant1/file1
hdfs dfs -cat /testhdfs/tenant2/file2
kinit -kt /root/keytab/tenant2.keytab tenant2
hdfs dfs -cat /testhdfs/tenant1/file1
hdfs dfs -cat /testhdfs/tenant2/file2
说明权限已经生效,账号仅能查看自身目录下的文件,其他目录权限已被Ranger锁死
Ranger为HDFS提供联合授权模型:
配置完成后,重启hdfs服务
禁用Ranger上的tenant1策略,然后用tenant1和tenant2账号分别查看,因为/testhdfs/tenant1上没有策略,虽然hdfs权限为777,但因为非联合授权,因此仍被Ranger拒绝访问;而/testhdfs/tenant2在Ranger有策略控制,因此tenant2仍有权限查看
配置审计日志在hdfs上存储,路径为hdfs://hdp315/ranger/audit,将所有租户的执行行为进行记录
kinit -kt /etc/security/keytabs/nn.service.keytab nn/[email protected]
hdfs dfs -cat /ranger/audit/hdfs/20230303/hdfs_ranger_audit_hdp02.hdp.com.1.log
Ambari上启用Ranger HA,需要提前准备好负载均衡器,ambari上只是在另外一台服务器上配置好Ranger,实现两台的高可用关系,但前面的负载不是Ambari来负责管理。前面的负载采用KeepAlived+HAProxy实现。
复用在OpenLDAP中的KeepAlived+HAProxy
在hdp04和hdp05上修改配置文件中的部分内容,/etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend tcp_389_openldap
bind *:389
mode tcp
stats uri /haproxy?stats
default_backend tcp_389_openldap
frontend http_6080_ranger
bind *:6080
http-request set-header X-Forwarded-Proto http
stats uri /haproxy?stats
default_backend http_6080_ranger
#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend tcp_389_openldap
mode tcp
balance roundrobin
server server1 hdp01.hdp.com:389 check weight 100
server server2 hdp02.hdp.com:389 check weight 1
backend http_6080_ranger
mode http
balance roundrobin
cookie LB insert
server server1 hdp01.hdp.com:6080 maxconn 200 weight 10 cookie 1 check inter 5000 rise 3 fall 3
server server2 hdp02.hdp.com:6080 maxconn 200 weight 10 cookie 2 check inter 5000 rise 3 fall 3
启动服务
systemctl restart haproxy
systemctl status haproyx
使用Yarn提交MapReduce任务的时候,中间结果会保存在HDFS,/user/username/,如果/user目录下用户目录下不存在,则被创建,当MR执行结束之后,中间结果会被删除,目录保留。因此需要在Ranger中对/user的权限做策略。
YARN的部分存储路径调整:
Node Manager
YARN NodeManager Local directories:/data01/hadoop/yarn/local
YARN NodeManager Log directories:/data01/hadoop/yarn/log
Application Timeline Server
yarn.timeline-service.leveldb-state-store.path:/data01/hadoop/yarn/timeline
yarn.timeline-service.leveldb-timeline-store.path:/data01/hadoop/yarn/timeline
Advanced yarn-hbase-env
is_hbase_system_service_launch:true
use_external_hbase:false
YARN可使用内置的HBase数据库,也可以使用外部;使用内置时,需要is_hbase_system_service_launch设置为true
Advanced ranger-yarn-security
Add YARN Authorization:取消勾选
该选项是禁用YARN本身的ACL权限控制,YARN队列的权限控制由RANGER统一管理
注:需要先对NameNode页面的认证取消了,否则ResourceManager修改后也不生效
MAPREDUCE2的部分存储路径调整:
Advanced mapred-site
mapreduce.jobhistory.recovery.store.leveldb.path:/data01/hadoop/mapreduce/jhs
Custom mapred-site
mapred.local.dir:/data01/hadoop/mapred
在ACTIONS->Enable ResourceManager HA中配置
启用HA后,会在/etc/hadoop/conf/yarn-site.xml中出现如下关于HA的配置项
指定zk下对应的文件目录为/yarn-leader-election,对应的rm节点为hdp01.hdp.com和hdp02.hdp.com
在zookeeper中查看也同样生成了对应的文件目录
①CPU资源调度
目前的CPU被划分为虚拟CPU,这里的虚拟CPU是yarn自己引入的概念,因为每个服务器的CPU计算能力不一样,有的机器可能是其他机器计算能力的两倍,然后可以通过多配置几个虚拟CPU弥补差异。在yarn中,CPU的相关配置如下:
yarn.nodemanager.resource.cpu-vcores
表示该节点上YARN可使用的虚拟CPU个数,默认是8,注意,目前推荐将该值设置为与物理CPU核数数目相同。如果节点CPU核数不够8个,则需要调减小这个值,而YARN不会智能的探测节点的物理CPU总数。
yarn.scheduler.minimum-allocation-vcores
单个任务可申请的最小虚拟CPU个数,默认是1,如果一个任务申请的CPU个数少于该数,则该对应的值改为这个数。
yarn.scheduler.maximum-allocation-vcores
单个任务可申请的最多虚拟CPU个数,默认是4。这里说的cpu个数都是说的虚拟cpu,默认的是1个物理cpu=2个虚拟cpu。
②Memory资源调度
yarn一般允许用户配置每个节点上可用的物理资源,注意,这里是"可用的",不是物理内存多少,就设置多少,因为一个服务器节点上会有若干的内存,一部分给yarn,一部分给hdfs,一部分给hbase。在yarn中,Memory相关的配置如下:
yarn.nodemanager.resource.memory-mb
设置该节点上yarn可使用的内存,默认为8G,如果节点内存资源不足8G,要减少这个值,yarn不会智能的去检测内存资源,一般这个设置yarn的可用内存资源
yarn.scheduler.minimum-allocation-mb
单个任务可申请的最小的内存大小,默认是1G,当内存不够时,会自动按照一定大小累加内存。
yarn.scheduler.maximum-allocation-mb
单个任务最大申请物理内存量,默认为8291MB
③示例
以hdp03-05(8C、8G)为例,
yarn.nodemanager.resource.cpu-vcores 虚拟core
这个参数根据自己生产服务器决定,比如服务器很富裕,那就直接1:1,设置成8,如果服务器不是很富裕,那就直接成1:2,设置成8,本次设置为16
yarn.nodemanager.resource.memory-mb 总内存
生产上一般要预留15-20%的内存,那么可用内存就是8*0.8=6.4G,本次设置为6G
yarn.scheduler.minimum-allocation-mb 单任务最小内存
如果设置成500M,那6/0.5 = 12,就是最多可以跑12个container
如果设置成1G,那6/1 = 6,就是最多可以跑6个container
本次设置为1G
yarn.scheduler.minimum-allocation-vcores 单任务最少vcore
如果设置vcore = 1,那么16/1 = 16,就是最多可以跑16个container,如果设置成这个,根据上面内存分配的情况,最多只能跑6个container,vcore有点浪费
如果设置vcore = 2,那么16/2 = 8,就是最多可以跑8个container
yarn.scheduler.maximum-allocation-vcores 单任务最多vcore
一般就设置成4个,cloudera公司做过性能测试,如果cpu大于等于5之后,cpu利用率反而不是很好(固定经验值)
yarn.scheduler.maximum-allocation-mb 单任务最大内存
这个要根据实际业务设定,如果有大任务
跳转至YARN Queue Manager页面,针对之前的租户tenant1和tenant2,新建资源队列,注意所有队列总和要为100%,否则会报错
租户与队列资源关系绑定
[u | g] [username : groupname] [yarn队列的名字]
本次绑定为
u:tenant1:tenant1,u:tenant2:tenant2
保存本次操作内容
查看resourcemanager页面,可以看到已经更新出新的资源队列
可使用官方提供的测试jar包
https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-mapreduce-examples
在OpenLDAP中创建账号ranger_yarn,重启UserSync服务后将账号同步至Ranger中,然后在kerberos中创建同样的账号(注:该测试jar包只能用账号ranger_yarn,队列offline)
kadmin.local
addprinc -randkey ranger_yarn
ktadd -kt /root/keytab/ranger_yarn.keytab ranger_yarn
在Yarn中创建队列及账号与队列的映射关系
队列offline、账号ranger_yarn都准备好后,在Ranger上创建授权关系
① 计算圆周率
使用ranger_yarn登录,运行计算圆周率任务
kinit -kt /root/keytab/ranger_yarn.keytab ranger_yarn
hadoop jar /root/hadoop-mapreduce-examples-3.1.1.3.0.1.4-1.jar pi -Dmapred.job.queue.name=offline 10 50
hadoop jar是hadoop运行jar包命令
第一个参数pi:表示MapReduce程序执行圆周率计算
第二个参数:用于指定map阶段运行的任务次数,并发度,这是是10
第三个参数:用于指定每个map任务取样的个数,这里是50
在Yarn中可查看Application的信息
此时在运行jar包时指定队列为tenant1,执行报错,说明权限
② 单词词频统计
首先创建要统计词频的文件,并上传到hdfs上,提前做好对ranger_yarn的hdfs授权
kinit -kt /etc/security/keytabs/nn.service.keytab nn/[email protected]
hdfs dfs -mkdir /testhdfs/ranger_yarn
kinit -kt /root/keytab/ranger_yarn.keytab ranger_yarn
hdfs dfs -put /root/wordcount_input /testhdfs/ranger_yarn
hdfs dfs -ls /testhdfs/ranger_yarn
运行词频统计jar包
kinit -kt /root/keytab/ranger_yarn.keytab ranger_yarn
hadoop jar /root/hadoop-mapreduce-examples-3.1.1.3.0.1.4-1.jar wordcount -Dmapred.job.queue.name=offline /testhdfs/ranger_yarn/wordcount_input /testhdfs/ranger_yarn/wordcount_output
第一个参数:wordcount表示执行单词统计
第二个参数:指定输入文件的路径
第三个参数:指定输出结果的路径(该路径不能已存在)
统计完成会在输出目录生成结果
hdfs dfs -cat /testhdfs/ranger_yarn/wordcount_output/part-r-00000
(1)查看命令
yarn application -list
yarn application -list -appStates <ALL,NEW,NEW_SAVING,SUBMITTED,ACCEPTED,RUNNING,FINISHED,FAILED,KILLED>
(2)Kill命令
根据id杀死任务
yarn application -kill <application_id>
(3)查看日志
查询Application日志
yarn logs -applicationId <ApplicationId>
查询Container日志
yarn logs -applicationId -containerId <ApplicationId> -containerId <ContainerId>
(4)查看尝试运行的任务
查看尝试运行的任务
yarn applicationattempt -list<ApplicationId>
查看尝试运行任务的状态
yarn applicationattempt -status <ApplicationAttemptId>
(5)查看容器
列出所有Container
yarn container -list <ApplicationAttemptId>
打印Container状态
yarn container -status <ContainerId>
启动时报错:
java.util.concurrent.ExecutionException: org.apache.zookeeper.KeeperException$NoNodeException: KeeperErrorCode = NoNode for /atsv2-hbase-secure/hbaseid
在Yarn中的CONFIGS->ADVANCED->Advanced yarn-hbase-env中,将is_hbase_system_service_launch启用
启动时无报错,等几分钟后报错并停止,在hdp02上查看日志,/var/log/hadoop-mapreduce/mapred/hadoop-mapred-historyserver-hdp02.log
报错信息为:
Error creating intermediate done directory: [hdfs://hdp315:8020/mr-history/tmp]
Permission denied: user=mapred, access=WRITE, inode="/mr-history"
查看hdfs上的目录权限,确认权限归属无问题
原因是Ranger上取消了联合授权功能,在Ranger上没有对应的策略开放该目录,导致mapred用户无法访问对应的目录,开启联合授权功能后恢复。
告警信息:ATS embedded HBase is NOT running on hdp01.hdp.com
resourceMananger的JVM内存是1G,内存太小导致的,将ResourceManager中的Java heap size的JVM内存增加到了2048MB
重启Yarn服务后告警消失
主要可能的原因是分配给容器的内存过小导致,正常情况下需要适当调整分配内存,本次是因为总体内存量不大,而在分配queue:offline的时候,设置的资源大小为5%,导致无法正常运行,而是一直停留在分配资源阶段,重新分配队列资源大小后恢复。
利用ambari创建的MySQL作为MetaStore,创建用户hive及数据库hive
mysql -uroot -p
CREATE DATABASE hive;
CREATE USER 'hive'@'%' IDENTIFIED BY 'lnyd@LNsy115';
GRANT ALL ON hive.* TO 'hive'@'%';
FLUSH PRIVILEGES;
在服务中添加Hive
安装hive时需要同步安装Tez
DATABASE
Hive Database:Existing MySQL / MariaDB
安装完成后,需要按照提示将hdfs、yarn等服务进行重启。
Ambari安装后,Hive使用了Tez作为计算引擎,也可以修改为MR或Spark,在配置文件中调整,/usr/hdp/3.1.5.0-152/hive/conf/hive-site.xml
<property>
<name>hive.execution.engine</name>
<value>tez</value>
</property>
ACTIONS->Add Hive Metastore
重启相关服务后完成HA启用。
ACTIONS->Add HiveServer2
重启HIVE和Tez服务后完成HA启用。
在Ranger上新建策略完成对租户的授权
权限策略可以精细到列
类似于mysql的命令行工具,但是只能操作本地的Hive服务,无法通过JDBC连接远程服务,且sql执行结果没有格式化,看起来不是很直观。
先用keytab登录,使用hive客户端进入
kinit -kt /etc/security/keytabs/hive.service.keytab hive/[email protected]
hive
可以设置一些基本参数,让hive使用起来更便捷:
让提示符显示当前库
set hive.cli.print.current.db=true;
显示查询结果时显示字段名称
set hive.cli.print.header=true;
设置只对当前会话有效,重启hive会话后就失效。
创建测试数据库test_hive_db
create database test_hive_db;
desc database test_hive_db;
从输出结果看,测试数据库test_hive_db存储在hdfs上,位置为hdfs://hdp315/warehouse/tablespace/managed/hive/test_hive_db.db
HiveServer2支持一个新的命令行Shell,称为:Beeline,后续将会使用Beeline替代Hive CLI。Beeline基于SQLLine CLI的JDBC客户端。Hive CLI和Beeline都属于命令行操作模式,主要区别是Hive CLI只能操作本地的Hive服务,而Beeline可以通过JDBC连接远程服务。
开启了kerberos认证的hadoop集群,hive默认使用kerberos认证。先以hive/[email protected]身份登录,创建数据库hive_db_tenant1和tenant2、表hive_table_tenant1和hive_table_tenant2,在ranger上分别将两个租户赋权到对应的数据库上,然后以tenant1身份连接,分别尝试连接两个数据库,看是否有权限访问
kinit -kt /etc/security/keytabs/hive.service.keytab hive/[email protected]
beeline -u 'jdbc:hive2://hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=hiveserver2;principal=hive/[email protected]'
create database hive_db_tenant1;
create database hive_db_tenant2;
create table hive_db_tenant1.hive_table_tenant1 (id int,name string,address string,phone string);
create table hive_db_tenant2.hive_table_tenant2 (id int,name string,address string,phone string);
kdestroy
kinit -kt /root/keytab/tenant1.keytab tenant1
beeline -u 'jdbc:hive2://hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=hiveserver2;principal=hive/[email protected]'
describe hive_db_tenant1.hive_table_tenant1;
describe hive_db_tenant2.hive_table_tenant2;
从结果看,无法访问hive_table_tenant2的表。
生成6GB大小的文件
#!/bin/bash
cat /dev/null > /root/bigFile.txt
for((i=1;i<=100000000;i++));
do
echo "$i,testname$i,testaddress$i,testphonenumber$i" >> /root/bigFile.txt;
done
本次测试使用tenant1
kinit -kt /root/keytab/tenant1.keytab tenant1
hdfs dfs -put /root/bigFile.txt /testhdfs/tenant1
beeline -u 'jdbc:hive2://hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=hiveserver2;principal=hive/[email protected]'
set tez.queue.name=tenant1;
① 导入测试
测试一次性导入和切分导入的性能
新建表,用于一次性导入
CREATE TABLE `test_tenant1_one`(
`id` int,
`name` string,
`address` string,
`phone` string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE
LOCATION 'hdfs://hdp315/testhdfs/tenant1/test_tenant1_one.db';
执行导入
LOAD DATA INPATH 'hdfs://hdp315/testhdfs/tenant1/bigFile.txt' INTO TABLE hive_db_tenant1.test_tenant1_one;
新建表,用于分桶导入,分桶的实质就是对分桶的字段做了hash,然后存放到对应文件中,所以说如果原有数据没有按key hash,需要在插入分桶的时候hash,也就是说向分桶表中插入数据的时候必然要执行一次MAPREDUCE,这也就是分桶表的数据基本只能通过从结果集查询插入的方式进行导入
CREATE TABLE `test_tenant1_bucket`(
`id` int,
`name` string,
`address` string,
`phone` string
)
CLUSTERED BY(id) INTO 16 buckets
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE
LOCATION 'hdfs://hdp315/testhdfs/tenant1/test_tenant1_bucket.db';
执行导入
INSERT OVERWRITE TABLE test_tenant1_bucket SELECT * FROM test_tenant1_one;
此时,分桶后的文件会分成16个分片
② 查询测试
对测试的数据库进行查询操作
SELECT SUM(id) FROM hive_db_tenant1.test_tenant1_bucket;
Select查询不报错,但count、insert、load等操作需要调用tez引擎时会报错
报错信息:
ERROR : Job Submission failed with exception 'java.io.IOException(org.apache.hadoop.yarn.exceptions.YarnException: org.apache.hadoop.security.AccessControlException: User hive does not have permission to submit application_1678378182198_0002 to queue default
默认调用的是default队列,需要手工指定使用的队列
mr指定队列:
set mapreduce.job.queuename=tenant1;
tez指定队列:
set tez.queue.name=tenant1;
在HBase中新建策略
先以hbase/[email protected]身份分别创建两个表空间及表
kinit -kt /etc/security/keytabs/hbase.service.keytab hbase/[email protected]
hbase shell
create_namespace 'tenant1'
create_namespace 'tenant2'
create 'tenant1:hbase_table1',{NAME=>'baseinfo',VERSIONS=>5},{NAME=>'extrainfo',VERSIONS=>3}
create 'tenant2:hbase_table2',{NAME=>'baseinfo',VERSIONS=>5},{NAME=>'extrainfo',VERSIONS=>3}
然后以tenant1身份登录,分别测试对两张表的权限
kinit -kt /root/keytab/tenant1.keytab tenant1
hbase shell
describe 'tenant1:hbase_table1'
describe 'tenant2:hbase_table2'
首先通过Kerberos认证,然后用hbase shell连接
kinit -kt /etc/security/keytabs/hbase.service.keytab hbase/[email protected]
hbase shell
注:向左删除是ctrl+backspace、向右删除是backspace。
hbase默认有两个表空间,它们是default和hbase
列出所有表空间
list_namespace
查看表空间下有的表
list_namespace_tables 'default'
创建表空间,禁止创建表到hbase表空间下
create_namespace 'tenant1'
查看表空间信息
describe_namespace 'tenant1'
删除表空间
drop_namespace 'tenant1'
创建表
该表中有两个列族baseinfo和extrainfo,baseinfo族中存储的每个值的最近时间版本数量为5,族参数必须大写,如NAME和VERSION
create 'tenant1:hbase_table1',{NAME=>'baseinfo',VERSIONS=>5},{NAME=>'extrainfo',VERSIONS=>3}
显示表结构
describe 'tenant1:hbase_table1'
修改表
会先判断有没有,有就修改,没有就增加,修改是alter
修改列族baseinfo,将主版本数量改为3
alter 'tenant1:hbase_table1',{NAME=>'baseinfo',VERSIONS=>3}
增加列族base
alter 'tenant1:hbase_table1',{NAME=>'base',VERSIONS=>5}
删除列族baseinfo,低版本的HBase需要先disable而不是alter,再delete
alter 'tenant1:hbase_table1',{NAME=>'baseinfo',METHOD=>'delete'}
判断表是否存在
exists 'tenant1:hbase_table1'
删除表
在删除表之前必须先disable禁用表然后再执行drop操作删除它
disable ‘tenant1:hbase_table1’
drop ‘tenant1:hbase_table1’
增加键值对到表中
向tenant1表空间的hbase_table1表中的base列族中添加name列,添加值为ligang
修改和增加都是put,存在时是修改,不存在时是增加
put 'tenant1:hbase_table1','001','base:name','liujingyu'
put 'tenant1:hbase_table1','001','base:age',37
查询001行的所有键值对
get 'tenant1:hbase_table1','001','base:name'
删除表中的键值对
delete 'tenant1:hbase_table1','001','base:name'
删除表中的所有数据并重置表的结构,实际上truncate属于DDL操作
truncate 'tenant1:hbase_table1'
扫描表中的所有行的数据
scan 'tenant1:hbase_table1'
统计表中的行记录数
count 'tenant1:hbase_table1'
添加Spark2服务
需要重启HDFS、YARN、MapReduce2、Hive、HBase等相关服务
在CONFIGS->Advanced spark2-env下的content里,将下面内容加#注释掉
export SPARK_HISTORY_OPTS='-Dspark.ui.filters=org.apache.hadoop.security.authentication.server.AuthenticationFilter -Dspark.org.apache.hadoop.security.authentication.server.AuthenticationFilter.params="type=kerberos,kerberos.principal={{spnego_principal}},kerberos.keytab={{spnego_keytab}}"'
访问页面,http://hdp01.hdp.com:18081/
查看/usr/hdp/3.1.5.0-152/spark2/conf/spark-env.sh
export HADOOP_HOME=${HADOOP_HOME:-/usr/hdp/3.1.5.0-152/hadoop}
export HADOOP_CONF_DIR=${HADOOP_CONF_DIR:-/usr/hdp/3.1.5.0-152/hadoop/conf}
/usr/hdp/3.1.5.0-152/hadoop-yarn/conf/yarn-site.xml
每个Spark应用程序都需要一个Spark环境,这是Spark RDD API的主要入口点。Spark Shell提供了一个名为“sc”的预配置Spark环境和一个名为“spark”的预配置Spark会话。使用Spark Shell的时候,本身是预配置了sc,即SparkConf和SparkContext的,但是在实际使用编辑器编程过程中是需要设置这些配置的。
启动spark-shell
spark-shell --master local
正确界面如下:
(2)加载本地文件
通过预置sc加载本地文件
val textFile = sc.textFile("file:///root/wordcount_input")
val后面的是变量textFile,而sc.textFile()中的这个textFile是sc的一个方法名称,这个方法用来加载文件数据。这两个textFile不是一个东西,不要混淆。实际上,val后面的是变量textFile。
要加载本地文件,必须采用“file:///”开头的这种格式。执行上上面这条命令以后,并不会马上显示结果,因为,Spark采用惰性机制,只有遇到“行动”类型的操作,才会从头到尾执行所有操作。
textFile.first()
first()是一个“行动”(Action)类型的操作,会启动真正的计算过程,从文件中加载数据到变量textFile中,并取出第一行文本。屏幕上会显示很多反馈信息,这里不再给出,你可以从这些结果信息中,找到word.txt文件中的第一行的内容。
正因为Spark采用了惰性机制,在执行转换操作的时候,即使我们输入了错误的语句,spark-shell也不会马上报错,而是等到执行“行动”类型的语句时启动真正的计算,那个时候“转换”操作语句中的错误就会显示出来。
将变量中的内容写回到本地文件/root/output中
val textFile = sc.textFile("file:///root/wordcount_input")
textFile.saveAsTextFile("file:///root/output")
上面的saveAsTextFile()括号里面的参数是保存文件的路径,不是文件名。saveAsTextFile()是一个“行动”(Action)类型的操作,所以,马上会执行真正的计算过程,从wordcount_input中加载数据到变量textFile中,然后,又把textFile中的数据写回到本地文件目录“/root/output”下面
ll /root/output/
cat /root/output/part-00000
val textFile = sc.textFile("hdfs://hdp315/testhdfs/tenant1/wordcount_input")
textFile.first()
待统计文件为/root/wordcount_input
spark-shell --master local
val textFile = sc.textFile("file:///root/wordcount_input")
val wordCount = textFile.flatMap(line => line.split(" ")).map(word => (word, 1)).ruduceByKey((a,b) => a + b)
wordCount.collect()
textFile包含了多行文本内容,textFile.flatMap(line => line.split(" “))会遍历textFile中的每行文本内容,当遍历到其中一行文本内容时,会把文本内容赋值给变量line,并执行Lamda表达式line => line.split(” “)。line => line.split(” “)是一个Lamda表达式,左边表示输入参数,右边表示函数里面执行的处理逻辑,这里执行line.split(” "),也就是针对line中的一行文本内容,采用空格作为分隔符进行单词切分,从一行文本切分得到很多个单词构成的单词集合。这样,对于textFile中的每行文本,都会使用Lamda表达式得到一个单词集合,最终,多行文本,就得到多个单词集合。textFile.flatMap()操作就把这多个单词集合“拍扁”得到一个大的单词集合。
然后,针对这个大的单词集合,执行map()操作,也就是map(word => (word, 1)),这个map操作会遍历这个集合中的每个单词,当遍历到其中一个单词时,就把当前这个单词赋值给变量word,并执行Lamda表达式word => (word, 1),这个Lamda表达式的含义是,word作为函数的输入参数,然后,执行函数处理逻辑,这里会执行(word, 1),也就是针对输入的word,构建得到一个tuple,形式为(word,1),key是word,value是1(表示该单词出现1次)。
程序执行到这里,已经得到一个RDD,这个RDD的每个元素是(key,value)形式的tuple。最后,针对这个RDD,执行reduceByKey((a, b) => a + b)操作,这个操作会把所有RDD元素按照key进行分组,然后使用给定的函数(这里就是Lamda表达式:(a, b) => a + b),对具有相同的key的多个value进行reduce操作,返回reduce后的(key,value),比如(“hadoop”,1)和(“hadoop”,1),具有相同的key,进行reduce以后就得到(“hadoop”,2),这样就计算得到了这个单词的词频。
建议找一台有外网的服务器来做sbt,因为需要下载很多依赖包
安装sbt
tar -zxvf /opt/sbt-1.8.2.tgz -C /usr/local/
将位于sbt/bin下面的sbt-launch.jar文件放在sbt目录下。
cp /usr/local/sbt/bin/sbt-launch.jar /usr/local/sbt/
在sbt目录下创建sbt脚本
chmod u+x /usr/local/sbt/sbt
确认是否成功
/usr/local/sbt/sbt sbtVersion
创建工程目录及相关文件
scala文件,/data01/project/wordcount/src/main/scala/wordcount.scala
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf
object WordCount {
def main(args: Array[String]) {
val inputFile = "hdfs://hdp315/testhdfs/ranger_yarn/wordcount_input"
val conf = new SparkConf().setAppName("WordCount")
val sc = new SparkContext(conf)
val textFile = sc.textFile(inputFile)
val wordCount = textFile.flatMap(line => line.split(" ")).map(word => (word, 1)).reduceByKey((a, b) => a + b)
wordCount.foreach(println)
}
}
sbt文件,/data01/project/wordcount/wordcount.sbt
name := "WordCount Project"
version := "1.0"
scalaVersion := "2.11.12"
libraryDependencies += "org.apache.spark" %% "spark-core" % "2.3.0"
进入到工程目录下,将整个工程打成jar包
/usr/local/sbt/sbt package
jar包在工程目录下的./target/scala-2.11/下
回到hdp01上,通过spark-submit提交jar包执行
kinit -kt /root/keytab/ranger_yarn.keytab ranger_yarn
spark-submit --class "WordCount" /root/wordcount-project_2.11-1.0.jar --deploy-mode cluster --master yarn
将nc作为服务器端,用户产生数据;启动sparkstreaming客户端程序,监听服务器端发送过来的数据,并对其数据进行显示。
在测试的nc服务端,启动nc程序,端口为1234
nc -l 1234
配置sbt文件,增加sparking-streaming依赖包,/data01/project/streamPrint/streamPrint.sbt
name := "streamPrint Project"
version := "1.0"
scalaVersion := "2.11.12"
libraryDependencies ++= Seq(
"org.apache.spark" %% "spark-core" % "2.3.0",
"org.apache.spark" %% "spark-streaming" % "2.3.0"
)
配置scala文件,/data01/project/streamPrint/src/main/scala/streamPrint.scala
import org.apache.spark._
import org.apache.spark.streaming._
import org.apache.spark.storage.StorageLevel
object StreamPrint {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("streamPrint")
val sc = new StreamingContext(conf, Seconds(5))
val lines = sc.socketTextStream("192.168.111.1", 1234, StorageLevel.MEMORY_AND_DISK)
if (lines != null) {
lines.print()
println("start!")
}
sc.start()
sc.awaitTermination()
}
}
进入到工程目录下,将整个工程打成jar包
/usr/local/sbt/sbt package
回到hdp01上,通过spark-submit提交jar包执行
kinit -kt /root/keytab/ranger_yarn.keytab ranger_yarn
spark-submit --class "StreamPrint" /root/streamprint-project_2.11-1.0.jar --deploy-mode cluster --master yarn
此时在nc服务端输入内容后,可在spark streaming中看到相应的内容
Spark streaming中的间隔,是在scala程序中设置的,val sc = new StreamingContext(conf, Seconds(5))因此是5秒输出一次。
–master
master的地址,提交任务到哪里执行
常见的选项有
local:提交到本地服务器执行,并分配单个线程
local[k]:提交到本地服务器执行,并分配k个线程
spark://HOST:PORT:提交到standalone模式部署的spark集群中,并指定主节点的IP与端口
mesos://HOST:PORT:提交到mesos模式部署的集群中,并指定主节点的IP与端口
yarn:提交到yarn模式部署的集群中
–deploy-mode
在本地(client)启动driver或在cluster上启动,默认是client
DEPLOY_MODE:设置driver启动的位置,可选项如下,默认为client
client:在客户端上启动driver,这样逻辑运算在client上执行,任务执行在cluster上
cluster:逻辑运算与任务执行均在cluster上,cluster模式暂时不支持于Mesos集群或Python应用程序
–class
应用程序的主类,仅针对java或scala应用
CLASS_NAME:指定应用程序的类入口,即主类,仅针对java、scala程序,不作用于python程序
–name
应用程序的名称
–jars
用逗号分隔的本地jar包,设置后,jar包将包含在driver和executor的classpath下
–packages
包含在driver和executor的classpath中的jar的maven坐标
–exclude-packages
为了避免冲突,指定的参数–package中不包含的jars包
–repositories
远程repository
附加的远程资源库(包含jars包)等,可以通过maven坐标进行搜索
–py-files
PY_FILES:逗号隔开的的.zip、.egg、.py文件,这些文件会放置在PYTHONPATH下,该参数仅针对python应用程序
–files
FILES:逗号隔开的文件列表,这些文件将存放于每一个工作节点进程目录下
–conf PROP=VALUE
指定spark配置属性的值,格式为PROP=VALUE,例如–confspark.executor.extraJavaOptions=“-XX:MaxPermSize=256m”
–properties-file
指定需要额外加载的配置文件,用逗号分隔,如果不指定,默认为conf/spark-defaults.conf
–driver-memory
Driver内存,默认1G
–driver-java-options
传给driver的额外的Java选项
–driver-library-path
传给driver的额外的库路径
–driver-class-path
传给driver的额外的类路径,用–jars添加的jar包会自动包含在类路径里
–driver-cores
Driver的核数,默认是1。在yarn或者standalone下使用
–executor-memory
每个executor的内存,默认是1G
–total-executor-cores
所有executor总共的核数。仅仅在mesos或者standalone下使用
–num-executors
启动的executor数量。默认为2。在yarn下使用
–executor-core
每个executor的核数。在yarn或者standalone下使用
下载链接为
https://repo.huaweicloud.com/apache/flink/flink-1.9.3/flink-1.9.3-bin-scala_2.12.tgz
https://repo.maven.apache.org/maven2/org/apache/flink/flink-shaded-hadoop-2-uber/2.6.5-7.0/flink-shaded-hadoop-2-uber-2.6.5-7.0.jar
http://www.java2s.com/Code/JarDownload/javax.ws/javax.ws.rs-api-2.0.jar.zip
上传到hdp01上,并复制到/var/www/html下
mkdir /var/www/html/flink
cp /opt/flink-1.9.3-bin-scala_2.12.tgz /var/www/html/flink/
cp /opt/flink-shaded-hadoop-2-uber-2.6.5-7.0.jar /var/www/html/flink/
cp /opt/javax.ws.rs-api-2.0.jar /var/www/html/flink/
在外网服务器上
git clone https://ghproxy.com/https://gitee.com/liujingwen-git/ambari-flink-service-master.git /root/FLINK
注:github.com无法直接下载,可以使用gproxy.com进行代理加速,拼接形成加速URL;或者将github.com在本地强制解析为140.82.114.4
将FLINK文件夹上传到hdp01的/var/lib/ambari-server/resources/stacks/HDP/3.1/services/上,对应的版本可在集群的服务器中查看
hdp-select status hadoop-client | sed 's/hadoop-client - \([0-9]\.[0-9]\).*/\1/'
从github上下载的ambari-flink-service文件较多,有些可以删除以及修改
文件位置:
/var/lib/ambari-server/resources/stacks/HDP/3.1/services/FLINK/metainfo.xml
修改版本
<name>FLINK</name>
<displayName>Flink</displayName>
<comment>Apache Flink is a streaming dataflow engine that provides data distribution, communication, and fault tolerance for distributed computations over data streams.</comment>
<version>1.9.3</version>
删除FLINK_MASTER,仅保留安装FLINK_CLIENT,在Flink on YARN模式下,master与ResourceManager合设,无需单独安装
<components>
<component>
<name>FLINK_CLIENT</name>
<displayName>FlinkCLIENT</displayName>
<category>CLIENT</category>
<cardinality>1+</cardinality>
<commandScript>
<script>scripts/flink_client.py</script>
<scriptType>PYTHON</scriptType>
<timeout>10000</timeout>
</commandScript>
</component>
</components>
文件位置:
/var/lib/ambari-server/resources/stacks/HDP/3.1/services/FLINK/configuration/flink-ambari-config.xml
修改安装路径
<property>
<name>flink_install_dir</name>
<value>/usr/hdp/3.1.5.0-152/flink</value>
<description>Location to install Flink</description>
</property>
修改安装包下载地址
<property>
<name>flink_download_url</name>
<value>http://hdp01.hdp.com/flink/flink-1.9.3-bin-scala_2.12.tgz</value>
<description>Snapshot download location. Downloaded when setup_prebuilt is true</description>
</property>
<property>
<name>flink_hadoop_shaded_jar</name>
<value>http://hdp01.hdp.com/flink/flink-shaded-hadoop-2-uber-2.6.5-7.0.jar</value>
<description>Flink shaded hadoop jar download location. Downloaded when setup_prebuilt is true</description>
</property>
</configuration>
文件位置:
/var/lib/ambari-server/resources/stacks/HDP/3.1/services/FLINK/configuration/flink-env.xml
修改JAVA环境变量
env.java.home: /usr/local/jdk1.8.0_351/jre/
文件位置:
/var/lib/ambari-server/resources/stacks/HDP/3.1/services/FLINK/package/scripts/flink_client.py
该文件的作用调用相关的环境变量,来实现整个安装过程
内容如下:
#!/usr/bin/env ptyhon
from resource_management import *
class FlinkClient(Script):
def install(self, env):
print 'Install the Flink Client'
import params
env.set_params(params)
self.configure(env)
# create flink log dir
Directory(params.flink_log_dir,
owner=params.flink_user,
group=params.flink_group,
create_parents=True,
mode=0775
)
Execute(format("rm -rf {flink_install_dir}/log/flink"))
Execute(format("ln -s {flink_log_dir} {flink_install_dir}/log/flink"))
def configure(self, env):
import params
env.set_params(params)
# write out flink-conf.yaml
properties_content = InlineTemplate(params.flink_yaml_content)
File(format(params.flink_install_dir + "/conf/flink-conf.yaml"), content=properties_content)
def status(self, env):
raise ClientComponentHasNoStatus()
if __name__ == "__main__":
FlinkClient().execute()
文件位置:
/var/lib/ambari-server/resources/stacks/HDP/3.1/services/FLINK/package/scripts/params.py
该文件定义了部分环境变量
内容如下:
#!/usr/bin/env python
from resource_management import *
from resource_management.libraries.script.script import Script
import sys, os, glob
from resource_management.libraries.functions.version import format_stack_version
from resource_management.libraries.functions.default import default
# server configurations
config = Script.get_config()
# params from flink-ambari-config
flink_install_dir = config['configurations']['flink-ambari-config']['flink_install_dir']
flink_numcontainers = config['configurations']['flink-ambari-config']['flink_numcontainers']
flink_numberoftaskslots= config['configurations']['flink-ambari-config']['flink_numberoftaskslots']
flink_jobmanager_memory = config['configurations']['flink-ambari-config']['flink_jobmanager_memory']
flink_container_memory = config['configurations']['flink-ambari-config']['flink_container_memory']
setup_prebuilt = config['configurations']['flink-ambari-config']['setup_prebuilt']
flink_appname = config['configurations']['flink-ambari-config']['flink_appname']
flink_queue = config['configurations']['flink-ambari-config']['flink_queue']
flink_streaming = config['configurations']['flink-ambari-config']['flink_streaming']
hadoop_conf_dir = config['configurations']['flink-ambari-config']['hadoop_conf_dir']
flink_download_url = config['configurations']['flink-ambari-config']['flink_download_url']
flink_hadoop_shaded_jar_url = config['configurations']['flink-ambari-config']['flink_hadoop_shaded_jar']
javax_ws_rs_api_jar = config['configurations']['flink-ambari-config']['javax_ws_rs_api_jar']
conf_dir=''
bin_dir=''
# params from flink-conf.yaml
flink_yaml_content = config['configurations']['flink-env']['content']
flink_user = config['configurations']['flink-env']['flink_user']
flink_group = config['configurations']['flink-env']['flink_group']
flink_log_dir = config['configurations']['flink-env']['flink_log_dir']
flink_log_file = os.path.join(flink_log_dir,'flink-setup.log')
temp_file='/tmp/flink.tgz'
添加用户和组
groupadd flink
useradd -d /home/flink -g flink flink
重启服务
ambari-server restart
在ambari中的Stack and Versions中可以看到flink的信息
添加flink服务
在Custom flink-env中新增
Key:yarn.client.failover-proxy-provider
Value:org.apache.hadoop.yarn.client.ConfiguredRMFailoverProxyProvider
实验:wordcount
从尚硅谷下载实验用的程序代码,在idea中对以socket形式接收数据流的代码进行修改,从192.168.111.1的nc处接收数据流,然后对词频统计后输出到本地文件中
public static void main(String[] args) throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
DataStream<String> stream = env.socketTextStream("192.168.111.1", 1234);
DataStream<Tuple2<String, Integer>> resultStream = stream.flatMap(new Tokenizer())
.keyBy(0)
.sum(1);
resultStream.print();
resultStream.writeAsText("/tmp/wc_result.txt");
env.execute("Flink Streaming Java API Skeleton");
}
在idea中的Build->Build Artifacts中选择build生成jar包,然后上传到hdp05上,使用一个租户进行kerberos认证后,提交flink任务。
cd /usr/hdp/3.1.5.0-152/flink/bin/
./flink run -m yarn-cluster /root/flink-tutorial-master.jar -c WordCountFromSocket
nc -l 1234
输入数据流后,中断nc进程,然后在hdp05上查看结果文件/tmp/wc_result.txt
在yarn上可以查看到对应的应用信息
报错信息:Error: Error: Unable to run the custom hook script [‘/usr/bin/python’, ‘/var/lib/ambari-agent/cache/stack-hooks/before-ANY/scripts/hook.py’, ‘ANY’, ‘/var/lib/ambari-agent/data/command-1360.json’, ‘/var/lib/ambari-agent/cache/stack-hooks/before-ANY’, ‘/var/lib/ambari-agent/data/structured-out-1360.json’, ‘INFO’, ‘/var/lib/ambari-agent/tmp’, ‘PROTOCOL_TLSv1_2’, ‘’]
2023-03-26 22:23:31,445 - The repository with version 3.1.5.0-152 for this command has been marked as resolved. It will be used to report the version of the component which was installed
通过ambari添加自定义服务时,总是不能自动增加service账号
python configs.py -u admin -p lnyd@LNsy115 -n HDP315 -l hdp01 -t 8080 -a get -c cluster-env | grep -i ignore_groupsusers_create
python configs.py -u admin -p lnyd@LNsy115 -n HDP315 -l hdp01 -t 8080 -a set -c cluster-env -k ignore_groupsusers_create -v true
安装后在启动时报错:resource_management.core.exceptions.Fail: Applying File[‘/usr/local/flink/conf/flink-conf.yaml’] failed, parent directory /usr/local/flink/conf doesn’t exist
不知什么原因没解压过去,需要手动解压到该目录
tar -zxvf /opt/flink-1.9.3-bin-scala_2.12.tgz -C /root/
mv /root/flink-1.9.3/* /usr/local/flink/
报错信息:
java.lang.NoClassDefFoundError: com/sun/jersey/core/util/FeaturesAndProperties
maven会自动下载相关的依赖jar包,因此需要将project下的jersey依赖jar包拷贝至flink的lib目录下
选择kafka进行安装
选择hdp03-05作为kafka的broker
Log directories:/data01/kafka-logs
下载链接:
https://gitcode.net/mirrors/yahoo/kafka-manager/-/archive/master/kafka-manager-master.tar.gz
在具有sbt编译环境的服务器(需要外网环境)上,对kafka-manager进行编译
unzip kafka-manager-2.0.0.2.zip -d /usr/local/
修改配置文件,/usr/local/kafka-manager-2.0.0.2/conf/application.conf
修改zookeeper连接地址
kafka-manager.zkhosts="hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181"
启动kafka-manager
cd /usr/local/kafka-manager-2.0.0.2/bin/
nohup bin/kafka-manager >/dev/null 2>&1 &
访问页面http://hdp01.hdp.com:9000
创建kafka集群
Cluster Name:hdp315,自定义名称
Cluster Zookeeper Hosts:hdp01.hdp.com:2181,hdp02.hdp02.com:2181,hdp03.hdp.com:2181
ZooKeeper账号信息
klist -ket /etc/security/keytabs/zk.service.keytab
jaas认证信息
/usr/hdp/3.1.5.0-152/zookeeper/conf/zookeeper_jaas.conf
/usr/hdp/3.1.5.0-152/zookeeper/conf/zookeeper_client_jaas.conf
com.sun.security.auth.module.Krb5LoginModule这个是类名,是kerberos对JAAS中的LoginModule的实现;required表示必须进行校验;其他几个是kerberos相关参数;后面会将该配置文件地址配置到jvm参数,其中的信息会被初始化到LoginContext上下文对象中。另外注意格式以及末尾的;分号。
开启sasl/kerberos认证,/usr/hdp/3.1.5.0-152/zookeeper/conf/zoo.cfg
Ambari自动创建的kafka账号信息如下
klist -kte /etc/security/keytabs/kafka.service.keytab
jaas认证信息,/usr/hdp/3.1.5.0-152/kafka/config/kafka_jaas.conf
KafkaServer:Kafka服务端
KafkaClient:Kafka客户端
Client:ZooKeeper客户端
Kerberos认证有两种方式,即利用票证缓存和指定keytab。
要使用存储在票证缓存中的Kerberos票证:
sasl.jaas.config=com.sun.security.auth.module.Krb5LoginModule required useTicketCache=true;
要使用keytab:
sasl.jaas.config=com.sun.security.auth.module.Krb5LoginModule required useKeyTab=true keyTab="/etc/security/keytabs/alice.keytab" principal="[email protected]";
开启sasl/kerberos认证,/usr/hdp/3.1.5.0-152/kafka/config/server.properties
核心配置如下
advertised.listeners=SASL_PLAINTEXT://hdp03.hdp.com:6667
listeners=SASL_PLAINTEXT://hdp03.hdp.com:6667
sasl.enabled.mechanisms=GSSAPI
sasl.kerberos.service.name=kafka
sasl.mechanism.inter.broker.protocol=GSSAPI
security.inter.broker.protocol=SASL_PLAINTEXT
jaas认证信息,/usr/hdp/3.1.5.0-152/kafka/config/kafka_client_jaas.conf
开启sasl/kerberos认证,新建文件/root/client.properties
security.protocol=SASL_PLAINTEXT
sasl.kerberos.service.name=kafka
sasl.mechanism=GSSAPI
Broker在启动时,会尝试去ZooKeeper中创建/controller节点。Kafka当前选举控制器的规则是:第一个成功创建/controller节点的Broker会被指定为控制器。同样,也会在zookeeper的/brokers/ids下创建一个临时znode。当broker宕机或主动关闭后,该broker与ZooKeeper的会话结束,这个znode会被自动删除。
zookeeper中还有一个与控制器有关的/controller_epoch持久节点,节点中存放的是一个整型的controller_epoch值(初始值为1)。controller_epoch用于记录控制器发生变更的次数,即记录当前的控制器是第几代控制器,也可以称为“控制器的纪元”。
在kafka集群中,首先启动hdp03节点,此时controller选举为hdp03,且broker下也只有hdp03,并且可以看到相关的详细信息。
ls /controller
get /controller
ls /brokers/ids
get /brokers/ids/1001
之后在kafka集群中增加节点hdp04,此时可以看到controller仍是hdp03,但broker下已经有hdp04节点。
ls /controller
get /controller
ls /brokers/ids
get /brokers/ids/1002
Kafka本身自带有一个授权的类kafka.security.auth.SimpleAclAuthorizer,可在server.properties配置,分为Acl和Ranger两种方式。
Acl控制
authorizer.class.name=kafka.security.auth.SimpleAclAuthorizer
Ranger控制
authorizer.class.name=org.apache.ranger.authorization.kafka.authorizer.RangerKafkaAuthorizer
当添加权限控制后,会在zk中创建2个节点
节点1:存储ACL信息节点kafka-acl
节点2:存储ACL变更信息节点kafka-acl-changes
内网发布地址用listeners,对外网发布地址时用advertised.listeners
创建topic
./kafka-topics.sh --create --zookeeper hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 --replication-factor 3 --partitions 3 --topic test-topic
partitions指定topic分区数,控制topic将分片成多少个log。可以显示指定,如果不指定则会使用broker(server.properties)中的num.partitions配置的数量
虽然增加分区数可以提供kafka集群的吞吐量、但是过多的分区数或者或是单台服务器上的分区数过多,会增加不可用及延迟的风险。因为多的分区数,意味着需要打开更多的文件句柄、增加点到点的延时、增加客户端的内存消耗;分区数也限制了consumer的并行度,即限制了并行consumer消息的线程数不能大于分区数;分区数也限制了producer发送消息是指定的分区。如创建topic时分区设置为1,producer发送消息时通过自定义的分区方法指定分区为2或以上的数都会出错的;这种情况可以通过alter –partitions 来增加分区数。
replication-factor指定topic每个分区的副本数,控制消息保存在几个broker(服务器)上,一般情况下等于broker的个数。如果没有在创建时显示指定或通过API向一个不存在的topic生产消息时会使用broker(server.properties)中的default.replication.factor配置的数量。
查看所有topic列表
./kafka-topics.sh --zookeeper hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 --list
查看指定topic信息
./kafka-topics.sh --zookeeper hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 --describe --topic test-topic
控制台向topic生产数据
./kafka-console-producer.sh --broker-list hdp03.hdp.com:6667,hdp04.hdp.com:6667,hdp05.hdp.com:6667 --topic test-topic
控制台消费topic的数据
./kafka-console-consumer.sh --zookeeper hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 --topic test-topic --from-beginning
增加topic分区数
./kafka-topics.sh --zookeeper hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 --alter --topic test-topic --partitions 10
删除topic
./kafka-topics.sh --delete --zookeeper hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 --topic test-topic
只会删除zookeeper中的元数据,消息文件须手动删除
在zookeeper中,以kafka登录后删除对应的文件
kinit -kt /etc/security/keytabs/kafka.service.keytab kafka/[email protected]
/usr/hdp/3.1.5.0-152/zookeeper/bin/zkCli.sh -server hdp01:2181,hdp02:2181,hdp03:2181
rmr /admin/delete_topics/test-topic
rmr /brokers/topics/test-topic
查看topic消费进度
./kafka-run-class.sh kafka.tools.ConsumerOffsetChecker --group group1
显示出consumer group的offset情况,必须参数为--group,不指定--topic,默认为所有topic
查看topic某分区偏移量最大(小)值
./kafka-run-class.sh kafka.tools.GetOffsetShell --topic test-topic --time -1 --broker-list hdp03.hdp.com:6667,hdp04.hdp.com:6667,hdp05.hdp.com:6667 --partitions 0
time为-1时表示最大值,time为-2时表示最小值
列出所有topic的用户组列表
./kafka-consumer-groups.sh --bootstrap-server hdp03.hdp.com:6667,hdp04.hdp.com:6667,hdp05.hdp.com:6667 --list
以hdp03作为producer,hdp04作为consumer
【hdp03】上执行启动生产者,并输入随机字符
kinit -kt /etc/security/keytabs/kafka.service.keytab kafka/[email protected]
./kafka-console-producer.sh --broker-list hdp03.hdp.com:6667,hdp04.hdp.com:6667,hdp05.hdp.com:6667 --topic test-topic --producer.config /root/producer.properties
【hdp04】上执行启动消费者,观察hdp03上输入字符后在hdp04上显示的内容
kinit -kt /etc/security/keytabs/kafka.service.keytab kafka/[email protected]
./kafka-console-producer.sh --broker-list
./kafka-console-consumer.sh --bootstrap-server hdp03.hdp.com:6667 --topic test-topic --consumer.config /root/consumer.properties
虽然增加分区数可以提供kafka集群的吞吐量、但是过多的分区数或者或是单台服务器上的分区数过多,会增加不可用及延迟的风险。因为多的分区数,意味着需要打开更多的文件句柄、增加点到点的延时、增加客户端的内存消耗;分区数也限制了consumer的并行度,即限制了并行consumer消息的线程数不能大于分区数;分区数也限制了producer发送消息是指定的分区。如创建topic时分区设置为1,producer发送消息时通过自定义的分区方法指定分区为2或以上的数都会出错的;这种情况可以通过alter –partitions 来增加分区数。
replication-factor指定topic每个分区的副本数,控制消息保存在几个broker(服务器)上,一般情况下等于broker的个数。如果没有在创建时显示指定或通过API向一个不存在的topic生产消息时会使用broker(server.properties)中的default.replication.factor配置的数量。
查看所有topic列表
./kafka-topics.sh --zookeeper hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 --list
查看指定topic信息
./kafka-topics.sh --zookeeper hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 --describe --topic test-topic
控制台向topic生产数据
./kafka-console-producer.sh --broker-list hdp03.hdp.com:6667,hdp04.hdp.com:6667,hdp05.hdp.com:6667 --topic test-topic
控制台消费topic的数据
./kafka-console-consumer.sh --zookeeper hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 --topic test-topic --from-beginning
增加topic分区数
./kafka-topics.sh --zookeeper hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 --alter --topic test-topic --partitions 10
删除topic
./kafka-topics.sh --delete --zookeeper hdp01.hdp.com:2181,hdp02.hdp.com:2181,hdp03.hdp.com:2181 --topic test-topic
只会删除zookeeper中的元数据,消息文件须手动删除
在zookeeper中,以kafka登录后删除对应的文件
kinit -kt /etc/security/keytabs/kafka.service.keytab kafka/[email protected]
/usr/hdp/3.1.5.0-152/zookeeper/bin/zkCli.sh -server hdp01:2181,hdp02:2181,hdp03:2181
rmr /admin/delete_topics/test-topic
rmr /brokers/topics/test-topic
查看topic消费进度
./kafka-run-class.sh kafka.tools.ConsumerOffsetChecker --group group1
显示出consumer group的offset情况,必须参数为–group,不指定–topic,默认为所有topic
查看topic某分区偏移量最大(小)值
./kafka-run-class.sh kafka.tools.GetOffsetShell --topic test-topic --time -1 --broker-list hdp03.hdp.com:6667,hdp04.hdp.com:6667,hdp05.hdp.com:6667 --partitions 0
time为-1时表示最大值,time为-2时表示最小值
列出所有topic的用户组列表
./kafka-consumer-groups.sh --bootstrap-server hdp03.hdp.com:6667,hdp04.hdp.com:6667,hdp05.hdp.com:6667 --list
7.实验:发布订阅
以hdp03作为producer,hdp04作为consumer
【hdp03】上执行启动生产者,并输入随机字符
kinit -kt /etc/security/keytabs/kafka.service.keytab kafka/[email protected]
./kafka-console-producer.sh --broker-list hdp03.hdp.com:6667,hdp04.hdp.com:6667,hdp05.hdp.com:6667 --topic test-topic --producer.config /root/producer.properties
【hdp04】上执行启动消费者,观察hdp03上输入字符后在hdp04上显示的内容
kinit -kt /etc/security/keytabs/kafka.service.keytab kafka/[email protected]
./kafka-console-producer.sh --broker-list
./kafka-console-consumer.sh --bootstrap-server hdp03.hdp.com:6667 --topic test-topic --consumer.config /root/consumer.properties
在外网服务器上,下载ambari-flume-service服务
git clone https://github.com/maikoulin/ambari-flume-service.git
下载flume的tar包,下载链接为
https://archive.apache.org/dist/flume/1.9.0/apache-flume-1.9.0-bin.tar.gz
将apache-flume-1.9.0-bin.tar.gz放到ambari-flume-service目录下
mkdir /root/ambari-flume-service/buildrpm/rpmbuild/SOURCES
cp /root/apache-flume-1.9.0-bin.tar.gz /root/ambari-flume-service/buildrpm/rpmbuild/SOURCES
执行编译生成rpm包,需要安装rpm-build命令,需要进入到shell脚本目录下执行
yum install -y rpm-build
cd /root/ambari-flume-service/buildrpm/
sh buildrpm.sh
将/root/ambari-flume-service/目录下的FLUME文件夹复制到hdp01的/var/lib/ambari-server/resources/stacks/HDP/3.1/services/目录下
重启ambari-server
ambari-server restart
在外网服务器上,将之前生成的rpm包(/root/ambari-flume-service/buildrpm/rpmbuild/RPMS/noarch/flume-1.9.0-1.el7.noarch.rpm)拷贝至hdp01的/opt下
创建flume的本地yum源
mkdir /var/www/html/flume
cp /opt/flume-1.9.0-1.el7.noarch.rpm /var/www/html/flume/
createrepo /var/www/html/flume/
配置所有节点的flume yum源
ansible all -m yum_repository -a 'name="flume" description="flume" baseurl="http://hdp01.hdp.com/flume" enabled=yes gpgcheck=no'
查看/etc/yum.repos.d/flume.repo文件
[flume]
baseurl = http://hdp01.hdp.com/flume
enabled = 1
gpgcheck = 0
name = flume
更新所有节点的yum配置
ansible all -m shell -a 'yum clean all'
ansible all -m shell -a 'yum makecache fast'
添加flume服务
需要重启HDFS、YARN等相关服务
默认安装在current下,建议将其移动至3.1.5.0-152下,并在软链接到current下
cp -r /usr/hdp/current/flume-server/ /usr/hdp/3.1.5.0-152/
rm -rf /usr/hdp/current/flume-server/
ln -s /usr/hdp/3.1.5.0-152/flume-server/ /usr/hdp/current/
**Exec Source:**用于文件监控,可以实时监控文件中的新增内容,类似于tail -F的效果
**NetCat TCP/UDP Source:**采集指定端口的数据,可以读取流经端口的每一行数据
**Spooling Directory Source:**采集文件夹中新增的文件
**Kafaka Source:**从Kafaka消息队列中采集数据
**File Channel:**使用文件来作为数据的存储介质。优点是数据不会丢失,缺点是相对内存效率有的慢
**Memory Channel:**使用内存作为数据的存储接受,优点效率高,缺点会丢失数据,会存在内存不足的情况
**Spillable Memory Channel:**使用内存和文件作为存储介质,即内存足够把数据存内存中,不足的时候再写入到文件中
**Logger Sink:**将数据作为日志处理,可以将其选择打印到控制台或写到文件中
**HDFS Sink:**将数据传输到HDFS中,主要针对离线计算场景
**Kafka Sink:**将数据传输到Kafka消息队列中,主要针对实时计算场景
以netcat为监测源,将输入内容写入到本地文件中
配置文件如下:
# 配置一个agent,agent的名称可以自定义(如a1)
# 指定agent的sources(如s1)、sinks(如k1)、channels(如c1)
a1.sources = s1
a1.channels= c1
a1.sinks = k1
# source定义
a1.sources.s1.type = netcat
a1.sources.s1.bind = 0.0.0.0
a1.sources.s1.port = 5556
# sink定义
a1.sinks.k1.type=file_roll
a1.sinks.k1.sink.directory=/data01/flume_data
a1.sinks.k1.sink.rollInterval=30
# channel定义
a1.channels.c1.type = memory
# 关系绑定
a1.sources.s1.channels = c1
a1.sinks.k1.channel = c1
输出的file_roll位置/data01/flume_data为目录,并非文件,并且需要手工创建
mkdir /data01/flume_data
chown flume:hadoop /data01/flume_data/
在hdp01上以netcat客户端连接hdp03.hdp.com:5556,并发送信息
nc hdp03.hdp.com 5556
在hdp03上查看结果文件
以本地日志文件为监测源,将输入内容写入到HDFS中
配置文件如下:
a2.sources = s1
a2.channels= c1
a2.sinks = k1
# source定义
a2.sources.s1.type = exec
a2.sources.s1.command = tail -f /var/log/messages
a2.sources.s1.shell = /bin/sh -c
# sink定义
a2.sinks.k1.type = hdfs
a2.sinks.k1.hdfs.kerberosPrincipal = [email protected]
a2.sinks.k1.hdfs.kerberosKeytab = /root/keytab/tenant1.keytab
a2.sinks.k1.hdfs.path = hdfs://hdp315/testhdfs/tenant1/pt_time=%Y%m%d%H%M
a2.sinks.k1.hdfs.useLocalTimeStamp = true
a2.sinks.k1.hdfs.fileType = DataStream
a2.sinks.k1.hdfs.writeFormat = Text
a2.sinks.k1.hdfs.filePrefix = flume-%Y%m%d%H%M
a2.sinks.k1.hdfs.round = true
a2.sinks.k1.hdfs.roundUnit = minute
a2.sinks.k1.hdfs.roundValue = 1
a2.sinks.k1.hdfs.rollInterval = 10
# channel定义
a2.channels.c1.type = memory
# 关系绑定
a2.sources.s1.channels = c1
a2.sinks.k1.channel = c1
sink中参数说明:
filePrefix:文件前缀,会在hdfs上生成的文件前面加上这个前缀,属于可选项
writeFormat:默认为Writable,建议改为Text,如果后期想使用hive或者impala操作这份数据的话,必须在生成数据之前设置为Text,Text表示是普通文本数据
fileType:默认为SequenceFile,还支持DataStream和CompressedStream;DataStream不会对输出数据进行压缩,CompressedStream会对输出数据进行压缩
rollInterval:默认值为30,单位是秒,表示hdfs多长时间切分一个文件,因为这个采集程序是一直运行的,只要有新数据,就会被采集到hdfs上面,hdfs默认30秒钟切分出来一个文件,如果设置为0表示不按时间切文件
rollSize:默认为1024,单位是字节,最终hdfs上切出来的文件大小都是1024字节,如果设置为0表示不按大小切文件
rollCount:默认为10,表示每隔10条数据切出来一个文件,如果设置为0表示不按数据条数切文件