跳轉到

Jenkins Pipeline 故障排查:Credentials 類型不匹配(SSH Key vs StringCredentials)融合演化版 v8

文檔資訊

  • 分類: debugging
  • 難度: intermediate
  • 預估閱讀時間: 12 分鐘
  • 標籤: jenkins, credentials, pipeline, shared-library, ssh, secret-text, ci-cd, gitlab-token, private-token, oauth2

摘要

Jenkins BuildServer Pipeline 失敗的完整排查與修復流程。根因是 AutoBuildSiteDeploy 觸發子任務時載入了 common@feat/shallow-fetch 分支,該分支對 credential 類型的期望從 SSH Key 變成 StringCredentials。修復分兩層:(1) BuildServer 端改回 common@main 並換用 Secret Text credential;(2) AutoBuildSiteDeploy 的 checkout stage 需要同步改用 PRIVATE-TOKEN header 方式而非 oauth2 URL 嵌入(oauth2 方式導致約 109 秒連線延遲)。Build #570 實測驗證 oauth2 延遲問題,Build #571 確認 PRIVATE-TOKEN header 修法有效。

關鍵學習

  • Jenkins shared library 的分支版本是隱性風險:common@feat/shallow-fetch 對 credential 類型的期望與 main 分支不同,Pipeline 代碼本身完全沒有改變,卻因 library 引用分支切換導致 credential 類型不符錯誤

  • credentials() 參數若指定具體 credentialType(如 BasicSSHUserPrivateKey),實際類型不符時直接報錯;改用 credentialType: 'Any' 可解除限制

  • Secret Text credential 用法:withCredentials([string(...)]) 搭配 http.extraHeader=PRIVATE-TOKEN: ${GIT_TOKEN},比 oauth2 URL 嵌入方式更安全且無連線延遲

  • oauth2:TOKEN@URL 方式在此環境下實測導致約 109 秒連線等待後被中斷(Build #570 實測:04:03:26 → 04:05:15 abort)

  • AutoBuildSiteDeploy 的 checkout stage 是獨立的 credential 設定,不會因 BuildServer 的 gitlabAccount 參數更換而自動更新,必須分開修改

  • Jenkins 原生 checkout() SCM 步驟直接以 SSH 方式 clone,不支援 Secret Text 類型的 credential,必須改成手動 git 指令

  • AutoBuildSiteDeploy 中 gitlabAccount 環境變數來自 Jenkins Job 的 Environment Variables 設定,不在 Jenkinsfile 的 parameters block 內

技術細節

錯誤訊息

ERROR: Credentials 'Make-Wish-Gitlab-Lupin' is of type 'SSH Username with private key'
where 'org.jenkinsci.plugins.plaincredentials.StringCredentials' was expected

根因分析

Build #566(成功) vs Build #567(失敗) 的關鍵差異:

  • Build #566:AutoBuildSiteDeploy 載入 library 'common@main'(revision 62530a8c
  • Build #567:AutoBuildSiteDeploy 載入 library 'common@feat/shallow-fetch'(revision 36d83889

feat/shallow-fetch 分支在 git.shallowFetch() 函數內部改用了 withCredentials([string(...)]) 方式讀取 credential,期望 StringCredentials,但 Make-Wish-Gitlab-Lupin 實際是 SSH Key 類型。Pipeline 代碼本身沒有改變,只有 library 分支改變,導致難以從表面診斷。

Credential 類型對照

Credential ID 類型 適用場景
Make-Wish-Gitlab-Lupin SSH Username with private key git clone via SSH
cheng-csp-gitlab-token Secret Text (StringCredentials) git clone via HTTPS with token

Build #569 新問題:credential not found

Environment Variables 改為 cheng-csp-gitlab-token 後,Build #569 日誌顯示:

Warning: CredentialId "cheng-csp-gitlab-token" could not be found.
ERROR: Error cloning remote repo 'origin'

原因:AutoBuildSiteDeploy 的 Checkout stage 使用的是 Jenkins 原生 checkout() SCM 步驟,該步驟直接以 SSH 方式 clone,不支援 Secret Text 類型的 credential

Build #570 oauth2 延遲問題(實測)

改用 oauth2:TOKEN@URL 方式後,實測 Build #570 出現約 **109 秒**連線等待:

[04:03:26] + git clone --branch main http://oauth2:****@[IP]:17080/pocketstore/genglobalsetting.git .
[04:03:26] Cloning into '.'
[04:05:15] Sending interrupt signal to process   ← 被手動中斷

原本正常應在 **18 秒**內完成,oauth2 URL 嵌入方式在此環境下存在嚴重的連線延遲問題。

What Changed

第一層修復:BuildServer Jenkinsfile

// 修改前
credentials(
    name: 'gitlabAccount',
    defaultValue: 'Make-Wish-Gitlab-Lupin',
    credentialType: 'com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey'
)

// 修改後
credentials(
    name: 'gitlabAccount',
    defaultValue: 'cheng-csp-gitlab-token',
    credentialType: 'Any'  // 不鎖定類型,相容 Secret Text
)

第二層修復:AutoBuildSiteDeploy

Jenkins Job 的 Environment Variables 中 gitlabAccount 改為 cheng-csp-gitlab-token

Checkout stage 從 Jenkins 原生 SSH checkout 改為手動 git + PRIVATE-TOKEN header:

// 修改後(正確方式)
withCredentials([string(credentialsId: "${gitlabAccount}", variable: 'GIT_TOKEN')]) {
    sh '''
        git checkout -b main
        git -c "http.extraHeader=PRIVATE-TOKEN: ${GIT_TOKEN}" fetch origin
        git pull --rebase=false origin main --prune --verbose
    '''
}

實測驗證過程

  • Build #570 實測確認 oauth2:TOKEN@URL 方式導致約 **109 秒**連線延遲(後被手動中斷)
  • Build #571 實測確認 PRIVATE-TOKEN header 方式成功,整體流程恢復正常
  • 釐清 Build #569 失敗原因:Jenkins 原生 checkout() SCM 不支援 Secret Text credential,必須改用手動 git 指令

So What

Jenkins shared library 的分支版本管理是**高度隱性的風險**。Pipeline 代碼本身完全沒有改變,只是 library 引用的分支從 main 切換到 feat/shallow-fetch,就能導致看似無辜的失敗,且錯誤訊息指向的是 credential 類型,而非 library 版本。

此外,從 SSH Key 遷移到 Secret Text token 時,有兩個重要的連帶效應需要處理:

  1. Jenkins 原生 checkout() SCM 步驟**不相容** Secret Text credential,必須改成手動 git 指令
  2. oauth2:TOKEN@URL 方式**在此環境下有嚴重連線延遲問題**(實測 109 秒),必須改用 PRIVATE-TOKEN header

Trade-offs

  • credentialType: 'Any' vs 鎖定具體類型:Any 靈活但失去類型安全保護,鎖定類型能在 pipeline 觸發時就報錯而非 runtime 才發現
  • SSH Key vs Secret Text Token:SSH 不需要嵌入 URL、安全性較高;Token 方式 HTTPS 連線更通用,但 Jenkins 原生 checkout() SCM 不支援 Secret Text
  • oauth2:TOKEN@URL vs PRIVATE-TOKEN header:前者語法簡單,但實測在此環境下導致約 109 秒連線延遲;後者語法稍複雜,但穩定且安全(token 不暴露在 URL 中)
  • library @main vs @feat/xxx:功能分支引入不穩定風險,生產環境建議只使用穩定 tag 或 main

Try It Fast

// BuildServer Jenkinsfile - credentials 參數修正
parameters {
    credentials(
        name: 'gitlabAccount',
        defaultValue: 'cheng-csp-gitlab-token',
        description: 'GitLab Token',
        credentialType: 'Any'  // 不鎖定類型,相容 Secret Text
    )
}

// AutoBuildSiteDeploy - Checkout stage 正確用法(Secret Text)
// ✅ 推薦:PRIVATE-TOKEN header(不將 token 暴露在 URL,無連線延遲)
withCredentials([string(credentialsId: "${gitlabAccount}", variable: 'GIT_TOKEN')]) {
    sh '''
        git checkout -b main
        git -c "http.extraHeader=PRIVATE-TOKEN: ${GIT_TOKEN}" fetch origin
        git pull --rebase=false origin main --prune --verbose
    '''
}

// ❌ 避免:oauth2 URL 嵌入(Build #570 實測導致約 109 秒連線延遲)
// withCredentials([string(credentialsId: gitlabAccount, variable: 'GIT_TOKEN')]) {
//     sh 'git clone http://oauth2:${GIT_TOKEN}@host/repo.git .'
// }

// ❌ 避免:Jenkins 原生 checkout() SCM 不支援 Secret Text credential
// checkout([$class: 'GitSCM', userRemoteConfigs: [[credentialsId: gitlabAccount, url: gitURL]]])

Recommendation

  1. 已完成:BuildServer Jenkinsfile 的 credentialType 改為 'Any'defaultValue 改為 'cheng-csp-gitlab-token'
  2. 已完成:AutoBuildSiteDeploy Jenkins Job 的 Environment Variables 中 gitlabAccount 改為 cheng-csp-gitlab-token
  3. 已完成:AutoBuildSiteDeploy Checkout stage 改用 PRIVATE-TOKEN header 方式,已通過 Build #571 驗證
  4. 短期:確認 common@feat/shallow-fetch 分支在合併至 main 前,不在任何生產 pipeline 中引用
  5. 長期:建立 shared library 版本管理策略,生產 pipeline 只使用 main 或穩定 tag,功能分支僅在測試環境使用
  6. 長期:在 feat/shallow-fetch 合併後,確認所有使用 git.shallowFetch() 的 pipeline 都已統一使用 Secret Text credential

本文檔由 Semi-Brain 自動生成

Session ID: 85f4d935-df5b-46e9-9e46-529869bf6e72

分析信心度: 97%