最近公司在降成本,發現了歐州、美國區兩個區每天存在
300$
的跨區流量費用,經過運維同學定位後發現絕大部分流量在emr
機器上。於是排查就開始了。
前言
首先附上我們的任務調度架構
- 我們大數據計算使用的是
AWS
的EMR(Elastic MapReduce)
集羣,由於AWS EMR
天然支持讀寫S3
,並且S3
相比較硬盤尤其便宜,所以我們的離線數據都是存儲在S3
。即:計算與存儲分離的架構。 - 任務調度與開發平臺使用的是赫拉任務調度,關於爲什麼選擇這款開源調度系統是因爲:爲了節省費用我們的離線EMR集羣只是在任務執行時創建使用,任務執行結束後自動銷燬,剛好赫拉任務調度支持動態
EMR
的創建並且支持彈性伸縮配置,還可以使用SSH
和直接在hadoop
機器上兩種方式提交任務。
下面介紹下,爲何我們產生了跨區域的外網流量。通過前言的敘述,大家都知道了我們是計算存儲分離的架構,我們美國區、歐洲區都是一樣的。本來是美國區的任務提交在美國區的 AWS EMR
集羣,存儲在美國區的S3 bucket
,歐洲區的任務提交在歐洲區的 AWS EMR
集羣,存儲在歐洲區的S3 bucket
。但是由於已離職的某位同事爲別人建庫時,美國庫的 location
建在了歐洲的S3 bucket
,也就導致了美國的 EMR
集羣計算時讀、寫使用的數據都來自於歐洲的 S3
.跨區域的流量就是這樣產生的。最後通過S3提供的數據遷移工具,然後批量修改表的 location
解決掉跨區域的流量,下面附上排查的過程和修復方法。
原因排查
當運維發現我們美國區 EMR
機器跨區域的流量較大時,運維定位了一臺機器給我,順便問了下歐洲 s3
的網關地址。
於是我立刻登錄上我們的 EMR
機器一頓亂操作。。
首先使用 netstat
命令加上 -p
參數定位到哪個進程在請求歐洲的網關地址
[root@ip-172-21-31-215 hadoop]# netstat -anop | grep 52.219
tcp 54 0 ::ffff:172.21.11.21:40484 ::ffff:52.219.11.21:443 ESTABLISHED 15428/java off (0.00/0/0)
從結果中可以看出,進程 id
爲 15428
的 java
進程在與歐洲網關地址通信。繼續使用ps命令查看 15428
是什麼任務
[root@ip-172-21-31-215 hadoop]# ps aux | grep 15428
yarn 15428 127 3.6 4101284 582660 ? Sl 03:35 2:23 /usr/lib/jvm/java-openjdk/bin/java -server -Xmx2048m -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:MaxHeapFreeRatio=70 -XX:+CMSClassUnloadingEnabled -XX:OnOutOfMemoryError=kill -9 %p -Djava.io.tmpdir=/mnt/yarn/usercache/hadoop/appcache/application_1586846714065_1434/container_1586846714065_1434_01_000002/tmp -Dspark.history.ui.port=18080 -Dspark.port.maxRetries=256 -Dspark.driver.port=37217 -Dspark.yarn.app.container.log.dir=/var/log/hadoop-yarn/containers/application_1586846714065_1434/container_1586846714065_1434_01_000002 org.apache.spark.executor.CoarseGrainedExecutorBackend --driver-url spark://CoarseGrainedScheduler@ip-172-21-30-200.eu-central-1.compute.internal:37217 --executor-id 1 --hostname ip-172-21-31-215.eu-central-1.compute.internal --cores 2 --app-id application_1586846714065_1434 --user-class-path file:/mnt/yarn/usercache/hadoop/appcache/application_1586846714065_1434/container_1586846714065_1434_01_000002/__app__.jar
root 18696 0.0 0.0 110520 2196 pts/0 S+ 03:37 0:00 grep --color=auto 15428
原來是 spark
任務的一個container
,定位到他的 app id
爲application_1586846714065_1434
。然後使用 yarn application -list
命令查看application_1586846714065_1434
的 name
[root@ip-172-21-31-215 hadoop]# yarn application -list | grep application_1586846714065_1434
20/04/22 03:37:31 INFO client.RMProxy: Connecting to ResourceManager at ip-172-21-30-200.eu-central-1.compute.internal/172.21.30.200:8032
application_1586846714065_1434 hera_job_id_1082 SPARK hadoop default RUNNING UNDEFINED 10% http://ip-172-21-30-200.eu-central-1.compute.internal:4041
赫拉任務調度提交的 spark sql
任務都會爲其重命名爲任務的 id
便於定位問題。通過上面的結果,我們可以清楚的看到是赫拉爲 1082
的任務在請求跨區域流量。通過在赫拉任務調度平臺上直接搜索1082就定位到是哪個腳本在執行任務了。最後我發現是算法庫的整個庫的 location
建在了歐洲區的 s3
。
hive> show create database sucx_algorithm;
OK
CREATE DATABASE `sucx_algorithm`
LOCATION
's3://sucx-big-data-eu/bi/sucx_algorithm'
Time taken: 0.539 seconds, Fetched: 3 row(s)
hive>
找到了原因,剩下就是如何遷移。
遷移數據
遷移涉及到 s3
數據的遷移和 hive
元數據的遷移。
首先我要知道默認情況下 AWS EMR
的元數據是存儲在 GLUE
的,由於GLUE
沒調研到如何查元數據信息,原本是打算直接使用 jdbc
連接hiveserver2
進行 show create table
來獲取表的 location
的。剛剛好我們今天要遷移 hive
元數據 glue
到 mysql
(遷移的方式是:獲取所有的建庫語句,建表語句,在新的 mysql
元數據集羣上執行建庫、建表腳本,然後再msck repair table
修復表即可),所以直接查詢元數據庫獲取所有要遷移表的 location
即可。
準備工作
首先獲取要遷移的庫的表所有 location
第一步:
首先從 DBS
中獲取庫的 DB_ID
mysql> select * from DBS where NAME='sucx_algorithm';
+-------+------+-----------------------------------------+----------------+------------+------------+
| DB_ID | DESC | DB_LOCATION_URI | NAME | OWNER_NAME | OWNER_TYPE |
+-------+------+-----------------------------------------+----------------+------------+------------+
| 31 | NULL | s3://sucx-big-data-eu/bi/sucx_algorithm | sucx_algorithm | hadoop | USER |
+-------+------+-----------------------------------------+----------------+------------+------------+
1 row in set (0.00 sec)
第二步:
執行以下 sql
查詢 sucx_algorithm
庫的所有表名和表對應的LOCATION,並把其結果存到一個文檔(可以通過mysql -hurl -uuser -Dmetastore -ppass -e "$sql" >location.txt
來存儲)
SELECT
TB.TBL_NAME,
SDS.LOCATION
FROM
(
SELECT
TBL_NAME,
SD_ID
FROM
TBLS
WHERE
DB_ID = 31
) TB
LEFT JOIN SDS ON TB.SD_ID = SDS.SD_ID
第二步產生的文檔格式應該是如下:
TBL_NAME | LOCATION |
---|---|
table1 | s3://sucx-big-data-eu/bi/sucx_algorithm/ table1 |
table2 | s3://sucx-big-data-eu/bi/sucx_algorithm/ table2 |
table3 | s3://sucx-big-data-eu/bi/sucx_algorithm/ table3 |
s3數據遷移與對比
s3
的數據要從歐洲區域的 bucket
遷移到美國區域的 bucket
,直接使用s3
提供的工具即可,由於我這裏沒有 s3
的操作權限,就不再演示。
關於遷移數據的對比,我是使用的hadoop fs -du -s
命令來檢測的,檢測大小的路徑爲準備工作中的 location
和其切換到美國後的 location
。
元數據的修改
元數據修改語句爲alter table tableName set location 's3://sucx-big-data-us/bi/sucx_algorithm/tableName'
庫location的修改
sucx_algorithm
的庫的 location
無法通過 alter
語句修改,所以我是直接更新的 DBS
元數據表。
總結
上面的 s3
數據遷移與對比與元數據的修改我使用了一個腳本來生成
//元數據庫生成的表名和location的數據
File file = new File("/Users/scx/Desktop/location.txt");
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;
StringBuilder shells = new StringBuilder("#!/bin/bash\n");
int start = 0;
int end = 10000;
int index = 0;
//是生成alter table的sql文件還是生成對比eu/us的文件大小的腳本
boolean alterSql = false;
while ((line = br.readLine()) != null) {
if (index >= end) {
break;
}
if (start > index++) {
continue;
}
String[] pair = line.split("\\s");
String tableName = pair[0];
String euLocation = pair[1];
//美國區的location
String usLocation = euLocation.replace("s3://sucx-big-data-eu/bi/", "s3://sucx-big-data-us/bi/");
if(alterSql) {
shells.append("alter table sucx_algorithm.").append(tableName).append(" set location '").append(usLocation).append("';\n");
} else {
shells.append("euSize=$(hadoop fs -du -s ").append(euLocation).append(" | awk '{print $1}')\n");
shells.append("usSize=$(hadoop fs -du -s ").append(usLocation).append(" | awk '{print $1}')\n");
shells.append("echo euSize is $euSize,usSize is $usSize,the index is ").append(index).append("\n");
shells.append("if [ $euSize -ne $usSize ]; then").append("\n");
shells.append(" echo ").append(euLocation).append(" not eq ").append(usLocation).append("\n");
shells.append(" exit 1").append("\n");
shells.append("fi").append("\n");
}
}
System.out.println(shells.toString());
} catch (IOException e) {
e.printStackTrace();
}
對於生成的alter sql
,我會全部 copy
到一個 alter.sql
文件中,然後使用hive -f
命令來修改所有表的location
。
對於生成的大小對比腳本,由於執行較慢,我會使用定義的 start
和 end
變量來進行切分,來提高對比的速度。