Oozie任務調度阻塞及內存優化方法

目 錄

0 引 言

1 Oozie的任務調度原理

 1.1 Oozie總體架構

 1.2 Oozie的執行模型(Action原理)

2 Yarn的資源調度原理

2.1 Yarn的相關定義

2.2 Yarn的運行機制

2.3 Yarn的資源調度原理

3 排查與優化方法

3.1 問題原因描述

3.2 優化方案

3.3 Yarn資源隊列設置

3.4 Oozie的調度配置

3.5 實驗結果

4 小 結


0 引 言

Oozie在執行過程中如果有多個fork產生並行任務的時候,這時會佔用很多內存,如果機器的內存不夠則會產生調度阻塞。或者是同時提交了多個Oozie任務,也會產生調度阻塞。本文針對上述問題,首先分析研究了Oozie的工作原理及Yarn的運行機制,並基於這兩種機制原理的基礎上,定位問題所在,實現了上述問題的優化,給出了具體的解決方案,最後對結果進行了驗證。

1 Oozie的任務調度原理

 1.1 Oozie總體架構

 Oozie的總體架構圖如下圖所示:

圖1:Oozie整體架構圖

      由架構圖可以看出Oozie主要包含以下幾個部分:

    (1)Oozie的客戶端:可以通過命令行進行提交任務,任務提交的入口,主要進行一些命令行的操作,包括啓動一個任務、運行任務、終止任務、恢復任務等操作。Oozie提供了RESTful API接口來接受用戶的提交請求(提交工作流作業)。其實,在命令行使用oozie -job xxx命令提交作業,本質上也是發HTTP請求向OozieServer提交作業。

    (2)Oozie Server服務器:Oozie Server服務器提供了對應的執行引擎,包括了工作流執行引擎、協調器引擎、bundle引擎。Oozie Server只負責查詢這些Action的執行狀態結果,從而降低了Oozie Server的負載。具體的作業不是在Oozie Server中執行, Oozie Server只對workflow負責,而不是對action負責,workflow中的action是在具體集羣中某個節點中運行的。真正的作業是交給yarn去執行,yarn將具體的action分配給具有充足資源的節點,讓該節點去運行。

    (3)元數據庫:定義了action執行的一些狀態信息,一般存放在Mysql數據庫中

    (4)hadoop:Oozie是依賴於HDFS進行調度的,際作業的具體執行是由Hadoop執行的,因而Oozie的作業必須放到hadoop集羣上。Oozie通過回調和輪詢的方式從hadoop集羣中獲取作業執行結果的。其中,回調是爲了降低開銷,輪詢是爲了保證可靠性。用戶在HDFS上部署好作業(MR作業),然後向Oozie提交Workflow,Oozie以異步方式將作業(MR作業)提交給Hadoop。這也是爲什麼當調用Oozie 的RESTful接口提交作業之後能立即返回一個jobId的原因,用戶程序不必等待作業執行完成(因爲有些大作業可能會執行很久(幾個小時甚至幾天))。Oozie在後臺以異步方式,再將workflow對應的Action提交給hadoop執行。(這點可類比kafka,Hbase等,kafka發送端發送數據的時候也是這種異步的策略)

 1.2 Oozie的執行模型(Action原理)

  oozie的執行模型原理圖如下圖所示:

(1) Oozie提供了RESTful API接口來接受用戶的提交請求(提交工作流作業)。當用戶在客戶端命令行使用oozie -job xxx命令提交作業,本質上也是發HTTP請求向OozieServer提交作業。

(2)OozieServer收到提交的作業命令後,由工作流引擎負責workflow的執行以及狀態的轉換。比如,從一個Action執行到下一個Action,或者workflow狀態由Suspend變成KILLED。Oozie以異步方式將作業(MR作業)提交給Hadoop。

注:這也是爲什麼當調用Oozie 的RESTful接口提交作業之後能立即返回一個jobId的原因,用戶程序不必等待作業執

行完成(因爲有些大作業可能會執行很久(幾個小時甚至幾天))。Oozie在後臺以異步方式,再將workflow對應的

