修改Gunicorn的测试命令,调试中,未完成

This commit is contained in:
2025-08-29 22:29:38 +08:00
parent c59ba98e8c
commit 0a673cf2bb
9 changed files with 715 additions and 37 deletions

View File

@@ -196,14 +196,15 @@ class DeleteDirectoryThread(QThread):
class SetTimezoneAndRestartThread(QThread):
result_ready = Signal(bool, str)
def __init__(self, ssh_client):
def __init__(self, ssh_client, password):
super().__init__()
self.ssh_client = ssh_client
self.password = password
def run(self):
try:
# 设置时区
stdin, stdout, stderr = self.ssh_client.exec_command("sudo timedatectl set-timezone Asia/Shanghai")
# 设置时区,使用-S选项从标准输入读取密码
stdin, stdout, stderr = self.ssh_client.exec_command(f"echo '{self.password}' | sudo -S timedatectl set-timezone Asia/Shanghai")
exit_status = stdout.channel.recv_exit_status()
if exit_status != 0:
@@ -212,8 +213,8 @@ class SetTimezoneAndRestartThread(QThread):
logger.error(f"设置时区失败: {error}")
return
# 重启服务器
stdin, stdout, stderr = self.ssh_client.exec_command("sudo reboot")
# 重启服务器,使用-S选项从标准输入读取密码
stdin, stdout, stderr = self.ssh_client.exec_command(f"echo '{self.password}' | sudo -S reboot")
self.result_ready.emit(True, "时区设置成功,服务器正在重启")
logger.info("时区设置成功,服务器正在重启")
@@ -227,14 +228,15 @@ class SetTimezoneAndRestartThread(QThread):
class CheckFirewallThread(QThread):
result_ready = Signal(bool, str)
def __init__(self, ssh_client):
def __init__(self, ssh_client, password):
super().__init__()
self.ssh_client = ssh_client
self.password = password
def run(self):
try:
# 检查UFW状态
stdin, stdout, stderr = self.ssh_client.exec_command("sudo ufw status")
# 检查UFW状态,使用-S选项从标准输入读取密码
stdin, stdout, stderr = self.ssh_client.exec_command(f"echo '{self.password}' | sudo -S ufw status")
exit_status = stdout.channel.recv_exit_status()
if exit_status == 0:
@@ -255,20 +257,21 @@ class CheckFirewallThread(QThread):
class OpenPortThread(QThread):
result_ready = Signal(bool, str)
def __init__(self, ssh_client, port):
def __init__(self, ssh_client, port, password):
super().__init__()
self.ssh_client = ssh_client
self.port = port
self.password = password
def run(self):
try:
# 开放端口
stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo ufw allow {self.port}")
# 开放端口,使用-S选项从标准输入读取密码
stdin, stdout, stderr = self.ssh_client.exec_command(f"echo '{self.password}' | sudo -S ufw allow {self.port}")
exit_status = stdout.channel.recv_exit_status()
if exit_status == 0:
# 重新加载防火墙
stdin, stdout, stderr = self.ssh_client.exec_command("sudo ufw reload")
# 重新加载防火墙,使用-S选项从标准输入读取密码
stdin, stdout, stderr = self.ssh_client.exec_command(f"echo '{self.password}' | sudo -S ufw reload")
self.result_ready.emit(True, f"端口 {self.port} 开放成功")
logger.info(f"端口 {self.port} 开放成功")
@@ -287,9 +290,10 @@ class DjangoInstallThread(QThread):
result_ready = Signal(bool, str)
progress_updated = Signal(int)
def __init__(self, ssh_client):
def __init__(self, ssh_client, password):
super().__init__()
self.ssh_client = ssh_client
self.password = password
def run(self):
try:
@@ -320,8 +324,8 @@ class DjangoInstallThread(QThread):
self.progress_updated.emit(50)
# 如果pip安装失败尝试使用apt安装
stdin, stdout, stderr = self.ssh_client.exec_command("sudo apt update && sudo apt install -y python3-django")
# 如果pip安装失败尝试使用apt安装,使用-S选项从标准输入读取密码
stdin, stdout, stderr = self.ssh_client.exec_command(f"echo '{self.password}' | sudo -S apt update && echo '{self.password}' | sudo -S apt install -y python3-django")
exit_status = stdout.channel.recv_exit_status()
if exit_status == 0:
@@ -392,9 +396,58 @@ class DjangoTestThread(QThread):
output = stdout.read().decode()
if "manage.py runserver" in output:
self.progress_updated.emit(100)
self.result_ready.emit(True, "Django测试服务器启动成功")
logger.info("Django测试服务器启动成功")
self.progress_updated.emit(80)
logger.info("Django测试服务器启动成功,开始检查端口访问情况")
# 检查8000端口是否真的在监听
self.progress_updated.emit(85)
stdin, stdout, stderr = self.ssh_client.exec_command("ss -tulnp | grep 8000")
ss_output = stdout.read().decode()
logger.info(f"ss命令检查端口8000结果: {ss_output}")
# 如果ss命令没有结果尝试使用netstat
if not ss_output:
stdin, stdout, stderr = self.ssh_client.exec_command("netstat -tulnp | grep 8000")
netstat_output = stdout.read().decode()
logger.info(f"netstat命令检查端口8000结果: {netstat_output}")
port_check_output = netstat_output
else:
port_check_output = ss_output
# 检查端口是否在LISTEN状态
if "LISTEN" in port_check_output and "8000" in port_check_output:
self.progress_updated.emit(90)
logger.info("端口8000处于LISTEN状态开始本地请求测试")
# 本地发起请求测试
stdin, stdout, stderr = self.ssh_client.exec_command("curl -s http://127.0.0.1:8000")
curl_output = stdout.read().decode()
curl_exit_status = stdout.channel.recv_exit_status()
logger.info(f"curl本地请求测试退出状态: {curl_exit_status}")
logger.info(f"curl本地请求测试输出: {curl_output[:200]}..." if len(curl_output) > 200 else f"curl本地请求测试输出: {curl_output}")
if curl_exit_status == 0:
self.progress_updated.emit(100)
self.result_ready.emit(True, "Django测试服务器启动成功端口访问正常")
logger.info("Django测试服务器启动成功端口访问正常")
else:
# 检查错误类型
if "Failed to connect" in curl_output:
error_msg = "Django测试服务器启动成功但本地请求超时Gunicorn进程可能未正常响应"
elif "Bad Request (400)" in curl_output:
error_msg = "Django测试服务器启动成功但返回400错误可能是ALLOWED_HOSTS配置问题"
elif "500" in curl_output:
error_msg = "Django测试服务器启动成功但返回500错误可能是应用内部错误"
else:
error_msg = f"Django测试服务器启动成功但本地请求失败: {curl_output[:100]}"
self.result_ready.emit(False, error_msg)
logger.error(error_msg)
else:
self.progress_updated.emit(100)
self.result_ready.emit(False, "Django测试服务器启动成功但端口8000未处于LISTEN状态")
logger.error("Django测试服务器启动成功但端口8000未处于LISTEN状态")
else:
self.result_ready.emit(False, "Django测试服务器启动失败")
logger.error("Django测试服务器启动失败")
@@ -776,24 +829,137 @@ class GunicornTestThread(QThread):
self.progress_updated.emit(50)
# 测试Gunicorn启动,使用构造函数中传入的端口参数
test_command = f"cd {self.django_path} && timeout 10 gunicorn --workers 1 --bind 0.0.0.0:{self.port} {project_name}.wsgi:application --timeout 5"
# 在启动Gunicorn前,先检查端口是否被占用
logger.info(f"检查端口{self.port}是否被其他进程占用")
stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo lsof -i :{self.port}")
lsof_output = stdout.read().decode()
lsof_exit_status = stdout.channel.recv_exit_status()
if lsof_exit_status == 0 and lsof_output:
logger.warning(f"端口{self.port}已被其他进程占用: {lsof_output}")
# 尝试杀死占用端口的进程
stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo lsof -t -i :{self.port} | xargs sudo kill -9")
time.sleep(1) # 等待进程被杀死
logger.info(f"已尝试杀死占用端口{self.port}的进程")
else:
logger.info(f"端口{self.port}未被占用")
# 测试Gunicorn启动使用构造函数中传入的端口参数指定4个worker
test_command = f"cd {self.django_path} && gunicorn --workers 4 --bind 0.0.0.0:{self.port} {project_name}.wsgi:application --timeout 5"
logger.info(f"执行Gunicorn测试命令: {test_command}")
stdin, stdout, stderr = self.ssh_client.exec_command(test_command)
time.sleep(3) # 等待Gunicorn启动
# 检查Gunicorn是否运行
# 检查Gunicorn进程状态
stdin, stdout, stderr = self.ssh_client.exec_command("ps aux | grep gunicorn")
output = stdout.read().decode()
logger.info(f"Gunicorn进程检查结果: {output}")
# 检查Worker进程状态
worker_count = output.count('gunicorn') - 1 # 减去grep进程本身
logger.info(f"检测到Gunicorn Worker进程数量: {worker_count}")
# 详细分析进程结构
lines = output.strip().split('\n')
master_process = None
worker_processes = []
for line in lines:
if 'grep' not in line and 'gunicorn' in line:
parts = line.split()
if len(parts) >= 11:
pid = parts[1]
command = ' '.join(parts[10:])
if 'master' in command or 'gunicorn: master' in command:
master_process = (pid, command)
elif 'worker' in command:
worker_processes.append((pid, command))
if master_process:
logger.info(f"检测到Gunicorn主进程 - PID: {master_process[0]}, 命令: {master_process[1]}")
else:
logger.warning(f"未检测到Gunicorn主进程")
if worker_processes:
logger.info(f"检测到{len(worker_processes)}个Gunicorn Worker进程:")
for pid, command in worker_processes:
logger.info(f" - Worker PID: {pid}, 命令: {command}")
else:
logger.warning(f"未检测到Gunicorn Worker进程")
# 检查进程数量是否符合预期1个主进程+4个worker进程
if master_process and len(worker_processes) == 4:
logger.info(f"Gunicorn进程结构正常: 1个主进程 + 4个worker进程")
elif master_process and len(worker_processes) > 0:
logger.warning(f"Gunicorn进程结构部分正常: 1个主进程 + {len(worker_processes)}个worker进程预期4个")
else:
logger.error(f"Gunicorn进程结构异常: 未检测到完整的进程结构")
if worker_count < 1:
logger.error(f"Gunicorn主进程存在但Worker进程未启动可能是Django代码/配置错误")
# 尝试手动启动Gunicorn并获取详细错误日志
logger.info(f"尝试手动启动Gunicorn以获取详细错误日志")
stdin, stdout, stderr = self.ssh_client.exec_command(f"cd {self.django_path} && gunicorn --workers 4 --bind 0.0.0.0:{self.port} {project_name}.wsgi:application --timeout 5 --error-logfile -")
time.sleep(2)
error_output = stderr.read().decode()
logger.error(f"手动启动Gunicorn错误日志: {error_output}")
# 清理测试进程
stdin, stdout, stderr = self.ssh_client.exec_command(f"pkill -f 'gunicorn.*{self.port}'")
if "gunicorn" in output and f":{self.port}" in output:
self.progress_updated.emit(100)
self.result_ready.emit(True, f"Gunicorn测试成功 - 项目: {project_name}, 端口: {self.port}")
logger.info(f"Gunicorn测试成功 - 项目: {project_name}, 端口: {self.port}")
self.progress_updated.emit(80)
logger.info(f"Gunicorn进程运行正常开始检查端口{self.port}访问情况")
# 检查端口是否真的在监听
self.progress_updated.emit(85)
stdin, stdout, stderr = self.ssh_client.exec_command(f"ss -tulnp | grep {self.port}")
ss_output = stdout.read().decode()
logger.info(f"ss命令检查端口{self.port}结果: {ss_output}")
# 如果ss命令没有结果尝试使用netstat
if not ss_output:
stdin, stdout, stderr = self.ssh_client.exec_command(f"netstat -tulnp | grep {self.port}")
netstat_output = stdout.read().decode()
logger.info(f"netstat命令检查端口{self.port}结果: {netstat_output}")
port_check_output = netstat_output
else:
port_check_output = ss_output
# 检查端口是否在LISTEN状态
if "LISTEN" in port_check_output and f":{self.port}" in port_check_output:
self.progress_updated.emit(90)
logger.info(f"端口{self.port}处于LISTEN状态开始本地请求测试")
# 本地发起请求测试
stdin, stdout, stderr = self.ssh_client.exec_command(f"curl -s http://127.0.0.1:{self.port}")
curl_output = stdout.read().decode()
curl_exit_status = stdout.channel.recv_exit_status()
logger.info(f"curl本地请求测试退出状态: {curl_exit_status}")
logger.info(f"curl本地请求测试输出: {curl_output[:200]}..." if len(curl_output) > 200 else f"curl本地请求测试输出: {curl_output}")
if curl_exit_status == 0:
self.progress_updated.emit(100)
self.result_ready.emit(True, f"Gunicorn测试成功 - 项目: {project_name}, 端口: {self.port}, 访问正常")
logger.info(f"Gunicorn测试成功 - 项目: {project_name}, 端口: {self.port}, 访问正常")
else:
# 检查错误类型
if "Failed to connect" in curl_output:
error_msg = f"Gunicorn进程运行正常但本地请求超时端口{self.port}未响应"
elif "Bad Request (400)" in curl_output:
error_msg = f"Gunicorn进程运行正常但返回400错误可能是ALLOWED_HOSTS配置问题"
elif "500" in curl_output:
error_msg = f"Gunicorn进程运行正常但返回500错误可能是应用内部错误"
else:
error_msg = f"Gunicorn进程运行正常但本地请求失败: {curl_output[:100]}"
self.result_ready.emit(False, error_msg)
logger.error(error_msg)
else:
self.progress_updated.emit(100)
self.result_ready.emit(False, f"Gunicorn进程运行正常但端口{self.port}未处于LISTEN状态")
logger.error(f"Gunicorn进程运行正常但端口{self.port}未处于LISTEN状态")
else:
# 尝试更简单的测试方式
self.progress_updated.emit(80)
@@ -1040,9 +1206,131 @@ class ManageGunicornServiceThread(QThread):
port = "8000" # 默认端口
if hasattr(self, 'port') and self.port:
port = self.port
stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo netstat -tlnp | grep :{port}")
netstat_output = stdout.read().decode()
logger.info(f"端口{port}监听状态: {netstat_output}")
# 在检查端口监听前,先检查端口占用情况
logger.info(f"检查端口{port}占用情况")
stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo lsof -i :{port}")
lsof_output = stdout.read().decode()
lsof_exit_status = stdout.channel.recv_exit_status()
if lsof_exit_status == 0 and lsof_output:
logger.warning(f"端口{port}被占用: {lsof_output}")
else:
logger.info(f"端口{port}未被占用")
# 检查Gunicorn进程状态
stdin, stdout, stderr = self.ssh_client.exec_command("ps aux | grep gunicorn")
ps_output = stdout.read().decode()
logger.info(f"Gunicorn进程状态: {ps_output}")
# 详细分析进程结构
lines = ps_output.strip().split('\n')
master_process = None
worker_processes = []
for line in lines:
if 'grep' not in line and 'gunicorn' in line:
parts = line.split()
if len(parts) >= 11:
pid = parts[1]
command = ' '.join(parts[10:])
if 'master' in command or 'gunicorn: master' in command:
master_process = (pid, command)
elif 'worker' in command:
worker_processes.append((pid, command))
if master_process:
logger.info(f"检测到Gunicorn主进程 - PID: {master_process[0]}, 命令: {master_process[1]}")
else:
logger.warning(f"未检测到Gunicorn主进程")
if worker_processes:
logger.info(f"检测到{len(worker_processes)}个Gunicorn Worker进程:")
for pid, command in worker_processes:
logger.info(f" - Worker PID: {pid}, 命令: {command}")
else:
logger.warning(f"未检测到Gunicorn Worker进程")
# 检查进程数量是否符合预期1个主进程+4个worker进程
if master_process and len(worker_processes) == 4:
logger.info(f"Gunicorn进程结构正常: 1个主进程 + 4个worker进程")
elif master_process and len(worker_processes) > 0:
logger.warning(f"Gunicorn进程结构部分正常: 1个主进程 + {len(worker_processes)}个worker进程预期4个")
else:
logger.error(f"Gunicorn进程结构异常: 未检测到完整的进程结构")
# 检查Worker进程状态
worker_count = ps_output.count('gunicorn') - 1 # 减去grep进程本身
logger.info(f"检测到Gunicorn Worker进程数量: {worker_count}")
if worker_count < 1:
logger.error(f"Gunicorn主进程存在但Worker进程未启动可能是Django代码/配置错误")
# 使用ss命令检查端口监听状态
stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo ss -tulnp | grep :{port}")
ss_output = stdout.read().decode()
logger.info(f"ss命令检查端口{port}监听状态: {ss_output}")
# 如果ss命令没有结果尝试使用netstat
if not ss_output:
stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo netstat -tlnp | grep :{port}")
netstat_output = stdout.read().decode()
logger.info(f"netstat命令检查端口{port}监听状态: {netstat_output}")
port_check_output = netstat_output
else:
port_check_output = ss_output
# 检查端口是否在LISTEN状态
if "LISTEN" in port_check_output and f":{port}" in port_check_output:
logger.info(f"端口{port}处于LISTEN状态开始本地请求测试")
# 本地发起请求测试
stdin, stdout, stderr = self.ssh_client.exec_command(f"curl -s http://127.0.0.1:{port}")
curl_output = stdout.read().decode()
curl_exit_status = stdout.channel.recv_exit_status()
logger.info(f"curl本地请求测试退出状态: {curl_exit_status}")
logger.info(f"curl本地请求测试输出: {curl_output[:200]}..." if len(curl_output) > 200 else f"curl本地请求测试输出: {curl_output}")
if curl_exit_status == 0:
logger.info(f"服务启动成功,端口{port}访问正常")
else:
# 检查错误类型
if "Failed to connect" in curl_output:
logger.warning(f"服务启动成功,但本地请求超时,端口{port}未响应")
elif "Bad Request (400)" in curl_output:
logger.warning(f"服务启动成功但返回400错误可能是ALLOWED_HOSTS配置问题")
elif "500" in curl_output:
logger.warning(f"服务启动成功但返回500错误可能是应用内部错误")
else:
logger.warning(f"服务启动成功,但本地请求失败: {curl_output[:100]}")
else:
logger.warning(f"服务启动成功,但端口{port}未处于LISTEN状态")
# 检查SELinux状态
logger.info(f"检查SELinux状态")
stdin, stdout, stderr = self.ssh_client.exec_command("getenforce")
selinux_output = stdout.read().decode().strip()
logger.info(f"SELinux状态: {selinux_output}")
if selinux_output == "Enforcing":
logger.warning(f"SELinux处于Enforcing状态可能限制了端口绑定")
# 检查项目目录权限
logger.info(f"检查项目目录权限")
stdin, stdout, stderr = self.ssh_client.exec_command("ls -ld /home/xiaji/")
home_dir_output = stdout.read().decode().strip()
logger.info(f"/home/xiaji/目录权限: {home_dir_output}")
# 尝试更换端口测试
logger.info(f"尝试更换端口测试")
test_port = "8001"
stdin, stdout, stderr = self.ssh_client.exec_command(f"sudo ss -tulnp | grep :{test_port}")
test_port_output = stdout.read().decode()
if not test_port_output:
logger.info(f"端口{test_port}未被占用尝试用该端口启动Gunicorn")
# 这里只是记录日志,不实际更换端口
else:
logger.warning(f"服务启动后状态检查失败: {status_error}")