背景
OpenShift是目前比较主流的基于Kubernetes的容器云解决方案,OpenShift在kubernetes基础上提供了快速部署k8s集群的Ansible脚本,DevOps能力以及一个易于使用的前端界面。本文主要讨论如何在OpenShift上拉起Storm集群。
Storm架构
Storm的架构是Slave-Master模式的,一个完整的Storm集群需要以下四种服务:
- 一个或多个Nimbus服务,storm集群的管理节点,可以实现基于Zookeeper的HA方案
- 一个UI服务,提供监控集群和拓扑(topology)运行前端界面
- 一个DRPC服务,提供了用户和正在运行的拓扑交互的分布式RPC接口
- 若干个Supervisor服务,storm集群的计算节点
Zookeeper方案
Storm集群的正常运行还需要依赖Zookeeper,因为Storm各组件的服务发现,nimbus节点的HA以及topology的信息都是存储在Zookeeper里的。
可以通过两种方式来为OpenShift上的Storm编排提供Zookeeper服务:
- 独占的Zookeeper集群: 在OpenShift集群上为每个Storm集群动态的启动一个独占的Zookeeper集群的编排,其至少包含三个Zookeeper镜像并提供外部存储方案(如GlusterFS)来持久化Zookeeper的数据
- 共享的Zookeeper集群:提前创建一个zookeeper集群,各storm集群实例共享使用;共享的zookeeper集群可以由OpenShift托管的zookeeper编排,也可以是在OpenShift集群外部的若干台与OpenShift集群保持网络连通的虚拟机或物理机器上手工搭建的Zookeeper集群;共享zk的方式需要考虑多个storm集群之间的数据冲突问题,因为storm集群会默认将数据存储到zookeeper的"/storm"目录下,所以每个storm集群实例在zookeeper上都需要有自己独立的数据存储目录,可以通过storm的配置项“storm.zookeeper.root”来指定。
Storm镜像构建
我们需要为storm的每个组件构建单独的docker镜像,即storm编排需要一个nimbus镜像,一个ui镜像,一个supervisor镜像以及一个drpc镜像。由于storm的apache官方安装包里包含了所有的组件,管理员可以根据不同的配置文件(conf/storm.yaml)和启动命令来决定当前启动的是哪一个组件,所以用于构建各个组件镜像的Dockerfile也只有启动命令和配置文件的差异。
以nimbus镜像的Dockerfile为例:
FROM docker.io/nathonfowlie/centos-jre
# 0.10.0
ENV STORM_VERSION 0.10.0
ENV APACHE_MIRROR http://apache.cs.utah.edu/storm
RUN curl -o /opt/apache-storm.tar.gz ${APACHE_MIRROR}/apache-storm-${STORM_VERSION}/apache-storm-${STORM_VERSION}.tar.gz && \
tar -C /opt -xzf /opt/apache-storm.tar.gz && \
rm /opt/apache-storm.tar.gz && \
mv /opt/apache-storm-${STORM_VERSION} /opt/apache-storm && \
mkdir -p /opt/apache-storm/storm-local && \
mkdir -p /opt/apache-storm/logs && \
mkdir -p /opt/apache-storm/logback-dist && \
mkdir -p /opt/apache-storm/conf-dist && \
cp /opt/apache-storm/conf/* /opt/apache-storm/conf-dist/
COPY logback-cluster.xml /opt/apache-storm/logback/cluster.xml
COPY logback-cluster.xml /opt/apache-storm/logback-dist/cluster-console.xml
COPY storm.yaml /opt/apache-storm/conf/
WORKDIR /opt/apache-storm
RUN chmod 777 -R /opt/apache-storm
EXPOSE 6700 6701 6702 6703 8080 6627 3772
VOLUME ["/opt/apache-storm/conf", "/opt/apache-storm/logback", "/opt/apache-storm/logs", "/opt/apache-storm/storm-local"]
CMD ["bin/storm nimbus"]
各镜像的Dockerfile差异主要在storm.yaml和CMD里的启动命令:
- nimbus的启动命令是"bin/storm nimbus",storm.yaml如下:
storm.zookeeper.servers:
- 10.128.0.46
storm.zookeeper.root: "/storm_001"
nimbus.thrift.port: 6627
storm.yaml三个参数分别指出了zookeeper集群的地址,storm数据在zookeeper里的存储位置,nimbus的thrift监听端口
- ui的启动命令是"bin/storm ui",storm.yaml如下:
storm.zookeeper.servers:
- 10.128.0.46
storm.zookeeper.root: "/storm_001"
nimbus.thrift.port: 6627
nimbus.host: "sb-instanceid-sn"
ui.port: 8080
storm.ymal里的四个参数分别代表zookeeper集群地址,storm数据在zookeeper里的存储目录,nimbus的thrift监听端口,nimbus服务的列表(nimbus HA场景下需要配多个),ui服务的监听端口
- supervisor的启动命令是"bin/storm supervisor",storm.yaml如下:
storm.zookeeper.servers:
- 10.128.0.46
storm.zookeeper.root: "/storm_001"
nimbus.thrift.port: 6627
nimbus.host: "sb-instanceid-sn"
storm.yaml里的四个参数分别代表zookeeper集群地址,storm数据在zookeeper里的存储目录,nimbus的thrift监听端口,nimbus服务的列表(nimbus HA场景下需要配多个)
- drpc的启动命令是"bin/storm drpc",storm.yaml如下:
storm.zookeeper.servers:
- 10.128.0.46
storm.zookeeper.root: "/storm_001"
nimbus.thrift.port: 6627
nimbus.host: "sb-instanceid-sn"
drpc.port: 3772
storm.yaml里的五个参数分别代表zookeeper集群地址,storm数据在zookeeper里的存储目录,nimbus的thrift监听端口,nimbus服务的列表(nimbus HA场景下需要配多个),drpc服务监听端口
storm编排
storm的编排将启动包含1个nimbus服务,1个ui服务,1个drpc服务以及2个supervisor服务在内的storm集群环境。要实现这样的配置的storm集群,需要为nimbus/ui/drpc/supervisor分别创建rc(replication controller),由kubernetes保证各个组件的服务可用性以及备份数目,具体配置如下:
- nimbus的rc,启动一个运行storm nimbus服务的pod并开放6627端口,nimbus rc的yaml如下:
apiVersion: v1
kind: ReplicationController
metadata:
name: storm-nimbus
spec:
replicas: 1
selector:
storm: nimbus
template:
metadata:
labels:
storm: nimbus
spec:
containers:
- name: nimbus
image: storm_nimbus:0.10.0
ports:
- containerPort: 6627
volumeMounts:
- name: storm-data
mountPath: /opt/apache-storm/storm-local
volumes:
- name: storm-data
emptyDir: {}
- ui的rc,将启动一个运行storm ui服务的pod并开放8080端口,ui rc的yaml如下:
apiVersion: v1
kind: ReplicationController
metadata:
name: storm-ui
spec:
replicas: 1
selector:
storm: ui
template:
metadata:
labels:
storm: ui
spec:
containers:
- name: ui
image: storm_ui:0.10.0
ports:
- containerPort: 8080
volumeMounts:
- name: storm-data
mountPath: /opt/apache-storm/storm-local
volumes:
- name: storm-data
emptyDir: {}
- supervisor的rc将启动2个运行storm supervisor服务的pod,supervisor rc的yaml文件如下:
apiVersion: v1
kind: ReplicationController
metadata:
name: storm-supervisor
spec:
replicas: 2
selector:
storm: supervisor
template:
metadata:
labels:
storm: supervisor
spec:
containers:
- name: supervisor
image: storm_supervisor:0.10.0
volumeMounts:
- name: storm-data
mountPath: /opt/apache-storm/storm-local
volumes:
- name: storm-data
emptyDir: {}
- drpc的rc将启动一个运行storm drpc服务的pod并开放3772端口,drpc rc的yaml文件如下:
apiVersion: v1
kind: ReplicationController
metadata:
name: storm-drpc
spec:
replicas: 1
selector:
storm: drpc
template:
metadata:
labels:
storm: drpc
spec:
containers:
- name: drpc
image: storm_drpc:0.10.0
ports:
- containerPort: 3772
volumeMounts:
- name: storm-data
mountPath: /opt/apache-storm/storm-local
volumes:
- name: storm-data
emptyDir: {}
各个组件的rc创建后,我们还需要考虑服务暴露的问题,即将storm集群中需要被用户和应用访问的服务暴露为kubernetes的service;在storm集群环境中用户或应用程序需要连接nimbus服务去提交topology的jar包,需要访问storm ui查看storm的集群状态和topology执行情况,需要通过drpc服务提供的drpc接口与topology交互。所以,我们需要为nimbus,ui,drpc各自创建一个service。
在OpenShift/Kubernetes中,可以通过两种方式将service暴露到集群之外:
- nodeport,在OpenShift/Kubernetes集群的所有node节点上开放一个指定端口,在集群外部可以用集群内任何一个node的ip/hostname加上该指定端口访问service
- external IP,将service绑定到OpenShift/Kubernetes集群内的某个指定节点的指定端口上,在集群外部可以通过该节点的ip/hostname加端口访问service
以下以external IP方式为例,nimbus/ui/drpc的service的yaml文件示例如下:
- nimbus的service
apiVersion: v1
kind: Service
metadata:
name: storm-nimbus
spec:
selector:
storm: nimbus
ports:
- protocol: TCP
port: 6627
externalIPs:
- 10.1.236.92
- ui的service
apiVersion: v1
kind: Service
metadata:
name: sb-instanceid-su
spec:
selector:
sb-instanceid-storm: ui
ports:
- protocol: TCP
port: 8080
externalIPs:
- 10.1.236.92
- drpc的service
apiVersion: v1
kind: Service
metadata:
name: sb-instanceid-sd
spec:
selector:
sb-instanceid-storm: drpc
ports:
- protocol: TCP
port: 3772
externalIPs:
- 10.1.130.155
测试
1 登陆OpenShift集群
2 通过oc命令创建nimbus/ui/supervisor/drpc的rc
3 通过oc命令创建nimbus/ui/supervisor/drpc的service
4 通过oc命令查看storm集群的pods以及services的运行状况
5 打开浏览器,通过storm-ui service提供的external IP和8080端口访问storm ui: http://10.1.236.92:8080
6 在客户端机器(比如自己的laptop)上下载与集群上运行的storm版本一致apache storm的安装包,如:http://apache.cs.utah.edu/storm/apache-storm-0.10.0/apache-storm-0.10.0.tar.gz
7 解压并进入目录apache-storm-0.10.0/conf,修改storm.yaml配置文件,指向storm集群的nimbus service暴露出来的external ip和端口(详见步骤4的截图):
storm.zookeeper.servers:
- 10.128.0.46
nimbus.thrift.port: 6627
nimbus.host: 10.1.236.92
8 通过storm命令行远程连接到storm的nimbus服务,查看storm集群中在执行的topology列表(当前列表为空)
9 如果需要提交topology的jar包到storm集群,则可以在客户端直接执行./bin/storm jar <topology-jar-path> <topology-jar-class>
改进
未来可以改进的地方包括:
1 通过docker的启动脚本动态注入storm.yaml需要的配置项,包括:
- storm.zookeeper.servers
- storm.zookeeper.root
- nimbus.thrift.port
- nimbus.host
- drpc.port
- ui.port
2 支持nimbus HA,目前仅支持单nimbus (nimbus HA特性只支持storm 1.0.0以上的storm版本,并且nimbus的配置项名称从nimbus.host修改为了nimbus.seeds)
3 支持supervisor数目的动态配置,目前是固定的一个编排启动2个supervisor