Action提交給hadoop執行

(3) Oozie通過 launcher job 運行某個具體的Action。launcher job是一個 map-only的MR作業,該作業在集羣中的執行也是分佈式的。這裏的launcher需要向yarn集羣申請AM運行,同時真正的任務運行也需要先申請AM

   launcher的作用:

  •    1)監控和運行具體的action
  •    2) 一個action對應一個launcher作業(action對應一個job,一個job啓動一個oozie-launcher
  •    3)Launcher job 負責調用對應任務的資源,調用對應的 CLI API 啓動 Hadoop、Hive 或者 Pig 作業等等。Launcher job啓動的僅是Map的mr作業,這個 Map 作業知道其所對應的工作流中某一個 Action 應該執行的操作,然後實際上執行 Action 操作的 Hadoop 作業將會被啓動,你可以認爲這些隨後啓動的作業是 Launcher 作業的子作業。
  •  4)Oozie launcher任務 的本質就是啓動任務所需要的客戶端,如hive任務,啓動hive客戶端,用於提交任務。
  •  5)除了MR Action外,launcher作業的生命週期和具體任務的生命週期一致。(這點可以在yarn上進行觀察)

 由launcher任務監控運行具體任務的優勢:減少了Oozie Server服務器的壓力,使Oozie Server服務器穩定運行。

    整個過程源碼簡單分析如下:

    Oozie執行Action時,即ActionExecutor(最主要的子類是JavaActionExecutor,hive、spark等action都是這個類的子類),JavaActionExecutor首先會提交一個LauncherMapper(map任務)到yarn,其中會執行LauncherMain(具體的action是其子類,比如JavaMain、SparkMain等),spark任務會執行SparkMain,在SparkMain中會調用org.apache.spark.deploy.SparkSubmit來提交任務。其實訴我的map任務就是識別你是什麼樣的任務(hive,shell,spark等),並通過該任務來啓動任務所需要的環境來提交任務。提供了提交任務的接口(如hive任務,啓動hive客戶端或beeline等)。

2 Yarn的資源調度原理

2.1 Yarn的相關定義

    Yarn 是一個資源調度平臺,負責爲運算程序提供服務器運算資源,相當於一個分佈式的操作系統,而各類運算程序則相當於運行於操作系統之上的應用程序。

   YARN 主要由 ResourceManager、NodeManager、ApplicationMaster 和 Container 等組件構成。

   具體各個組件之間的含義如下圖所示:

2.2 Yarn的運行機制

   YARN 的運行機制如下圖所示:

   yarn的工作原理:

工作機制詳解
(0)Mr 程序提交到客戶端所在的節點。
(1)Yarnrunner 向 Resourcemanager 申請一個 Application。
(2)rm將該應用程序的資源路徑返回給 yarnrunner。
(3)該程序將運行所需資源提交到 HDFS 上。
(4)程序資源提交完畢後,申請運行 mrAppMaster。
(5)RM 將用戶的請求初始化成一個 task。
(6)其中一個 NodeManager 領取到 task 任務。
(7)該 NodeManager 創建容器 Container,併產生 MRAppmaster。
(8)Container 從 HDFS 上拷貝資源到本地。
(9)MRAppmaster 向 RM 申請運行 maptask 資源。
(10)RM 將運行 maptask 任務分配給另外兩個 NodeManager,另兩個 NodeManager 分
別領取任務並創建容器。
(11)MR 向兩個接收到任務的 NodeManager 發送程序啓動腳本,這兩個 NodeManager
分別啓動 maptask,maptask 對數據分區排序。
(12)MrAppMaster 等待所有 maptask 運行完畢後,向 RM 申請容器,運行 reduce task。
(13)reduce task 向 maptask 獲取相應分區的數據。
(14)程序運行完畢後,MR 會向 RM 申請註銷自己。

