基于Docker Swarm的JStorm集群实践

现在是9102年, 虽然JStorm已经过时了, Docker Swarm也是明日黄花, 但是这一点也不妨碍我写下这篇文章 ?

本文项目源码

 

  • 基本概念
    • Swarm
    • JStorm
  • 集群搭建
    • 初始化Swarm集群
    • 网络初始化
    • Compose file v3 参考
    • 部署JStrom集群
    • 提交JStorm计算任务
      • 启动
      • 任务更新
  • 总结

基本概念

Swarm

Docker Swarm是Docker原生的集群管理工具, 用于管理多台服务器上的Docker容器集群, 并提供扩容, 缩容, 滚动更新等多种功能, 简单易用好上手.Swarm集群上适合部署无状态服务.

在Swarm集群中,节点有两种角色,manager和worker,Swarm命令基本只能在manager节点执行, 一个集群可以有多个manager节点, 但只有一个节点可以成为leader manager node,视服务器具体数量分配manager和worker.

Swarm中task是一个单一的Docker容器, service是指一组task的集合, 即service包含了多个相同的Docker容器, 同Docker-Compose中的service是相同的含义, service定义了这些task的属性,services有两种模式:

  • replicated services:只运行指定个数的tasks
  • global services:每个节点上运行一个此task

Swarm中有两种网络模式: vip, dnsrr

  • vip(默认): 每一个service对应一个vip(虚拟ip), service的vip对应映射多个具体容器的ip, service的ip与容器ip隔离保持不变 , 在vip网络模式下, 可以映射端口到宿主机, 这样可以通过宿主机IP+端口的形式访问到服务
  • dnsrr: 每一个service对应其所有容器ip的列表, 通过service name访问时将轮询各个容器, 这种网络模式下不可映射端口到宿主机

当前Swarm的网络可能会踩到某些坑, 不太好用

 

JStorm

JStorm是一个分布式实时计算引擎

JStorm集群包含两类节点: 主控节点(Nimbus)和工作节点(Supervisor). 其分别对应的角色如下:

主控节点(Nimbus)上运行Nimbus Daemon. Nimbus负责接收Client提交的Topology, 分发代码, 分配任务给工作节点, 监控集群中运行任务的状态等工作

工作节点(Supervisor)上运行Supervisor Daemon, Supervisor通过subscribe Zookeeper相关数据监听Nimbus分配过来任务, 据此启动或停止Worker工作进程. 每个Worker工作进程执行一个Topology任务的子集; 单个Topology的任务由分布在多个工作节点上的Worker工作进程协同处理.

Nimbus和Supervisor节点之间的协调工作通过Zookeeper实现. 此外, Nimbus和Supervisor本身均为无状态进程, 支持Fail Fast; JStorm集群节点的状态信息或存储在Zookeeper, 或持久化到本地, 这意味着即使Nimbus/Supervisor宕机, 重启后即可继续工作. 这个设计使得JStorm集群具有非常好的稳定性.

在部署上, Nimbus节点和Supervisor节点两者的区别就是启动命令不同jstorm nimbusjstorm supervisor

 

集群搭建

准备

  • 一台及以上的同一内网服务器
  • Swarm需要高版本的Docker(17,18)
  • 每台服务器上需要导入docker swarm镜像
    $ docker run --rm swarm -v 查看swarm版本, 若本机不存在会直接去拉swarm镜像, 每台服务的docker版本和swarm需要统一
  • Swarm集群中各个节点的机器上都要有相应的镜像(启动时不会自动pull镜像), 如果容器需要volume外部目录也要手动创建, 这个不像Docker容器本身一样会自己去创建

初始化Swarm集群

选择一台机器作为manage节点初始化Swarm集群, 执行如下命令:

$ docker swarm init --advertise-addr 本机ip   # 执行之后,出现worker的join-token,比如下面示例:
[admin@localhost ~]$ docker swarm init --advertise-addr 10.0.0.203
Swarm initialized: current node (dfsazah8sv5rlerkkiqmaj1xk) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-476ch09ncm7isnl7ydyxpkgidr5x33p7p5n861emt924hs00ue-6lz0sdviclopxq94mkjcpf8td  10.0.0.203:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

