欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于Docker Swarm的JStorm集群实践

程序员文章站 2024-03-01 13:25:34
...

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

本文项目源码

 

基本概念

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,比如下面示例:
[[email protected] ~]$ 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后即创建如下三个服务:

[[email protected] ~]$ 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的本身机制实际上大同小异.