記一次大數據跨區域流量排查及修復

最近公司在降成本,發現了歐州、美國區兩個區每天存在 300$ 的跨區流量費用,經過運維同學定位後發現絕大部分流量在 emr 機器上。於是排查就開始了。

前言

首先附上我們的任務調度架構

在這裏插入圖片描述

  • 我們大數據計算使用的是 AWSEMR(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)

從結果中可以看出,進程 id15428java 進程在與歐洲網關地址通信。繼續使用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 idapplication_1586846714065_1434。然後使用 yarn application -list 命令查看application_1586846714065_1434name

[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 元數據 gluemysql(遷移的方式是:獲取所有的建庫語句,建表語句,在新的 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
對於生成的大小對比腳本,由於執行較慢,我會使用定義的 startend 變量來進行切分,來提高對比的速度。

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