以mr作業提交的資源調度進一步說明如下:

 (1)作業提交

  •          第 0 步:client 調用 job.waitForCompletion 方法,向整個集羣提交 MapReduce 作業。
  •          第 1 步:client 向 RM 申請一個作業 id。
  •          第 2 步:RM 給 client 返回該 job 資源的提交路徑和作業 id。
  •          第 3 步:client 提交 jar 包、切片信息和配置文件到指定的資源提交路徑。
  •          第 4 步:client 提交完資源後,向 RM 申請運行 MrAppMaster。

(2)作業初始化

  •         第 5 步:當 RM 收到 client 的請求後,將該 job 添加到容量調度器中。
  •         第 6 步:某一個空閒的 NM 領取到該 job。
  •         第 7 步:該 NM 創建 Container,併產生 MRAppmaster。
  •         第 8 步:下載 client 提交的資源到本地。

(3)任務分配

  •         第 9 步:MrAppMaster 向 RM 申請運行多個 maptask 任務資源。
  •         第 10 步:RM 將運行 maptask 任務分配給另外兩個 NodeManager,另兩個 NodeManager分別領取任務並創建容器。

(4)任務運行

  •         第 11 步:MR 向兩個接收到任務的 NodeManager 發送程序啓動腳本,這兩個NodeManager 分別啓動 maptask,maptask 對數據分區排序。
  •        第12步:MrAppMaster等待所有maptask運行完畢後,向RM申請容器,運行reduce task。
  •        第 13 步:reduce task 向 maptask 獲取相應分區的數據。
  •        第 14 步:程序運行完畢後,MR 會向 RM 申請註銷自己。

(5)進度和狀態更新

YARN 中的任務將其進度和狀態(包括 counter)返回給應用管理器, 客戶端每秒(通過

mapreduce.client.progressmonitor.pollinterval 設置)嚮應用管理器請求進度更新, 展示給用戶。

(6)作業完成
        除了嚮應用管理器請求作業進度外, 客戶端每 5 分鐘都會通過調用 waitForCompletion()來檢查作業是否完成。時間間隔可以通過 mapreduce.client.completion.pollinterval 來設置。作業完成之後, 應用管理器和 container 會清理工作狀態。作業的信息會被作業歷史服務器存儲以備之後用戶覈查。

 小結:  YARN只負責程序運行所需要資源的分配回收等調度任務,和MapReduce程序沒有什麼耦合。所以許許多多的其他的程序也可以在YARN上運行,比如說Spark,Storm等。yarn並不清楚用戶提交的程序的運行機制,yarn只提供運算資源的調度(用戶程序向yarn申請資源,yarn就負責分配資源),yarn中的主管角色叫ResourceManager,yarn中具體提供運算資源的角色叫NodeManager,這樣一來,yarn其實就與運行的用戶程序完全解耦,就意味着yarn上可以運行各種類型的分佈式運算程序(mapreduce只是其中的一種),比如mapreduce、storm程序,spark程序等,所以,spark、storm等運算框架都可以整合在yarn上運行,只要他們各自的框架中有符合yarn規範的資源請求機制即可,這樣Yarn就成爲一個通用的資源調度平臺,從此,企業中以前存在的各種運算集羣都可以整合在一個物理集羣上,提高資源利用率,方便數據共享。

2.3 Yarn的資源調度原理

目前,Hadoop作業調度器主要有三種:FIFOCapacity SchedulerFair SchedulerHadoop2.7.2默認的資源調度器是Capacity Scheduler

具體設置詳見:yarn-default.xml文件

<property>
    <description>The class to use as the resource scheduler.</description>
    <name>yarn.resourcemanager.scheduler.class</name>
<value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value>
</property>
  • 1.先進先出調度器(FIFO) 

  • 2.容量調度器(Capacity Scheduler) 

  •  3.公平調度器(Fair Scheduler)

2.4 Yarn資源管理器參數調優詳解

調優的本質是針對container來講的,因爲任務是在Container中運行的,往往是對容器中的核數、內存等進行配置

CPU資源的調度

在yarn中,一個物理core等價於兩個vcore,這個在CDH版本的hadoop中有一個參數控制(不要輕易調)
 (1)yarn.nodemanager.resource.cpu-vcores
    可爲Container分配的容器數量,這個參數在Resource Scheduler爲Container分配資源時可以用到。
