在 RedHat的方案中,GlusterFS集群被部署在独立的服务器集群上,这适用于较大的集群规模及对性能和存储要求高的场景。在机器有限的情况下,我们也可以把Kubernetes集群的每个Node节点都当作一个GlusterFS的存储节点,并采用DaemonSet的调度方式将GlusterFS部署到Kubernetes集群上,具体的部署方式在Kubernetes 的 GitHub网站中有详细的说明文档,以Pod方式部署GlusterFS集群也使得GlusterFS 的部署和运维都变得非常简单。
提供全文检索能力的ElasticSearch集群也很容易被部署到Kubernetes中,前面提到的日志集中收集与查询分析的三件套ELK目前基本上全部以Pod的方式部署,以实现Kubernetes集群日志与用户应用日志的统一收集、查询、分析等功能。
在当前热门的大数据领域中,很多系统也都能以容器化方式部署到Kubernetes集群中,比如Hadoop、HBase、Spark 及 Storm等重量级集群。下一节将给出 Storm On Kubernetes 的建模方案,并且将其部署到Kubernetes集群中,最后提交第6章的WordCountTopology 作业并且观察运行结果。
Storm On Kubernetes 实战
通过第6章的学习,我们知道一个 Storm集群是由ZooKeeper、Nimbus (Master)及一些Supervisor (Slave)节点组成的,集群的配置文件默认保存在 conf/storm.yaml中,最关键的配置参数如下。
- storm.zookeeper.servers: ZooKeeper集群的节点IP地址列表。
- nimbus.seeds:Nimbus的IP地址。
- supervisor.slots.ports:Supervisor 中的Worker 监听端口列表。
从上述关键配置信息及Storm集群的工作原理来看,我们首先需要将ZooKeeper建模为Kubernetes Service,以便提供一个固定的域名地址,使得Nimbus 与Supervisor能够访问它。下面是ZooKeeper 的建模过程(为了简单起见,我们只建模一个ZooKeeper节点)。
首先,定义ZooKeeper对应的Service,Service名称为ku8-zookeeper,关联的标签为app=ku8-zookeeper 的Pod:
apiVersion: v1kind: Servicemetadata:
name: ku8-zookeeperspec:
ports:
name: clientport: 2181selector:
app: ku8-zookeeper
其次,定义ZooKeeper对应的RC:
apiversion: v1
kind: Replicationcontrollermetadata:
name: ku8-zookeeper-lspec:
replicas: 1template:
metadata:
labels:
app: ku8-zookeeperspec:
containers:
name: server
image: jplock/ zookeeper
imagePu1lPolicy: IfNotPresentports:
-containerPort: 2181
接下来,我们需要将Nimbus也建模为Kubernetes Service,因为Storm客户端需要直接访问Nimbus服务以提交拓扑任务,所以在conf/storm.yaml中存在nimbus.sceds参数。由于Nimbus在6627端口上提供了基于Thrift的 RPC服务,因此对Nimbus服务的定义如下:
apiversion: v1kind: Servicemetadata:
name: nimbusspec:
selector:
app: storm-nimbusports:
-name: nimbus-rpc
port: 6627
targetPort:6627
考虑到在storm.yaml配置文件中有很多参数,所以为了实现任意参数的可配置性,我们可以用Kubernetes的Config Map资源对象来保存storm.yaml,并映射到Nimbus(以及 Supervisor)节点对应的Pod实例上。下面是在本案例中使用的storm.yaml 文件(storm-conf.yaml)的内容:
storm.zookeeper.servers: [ku8-zookeeper]
nimbus.seeds: [nimbus]storm.log.dir: "log"
storm. local.dir: "storm-data"supervisor.slots.ports:
-6700
670167026703
将上述配置文件创建为对应的ConfigMap ( storm-config),可以执行下面的命令:
kubelet create configmap storm-config --from-file=storm-conf.yaml
然后,storm-config 就可以被任意Pod 以 Volume方式挂载到容器内的任意指定路径上了。接下来,我们可以继续建模 Nimbus服务对应的Pod。在从Docker Hub上搜寻相关 Storm镜像并进行分析后,我们选择了Docker 官方提供的镜像storm:1.0。相对于其他Storm镜像来说,官方维护的这个镜像有以下优点。
- Storm版本新。
- Storm整体只有一个镜像,通过容器的command 命令参数来决定启动的是哪种类型的节点,比如Nimbus主节点、Nimbus-ui管理器或者Supervisor 从节点。
- 标准化的Storm进程启动方式,可以将conf/storm.yaml配置文件映射到容器外,因此可以采用Kubernetes 的 ConfigMap特性。
采用storm:1.0镜像定义Nimbus Pod的YAML文件如下:
apiversion: v1kind: Pod
metadata:
name: nimbuslabels:
app: storm-nimbusspec:
volumes:
name: config-volumeconfigMap:
name: storm-configitems:
一key:storm-conf.yaml
path:storm.yaml
containers:
- name: nimbus
image: storm: 1.0
imagePullPolicy: IfNotPresentports:
-containerPort: 6627
command:【"storm" ,"nimbus" ]volumeMounts:
- name: config-volumemountPath: /conf
restartPolicy: Always
这里我们需要关注两个细节:第1个细节是ConfigMap 的使用方法,首先要把之前定义的ConfigMap ——storm-config映射为Pod的一个Volume,然后在容器中将此Volume挂接到某个具体的路径上;第2个细节是容器的参数 command,上面的command: [ "storm" , "nimbus"]表示此容器启动的是nimus进程。
类似地,我们定义storm-ui服务,这是一个Web管理程序,提供了图形化的Storm管理功能,因为需要在Kubernetes集群之外访问它,所以我们通过NodePort方式映射8080端口到主机上的30010。storm-ui服务的YAML定义文件如下:
apiversion: v1kind: Servicemetadata:
name: storm-uispec:
type: NodePortselector:
app:storm-uiports:
-name: web
port: 8080
targetPort: 8080nodePort:30010
最后,我们来建模Supervisor。Supervisor看似不需要被建模为Service,因为Supervisor 不会被主动调用,但实际上Supervisor节点之间会相互发起通信,因此Supervisor节点注册到ZooKeeper 上的地址必须能被相互访问,在 Kubernetes平台上有两种方式解决此问题。
第1种方式,Supervisor节点注册到ZooKeeper上时,不用主机名(Pod名称),而是采用Pod的P地址。
第2种方式,用Headless Service模式,每个Supervisor节点都被建模为一个HeadlessService,并且确保Supervisor节点的容器名称(主机名)与Headless Service的名称一样,此时Supervisor节点注册到ZooKeeper 上的地址就跟Headless Service名称一样了,Supervisor节点之间都能用对方的Headless Service的域名进行通信。
其中,第1种方式需要修改Supervisor的启动脚本和对应的参数才能进行,实现起来比较麻烦,第②种方式无须修改镜像就能实现,所以我们采用了第﹖种方式建模。下面是某个Supervisor节点的Service定义,注意 clusterIP: None的特殊写法:
apiversion: v1
kind: Servicemetadata:
name:storm-supervisorspec:
clusterIP:Noneselector:
app:storm-supervisorports:
- port: 8000
storm-supervisor 这个节点对应的 Pod定义如下,需要注意Pod 的名称为storm-supervisor,并且command的值为[ "storm", "supervisor"]:
apiversion: v1kind: Pod
metadata:
name: storm-supervisorlabels:
app: storm-supervisorspec:
volumes:
name: config-volumeconfigMap:
name: storm-configitems:
一key:storm-conf.yaml
path: storm.yaml
containers:
name: storm-supervisorimage: storm: 1.0
imagePullPolicy: IfNotPresent
command:["storm", "supervisor" ]volumeMounts:
-name: config-volumemountPath: /conf
restartPolicy:Always
我们可以定义多个Supervisor 节点,比如在本案例中定义了两个Supervisor节点。在成功部署到Kubernetes集群后,我们通过Storm UI的30010端口进入Storm的管理界面,可以看到如下界面。
下面这个截图验证了两个Supervisor 节点也可以被成功注册在集群中,我们看到每个节点都有4个Slot,这符合我们在storm.yaml中的配置。
至此,Storm集群在Kubernetes 上的建模和部署已经顺利完成了。接下来我们看看如何在Storm集群中提交之前学习过的WordCountTopology作业并且观察它的运行情况。
首先,我们可以去 https:/ljar-download.com/下载编译好的 WordCountTopology 作业的JAR文件storm-starter-topologies-1.0.3.jar,然后通过Storm Client工具将该Topology作业提交到Storm集群中,提交作业的命令如下:
storm jar/userlib/storm-starter-topologies-1.0.3.jar org.apache.storm.starter.ordcountTopology topology
由于在storm:1.0镜像中已经包括了Storm Client 工具,所以最简便的方式是定义一个Pod,然后把下载下来的storm-starter-topologies-1.0.3.jar作为Volume映射到Pod里的/userlib/目录下。将容器的启动命令设置为上述提交作业的命令即可实现,下面是此Pod 的YAML定义:
apiversion: v1
kind: Podmetadata:
name: storm-topo-examplespec:
volumes:
name:user-libhostPath:
path: /root/stormname: config-volumeconfigMap:
name:storm-configitems:
-key: storm-conf.yaml
path: storm. yaml
containers:
name: storm-topo-exampleimage: storm: 1.0
imagePullPolicy: IfNotPresent
command: [ "storm","jar", "/userlib/storm-starter-topologies-1.0.3.jar",
"org.apache.storm.starter.WordCountTopology", "topology" ]
volumeMounts:
- name: config-volumemountPath: /conf
name:user-lib
mountPath: /userlib
restartPolicy: Never
上述定义有如下关键点。
- 将storm-starter-topologies-1.0.3.jar 放在主机的/root/storm目录中。
- 容器的启动命令是storm client,提交Topology 作业。
- Pod重启策略为Never,因为只要提交完Topology 作业即可。
创建上述 Pod以后,我们查看该Pod 的日志,如果看到下面这段输出,则表明WordCountTopology的拓扑作业已经被成功提交到Storm集群中了。
接下来,我们进入Storm UI去看看作业的执行情况。下图是WordCountTopology的汇总信息,状态为Active,运行了8分钟,占用了3个Worker进程,总共运行了28个Task。