王雪迎 ,畢業于中國地質大學計算機專業,高級工程師,擁有20年數據庫、數據倉庫相關技術經驗。曾先后供職于北京現代商業信息技術有限公司、北京在線九州信息技術服務有限公司、華北計算技術研究所、北京優貝在線網絡科技有限公司,擔任DBA、數據架構師等職位。
第9章
?定期自動執行ETL作業?
一旦數據倉庫開始使用,就需要不斷從源系統給數據倉庫提供新數據。為了確保數據流的穩定,需要使用所在平臺上可用的任務調度器來調度ETL定期執行。調度模塊是ETL系統必不可少的組成部分,它不但是數據倉庫的基本需求,也對項目的成功起著舉足輕重的作用。
操作系統一般都為用戶提供調度作業的功能,如Windows的“計劃任務”和UNIX/Linux的cron系統服務。絕大多數Hadoop系統都運行在Linux之上,因此本章詳細討論兩種Linux上定時自動執行ETL作業的方案。一種是經典的crontab,這是操作系統自帶的功能,二是Hadoop生態圈中的Oozie組件。為了演示Hadoop對數據倉庫的支持能力,我們的示例將使用后者實現ETL執行自動化。
9.1crontab
上一章我們已經準備好用于定期裝載的regular_etl.shshell腳本文件,可以很容易地用crontab命令創建一個任務,定期運行此腳本。
#修改文件屬性為可執行
chmod755/root/regular_etl.sh
#編輯crontab文件內容
crontab-e
#添加如下一行,指定每天2點執行定期裝載作業,然后保存退出
02***/root/regular_etl.sh
這就可以了,需要用戶做的就是如此簡單,其他的事情交給cron系統服務去完成。提供cron服務的進程名為crond,這是Linux下一個用來周期性執行某種任務或處理某些事件的守護進程。當安裝完操作系統后,會自動啟動crond進程,它每分鐘會定期檢查是否有要執行的任務,如果有則自動執行該任務。
Linux下的任務調度分為兩類,系統任務調度和用戶任務調度。
?系統任務調度:系統需要周期性執行的工作,比如寫緩存數據到硬盤、日志清理等。在/etc目錄下有一個crontab文件,這個就是系統任務調度的配置文件。
?用戶任務調度:用戶要定期執行的工作,比如用戶數據備份、定時郵件提醒等。用戶可以使用crontab命令來定制自己的計劃任務。所有用戶定義的crontab文件都被保存在/var/spool/cron目錄中,其文件名與用戶名一致。
1.crontab權限
Linux系統使用一對allow/deny文件組合判斷用戶是否具有執行crontab的權限。如果用戶名出現在/etc/cron.allow文件中,則該用戶允許執行crontab命令。如果此文件不存在,那么如果用戶名沒有出現在/etc/cron.deny文件中,則該用戶允許執行crontab命令。如果只存在cron.deny文件,并且該文件是空的,則所有用戶都可以使用crontab命令。如果這兩個文件都不存在,那么只有root用戶可以執行crontab命令。allow/deny文件由每行一個用戶名構成。
2.crontab命令
通過crontab命令,我們可以在固定間隔的時間點執行指定的系統指令或shell腳本。時間間隔的單位可以是分鐘、小時、日、月、周及以上的任意組合。crontab命令格式如下:
crontab[-uuser]file
crontab[-uuser][-e|-l|-r]
說明:
?-uuser:用來設定某個用戶的crontab服務,此參數一般由root用戶使用。
?file:file是命令文件的名字,表示將file作為crontab的任務列表文件并載入crontab。如果在命令行中沒有指定這個文件,crontab命令將接受標準輸入,通常是鍵盤上鍵入的命令,并將它們載入crontab。
?-e:編輯某個用戶的crontab文件內容。如果不指定用戶,則表示編輯當前用戶的crontab文件。如果文件不存在,則創建一個。
?-l:顯示某個用戶的crontab文件內容,如果不指定用戶,則表示顯示當前用戶的crontab文件內容。
?-r:從/var/spool/cron目錄中刪除某個用戶的crontab文件,如果不指定用戶,則默認刪除當前用戶的crontab文件。
注意:如果不經意地輸入了不帶任何參數的crontab命令,不要使用Control-d退出,因為這會刪除用戶所對應的crontab文件中的所有條目。代替的方法是用Control-c退出。
3.crontab文件
用戶所建立的crontab文件中,每一行都代表一項任務,每行的每個字段代表一項設置。它的格式共分為六個字段,前五段是時間設定段,第六段是要執行的命令段,格式如下:
.----------------分鐘(0-59)
|.-------------小時(0-23)
||.----------日期(1-31)
|||.-------月份(1-12)
||||.----星期(0-6,代表周日到周一)
|||||
*****要執行的命令,可以是系統命令,也可以是自己編寫的腳本文件。
在以上各個時間字段中,還可以使用如下特殊字符:
?星號(*):代表所有可能的值,例如“月份”字段如果是星號,則表示在滿足其他字段的制約條件后每月都執行該命令操作。
?逗號(,):可以用逗號隔開的值指定一個列表范圍,例如,“1,2,5,7,8,9”。
?中杠(-):可以用整數之間的中杠表示一個整數范圍,例如“2-6”表示“2,3,4,5,6”。
?正斜線(/):可以用正斜線指定時間的間隔頻率,例如“0-23/2”表示每兩小時執行一次。同時正斜線可以和星號一起使用,例如*/10,如果用在“分鐘”字段,表示每十分鐘執行一次。
注意,“日期”和“星期”字段都可以指定哪天執行,如果兩個字段都設置了,則執行的日期是兩個字段的并集。
4.crontab示例
#每1分鐘執行一次command
*****command
#每小時的第3和第15分鐘執行
3,15****command
#在上午8點到11點的第3和第15分鐘執行
3,158-11***command
#每隔兩天的上午8點到11點的第3和第15分鐘執行
3,158-11*/2**command
#每個星期一的上午8點到11點的第3和第15分鐘執行
3,158-11**1command
#每晚的21:30執行
3021***command
#每月1、10、22日的4:45執行
4541,10,22**command
#每周六、周日的1:10執行
101**6,0command
#每天18:00至23:00之間每隔30分鐘執行
0,3018-23***command
#每星期六的晚上11:00執行
023**6command
#每一小時執行一次
**/1***command
#晚上11點到早上7點之間,每隔一小時執行一次
*23-7/1***command
#每月的4號與每周一到周三的11點執行
0114*1-3command
#一月一號的4點執行
0411*command
#每小時執行/etc/cron.hourly目錄內的腳本
01****rootrun-parts/etc/cron.hourly
說明:run-parts會遍歷目標文件夾,執行第一層目錄下具有可執行權限的文件。
5.crontab環境
有時我們創建了一個crontab任務,但是這個任務卻無法自動執行,而手動執行腳本卻沒有問題,這種情況一般是由于在crontab文件中沒有配置環境變量引起的。cron從用戶所在的主目錄中使用shell調用需要執行的命令。cron為每個shell提供了一個默認的環境,Linux下的定義如下:
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=用戶名
HOME=用戶主目錄
在crontab文件中定義多個調度任務時,需要特別注意的一個問題就是環境變量的設置,因為我們手動執行某個腳本時,是在當前shell環境下進行的,程序能找到環境變量;而系統自動執行任務調度時,除了默認的環境,是不會加載任何其他環境變量的。因此就需要在crontab文件中指定任務運行所需的所有環境變量。
不要假定cron知道所需要的特殊環境,它其實并不知道。所以用戶要保證在shell腳本中提供所有必要的路徑和環境變量,除了一些自動設置的全局變量。以下三點需要注意:
?腳本中涉及文件路徑時寫絕對路徑;
?腳本執行要用到環境變量時,通過source命令顯式引入,例如:
#!/bin/sh
source/etc/profile
?當手動執行腳本沒問題,但是crontab不執行時,可以嘗試在crontab中直接引入環境變量解決問題,例如:
0****./etc/profile;/bin/sh/path/to/myscript.sh
6.重定向輸出郵件
默認時,每條任務調度執行完畢,系統都會將任務輸出信息通過電子郵件的形式發送給當前系統用戶。這樣日積月累,日志信息會非常大,可能會影響系統的正常運行。因此,將每條任務進行重定向處理非常重要。可以在crontab文件中設置如下形式,忽略日志輸出:
0*/3***/usr/local/myscript.sh>/dev/null2>&1
“>/dev/null2>&1”表示先將標準輸出重定向到/dev/null,然后將標準錯誤重定向到標準輸出。由于標準輸出已經重定向到了/dev/null,因此標準錯誤也會重定向到/dev/null,這樣日志輸出問題就解決了。
7.生成日志文件
可以將crontab執行任務的輸出信息重定向到一個自定義的日志文件中,例如:
8***rm/home/someuser/tmp/*>/home/someuser/cronlogs/clean_tmp_dir.log
9.2Oozie簡介
除了利用操作系統提供的功能以外,Hadoop生態圈的工具也可以完成同樣的調度任務,而且更靈活,這個組件就是Oozie。
Oozie是一個管理Hadoop作業、可伸縮、可擴展、可靠的工作流調度系統,它內部定義了三種作業:工作流作業、協調器作業和Bundle作業。工作流作業是由一系列動作構成的有向無環圖(DAGs),協調器作業是按時間頻率周期性觸發Oozie工作流的作業,Bundle管理協調器作業。Oozie支持的用戶作業類型有Javamap-reduce、Streamingmap-reduce、Pig、Hive、Sqoop和Distcp,及其Java程序和shell腳本或命令等特定的系統作業。
Oozie項目經歷了三個主要階段。第一版Oozie是一個基于工作流引擎的服務器,通過執行HadoopMapReduce和Pig作業的動作運行工作流作業。第二版Oozie是一個基于協調器引擎的服務器,按時間和數據觸發工作流執行。它可以基于時間(如每小時執行一次)或數據可用性(如等待輸入數據完成后再執行)連續運行工作流。第三版Oozie是一個基于Bundle引擎的服務器。它提供更高級別的抽象,批量處理一系列協調器應用。用戶可以在bundle級別啟動、停止、掛起、繼續、重做協調器作業,這樣可以更好地簡化操作控制。
使用Oozie主要基于以下兩點原因:
?在Hadoop中執行的任務有時候需要把多個MapReduce作業連接到一起執行,或者需要多個作業并行處理。Oozie可以把多個MapReduce作業組合到一個邏輯工作單元中,從而完成更大型的任務。
?從調度的角度看,如果使用crontab的方式調用多個工作流作業,可能需要編寫大量的腳本,還要通過腳本來控制好各個工作流作業的執行時序問題,不但不好維護,而且監控也不方便。基于這樣的背景,Oozie提出了Coordinator的概念,它能夠將每個工作流作業作為一個動作來運行,相當于工作流定義中的一個執行節點,這樣就能夠將多個工作流作業組成一個稱為CoordinatorJob的作業,并指定觸發時間和頻率,還可以配置數據集、并發數等。
9.2.1Oozie的體系結構
Oozie的體系結構如圖9-1所示。
圖9-1Oozie體系結構
Oozie是一種JavaWeb應用程序,它運行在JavaServlet容器,即Tomcat中,并使用數據庫來存儲以下內容:
?工作流定義。
?當前運行的工作流實例,包括實例的狀態和變量。
Oozie工作流是放置在DAG(有向無環圖DirectAcyclicGraph)中的一組動作,例如,Hadoop的Map/Reduce作業、Pig作業等。DAG控制動作的依賴關系,指定了動作執行的順序。Oozie使用hPDL這種XML流程定義語言來描述這個圖。
hPDL是一種很簡潔的語言,它只會使用少數流程控制節點和動作節點。控制節點會定義執行的流程,并包含工作流的起點和終點(start、end和fail節點)以及控制工作流執行路徑的機制(decision、fork和join節點)。動作節點是實際執行操作的部分,通過它們工作流會觸發執行計算或者處理任務。Oozie為以下類型的動作提供支持:HadoopMapReduce、HadoopHDFS、Pig、Java和Oozie的子工作流。而SSH動作已經從Oozieschema0.2之后的版本中移除了。
所有由動作節點觸發的計算和處理任務都不在Oozie中運行。它們是由Hadoop的MapReduce框架執行的。這種低耦合的設計方法讓Oozie可以有效利用Hadoop的負載平衡、災難恢復等機制。這些任務主要是串行執行的,只有文件系統動作例外,它是并行處理的。這意味著對于大多數工作流動作觸發的計算或處理任務類型來說,在工作流操作轉換到工作流的下一個節點之前都需要等待,直到前面節點的計算或處理任務結束了之后才能夠繼續。Oozie可以通過兩種不同的方式來檢測計算或處理任務是否完成,這就是回調和輪詢。當Oozie啟動了計算或處理任務時,它會為任務提供唯一的回調URL,然后任務會在完成的時候發送通知給這個特定的URL。在任務無法觸發回調URL的情況下(可能是因為任何原因,比方說網絡閃斷),或者當任務的類型無法在完成時觸發回調URL的時候,Oozie有一種機制,可以對計算或處理任務進行輪詢,從而能夠判斷任務是否完成。
Oozie工作流可以參數化,例如在工作流定義中使用像${inputDir}之類的變量等。在提交工作流操作的時候,我們必須提供參數值。如果經過合適地參數化,比如使用不同的輸出目錄,那么多個同樣的工作流操作可以并發執行。
一些工作流是根據需要觸發的,但是大多數情況下,我們有必要基于一定的時間段、數據可用性或外部事件來運行它們。Oozie協調系統(Coordinatorsystem)讓用戶可以基于這些參數來定義工作流執行計劃。Oozie協調程序讓我們可以用謂詞的方式對工作流執行觸發器進行建模,謂詞可以是時間條件、數據條件、內部事件或外部事件。工作流作業會在謂詞得到滿足的時候啟動。不難看出,這里的謂詞,其作用和SQL語句的WHERE子句中的謂詞類似,本質上都是在滿足某些條件時觸發某種事件。
有時,我們還需要連接定時運行、但時間間隔不同的工作流操作。多個以不同頻率運行的工作流的輸出會成為下一個工作流的輸入。把這些工作流連接在一起,會讓系統把它作為數據應用的管道來引用。Oozie協調程序支持創建這樣的數據應用管道。
9.2.2CDH5.7.0中的Oozie
CDH5.7.0中,Oozie的版本是4.1.0,其元數據存儲使用MySQL(4.4節CDH安裝中有相關配置)。關于CDH5.7.0中Oozie的屬性,參考以下鏈接:
https://www.cloudera.com/documentation/enterprise/latest/topics/cm_props_cdh570_oozie.html
9.3建立定期裝載工作流
對于剛接觸Oozie的用戶來說,前面介紹的概念過于抽象,不易理解,那么就讓我們一步步創建銷售訂單示例ETL的工作流,在實例中學習Oozie的特性和用法。
1.修改資源配置
Oozie運行需要使用較高的內存資源,因此要將以下兩個YARN參數的值調大:
?yarn.nodemanager.resource.memory-mb:NodeManage總的可用物理內存。
?yarn.scheduler.maximum-allocation-mb:一個MapReduce任務可申請的最大內存。
如果分配的內存不足,在執行工作流作業時會報類似下面的錯誤:
org.apache.oozie.action.ActionExecutorException:JA009:org.apache.hadoop.yarn.exceptions.InvalidResourceRequestException:Invalidresourcerequest,requestedmemory<0,orrequestedmemory>maxconfigured,requestedMemory=1536,maxMemory=1500
我們的實驗環境中,每個Hadoop節點所在虛擬機的總物理內存為8GB,所以把這兩個參數都設置為2GB。修改的方法有兩種,可以編輯yarn-site.xml文件里的屬性,如:
yarn.nodemanager.resource.memory-mb
2000
yarn.scheduler.maximum-allocation-mb
2000
或者在ClouderaManager中修改,yarn.nodemanager.resource.memory-mb參數在YARN服務的NodeManager范圍里,yarn.scheduler.maximum-allocation-mb參數在YARN服務的ResourceManager范圍里。無論使用哪種方法,修改后都需要保存更改并重啟Hadoop集群。
2.啟用OozieWebConsole
默認安裝CDH時,OozieWebConsole是禁用的,為了后面方便監控Oozie作業的執行,需要將其改為啟用狀態。“啟用Oozie服務器Web控制臺”屬性在Oozie服務的“OozieServerDefaultGroup”里。具體的做法是:
下載ext-2.2包,解壓縮到Oozie服務器實例所在節點的/var/lib/oozie/目錄下。
登錄ClouderaManager管理控制臺,進入Oozie服務頁面。
單擊“配置”標簽。
定位“啟用Oozie服務器Web控制臺”屬性,或者在搜索框中輸入該屬性名查找。
選擇“啟用Oozie服務器Web控制臺”的復選框。
單擊“保存更改”按鈕提交所做的修改。
重啟Oozie服務。
3.啟動Sqoop的sharemetastoreservice
……