如果它的值被設置爲-1,且參數yarn.nodemanager.resource.detect-hardware-capabilities的值爲
true,則不限制yarn使用CPU的數量,也可以設置爲8
(2)yarn.nodemanager.resource.detect-hardware-capabilities
    是否自動檢測節點的CPU和內存 默認爲false
(3)yarn.scheduler.minimum-allocation-vcores               
    爲每個Container分配的最少的vcore,小於這個參數設定的值則會拋出異常 默認爲1
(4)yarn.scheduler.maximum-allocation-vcores           
    爲每個Container分配的最多的vcore,大於這個參數設定的值則會拋出異常 默認爲4(生產調)

Memory資源的調優

(1) NM佔用機器的內存大小,單位MB   可設置爲50*1024 (生產可根據自己配置調)

yarn.nodemanager.resource.memory-mb     


(2)Container包含的最小內存,單位MB  可設置1024(生產可根據自己配置調)

yarn.scheduler.minimum-allocation-mb        

(3)Container包含的最大內存,單位MB  可設置8192(生產調)

yarn.scheduler.maximum-allocation-mb

隨着任務的運行,計算越來越大,任務消耗的資源越來越多,Container包含的內存也應該逐漸增加,在yarn中有一個參數可以設置每次內存加大的額度(yarn.scheduler.maximum-allocation-mb),當內存達到參數設置的最大內存還不夠時,任務會被yarn殺掉.

(4) Container內存不夠用時一次性加多少內存 單位MB 可設置2048

yarn.scheduler.increment-allocation-mb  

(5)容器是否會執行物理內存限制     默認爲True
        執行物理內存限制時,當Container達到最大物理內存限制時,該參數爲True時,會殺死任務

yarn.nodemanager.pmem-check-enabled 

(6)容器是否會執行虛擬內存限制    默認爲True
         執行虛擬限制時,當Container達到最大物理虛擬限制時,該參數爲True時,會殺死任務 

yarn.nodemanager.vmem-check-enabled 

 (7) 使用虛擬內存和物理內存的比值 默認爲2.1

yarn.nodemanager.vmem-pmem-ratio

  虛擬內存調優

  •     vm.swappiness 的值介於0到100之間
  •     swappiness=0的時候表示最大限度使用物理內存,然後纔是swap空間。
  •     swappiness=100的時候表示積極的使用swap分區,並且把內存上的數據及時的搬運到swap空間裏面。        
  •     linux的基本默認設置爲60。
  •     當任務處於高度計算時,不允許慢,允許掛掉後通過各種形式重啓任務,這時候將使用虛擬內存的積極性調低     vm.swappiness 的值可設置較大。當任務在一般計算時,可以慢,不允許掛,這時候將使用虛擬內存的積極性調高 。

 生產上一般允許慢不允許掛,然後vm.swappiness設置爲10。

[root@bigdata3 ~]# cat /proc/sys/vm/swappiness
60
也就是說,你的內存在使用到100-60=40%的時候,就開始會使用虛擬內存。
 
臨時調整(重啓失效):
    [root@bigdata3 ~]# sysctl vm.swappiness=10
    vm.swappiness = 10
    [root@timeserver ~]# cat /proc/sys/vm/swappiness
    10
 
永久調整,先完成臨時調整,再執行以下操作:
    [root@bigdata3 ~]# vi /etc/sysctl.conf
    # Controls the maximum number of shared memory segments, in pages
    在“kernel.shmall = 4294967296”後加上
    “vm.swappiness=10”
 
    [root@bigdata3 ~]# sysctl -p
    vm.swappiness = 10

在Ambarri平臺中可以通過可視化方式對上述參數進行設置。如下圖所示:

 

3 排查與優化方法

3.1 問題原因描述

