如何在正式環境升級到 Django 2

Sandi Wu
iCHEF
Published in
6 min readMar 11, 2022

這篇適合:想在正式服務中零停機的升級 Django。

會涵蓋的內容:為什麼升級?怎麼升級?升級有什麼建議?

為什麼要升級?

最主要的動力當然是新功能,Django 2 多了 ORM 支援 Window functions、新的 URL routing syntax、支援手機互動的 Admin 頁面等。

Django 2.2 也多了一些實用的功能,像是bulk_create()可以使用 ignore_conflicts來跳過錯誤不影響其他建立、Aggregate多了 DISTINCT可以使用、 RelatedManager的操作也可以用在Many-to-many上、Meta.ordering 不會再影響 GROUP BY、用 condition可以達到 partial index的效果、新增 bulk_update、ORM Model 有了 database constraint等等。

反面來看,Django 已經在 2021/12/7 釋出 Django 4,現在似乎是個升級 Django 2 的好機會。

要怎麼升級呢?

這次執行的升級是 Django 1.11 升級到 Django 2.0。那為什麼不一次升級到 Django 2.2 或是 Django 3 呢?升級的過程中,如果是影響範圍比較大的套件,通常會建議一次升級只升到最近的版號,避免一次更改過大、會有更多不相容、需要注意的文件和細節更多,也避免心太累而放棄。

確定要升級後,接著就是升級計畫。因為 iCHEF 是隨時都有使用者的服務,目標是 Zero Downtime (零停機) 的完成升級計畫,大致的步驟如下:

  1. Local 升級後跑測試
  2. 在正式環境開啟 DeprecationWarning 並追蹤
  3. 測試環境跑自動化
  4. 多設置新的 Stack 平行跑並分流
  5. 正式上線 Django 2

Local 升級後跑測試

如果你的服務有寫測試,那第一步可以升級到 Django 2 後,直接 local 或是用 CI 跑測試。

Django 會提醒你哪些東西已經 deprecated 了,Django 文件中基本上都有寫,有些也會直接在 Warning 中建議使用的寫法。Warning 範例如下:

RemovedInDjango20Warning: `Options.virtual_fields` is deprecated, use `private_fields` instead.

注意:有一些相對應的套件需要升級,因為這次的做法會要在 Django 1 和 Django 2 之間進行切換,所以套件需要在 Django 1.11 和 Django 2 之間相容的。

在正式環境開啟 DeprecationWarning 並追蹤

只跑測試是不夠的,像是 iCHEF 測試覆蓋率雖然有 94%,但還是會有沒有覆蓋到的 Code 或是沒有覆蓋的情境。

所以,我們透過在正式環境中,不影響使用者的情況下,開啟 Python DeprecationWarning 並追蹤幾週 (iCHEF 是使用 CloudWatch Metrics),透過大量的使用者和不同的使用情境來確保 Code 都有被跑過,盡可能補足測試沒有覆蓋到、但實際上會產生出 DeprecationWarning 的 Code。

追蹤到 DeprecationWarning 後,就可以逐一修正,並也要記得確保 Django 1 和 2 都相容。

測試環境跑自動化

因為 iCHEF 的 QE 團隊有寫自動化測試,進一步善用自動化測試來確保重要的功能都可以使用,佈版到測試環境後,直接針對 Client 和 Server 之間的功能跑自動化測試,確保重要功能沒問題。

多設置新的 stack 平行跑並分流

因為 iCHEF 使用 AWS 的服務,這邊可以善用 Global Accelerator 的功能。

先從頻繁使用的測試環境開始,新增一個一模一樣的 Stack 後,新的 Stack 使用 Django 2 來佈版,DB 共用。一開始可以先分流 10% 到新的 Stack,沒有遇到什麼問題的話,逐漸增加流量,到 100% 為止,並持續追蹤有沒有出現什麼異常的錯誤。

跑了兩週後,正式環境也做一樣的設置,一有問題就可以用 Global Accelerator 切回去 Django 1 的環境。(要注意:Global Accelerator 還是會有小小的 Delay,不是即時的)

如果是自架的伺服器,也可以透過 Nginx 作為負載平衡器來分流,達到一樣的效果。

正式上線 Django 2

最後一步就是合進去,正式上線啦!

過程中遇到最大的跳戰?

主要花比較多時間修正的是 Django Rest Framework 升級和移除 Bytestring,如果你也在升級卻有些卡關,說不定是以下的問題。

Django Rest Framework 升級

因為 Django Rest Framework 也必須從 2 升級到 3,這邊的變化滿大的,沒有什麼快速的解法,就是用 Django 1 和 Django 2 都跑跑看,並搭配 Debug mode 一步一步看錯在哪裡。

移除 Bytestrings

Django 1 為了支援 Python 2 string,之前可以接受 Bytestrings 和 Unicode strings,從 Django 2 開始,不再支援 Python 2,所以也把 Bytestring 的支援拿掉。雖然修正的方法滿簡單的,只要用 decode() 把它轉成正確的格式,但在找錯的過程中比較難發現問題到底是什麼。

如果我也想升級,有什麼建議嗎?

完整的事前溝通

因為要在測試環境和正式環境都套用 Django 2,會影響到的內部、外部人員都很多,事前的溝通很重要,清楚地寫出「時間」、「影響範圍」、「發現問題要找誰」,工程部的公告範例如下:

【升級 Django 2 注意事項】

正式環境 從今天開始連續兩週(xx/xx-xx/xx)會分流一部份到 Django 2 的環境,對使用者沒有任何影響,上版流程不變,Django 2 的佈版我會處理,再麻煩有 hotfix 的話提前通知我 (晚上怕影響到值班的同事,下班前我會分流回去原本的 Django 1) 有任何疑問再麻煩跟我說。

流程:佈版流程、監測流程、出事流程

上版前要先確認機器都是準備好的,再把流量切過去。

過程中要有方法監測,像是 iCHEF 有透過 CloudWatch 監測 5xx 的比例、登入失敗的比例等等,以及用 Sentry 監測噴出的 Error。

如果出現問題了,初步判斷跟 Django 2 有關、且影響到大量使用者,就先切回到 Django 1 (在我測試的三週多內,有切換多次,但都是保險起見先切換,事後深入看後,並沒有 Django 2 造成的錯誤)。

Coverage 足夠

如果 Coverage 夠高,在 Local 升級 Django 2 跑測試,修正出現的錯誤就完成了一大半功夫。

但如果 Coverage 不夠高,也不用擔心升級之路遙不可及,用以上的方法在正式環境中開啟 DeprecationWarning、測試環境多執行一陣子,也是可以盡可能地讓功能都有被使用、Code 都有被執行到。

結語

這次的升級計畫,感謝另外一位同事協力合作,礙於我們兩個身上都有功能開發的專案需要優先完成,都是中間抽空檔來修正 CI 的錯誤,因此前前後後花了半年之久。期待之後升級 Django 2.1、Django 3、Django 4 都會順利快速許多!

--

--

Sandi Wu
iCHEF
Editor for

iCHEF Backend Engineer. Graduated from NTU Finance. Used to be an analyst at AppWorks.