复制该命令后在其他机器上执行即以worker身份加入集群

也可以通过如下命令获得join-token:

$ docker swarm join-token worker     # 获取manager的token,以woker加入  
$ docker swarm join-token manager     # 获取manager的token,以manager加入
$ docker swarm inspect  # 查看集群详细信息
$ docker node ls        # 查看集群节点列表

执行docker info可以检查到swarm相关的信息, 如下所示

Swarm: active
 NodeID: 571mtvouc9jqml6s8gxb8ae5l
 Is Manager: true
 ClusterID: u79o25zg5ra856r21v2atj53k
 Managers: 1
 Nodes: 3
 Default Address Pool: 10.0.0.0/8  # 此处需要注意
 SubnetSize: 24
 Orchestration:
  Task History Retention Limit: 5
 Raft:
  Snapshot Interval: 10000
  Number of Old Snapshots to Retain: 0
  Heartbeat Tick: 1
  Election Tick: 10
 Dispatcher:
  Heartbeat Period: 5 seconds
 CA Configuration:
  Expiry Duration: 3 months
  Force Rotate: 0
 Autolock Managers: false
 Root Rotation In Progress: false
 Node Address: 10.0.0.203
 Manager Addresses:
  10.0.0.203:2377
# work节点
Swarm: active
 NodeID: wam5ww8oxa55v6bhka40yeay1
 Is Manager: false
 Node Address: 10.1.2.7
 Manager Addresses:
  10.0.0.203:2377
  • 在manager节点安装swarm可视化界面 , 用于查看集群中各个容器的状态
$ docker pull dockersamples/visualizer   
$ docker run -it -d -p 8080:8080 --name swarm-status -v /var/run/docker.sock:/var/run/docker.sock dockersamples/visualizer  

网络初始化

预先创建一个overlay网络(跨主机的网络管理 )

docker network create --driver overlay net_swarm_test

overlay网络的网段即在docker info的信息中显示的Default Address Pool这一值, 表示swarm所创建的容器ip网段, 默认值为10.0.0.0/8, 如果我们的宿主机也是这个网段, 那么在Swarm集群中的服务想访问overly网络之外的地址的时候, 就会出不去, 被overlay的网关转发后无法转发到宿主机的网关上, 此时需要修改overlay网络的网段, 添加参数--subnet, 例如:--subnet=192.168.0.0/16

执行docker network ls就可以看到创建的该网络

Compose file v3 参考

在Swarm集群上启动服务可以使用两种方式

  1. 使用docker service create 命令, 类似于docker run, 以纯命令行的形式启动服务
  2. 使用compose yaml文件编排服务, 然后使用 docker stack deploy -c swarm-compose.yaml servicePrefixName 启动服务, 类似与docker-compose

推荐使用第2种, 具备更好的维护性

Swarm基于Compose yaml 文件的编排需要使用Compose v3的配置

示例如下:

version: "3.2"  
services:  
  nginx:                               # swarm service name中下划线的后面部分
   #build: ./web/nginx                 # 不执行build, 在swarm集群中,通过集群分发应用的镜像,可以将镜像推送到Registry仓库 
    image: nginx:1.13.1                # 指定应用所需的镜像,服务器需要预先准备导入该镜像  
    ports:                             # 当endpoint_mode 为vip时才能映射service端口到宿主机上
      - "18080:80"
    networks:
      - overlay                        # 这里的overlay可以理解为是一个变量名, 指向下面定义的overlay网络
    volumes:                           # 创建容器的服务器上需要有volumes参数所指定的文件及文件夹,swarm不会自己去创建
      - ./logs/nginx:/var/log/nginx
      # - ./web/nginx/nginx.conf:/etc/nginx/nginx.conf   # 对于配置, 推荐做在镜像中
    deploy:
     # endpoint_mode: dnsrr                 # 默认是vip, 3.2开始支持这个参数
      replicas: 2                           # replicas模式,指定集群中实例的个数
     #mode: global                          # global模式
      update_config:                        # 这个选项用于告诉 Compose 使用怎样的方式升级, 以及升级失败后怎样回滚原来的服务。
        parallelism: 1                      # parallelism: 服务中多个容器同时更新
        delay: 10s                          # delay: 设置每组容器更新之间的延迟时间
        failure_action: continue            # failure_action: 设置更新失败时的动作, 可选值有 continue 与 pause (默认是:pause)。
        monitor: 60s                        # monitor: 每次任务更新时,监视是否故障的持续时间 (ns|us|ms|s|m|h) (默认:0s)。
        max_failure_ratio: 0.3              # max_failure_ratio: 更新期间容忍的失败率	 
        order: start-first                  # 更新期间的操作顺序 stop-first(旧的task在新的启动前先停止), start-first(启动新的,   务, 并且运行的任务将短暂地重叠), default(stop-first)   V3.4支持   
      restart_policy:	                        # 重启策略
        condition: on-failure                   #  重启条件none, on-failure, any (default: any).
        delay: 5s                               # 在重新启动尝试之间等待多长时间, 指定为持续时间(默认值:0)
        #max_attempts: 3                        # 设置最大的重启尝试次数, 默认是永不放弃
        window: 120s                            # 在决定重新启动是否成功之前要等待多长时间, 默认是立刻判断, 有些容器启动时间比较, , 指定一个“窗口期”非常重要
      placement:                              
        constraints: [node.role == manager]     # Constraint 过滤器是绑定到节点的键值对(https://docs.docker.com/engine/reference/commandline/service_create/#specify-service-constraints-constraint)
    #environment:
    #  - "affinity:image==redis"                # 调度已经拉取的'redis'镜像的容器,也可以通过容器环境变量指定容器所在的位置(https://docs.docker.com/compose/swarm/#manual-scheduling) 


networks:
  overlay:      # 定义网络, overlay, 被上面的overlay引用                           
    external:         
      name: ctuswarm_overlay                     # 使用外部名为ctuswarm_overlay的overlay网络

官方文档, 其他参考

当我们使用上述这一份yaml文件创建service时, 使用命令 docker stack deploy -c swarm-compose.yaml swarm, 会创建一个名为swarm_nginx的服务, 命令中的swarm和yaml中的nginx共同组成了service的名字, 并映射了18080端口到宿主机上, 基于docker swarm的routing mesh可以通过任意一台机器的ip:port访问这个nginx应用
容器(服务)之间的访问可以通过服务名称:端口直接访问, 例如:nginx需要转发某些请求到其他web服务上可以在nginx.conf中直接配置service_name:port进行转发, service_name的解析即 vip与dnsrr 这两种网络模式的实现.

部署JStrom集群

定义JStrom service的yaml文件, 保存为jstorm-docker-compose.yml文件

version: '3.2'
services:
    nimbus:
        image: timestatic/jstorm:2.2.1
        deploy:
            replicas: 1
            update_config:
              parallelism: 1
              delay: 10s
              failure_action: continue
              monitor: 60s
              max_failure_ratio: 0.3
            restart_policy:
              condition: on-failure
              delay: 5s
              window: 60s
        volumes:
             - /mydata/jstorm/nimbus/log:/opt/jstorm/logs  # mkdir -p /mydata/jstorm/nimbus/log
        environment:
           - storm_zookeeper_servers=10.1.2.7 # zookeeper address
           - nimbus_seeds=jstormswarm_nimbus  # nimbus service name
        networks:
          - default
        command: jstorm nimbus

    supervisor:
        image: timestatic/jstorm:2.2.1
        deploy:
            replicas: 1
            update_config:
              parallelism: 1
              delay: 10s
              failure_action: continue
              monitor: 60s
              max_failure_ratio: 0.3
            restart_policy:
              condition: on-failure
              delay: 5s
              window: 60s
        volumes:
             - /mydata/jstorm/supervisor/log:/opt/jstorm/logs  # mkdir -p /mydata/jstorm/supervisor/log
        environment:
           - storm_zookeeper_servers=10.1.2.7 # zookeeper address
           - nimbus_seeds=jstormswarm_nimbus  # nimbus service name
        networks:
          - default
        command: jstorm supervisor

    ui:
        image: timestatic/jstorm-ui:2.2.1
        ports:
          - "8080:8080"
        deploy:
            replicas: 1
            restart_policy:
              condition: on-failure
              delay: 5s
              window: 60s
        environment:
           - storm_zookeeper_servers=10.1.2.7 # zookeeper address
           - nimbus_seeds=jstormswarm_nimbus  # nimbus service name
        volumes:
             - /mydata/jstorm/ui/log:/usr/local/tomcat/logs  # mkdir -p /mydata/jstorm/ui/log
        networks:
          - default