Oozie在執行過程中如果有多個fork產生並行任務的時候,這時會佔用很多內存,如果機器的內存不夠則會產生調度阻塞。或者是同時提交了多個Oozie任務,也會產生調度阻塞。由上面Oozie原理分析可以知道,導致Oozie比較佔內存的原因是Oozie啓動一個任務時會先啓動Oozie launcher任務,該任務佔內存比較多,Oozie launcher的聲明週期是數據任務開始之前到結束,資源不會釋放,如果此時數據任務得不到充足的資源就會一直等待有資源過來來執行數據任務,這樣就導致了相互等待的過程,造成了死鎖現象,導致整個集羣陷入阻塞。

3.2 優化方案

(1)Oozie launcher單個佔用資源較大會使用2個container,2core,如果一個container默認佔2G的話,2個container則佔4G

(2)優化方案

  •         減小oozie launcher的資源佔用
  •         限制Oozie併發數量
  •         Yarn中設置不同的隊列,將Oozie launcher單獨作爲一個隊列,任務作爲一個隊列。
  •         採用公平調度器。
  •         降低Yarn中Container最小內存限制。
  •         合併HiveQL腳本。

     

    可以將某些步驟合併到同一個HiveQL腳本中,這會降低Oozie輪詢YARN的開銷。Oozie會向YARN詢問一個查詢是
    
    否結束,如果是就啓動另一個Oozie launcher,然後該Oozie launcher啓動另一個Hive會話。當然,對於出現
    
    查詢出錯的情況,這種合併做法的控制粒度較粗,可能在重新啓動動作前需要做一些手工清理的工作。

3.3 Yarn資源隊列設置

(1)在Ambarri平臺中進行隊列配置

(2) 設置 yarn.resourcemanager.scheduler.class

org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler

(3) Capacity Scheduler,定義ooize、default兩個隊列,分別佔30%及70%。配置內容如下:

capacity-scheduler=null
yarn.scheduler.capacity.default.minimum-user-limit-percent=100
yarn.scheduler.capacity.maximum-am-resource-percent=0.4
yarn.scheduler.capacity.maximum-applications=10000
yarn.scheduler.capacity.node-locality-delay=40
yarn.scheduler.capacity.root.accessible-node-labels=*
yarn.scheduler.capacity.root.acl_administer_queue=*
yarn.scheduler.capacity.root.capacity=100
yarn.scheduler.capacity.root.default.acl_administer_jobs=*
yarn.scheduler.capacity.root.default.acl_submit_applications=*
yarn.scheduler.capacity.root.default.capacity=70
yarn.scheduler.capacity.root.default.maximum-capacity=70
yarn.scheduler.capacity.root.default.state=RUNNING
yarn.scheduler.capacity.root.default.user-limit-factor=1
yarn.scheduler.capacity.root.queues=default,oozie   
yarn.scheduler.capacity.root.oozie.acl_administer_jobs=*
yarn.scheduler.capacity.root.oozie.acl_submit_applications=*
yarn.scheduler.capacity.root.oozie.capacity=30
yarn.scheduler.capacity.root.oozie.maximum-capacity=30
yarn.scheduler.capacity.root.oozie.state=RUNNING
yarn.scheduler.capacity.root.oozie.user-limit-factor=1

(4)配置完後保存並重啓Yarn即可生效

3.4 Oozie的調度配置

(1)輸入腳本input.sh

#!/bin/sh
chmod 777 -R /tmp
echo var1=test1
echo var2=test2

(2)輸出腳本output.sh

#!/bin/sh	

echo $1
echo $2
echo $1 >>/tmp/$1.log  2>&1 ;
echo $2 >>/tmp/$2.log  2>&1 ;

(3)workflow編寫

    在workflow中配置以下兩個參數,用來降低Oozie launcher內存佔用

oozie.launcher.mapreduce.map.memory.mb 512 --減少給“hive2等”oozie.launcher作業分配的資源

oozie.launcher.yarn.app.mapreduce.am.resource.mb  512 --減少oozie.launcher使用AppMaster資源的總內存大小。

    如果是在oozie.site.xml中配置則是全局作用,在Oozie Workflow中配置則是該任務生效,一般建議在Workflow中配置(比較麻煩) 

