专栏名称: 唤之
目录
相关文章推荐
程序猿  ·  TCP 才不傻! ·  2 天前  
OSC开源社区  ·  谈开源大模型的技术主权问题 ·  3 天前  
程序员的那些事  ·  感谢 ... ·  6 天前  
程序员的那些事  ·  字节用 8266.8 ... ·  5 天前  
51好读  ›  专栏  ›  唤之

在Kubernetes平台上运行Hadoop的实践

唤之  · 掘金  · 程序员  · 2018-04-12 05:37

正文

Hadoop与Kubernetes就好像江湖里的两大绝世高手,一个是成名已久的长者,至今仍然名声远扬,一个则是初出茅庐的青涩少年,骨骼惊奇,不走寻常路,一出手便惊诧了整个武林。Hadoop与Kubernetes之间有很深的渊源,因为都出自IT豪门——Google,只不过,后者是亲儿子,正因为有大佬背书,所以Kubernetes一出山,江湖各路门派便都蜂拥而至,拥护称王。
不知道是因为Hadoop是干儿子的缘故还是因为“廉颇老矣”,总之,Hadoop朋友圈的后辈们如Spark、Storm等早都有了在Kubernetes上部署运行的各种资料和案例,但Hadoop却一直游离于Kubernetes体系之外,本文我们给出Hadoop在Kubernetes上的实践案例,以弥补这种缺憾。
Hadoop容器化的资料不少,但Hadoop部署在Kubernetes上的资料几乎没有,这主要是以下几个原因导致的:
第一, Hadoop集群重度依赖DNS机制,一些组件还使用了反向域名解析,以确定集群中的节点身份,这对Hadoop在Kubernetes上的建模和运行带来极大挑战,需要深入了解Hadoop集群工作原理并且精通Kubernetes,才能很好解决这一难题。
第二, Hadoop新的Map-Reduce计算框架Yarn的模型出现的比较晚,它的集群机制要比HDFS复杂,资料也相对较少,增加了Hadoop整体建模与迁移Kubernetes平台的难度。
第三, Hadoop与Kubernetes分别属于两个不同的领域,一个是传统的大数据领域,一个是新兴的容器与微服务架构领域,这两个领域之间交集本来很小,加之Hadoop最近几年已经失去焦点(这点从百度搜索关键词就能发现),所以,没有多少人关注和研究Hadoop在Kubernetes的部署问题,也是情理之中的事情。
Hadoop 2.0其实是由两套完整的集群所组成,一个是基本的HDFS文件集群,一个是YARN资源调度集群,如下图所示:

因此在Kubernetes建模之前,我们需要分别对这两种集群的工作机制和运行原理做出深入的分析,下图是HDFS集群的架构图:

我们看到,HDFS集群是由NameNode(Master节点)和Datanode(数据节点)等两类节点所组成,其中,客户端程序(Client)以及DataNode节点会访问NameNode,因此,NameNode节点需要建模为Kubernetes Service以提供服务,以下是对应的Service定义文件:
apiVersion: v1kind: Servicemetadata:  name: k8s-hadoop-masterspec:  type: NodePort  selector:    app: k8s-hadoop-master  ports:    - name: rpc      port: 9000      targetPort: 9000    - name: http      port: 50070      targetPort: 50070      nodePort: 32007
其中,NameNode节点暴露2个服务端口:
  • 9000端口用于内部IPC通信,主要用于获取文件的元数据

  • 50070端口用于HTTP服务,为Hadoop 的Web管理使用

为了减少Hadoop镜像的数量,我们构建了一个镜像,并且通过容器的环境变量HADOOP_NODE_TYPE来区分不同的节点类型,从而启动不同的Hadoop组件,下面是镜像里的启动脚本startnode.sh的内容:
#!/usr/bin/env bashsed -i "s/@HDFS_MASTER_SERVICE@/$HDFS_MASTER_SERVICE/g" $HADOOP_HOME/etc/hadoop/core-site.xmlsed -i "s/@HDOOP_YARN_MASTER@/$HDOOP_YARN_MASTER/g" $HADOOP_HOME/etc/hadoop/yarn-site.xmlyarn-masterHADOOP_NODE="${HADOOP_NODE_TYPE}"if [ $HADOOP_NODE = "datanode" ]; then        echo "Start DataNode ..."        hdfs datanode  -regularelse    if [  $HADOOP_NODE = "namenode" ]; then        echo "Start NameNode ..."        hdfs namenode    else        if [ $HADOOP_NODE = "resourceman" ]; then            echo "Start Yarn Resource Manager ..."            yarn resourcemanager        else             if [ $HADOOP_NODE = "yarnnode" ]; then                 echo "Start Yarn Resource Node  ..."                 yarn nodemanager                 else                               echo "not recoginized nodetype "             fi        fi    fi  fi
我们注意到,启动命令里把Hadoop配置文件(core-site.xml与yarn-site.xml)中的HDFS Master节点地址用环境变量中的参数HDFS_MASTER_SERVICE来替换,YARN Master节点地址则用HDOOP_YARN_MASTER来替换。下图是Hadoop HDFS 2节点集群的完整建模示意图:

图中的圆圈表示Pod,可以看到,Datanode并没有建模Kubernetes Service,而是建模为独立的Pod,这是因为Datanode并不直接被客户端所访问,因此无需建模Service。当Datanode运行在Pod容器里的时候,我们需要修改配置文件中的以下参数,取消DataNode节点所在主机的主机名(DNS)与对应IP地址的检查机制:

dfs.namenode.datanode.registration.ip-hostname-check=false

如果上述参数没有修改,就会出现DataNode集群“分裂”的假象,因为Pod的主机名无法对应Pod的IP地址,因此界面会显示2个节点,这两个节点都状态都为异常状态。
下面是HDFS Master节点Service对应的Pod定义:
apiVersion: v1kind: Podmetadata:  name: k8s-hadoop-master  labels:    app: k8s-hadoop-masterspec:  containers:    - name: k8s-hadoop-master      image: kubeguide/hadoop      imagePullPolicy: IfNotPresent      ports:        - containerPort: 9000        - containerPort: 50070          env:        - name: HADOOP_NODE_TYPE          value: namenode        - name: HDFS_MASTER_SERVICE          valueFrom:            configMapKeyRef:              name: ku8-hadoop-conf              key: HDFS_MASTER_SERVICE        - name: HDOOP_YARN_MASTER          valueFrom:            configMapKeyRef:              name: ku8-hadoop-conf              key: HDOOP_YARN_MASTER  restartPolicy: Always
下面是HDFS的Datanode的节点定义(hadoop-datanode-1):
apiVersion: v1kind: Podmetadata:    name: hadoop-datanode-1    labels:      app: hadoop-datanode-1spec:  containers:    - name: hadoop-datanode-1      image: kubeguide/hadoop      imagePullPolicy: IfNotPresent      ports:        - containerPort: 9000        - containerPort: 50070          env:        - name: HADOOP_NODE_TYPE          value: datanode        - name: HDFS_MASTER_SERVICE          valueFrom:            configMapKeyRef:              name: ku8-hadoop-conf              key: HDFS_MASTER_SERVICE        - name: HDOOP_YARN_MASTER          valueFrom:            configMapKeyRef:              name: ku8-hadoop-conf              key: HDOOP_YARN_MASTER          restartPolicy: Always
实际上,Datanode可以用DaemonSet方式在每个Kubernerntes节点上部署一个,在这里为了清晰起见,就没有用这个方式 定义。接下来,我们来看看Yarn框架如何建模,下图是Yarn框架的集群架构图:

我们看到,Yarn集群中存在两种角色的节点:ResourceManager以及NodeManger,前者属于Yarn集群的头脑(Master),后者是工作承载节点(Work Node),这个架构虽然与HDFS很相似,但因为一个重要细节的差别,无法沿用HDFS的建模方式,这个细节就是Yarn集群中的ResourceManager要对NodeManger节点进行严格验证,即NodeManger节点的节点所在主机的主机名(DNS)与对应IP地址严格匹配,简单来说,就是要符合如下规则:
NodeManger建立TCP连接时所用的IP地址,必须是该节点主机名对应的IP地址,即主机DNS名称解析后返回节点的IP地址。
所以我们采用了Kubernetes里较为特殊的一种Service——Headless Service来解决这个问题,即为每个NodeManger节点建模一个Headless Service与对应的Pod,下面是一个ResourceManager与两个NodeManger节点所组成的Yarn集群的建模示意图:

Headless Service的特殊之处在于这种Service没有分配Cluster IP,在Kuberntes DNS里Ping这种Service的名称时,会返回后面对应的Pod的IP地址,如果后面有多个Pod实例,则会随机轮询返回其中一个的Pod地址,我们用Headless Service建模NodeManger的时候,还有一个细节需要注意,即Pod的名字(容器的主机名)必须与对应的Headless Service的名字一样,这样一来,当运行在容器里的NodeManger进程向ResourceManager发起TCP连接的过程中会用到容器的主机名,而这个主机名恰好是NodeManger Service的服务名,而这个服务名解析出来的IP地址又刚好是容器的IP地址,这样一来,就巧妙的解决了Yarn集群的DNS限制问题。
下面以yarn-node-1为例,给出对应的Service与Pod的YAM文件,首先是yarn-node-1对应的Headless Service的YAM定义:
apiVersion: v1kind: Servicemetadata:  name: yarn-node-1spec:  clusterIP: None  selector:    app: yarn-node-1  ports:     - port: 8040
注意到定义中“clusterIP:None”这句话,表明这是一个Headless Service,没有自己的Cluster IP地址,下面给出YAM文件定义:
apiVersion: v1kind: Podmetadata:  name: yarn-node-1  labels:    app: yarn-node-1spec:  containers:    - name: yarn-node-1      image: kubeguide/hadoop      imagePullPolicy: IfNotPresent      ports:        - containerPort: 8040        - containerPort: 8041           - containerPort: 8042              env:        - name: HADOOP_NODE_TYPE          value: yarnnode        - name: HDFS_MASTER_SERVICE          valueFrom:            configMapKeyRef:              name: ku8-hadoop-conf              key: HDFS_MASTER_SERVICE        - name: HDOOP_YARN_MASTER          valueFrom:            configMapKeyRef:              name: ku8-hadoop-conf              key: HDOOP_YARN_MASTER            restartPolicy: Always
ResourceManager的YAML定义没有什么特殊的地方,其中Service定义如下:
apiVersion: v1kind: Servicemetadata:  name: ku8-yarn-masterspec:  type: NodePort  selector:    app: yarn-master  ports:     - name: "8030"              port: 8030     - name: "8031"            port: 8031     - name: "8032"       port: 8032          - name: http       port: 8088       targetPort: 8088       nodePort: 32088
对应的Pod定义如下:
apiVersion: v1kind: Podmetadata:  name: yarn-master  labels:    app: yarn-masterspec:  containers:    - name: yarn-master      image: kubeguide/hadoop      imagePullPolicy: IfNotPresent      ports:        - containerPort: 9000        - containerPort: 50070          env:        - name: HADOOP_NODE_TYPE          value: resourceman        - name: HDFS_MASTER_SERVICE          valueFrom:            configMapKeyRef:              name: ku8-hadoop-conf              key: HDFS_MASTER_SERVICE        - name: HDOOP_YARN_MASTER          valueFrom:            configMapKeyRef:              name: ku8-hadoop-conf              key: HDOOP_YARN_MASTER            restartPolicy: Always
目前这个方案,还遗留了一个问题有待解决:HDFS NameNode节点重启后的文件系统格式化问题,这个问题可以通过启动脚本来解决,即判断HDFS文件系统是否已经格式化过,如果没有,就启动时候执行格式化命令,否则跳过格式化命令。
安装完毕后,我们可以通过浏览器访问Hadoop的HDFS管理界面,点击主页上的Overview页签会显示我们熟悉的HDFS界面:

切换到Datanodes页签,可以看到每个Datanodes的的信息以及当前状态:

接下来,我们可以登录到NameNode所在的Pod里并执行HDSF命令进行功能性验证,下面的命令执行结果是建立一个HDFS目录,并且上传一个文件到此目录中:
root@hadoop-master:/usr/local/hadoop/bin# hadoop fs -ls  /root@hadoop-master:/usr/local/hadoop/bin# hadoop fs -mkdir /leader-usroot@hadoop-master:/usr/local/hadoop/bin# hadoop fs -ls /Found 1 itemsdrwxr-xr-x   - root supergroup          0 2017-02-17 07:32 /leader-usroot@hadoop-master:/usr/local/hadoop/bin# hadoop fs -put hdfs.cmd /leader-us
然后,我们可以在HDFS管理界面中浏览HDFS文件系统,验证刚才的操作结果:

接下来,我们再登录到hadoop-master对应的Pod上,启动一个Map-Reduce测试作业——wordcount,作业启动后,我们可以在Yarn的管理界面中看到作业的执行信息,如下图所示:

当作业执行完成后,可以通过界面看到详细的统计信息,比如wordcount的执行结果如下图所示:






请到「今天看啥」查看全文