从零开始自动部署Django项目(二):使用Python编写Git Hooks

本文介绍如何通过Python编写的Git Hooks,在向生产服务推送时自动部署Django项目。内容包括杀掉Python debug server进程,更新前端和后端的release分支,以及重启服务器。文章详细阐述了实现过程中的关键步骤和技术,如使用subprocess模块执行shell命令,解决Git Hooks执行时的$GIT_DIR问题,以及nohup启动debug server的注意事项。

引言

在上一篇从零开始自动部署Django项目(一):开发配置与生产配置,已经给出了通过环境变量来实现不同配置选择的解决方案。既然是环境变量,那就可以通过shell脚本或者python脚本来实现。
接下来,我将通过使用python编写的Git Hooks来实现在向生产服务Git推送的时候完成自动部署。
不太了解Git Hooks的同学可以参考我这篇文章: 服务端自动部署静态项目的几种方法

准备

首先明确Git Hooks要完成哪些任务:

  • 判断python debug server在centos系统服务器上是否运行在指定端口,如果在运行则杀掉该进程。
  • git pull 前端的release分支。
  • git pull 后端的release分支并通过nohup重启python debug server。

明确要完成的任务之后,还有几点利用Python编写Git Hooks要注意的地方:

杀掉Python debug server进程

如果server 进程运行在9999端口,使用linux提供的命令如何杀掉进程呢?
首先通过lsof命令观察9999端口是否有进程运行,如果有则获取该进程的pid,得到以后通过kill命令杀掉该进程。因此,我们要在Python中完成对lsof命令输出的截取。
其中,subprocess.call()调用会返回命令执行的状态,1表示错误,0表示正常执行。

import subprocess
import os
import signal

# kill the python debug server
server_status = subprocess.call(lsof_command, shell=True)

if server_status == 0:
    server_res_bytes = subprocess.check_output("lost -i:9999 | tail -n 1", shell=True)
    server_res = server_res_bytes.split()
    python_server_pid = int(server_res[1])
    try:
        os.kill(python_server_pid, signal.SIGKILL)
    except OSError:
        pass

更新前端和后端分支

在确认Python debug server已经被杀掉的基础上,可以进行对分支的pull和checkout了。

# checkout the FrontEnd branch
os.chdir(frontend_path)
subprocess.call("git fetch --all", shell=True)
subprocess.call("git reset --hard origin/" + fronend_branch, shell=True)

# checkout the BackEnd branch
os.chdir(backend_path)
subprocess.call("git fetch --all", shell=True)
subprocess.call("git reset --hard origin/" + backend_branch, shell=True)

OK,但是在这里我遇到了一个神秘的错误:

remote: fatal: Not a git repository: '.'

这是为什么呢?原来Git Hooks在执行的时候会设置$GIT_DIR变量为.而并非.git,这会导致Git会去寻找./HEAD,而并非.git/HEAD,因此在hooks脚本一开始需要执行unset GIT_DIR或者把GIT_DIR设置成.git。
在一开始的时候,我直接调用了subprocess.call(‘unset GIT_DIR’, shell=True),然而并不起作用,这是因为subprocess只是在子进程中unset了变量,而并非当前的Python进程,因此我要通过os.environ影响当前Python进程衍生的子进程。

# didn't work
subprocess.call('unset GIT_DIR', shell=True)

# work fine
os.unsetenv('GIT_DIR')

stackoveflow传送门:Git checkout in post-receive hook: “Not a git repository ‘.’”
以及Git如果在Git仓库中Git_DIR就会被设置成.git,而非git仓库的时候就是.,而Git命令在执行的时候会默认去寻找.git目录。传送门:Receiving “fatal: Not a git repository” when attempting to remote add a Git repo

重启Python debug server

最后只需要通过manage.py 重启debug server就可以了,但在这之前需要设置上一篇中提及在生产环境的环境变量。

# restart the python debug server
os.putenv('DJANGO_PRODUCTION_SETTINGS', 'TRUE')
try:
    subprocess.call("nohup python manage.py runserver 9999 &", shell=True)
except Exception:
    pass

OK,通过nohup启动之后我尴尬地发现在提交的时候远程卡住了除非我发出了终止指令。。。
这是因为通过ssh启动的nohup必须要重定向stdin,stdout,stderr。
stackoveflow传送门:Use SSH to start a background process on a remote server, and exit session
简单说一下linux的重定向,0是标准输入,1是标准输出,2是错误输出,如今我想把1和2都输入到一个文件中,不能直接使用1> file 2> file,因为这会引起文件的接受混乱,必须用特殊的语法,比如2>&1。

# work fine
subprocess.call("nohup python manage.py runserver 9999 > /nohup.out 2>&1 &", shell=True)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值