Docker叢集輕鬆部署ApacheStorm

易立發表於2016-05-18

14634993989550

Apache Storm是一個非常常用的實時流計算框架。最近有客戶來諮詢如何在Docker中執行Apache Storm的問題。我之前讀過一篇文章介紹Apache Storm在Docker環境的部署,The Joy Of Deploying Apache Storm On Docker Swarm。文章寫的很好,但是整個過程需要從手工構建Docker叢集環境開始,再一步步把Storm配置起來,雖然作者提到整個過程是”a real joy”,估計絕大多數使用者依然會望而生畏。

利用Docker Compose模板,我們可以在本地單機Docker環境快速地搭建一個Apache Storm叢集,進行應用開發測試。

基於阿里雲容器服務,我們可以在公共雲上輕鬆搭建一個分散式Apache Storm叢集,進一步簡化部署複雜性並提供生產級別的高可用性,讓您感受到穿越風暴(storm)的快樂。

除此之外,本文還會介紹Docker Compose開發和雲端部署經驗,以及容器部署的排程約束。

Storm示例部署架構

本文的Storm示例部署架構如下:

14634983956608

其中包含如下容器

  • zookeeper:Apache Zookeeper三節點部署
  • nimbus:Storm Nimbus
  • ui:Storm UI
  • supervisor:Storm Supervisor (一個或多個)
  • topology:Topology部署工具,其中示例應用基於“官方示例storm-starter”程式碼構建

這裡使用到的Storm Docker映象”registry.aliyuncs.com/denverdino/baqend-storm”,來自於Baqend Techt提供的baqend/storm映象。您也可以利用它的Github專案自己構建。

本文的Docker Compose文件,和示例應用等可以從https://github.com/denverdino/docker-storm獲得。

利用Docker Compose在本地開發測試

如果您希望在直接雲端部署驗證,可以直接跳過這一節。

在Mac/Windows上,開發者只需利用Docker Toolbox就可安裝配置本地的Docker環境。它已經包括了Docker和Docker Compose等工具可以方便地進行容器映象和編排模板開發。

首先我們下載程式碼

git clone https://github.com/denverdino/docker-storm.git
cd docker-swarm/local

在Docker Compose模板“docker-compose.yml”中,描述了上圖所示的Storm應用架構。其具體內容如下:

version: `2`
services:
  zookeeper1:
    image: registry.aliyuncs.com/denverdino/zookeeper:3.4.8
    container_name: zk1.cloud
    environment:
      - SERVER_ID=1
      - ADDITIONAL_ZOOKEEPER_1=server.1=0.0.0.0:2888:3888
      - ADDITIONAL_ZOOKEEPER_2=server.2=zk2.cloud:2888:3888 
      - ADDITIONAL_ZOOKEEPER_3=server.3=zk3.cloud:2888:3888
  zookeeper2:
    image: registry.aliyuncs.com/denverdino/zookeeper:3.4.8
    container_name: zk2.cloud
    environment:
      - SERVER_ID=2
      - ADDITIONAL_ZOOKEEPER_1=server.1=zk1.cloud:2888:3888
      - ADDITIONAL_ZOOKEEPER_2=server.2=0.0.0.0:2888:3888 
      - ADDITIONAL_ZOOKEEPER_3=server.3=zk3.cloud:2888:3888
  zookeeper3:
    image: registry.aliyuncs.com/denverdino/zookeeper:3.4.8
    container_name: zk3.cloud
    environment:
      - SERVER_ID=3
      - ADDITIONAL_ZOOKEEPER_1=server.1=zk1.cloud:2888:3888
      - ADDITIONAL_ZOOKEEPER_2=server.2=zk2.cloud:2888:3888 
      - ADDITIONAL_ZOOKEEPER_3=server.3=0.0.0.0:2888:3888
  ui:
    image: registry.aliyuncs.com/denverdino/baqend-storm:1.0.0
    command: ui -c nimbus.host=nimbus
    environment:
      - STORM_ZOOKEEPER_SERVERS=zk1.cloud,zk2.cloud,zk3.cloud
    restart: always
    container_name: ui
    ports:
      - 8080:8080
    depends_on:
      - nimbus
  nimbus:
    image: registry.aliyuncs.com/denverdino/baqend-storm:1.0.0
    command: nimbus -c nimbus.host=nimbus
    restart: always
    environment:
      - STORM_ZOOKEEPER_SERVERS=zk1.cloud,zk2.cloud,zk3.cloud
    container_name: nimbus
    ports:
      - 6627:6627
  supervisor:
    image: registry.aliyuncs.com/denverdino/baqend-storm:1.0.0
    command: supervisor -c nimbus.host=nimbus -c supervisor.slots.ports=[6700,6701,6702,6703]
    restart: always
    environment:
      - affinity:role!=supervisor
      - STORM_ZOOKEEPER_SERVERS=zk1.cloud,zk2.cloud,zk3.cloud
    depends_on:
      - nimbus
  topology:
    build: ../storm-starter
    command: -c nimbus.host=nimbus jar /topology.jar org.apache.storm.starter.RollingTopWords production-topology remote
    depends_on:
      - nimbus
