IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> apache2.4 + django + windows10 自动化部署 -> 正文阅读

[Python知识库]apache2.4 + django + windows10 自动化部署

apache2.4 + django + windows10 自动化部署

在django项目中建立一个webhooks url 路由, 在该路由下异步执行自动化部署脚本 在未部署的时候, 访问该路由 是没有问题的 成功执行了脚本

脚本涉及两个内容 拉取更新本地代码 和 重启 apache2.4

apache2.4 部署djano之后, 项目访问没有问题,但是在执行自动化部署脚本的时候 遇到了如下问题

  1. 找不到虚拟环境下python 解释器的路径, sys.executable 返回的是 httpd.exe 的路径

  2. 自动化脚本中不能引入django项目中的包文件, 只能引入当前python环境下的包文件!

  3. 自动化脚本中执行的环境变量与预期不同 %USERPROFILE% 路径 变为了 C:\\WINDOWS\\system32\\config\\systemprofile

apache2.4 部署之后 找不到当前python解释器的路径

  1. Bug 复现

    # 在 django 任意视图中输出 sys.executable
    def test(request)
    	"""
    	测试 视图
    	"""
        logger.info("sys.executable: {}".format(sys.executable))
    
    

    然后重启apache 访问该视图 你会惊讶 因为它输出的是: c:\apache24\bin\httpd.exe

    why???

    抱着疑惑 我直接打印了sys.path 试图 找到 python 路径

    但是它输出的是 虚拟环境下的 包文件路径 和 项目根路径

    像这样: e:\\development\\django_project\\env\\Lib\\site-packages

  2. 解决方案

    好吧 我通过这个路径还是可以找到当前python 路径的\\Scripts\\python.exe 替换掉后面的\\Lib\\site-packages

自动化脚本中 git pull 指令报错

报错内容如下

Host key verification failed.
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.

说我没有权限, 怎么可能 我刚才都push了 一番百度之后 发现问题好像归结到了 .ssh 目录的问题

一般来说 git 拉取代码的时候 会建立ssh 连接 那么 ssh 寻找 ssh_key 的时候 会从当前用户目录下寻找 .ssh 目录 里面所对应的key

所以 我这次直接打印了 %USERPROFILE% 的值

果不其然, 在apache 部署之后 %USERPROFILE% 变成了 C:\\WINDOWS\\system32\\config\\systemprofile

啊! 这? 我没办法了, 这个不知道该从何下手, 于是就把 当前用户的 .shh 目录 复制了一份到 上面的路径去

重启 apache 脚本正常运行

自动化脚本中 引入 django 项目的自定义包文件

我手贱优化了一下 部署脚本

将一些密钥提取到了 conf\env.py 里面 统一管理

手动启动 django 项目 完美运行 通过apache 部署之后 就没有反应

我随后又加了logger 发现 导入 env 的时候报错找不到 该包文件

简直了!!! 好吧 只能 通过 打开文件的方式 加载 密钥了

这次 完美运行 !!!

subprocess.py 报错

欣喜若狂的 进行测试

git commit -m "fix: 测试部署脚本"

git push

然后 等了好一会 没有反应 我打开更新日志看了看

报错了:

[2021-11-14 02:27:01,467][root.<module>():119] [ERROR] Traceback (most recent call last):
  File "E:\development\django_project\utils\auto_update.py", line 117, in <module>
    update()
  File "E:\development\django_project\utils\auto_update.py", line 88, in update
    flag, git_res = git_pull()
  File "E:\development\django_project\utils\auto_update.py", line 55, in git_pull
    messages = exec_cmd(message_list)
  File "E:\development\django_project\utils\util.py", line 54, in exec_cmd
    out, err = proc.communicate(timeout=timeout)
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\Lib\subprocess.py", line 964, in communicate
    stdout, stderr = self._communicate(input, endtime, timeout)
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python37\Lib\subprocess.py", line 1317, in _communicate
    stdout = stdout[0]
IndexError: list index out of range

相关代码如下: 注释部分 是我之后加的

# django_project\utils\auto_update.py
from pip._internal import main as pip
def exec_cmd(cmd: str, timeout=10):
   proc = subprocess.Popen(cmd,
                           shell=True,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.STDOUT,
                           encoding='gbk')
   res = []
   try:
       out, err = proc.communicate(timeout=timeout)
   except subprocess.TimeoutExpired as ex:
       res.append("commend_timeout:{}".format(ex))
       return res
   for line in out.splitlines():
       res.append(line)

   return res

