loading

Resources

26Dec 2017

解釋 Hadoop Delegation Token 下篇

2017 年12 月07 日
作者:Xiao Chen、Yongjun Zhang
原文:Cloudrea
 
接續上篇內容
 
快取記憶體中找不到Token
有時候,應用程式的錯誤狀況是AuthenticationException,內含一個InvalidToken 異常狀況。異常訊息顯示「快取記憶體中找不到Token」。猜猜看發生這個錯誤的原因,以及它與「token 已逾期」錯誤之間有何差異?
 
….
17/10/22 20:55:47 INFO mapreduce.Job: Job job_1508730362330_0003 failed with state FAILED due to: Application application_1508730362330_0003 failed 2 times due to AM Container for appattempt_1508730362330_0003_000002 exited with  exitCode: -1000
For more detailed output, check application tracking page:https://example.cloudera.com:8090/proxy/application_1508730362330_0003/Then, click on links to logs of each attempt.
Diagnostics: org.apache.hadoop.security.authentication.client.AuthenticationException: org.apache.hadoop.security.token.SecretManager$InvalidToken: token (kms-dt owner=xiao, renewer=yarn, realUser=, issueDate=1508730937041, maxDate=1509335737041, sequenceNumber=8, masterKeyId=73) can’t be found in cache
java.io.IOException: org.apache.hadoop.security.authentication.client.AuthenticationException: org.apache.hadoop.security.token.SecretManager$InvalidToken: token (kms-dt owner=xiao, renewer=yarn, realUser=, issueDate=1508730937041, maxDate=1509335737041, sequenceNumber=8, masterKeyId=73) can’t be found in cache
at org.apache.hadoop.crypto.key.kms.LoadBalancingKMSClientProvider.decryptEncryptedKey(LoadBalancingKMSClientProvider.java:294)
at org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.decryptEncryptedKey(KeyProviderCryptoExtension.java:528)
at org.apache.hadoop.hdfs.DFSClient.decryptEncryptedDataEncryptionKey(DFSClient.java:1448)

at org.apache.hadoop.fs.FileSystem.open(FileSystem.java:784)
at org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:367)
at org.apache.hadoop.yarn.util.FSDownload.copy(FSDownload.java:265)
at org.apache.hadoop.yarn.util.FSDownload.access$000(FSDownload.java:61)
at org.apache.hadoop.yarn.util.FSDownload$2.run(FSDownload.java:359)
at org.apache.hadoop.yarn.util.FSDownload$2.run(FSDownload.java:357)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.hadoop.security.authentication.client.AuthenticationException: org.apache.hadoop.security.token.SecretManager$InvalidToken: token (kms-dt owner=xiao, renewer=yarn, realUser=, issueDate=1508730937041, maxDate=1509335737041, sequenceNumber=8, masterKeyId=73) can’t be found in cache

at org.apache.hadoop.crypto.key.kms.KMSClientProvider.call(KMSClientProvider.java:535)
 
 
解釋:
上述兩項錯誤出自同一個原因:用來驗證的token 已逾期,而且無法再用來進行驗證。
第一則訊息明確指出該token 已逾期,因為該token 仍然儲存在伺服器中。所以當伺服器檢驗token 時,逾期時間的檢驗失敗,出現「token 已逾期」的異常狀況。
現在您來猜猜會在什麼情況下發生第二個錯誤?記得「伺服器端的Delegation Token」一節我們曾解釋過,伺服器有一個背景執行緒來移除逾期的token。所以,如果在伺服器的背景執行緒把token 移除之後再使用token 進行驗證,則伺服器上的檢驗邏輯無法找到這個token。結果就會出現異常狀況,表示「找不到token」。
下圖顯示這些事件的順序。
  Figure 4: Life Cycle of Delegation Token  
4Delegation Token 的生命週期
 
請注意,明確取消一個 token 時,它會立即從儲存庫中移除。所以,若是 token 被取消了,錯誤一定是「找不到 token」。
為了進一步對這些錯誤除錯,必須使用 grep 指令取得該特定 token 序號的用戶端日誌記錄以及伺服器日誌記錄 (在上述範例中「sequenceNumber=7」或「sequenceNumber=8)。您應該可以在伺服器日誌記錄中看到與建立、更新 (若有)、取消 (若有) token 相關的事件。
 