networks:
  default:
    external: 
      name: test-storm

首先我們來構建所需的topology容器映象,它會利用”../storm-starter”中定義的Dockerfile來構建映象。如果您需要測試自己的應用,只需要更新其中的jar檔案即可。

我們可以執行下列命令構建測試映象:

docker-compose build

為了充分利用Docker網路的能力,我們需要為 Apache Storm 應用建立一個自定義的bridge網路

docker network create test-storm

提示: 關於容器網路的介紹,請參見之前的博文學習Docker容器網路模型 – 搭建分散式Zookeeper叢集

現在我們可以用下面的命令來一鍵部署Storm應用

docker-compose up -d

部署完畢,檢查Storm應用狀態

docker-compose ps

當UI容器啟動後,我們可以利用下面的URL在瀏覽器中來訪問Storm UI, http://192.168.99.100:8080/index.html

注意: 如果您本地Docker環境配置與本文不一致,相應的Docker虛機IP地址可能有所不同。您可以通過docker-machine ip default來獲取相應的IP地址,並替換上面URL中的”192.168.99.100″

14634105920049

利用如下命令,您可以伸縮supervisor的數量,比如伸縮到3個例項

docker-compose scale supervisor=3

您也許會發現Web介面中並沒有執行中的topology。我們不是已經在Compose模板中定義“topology”容器依賴於“nimbus”服務容器嗎?這個原因是Docker Compose只能保證容器的啟動順序,但是無法確保所依賴容器中的應用已經完全啟動並可以被正常訪問了。

所以,這個我們需要執行下面的命令來再次啟動“topolgoy”服務應用來提交拓撲

docker-compose start topology

稍後重新整理Storm UI,我們可以發現Storm應用已經部署成功了

在阿里雲上一鍵部署Apache Storm

在本地Docker單機環境只能做些簡單的開發測試。而利用阿里雲容器服務,我們可以在一組虛擬機器部署和執行分散式Apache Storm。

為了確保應用的可用性,我們需要將Zookeeper的三個容器例項部署到不同的ECS虛擬機器上。同理,也需要將supervisor的容器例項部署到不同的ECS虛擬機器上。

注意:

  • 在這個試驗中我們需要建立一個最少包含3個ECS節點的叢集。如果叢集中的節點數量不足三個,會導致部署失敗。
  • 容器服務支援按量付費的ECS例項,可以按小時付費,對20個節點以下叢集不收額外的管理費用。測試之後您可以隨時釋放ECS例項或Docker叢集。

首先,我們對之前的Docker Compose模板做一些調整,來適合雲端部署。更新版本如下:

