// 這本是一個不應該出現的問題...
問題描述:
有一臺用來部署項目的 Jenkins 服務器,然後新開了一臺應用服務器,需要通過 Jenkins 將打包後的代碼部署到應用服務器
但由於權限控制,我不知道兩邊服務器的密碼,應用服務器(記爲 dev.server)也只能通過跳板機訪問
而 Jenkins 服務器(記爲 jenkins.server)我沒有任何辦法訪問,只能訪問搭建好的 Jenkins 頁面
在這樣的背景下, 如果在 Jenkins 腳本中通過 ssh 直連應用服務器
就會出現 Host key verification failed 錯誤
分析原因:
其實原因很簡單,通過 ssh 訪問服務器是需要攜帶認證信息的,比如用戶和密碼,或者通過公鑰免密登錄
但這兩個方案都不適用,因爲我既不知道應用服務器的密碼,也無法拿到 Jenkins 服務器的公鑰
解決方案:
於是一次迂迴大作戰打響了
第一回合:Jenkinsfile 攜帶私鑰
由於問題的本質是認證失敗,我便打算通過 Jenkins credentials 來處理
首先在 Jenkins 中創建一個類型爲 SSH Username with private key 的全局憑據
並將應用服務器的私鑰粘貼進去
cat /root/.ssh/id_rsa
// 完整流程可以參考《Jenkins----憑據管理之配置ssh私鑰》
然後需要調整 Jenkinsfile 腳本,用 withCredentials 方法將需要憑據的部分給包起來
比如我原本的腳本是:
stages {
stage('Init ssh server'){
steps {
sh "ssh ${env._HOST_NAME} 'mkdir -p ${env._DEPLOY_PATH}'"
}
}
}
加入憑據之後就是:
stages {
stage('Init ssh server'){
steps {
withCredentials(bindings: [
sshUserPrivateKey(credentialsId: 'jenkins-ssh-key-xxxx', keyFileVariable: 'SSH_KEY_XXX')
]) {
sh "ssh ${env._HOST_NAME} 'mkdir -p ${env._DEPLOY_PATH}'"
}
}
}
}
// 建議使用 Jenkins 的片段生成器
配置好之後,我再次回放腳本
在我期待的目光下,Console Output 又拋出那個熟悉錯誤 Host key verification failed
第二回合:嘗試配置免密登錄
withCredentials 的方案失敗之後,我再次梳理了一下整個流程,還是決定配置免密登錄
我先用自己的電腦來測試,獲取到本地的公鑰
cat ~/.ssh/id_rsa.pub
然後通過跳板機進入應用服務器,打開 authorized_keys 文件(若無則創建)
vi ~/.ssh/authorized_keys
然後將本地公鑰粘貼到應用服務器 authorized_keys 的末尾
如果知道應用服務器的密碼,上述過程還可以簡化爲一行命令:
ssh-copy-id user@hostname
運行之後需要輸入對應用戶的密碼,然後就會把本地的公鑰追加到 authorized_keys
此外還需要調整 ssh 的配置文件以及訪問權限,詳細配置可以參考《服務器配置免密碼密鑰登錄》
不過運維已經完成了應用服務器的基本配置,在修改了 authorized_keys 之後,我就能在本地鏈接應用服務器了
眼見本地連接成功,我急忙腆着臉去找 Jenkins 服務器的管理員,費了好多口舌,他終於把 Jenkins 服務器的公鑰給我了
隨後我將 Jenkins 服務器的公鑰也配到應用服務器的 authorized_keys 中,再次回放腳本
然而,結果依然是 Host key verification failed
同樣的配置,爲什麼本地能正常訪問,Jenkins 服務器卻無法訪問呢?
決戰:本地直連 Jenkins 服務器
我想了許久,也沒有想通本地與 Jenkins 服務器的差異
最後把心一橫,決定想辦法連上 Jenkins 服務器看看
可運維始終不肯開放跳板機權限,在我束手無措的時候,突然想到 Jenkinsfile 本身就是在 Jenkins 服務器上跑腳本
那麼... 我豈不是可以通過 Jenkins 腳本,將我本地的公鑰添加到 Jenkins 服務器的 authorized_keys 中,實現本地到 Jenkins 服務器的免密登錄?
於是我在 Jenkins 腳本中添加了這一行命令:
sh "echo 'ssh-rsa xxxxxx' >> ~/.ssh/authorized_keys"
其中的 'ssh-rsa xxxxxx' 就是我本地公鑰 id_rsa.pub
腳本跑完之後,我在本地使用 ssh 訪問 Jenkins 服務器:
沒有讓我輸入密碼,這說明免密登錄配置成功了。但有以下提示:
這個提示並不陌生, 首次連接服務器時都會有這個確認信息
確認之後,這個遠程服務器的信息就會記錄到本地的 ~/.ssh/known_hosts 中
我突然想到,Jenkins 服務器也是第一次訪問應用服務器,會不會也有這個確認過程?而在腳本中無法確認,所以就會報錯
此時我已經連上了 Jenkins 服務器,並且在應用服務器的 authorized_keys 中添加了 Jenkins 服務器的公鑰
接着我在 Jenkins 服務器上,再次嘗試通過 ssh 訪問應用服務器,果然出現了上面的確認提示!
我雙手顫抖着輸入 yes,隨後回到 Jenkins 部署頁面,回放腳本
這次一路暢通,沒有出現任何報錯。看到腳本執行成功的綠色標記,我長舒了一口氣
再來回溯整個問題,其實就是 known_hosts 導致的。如果解決了 known_hosts 的問題,使用 withCredentials 也是可以的
最後補充一篇《SSH之known_hosts文件》作爲參考~