長時間執行的應用程式
至此,您知道關於 Hadoop Delegation Token 的所有基本知識。但是仍有一處未提:我們知道 Delegation Token 超過最長使用壽命即無法更新,所以當應用程式的執行時間長於最長使用壽命時要怎麼辦?
不幸的,Hadoop 對於所有這類應用程式並沒有一致的做法,所以沒有一種魔法設定可以讓任何應用程式套用後即「迎刃而解」。但是仍然有辦法可行。對於 Spark 提交的應用程式而言,好消息是 Spark 已導入了這些魔法參數
Spark 取得 Delegation Token 並用它們進行驗證,與我們在前面小節所描述的相同。不過,Spark 不會更新 token,反而在 token 快要逾期時取得新的 token。如此一來,應用程式便能永遠執行下去。相關程式碼請參閱這裡
請注意,這麼做必須提供 Kerberos keytab 給您的 Spark 應用程式。
 
但是,若是您正執行一個執行長時間執行的服務應用程式,並要由應用程式明確地處理 token?這種做法會牽涉兩個部份:更新 token 直至最長使用壽命;在最長使用壽命到達時更換 token
 
請注意,這並不是慣常的做法,而且唯有在您正執行一項服務時才建議使用。
導入 Token 更新者讓我們先研究應該如何完成 token 更新程序。最好的方法是研究 YARN RM DelegationTokenRenewer 程式碼。(2018/01/02 更新連結,感謝L.c. Hsieh)
那個類別有一些要點:
  1. 它是管理所有 token 的單一類別。它在內部有一個執行緒池來更新 token,以及另一個執行緒來取消 token。在逾期時間到達 90% 時進行更新。取消作業會有一些延遲 (30 ) 以避免資源競奪。
  2. 每個 token 的逾期時間會分開管理。token 的逾期時間是按照程式設定去呼叫 token renew() API 來擷取的。
API 回傳的數值便是逾期時間。
 
 
1
2
3
4
5
6
7
8
dttr.expirationDate=
          UserGroupInformation.getLoginUser().doAs(
            newPrivilegedExceptionAction<Long>(){
              @Override
              publicLongrun()throwsException{
                returndttr.token.renew(dttr.conf);
              }
            });
 
YARN RM 在收到token 之後立即更新還有另一個原因:要知道它何時逾期。
  1. 將token 的識別碼解碼並呼叫它的getMaxDate() API即可取得每個token 的最長使用壽命。呼叫相同的API 也可以取得識別碼中其他欄位。    
 
 
1
2
3
4
5
6
7
8
9
if(token.getKind().equals(HDFS_DELEGATION_KIND)){
        try{
          AbstractDelegationTokenIdentifier identifier=
              (AbstractDelegationTokenIdentifier)token.decodeIdentifier();
          maxDate= identifier.getMaxDate();
        }catch(IOExceptione){
          thrownewYarnRuntimeException(e);
        }
      }
 
 
  1. 因為2 和3,不需要讀取任何設定來決定更新間隔和最長使用壽命。伺服器的設定可能不定時變更。用戶端不應該依賴該設定,且沒有方法可以瞭解它。
 
在最長使用壽命之後處理token
Token 更新者僅會在到達最長使用壽命之後更新token。在最長使用壽命到達之後,工作便會執行失敗。
若您的應用程式長時間執行,您應該考慮使用YARN 說明文件中關於長時間型服務所描述的機制,或是增加邏輯到您本身的delegation token 更新者類別,當既有token 即將到達最長使用壽命時擷取新的delegation token。
 
Token 的其他用途
恭喜!關於Delegation Token 的概念和詳細資料您已經閱讀完畢。本文仍有數個相關面向尚未提及。以下是這些面向的簡介。
 
其他服務的Delegation token:例如Apache Oozie、Apache Hive 和Apache Hadoop 的YARN RM 等服務也使用Delegation Token 進行驗證。
 
Block Access Token:HDFS 用戶端存取檔案時會先聯絡NameNode,以便取得特定檔案的區塊位置,然後直接在DataNode 上存取區塊。檔案權限檢查會在NameNode 進行。但是在DataNodes 上的後續資料區塊存取,也必須進行授權檢查。Block Access Token 這樣便派上用場。它們由HDFS NameNode 派發到用戶端,然後再由用戶端傳遞到DataNode。Block Access Token 的使用壽命短(預設10 小時) 而且無法更新。若Block Access Token 逾期,用戶端必須請求一個新的。
 