version: `2`
services:
  zookeeper1:
    image: registry.aliyuncs.com/denverdino/zookeeper:3.4.8
    container_name: zk1.cloud
    environment:
      - SERVER_ID=1
      - ADDITIONAL_ZOOKEEPER_1=server.1=0.0.0.0:2888:3888
      - ADDITIONAL_ZOOKEEPER_2=server.2=zk2.cloud:2888:3888 
      - ADDITIONAL_ZOOKEEPER_3=server.3=zk3.cloud:2888:3888
      - affinity:role!=zookeeper
    labels:
      role: zookeeper
  zookeeper2:
    image: registry.aliyuncs.com/denverdino/zookeeper:3.4.8
    container_name: zk2.cloud
    environment:
      - SERVER_ID=2
      - ADDITIONAL_ZOOKEEPER_1=server.1=zk1.cloud:2888:3888
      - ADDITIONAL_ZOOKEEPER_2=server.2=0.0.0.0:2888:3888 
      - ADDITIONAL_ZOOKEEPER_3=server.3=zk3.cloud:2888:3888
      - affinity:role!=zookeeper
    labels:
      role: zookeeper
  zookeeper3:
    image: registry.aliyuncs.com/denverdino/zookeeper:3.4.8
    container_name: zk3.cloud
    environment:
      - SERVER_ID=3
      - ADDITIONAL_ZOOKEEPER_1=server.1=zk1.cloud:2888:3888
      - ADDITIONAL_ZOOKEEPER_2=server.2=zk2.cloud:2888:3888 
      - ADDITIONAL_ZOOKEEPER_3=server.3=0.0.0.0:2888:3888
      - affinity:role!=zookeeper
    labels:
      role: zookeeper
  ui:
    image: registry.aliyuncs.com/denverdino/baqend-storm:1.0.0
    command: ui -c nimbus.host=nimbus
    environment:
      - STORM_ZOOKEEPER_SERVERS=zk1.cloud,zk2.cloud,zk3.cloud
    restart: always
    container_name: ui
    labels: 
      aliyun.routing.port_8080: storm-ui
    ports:
      - 8080:8080
    depends_on:
      - nimbus
  nimbus:
    image: registry.aliyuncs.com/denverdino/baqend-storm:1.0.0
    command: nimbus -c nimbus.host=nimbus
    restart: always
    environment:
      - STORM_ZOOKEEPER_SERVERS=zk1.cloud,zk2.cloud,zk3.cloud
    container_name: nimbus
    ports:
      - 6627:6627
    labels: 
      aliyun.probe.url: tcp://container:6627
      aliyun.probe.initial_delay_seconds: "10"
  supervisor:
    image: registry.aliyuncs.com/denverdino/baqend-storm:1.0.0
    command: supervisor -c nimbus.host=nimbus -c supervisor.slots.ports=[6700,6701,6702,6703]
    restart: always
    environment:
      - affinity:role!=supervisor
      - STORM_ZOOKEEPER_SERVERS=zk1.cloud,zk2.cloud,zk3.cloud
    labels:
      role: supervisor
      aliyun.scale: "3"
    depends_on:
      - nimbus
  topology:
    image: registry.aliyuncs.com/denverdino/storm-starter:1.0.0
    command: -c nimbus.host=nimbus jar /topology.jar org.apache.storm.starter.RollingTopWords production-topology remote
    labels:
      aliyun.depends: nimbus
    depends_on:
      - nimbus

這個模板只是針對本地的compose模板做了幾個簡單的調整和增強。其中變動的部分如下:

  • 對“topology”服務,我們指明瞭預先構建的Docker映象而非像開發環境中那樣動態構建映象
  • 容器服務預設為每個叢集建立了跨節點的容器網路,無需在compose模板中提供網路描述

除此之外,模板中利用了下列阿里雲擴充套件來簡化容器部署