def git_pull():
   """
       git pull
   :return:
   """
   pull = 'git -C {} pull --force origin master:master'.format(project_path)
   change_file_list = 'git -C {} diff --name-status HEAD@{{1}}..HEAD'.format(project_path)
   message_list = 'git -C {} log HEAD@{{1}}..HEAD --pretty=format:"%s from %cn at %ci"'.format(project_path)
   res_lines = exec_cmd(pull, 20)
   logging.info(res_lines)
   res_lines.insert(0, '<span style="color:red;">1. git pull</span>')
   if 'Already up to date.' != res_lines[-1]:
       return False, res_lines
   change_file = exec_cmd(change_file_list, 20)
   logging.info(change_file)
   change_file.insert(0, '<span style="color:red;">2. change file list</span>')
   res_lines.extend(change_file)
   # time.sleep(1) 
   # try:
   messages = exec_cmd(message_list, 20)
   # logging.info(messages)
   messages.insert(0, '<span style="color:red;">3. push message</span>')
   res_lines.extend(messages)
   # except Exception:
   #    logging.error('message: 获取失败: {}'.format(traceback.format_exc()))
   for item in change_file:
       if 'requirments.txt' in item:
           pip(['install', '-r', os.path.join(project_path, 'requirments.txt'), '-i',
                'https://pypi.douban.com/simple'])
           break
   return True, res_lines

我随后添加了注释部分的logger 发现 这样一条输出 [2021-11-14 02:54:18,058][root.git_pull():60] [INFO] ['style: �����°��?����� from bangenlanbai at 2021-11-14 02:17:20 +0800']

中文??? commit ? 怎么解码失败了. proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='gbk')

好吧 换utf-8 试试 , 换完之后 发现这里不报错了, 但是 下面 通过 net stop apache2.4net start apache2.4 又会解码失败

所以 这个函数 被我改成了

def exec_cmd(cmd: str, timeout=10):
    proc = subprocess.Popen(cmd,
                            shell=True,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT,
                            encoding=None) # 这里 不传encoding 就会返回 字节类型的 输出
    res = []
    try:
        out, err = proc.communicate(timeout=timeout)
    except subprocess.TimeoutExpired as ex:
        res.append("commend_timeout:{}".format(ex))
        return res
    for line in out.splitlines():
        # line 此时 是 bytes 类型
        try:
            line_str = str(line, encoding='GBK')  # 在这里尝试编码
        except Exception:
            line_str = str(line, encoding='utf-8')
		res.append(res)
    return res

改完之后 脚本完美运行 !

还有一个小问题

python 默认的日志记录到文件 会出现GBK编码问题

# 默认的日志配置 文件格式 是 GBK
logging.basicConfig(level=logging.INFO,
                    filename=log_path,
                    filemode='a',
                    format=
                    '[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s',
                    )

# 使用 logging.FileHandler(filename=log_path, encoding='utf-8') 指定文件编码 就不会出问题
logger = logging.getLogger('auto_update')
file_handel = logging.FileHandler(filename=log_path, encoding='utf-8')
logger.setLevel('INFO')
file_handel.setFormatter(
    logging.Formatter('[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s'))
logger.addHandler(file_handel)

完善后的部署脚本如下

# -*- coding:utf-8  -*-
# @Time     : 2021/11/12 19:32
# @Author   : BGLB
# @Software : PyCharm
# auto_update.py 

import datetime
import logging
import os
import subprocess
import sys
import time
import traceback

from dingtalkchatbot.chatbot import DingtalkChatbot
from pip._internal import main as pip

self_path = os.path.abspath(__file__)

project_path = os.path.dirname(os.path.dirname(self_path))
log_path = os.path.join(project_path, 'logs', 'auto_update.log')

logger = logging.getLogger('auto_update')
file_handel = logging.FileHandler(filename=log_path, encoding='utf-8')
logger.setLevel('INFO')
file_handel.setFormatter(
    logging.Formatter('[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s'))
logger.addHandler(file_handel)


def exec_cmd(cmd: str, timeout=10):
    proc = subprocess.Popen(cmd,
                            shell=True,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT,
                            encoding=None)
    res = []
    try:
        out, err = proc.communicate(timeout=timeout)
    except subprocess.TimeoutExpired as ex:
        res.append("commend_timeout:{}".format(ex))
        return res
    for line in out.splitlines():
        try:
            line_str = line.decode()
        except Exception:
            line_str = line.decode('GBK')

        res.append(line_str)

    return res


def start_auto_update():
    """
        启动自己
    :return:
    """
    python_path = sys.executable if sys.executable.endswith('python.exe') else sys.path[0].replace('Lib\\site-packages',
                                                                                                   'Scripts\\python.exe')
    os.system('start "auto_update" /d {} {} {}'.format(project_path, python_path, self_path))


