將 Django 應用程式部署到 Heroku,與使用 conda 環境的注意事項
好的,其實這篇本來沒有在預計要寫的文章中,但是它橫空出世的原因是我原本以為前幾天才剛用 Heroku 部署了一個網站(就是這個網站)(這個部署過程又是另一個警世故事了,還因為遲遲無法將資料庫從 SQLite3 搬遷到 PostgreSQL 而打掉重練),今天想說一鼓作氣再把另一個網站(今天的主角)也上線,再做一次,應該能夠行雲流水地搞定吧?
當然不。
先介紹一下本日主角:這是在 Anaconda 環境下用 Django(一個用 Python 寫成的網頁框架)做的網頁應用程式,資料庫原本是用 SQLite3,部署時將 production 環境的資料庫改成(受 Heroku 寵愛的)PostgreSQL。
儘管同時參照擁有清楚說明的 MDN 教學 與 Heroku 本身的文件,除了更改資料庫設定、重新設定應用程式金鑰位置等等之外,也依序新增了 Procfile, requirements.txt, 與 runtime.txt,還有下載相關的套件(這邊就不細講了,連結裡面都有詳細的說明),但是在最後執行 git push heroku master 時,出現了令人匪夷所思的狀況:
Could not find a version that satisfies the requirement anaconda-client==1.6.14
Heroku 會要求 anaconda-client 這個套件是因為 requirements.txt 中有將 anaconda-client 列出來,而 requirements.txt 是使用了 pip3 freeze > requirements.txt 這個指令製造出來的,這個指令翻譯出來就是「把現在這個環境中有安裝的套件都列出來,存進一個叫做 requirements.txt 的檔案裡,如果沒有這個檔案就製造一個」,將一個 Django 應用程式部署到 Heroku 時,這是一個必要的檔案,讓 Heroku 知道這個應用程式需要搭配哪些套件。
而 Heroku 報錯的這個狀況讓我覺得匪夷所思的原因是,它明明就有被安裝在我的環境啊(我在 conda 的 default 環境 (base)中,使用指令 conda list env 可以看到你在 conda 有哪些虛擬環境,而指令 conda list 或 pip list 都可以讓你看到這個環境裝了哪些套件)。順帶一提,其實原本開這個專案時想要使用比較常被拿來舉例的 virtualenv,但最後沒有,為什麼呢?因為忘了XD 總之 conda 與 virtualenv 做的事基本上是一樣的,也就是讓你開虛擬環境(不過 conda 可以做更多,因為 conda 主會出現的主要原因是看見了 virtualenv 的限制,可以參考 Stack Overflow 的這個回答))。
後來自己摸索了一段時間後,發現應該是因為我是在 conda base 的環境中,裡面其實有很多跟這個專案無關的套件,而不必要的套件一多就會出現像是相依性(dependency)的問題。
怎麼解?一開始的想法是,既然 Heroku 看的是 requirements.txt 這個檔案來部署套件,但現在這個檔案裡面有太多不相干的套件,那就直接修改檔案,暴力幫它瘦身!
於是,一個原本有 216 行的檔案,被刪到只剩下 10 行左右XD
刪完之後,果真 push 沒問題了,但是當我滿心期待的打下
heroku open
打開我的專案時,卻打不開。用 heroku logs — tail 檢查 log 狀態時,找到了
heroku[router]: at=error code=H10 desc=”App crashed”
這個非常含糊的錯誤。
錯誤的原因實在可能有太多,但我想,十之八九是跟剛剛被我手動去蕪存菁的 requirements.txt 檔案有關,畢竟當時刪除套件的時候,完全只憑藉我個人微薄的網路知識與不到兩個月的 Django 學習經驗來判斷,非常有可能刪到了沒有被直接指名卻默默被需要的套件們。
既然我不能自己判斷什麼能刪什麼不能刪,而產生這個清單的 pip3 freeze 指令又會把所有在這個環境中的套件列出來,那麼答案很明顯了,就是創一個虛擬環境,在環境裡面重新安裝那些我需要的套件,這樣這個環境中就只會有系統自動裝好的(也就是系統自己需要的套件)與我需要的套件了。
於是在終端機打
conda create — name django_env — python=3.6.6
source activate django_env
第一行指令創立了一個叫做 django_env 的虛擬環境,使用 version = 3.6.6 的 Python。之之所以會使用 3.6.6 版本是因為 Heroku 建議使用 3.6.6 或是 3.7.0 的版本(在我書寫的現在)。第二行指令進入 django_env 這個環境。
可以用 conda env list 再確認一次。成功進入的話就會在命令提示元前面出現(你的虛擬環境)的提示。
進來之後,你至少會需要裝的有 django, gunicorn, dj-database-url, psycopg2, whitenoise, 我自己則是再裝了 ipython,基本上你在 source code 裡面有引入的套件都要裝過一遍,這樣輸出的時候才會正確。安裝的方法是
pip install [package_name]
重點來了!
在 conda 的虛擬環境中,使用 pip3 跟 pip 是不一樣的!在一般的情況下,pip 與 pip3 是可以交互使用的,至於為什麼會出現這兩種指令就暫且按下不表,有心的可以自己 google 一下,總之,在 conda 的虛擬環境中,使用 pip 做的舉動才會是專屬於這個虛擬環境的,使用 pip3 下指令則這個虛擬環境會被直接無視。 舉個例子:
同樣在 django_env 這個環境,同樣是 install django 這個指令,如果用 pip3 下的話,會出現「Requirement already satisfied:…(略)」的訊息,但是如果是用 pip 下,則會開始跑安裝程序,並把 django 裝在 /anaconda3/envs/django_env/ 目錄下面。
所以一定要記得安裝的時候要用 pip 而不是 pip3(尤其像是剛剛介紹的 MDN 教學 都是叫你用 pip3,很容易直接複製貼上),輸出的時候也要用 pip freeze > requirements.txt。
好了!現在我們有了一份乾淨又簡潔的 requirement list,重新推上 heroku,成功!!!
沒有成功。再次出現了
Could not find a version that satisfies the requirement ……
的訊息,只是這次是另一個套件。這時候該怎麼做呢?使用虛擬環境的好處就是,你可以隨意的玩(破壞),如果真的被玩壞了就刪掉環境重新裝一個就好,所以既然這個套件出問題,那就先 pip uninstall [這個不配合的套件名字] ,重新輸出一次,看看結果如何。
結果就成功了。
所以說,有時候就是需要一些誤打誤撞的勇氣。
還有,將 django 應用程式部署到 heroku 上時,記得善用 virtual environment (不管是 virtualenv 還是 conda),可以避免引入不必要的套件,日後也比較好管理與 debug。最重要的是,記得 pip 與 pip3 的分別。
雖然已盡能力所及地確保資料的正確性,但我恐怕還是會有不對/不精確的觀念或用字,如果願意指正我的話我會非常感激!
文章同步發表於 Medium。