現在是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 nimbus
和jstorm 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集羣上啓動服務可以使用兩種方式
- 使用docker service create 命令, 類似於docker run, 以純命令行的形式啓動服務
- 使用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的本身機制實際上大同小異.