Python 工程管理及 virtualenv 的遷移

virtualenv 是管理 python 工程的利器,它可以很好的幫你維護項目中的依賴餐屎,使用 virtualenv析命,還能保持 global 庫的干凈、不會被不同項目中的第三方庫所污染局骤。

virtualenv 的默認功能簡單好用,可一旦涉及到多人協(xié)作暴凑,或部署到不同的環(huán)境中時峦甩,錯誤的使用 virtualenv 會給你帶來一些麻煩,從而你需要花很多時間在解決這些問題上现喳。本文的目的就是總結(jié)過去使用 virtualenv 的經(jīng)驗凯傲,希望能幫你找到一種正確的打開方式。

首先嗦篱,創(chuàng)建一個空的 virtualenv 時冰单,你的目錄中會包含以下文件和目錄

drwxr-xr-x   7 fengyajie  staff   224B Mar 21 22:49 .
drwxr-xr-x   8 fengyajie  staff   256B Mar 21 20:28 ..
lrwxr-xr-x   1 fengyajie  staff    83B Mar 21 22:49 .Python -> /usr/local/Cellar/...
drwxr-xr-x  16 fengyajie  staff   512B Mar 21 22:49 bin
drwxr-xr-x   3 fengyajie  staff    96B Mar 21 22:49 include
drwxr-xr-x   3 fengyajie  staff    96B Mar 21 22:49 lib
-rw-r--r--   1 fengyajie  staff    61B Mar 21 22:49 pip-selfcheck.json

接著當(dāng)你執(zhí)行 source bin/activate 后,你安裝的依賴都會在 lib 目錄下灸促,這一點很誘人诫欠,會讓你覺得一切盡在掌握,因為該應(yīng)用程序所需要的一切庫文件全在這個 app 的根目錄下浴栽,所以當(dāng)這個應(yīng)用需要部署時荒叼,為了避免產(chǎn)生 ImportError: No module named xxx 錯誤,你會很容易的想到將本地這個 app 目錄打包典鸡,然后放到遠程服務(wù)器或容器中去執(zhí)行被廓。

當(dāng)你這么做時,你會發(fā)現(xiàn)雖然在遠程是可以執(zhí)行 source bin/activate 命令以進入 virtualenv 萝玷,但此時你引用的 python 可執(zhí)行文件卻并不是 ${app}/bin/pyhton伊者,而是 global 環(huán)境中的那個 /usr/bin/python英遭,所以 ${app}/lib 下的所有依賴包路徑仍然是沒有被包含進 sys.path 的。

這時亦渗,你才發(fā)現(xiàn)自己的假設(shè)是錯誤的,并開始懷疑自己使用 virtualenv 的方式存在問題汁尺,于是便 google 各種解決方案法精,但項目已處于部署階段,時間緊迫痴突,你很可能找不到最優(yōu)的辦法搂蜓,只能退而求其次,尋求次優(yōu)解辽装,畢竟依賴包都在嘛帮碰,改下 sys.path 不就好了嘛?確實很容易想到這種方法拾积,但又不想手動改殉挽,那就寫個程序改吧,也不難:

# set_sys_path.py
def set_sys_path():
    import sys
    for path in sys.path:
        if os.path.split(path)[1] == 'site-packages':
            home = os.path.abspath(os.path.dirname(__file__))
            pypath = os.path.join(home, 'lib/python2.7')
            pypath_sitepackage = os.path.join(home, 'lib/python2.7/site-packages')
            pth = os.path.join(path, 'pth.pth')
            with open(pth, 'w') as f:
                    f.write("%s\n" % pypath)
                    f.write("%s\n" % pypath_sitepackage)

if __name__ == "__main__":
    set_sys_path()

上面的程序很簡單拓巧,它將 ${app}/lib/python2.7${app}/python2.7/site-packages 兩個依賴路徑寫到 pth.pth 文件中斯碌,并將該文件 mv 到 global 的 site-packages 目錄下,這樣當(dāng)你啟動 global 的 python 時肛度,會自動將 pth.pth 里的路徑添加到 sys.path 下傻唾,這樣只需要在啟動你的 app 之前,執(zhí)行該腳本即可承耿,如下:

$ python set_sys_path.py
$ python main.py

問題暫時解決了冠骄,這次你的 app 也順利發(fā)布了;但還沒結(jié)束加袋,我們希望在測試機集群上把 app 的自動化測試做起來凛辣,在做自動化測試時,系統(tǒng)會隨機給你分配一臺機器資源锁荔,當(dāng)測試完成后蟀给,資源會被回收。你心想阳堕,這仍然很簡單嘛跋理,本地測試已經(jīng)覆蓋得很全了,只要自動化系統(tǒng)利用 git 把代碼拉下來恬总,先執(zhí)行 set_sys_path.py 設(shè)置 sys.path前普,再執(zhí)行 python test.py(測試入口)就可以了。

可這時又出現(xiàn)問題了壹堰,自動化測試在執(zhí)行 set_sys_path.py 時拭卿,報 Permission denied 錯誤骡湖,原因是測試機為了保持環(huán)境不被污染,不允許你將 pth.pth 復(fù)制到 global 的 site-packages 下峻厚。

遇到這個問題怎么辦响蕴?其實也很容易解決:我們都知道 python 中有個環(huán)境變量 PYTHONPATH 可以用來設(shè)置 sys.path,既然沒有寫文件的權(quán)限惠桃,那定義環(huán)境變量總該可以吧:

$ export PYTHONPATH=$PYTHONPATH:${app}/lib/python2.7:${app}/lib/python2.7/site-packages
$ python main.py

