ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

CentOS宝塔面板部署Django:从环境配置到Nginx与uWSGI上线

CentOS宝塔面板部署Django:从环境配置到Nginx与uWSGI上线 简介面向CentOS运维与Django开发者的实战教程系统讲解如何借助宝塔面板快速完成Django项目部署。教程从安装宝塔面板、Python项目管理器和Nginx等基础环境讲起覆盖项目代码上传、Python/Django项目创建、域名映射、Nginx反向代理及静态/媒体文件配置等关键步骤并附有重启与加载配置的完整流程。资源为1个PDF文件压缩包大小仅139KB内容精炼适合Linux基础薄弱、希望采用可视化面板管理服务的初学者参考。该教程已有2371人学习步骤中兼顾常见路径与端口设置提醒能帮助读者避开部署过程中的典型坑点快速搭建可正常访问且支持静态资源的Django站点。1. 一台 CentOS 上让 Django 跑起来为什么绕不开宝塔面板在 CentOS 上部署 Django 项目最折磨人的往往不是 Django 本身而是它周围那一圈基础设施Python 版本对不对、Nginx 怎么转发、uwsgi 日志去哪看、MySQL 的 socket 通不通。每一样单拎出来都能查半天资料组合在一起就成了新手眼里的黑匣子。宝塔面板把 Nginx、MySQL、Redis 这些服务的安装和运维收敛成图形化操作你只需要把精力放在 Django 项目的配置和 uWSGI 的衔接上这条路也是目前个人站长和小团队部署 Django 最多人走通的一条线。这套组合适合两类人一是本地能跑 python manage.py runserver、但第一次面对服务器的初学者二是手里同时维护多个站点、不想为每个环境重复编译 Nginx 和数据库的开发者。有人会问 1panel 和宝塔哪个好我的观点很直接如果你只是要快速把 Django 项目挂上线选宝塔更省事因为它的教程沉淀足够多踩坑记录也基本都能搜到。本文从装系统前的检查讲起一路到用 uWSGI 和 Nginx 把项目真正跑起来最后收在部署完的自检清单上照着做一遍你就能获得一台从外网访问的 Django 服务。2. 装宝塔前先摸清家底CentOS 版本、内存下限与软件源2.1 系统选择CentOS 7.9 仍是主流先看三行命令的结果部署 CentOS 服务器第一步不是下载镜像而是先确认三件事系统版本、内存大小、磁盘空间。登录服务器后先执行下面这组命令cat /etc/redhat-release free -h df -h /www /home /var第一行看系统发行版第二行看内存总量和已用内存第三行看磁盘剩余空间。之所以强调先看这三项是因为后续装宝塔面板时内存和磁盘会直接决定你是否要加班加点处理环境问题。手动装系统时镜像建议选 CentOS 7.9 的 Minimal 版本带桌面的版本会白白吃掉几百兆内存下载 ISO 时别在官网到处乱找国内镜像站基本都保留了完整版本速度也更快。CentOS 7 已经停止官方维护有个现实问题新装的 7.9 系统默认 YUM 源可能已经失效执行 yum install 直接报错或者拉不到包。这种情况不是错觉是源地址指向了已归档的仓库。换源这一步在装任何软件之前做掉否则后面每一步都会卡壳cd /etc/yum.repos.d/ sed -i s/mirrorlist/#mirrorlist/g CentOS-Base.repo sed -i s|#baseurlhttp://mirror.centos.org|baseurlhttp://vault.centos.org|g CentOS-Base.repo yum clean all yum makecache这段操作把原来的 mirrorlist 注释掉将镜像源切到 Vault 归档仓库让已经停止维护的 CentOS 7 仍能继续安装和更新软件包。执行完 makecache 不报错说明源已经通了。这一步做完不要急着装东西顺手再确认一个容易被忽略的细节系统自带的 python3 版本。CentOS 7.9 默认自带的是 Python 3.6而 Django 4 以上的框架版本要求 Python 3.8 起步这个版本差异会在后面装依赖时被放大成一道坎。具体怎么解决到第三章我会展开说。2.2 宝塔面板安装失败的内幕至少 3700MB 内存的本意是什么宝塔面板的安装脚本对内存有硬性检查低于 3700MB 会直接拒绝安装报错信息大意是“至少需要 3700MB 内存才能安装”。这个数字第一次见的人会觉得莫名其妙其实它是面板用来保证运行时 PHP、MySQL、Nginx 三个服务能同时驻留内存的经验值。如果你手上是一台 1G 或 2G 内存的轻量服务器不要硬着头皮重复执行安装脚本先补一块 swap 交换分区if [ ! -f /swapfile ]; then dd if/dev/zero of/swapfile bs1M count2048 chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo /swapfile swap swap defaults 0 0 /etc/fstab fi free -h这段脚本创建一个 2GB 的 swapfile赋予 600 权限防止普通用户读取格式化为 swap 分区并临时启用最后写入 /etc/fstab 让重启后仍然生效。dd 的 bs1M count2048 表示按 1MB 块大小写 2048 次总共生成 2GB 文件这个大小对宝塔安装足够对正式运行则偏紧。swap 是用磁盘换内存读写速度比物理内存慢得多真正跑生产时建议还是升级内存swap 只作为兜底不能指望它让项目流畅运行。安装面板本身没有特别多的技巧到官网复制针对 CentOS 的安装命令一条 curl 或 wget 命令下载安装脚本然后 bash 执行。注意不要直接在服务器上乱搜安装包以官网当前提供的脚本为准。安装过程持续几分钟期间保持 SSH 连接稳定输出里出现面板地址、账号和初始密码后要及时记下来尤其是那个安全入口的路径后缀是一次性的忘了只能去配置文件里翻。2.3 面板初始化和软件选型Nginx、MySQL 先装Python 的坑单独讲第一次登录宝塔面板会要求绑定账号并设置安全入口。绑定这一步按流程走就行不绑定后面没法继续使用面板功能。进入面板首页后系统会提示安装推荐软件。在这里不要一键全装只选需要的那几项Nginx 选稳定版用来做反向代理和静态文件分发MySQL 选 5.7 或 8.0具体取决于你在本地开发时用的哪个版本跨大版本迁移数据容易出现字符集和排序规则不一致的问题PHP 如果项目用不到就完全跳过它除了增加内存占用没有意义。Python 环境的坑比较隐蔽。宝塔的“软件商店”里有个 Python 项目管理器听起来很省事但它的虚拟环境和真实服务器环境经常有版本错位尤其当你需要 Python 3.8 时面板可能提供的还是 3.6。我一般不会把希望全部寄托在面板插件上而是直接在服务器上检查现有 Python 版本再决定是用 pyenv 装新版还是直接编译安装。这一步先不做留到项目依赖安装时再说。到这里CentOS 系统、软件源、宝塔面板三件事已经搞定可以正式进入 Django 项目的改造环节。3. 上线前的三个必改项settings、依赖锁定与数据库切换3.1 settings.py 的四处改动从本地能跑到服务器能跑的差距本地开发时 Django 项目跑得再欢也不能把代码原封不动丢到服务器上。本地和服务器环境的差异大部分集中在 settings.py 里。一个最常见的翻车现象是代码上传后页面打开直接报错浏览器提示“DisallowedHost”这就是 ALLOWED_HOSTS 没配置导致的。部署前需要改动的关键配置如下from pathlib import Path import os BASE_DIR Path(__file__).resolve().parent.parent DEBUG False ALLOWED_HOSTS [example.com, www.example.com] SECRET_KEY os.environ.get(DJANGO_SECRET_KEY, 一串足够长的随机字符) DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: django_project, USER: django_user, PASSWORD: 你的数据库密码, HOST: 127.0.0.1, PORT: 3306, OPTIONS: {charset: utf8mb4}, } } STATIC_ROOT BASE_DIR / staticfiles MEDIA_ROOT BASE_DIR / media STATIC_URL /static/ MEDIA_URL /media/DEBUG 必须设为 False否则服务器会直接把堆栈信息暴露给访问者这不仅是安全问题还会让页面渲染速度明显变慢。ALLOWED_HOSTS 填你实际要使用的域名或服务器 IP多个域名用逗号分隔开发阶段临时用 [*] 也不是不行但上线前一定要收敛。SECRET_KEY 在本地开发时往往直接写在配置里到了服务器最好改成从环境变量读取避免代码仓库泄露导致签名被伪造。最容易被漏掉的是 STATIC_ROOT 和 MEDIA_ROOT这两个路径决定 collectstatic 把静态文件收拢到哪里以及用户上传的文件落在哪个目录后面配 Nginx 时alias 路径要和这两个值完全对应。3.2 用虚拟环境锁定依赖pip freeze 不是万能的依赖管理的目标只有一个本地能跑的库服务器上装出同一套。很多人习惯在本地执行 pip freeze requirements.txt然后把这个文件传到服务器直接 pip install -r结果发现要么装出一堆无关包要么因版本冲突当场报错。pip freeze 会把本地环境里所有包都导出其中很多是只有开发才需要的工具这类杂讯会让排查变得很困难。我习惯手动维护一份最小依赖清单只在 requirements.txt 里写项目真正用到的框架和库Django4.2.7 mysqlclient2.2.0 uWSGI2.0.23 gunicorn21.2.0固定版本号是必须的Django 4.2 和 Django 5.0 在兼容性上有明显差异不锁版本等于把线上环境交给运气。到了服务器的项目目录下创建虚拟环境并安装依赖cd /www/wwwroot/django_project python3 -m venv venv source venv/bin/activate pip install --upgrade pip pip install -r requirements.txtpython3 -m venv 是创建虚拟环境的命令后续安装的包都会落到 venv 目录下不会污染系统环境。source venv/bin/activate 激活环境后命令行提示符会带上 (venv) 前缀这代表当前处于虚拟环境内。如果服务器自带的 Python 版本低于项目要求比如 Django 5.0 项目配 Python 3.6创建虚拟环境这步就会失败或装不上依赖这时先解决 Python 版本问题再回头继续。解决方式是编译安装新版 Python下载源码包、配置 prefix、make install整个过程约十几分钟编译前要保证系统已安装 gcc 和 make这一步没有捷径。3.3 数据库从 SQLite 切换到 MySQL先建库再 migrateDjango 默认的 SQLite 适合开发和测试但到了服务器上并发写入和多进程访问会成为瓶颈。宝塔面板里切换 MySQL 的操作很简单进入面板的数据库页面点击添加数据库填上库名和用户名生成一个随机密码。这里要留意“访问权限”这一项选择“本地服务器”即可不需要勾选远程访问因为 Django 和 MySQL 在同一台机器上通信走 127.0.0.1 就够了。数据库建好之后回到服务器端的项目目录先安装驱动再执行迁移source /www/wwwroot/django_project/venv/bin/activate pip install mysqlclient python manage.py migratemysqlclient 是 Django 官方文档推荐的 MySQL 驱动但它依赖系统的 mysql-devel 和 gcc 才能编译。宝塔环境里经常遇到编译失败报错信息指向缺少头文件解决方法是先 yum install mysql-devel gcc python3-devel再重新安装。如果实在编译不过退路是改用 PyMySQL在项目目录的init.py 或 settings.py 里加两行把 pymysql 伪装成 mysqldb。这个方法相对省事但复杂查询场景下兼容性不如官方驱动所以编译这条路能走通就优先走通。migrate 成功之后紧接着执行 collectstatic把项目依赖的静态文件统一收拢到之前配置好的 STATIC_ROOT 目录python manage.py collectstatic --noinput--noinput 参数让命令不再交互式确认适合在部署脚本里直接执行。执行完成后检查一下 staticfiles 目录里是否出现了 admin 子目录这是 Django 后台管理界面的样式文件出现即说明收集成功。到这里项目代码本身已经具备了被服务器承载的条件下一步要解决的是怎么让它常驻运行——这份工作落到 uWSGI 头上。4. uWSGI 接管 Django进程参数、配置文件与 supervisor 守护4.1 为什么上线不能用 runserver开发服务器的三个天花板python manage.py runserver 是 Django 自带的开发服务器它天生只为一个场景设计你在本地改代码它自动重载并显示详细报错。到了生产环境runserver 有三个致命问题单进程处理请求CPU 多核完全用不上没有进程守护SSH 断开后服务跟着退出性能上限极低稍微有点并发就会出现请求排队。解决这个问题的通用做法是用 WSGI 服务器承载 Django 应用常见选择是 uWSGI 或 gunicorn。uWSGI 和 gunicorn 都符合 WSGI 协议但 uWSGI 对 Nginx 的 unix socket 支持成熟再加上一个 uWSGI 进程可以同时管理多进程和多线程我这边部署 Django 项目基本都是 uWSGI 优先。4.2 最小可运行的 uWSGI 配置每个参数都对应一个线上问题uWSGI 可以直接用命令行参数启动但参数一多就变成灾难。我习惯把配置写进一个 ini 文件随项目一起管理。下面是一个最小可运行的配置放在项目目录的 deploy/uwsgi.ini 下[uwsgi] chdir /www/wwwroot/django_project module django_project.wsgi:application master true processes 4 threads 2 socket /tmp/django_project.sock chmod-socket 664 vacuum true max-requests 5000 daemonize /var/log/django_project_uwsgi.log pidfile /var/run/django_project.pid逐项说明这几个参数的作用chdir 指定项目根目录也就是 manage.py 所在的位置uWSGI 启动后会先切换到这个目录保证相对路径的准确性。module 指向项目内 wsgi.py 暴露出的 application 对象这个对象是 Django 和 WSGI 服务器之间的桥梁路径格式是“项目配置包名.wsgi:application”。master 设为 true 后uWSGI 会启动一个主进程来管理子进程主进程负责回收异常退出的 worker这是服务稳定性的基础。processes 和 threads 配合决定并发模型4 个进程乘 2 个线程等于 8 个并发处理单元对一台 2 核服务器来说已经是合理配置别再往上加进程太多反而会因为上下文切换降低吞吐。socket 参数指定监听的本地 socket 文件uWSGI 与 Nginx 就通过这个文件通信而不是占用 TCP 端口。chmod-socket 设成 664是为了让 Nginx 的 www 用户能读写这个 socket 文件否则后面会出现 403 权限错误。vacuum 在进程退出时自动清理 socket 文件避免残留文件导致下次启动报“Address already in use”。max-requests 让每个 worker 处理满 5000 个请求后自动重启一次这是对抗内存泄漏的土办法比写监测脚本省心。daemonize 把日志写入独立文件pidfile 记录主进程号这两个配合起来方便排查问题和做优雅重启。第一次启动可以不开 daemonize前台运行看输出确认无误后再以守护方式正式拉起来cd /www/wwwroot/django_project venv/bin/uwsgi --ini deploy/uwsgi.ini执行后观察日志输出的最后几行如果出现“WSGI app 0 (mountpoint) ready”的字样说明 uWSGI 已经成功加载了 Django 应用。4.3 用 supervisor 守护 uWSGI进程死了能自己爬起来uWSGI 能跑起来不代表能一直跑下去。进程被系统杀掉、服务器重启、代码异常导致崩溃这些情况都需要一个外部机制把 uWSGI 拉起来。常见做法是 systemd 服务或 supervisor。宝塔面板的“进程守护管理器”插件也能干这活但插件版本更新慢而且配置方式绕了一圈还是在写 supervisor 的配置文件。直接用 supervisor 更透明配置如下保存到 /etc/supervisord.d/django_project.ini[program:django_project] command/www/wwwroot/django_project/venv/bin/uwsgi --ini /www/wwwroot/django_project/deploy/uwsgi.ini autostarttrue autorestarttrue redirect_stderrtrue stdout_logfile/var/log/django_project_supervisor.log stopsignalINTcommand 必须写 uWSGI 的完整路径因为 supervisor 启动时的环境变量和当前 SSH 会话不同直接写 uwsgi 可能找不到命令。autostart 让 supervisor 启动时就拉起 uWSGIautorestart 在进程异常退出后自动重新拉起这两项配合起来相当于给服务上了双重保险。redirect_stderr 把 uWSGI 的错误输出合并到主日志省得分开看两个日志文件。stopsignal 用 INT 信号让 uWSGI 收到停止指令时优雅退出等当前请求处理完再关闭进程而不是立刻杀死。配置写好后执行 supervisorctl update 加载新配置再执行 supervisorctl status 检查状态supervisorctl update supervisorctl status django_project看到状态为 RUNNING 就是正常。这套进程守护方式不只是 Django 能用以后再部署 agent 服务、定时任务脚本或者其他任何需要常驻的程序都可以照这个模板套一层 supervisor通用性很强。到这里Django 已经被 uWSGI 跑起来了但外网访问还需要 Nginx 在前面做中转下一章就处理这部分。5. Nginx 反向代理与静态文件宝塔站点配置与避坑排查5.1 在宝塔里添加站点并写入 uwsgi_pass 转发规则uWSGI 通过 socket 文件对外提供 Web 服务但它不适合直接暴露给公网用户前面还需要一层 Nginx。Nginx 在这里干两件事把 HTTP 请求转成 uWSGI 协议发给 Django以及高效地吐出静态文件。宝塔里操作 Nginx 不需要手动编辑主配置进入“网站”页面点击“添加站点”填入你的域名或服务器 IPPHP 版本选择纯静态提交后站点目录会自动创建配置文件也生成好了。接下来进入站点的设置面板找到配置文件编辑入口在默认的 server 块里加入 Django 的转发和静态文件规则server { listen 80; server_name example.com; access_log /www/wwwlogs/example.com.log; error_log /www/wwwlogs/example.com.error.log; location /static/ { alias /www/wwwroot/django_project/staticfiles/; } location /media/ { alias /www/wwwroot/django_project/media/; } location / { include uwsgi_params; uwsgi_pass unix:///tmp/django_project.sock; uwsgi_read_timeout 60; uwsgi_send_timeout 60; } }location / 块是核心include uwsgi_params 把 uWSGI 协议需要的请求头参数导入uwsgi_pass 指定要转发的 socket 文件地址这里要和 uWSGI 配置文件里的 socket 路径完全一致。uwsgi_read_timeout 和 uwsgi_send_timeout 设为 60 秒防止某些处理时间长的接口触发超时。配置文件保存后执行 nginx -t 检查语法再在面板里重载 Nginx 服务。此时访问你的域名Django 应用已经拦在 Nginx 后面开始工作了。5.2 静态文件的收尾工作collectstatic 与 alias 的对应关系换到生产环境后Django 不再处理静态文件全交给 Nginx。Nginx 收到 /static/ 开头的请求时会直接到 alias 指定的目录里找文件根本不经过 Django。在这条链路里最容易出问题的不是 Nginx 配置而是 collectstatic 有没有执行。settings.py 里的 STATIC_ROOT 配置了收集目标目录collectstatic 把 Django 自带的 admin 静态文件和你项目里的 static 目录文件全部收纳进这个文件夹。Nginx 的 alias 路径必须指向这个目录多写或少写一层都会导致 404。media 目录同理用户上传的图片和附件会写进 MEDIA_ROOTNginx 的 /media/ location 也要指向同样的位置。检查一下 staticfiles 目录权限是否让 Nginx 用户有读取权限ls -ld /www/wwwroot/django_project/staticfiles chown -R www:www /www/wwwroot/django_project/staticfiles chown -R www:www /www/wwwroot/django_project/mediawww 用户是宝塔 Nginx 默认的运行用户静态文件和媒体目录都要归 www 所有否则 Nginx 读取时触发权限不足页面上的 CSS 和图片就会全部 404。这是部署阶段最容易忽略的一步本地开发时不用考虑权限问题上了服务器就完全不一样了。5.3 部署后最常见的 5 个坑现象、原因与解决方法第一个坑是 502 Bad Gateway。现象是首页打得开但所有接口都报 502。常见原因是 uWSGI 没有运行或者 socket 文件路径不一致。排查方法是先看 supervisor 状态再确认 /tmp/django_project.sock 是否存在两条命令就能定位问题。解决方式是对应调整 supervisor 里的启动命令或 Nginx 里的 socket 路径。第二个坑是 403 Forbidden。现象是页面能访问但提示无权限查 Nginx 错误日志能看到 permission denied 相关记录。原因是 uWSGI 创建的 socket 文件权限不够Nginx 的 www 用户无法访问。解决方法是把 uWSGI 配置里的 chmod-socket 设为 664然后重启 uWSGI让 socket 文件重新按新权限生成。第三个坑是页面返回的是源码而不是渲染后的页面。现象是浏览器显示一串 HTML 代码或者提示下载文件。原因是 Nginx 没有把请求转发给 uWSGIlocation / 块里缺了 uwsgi_pass 配置请求被当成静态文件处理。解决方法是补上转发规则确认 include uwsgi_params 和 uwsgi_pass 都在同一个 location 块里。第四个坑是后台管理页面全裸奔样式全丢。现象是 admin 页面能看到表格但没有任何 CSS排版彻底塌掉。原因是 admin 的静态文件没有被 collectstatic 收集。解决方法是进入虚拟环境执行 collectstatic --noinput然后确认 staticfiles/admin 目录下存在 css、js 等子目录。第五个坑是改完代码后线上不生效。现象是本地改了一行代码push 到服务器后页面没变化。原因是 uWSGI 默认不会自动重载代码worker 进程里跑的还是旧版本。解决方法是执行 supervisorctl restart django_project或者发送 HUP 信号给 uWSGI 主进程做温和重载后者能避免中断正在进行的请求。6. 部署完别急着收工一套可复用的自检流程与日志习惯6.1 五个命令完成上线初检网站能打开只是第一步上线之后还要按顺序跑一遍自检。把下面这套检查固化到自己的部署流程里能省掉很多半夜被叫起来的场景。检查项、命令和期望结果对比如下检查项命令期望结果页面响应状态curl -I http://127.0.0.1/返回 200 OKuWSGI 进程状态supervisorctl status django_project状态为 RUNNINGsocket 文件存在ls -l /tmp/django_project.sock文件存在且权限 664静态文件可访问curl -I http://127.0.0.1/static/admin/css/base.css返回 200 OKNginx 错误日志tail -n 50 /www/wwwlogs/example.com.error.log无 traceback 或 permission denied这些命令可以合并成一个脚本放在项目里每次部署后执行一遍省得手动敲五遍。6.2 一套日志习惯和收尾动作日志是线上问题排查的唯一线索我一般把日志分成两个查看入口Nginx 的 error.log 看外层请求错误uWSGI 的 daemonize 日志看 Django 内部异常。执行 tail -f 同时盯住两个文件等用户复现问题报错信息会直接滚出来。tail -f /www/wwwlogs/example.com.error.log /var/log/django_project_uwsgi.log另外每次发布前固定执行一轮 collectstatic 与 migrate把这两步写进发布清单而不是靠大脑记忆。我第一次部署 Django 项目时就是在 collectstatic 这一步上栽了跟头admin 样式全丢排查了很久才反应过来是静态文件没收集。后来把这个动作固化成习惯就再也没翻过车。希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表