Django+PostgreSQL在Ubuntu 14.04生产环境部署实战
1. 为什么 Django 默认用 SQLite 却偏偏要换 PostgreSQL?——从 Ubuntu 14.04 环境说起
你刚跑通第一个 Django 项目,python manage.py runserver启动成功,页面弹出来那一刻特别有成就感。但很快,同事在 Slack 里甩来一句:“别在本地用 SQLite 做开发了,上线前得切 PostgreSQL,不然字段类型、事务行为、并发锁机制全对不上。”你点开settings.py里的DATABASES配置,发现默认确实是 SQLite —— 轻量、免配置、适合教学演示。可真实业务一上来,你就得面对:用户注册并发写入时的竞态问题、JSON 字段存结构化数据的需求、全文检索要支持中文分词、地理坐标要做空间查询……这些,SQLite 做不了,MySQL 做得勉强,而 PostgreSQL 不仅原生支持,还做得极稳。
我第一次在 Ubuntu 14.04 上把 Django 项目从 SQLite 切到 PostgreSQL,是在一个社区投票系统上线前两周。当时没做充分压测,上线后第三天凌晨三点收到告警:数据库连接池耗尽,所有 API 接口返回 500。排查发现,Django 的CONN_MAX_AGE=0(即每次请求都新建连接),而 PostgreSQL 默认max_connections=100,但 Ubuntu 14.04 的内核参数ulimit -n只设了 1024,加上 uwsgi worker 数量没调优,连接数瞬间打穿。这不是 PostgreSQL 的锅,是没理解它和 Django 在 Linux 底层资源调度上的耦合逻辑。Ubuntu 14.04 虽然已是老版本(2014 年发布,2019 年停止标准支持),但它至今仍被大量遗留政企系统、教育平台、嵌入式网关设备所采用——不是因为新系统不能用,而是因为整套部署链路(Ansible 脚本、监控探针、备份策略)都固化在这一版上,升级成本远高于维护成本。所以,“How To Use PostgreSQL with your Django Application on Ubuntu 14.04” 这个标题,表面是教安装步骤,实质是教你在资源受限、内核老旧、包管理陈旧的生产环境中,如何让两个成熟但“脾气不合”的组件——Python 生态的 Django 和 C 生态的 PostgreSQL——真正协同工作,而不是互相拖垮。
关键词里虽然空着,但热搜词已经暴露了真实战场:postgresql和mysql区别是选型焦虑,django项目部署是落地卡点,dbeaver连接postgresql是调试刚需,ubuntu 安装postgresql 14+是版本兼容陷阱。尤其要注意ubuntu postgresql 二进制安装和源码安装postgresql这两条热词——它们直指 Ubuntu 14.04 的核心矛盾:官方源里只有 PostgreSQL 9.3(2013 年发布),而 Django 2.0+ 已要求至少 9.4,Django 3.2+ 明确推荐 10+。你不可能靠apt-get install postgresql一步到位。必须亲手编译或引入第三方源,而这一步,决定了后续所有连接、权限、扩展加载是否顺畅。这不是“会不会”的问题,而是“敢不敢在生产环境动底层数据库”的实操门槛。接下来的内容,不讲虚的,只拆解我在三个不同客户现场踩过的坑:一次是政务内网离线环境,一次是教育云虚拟机集群,一次是银行前置机——它们共同运行在 Ubuntu 14.04 上,都用 Django 写业务,也都最终选择了 PostgreSQL,但每条路径都不一样。
2. Ubuntu 14.04 下 PostgreSQL 的三种安装路径:为什么不能只信 apt?
Ubuntu 14.04 的apt源里,postgresql包版本锁定在9.3.26(最后安全更新于 2019 年)。这个版本能跑 Django 1.11,但无法支持JSONField(Django 3.1+)、ArrayField的高级索引(如 GIN + jsonb_path_ops)、pg_trgm模糊搜索等现代功能。更致命的是,它不支持SCRAM-SHA-256认证协议,而新版客户端(如 psycopg3、DBeaver 23+)默认启用该协议,导致连接直接拒绝。所以,第一步必须明确:你不是在“安装 PostgreSQL”,而是在“为 Django 选择一个能长期共存的 PostgreSQL 运行时”。我实测过三种路径,每种都有明确适用场景和硬性约束。
2.1 路径一:APT 官方源 + 手动降级 Django(仅限验证/测试环境)
这是最“安全”的路径,但也是最危险的妥协。操作流程如下:
# 更新源并安装(会自动拉取 9.3) sudo apt-get update sudo apt-get install postgresql postgresql-contrib python-dev # 创建数据库用户(注意:Ubuntu 14.04 默认创建 'postgres' 系统用户,但 Django 不建议用它) sudo -u postgres createuser --interactive --pwprompt myappuser sudo -u postgres createdb -O myappuser myappdb # 修改 pg_hba.conf(关键!Ubuntu 14.04 默认路径是 /etc/postgresql/9.3/main/pg_hba.conf) # 在文件末尾添加: # local myappdb myappuser md5 # host myappdb myappuser 127.0.0.1/32 md5 sudo service postgresql restart提示:此路径下,Django
settings.py必须显式指定ENGINE:'django.db.backends.postgresql_psycopg2',且psycopg2版本不能高于 2.7.7(否则会因协议不兼容报server closed the connection unexpectedly)。我试过用 pip3 install psycopg2==2.7.7,但 Ubuntu 14.04 的 Python 3.4 编译器不支持其 wheel,必须pip3 install psycopg2-binary==2.7.7。这带来新问题:binary 包自带 libpq,可能与系统libpq5冲突,导致ImportError: libssl.so.1.0.0: cannot open shared object file。解决方案是sudo apt-get install libssl1.0.0(Ubuntu 14.04 默认是 1.0.1f,需手动降级)——但降级 libssl 会影响整个系统的 HTTPS 通信,所以此路径仅允许用于本地开发机或隔离的 Docker 容器,绝对不可用于生产服务器。
2.2 路径二:PostgreSQL Global Development Group (PGDG) 第三方源(推荐用于准生产环境)
这是平衡安全与功能的最佳实践。PGDG 提供了针对各 Ubuntu 版本的独立仓库,其中 Ubuntu 14.04 对应trusty-pgdg,可安装 PostgreSQL 9.6(2016 年发布,2021 年 EOL)或 10(2017 年发布,2022 年 EOL)。9.6 已足够支撑 Django 2.2,且比 9.3 多出UPSERT(ON CONFLICT)、并行查询、逻辑复制等关键特性。操作步骤严格按 PGDG 官方文档执行:
# 添加密钥和源(注意:Ubuntu 14.04 代号是 trusty) wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - echo "deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list # 更新并安装(这里选 9.6,避免 10+ 的 systemd 兼容问题) sudo apt-get update sudo apt-get install postgresql-9.6 postgresql-client-9.6 postgresql-contrib-9.6 # 初始化集群(PGDG 源不会自动初始化,必须手动) sudo pg_createcluster 9.6 main --start # 创建用户和库(同上,但版本号变为 9.6) sudo -u postgres createuser --interactive --pwprompt myappuser sudo -u postgres createdb -O myappuser myappdb注意:
pg_createcluster是postgresql-common包提供的工具,它会自动创建/etc/postgresql/9.6/main/目录结构,并生成postgresql.conf和pg_hba.conf。关键在于,它默认启用unix_socket_directories = '/var/run/postgresql',而 Django 的HOST参数若留空或设为'',会尝试连接/tmp/.s.PGSQL.5432(老路径),导致ConnectionRefusedError。必须在settings.py中显式设置HOST='/var/run/postgresql'。这个细节,99% 的教程都不会提,但它是 Ubuntu 14.04 + PGDG 源下 Django 连接失败的头号原因。
2.3 路径三:源码编译安装(唯一适用于高安全合规场景)
当客户要求“所有组件必须自主可控、无第三方依赖、可审计二进制”时,APT 和 PGDG 都不满足。我曾在某省级社保平台项目中执行此方案:从 PostgreSQL 官网下载 9.6.24 源码(最后一个支持 Ubuntu 14.04 内核的稳定版),全程离线编译。过程极其繁琐,但换来的是完全掌控:
# 准备编译环境(Ubuntu 14.04 默认缺很多 dev 包) sudo apt-get install build-essential zlib1g-dev libreadline-dev libssl-dev # 解压并配置(关键参数:--prefix 指定安装路径,避免污染 /usr;--with-openssl 启用 SSL) tar -xzf postgresql-9.6.24.tar.gz cd postgresql-9.6.24 ./configure --prefix=/opt/pgsql-9.6.24 --with-openssl --with-python # 编译安装(耗时约 25 分钟,CPU 占满) make && sudo make install # 初始化数据目录(必须用 postgres 用户执行) sudo adduser postgres sudo mkdir -p /data/pgsql-9.6.24/data sudo chown -R postgres:postgres /data/pgsql-9.6.24 sudo -u postgres /opt/pgsql-9.6.24/bin/initdb -D /data/pgsql-9.6.24/data # 启动服务(不能用 systemctl,Ubuntu 14.04 是 upstart) echo "start on runlevel [2345]" | sudo tee /etc/init/postgresql-9.6.conf echo "exec sudo -u postgres /opt/pgsql-9.6.24/bin/pg_ctl -D /data/pgsql-9.6.24/data -l /data/pgsql-9.6.24/logfile start" | sudo tee -a /etc/init/postgresql-9.6.conf sudo start postgresql-9.6实战心得:源码编译最大的坑是
libxml2版本。Ubuntu 14.04 自带 libxml2 2.9.1,但 PostgreSQL 9.6 要求 >= 2.7.6,看似满足,实则其xml2-config --version输出格式有 bug,导致 configure 脚本误判为不支持。解决方案是下载 libxml2 2.9.4 源码,./configure --prefix=/opt/libxml2 && make && sudo make install,然后在 PostgreSQL configure 时加PKG_CONFIG_PATH=/opt/libxml2/lib/pkgconfig。这个过程没有捷径,必须逐行读config.log文件定位失败点。好处是,一旦编译成功,/opt/pgsql-9.6.24/bin/psql --version输出干净,ldd /opt/pgsql-9.6.24/bin/psql显示所有依赖都在/opt下,审计报告可以直接截图提交。
3. Django settings.py 的七处致命配置:从连接池到时区的深度校准
安装完 PostgreSQL 只是开始,Django 的数据库配置才是决定系统生死的关键。我见过太多项目,PostgreSQL 装得完美,psql -U myappuser -d myappdb连接秒通,但 Django 一启动就报django.db.utils.OperationalError: FATAL: password authentication failed for user "myappuser"。问题不在密码,而在settings.py里七个看似微小、实则致命的配置项。它们共同构成 Django 与 PostgreSQL 之间的“通信协议”,任何一项错位,都会引发雪崩。
3.1 ENGINE 和 NAME:为什么必须用 'postgresql' 而非 'postgresql_psycopg2'
Django 1.9+ 开始,'django.db.backends.postgresql_psycopg2'已被弃用,官方文档明确要求使用'django.db.backends.postgresql'。这不是简单的字符串替换。psycopg2是 Python 的 PostgreSQL 驱动,而'postgresql'是 Django 内部封装的统一接口。当你写'postgresql_psycopg2'时,Django 会跳过部分连接参数校验,直接透传给驱动;而'postgresql'会先解析OPTIONS字典,再构造标准连接字符串。在 Ubuntu 14.04 上,psycopg2-binary==2.7.7存在一个已知 bug:若OPTIONS中包含options='-c default_transaction_isolation=repeatable read',它会错误地将-c当作命令行参数而非连接选项,导致启动失败。而'postgresql'引擎会自动过滤非法选项,保证启动成功率。因此,正确配置是:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', # 必须是这个 'NAME': 'myappdb', 'USER': 'myappuser', 'PASSWORD': 'your_secure_password', 'HOST': '/var/run/postgresql', # 注意:不是 'localhost',也不是空字符串 'PORT': '5432', 'OPTIONS': { 'options': '-c search_path=myapp_schema,public' # 指定默认 schema } } }3.2 HOST 和 PORT:Unix Socket vs TCP/IP 的性能与安全博弈
Ubuntu 14.04 默认启用 Unix Socket,路径为/var/run/postgresql/.s.PGSQL.5432。Django 连接时,若HOST为空或'localhost',它会优先尝试 TCP/IP(即127.0.0.1:5432),这比 Unix Socket 慢 15%-20%,且多一层网络栈。但若HOST设为'/var/run/postgresql'(socket 目录),Django 会自动生成 socket 文件路径,连接速度提升明显。然而,这带来权限问题:Django 进程(如 uwsgi)通常以www-data用户运行,而/var/run/postgresql目录属主是postgres:postgres,默认权限drwxr-s---,www-data组无访问权。解决方案有两个:
- 改目录权限(简单粗暴):
sudo chmod 755 /var/run/postgresql,但违反最小权限原则; - 加用户进组(推荐):
sudo usermod -a -G postgres www-data,然后重启 uwsgi。
提示:
PORT字段在此场景下必须留空或设为'',否则 Django 会强制走 TCP/IP。我曾因PORT='5432'导致连接延迟从 2ms 涨到 18ms,压测时 QPS 直接掉 30%。
3.3 CONN_MAX_AGE:连接池的双刃剑与 Ubuntu 14.04 的 ulimit 诅咒
CONN_MAX_AGE控制数据库连接复用时间(秒)。设为0表示每次请求都新建连接;设为60表示连接最多复用 60 秒;设为None表示永久复用。在 Ubuntu 14.04 上,ulimit -n默认是 1024,而每个 Django worker(uwsgi 或 gunicorn)会维护自己的连接池。若workers=4,CONN_MAX_AGE=60,高峰期每个 worker 可能持有 20+ 连接,则总连接数轻松突破 1000,触发Too many open files错误。我的解决策略是:永远不设CONN_MAX_AGE=None,且根据ulimit -n动态计算上限。公式为:max_connections_per_worker = (ulimit_n - 256) // workers。例如ulimit -n 4096,workers=4,则CONN_MAX_AGE=30,MAX_CONNS=300(由 PostgreSQLmax_connections控制)。在settings.py中:
import os ULIMIT_N = int(os.popen('ulimit -n').read().strip()) WORKERS = 4 MAX_CONNS_PER_WORKER = max(5, (ULIMIT_N - 256) // WORKERS) # 至少保留 5 个连接给系统 DATABASES = { 'default': { # ... 其他配置 'CONN_MAX_AGE': 30, 'OPTIONS': { 'MAX_CONNS': MAX_CONNS_PER_WORKER, } } }3.4 TIME_ZONE 和 USE_TZ:PostgreSQL 时区与 Django 的隐式转换陷阱
Django 默认USE_TZ=True,要求数据库存储 UTC 时间,应用层负责时区转换。但 PostgreSQL 的timestamp without time zone类型不存时区信息,timestamp with time zone(即timestamptz)才存。Django 的DateTimeField在USE_TZ=True时,会强制映射为timestamptz。问题来了:Ubuntu 14.04 的 PostgreSQL 9.3/9.6 默认timezone参数是local(即系统时区),而系统时区可能是Asia/Shanghai。当 Django 写入2023-10-01 12:00:00+08:00时,PostgreSQL 会将其转为2023-10-01 04:00:00+00:00存储;读取时再转回+08:00。但如果postgresql.conf中timezone='UTC',而 DjangoTIME_ZONE='Asia/Shanghai',写入时会多转一次,导致时间错乱 8 小时。我的铁律是:DjangoTIME_ZONE和 PostgreSQLtimezone必须一致,且USE_TZ=True。检查并修改:
# 查看当前 timezone sudo -u postgres psql -c "SHOW timezone;" # 修改 /etc/postgresql/*/main/postgresql.conf # timezone = 'Asia/Shanghai' sudo service postgresql restart3.5 OPTIONS 中的 client_encoding 和 options:字符集与会话参数的精准控制
Ubuntu 14.04 的 locale 默认是en_US.UTF-8,但很多中文系统会设为zh_CN.UTF-8。PostgreSQL 创建数据库时,若未指定LC_COLLATE和LC_CTYPE,会继承系统 locale,导致ORDER BY排序异常(如“张三”排在“李四”后面)。Django 连接时,必须显式声明client_encoding,否则 psycopg2 可能用错编码。正确配置:
'OPTIONS': { 'client_encoding': 'UTF8', 'options': '-c default_transaction_isolation=repeatable read -c work_mem=16MB' }work_mem=16MB是关键:Ubuntu 14.04 内存有限,work_mem过大会导致排序、哈希操作内存溢出,过小则频繁写磁盘。我通过EXPLAIN ANALYZE观察慢查询的Sort Method: external merge Disk: 12345kB,反推合理值。16MB 是 4GB 内存机器的黄金值。
3.6 TEST 名称:避免测试库名冲突导致迁移失败
Django 测试时会自动创建test_<NAME>库。若NAME='myappdb',则测试库为test_myappdb。但 PostgreSQL 9.3 不支持CREATE DATABASE test_myappdb TEMPLATE myappdb(模板库不能是正在使用的库),导致python manage.py test报database "test_myappdb" does not exist。解决方案是显式指定测试库名,且确保它与主库名无关:
'DEFAULT': { # ... 主库配置 }, 'TEST': { 'NAME': 'myappdb_test', # 独立名称 'MIRROR': 'default', # 镜像主库结构,不镜像数据 }3.7 ATOMIC_REQUESTS:全局事务的代价与替代方案
ATOMIC_REQUESTS=True会让每个 HTTP 请求包裹在BEGIN...COMMIT中。听起来很安全,但在 Ubuntu 14.04 上,它会显著增加连接持有时间,加剧CONN_MAX_AGE压力。更糟的是,Django 的ATOMIC_REQUESTS无法处理SELECT FOR UPDATE等显式锁,容易死锁。我的经验是:禁用ATOMIC_REQUESTS,改用@transaction.atomic装饰器精准控制。只在真正需要事务的视图(如支付扣款、库存扣减)上标注,其他读操作保持无事务,释放连接更快。
4. 从零构建可复现的 Django + PostgreSQL 部署脚本:适配 Ubuntu 14.04 的最小化 Ansible Playbook
手工执行apt-get install、sudo -u postgres createdb等命令,适合学习,但无法用于生产。生产环境要求:可重复、可审计、可回滚、无交互。我基于 Ansible 2.0(Ubuntu 14.04 默认支持的最高版)编写了一个最小化 Playbook,它能在 5 分钟内,在一台裸机 Ubuntu 14.04 上完成 PostgreSQL 9.6 + Django 2.2 的完整部署,且所有步骤均可验证。脚本不依赖外部网络(除首次 apt update),所有包均预下载缓存。
4.1 目录结构与变量设计:解耦环境与逻辑
Playbook 结构如下:
django-postgres-1404/ ├── group_vars/ │ └── all.yml # 全局变量:postgres_version, django_version, app_user ├── roles/ │ ├── postgresql/ # PostgreSQL 安装与配置 │ ├── django-app/ # Django 项目部署 │ └── nginx-uwsgi/ # 反向代理与进程管理 ├── site.yml # 主入口 └── requirements.txt # Python 依赖清单group_vars/all.yml是核心,定义了所有可配置项:
# PostgreSQL 配置 postgres_version: "9.6" postgres_data_dir: "/data/pgsql-{{ postgres_version }}/data" postgres_conf_dir: "/etc/postgresql/{{ postgres_version }}/main" # Django 配置 django_app_name: "myapp" django_app_path: "/opt/{{ django_app_name }}" django_app_user: "www-data" django_secret_key: "{{ lookup('password', '/dev/null length=50 chars=ascii_letters,digits') }}" # 安全加固 ssh_port: 2222 firewall_rules: - port: 5432 proto: tcp state: enabled - port: 80 proto: tcp state: enabled注意:
django_secret_key使用 Ansible 的passwordlookup 插件动态生成,避免硬编码。firewall_rules显式开放 5432 端口,因为 Ubuntu 14.04 的 ufw 默认 deny all。
4.2 PostgreSQL Role:从源码编译到服务注册的全链路
roles/postgresql/tasks/main.yml是精华所在。它不调用apt,而是执行源码编译全流程:
- name: Install build dependencies apt: name: "{{ item }}" state: present loop: - build-essential - zlib1g-dev - libreadline-dev - libssl-dev - libxml2-dev - name: Download PostgreSQL source get_url: url: "https://ftp.postgresql.org/pub/source/v{{ postgres_version }}/postgresql-{{ postgres_version }}.tar.gz" dest: "/tmp/postgresql-{{ postgres_version }}.tar.gz" checksum: "sha256:{{ postgres_checksum }}" # 校验和预存于 vars/main.yml - name: Extract and compile command: | tar -xzf /tmp/postgresql-{{ postgres_version }}.tar.gz -C /tmp cd /tmp/postgresql-{{ postgres_version }} ./configure --prefix=/opt/pgsql-{{ postgres_version }} --with-openssl make -j$(nproc) sudo make install args: executable: /bin/bash - name: Initialize cluster command: "/opt/pgsql-{{ postgres_version }}/bin/initdb -D {{ postgres_data_dir }}" become: yes become_user: postgres args: creates: "{{ postgres_data_dir }}/PG_VERSION" # 幂等性:只在目录不存在时执行 - name: Configure postgresql.conf lineinfile: path: "{{ postgres_conf_dir }}/postgresql.conf" line: "{{ item }}" loop: - "listen_addresses = '127.0.0.1'" - "port = 5432" - "max_connections = 200" - "shared_buffers = 512MB" - "timezone = 'Asia/Shanghai'" notify: restart postgresql - name: Configure pg_hba.conf lineinfile: path: "{{ postgres_conf_dir }}/pg_hba.conf" line: "host {{ django_app_name }}db {{ django_app_user }} 127.0.0.1/32 md5" notify: reload postgresql关键技巧:
notify触发 handler,handlers/main.yml中定义:- name: restart postgresql service: name: postgresql-{{ postgres_version }} state: restarted - name: reload postgresql service: name: postgresql-{{ postgres_version }} state: reloaded这样,配置修改后自动 reload,无需手动
sudo service postgresql reload。
4.3 Django App Role:虚拟环境隔离与迁移自动化
roles/django-app/tasks/main.yml确保 Python 环境纯净:
- name: Create app directory file: path: "{{ django_app_path }}" state: directory owner: "{{ django_app_user }}" group: "{{ django_app_user }}" mode: '0755' - name: Create virtualenv pip: name: virtualenv state: present become: yes - name: Create venv command: "/usr/local/bin/virtualenv --python=python3.4 {{ django_app_path }}/venv" args: creates: "{{ django_app_path }}/venv/bin/activate" - name: Install Python packages pip: name: "{{ item }}" virtualenv: "{{ django_app_path }}/venv" loop: "{{ lookup('file', 'requirements.txt') | split('\n') }}" when: item != '' - name: Copy Django project copy: src: "../src/" dest: "{{ django_app_path }}/src/" owner: "{{ django_app_user }}" group: "{{ django_app_user }}" - name: Run migrations django_manage: app_path: "{{ django_app_path }}/src" command: migrate app_settings: "settings.production" virtualenv: "{{ django_app_path }}/venv" become: yes become_user: "{{ django_app_user }}"注意:
django_manage模块是 Ansible 的 Django 专用模块,它会自动激活 venv 并执行python manage.py migrate。app_settings指向settings.production,其中已预置了前述DATABASES配置。
4.4 Nginx + uWSGI:进程守护与健康检查的闭环
roles/nginx-uwsgi/tasks/main.yml实现零宕机部署:
- name: Install nginx and uwsgi apt: name: "{{ item }}" state: present loop: - nginx - uwsgi - uwsgi-plugin-python3 - name: Configure nginx template: src: nginx.conf.j2 dest: /etc/nginx/sites-available/{{ django_app_name }} notify: reload nginx - name: Enable nginx site file: src: /etc/nginx/sites-available/{{ django_app_name }} dest: /etc/nginx/sites-enabled/{{ django_app_name }} state: link - name: Configure uWSGI template: src: uwsgi.ini.j2 dest: /etc/uwsgi/apps-available/{{ django_app_name }}.ini notify: restart uwsgi - name: Enable uWSGI app file: src: /etc/uwsgi/apps-available/{{ django_app_name }}.ini dest: /etc/uwsgi/apps-enabled/{{ django_app_name }}.ini state: linktemplates/uwsgi.ini.j2中的关键参数:
[uwsgi] module = {{ django_app_name }}.wsgi:application master = true processes = 4 threads = 2 socket = /run/uwsgi/{{ django_app_name }}.sock chmod-socket = 664 chown-socket = www-data:www-data vacuum = true die-on-term = true harakiri = 30harakiri=30是救命参数:若请求超过 30 秒未响应,uWSGI 强制杀死 worker,防止一个慢查询拖垮整个进程池。这在 Ubuntu 14.04 的老旧硬件上尤为重要。
5. 故障排查实战:从 “connection refused” 到 “permission denied” 的完整诊断链
部署完成后,90% 的问题不是出在代码,而是出在操作系统与数据库的胶水层。我整理了一份 Ubuntu 14.04 + Django + PostgreSQL 的故障树,覆盖从连接建立到查询执行的全路径。每个节点都附带one-liner诊断命令和修复方案,可直接粘贴到终端执行。
5.1 连接层故障:connection refused的五种根因
connection refused是最常见错误,但原因千差万别。必须按顺序排查:
| 步骤 | 诊断命令 | 预期输出 | 问题定位 | 修复方案 |
|---|---|---|---|---|
| 1. PostgreSQL 服务是否运行? | sudo service postgresql status | postgresql is running | 服务未启动 | sudo service postgresql start |
| 2. PostgreSQL 是否监听 5432? | sudo netstat -tulpn | grep :5432 | tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN 1234/postgres | 未监听 localhost | 检查postgresql.conf中listen_addresses和port |
| 3. pg_hba.conf 是否允许连接? | sudo cat /etc/postgresql/*/main/pg_hba.conf | grep -A5 -B5 "myappdb" | host myappdb myappuser 127.0.0.1/32 md5 | 规则缺失或顺序错误 | 在pg_hba.conf顶部添加规则,sudo service postgresql reload |
| 4. Django HOST 配置是否正确? | python -c "from django.conf import settings; print(settings.DATABASES['default']['HOST'])" | /var/run/postgresql | HOST 设为localhost或空 | 修改settings.py,HOST='/var/run/postgresql' |
| 5. Unix Socket 权限是否正确? | ls -ld /var/run/postgresql | drwxr-s--- 2 postgres postgres 4096 Oct 1 10:00 /var/run/postgresql | www-data不在postgres组 | sudo usermod -a -G postgres www-data |
提示:第 4 步和第 5 步常被同时触发。
HOST='localhost'会走 TCP/IP,但pg_hba.conf若只配了local规则(Unix Socket),则连接失败;反之,HOST='/var/run/postgresql'但权限不足,也会失败。必须同步检查。
5.2 认证层故障:password authentication failed的深层解析
此错误看似密码错,实则涉及三重认证链:Django 连接字符串 → PostgreSQLpg_hba.conf→ PostgreSQL 用户密码哈希。排查链路:
- 确认用户密码已设置:
sudo -u postgres psql -c "\du myappuser",查看Password列是否为********(表示已设); - 确认密码哈希算法:PostgreSQL 9.6 默认用
md5,但若用户是CREATE USER myappuser WITH ENCRYPTED PASSWORD 'xxx'创建,则用scram-sha-256(9.6 不支持)。必须用ALTER USER myappuser PASSWORD 'xxx'重设; - 确认
pg_hba.conf规则匹配:host规则要求md5,local规则要求peer或md5。若规则是local all all peer,则HOST='/var/run/postgresql'时,Django 进程用户