networks:
        default:
          external:
            name: net_swarm_test

执行启动命令docker stack deploy -c jstorm-docker-compose.yml jstormswarm后即创建如下三个服务:

[admin@localhost ~]$ docker service ls
ID                  NAME                     MODE                REPLICAS            IMAGE                                               PORTS
8mkllcx4pmtu        jstormswarm_nimbus       replicated          1/1                 timestatic/jstorm:2.2.1   
qtv0d1mujyww        jstormswarm_supervisor   replicated          1/1                 timestatic/jstorm:2.2.1   
quftb2thxn5p        jstormswarm_ui           replicated          1/1                 timestatic/jstorm-ui:2.2.1                         *:8080->8080/tcp

可以使用docker service scale对Nimbus或Supervisor进行扩容, 例如: docker service scale jstormswarm_supervisor=3, 将supervisor的实例数变为3个

 

提交JStorm计算任务

启动

一个JStorm任务(即Topology)的内容通常都是打在一个Jar包中, 通过jstorm jar xx.jar xxx.MainClass topologyName命令提交到集群中. 同时, 这个任务可能会更新、删除. 因此, 我们可以将这个任务jar包作为一个Docker镜像, 基础镜像即JStorm镜像, 提交任务即启动这个容器同时连接到相应的Nimbus和ZooKeeper地址, 当这个任务提交完之后, 这个容器的使命也就可以认为是结束了.

将一个Topology定义为Swarm的service, 如下, 保存为demo.yaml文件:

version: "3.2"
services:
    demo:
#      image: timestatic/jstorm:2.2.1
      image: jstrom_demo:1.0
      networks:
        - default
      environment:
        - storm_zookeeper_servers=10.1.2.7
        - nimbus_seeds=jstormswarm_nimbus
#      volumes:
#        - ./jstorm-demo-jar-with-dependencies.jar:/jstorm-demo.jar 
      deploy:
          replicas: 1
          update_config:
              parallelism: 1
              delay: 10s
              failure_action: continue
              monitor: 60s
              max_failure_ratio: 0.3
          restart_policy:
              condition: on-failure
              delay: 5s
              window: 60s
      command: jstorm jar /jstorm-demo.jar com.github.timestatic.jstormdemo.Application wordCountDemo 
networks:
  default:
    external:
          name: net_swarm

启动docker stack deploy -c demo.yaml jstorm, 这样其实就启动了一个只有一个容器的service, 而在容器的启动过程中即提交了Topology, 提交完之后, 即使这个容器挂掉也没有关系. 当然, 这个容器本身不作为集群中的一个节点.

任务更新

对于任务的更新, 需要先到任意一个JStorm容器中kill掉原先的Topology, 然后build一个新的任务镜像, 基于Swarm的更新机制, 可以使用docker service update --image imageName:versionNo serviceName 命令进行更新.

当然也可以直接volume任务jar包然后直接重启

 

总结

Swarm给我们带来了什么?

因为Nimbus和Supervisor均为无状态进程, 所以在扩容缩容上非常方便, 基于Swarm service的重启机制可以在故障时进行Nimbus和Supervisor的自动重启, 对于任务的提交和更新, 由于JStorm的本身机制实际上大同小异.

你可能感兴趣的:(Docker,JStorm,docker,swarm,jstorm)