如何在 Travis CI 加快 Python 單元測試速度

StreetVoice 是用 Travis-CI 來跑自動測試, 這次透過 Travis-CI 的 cache 機制搭配 pip wheel 來避免重新編譯部分套件, 來達成流程加速, 透過這篇文章來記錄、分享一下心得。

先說結論: 安裝 Python 套件的時間從 14x 秒, 變成了 4x 秒。

下面先附上兩個截圖, 分別是調整前調整後:

Before

After

內容

  • 緣起
  • 真正的問題
  • Travis CI 的快取
  • 使用 pip wheel
  • 流程概略
  • 心得

緣起

原本一開始只是因為先前從 pocket 跳槽到 instapaper 後, 開始用 reread.io 來清空原本在 Pocket 裡面的文章, 他會每天寄送一封包含 10 個 Pocket 連結的 email 給我, 我就一天清空十篇。

當然有的看、有的不看 (手勢)

而前幾天剛好透過 reread.io 發現這篇兩年多前存到 Pocket 的文章 Effective TDD: Tricks to speed up Django tests (up to 10x faster!), 想說剛好禮拜五, 那就來試著照做一下好了。

註: 我們禮拜五通常不做一些重大 feature 的部署, 或是盡量不部署, 禮拜五通常拿來做一些有趣或是改善流程的事情。 但老實說, 因為時常太忙, 長忘記要做這類事情 XD 但是我覺得這件事真的滿重要的。

不過其實 StreetVoice 部分的單元測試已經有調整過了, 所以這篇文章可以 follow 得點其實也不是真的太多。 實際調整後發現用處不大, 改善的速度有限, 可能也因為這篇文章久了 XD

真正的問題

後來覺得最慢的其實還是 pip 安裝其他 Python / Django 相關的套件的時間, 因為 StreetVoice 用了很多有的沒的, 所以光是安裝 Python / Django 套件就需要兩分多鐘了, 所以後來轉向解決這個問題。

Travis CI 的快取

測試的過程中, 發現可能是因為改了 .travis.yml 預設的結構, 所以快取機制實際上沒有啟用。

原本 .travis.yml 裡面只要有 cache: pip 這一行就會預設快取 pip 安裝的相關套件了, 不過後來調整成下面這樣後才有真正的被快取到。

cache:  
  directories:
    - $HOME/.cache/pip

使用 pip wheel

再來就是要利用 pip wheel 來加快安裝速度, 什麽是 pip wheel? 簡單的說明如下

Wheel is a built-package format, and offers the advantage of not recompiling your software during every install. For more details, see the wheel docs: http://wheel.readthedocs.org/en/latest.

如果你有安裝像是 PIL, Pillow, 或是 numpy 這類不是純 python 寫的套件的話, 通常都會發現, 在 pip 安裝套件的過程中, 都還會需要在編譯一次。

所以即便原本我們的測試流程原本就已經有用上了 Travis-CI 的 pip cache 功能, 但還是需要重新編譯部分套件, 因為只 cache 了下載套件的部分, 而使用 pip wheel 可以避免再重新編譯, 速度加快很多。

首先, 因為目前 Travis-CI 目前預設的 pip 是 6, 所以得先升級個 pip, 把 pip 升級到 7, 然後再另外安裝 wheel。 聽說 pip 8 已經內建 wheel 了。

before_install:  
  - pip install -U pip
  - pip install wheel

再來, 設定一下 pipwheel 用的相關的系統變數:

env:  
  global:
    - PIP_WHEEL_DIR=$HOME/.cache/pip/wheels
    - PIP_FIND_LINKS=file://$HOME/.cache/pip/wheels

接著便可以透過下面兩行來安裝套件:

install:  
  - pip wheel -r requirements.txt
  - pip install -r requirements.txt

分別解釋一下:

  1. pip wheel -r requirements.txt

    根據先前的系統變數 PIP_WHEEL_DIR, 會把套件包好放進 $HOME/.cache/pip/wheels 裡。

  2. pip install -r requirements.txt

    根據 PIP_FIND_LINKS 的設定, 便是叫 pip 先去 $HOME/.cache/pip/wheels 找到包好的 wheel 套件來安裝。

流程概略

流程概略大致上是這樣:

  1. pip wheel 把套件都下載包進 X 目錄。
  2. Travis-CI 在流程跑完會把 X 目錄給 cache 起來。
  3. 每次 Travis-CI 的流程開始前, 會先把 cache 抓回來。
  4. 後續 pip wheel 看到 X 目錄已經有包好的 .whl 檔案後, 便不用再重新編譯一次套件了。

心得

CI 真的很重要, 尤其是他的速度, 特別是因為我們現在都透過 Travis-CI 觸發自動部署, 當 develop 分支 build 通過, 便會自動呼叫 saltstack 自動部署 develop 的程式到 staging 環境, 如果是 master build 成功, 則會自動部署到 production。

所以每次等待 build 成功到上線的時間其實都有點難熬

雖然這次透過調整 Django 相關設定的部分的效能並沒有很明顯, 後來我轉而去處理套件安裝, 不過隨著我們單元測試的增加, 我想往後還是會帶來一些效益, 畢竟我們現在 Coverage 大概才 50% (噓), 不過我想大家知道我們工程師人少 (笑)

倒是 ... 有興趣的寫 Python / Django 的 developer 可以來信給我: tzangms [at] streetvoice.com

tzangms

Read more posts by this author.

Subscribe to Oceanic / 海海人生

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!