cron + python 實現自動部署
網站並不是一次部署就能一勞永逸了,每當本地的代碼有更新後,還需要重新部署一遍。而往往部署代碼的流程基本上都是相似的,沒必要每次都手動部署。網路上有不少自動部署網站的方法,比如Fabric和webhook等。Fabric不支持python3,我又不想再去學python2,就想著使用別的辦法來實現自動化部署。最後我選擇使用python腳本 + cron定時任務來完成一些簡單的部署工作。
以本站的Django項目為例,部署的大致流程是:
- git拉取最新的項目代碼
- 如果有新的依賴,則安裝新的依賴
- 如果Model發生了改變,遷移資料庫
- 重啟伺服器
實現自動部署的思路很簡單,用python腳本執行以上的命令,然後設置cronjob,讓該腳本每天定時運行就行。這樣每當我們將新代碼上傳至遠程倉庫後,伺服器就會自動下載更新代碼。
1、用python執行shell命令
用python的subprocess可以向shell中輸入命令,只需調用subprocess.run(command)
即可。
run
函數的第一個參數是字元串或列表,輸入列表即把shell命令的每個部分分開,或者直接輸入完整的命令字元串,輸入字元串時還需指定參數shell=True
。
run函數返回一個CompletedProcess對象,包括輸入的命令和返回碼,返回碼為0時表示命令正常執行,非0表示產生異常。
默認情況下命令產生的輸出和錯誤信息會直接列印出來,指定stdout=subprocess.PIPE
和stderr=subprocess.PIPE
參數可以收集在CompletedProcess對象中,同時還需指定編碼方式encoding=utf-8
。
CompletedProcess.check_returncode()
檢查返回碼,非0則拋出CalledProcessError。
例如,在python解釋器中執行git status:
>>> import subprocess>>> subprocess.run([git, status])On branch masterYour branch is up to date with origin/master.nothing to commit, working tree cleanCompletedProcess(args=[git, status], returncode=0)
2、發送部署結果郵件
自動化部署的python腳本運行在伺服器上,部署的結果可以通過郵件發送到自己的郵箱中,這樣不用登錄伺服器也能知道部署的情況。
Python內置對SMTP的支持,可以發送純文本郵件、HTML郵件以及帶附件的郵件。Python對SMTP支持有smtplib和email兩個模塊,email負責構造郵件,smtplib負責發送郵件。
Django提供了包裝好的模塊django.core.mail,可以更便捷地發送郵件,先在settings.py中設置發信代理,
EMAIL_HOST = smtp.mxhichina.comEMAIL_PORT = 465EMAIL_USE_SSL = TrueEMAIL_HOST_USER = from@example.comEMAIL_HOST_PASSWORD = ******
然後即可發送郵件。
from django.core.mail import send_mailsend_mail( 郵件標題, 郵件內容, from@example.com, [to@example.com],)
3、自動部署的示例代碼
import os, sys, subprocess, tracebackfrom django import setupfrom django.core.mail import send_mail# 切換工作目錄至指定的文件夾def prepare(): os.chdir(/path/to/project) sys.path.append(os.getcwd())# 固定常用參數,不用每次都寫那麼多重複代碼def run(args): return subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding=utf-8, shell=True)def main(): prepare() results = [] # 保存運行結果 try: results.append(run(git pull origin master:master)) # 出錯就停止運行 results[-1].check_returncode() if Already up-to-date. in results[-1].stdout: # 沒有更新就終止部署 return 0 # 安裝依賴 results.append(run(pip install -r requirement.txt)) results[-1].check_returncode() results.append(run(python3 manage.py makemigrations)) results[-1].check_returncode() # 如果沒資料庫變化就不需要執行migrate if No changes detected in results[-1].stdout: results.append(run(python3 manage.py migrate)) results[-1].check_returncode() # 重啟伺服器 results.append(run(apachectl restart)) if results[-1].returncode: # 重啟伺服器的過程中要是出錯就得回滾git版本 # 然後重啟,避免伺服器宕機 error_result = results[-1] results.append(run(apachectl status)) if Active: active (running) not in results[-1].stdout: results.append(run(git reset --hard HEAD^)) results.append(run(apachectl restart)) error_result.check_returncode() # 處理錯誤信息 except Exception: message = 部署過程中發生了錯誤!
+ traceback.format_exc() +
else: message = 部署完成
# 由於重啟伺服器需要root許可權因此要以root用戶運行該段代碼 # 部署完畢後將新添加的文件許可權改回普通用戶 results.append(run(chown -R fossen:fossen /path/to/project)) # 格式化部署信息 message +=
——————詳情——————
for r in results: message += 命令:{} | code:{}
stdout:{}
stderr:{}
——————————————
.format(str(r.args), str(r.returncode), str(r.stdout), str(r.stderr)) # 用django自帶的函數發送部署結果郵件 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoproject.settings") setup() # 在setting.py中定義郵箱與密碼 send_mail(自動部署結果, message, admin@fossen.cn,[fossen@fossen.cn]))if __name__ == "__main__": main()
4、設置cron任務
由於重啟伺服器需要root許可權,最好以root身份運行自動部署程序,直接修改crontab
sudo vim /etc/crontab
添加命令,下面這條命令意味著每天凌晨3點自動更新項目代碼。
0 3 * * * root python /path/to/project/deployment.py
重啟crond程序,使命令生效
service crond restart
全部設置好了以後,只要git上傳代碼,伺服器就會自動檢查更新並部署代碼了,接著就是坐等部署結果的郵件。
這樣自動部署的缺點還是很明顯的,部署過程中難免有bug,但我們卻沒辦法第一時間知道。所以還是要多檢查自己的郵箱,免得哪天網站癱了都不知道。
如果某次部署比較可能產生bug,最好直接在伺服器上運行該腳本,即時排查問題。
推薦閱讀:
※centos升級python2.6至2.7
※為什麼有人說 Python 的多線程是雞肋呢?
※如何挑選你的第一門編程語言
※2. Add Two Numbers