news 2026/10/22 4:10:18

Ubuntu上从零搭建Django项目:虚拟环境、PostgreSQL、Gunicorn与Nginx完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ubuntu上从零搭建Django项目:虚拟环境、PostgreSQL、Gunicorn与Nginx完整指南

1. 为什么在Ubuntu上搭建Django项目值得单独写一篇

很多人第一次接触Django,是在Windows或者macOS上跟着教程跑通了runserver,觉得“不过如此”。但真正把项目往服务器上一扔,各种环境问题、依赖冲突、静态文件404、数据库连接被拒就全冒出来了。Ubuntu作为最主流的服务器操作系统之一,和Django的组合几乎是Python Web开发者的必修课。这篇文章要聊的,就是怎么在Ubuntu上从零把一个Django项目搭起来,不是那种“跑通就行”的演示,而是能直接拿去当项目起点的完整流程。

核心关键词就几个:Ubuntu、Django、虚拟环境、PostgreSQL、Gunicorn、Nginx、静态文件、部署。这套组合是目前Python Web后端最经典的落地方式,适合刚学完Django基础、准备把项目真正跑在服务器上的开发者,也适合那些在Windows上写得好好的、一上Linux就懵的朋友。我会把每一步为什么这么做讲清楚,包括参数怎么算、坑在哪里、哪些操作看起来没问题实际上会埋雷。

整篇内容基于我自己的实操经验,环境是Ubuntu 22.04 LTS,Python 3.10,Django 4.2 LTS。不同版本细节会有差异,但思路是通用的。你跟着走一遍,应该能搭出一个结构清晰、能扛住基本访问压力的Django项目骨架。

2. 搭建前的整体设计与思路拆解

2.1 为什么不用系统自带的Python直接跑

Ubuntu系统里预装了Python,很多人图省事直接pip install django就开干。这个做法在个人玩具项目上没问题,但一旦项目稍微正经一点,就会遇到两个致命问题:一是系统自带的Python被系统工具依赖,你装的包可能和系统包冲突,搞不好把系统工具搞崩;二是不同项目依赖的Django版本、第三方库版本不一样,全局安装只能装一个版本,项目A要Django 3.2,项目B要Django 4.2,直接打架。

所以第一步必须是虚拟环境。虚拟环境本质上就是给每个项目单独开一个Python运行空间,里面的包互不干扰。你可以把它理解成每个项目有自己的工具箱,而不是所有人共用一套工具,谁用谁拿、用完放回,不会串。

2.2 数据库选SQLite还是PostgreSQL

Django默认用SQLite,一个文件就是数据库,开发阶段确实方便。但SQLite在高并发写入、复杂查询、多用户同时操作时表现很差,而且不支持一些PostgreSQL才有的字段类型和索引特性。项目一旦要上线,基本都要换PostgreSQL。

我的建议是:开发阶段就可以直接用PostgreSQL。原因很简单,SQLite和PostgreSQL在字段类型、约束行为、大小写敏感、事务处理上都有差异,开发用SQLite、上线换PostgreSQL,很容易出现“本地好好的,线上就报错”的情况。一开始就用PostgreSQL,能避免这种迁移带来的隐性bug。

2.3 应用服务器为什么选Gunicorn

Django自带的runserver是开发服务器,单线程、性能差、没有进程管理,绝对不能用于生产环境。生产环境需要一个WSGI服务器来跑Django应用,Gunicorn是其中最主流的选择之一。它稳定、配置简单、支持多worker进程,配合Nginx做反向代理,就是一套标准的Python Web部署方案。

Nginx负责处理静态文件、转发动态请求、做负载均衡和SSL终止,Gunicorn负责跑Python应用逻辑。两者分工明确,各干各擅长的事。

2.4 整体架构长什么样

整个请求链路是这样的:用户请求先到Nginx,Nginx判断是静态文件就直接返回,是动态请求就转发给Gunicorn,Gunicorn把请求交给Django处理,Django查数据库、渲染模板、返回响应,再原路返回给用户。