<!--
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  測試oozie利用capture-output公共傳參,這樣可以在第一個腳本中定義hive,過車參數,扳動參數
-->
<workflow-app xmlns="uri:oozie:workflow:0.4" name="get_var">
    <start to="get_var"/>
    <action name="get_var">
        <shell xmlns="uri:oozie:shell-action:0.2">
            <job-tracker>${jobTracker}</job-tracker>
            <name-node>${nameNode}</name-node>
            <configuration>
                <property>
                    <name>mapred.job.queue.name</name>
                    <value>${queueName}</value>
                </property>
		<property>
                    <name>oozie.launcher.mapred.job.queue.name</name>
                    <value>${launcherName}</value>
                </property>
		<property>
                    <name>oozie.launcher.mapreduce.map.memory.mb</name>
                    <value>512</value>
                </property>
		<property>
                    <name>oozie.launcher.yarn.app.mapreduce.am.resource.mb</name>
                    <value>512</value>
                </property>
            </configuration>
            <exec>input.sh</exec>
	    <file>input.sh</file>
	    <capture-output/> 
        </shell>
        <ok to="use_var"/>
        <error to="fail"/>
    </action>
	<action name="use_var">
        <shell xmlns="uri:oozie:shell-action:0.2">
            <job-tracker>${jobTracker}</job-tracker>
            <name-node>${nameNode}</name-node>
            <configuration>
                <property>
                    <name>mapred.job.queue.name</name>
                    <value>${queueName}</value>
                </property>
		<property>
                    <name>oozie.launcher.mapred.job.queue.name</name>
                    <value>${launcherName}</value>
                </property>
		<property>
                    <name>oozie.launcher.mapreduce.map.memory.mb</name>
                    <value>512</value>
                </property>
		<property>
                    <name>oozie.launcher.yarn.app.mapreduce.am.resource.mb</name>
                    <value>512</value>
                </property>
            </configuration>
            <exec>output.sh</exec>
	    <argument>${wf:actionData('get_var')['var1']}</argument>
	    <argument>${wf:actionData('get_var')['var2']}</argument>
	    <file>output.sh</file>
        </shell>
        <ok to="end"/>
        <error to="fail"/>
    </action>
    <kill name="fail">
        <message>Shell action failed, error message[${wf:errorMessage(wf:lastErrorNode())}]</message>
    </kill>
    <end name="end"/>
</workflow-app>

(4) job.propertites

nameNode=hdfs://iotcluster:8020
jobTracker=iotcluster:8050
queueName=default
launcherName=oozie
worksRoot=phmworks/apps/shell/TEST_OOZIE/TEST_PARA
user.name=oozie

oozie.wf.application.path=${nameNode}/user/${user.name}/${worksRoot}/workflow.xml

(5)Yarn中容器最小內存限制設置

        上述中設置了Oozie launcher啓動過程中需要的參數,降低爲512M,由於Yarn中容器最小內存限制爲1G,此時便會產生衝突,導致了配置參數不會生效,因而需要設置Yarn容器的最小限制。可以對如下參數進行設置,配置爲512M

yarn.scheduler.minimum-allocation-mb  

或者 在自己所在的平臺上進行配置,由於我司採用Ambarri平臺,因而可配置,如下圖所示:

重啓服務後參數生效。

3.5 實驗結果

實驗結果如下:可以看到Oozie launcher在oozie隊列中,由原來的4G現在降爲1G,任務運行的速度也明顯得到提升。

 

4 小 結

針對實際功能工作中Oozie佔用內存較大及調度阻塞問題,本文首先分析研究了Oozie的工作原理及Yarn的運行機制,並基於這兩種機制原理的基礎上,定位問題所在,實現了上述問題的優化,給出了具體的解決方案,並對結果進行了驗證。

參考鏈接:

https://blog.csdn.net/pengzonglu7292/article/details/80550252

https://blog.csdn.net/u010452388/article/details/98234147?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-15.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-15.nonecase

https://blog.csdn.net/njzhujinhua/article/details/80872128

https://blog.csdn.net/bigdataprimary/article/details/84314377

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