def git_pull():
    """
        git pull
    :return:
    """
    pull = 'git -C {} pull --force origin master:master'.format(project_path)
    change_file_list = 'git -C {} diff --name-status HEAD@{{1}}..HEAD'.format(project_path)
    message_list = 'git -C {} log HEAD@{{1}}..HEAD --pretty=format:"%s from %cn at %ci"'.format(project_path)
    res_lines = exec_cmd(pull, 20)
    logger.info(res_lines)
    res_lines.insert(0, '<span style="color:red;">1. git pull</span>')
    if 'Already up to date.' != res_lines[-1]:
        return False, res_lines
    change_file = exec_cmd(change_file_list, 20)
    logger.info(change_file)
    change_file.insert(0, '<span style="color:red;">2. change file list</span>')
    res_lines.extend(change_file)
    time.sleep(1)
    messages = exec_cmd(message_list, 20)
    logging.info(messages)
    messages.insert(0, '<span style="color:red;">3. push message</span>')
    res_lines.extend(messages)
    for item in change_file:
        if 'requirments.txt' in item:
            try:
                logger.info('下载 requirments.txt')
                pip(['install', '-r', os.path.join(project_path, 'requirments.txt'), '-i',
                     'https://pypi.douban.com/simple'])
                break
            except Exception:
                log_str = f"下载失败:\n{traceback.format_exc()}"
                logger.error(log_str)
                res_lines.append(log_str)
                return False, res_lines
    return True, res_lines


def restart_apache():
    """
        重启 apache
    :return:
    """

    cmd_line = ['net stop apache2.4', 'net start apache2.4']
    res = []
    for cmd in cmd_line:
        res.extend(exec_cmd(cmd, 60*2))
        time.sleep(1)
    if 'apache2.4 服务已经启动成功。' in res:
        return True, res
    return False, res


def update():
    """
        更新
    :return:
    """
    # from conf.env import DINGDING_ROBOT_URL, DINGDING_SECRET, DINGDING_AT_MOBILES
    DINGDING_ROBOT_URL = ''
    DINGDING_SECRET = ''
    DINGDING_AT_MOBILES = []
	# 这里读取env 配置还是有点很不优雅
    env_path = os.path.join(project_path, 'conf', 'env.py')
    with open(env_path, encoding='utf=8', mode='r') as f:
        text = f.readlines()
    for item in text:
        if item and item.startswith('DINGDING'):
            value = item.strip(' ').strip('\n').split('=', 1)[-1]

            if 'DINGDING_ROBOT_URL' in item:
                DINGDING_ROBOT_URL = eval(value)

            if item and 'DINGDING_SECRET' in item:
                DINGDING_SECRET = eval(value)

            if item and 'DINGDING_AT_MOBILES' in item:
                DINGDING_AT_MOBILES = eval(value)

    dingding = DingtalkChatbot(DINGDING_ROBOT_URL, secret=DINGDING_SECRET)
    flag, git_res = git_pull()
    markdown = '# 自动化部署日志\n\nstart_time: {}\n\n'.format(datetime.datetime.now().strftime('%Y-%m-%d ''%H:%M:%S'))
    markdown += '\n\n**pull update**\n'
    for index, item in enumerate(git_res):
        prefix = '' if item.endswith('</span>') else '> - '
        markdown += '\n{}{}\n'.format(prefix, item.lstrip())

    if not flag:
        markdown += '\n\n\n**<span style="color: red">git pull 失败</span> 快去看看**\n'
        dingding.send_markdown(title='自动化部署失败', text=markdown, at_mobiles=DINGDING_AT_MOBILES)
        return

    flag, restart_apache_res = restart_apache()
    markdown += '\n\n\n**restart apache**\n>\n'
    for item in restart_apache_res:
        if item.strip(' '):
            markdown += '> - {}\n'.format(item.lstrip(' '))

    if not flag:
        markdown += '\n\n\n**<span style="color: red">restart apache 失败</span> 快去看看**\n\n'
        dingding.send_markdown(title='自动化部署失败', text=markdown, at_mobiles=DINGDING_AT_MOBILES)
        return

    markdown += '\n\n\nend_time: {}\n\n\n'.format(datetime.datetime.now().strftime('%Y-%m-%d ''%H:%M:%S'))
    dingding.send_markdown(title='自动化部署成功', text=markdown, at_mobiles=DINGDING_AT_MOBILES)

# 钉钉机器人 markdown 语法 好像不支持 html 语法
if __name__ == '__main__':
    try:
        update()
    except Exception:
        logger.error(traceback.format_exc())

项目结构

─django_project
    │  manage.py
    │  requirments.txt
    ├─conf
    │  │  env.py
    ├─utils
    │  │  auto_update.py
    ├─logs
    │  │  ...
    ├─django_project
    │  │  setting.py
    ├─test_app
    │  │  view.py
    │  │  models.py
    │  │  urls.py
  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-11-15 15:49:40  更:2021-11-15 15:50:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 23:45:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码