这个架构的好处是静态文件由Nginx直接处理,速度快、不占用Python进程;动态请求由Gunicorn管理,可以灵活调整worker数量;数据库独立运行,方便备份和扩展。

3. 核心细节解析与实操要点

3.1 Ubuntu基础环境准备

拿到一台干净的Ubuntu服务器,第一件事不是急着装Django,而是把基础环境理顺。先更新软件源,确保装的是最新稳定版:

sudo apt update sudo apt upgrade -y

然后装一些基础工具,后面编译Python包、管理服务都要用到:

sudo apt install -y build-essential libpq-dev python3-dev python3-pip python3-venv git curl

这里解释几个关键包的作用。build-essential提供gcc、make等编译工具,很多Python包安装时需要编译C扩展;libpq-dev是PostgreSQL的客户端开发库,装psycopg2的时候必须要有;python3-venv是虚拟环境模块,Ubuntu默认可能没装;git用于拉代码,curl用于测试接口。

注意:不要用sudo pip install装任何Python包。sudo pip会把包装到系统Python里,污染系统环境,后面出问题很难排查。所有Python包都应该在虚拟环境里用普通用户权限安装。

3.2 创建项目目录与虚拟环境

我习惯把项目放在/home/用户名/projects/项目名下面,而不是/var/www。原因是普通用户对home目录有完全权限,操作方便,不需要频繁sudo;而/var/www通常需要root权限,容易因为权限问题导致文件读写失败。

mkdir -p ~/projects/myproject cd ~/projects/myproject python3 -m venv venv source venv/bin/activate

执行source venv/bin/activate之后,命令行提示符前面会出现(venv),说明虚拟环境激活成功。这时候which python应该指向~/projects/myproject/venv/bin/python,而不是/usr/bin/python3。

虚拟环境激活后,先升级pip,再装Django和数据库驱动:

pip install --upgrade pip pip install django psycopg2-binary gunicorn

这里用psycopg2-binary而不是psycopg2,是因为binary版本自带编译好的二进制文件,不需要本地编译,安装快、不容易出错。生产环境如果对性能有极致要求,可以装源码版psycopg2,但需要确保libpq-dev和python3-dev已经装好。

3.3 创建Django项目与应用

django-admin startproject config . python manage.py startapp core

注意startproject后面的config .,这个点表示在当前目录创建项目,而不是再套一层目录。项目配置文件会放在config/目录下,manage.py在项目根目录。这种结构比默认的myproject/myproject/更清晰,根目录直接就是项目根。

startapp core创建了一个叫core的应用。应用名可以按业务来定,比如用户相关叫accounts,订单相关叫orders。Django项目可以由多个应用组成,每个应用负责一块独立功能。

创建完应用后,要在config/settings.py的INSTALLED_APPS里注册:

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'core', ]

3.4 数据库配置与用户创建

先装PostgreSQL:

sudo apt install -y postgresql postgresql-contrib sudo systemctl start postgresql sudo systemctl enable postgresql

然后创建数据库和用户。PostgreSQL默认有一个postgres超级用户,切换到它来操作:

sudo -u postgres psql

在psql里执行:

CREATE DATABASE myproject_db; CREATE USER myproject_user WITH PASSWORD 'your_strong_password'; ALTER ROLE myproject_user SET client_encoding TO 'utf8'; ALTER ROLE myproject_user SET default_transaction_isolation TO 'read committed'; ALTER ROLE myproject_user SET timezone TO 'Asia/Shanghai'; GRANT ALL PRIVILEGES ON DATABASE myproject_db TO myproject_user; \q

这里几个设置值得说明。client_encoding设为utf8确保中文不乱码;default_transaction_isolation设为read committed是Django官方推荐的事务隔离级别;timezone设成东八区,避免时间显示错乱。

注意:PostgreSQL 15及以上版本,普通用户对public schema默认没有CREATE权限,需要额外授权。如果后面migrate时报权限错误,在psql里执行GRANT ALL ON SCHEMA public TO myproject_user;即可。

然后在settings.py里配置数据库连接:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'myproject_db', 'USER': 'myproject_user', 'PASSWORD': 'your_strong_password', 'HOST': 'localhost', 'PORT': '5432', } }

