失敗的重新發布絕不會刪除運作中的後端
9 月 25 日,一次失敗的重新發布刪掉了某個應用程式的後端,而那個後端已經穩定運作了大約兩個半小時。負責復原失敗發布的程式碼,把發布過程中碰過的每一個伺服器應用程式都移除了,包括在發布開始前就已經在提供服務的應用程式。現在只有發布時新建的應用程式會被刪除;已經存在的應用程式會被還原到它在發布前執行的確切版本。同一天的另外兩項修正,確保復原在託管平台自己的部署工具失敗時也會執行,並且健康的應用程式不會只因為當時處於休眠狀態就被判定為失敗。
後端如何發布
當你的 Mythex 應用程式包含後端(也就是 API 或伺服器,而不只是靜態檔案)時,發布會先用 Dockerfile(將應用程式打包成映像檔的配方)建置它,接著把這個映像檔部署到我們託管平台上的機器。然後健康檢查會等待新版本真正啟動並穩定運作,發布才會被標示為上線。
過程中只要有任何環節失敗,就會執行復原來撤銷變更。把復原做對,比發布流程中幾乎任何其他部分都重要,因為它正是在某件事已經出錯時才會執行。
問題一:復原刪掉了本該還原的東西
復原程式會記錄一份清單,列出發布部署過的伺服器應用程式;一旦失敗,就把清單上的每個應用程式全部銷毀。對第一次發布來說,這樣做沒有問題:應用程式是剛剛才建立的,沒有任何東西依賴它,刪除後就回到原來的狀態。
但對重新發布來說,這樣做就完全錯了。應用程式已經存在,並且正在為真實的訪客提供服務。9 月 25 日,一個已經上線約兩個半小時的應用程式 API,就因為一次失敗的重新發布而被刪除。
同一段程式碼裡還藏著第二種失敗模式。如果刪除沒有成功,壞掉的新映像檔就會留在機器上,而我們卻告訴擁有者他們目前的網站仍在線上。健康檢查失敗時,新映像檔早已部署到每一台機器上。什麼都不做,並不等於已經復原。
修正方式:只銷毀自己建立的,其餘還原
現在每次發布都會為它所碰觸的每個伺服器應用程式記錄:
- 這次發布是否建立了它,以及
- 如果不是,它在發布前執行的是哪個映像檔。
復原失敗時,每個應用程式會依下列三種情況處理:
- **由本次發布建立:**照舊銷毀。
- **原本就存在,且已知先前的映像檔:**用相同設定重新部署先前的映像檔。不需要建置,只是把映像檔換回去。
- **原本就存在,但先前的映像檔未知:**不予處理。已存在的應用程式永遠不會被刪除,即使我們無法還原它。
這些規則是一個小型的純函式,並附有自己的測試,因此只要有測試失敗,決策就無法退回「全部銷毀」的做法。
問題二:部署工具可能在我們記下如何復原之前就失敗
修正之後不久,我們發現還有一個缺口。託管平台的部署工具會自行執行冒煙測試:如果新機器當機,它就會以錯誤結束。但這個動作發生在新映像檔已經部署到機器上之後。
我們的程式直到部署工具成功返回後,才會把應用程式登記到復原清單。當工具本身讓部署失敗時,應用程式還沒有進入復原清單,因此什麼都不會被還原。在我們的預備環境中,一次壞掉的重新發布就讓壞掉的映像檔繼續提供服務。
修正方式:先登記復原,再開始動作
現在,應用程式一經準備好、還沒有把任何東西部署上去之前,就會登記到復原清單,並記下它是否為新建立、以及它原本執行的映像檔。不論之後哪個步驟失敗,復原都知道該還原什麼。我們在本機驗證過:重新發布在部署工具自己的冒煙測試中失敗,而先前的版本仍持續回應請求。
問題三:休眠中的應用程式被判定為「從未啟動」
Mythex 上已發布的應用程式閒置時會休眠,以節省你的點數。重新發布會讓休眠的機器維持停止狀態,因此健康檢查必須把它喚醒。過去的做法是請求平台啟動機器一次,然後忽略回應。
9 月 25 日,那一次啟動請求沒有得到任何結果。一個健康的 API 就這樣停在停止狀態,整整五分鐘的健康檢查期間都沒有起來,最後被判定為「1 台機器從未啟動(最後狀態:stopped)」。
平台有時確實會拒絕啟動。我們在重現這個問題時,平台在我們沒有刻意觸發的情況下回傳了「lease currently held」衝突;而當機器正在更新時,它也會拒絕啟動。
修正方式:持續要求,並主動叫醒
健康檢查現在會:
- 在等待期間,每 20 秒再次要求啟動停止中的機器,
- 記錄平台拒絕時的回應內容,
- 並像一般訪客那樣向應用程式發送一次普通請求。平台的代理在收到請求時會喚醒休眠中的機器。
我們用一個第一次啟動會被拒絕的臨時應用程式,在真實平台上重現了這個問題。修正前,發布在 91 秒後失敗;修正後,20 秒內就完成發布。真正在開機時當機的應用程式仍然會失敗,這是正確的結果。
我們的收穫
- **復原必須分辨「新建立」與「原本就存在」。**對你建立的東西來說,「復原」就是刪除;對原本就存在的東西來說,「復原」則是還原。把兩者等同看待,正是安全機制刪掉運作中應用程式的原因。
- **行動之前,先寫下如何復原。**如果只有在某個步驟成功後才記錄復原方式,那麼一個中途失敗的步驟,就沒有可以用來復原的東西。
- **絕不忽略你所依賴的請求的回應。**一個被忽略的「不行」,就讓一個健康的應用程式變成發布失敗。
同一天的另一項修正是:應用程式在發布後立即當機的攔截機制。若發布在你這一端失敗,請參閱文件中的發布疑難排解。