Authentication Token:我們特別探討了Delegation Token。Hadoop 也有Authentication Token 的概念,這是一個成本較低且更有延展性的驗證機制。它類似伺服器和用戶端之間的cookie。Authentication Token 由伺服器授予,無法更新或是用來模擬其他token。而且與Delegation Token 不同,不需要由伺服器個別儲存。您不需要針對Authentication Token 編寫明確的程式碼。
 
結論
Delegation Token 在Hadoop 生態系統中扮演重要的角色。您現在應該瞭解Delegation Token 的用途、使用方法以及它們如此運用的原因。在編寫應用程式及除錯時,這些知識至關緊要。
 
附錄
附錄A:伺服器端的設定。
下表是與Delegation Token 相關的設定。請參閱伺服器端的Delegation Token瞭解這些屬性。
屬性
在HDFS 中的設定名稱
 
在HDFS 中的預設值
 
在KMS 中的設定名稱
 
在KMS 中的預設值 
 
更新間隔
dfs.namenode.delegation.token.renew-
 interval
86400000
 ( 1 天)
hadoop.kms.authentication.delegation-token.renew-
 interval.sec
86400
 ( 1 天)
最長使用壽命
dfs.namenode.delegation.token.max-
 lifetime
604800000
 ( 7 天)
hadoop.kms.authentication.delegation-token.max-
 lifetime.sec
604800
 ( 7 天)
背景移除逾期Token 的時間間隔 
 
不可設定
3600000
 ( 1 小時)
hadoop.kms.authentication.delegation-token.removal-
 scan-interval.sec
3600
 ( 1 小時)
主要金鑰來回作業間隔
dfs.namenode.delegation.key.update-
 interval
86400000
 ( 1 天)
hadoop.kms.authentication.delegation-token.update-
 interval.sec
86400
 ( 1 天)
 
 
表4:HDFS 和KMS 的設定屬性與預設值
 
附錄B:使用Delegation Token 進行驗證的程式碼範例。
在查看下列程式碼之前必須先瞭解一個概念:UserGroupInformation(UGI) 類別。UGI 是Hadoop 的公用API,可編寫驗證的程式碼。在下列程式碼範例中使用了UGI,在前述的一些異常狀況堆疊追蹤中也有出現。
GetFileStatus 是使用UGI 存取HDFS 的例子。如需詳細資料請參閱FileSystem 類別javadoc
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
UserGroupInformation tokenUGI= UserGroupInformation.createRemoteUser("user_name");
UserGroupInformation kerberosUGI= UserGroupInformation.loginUserFromKeytabAndReturnUGI("principal_in_keytab","path_to_keytab_file");
Credentials creds= newCredentials();
kerberosUGI.doAs((PrivilegedExceptionAction<Void>)()-> {
  Configuration conf= newConfiguration();
  FileSystem fs= FileSystem.get(conf);
  fs.getFileStatus(filePath);// kerberosUGI can authenticate via Kerberos
 
  // get delegation tokens, add them to the provided credentials. set renewer to yarn
  Token[]newTokens= fs.addDelegationTokens("yarn",creds);
  // Add all the credentials to the UGI object
  tokenUGI.addCredentials(creds);
 
  // Alternatively, you can add the tokens to UGI one by one.
  // This is functionally the same as the above, but you have the option to log each token acquired.
  for(Tokentoken: newTokens){
    tokenUGI.addToken(token);
  }
  returnnull;
});
 
 
請注意,addDelegationTokens RPC 呼叫是由Kerberos 驗證調用的。否則,它會造成IOException 的錯誤,其中文字表示「Delegation Token 必須使用Kerberos 或Web 驗證才能分發」。
現在,我們可以使用所取得的delegation token 來存取HDFS。
 
 
1
2
3
4
5
6
tokenUGI.doAs((PrivilegedExceptionAction<Void>)()-> {
 Configuration conf= newConfiguration();
 FileSystem fs= FileSystem.get(conf);
 fs.getFileStatus(filePath);// tokenUGI can authenticate via Delegation Token
 returnnull;
});
 
Xiao Chen 和Yongjun Zhang 是Cloudera 的軟體工程師、Hadoop committer/PMC 成員。

Back to list.
Prev
解釋 Hadoop Delegation Token 上篇
解釋 Hadoop Delegation Token 上篇
Next
利用 Cloudera 建立生產建議系統
利用 Cloudera 建立生產建議系統