最近幾年裡,Python 已成為資料科學、機器學習和深度學習領域的一門流行的程式語言。只需再配上查詢語言 SQL 即可完成大多數工作。SQL 很棒,用英語即可發出指令,且只需指示想要什麼,而無需關心具體如何查詢。這使得底層的查詢引擎可以不改變 SQL 查詢就能對其進行優化。Python 也很棒,它有大量高質量的庫,本身也易於使用。
作業編排是執行日常任務並使其自動化的行為。在過去,這通常是通過 CRON 作業完成的。而在最近幾年,越來越多的企業開始使用 Apache Airflow 和 Spotify 的 Luigi 等建立更強大的系統。這些工具可以監控作業、記錄結果並在發生故障時重新執行作業。如果您有興趣,我曾寫過一篇部落格文章,其中包括 Airflow 的背景故事,題為《使用 Airflow 構建資料管道》。
作為資料探索和視覺化工具的 Notebooks 在過去幾年中也在資料領域變得非常流行。像 Jupyter Notebook 和 Apache Zeppelin 這樣的工具旨在滿足這一需求。Notebooks 不僅向您顯示分析結果,還顯示產生這些結果的程式碼和查詢。這有利於發現疏忽並可幫助分析師重現彼此的工作。
Airflow 和 Jupyter Notebook 可以很好地協同工作,您可以使用 Airflow 自動將新資料輸入資料庫,然後資料科學家可以使用 Jupyter Notebook 進行分析。
在這篇博文中,我將安裝一個單節點的 Hadoop,讓 Jupyter Notebook 執行並展示如何建立一個 Airflow 作業,它可以獲取天氣資料來源,將其儲存在 HDFS 上,再轉換為 ORC 格式,最後匯出到 Microsoft Excel 格式的電子表格中。
我正在使用的機器有一個主頻為 3.40 GHz 的 Intel Core i5-4670K CPU、12 GB 的 RAM 和 200 GB 的 SSD。我將使用全新安裝的 Ubuntu 16.04.2 LTS,並根據我的部落格文章《Hadoop 3:單節點安裝指南》 中的說明構建安裝單節點 Hadoop。
安裝依賴項
接下來將安裝 Ubuntu 上的依賴項。 git 包將用於從 GitHub 獲取天氣資料集,其餘三個包是 Python 本身、Python 包安裝程式和 Python 環境隔離工具包。
$ sudo apt install \
git \
python \
python-pip \
virtualenv
複製程式碼
Airflow 將依靠 RabbitMQ 的幫助來跟蹤其作業。下面安裝 Erlang,這是編寫 RabbitMQ 的語言。
$ echo "deb http://binaries.erlang-solutions.com/debian xenial contrib" | \
sudo tee /etc/apt/sources.list.d/erlang.list
$ wget -O - http://binaries.erlang-solutions.com/debian/erlang_solutions.asc | \
sudo apt-key add -
$ sudo apt update
$ sudo apt install esl-erlang
複製程式碼
下面安裝 RabbitMQ。
$ echo "deb https://dl.bintray.com/rabbitmq/debian xenial main" | \
sudo tee /etc/apt/sources.list.d/bintray.rabbitmq.list
$ wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | \
sudo apt-key add -
$ sudo apt update
$ sudo apt install rabbitmq-server
複製程式碼
下面將安裝此博文中使用的 Python 上的依賴項和應用程式。
$ virtualenv .notebooks
$ source .notebooks/bin/activate
$ pip install \
apache-airflow \
celery \
cryptography \
jupyter \
jupyterthemes \
pyhive \
requests \
xlsxwriter
複製程式碼
配置 Jupyter Notebook
我將為 Jupyter 建立一個資料夾來儲存其配置,然後為伺服器設定密碼。如果不設定密碼,您就會獲得一個冗長的 URL,其中包含用於訪問 Jupyter 網頁介面的金鑰。每次啟動 Jupyter Notebook 時,金鑰都會更新。
$ mkdir -p ~/.jupyter/
$ jupyter notebook password
複製程式碼
Jupyter Notebook 支援使用者介面主題。以下命令將主題設定為 Chesterish。
$ jt -t chesterish
複製程式碼
下面命令列出當前安裝的主題。內建的主題在 GitHub上都有螢幕截圖。
$ jt -l
複製程式碼
要返回預設主題,請執行以下命令。
$ jt -r
複製程式碼
通過 Jupyter Notebook 查詢 Spark
首先確保您執行著 Hive 的 Metastore、Spark 的 Master & Slaves 服務,以及 Presto 的服務端。以下是啟動這些服務的命令。
$ hive --service metastore &
$ sudo /opt/presto/bin/launcher start
$ sudo /opt/spark/sbin/start-master.sh
$ sudo /opt/spark/sbin/start-slaves.sh
複製程式碼
下面將啟動 Jupyter Notebook,以便您可以與 PySpark 進行互動,PySpark 是 Spark 的基於 Python 的程式設計介面。
$ PYSPARK_DRIVER_PYTHON=ipython \
PYSPARK_DRIVER_PYTHON_OPTS="notebook
--no-browser
--ip=0.0.0.0
--NotebookApp.iopub_data_rate_limit=100000000" \
pyspark \
--master spark://ubuntu:7077
複製程式碼
請注意,上面的 master 的 URL 以 ubuntu 為主機名。此主機名是 Spark Master 服務端繫結的主機名。如果無法連線到 Spark,請檢查 Spark Master 服務端的日誌,查詢它已選擇繫結的主機名,因為它不接受定址其他主機名的連線。這可能會令人困惑,因為您通常會期望像 localhost 這樣的主機名無論如何都能正常工作。
執行 Jupyter Notebook 服務後,用下面命令開啟網頁介面。
$ open http://localhost:8888/
複製程式碼
系統將提示您輸入為 Jupyter Notebook 設定的密碼。在右上角輸入後,您可以從下拉選單中建立新的筆記本。我們感興趣的兩種筆記本型別是 Python 和終端。終端筆記本使用您啟動 Jupyter Notebook 的 UNIX 帳戶為您提供 shell 訪問許可權。而我將使用的是 Python 筆記本。
啟動 Python 筆記本後,將以下程式碼貼上到單元格中,它將通過 Spark 查詢資料。調整查詢以使用您在安裝中建立的資料集。
cab_types = sqlContext.sql("""
SELECT cab_type, COUNT(*)
FROM trips_orc
GROUP BY cab_type
""")
cab_types.take(2)
複製程式碼
這就是上面查詢的輸出結果。只返回了一條記錄,包括兩個欄位。
[Row(cab_type=u'yellow', count(1)=20000000)]
複製程式碼
通過 Jupyter Notebook 查詢 Presto
在前面用來查詢 Spark 的筆記本中,也可以查詢 Presto。某些 Presto 查詢的效能可能超過 Spark,趁手的是這兩者可以在同一個筆記本中進行切換。在下面的示例中,我使用 Dropbox 的 PyHive 庫來查詢 Presto。
from pyhive import presto
cursor = presto.connect('0.0.0.0').cursor()
cursor.execute('SELECT * FROM trips_orc LIMIT 10')
cursor.fetchall()
複製程式碼
這是上述查詢的部分輸出。
[(451221840,
u'CMT',
u'2011-08-23 21:03:34.000',
u'2011-08-23 21:21:49.000',
u'N',
1,
-74.004655,
40.742162,
-73.973489,
40.792922,
...
複製程式碼
如果您想在 Jupyter Notebook 中生成資料圖表,可以看看《在 Jupyter Notebook 中使用 SQLite 視覺化資料》這篇博文,因為它有幾個使用 SQL 的繪圖示例,可以與 Spark 和 Presto 一起使用。
啟動 Airflow
下面將建立一個 ~/airflow 資料夾,設定一個用於儲存在網頁介面上設定的 Airflow 的狀態和配置集的 SQLite 3 資料庫,升級配置模式併為 Airflow 將要執行的 Python 作業程式碼建立一個資料夾。
$ cd ~
$ airflow initdb
$ airflow upgradedb
$ mkdir -p ~/airflow/dags
複製程式碼
預設情況下,Presto、Spark 和 Airflow 的網頁介面都使用 TCP 8080 埠。如果您先啟動了 Spark,Presto 就將無法啟動。但如果您是在 Presto 之後啟動 Spark,那麼 Presto 將在 8080 上啟動,而 Spark Master 服務端則會使用 8081,如果仍被佔用,會繼續嘗試更高階口,直到它找到一個空閒的埠。之後, Spark 將為 Spark Worker 的網頁介面選擇更高的埠號。這種重疊通常不是問題,因為在生產設定中這些服務通常存在於不同的機器上。
因為此安裝中使用了 8080 - 8082 的 TCP 埠,我將在埠 8083 上啟動 Airflow 的網頁介面。
$ airflow webserver --port=8083 &
複製程式碼
我經常使用以下命令之一來檢視正在使用的網路埠。
$ sudo lsof -OnP | grep LISTEN
$ netstat -tuplen
$ ss -lntu
複製程式碼
Airflow 的 Celery 代理和作業結果的儲存都預設使用 MySQL。這裡改為使用 RabbitMQ。
$ vi ~/airflow/airflow.cfg
複製程式碼
找到並編輯以下設定。
broker_url = amqp://airflow:airflow@localhost:5672/airflow
celery_result_backend = amqp://airflow:airflow@localhost:5672/airflow
複製程式碼
上面使用了 airflow 作為使用者名稱和密碼連線到 RabbitMQ。賬號密碼可以隨意自定。
下面將為 RabbitMQ 配置上述賬號密碼,以便它能訪問 Airflow 虛擬主機。
$ sudo rabbitmqctl add_vhost airflow
$ sudo rabbitmqctl add_user airflow airflow
$ sudo rabbitmqctl set_user_tags airflow administrator
$ sudo rabbitmqctl set_permissions -p airflow airflow ".*" ".*" ".*"
複製程式碼
將 Airflow 連線到 Presto
下面將開啟 Airflow 網頁介面。
$ open http://localhost:8083/
複製程式碼
開啟 Airflow 網頁介面後,單擊頂部的 “Admin” 導航選單,然後選擇 “Connections”。您將看到一長串預設資料庫連線。單擊以編輯 Presto 連線。 Airflow 連線到 Presto 需要進行以下更改。
- 將 schema 從 hive 改為 default。
- 將埠從 3400 改為 8080。
儲存這些更改,然後單擊頂部的 “Data Profiling” 導航選單,選擇 “Ad Hoc Query”。從查詢框上方的下拉選單中選擇 “presto_default”,您就應該可以通過 Presto 執行 SQL 程式碼了。下面是針對我在安裝中匯入的資料集執行的示例查詢。
SELECT count(*)
FROM trips_orc;
複製程式碼
下載天氣資料集
可以將 Airflow DAG 視為定時執行的作業。在下面的示例中,我將在 GitHub 上獲取 FiveThirtyEight 資料倉儲提供的天氣資料,將其匯入 HDFS,將其從 CSV 轉換為 ORC 並將其從 Presto 匯出為 Microsoft Excel 格式。
以下內容將 FiveThirtyEight 的資料儲存克隆到名為 data 的本地資料夾中。
$ git clone \
https://github.com/fivethirtyeight/data.git \
~/data
複製程式碼
然後我將啟動 Hive 並建立兩個表。一個存資料集的 CSV 格式,另一個存資料集的 Presto 和 Spark 友好的 ORC 格式。
$ hive
複製程式碼
CREATE EXTERNAL TABLE weather_csv (
date_ DATE,
actual_mean_temp SMALLINT,
actual_min_temp SMALLINT,
actual_max_temp SMALLINT,
average_min_temp SMALLINT,
average_max_temp SMALLINT,
record_min_temp SMALLINT,
record_max_temp SMALLINT,
record_min_temp_year INT,
record_max_temp_year INT,
actual_precipitation DECIMAL(18,14),
average_precipitation DECIMAL(18,14),
record_precipitation DECIMAL(18,14)
) ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
LOCATION '/weather_csv/';
CREATE EXTERNAL TABLE weather_orc (
date_ DATE,
actual_mean_temp SMALLINT,
actual_min_temp SMALLINT,
actual_max_temp SMALLINT,
average_min_temp SMALLINT,
average_max_temp SMALLINT,
record_min_temp SMALLINT,
record_max_temp SMALLINT,
record_min_temp_year INT,
record_max_temp_year INT,
actual_precipitation DOUBLE,
average_precipitation DOUBLE,
record_precipitation DOUBLE
) STORED AS orc
LOCATION '/weather_orc/';
複製程式碼
建立 Airflow DAG
下面的 Python 程式碼是 Airflow 作業(也稱為DAG)。每隔 30 分鐘,它將執行以下操作。
- 清除 HDFS上 /weather_csv/ 資料夾中的任何現有資料。
- 將 ~/data 資料夾中的 CSV 檔案複製到 HDFS 上的 /weather_csv/ 資料夾中。
- 使用 Hive 將 HDFS 上的 CSV 資料轉換為 ORC 格式。
- 使用 Presto 將 ORC 格式的資料匯出為 Microsoft Excel 2013 格式。
在下面的 Python 程式碼中有一個指向 CSV 的位置,完整路徑為 /home/mark/data/us-weather-history/*.csv,請將其中的 “mark” 更換為您自己的 UNIX 使用者名稱。
$ vi ~/airflow/dags/weather.py
複製程式碼
from datetime import timedelta
import airflow
from airflow.hooks.presto_hook import PrestoHook
from airflow.operators.bash_operator import BashOperator
from airflow.operators.python_operator import PythonOperator
import numpy as np
import pandas as pd
default_args = {
'owner': 'airflow',
'depends_on_past': False,
'start_date': airflow.utils.dates.days_ago(0),
'email': ['airflow@example.com'],
'email_on_failure': True,
'email_on_retry': False,
'retries': 3,
'retry_delay': timedelta(minutes=15),
}
dag = airflow.DAG('weather',
default_args=default_args,
description='將天氣資料複製到 HDFS 並匯出為 Excel',
schedule_interval=timedelta(minutes=30))
cmd = "hdfs dfs -rm /weather_csv/*.csv || true"
remove_csvs_task = BashOperator(task_id='remove_csvs',
bash_command=cmd,
dag=dag)
cmd = """hdfs dfs -copyFromLocal \
/home/mark/data/us-weather-history/*.csv \
/weather_csv/"""
csv_to_hdfs_task = BashOperator(task_id='csv_to_hdfs',
bash_command=cmd,
dag=dag)
cmd = """echo \"INSERT INTO weather_orc
SELECT * FROM weather_csv;\" | \
hive"""
csv_to_orc_task = BashOperator(task_id='csv_to_orc',
bash_command=cmd,
dag=dag)
def presto_to_excel(**context):
column_names = [
"date",
"actual_mean_temp",
"actual_min_temp",
"actual_max_temp",
"average_min_temp",
"average_max_temp",
"record_min_temp",
"record_max_temp",
"record_min_temp_year",
"record_max_temp_year",
"actual_precipitation",
"average_precipitation",
"record_precipitation"
]
sql = """SELECT *
FROM weather_orc
LIMIT 20"""
ph = PrestoHook(catalog='hive',
schema='default',
port=8080)
data = ph.get_records(sql)
df = pd.DataFrame(np.array(data).reshape(20, 13),
columns=column_names)
writer = pd.ExcelWriter('weather.xlsx',
engine='xlsxwriter')
df.to_excel(writer, sheet_name='Sheet1')
writer.save()
return True
presto_to_excel_task = PythonOperator(task_id='presto_to_excel',
provide_context=True,
python_callable=presto_to_excel,
dag=dag)
remove_csvs_task >> csv_to_hdfs_task >> csv_to_orc_task >> presto_to_excel_task
if __name__ == "__main__":
dag.cli()
複製程式碼
使用該程式碼開啟 Airflow 的網頁介面並將主頁底部的 “weather” DAG 旁邊的開關切換為 “on”。
排程程式將建立一個作業列表交給 workers 去執行。以下內容將啟動 Airflow 的排程程式服務和一個將完成所有預定作業的 worker。
$ airflow scheduler &
$ airflow worker &
複製程式碼
感謝您抽出寶貴時間閱讀這篇文章。我為北美和歐洲的客戶提供諮詢、架構和實際開發服務。如果您有意探討我的產品將如何幫助您的業務,請通過 LinkedIn 與我聯絡。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。