標籤:

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.PIPEstderr=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

TAG:Python | 部署 | Cron |