生产环境不要把密码硬编码在settings.py里,应该用环境变量读取。可以用os.environ.get('DB_PASSWORD')的方式,配合.env文件或系统环境变量。

3.5 时区、语言与静态文件配置

settings.py里几个关键配置:

LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_TZ = True STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'staticfiles' STATICFILES_DIRS = [BASE_DIR / 'static'] MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media'

USE_TZ = True表示数据库存UTC时间,显示时按TIME_ZONE转换。这个设置建议保持True,避免跨时区部署时时间混乱。STATIC_ROOT是collectstatic收集静态文件的目标目录,Nginx会从这个目录读静态文件。STATICFILES_DIRS是开发时额外的静态文件目录。

3.6 生成数据库表与创建超级用户

python manage.py makemigrations python manage.py migrate python manage.py createsuperuser

makemigrations根据模型生成迁移文件,migrate把迁移应用到数据库。createsuperuser创建后台管理账号,按提示输入用户名、邮箱、密码。

到这里,一个基础的Django项目就跑起来了。可以先用python manage.py runserver 0.0.0.0:8000测试一下,浏览器访问服务器IP的8000端口,能看到Django欢迎页就说明基础环境没问题。

4. 实操过程与核心环节实现

4.1 Gunicorn配置与systemd服务管理

开发服务器测试通过后,就要上Gunicorn了。先手动跑一下确认能启动:

gunicorn --bind 0.0.0.0:8000 config.wsgi:application

config.wsgi是wsgi模块路径,application是WSGI应用对象。能正常启动、浏览器能访问,说明Gunicorn配置没问题。

但手动跑有个问题:终端一关,进程就没了;服务器重启,服务也不会自动起来。所以要用systemd来管理Gunicorn进程。

创建服务文件:

sudo nano /etc/systemd/system/myproject.service

内容如下:

[Unit] Description=Gunicorn daemon for myproject After=network.target [Service] User=your_username Group=www-data WorkingDirectory=/home/your_username/projects/myproject ExecStart=/home/your_username/projects/myproject/venv/bin/gunicorn \ --access-logfile - \ --error-logfile - \ --workers 3 \ --bind unix:/home/your_username/projects/myproject/myproject.sock \ config.wsgi:application [Install] WantedBy=multi-user.target

worker数量怎么定?一个常见的经验公式是2 * CPU核心数 + 1。比如2核CPU就设5个worker。但这不是绝对的,还要看应用是CPU密集型还是IO密集型。IO密集型可以适当多设,CPU密集型设太多反而增加上下文切换开销。我一般先用2 * 核心数 + 1,然后根据实际负载调整。

--bind unix:...sock表示用Unix socket而不是TCP端口。Unix socket比TCP快,而且不需要占用端口,Nginx和Gunicorn在同一台机器上时推荐用这种方式。

启动并设置开机自启:

sudo systemctl start myproject sudo systemctl enable myproject sudo systemctl status myproject

注意:socket文件的目录权限要正确。Gunicorn以your_username身份运行,Nginx以www-data身份运行,两者都要能访问socket文件。把用户加入www-data组,或者把socket目录权限设为775、属组为www-data,都能解决。我遇到过socket权限不对导致502的情况,排查了半天才发现是权限问题。

4.2 Nginx反向代理配置

装Nginx:

sudo apt install -y nginx sudo systemctl start nginx sudo systemctl enable nginx

创建站点配置:

sudo nano /etc/nginx/sites-available/myproject

内容:

server { listen 80; server_name your_domain_or_ip; location = /favicon.ico { access_log off; log_not_found off; } location /static/ { alias /home/your_username/projects/myproject/staticfiles/; } location /media/ { alias /home/your_username/projects/myproject/media/; } location / { include proxy_params; proxy_pass http://unix:/home/your_username/projects/myproject/myproject.sock; } }

启用站点:

sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx

nginx -t测试配置语法,没问题再reload。location /static/和location /media/让Nginx直接处理静态文件和媒体文件,不经过Gunicorn,效率高很多。

4.3 静态文件收集与权限处理

Django的静态文件分散在各个应用的static目录里,部署时需要集中收集到STATIC_ROOT:

python manage.py collectstatic

这个命令会把所有静态文件复制到staticfiles/目录。Nginx配置里的alias指向这个目录。

权限问题是个高频坑。Nginx以www-data身份运行,如果staticfiles目录或文件权限不对,Nginx读不到,就会返回403。解决办法:

chmod -R 755 /home/your_username/projects/myproject/staticfiles

如果home目录权限太严(比如700),Nginx也进不去。需要确保/home/your_username至少有执行权限(x),让www-data能穿过目录到达staticfiles。

实操心得:我习惯在部署脚本里加一句find staticfiles -type d -exec chmod 755 {} \;和find staticfiles -type f -exec chmod 644 {} \;,确保目录和文件权限统一,避免因为某个文件权限不对导致404或403。

4.4 防火墙与安全组配置

Ubuntu默认可能开了ufw防火墙。如果开了,要放行Nginx的80和443端口:

sudo ufw allow 'Nginx Full' sudo ufw allow OpenSSH sudo ufw enable

如果用的是云服务器,还要在云平台的安全组里放行对应端口。这一步经常被忽略,导致本地能访问、外网访问不了。

4.5 环境变量与敏感信息管理

生产环境的SECRET_KEY、数据库密码、第三方API密钥都不应该写在代码里。推荐用环境变量管理。可以在systemd服务文件里加EnvironmentFile:

[Service] EnvironmentFile=/home/your_username/projects/myproject/.env

.env文件内容:

DJANGO_SECRET_KEY=your_secret_key_here DB_PASSWORD=your_db_password DEBUG=False ALLOWED_HOSTS=your_domain,www.your_domain

settings.py里读取:

import os SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY') DEBUG = os.environ.get('DEBUG', 'False') == 'True' ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')

.env文件要加入.gitignore,绝对不能提交到代码仓库。

5. 常见问题与排查技巧实录

5.1 502 Bad Gateway排查思路

502是Nginx连不上Gunicorn的典型表现。排查顺序:

  1. 先看Gunicorn服务状态:sudo systemctl status myproject,确认进程在跑。
  2. 看Gunicorn日志:sudo journalctl -u myproject -n 50,有没有报错。
  3. 检查socket文件是否存在:ls -l /home/your_username/projects/myproject/myproject.sock。
  4. 检查socket权限:Nginx用户能不能访问这个socket。
  5. 检查Nginx错误日志:sudo tail -f /var/log/nginx/error.log。

最常见的原因是socket权限不对,或者Gunicorn根本没启动成功。还有一种情况是Gunicorn启动时报了Python异常,进程直接退出,systemd反复重启,这时候journalctl里能看到完整的Python traceback。

5.2 静态文件404或403

404通常是路径不对,403通常是权限不对。先确认collectstatic执行过,STATIC_ROOT目录里有文件。然后检查Nginx配置里的alias路径是否和STATIC_ROOT一致。最后检查权限,确保www-data能读到文件。

还有一个隐蔽的坑:Django的DEBUG=False时,Django自己不再处理静态文件,完全依赖Nginx。如果Nginx配置没生效,就会404。开发时DEBUG=True能访问,上线后404,基本都是这个原因。

5.3 数据库连接被拒

报错类似could not connect to server: Connection refused或FATAL: password authentication failed。排查:

  • PostgreSQL服务是否在跑:sudo systemctl status postgresql
  • 用户名密码是否正确:psql -U myproject_user -d myproject_db -h localhost
  • pg_hba.conf里的认证方式是否允许密码登录
  • 数据库用户是否有连接权限

PostgreSQL默认只监听localhost,如果Django和数据库不在同一台机器,要改postgresql.conf里的listen_addresses,并在pg_hba.conf里放行对应IP。

5.4 中文乱码问题

数据库编码、客户端编码、Django配置三处都要是UTF-8。创建数据库时指定ENCODING 'UTF8',pg_hba.conf里客户端编码设为utf8,Django的settings.py里DEFAULT_CHARSET保持默认utf-8。如果已经建了库发现编码不对,只能重建数据库。

5.5 常见问题速查表

现象可能原因排查命令/操作
502 Bad GatewayGunicorn未启动或socket权限不对systemctl status myproject、检查socket权限
静态文件404collectstatic未执行或Nginx路径不对python manage.py collectstatic、检查alias
静态文件403文件权限不足chmod -R 755 staticfiles
数据库连接被拒服务未启动或认证失败systemctl status postgresql、检查pg_hba.conf
中文乱码编码不一致检查数据库、客户端、Django三处编码
修改代码不生效Gunicorn未重启sudo systemctl restart myproject
外网访问不了防火墙或安全组未放行ufw status、检查云平台安全组

避坑技巧:每次改完代码,记得sudo systemctl restart myproject。Gunicorn不会自动热加载,改了Python代码不重启,跑的还是旧代码。这个坑我踩过不止一次,明明改了逻辑,测试还是旧行为,查半天才发现是没重启。

6. 项目结构优化与后续扩展建议

6.1 推荐的目录结构

一个清晰的项目结构能让后续维护轻松很多。我常用的结构:

myproject/ ├── config/ │ ├── settings/ │ │ ├── base.py │ │ ├── dev.py │ │ └── prod.py │ ├── urls.py │ └── wsgi.py ├── core/ │ ├── migrations/ │ ├── templates/ │ ├── static/ │ ├── models.py │ ├── views.py │ └── urls.py ├── static/ ├── media/ ├── staticfiles/ ├── venv/ ├── .env ├── manage.py └── requirements.txt

把settings拆成base、dev、prod三个文件,开发和生产环境用不同的配置。DJANGO_SETTINGS_MODULE环境变量指定用哪个。这样本地开发可以开DEBUG、用SQLite,生产环境关DEBUG、用PostgreSQL,互不干扰。

6.2 依赖管理

用pip freeze > requirements.txt导出依赖,部署时pip install -r requirements.txt安装。但pip freeze会把所有间接依赖都列出来,版本锁得很死。更好的做法是用pip-tools,维护一个requirements.in只写直接依赖,用pip-compile生成锁定的requirements.txt。

6.3 日志配置

生产环境必须有完善的日志。Django的LOGGING配置可以按级别输出到文件,Gunicorn的access log和error log也要保留。日志文件要配置轮转,避免磁盘被写满。可以用系统的logrotate,也可以用Python的RotatingFileHandler。

6.4 后续可以扩展的方向

项目跑起来只是起点。后面可以加Celery做异步任务,加Redis做缓存和消息队列,加Sentry做错误监控,加Docker做容器化部署。但不要一上来就堆这些,先把基础架构跑稳,遇到性能瓶颈或业务需求再逐步引入。过早引入复杂组件,只会增加维护成本。

我个人在实际操作中的体会是,Django项目部署最难的从来不是某个命令记不住,而是环境之间的差异和权限问题。同一套流程,在A服务器上跑得好好的,换到B服务器就可能因为Python版本、PostgreSQL版本、目录权限不同而出各种幺蛾子。所以每换一个环境,都要把关键版本和权限检查一遍,别想当然。另外,把部署过程写成脚本,每次部署照着跑,比手动敲命令靠谱得多,也方便回溯问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 10:28:03

磷酸铁锂串联电池组主动均衡方案设计与调试实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/21 12:25:48

h265_decoder.zip:HEVC解码SDK调试与ABI兼容性实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/21 5:04:26

复合指标计算 Text2SQL 实操:大模型生成包含比率与同环比的精准 SQL

在很多刚接触 Text2SQL 的团队看来,“大模型能够生成包含 WHERE 和 GROUP BY 的简单聚合 SQL”就已经大功告成了。然而,当把系统真正推向生产环境的业务分析师和财务总监时,系统往往会在第一时间惨遭嫌弃:因为业务在日常经营管理中…

作者头像 李华
网站建设 2026/10/18 10:41:13

ARM汇编全景解析:寄存器模型、指令系统与函数调用约定

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华