為了保證“zookeeper”服務的高可用性,我們需要描述服務的“anti-affinity”(反親和性),也就是不允許任意兩個zookeeper容器例項部署在同一個VM上。因為容器服務提供了相容“Docker Swarm”的服務部署約束策略,我們可以利用容器的標籤過濾器(label filter)來支援這個場景

為每個“zookeeper”服務,定義 role: zookeeper 標籤,並用環境變數定義部署約束 affinity:role!=zookeeper。這樣叢集不會把zookeeper容器排程到已經擁有role: zookeeper 標籤的節點上

同樣,“supervisor”服務容器也利用了反親和性約束將容器部署到不同節點上。而且”supervisor”服務的aliyun.scale: "3" 指明瞭容器的伸縮數量,所以系統會自動部署3個Supervisor容器到3個不同的ECS節點上。

另外,上節談到了docker-compose無法解決“topology”和“nimbus”容器之間的依賴次序,而容器服務提供了一個更加優雅的方案來保證容器之間的正確依賴

  • 為”nimbus”容器新增標籤aliyun.probe.url: tcp://container:6627 可以通過tcp埠6627的可用性來判斷容器的健康狀態
  • 為”topology”容器新增標籤aliyun.depends: nimbus ,讓其等待”nimbus”進入健康狀態之後再啟動。這就確保了容器應用之間的正確的時序依賴。

我們還利用aliyun.routing.port_8080: storm-ui標籤,這樣可以為“ui”服務提供虛擬域名“storm-ui”來直接訪問Storm UI,而無需關係Storm UI容器具體部署在叢集中的哪一個節點。

通過容器服務控制檯,基於以上面的模板之間部署一個容器應用,幾分鐘以後一個分散式Storm應用已經在雲端執行起來!我們點選“ui”服務端點,就可以開啟Storm UI。

14634939887568

檢查應用的容器列表,這裡我們可以發現不同的zookeeper容器被部署到不同的ESC例項上。

14634941008040

Topoloy也已經成功提交,這個過程是不是很簡單 ?

自定義節點排程

上文中,利用簡單的排程約束可以讓系統在叢集中自動挑選合適的節點部署容器。但在真實的場景中,很多使用者希望能夠把容器部署到指定的一個或多個節點之上。這時應該如何做呢?

對於這類需求,我們可以利用節點過濾器(Node filters)來解決。

當節點加入叢集之後,容器服務會為每個節點提供一些預定義的標籤。

14634964932176

此外,使用者還可以為節點編輯自定義標籤。在控制檯叢集列表頁面,選擇叢集並點選更多操作,我們可以看見如下操作選單:

14634958603871

點選“使用者標籤管理”,就可以方便地編輯節點標籤

14635314658361

提示: 與Docker的社群實現不同,在容器服務中為指定Docker節點新增標籤,無需編輯配置檔案並重啟Docker Engine即可生效。

下面的示例,我們希望把三個zookeeper節點依次部署到第一、第二和第三號節點。另外把“ui”和“nimbus”容器部署到包含“server:manager”標籤的節點上。這時候Compose模板如下:

version: `2`
services:
  zookeeper1:
    image: registry.aliyuncs.com/denverdino/zookeeper:3.4.8
    container_name: zk1.cloud
    environment:
      - SERVER_ID=1
      - ADDITIONAL_ZOOKEEPER_1=server.1=0.0.0.0:2888:3888
      - ADDITIONAL_ZOOKEEPER_2=server.2=zk2.cloud:2888:3888 
      - ADDITIONAL_ZOOKEEPER_3=server.3=zk3.cloud:2888:3888
      - constraint:aliyun.node_index==1
  zookeeper2:
    image: registry.aliyuncs.com/denverdino/zookeeper:3.4.8
    container_name: zk2.cloud
    environment:
      - SERVER_ID=2
      - ADDITIONAL_ZOOKEEPER_1=server.1=zk1.cloud:2888:3888
      - ADDITIONAL_ZOOKEEPER_2=server.2=0.0.0.0:2888:3888 
      - ADDITIONAL_ZOOKEEPER_3=server.3=zk3.cloud:2888:3888
      - constraint:aliyun.node_index==2
  zookeeper3:
    image: registry.aliyuncs.com/denverdino/zookeeper:3.4.8
    container_name: zk3.cloud
    environment:
      - SERVER_ID=3
      - ADDITIONAL_ZOOKEEPER_1=server.1=zk1.cloud:2888:3888
      - ADDITIONAL_ZOOKEEPER_2=server.2=zk2.cloud:2888:3888 
      - ADDITIONAL_ZOOKEEPER_3=server.3=0.0.0.0:2888:3888
      - constraint:aliyun.node_index==3
  ui:
    image: registry.aliyuncs.com/denverdino/baqend-storm:1.0.0
    command: ui -c nimbus.host=nimbus
    environment:
      - STORM_ZOOKEEPER_SERVERS=zk1.cloud,zk2.cloud,zk3.cloud
      - constraint:server==manager
    restart: always
    container_name: ui
    labels: 
      aliyun.routing.port_8080: storm-ui
    ports:
      - 8080:8080
    depends_on:
      - nimbus
  nimbus:
    image: registry.aliyuncs.com/denverdino/baqend-storm:1.0.0
    command: nimbus -c nimbus.host=nimbus
    restart: always
    environment:
      - STORM_ZOOKEEPER_SERVERS=zk1.cloud,zk2.cloud,zk3.cloud
      - constraint:server==manager
    container_name: nimbus
    ports:
      - 6627:6627
    labels: 
      aliyun.probe.url: tcp://container:6627
      aliyun.probe.initial_delay_seconds: "10"
  supervisor:
    image: registry.aliyuncs.com/denverdino/baqend-storm:1.0.0
    command: supervisor -c nimbus.host=nimbus -c supervisor.slots.ports=[6700,6701,6702,6703]
    restart: always
    environment:
      - affinity:role!=supervisor
      - STORM_ZOOKEEPER_SERVERS=zk1.cloud,zk2.cloud,zk3.cloud
    labels:
      role: supervisor
      aliyun.scale: "3"
    depends_on:
      - nimbus
  topology:
    image: registry.aliyuncs.com/denverdino/storm-starter:1.0.0
    command: -c nimbus.host=nimbus jar /topology.jar org.apache.storm.starter.RollingTopWords production-topology remote
    labels:
      aliyun.depends: nimbus
    depends_on:
      - nimbus

我們可以非常方便地利用下面約束來選擇包含指定標籤的節點

  • zookeeper1:constraint:aliyun.node_index==1 等等
  • nimbus,ui:constraint:server==manager

當Storm部署成功後,再檢視相應的容器部署。它們是不是完全依照您的指令來部署到指定節點上了呢?:-)

針對節點的容器排程約束,不但可以讓你更好地對叢集內節點進行分類管理,還非常適合使用host網路的應用場景。

如果您希望進一步瞭解容器服務支援的排程和編排能力,您可以檢視參考文件

總結

利用容器和容器編排技術可以大大簡化大資料框架的部署複雜性,提升部署效率。可以讓資料工程師輕鬆在本地和雲端搭建自己的計算環境,進行開發、測試和生產部署。

利用阿里雲容器服務可以極大簡化Docker叢集的建立和管理。容器服務不但完全擁抱社群生態,讓本地開發的Docker映象和Compose模板平滑地遷移到雲端;更在編排、排程等方面提供了增強,讓您的應用部署運維更加簡單,並充分利用阿里雲的能力。工程師可以在Compose模板中結合靈活的容器部署約束,隨心控制容器部署位置,方便地利用Docker技術一鍵搭建完備的分散式應用並滿足其SLA。

再次感謝Baqend Techt提供的baqend/storm映象

想了解更多容器服務的內容,請點選 https://www.aliyun.com/product/containerservice


相關文章