基於Docker Swarm的JStorm集羣實踐

現在是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,比如下面示例:
[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的本身機制實際上大同小異.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章