果然可行浦夷,你再一次「順利」的完成了需求。

經(jīng)歷過多次折騰后辜王,我們發(fā)現(xiàn)這種使用 virtualenv 和修改 sys.path 的方法不算很好劈狐,還容易出錯。于是開始思考最初的那個問題呐馆,virtualenv 該怎么遷移肥缔?有沒有更好的辦法?答案肯定是有的汹来,在此之前续膳,我們先仔細觀察 virtualenv 產(chǎn)生的文件,會發(fā)現(xiàn)其中有 28 個軟連接俗慈,它們的源文件均在 global 庫中姑宽,如下所示

$ find . -type l
./.Python
./bin/python
./bin/python2
./include/python2.7
./lib/python2.7/lib-dynload
./lib/python2.7/encodings
...

所以,當(dāng)你把整個 virtualenv 打包闺阱,放到另一個環(huán)境中運行時炮车,肯定是會失敗的,因為軟連接失效了酣溃,于是,再一次證實這種把整個 virtualenv 打包的方法赊豌,實際上是錯誤的扛或,virtualenv 就只是一個 local 方案,而不是讓你可以「處處運行」的工具碘饼。

但 virtualenv 的隔離功能熙兔,可以讓你只關(guān)注項目范圍內(nèi)的依賴包,所以我們可以利用 pip freeze 命令艾恼,將項目內(nèi)的依賴保存到一個叫 requirements.txt 的文件中住涉,這樣在任何其他環(huán)境,我們只要根據(jù) requirements.txt 文件來安裝項目所需的依賴包钠绍,即可將本地的運行環(huán)境克隆出來舆声,而且這種克隆出來的環(huán)境更純粹,不會受到源環(huán)境或 global 庫的影響,沒有不確定性媳握。下面我們用一個例子來具體說明下:

假設(shè) Bob 和 Alice 同在一個團隊碱屁,他們決定使用 python 來開發(fā)新項目,一開始蛾找,Bob 在 github 上創(chuàng)建了一個新 repo娩脾,并在本地初始化它:

# 從 github clone 項目
$ git clone https://github.com/your_group/your_repo.git
$ cd your_repo
# 創(chuàng)建并進入 virtualenv
$ virtualenv .
$ source bin/activate
# 修改 .gitignore,過濾掉 virtualenv 產(chǎn)出的文件
$ cat .gitignore
*.py[cod]
__pycache__/
.Python
bin/
include/
lib/
pip-selfcheck.json
# 在本地安裝基本依賴打毛,例如 Flask晦雨、gevent、gunicorn 等
$ pip install Flask gevent gunicorn -i https://pypi.mirrors.ustc.edu.cn/simple/
# 將本地依賴寫入 requirements.txt
$ pip freeze > requirements.txt
# 將變更提交到 github
$ git add .
$ git commit -m "init project"
$ git push origin master
# 繼續(xù)開發(fā)
# ...

Bob 完成了初始化隘冲,實際上他只提交了 .gitignorerequirements.txt 兩個文件到 git 中,之后 Alice 也可以加入進來了:

# 從 github clone 項目
$ git clone https://github.com/your_group/your_repo.git
$ cd your_repo
# 創(chuàng)建并進入 virtualenv
$ virtualenv .
$ source bin/activate
# 根據(jù) requirements.txt 文件下載項目所需的依賴
$ pip install Flask gevent gunicorn -i https://pypi.mirrors.ustc.edu.cn/simple/
# 繼續(xù)開發(fā)
# ...

可以看到绑雄,通過這樣的步驟展辞,Bob 和 Alice 不僅有了一摸一樣的開發(fā)環(huán)境,還能最小化 git 倉庫的大小万牺,且按照這樣的思路罗珍,他們還可以把相同的環(huán)境克隆到測試機上,以及 Docker 鏡像中脚粟。顯然覆旱,這種一致性不僅可以提高開發(fā)效率,還可以提高后續(xù)的運維效率核无。

相關(guān)文章:

參考:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扣唱,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子团南,更是在濱河造成了極大的恐慌噪沙,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吐根,死亡現(xiàn)場離奇詭異正歼,居然都是意外死亡,警方通過查閱死者的電腦和手機拷橘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門局义,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人冗疮,你說我怎么就攤上這事萄唇。” “怎么了赌厅?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵穷绵,是天一觀的道長。 經(jīng)常有香客問我特愿,道長仲墨,這世上最難降的妖魔是什么勾缭? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮目养,結(jié)果婚禮上俩由,老公的妹妹穿的比我還像新娘。我一直安慰自己癌蚁,他們只是感情好幻梯,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著努释,像睡著了一般碘梢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伐蒂,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天煞躬,我揣著相機與錄音,去河邊找鬼逸邦。 笑死恩沛,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缕减。 我是一名探鬼主播雷客,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼桥狡!你這毒婦竟也來了搅裙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤总放,失蹤者是張志新(化名)和其女友劉穎呈宇,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體局雄,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡甥啄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了炬搭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜈漓。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖宫盔,靈堂內(nèi)的尸體忽然破棺而出融虽,到底是詐尸還是另有隱情,我是刑警寧澤灼芭,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布往衷,位于F島的核電站效诅,受9級特大地震影響适秩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茴迁,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望萤衰。 院中可真熱鬧堕义,春花似錦、人聲如沸脆栋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椿争。三九已至怕膛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秦踪,已是汗流浹背嘉竟。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留洋侨,地道東北人。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓倦蚪,卻偏偏與公主長得像希坚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子陵且,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容