文章

Django 项目生产部署

部署概述

在 Linux 系统中,Django 项目的生产环境部署是一个综合性任务,主要涉及 Django 项目配置、Web 应用托管和代理服务器配置、生产数据库配置以及服务进程管理等内容。

项目结构

project/
│
├── backend/                   # 后端代码目录
│   ├── backend/               # 后端同名应用
│   │   ├── __init__.py
│   │   ├── asgi.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   ├── wsgi.py
│   │   └── ...
│   ├── app/                   # 后端应用代码
│   │   ├── migrations/        # 数据库迁移文件
│   │   ├── __init__.py
│   │   ├── admin.py
│   │   ├── apps.py
│   │   ├── models.py
│   │   ├── urls.py
│   │   ├── views.py
│   │   ├── tasks.py
│   │   └── ...
│   ├── conf/                # 后端配置文件
│   │   ├── cert/            # SSL 证书
│   │   ├── uwsgi.ini        # uWSGI 配置文件
│   │   └── ...
│   ├── settings/            # 不同环境配置文件
│   │   └── common.py        # 基础配置模块
│   │   └── development.py   # 开发环境配置
│   │   └── production.py    # 生成环境配置
│   ├── static/              # 静态资源
│   │   └── ...
│   ├── requirements.txt       # Python 依赖文件
│   ├── Dockerfile             # 后端 Dockerfile
│   ├── manage.py              # Django 管理脚本
│   └── ...
│
├── docs/                      # 项目文档
│   ├── architecture.md        # 架构文档
│   └── ...
│
├── .gitignore                  # Git 忽略文件
├── docker-compose.yml          # Docker Compose 配置文件
└── README.md                   # 项目说明文件

>本例 Django 项目中,前端页面使用的是 Django 渲染模板,主要涉及后端内容的部署。

Django 项目配置

使用 settings.py 分环境模块

settings.py 拆分为多个文件,例如 common.pydevelopment.pyproduction.py,并在每个环境配置文件中配置不同内容。

  • settings/common.py 定义在开发和生产之间共同的基本设置。
  • settings/development.py 定义开发环境的配置内容。
  • settings/production.py 定义生产环境的配置内容。

参考: https://developer.mozilla.org/zh-CN/docs/Learn/Server-side/Django/Deployment

DEBUG 设置

确保在生产环境中 DEBUGFalse,以避免泄露调试信息。

DEBUG = False

ALLOWED_HOSTS 设置

使用 * 允许所有主机名访问,但在生产环境中最好指定具体的主机名或 IP 地址。

ALLOWED_HOSTS = ['*']

ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']

安全配置

# HSTS 确保浏览器只通过 HTTPS 连接到项目网站
SECURE_HSTS_SECONDS = 3600
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

# SSL 重定向,自动将所有 HTTP 请求重定向到 HTTPS
SECURE_SSL_REDIRECT = True

# 防止浏览器对相应内容类型嗅探
SECURE_CONTENT_TYPE_NOSNIFF = True

# 启用浏览器 XXS 过滤器
SECURE_BROWSER_XSS_FILTER = True

# 启用安全 Cookie 确保仅通过 HTTPS 传输
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

# 点击劫持保护
X_FRAME_OPTIONS = 'DENY'
  • settings.py 相关 cookiessl 安全设置会迫使 http 请求重定向到 https
  • HSTS(HTTP Strict Transport Security) 配置根据需求,可以将 SECURE_HSTS_SECONDS 设置更长时间(例如一年)。
  • X_FRAME_OPTIONS:点击劫持保护,禁止其他网站通过 iframe 嵌入自己的项目网页,防止点击劫持攻击。

敏感信息

在 Django 项目中使用 .env 文件来集中管理环境变量,并通过 python-dotenv 库将其加载到环境中。

生成密钥 SECRET_KEY

首先,为确保 SECRET_KEY 密钥的安全性,使用如下 Python 脚本生成随机且唯一的密钥:

import secrets

def generate_secret_key(length=50):
    return secrets.token_urlsafe(length)

print(generate_secret_key())

创建 .env 文件

将所有敏感信息存储在环境变量中,避免硬编码。例如配置安全密钥、数据库用户名和密码等。

# .env file  
# 使用 python-dotenv 包读取环境变量  
  
# 调试开发环境  
#DJANGOSETTINGSMODULE='settings.development'  
  
# 部署生产环境  
DJANGO_SETTINGS_MODULE='settings.production'  
  
# 将根目录的 utils.py 中的 getenv 函数生成的密钥粘贴此处  
SECRET_KEY='gPi5ie7KAYhR5s7YuemgJHIKMgA5p9jxnG_VuUS32H5-JylowuXbXXtz4K179pcUCyg'

# 服务器主机地址  
HOST='43.153.xxx.xxx'

# 数据库用户名和密码
DB_USER='quill'
DB_PASS='xxxx'
  • 在部署代码阶段,安装依赖库 python-dotenv

>注意:.env 文件不应上传到版本控制系统(如 Git),通常通过 .gitignore 忽略。

加载 .env 文件

在项目的 settings 文件(本例 settings/production.py)中加载 .env 存储的环境变量:

import os
from dotenv import load_dotenv

# 加载 .env 文件中的环境变量
load_dotenv()

# 从环境变量中获取配置
SECRET_KEY = os.getenv('SECRET_KEY')
HOST = os.getenv('HOST')
DB_USER = os.getenv('DB_USER')
DB_PASS = os.getenv('DB_PASS')
REDIS_AUTH = os.getenv('REDIS_AUTH')

if not SECRET_KEY or not DB_USER or not DB_PASS or not REDIS_AUTH:
    raise ValueError('Missing one or more required environment variables.')

>注意:使用环境变量时应小心敏感信息的泄露,确保环境变量的访问权限是受保护的。

安全管理

应避免将环境变量文件或敏感信息上传到版本控制系统(如 Git)中。在 .gitignore 中添加如下内容:

.env
database/db.sqlite3
logs/nginx/*
logs/uwsgi/*
!logs/nginx/.gitkeep
!logs/uwsgi/.gitkeep
venv/*

安全测试

运行命令检查,以确保没有遗漏的安全设置。

python manage.py check --deploy

静态文件配置项

Django 项目有一些 CSS、JavaScript 等静态文件分散在项目的各个应用中,为了方便让 Nginx 处理对这些静态文件的请求,把项目中的全部静态文件收集到一个统一的目录,即项目的根目录,命名为 static

# 其他配置...
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
  • STATIC_ROOT 是用于生产环境,指定了 collectstatic 命令将静态文件集中到的目录。
  • STATICFILES_DIRS 是用于开发环境,指定了额外的静态文件目录供 Django 查找。

为了在开发和生产环境中使用不同的 settings.py 配置文件,接下来更新 manage.pyuwsgi.py 文件内容。

manage.py 项目管理

动态加载 Django 配置,提供一个易于管理的项目入口点,以支持不同环境(开发、生产)使用不同的配置。

from dotenv import load_dotenv

# 加载 .env 文件中的环境变量
load_dotenv()

def main():
    """Run administrative tasks."""
    # 从环境变量中获取 Django 设置模块
    # settings_module = os.getenv('DJANGO_SETTINGS_MODULE', 'backend.settings')
    settings_module = os.getenv('DJANGO_SETTINGS_MODULE')
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)
# 其他代码

backend/wsgi.py 设置环境

一个 WSGI 兼容的 Web 服务器网关接口文件。用于将 Django 项目运行在 WSGI 兼容 Web 服务器上,如 Apache、Nginx 等。

在后端项目同名应用下的 wsgi.py 接口文件中更新配置:通过 dotenv 加载 .env 文件中的环境变量,动态配置 Django 项目的 DJANGO_SETTINGS_MODULE 环境变量。

from dotenv import load_dotenv

# 加载 .env 文件中的环境变量
load_dotenv()

# 从环境变量中获取 Django 设置模块
# settingsmodule = os.getenv('DJANGOSETTINGSMODULE', 'backend.settings')
settings_module = os.getenv('DJANGO_SETTINGS_MODULE')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', settings_module)

# 其他代码

安装 MySQL

开发环境调试使用的是 Django 框架自带的的 SQLite3,生产环境使用 MySQL 关系数据库。

要安装 MySQL 需要先到 MySQL官方网站下载对应的 RPM文件,选择适合项目和系统的版本。

本例,为兼容项目和 CentOS 9 系统使用 MySQL 版本:8.0.39

MySQL 数据库解压,安装需要的组件:

rpm -ivh mysql-community-common-8.0.39-1.el9.x86_64.rpm && \
rpm -ivh mysql-community-libs-8.0.39-1.el9.x86_64.rpm && \
rpm -ivh mysql-community-debuginfo-8.0.39-1.el9.x86_64.rpm && \
rpm -ivh mysql-community-client-debuginfo-8.0.39-1.el9.x86_64.rpm && \ 
rpm -ivh mysql-community-client-plugins-8.0.39-1.el9.x86_64.rpm && \ 
rpm -ivh mysql-community-client-8.0.39-1.el9.x86_64.rpm && \
rpm -ivh mysql-community-icu-data-files-8.0.39-1.el9.x86_64.rpm && \
rpm -ivh mysql-community-server-8.0.39-1.el9.x86_64.rpm  && \
rpm -ivh mysql-community-devel-8.0.39-1.el9.x86_64.rpm

启动数据库服务,进入数据库中,创建项目使用的数据库用户信息,确保用户允许远程连接。

-- 登录 MySQL
mysql -u root -p

-- 创建允许远程连接的用户
CREATE USER '用户名'@'%' IDENTIFIED BY '密码';
GRANT ALL PRIVILEGES ON blog.* TO '用户名'@'%';
FLUSH PRIVILEGES;

Django 中配置数据库连接:

DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'database', 'db.sqlite3'),
    # }

    'default': {
        # 数据库引擎配置
        'ENGINE': 'django.db.backends.mysql',
        # 数据库的名称
        'NAME': 'blog',
        # 数据库服务器的 IP 地址(如果是本机,可以配置成 localhost 或 127.0.0.1)
        'HOST': HOST,
        # 启动 MySQL 服务的端口号
        'PORT': 3306,
        # 数据库用户名和口令
        'USER': DB_USER,
        'PASSWORD': DB_PASS,
        # 数据库使用的字符集
        'CHARSET': 'utf8mb4',
        # 数据库时间日期的时区设定
        'TIME_ZONE': 'Asia/Chongqing',
        # 设置连接最大存活时间(例如 600 秒,0 表示无连接池,None 表示永久连接)
        'CONN_MAX_AGE': 600,
    }
}

>若使用购买的云服务器,可能需要开启 3306 端口(若数据库部署与项目程序不在同一主机上,还需要管控 IP 地址)。

安装 Python

如果需要清除旧版本的安装,删除对应的安装的文件夹即可。

rm -rf  /usr/local/python38/

下载:

wget https://www.python.org/ftp/python/3.9.13/Python-3.9.13.tar.xz

参考 Python 源码编译安装

部署代码

拉取项目代码

本例,在 /usr/local/share/ 工作目录拉取项目代码。

git clone git@github.com:littlekj/helloBlog.git

创建虚拟环境

推荐在虚拟环境部署项目代码。本例使用 venv + pip 来管理 Python 项目中的虚拟环境和依赖。

参考 使用 venv + pip 管理虚拟环境

使用 python3 -m venv 命令来创建虚拟环境。虚拟环境将会创建一个包含 Python 解释器的独立目录,避免与系统的 Python 版本发生冲突。

本例,在工作的项目文件夹根目录(helloBlog/),初始化虚拟环境:

python3 -m venv venv  # 创建虚拟环境,'venv' 为虚拟环境文件夹

这样会在当前目录下创建一个名为 venv 的文件夹,包含一个干净的 Python 环境。

本例,虚拟环境路径即为 /usr/local/share/helloBlog/venv

激活虚拟环境

source venv/bin/activate

激活后,你的命令行提示符会发生变化,通常会显示虚拟环境的名称,例如 (venv),表示你现在在该虚拟环境中。

安装项目依赖(使用 pip)

在虚拟环境激活后,所有通过 pip 安装的包都会安装在该环境中,而不会影响到系统的全局环境。

安装单个依赖

pip install <package_name>  # 安装指定包,如 requests

安装多个依赖

如果你的项目中有多个依赖,可以在 requirements.txt 文件中列出依赖项。然后通过以下命令安装所有依赖:

pip install -r requirements.txt

数据迁移处理

数据库迁移

进入后端项目根目录(helloBlog/backend/),进行数据库迁移:

python manage.py migrate

创建管理员账户

python manage.py createsuperuser

收集静态文件

# 收集静态文件 
python manage.py collectstatic

Django 框架项目使用 Python manager.py runserver 启动服务用于开发测试,不建议用于生产环境。所以,生产环境的应用使用 WSGI 服务器托管启动。

配置应用服务器

在 Django 项目部署中,WSGI(Web Server Gateway Interface)是常用的接口标准。它定义了 Web 服务器与 Web 应用之间的通信方式,提供了一种将 Web 应用与服务器或中间件连接起来处理请求的规范。

uWSGI 是一个高性能的 WSGI 应用服务器,常用于托管 Python Web 应用程序,专门处理动态内容(应用的逻辑)。它支持多种应用程序协议、服务模型和部署选项。

uWSGI 典型部署结构:

客户端浏览器
     ↓
	Nginx(处理静态文件、负载均衡)
     ↓
	uWSGI(运行 Python 应用,如 Django)
     ↓
	Django App(处理业务逻辑)

Nginx 和 uWSGI 之间通过 uWSGI 协议通信,效率更高,比走完整的 HTTP 协议栈更轻量。

uWSGI 配置文件可以是用 INI 文件格式,通常文件扩展名为 .ini。配置文件包含了服务器的运行时配置选项,如应用路径、端口、进程数等。

安装 uWSGI:

venv 虚拟环境中安装:

pip install uwsgi

项目 uWSGI 配置文件:

在后端项目中编辑 uWSGI 配置文件 conf/uwsgi.ini

# conf/uwsgi.ini
[uwsgi]
# 配置前导路径,通常是项目的根目录
base=/usr/local/share/helloBlog/backend

# 项目名称,用于指代应用
name=backend

# 指定 uWSGI 启动时的工作目录
chdir=%(base)

# 指定 WSGI 应用模块,格式为’模块名:应用名‘
module=%(name).wsgi:application

# 启动守护进程模式,将 uWSGI 置于后台运行
master=true

# 设置进程数,通常 CPU 核心数的 2 到 4倍
processes=4

# 指定 PID 文件的位置,便于进程管理
#pidfile=/run/uwsgi.pid
pidfile=/tmp/uwsgi.pid

# 退出时自动清理 Unix 套接字和 PID 文件
vacuum=true

# 设置每个工作进程处理的最大请求数,超过会重启该进程
max-requests=5000

# 启用 thunder lock,优化多进程环境下的锁性能
thunder-lock = true

# 启用线程支持,如果应用程序依赖线程则需要启用
enable-threads = true

# 指定监听队列的长度
listen = 120

# 处理更大的请求数据块
buffer-size = 32768  # 增加缓冲区大小
harakiri = 60        # 设置超时时间
post-buffering = 4096  # 处理较大 POST 数据块

# 使用非 root 用户运行 uWSGI,提高安全性
uid=www-data
gid=www-data

# 设置 Python 虚拟环境的路径
pythonhome=/usr/local/share/helloBlog/venv

# 指定通信的地址和端口,格式为‘IP:端口’
# 如果使用 Unix 套接字,格式为‘socket=/tmp/uwsgi.sock’
socket=127.0.0.1:8000

# 将日志输出到指定文件
logto=%(base)/logs/uwsgi/uwsgi.log

# 或让 uWSGI 进程在后台运行并记录日志
#daemonize=%(base)/logs/uwsgi/uwsgi-daemon.log
  • 设置监听队列,uWSGI 文档
  • 设置缓冲区大小以处理更大的请求数据块。
  • 可以使用普通用户 www-data 以安全的方式运行,赋予权限:
chown -R www-data:www-data /path/to/your/project
chown www-data:www-data /path/to/your/uwsgi.sock
chown www-data:www-data /path/to/your/logs

启动 uWSGI 服务器

>可略过,后续使用 Systemd 管理服务进程。

安装 nohup 工具:

yum install coreutils

启动服务器:

进入后端项目根目录执行:

nohup uwsgi --ini conf/uwsgi.ini -b 65535 > logs/uwsgi.log 2>&1 &
  • nohup 是一个工具,用于在后台运行命令并忽略挂起信息。nohup 允许 uWSGI 进程在用户注销或中断关闭后继续运行,适用于生产环境。
  • -b 65535 设置请求的缓冲区大小。
  • > uwsgi.log 2>&1 将标准输出和标准错误重定向到 uwsgi.log 文件。不指定输出文件,nohup 默认输出重定向到 nohup.out
  • 使用 & 符号将进程放到后台运行。

参考 uWSGI 文档如何使用 WSGI 部署 Django 项目

配置 Web 服务器

在现代 Web 架构中,Nginx 作为反向代理服务器代表后端应用服务器处理客户端请求,它位于目标服务器的前端,处理客户端发来的请求,并将请求转发给相应的内部服务器。

uWSGI 和 Nginx 实现项目的动静分离,Nginx 可以处理静态文件请求,动态请求则转发给 uWSGI。Nginx 和 uWSGI 通过 uWSGI 协议或 HTTP 协议进行高效的通信,分离职责,可以最大化性能。

>Nginx 和 uWSGI 之间常用 uWSGI 协议或 HTTP 协议。uWSGI 协议比 HTTP 更高效,是推荐的选择。

安装 Nginx

yum install nginx

配置 Nginx

修改全局配置文件(/etc/nginx/nginx.conf):

# 配置用户
user www-data;

# 工作进程数(建议跟CPU的核数量一致)
worker_processes auto;

# 进程文件
pid /run/nginx.pid;

# 加载动态模块配置
include /usr/share/nginx/modules/*.conf;

# 工作模式(多路IO复用方式)和连接上限
events {
    use epoll;  # `epoll` 是一种 I/O 事件通知机制,提供处理高并发请求的效率
    worker_connections 1024;
}

# HTTP服务器相关配置
http {
    # 日志格式
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    # 访问日志
    access_log  /var/log/nginx/access.log  main;

    # 错误日志
    error_log  /var/log/nginx/error.log;

    # 开启高效文件传输模式
    sendfile            on;

    # 用sendfile传输文件时有利于改善性能
    tcp_nopush          on;

    # 禁用Nagle来解决交互性问题
    tcp_nodelay         on;

    # 客户端保持连接时间
    keepalive_timeout   65;

    # MIME类型配置的最大哈希大小
    types_hash_max_size 4096;

    # 包含MIME类型的配置
    include             /etc/nginx/mime.types;

    # 默认使用二进制流格式
    default_type        application/octet-stream;

    # 加载模块化配置
    include /etc/nginx/conf.d/*.conf;

    # 包含项目的Nginx配置文件
    #include /usr/local/share/helloBlog/backend/conf/*.conf;
 
    client_header_buffer_size 2k;
    large_client_header_buffers 4 8k;
    client_max_body_size 2m;

    # 拦截访问 IP 的请求
    server {
        listen 80;
        server_name 43.153.xxx.xxx;
        return 301 https://example.com$request_uri;
    }

    server {
        listen 443 ssl http2;
        server_name 43.153.xxx.xxx;
    
        #ssl_certificate     example.com_bundle.crt;
        #ssl_certificate_key example.com.key;
        ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

        return 301 https://example.com$request_uri;
    }

    # HTTP 服务器配置
    server {
        listen       80;
        listen       [::]:80;
        server_name  example.com;  # 指定处理请求的域名地址
        return 301 https://example.com$request_uri;  # 将所有 HTTP 请求重定向到 HTTPS

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        # 处理主页面请求
        location / {# 匹配所有请求路径
            include uwsgi_params;
            uwsgi_pass 127.0.0.1:8000;  # uWSGI socket 地址
        }

        # 处理 Twikoo 服务请求
        location /comment {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        # 处理静态文件请求
        location /static/ {
            alias /usr/local/share/helloBlog/backend/static/;  # 静态文件存储路径
            expires 30d;  # 缓存静态文件 30 天
        }
        # 日志文件配置
        #access_log /usr/local/share/helloBlog/backend/logs/nginx/access.log;
        #error_log /usr/local/share/helloBlog/backend/logs/nginx/error.log;
    }

    # HTTPS 服务器配置
    server {
        listen       443 ssl http2; # 在 HTTPS 中启用 HTTP/2,以提高性能
        listen       [::]:443 ssl http2;
        server_name  example.com;  # 指定处理请求的域名地址

        # SSL 配置
        #ssl_certificate     example.com_bundle.crt;
        #ssl_certificate_key example.com.key;
        ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
        ssl_session_timeout 5m;
        
        # 兼容性更好的协议版本
        ssl_protocols TLSv1.2 TLSv1.3;

        # 允许的加密套件(兼顾安全性与兼容性)
        ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:!MD5;
        
        ssl_prefer_server_ciphers on;

        # 关闭 OCSP Stapling(对搜索引擎友好)
        ssl_stapling off;
        ssl_stapling_verify off;

        include /etc/nginx/default.d/*.conf;

        # 处理所有路径的请求
        location / {
            include uwsgi_params;  # 包含与 uWSGI 服务器通信所需的标准参数
            uwsgi_pass 127.0.0.1:8000;  # uWSGI socket 地址
        }
        
        # 处理 Twikoo 服务请求
        location /comment {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        # 处理静态文件请求
        location /static/ {
            alias /usr/local/share/helloBlog/backend/static/;  # 静态文件存储路径
            expires 30d;  # 缓存静态文件 30 天
        }
        # 日志文件配置
        #access_log /usr/local/share/helloBlog/backend/logs/nginx/access.log;
        #error_log /usr/local/share/helloBlog/backend/logs/nginx/error.log;
    }
}

说明:

  • server_name 可以指定域名或公网 IP,让所有来自该域名或 IP 地址的请求被 Nginx 服务处理。
  • location /:是 Nginx 配置的规则, / 是通用匹配所有以 / 开头的请求路径。
    • include uwsgi_params;: 包含了与 uWSGI 服务器通信所需的标准参数。uwsgi_params 包括了与 uWSGI 服务器进行交互所需的 HTTP 请求头和参数。
    • uwsgi_pass 127.0.0.1:8000; 将请求转发到 127.0.0.1:8000,即本地回环地址上的 uWSGI 服务器。
    • Nginx 中使用回环地址将请求转发到本机上的 uWSGI 实例。它是安全的,避免外部访问直接到 uWSGI 的端口,使所有请求都必须经过 Nginx,从而运行 Nginx 进行额外的处理和安全控制。
    • 相应地,如果 uWSGI 服务器仅绑定在 127.0.0.1 上,并且没有绑定到外部接口(如 0.0.0.0),那么外部用户将无法直接访问这个端口。它只能监听本地 Nginx 访问。
  • location /static/:这个块处理所有以 /static/ 开头的 URL 请求。
    • 浏览器请求,Nginx 接收请求,匹配 location /static/ 块。
    • Nginx 在配置参数 alias 对应路径上寻找静态资源。Nginx 将其返回给浏览器,完成静态资源的加载访问。
    • Nginx 直接处理静态文件的请求,而不经过 Web 应用服务器,从而减轻其负担,并加速静态资源的传输。

HTTPS 加密配置

网站部署建议使用 HTTPS 加密访问,可以到相关网站申请域名,国内服务器需要备案;然后使用 openssl 自签名配置 SSL 证书,或在域名网站生成 SSL 证书,存放在 Nginx 配置文件指定的路径。

网站访问

公网 IP(Public IP)是指你在互联网上的唯一地址,它允许其他设备或服务从全球范围内访问你的服务器或计算机。

在 Linux 系统下,公网 IP 可以通过 curl 命令获取。

获取公网 IPv4

# curl -s https://ipinfo.io/ip
101.34.xxx.xxx

>注意:若开启了代理服务,获取的公网 IP 一般会有变化。

防火墙设置

确保服务器的防火墙允许相关端口的流量:HTTP (80)、HTTPS (443) 和 MySQL (3306) 等。

负载均衡

当服务器性能不佳时,可以部署多个后端应用服务器,使用 Nginx 的负载均衡器,将请求分发到指定的后端应用服务器,并根据权重进行负载均衡。

修改 nginx.conf 配置(未验证):

# 配置用户
user www-data;

# 工作进程数(建议跟CPU的核数量一致)
worker_processes auto;

# 进程文件
pid /run/nginx.pid;

# 加载动态模块配置
include /usr/share/nginx/modules/*.conf;

# 工作模式(多路IO复用方式)和连接上限
events {
    use epoll;  # `epoll` 是一种 I/O 事件通知机制,提供处理高并发请求的效率
    worker_connections 1024;
}

# HTTP服务器相关配置
http {
    # 日志格式
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    # 访问日志
    access_log  /var/log/nginx/access.log  main;

    # 错误日志
    error_log  /var/log/nginx/error.log;

    # 开启高效文件传输模式
    sendfile            on;

    # 用sendfile传输文件时有利于改善性能
    tcp_nopush          on;

    # 禁用Nagle来解决交互性问题
    tcp_nodelay         on;

    # 客户端保持连接时间
    keepalive_timeout   65;

    # MIME类型配置的最大哈希大小
    types_hash_max_size 4096;

    # 包含MIME类型的配置
    include             /etc/nginx/mime.types;

    # 默认使用二进制流格式
    default_type        application/octet-stream;

    # 加载模块化配置
    include /etc/nginx/conf.d/*.conf;

    # 包含项目的Nginx配置文件
    #include /usr/local/share/helloBlog/backend/conf/*.conf;
 
    client_header_buffer_size 2k;
    large_client_header_buffers 4 8k;
    client_max_body_size 2m;

    # 负载均衡配置
	upstream backend {
        server 192.168.1.100 weight=2;  # 后端应用服务器 1,权重为 2
        server 192.168.1.101 weight=1;  # 后端应用服务器 2,权重为 1
        server 192.168.1.102 weight=1;  # 后端应用服务器 3,权重为 1

        keepalive 32;  # 提高与后端通信效率
    }

    # 拦截访问 IP 的请求
    server {
        listen 80;
        server_name 43.153.xxx.xxx;
        return 301 https://example.com$request_uri;
    }

    server {
        listen 443 ssl http2;
        server_name 43.153.xxx.xxx;
    
        #ssl_certificate     example.com_bundle.crt;
        #ssl_certificate_key example.com.key;
        ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

        return 301 https://example.com$request_uri;
    }

    # HTTP 服务器配置
    server {
        listen       80;
        listen       [::]:80;
        server_name  example.com;  # 指定处理请求的域名地址
        return 301 https://example.com$request_uri;  # 将所有 HTTP 请求重定向到 HTTPS

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        # 处理主页面请求
        location / {# 匹配所有请求路径
            include uwsgi_params;
            uwsgi_pass http://backend;  # 使用负载均衡池
        }

        # 处理 Twikoo 服务请求
        location /comment {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        # 处理静态文件请求
        location /static/ {
            alias /usr/local/share/helloBlog/backend/static/;  # 静态文件存储路径
            expires 30d;  # 缓存静态文件 30 天
        }
        # 日志文件配置
        #access_log /usr/local/share/helloBlog/backend/logs/nginx/access.log;
        #error_log /usr/local/share/helloBlog/backend/logs/nginx/error.log;
    }

    # HTTPS 服务器配置
    server {
        listen       443 ssl http2; # 在 HTTPS 中启用 HTTP/2,以提高性能
        listen       [::]:443 ssl http2;
        server_name  example.com;  # 指定处理请求的域名地址

        # SSL 配置
        #ssl_certificate     example.com_bundle.crt;
        #ssl_certificate_key example.com.key;
        ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
        ssl_session_timeout 5m;
        
        # 兼容性更好的协议版本
        ssl_protocols TLSv1.2 TLSv1.3;

        # 允许的加密套件(兼顾安全性与兼容性)
        ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:!MD5;
        
        ssl_prefer_server_ciphers on;

        # 关闭 OCSP Stapling(对搜索引擎友好)
        ssl_stapling off;
        ssl_stapling_verify off;

        include /etc/nginx/default.d/*.conf;

        # 处理所有路径的请求
        location / {
            include uwsgi_params;  # 包含与 uWSGI 服务器通信所需的标准参数
            uwsgi_pass http://backend;  # 使用负载均衡池
        }
        
        # 处理 Twikoo 服务请求
        location /comment {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        # 处理静态文件请求
        location /static/ {
            alias /usr/local/share/helloBlog/backend/static/;  # 静态文件存储路径
            expires 30d;  # 缓存静态文件 30 天
        }
        # 日志文件配置
        #access_log /usr/local/share/helloBlog/backend/logs/nginx/access.log;
        #error_log /usr/local/share/helloBlog/backend/logs/nginx/error.log;
    }
}

说明:

  • 负载均衡配置:在 http 部分添加 upstream backend,定义了负载均衡池 backendserver 指令用于指定后端服务器静态 IP 地址或 DNS 名称及其权重。
  • HTTPS 服务器中更新 uwsgi_passhttp://backend,将请求转发到负载均衡池。这样,HTTPS 请求将通过负载均衡池转发到不同的后端服务器。

管理服务进程

systemd 是现代 Linux 系统的标准服务管理器。它更加适合于生产环境,尤其是大规模部署的应用。它提供了稳定的进程管理、日志、资源限制和自动重启等功能。

创建 uWSGI 服务

创建服务文件

sudo vim /etc/systemd/system/myblog-uwsgi.service

写入内容

[Unit]
Description=MyBlog uWSGI Service
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/usr/local/share/helloBlog/backend
Environment=PYTHONPATH=/usr/local/share/helloBlog/backend
ExecStart=/usr/local/share/helloBlog/venv/bin/uwsgi --ini /usr/local/share/helloBlog/backend/conf/uwsgi.ini
ExecStop=/bin/kill -QUIT $MAINPID
Restart=always
RestartSec=3
KillSignal=SIGQUIT
TimeoutStopSec=30           # 给 uWSGI 更长时间来停止
StandardOutput=journal      # 输出日志到 journal
StandardError=journal       # 错误日志输出到 journal
SyslogIdentifier=myblog-uwsgi
LimitNOFILE=65536           # 限制文件描述符数量
LimitMEMLOCK=536870912      # 限制内存锁定
MemoryLimit=536870912       # 限制内存使用

[Install]
WantedBy=multi-user.target

创建 Elasticsearch 服务

> 注意:Elasticsearch 不能用 root 用户运行,必须用专用用户。

确保 elasticuser 存在

# 创建用户(如果还没创建)
sudo useradd -r -m -s /bin/bash elasticuser

# 授予 elasticsearch 目录权限
sudo chown -R elasticuser:elasticuser /opt/elasticsearch

创建服务文件

sudo vim /etc/systemd/system/elasticsearch.service

写入内容

[Unit]
Description=Elasticsearch 7.x
After=network.target
After=syslog.target

[Service]
Type=simple
User=elasticuser
Group=elasticuser
ExecStart=/opt/elasticsearch/bin/elasticsearch
ExecStop=/bin/kill -TERM $MAINPID
Restart=always
RestartSec=3
LimitMEMLOCK=infinity
LimitNOFILE=65536
LimitNPROC=4096
LimitAS=infinity
WorkingDirectory=/opt/elasticsearch
StandardOutput=journal
StandardError=journal
TimeoutStopSec=30
KillMode=process
Environment="ES_JAVA_OPTS=-Xms512m -Xmx1g"  # 设置 JVM 堆内存

[Install]
WantedBy=multi-user.target

启用并启动服务

# 重载 systemd 配置
sudo systemctl daemon-reload

# 启用开机自启
sudo systemctl enable myblog-uwsgi.service
sudo systemctl enable elasticsearch.service

# 启动服务
sudo systemctl start myblog-uwsgi.service
sudo systemctl start elasticsearch.service

查看状态和日志

# 查看状态
sudo systemctl status myblog-uwsgi.service
sudo systemctl status elasticsearch.service

# 查看日志(uWSGI)
sudo journalctl -u myblog-uwsgi.service -f

# 查看日志(Elasticsearch)
sudo journalctl -u elasticsearch.service -f

站点访问

一切准备就绪后,启动 Web 服务器、应用服务器、数据库及相关服务进程,即可通过域名访问站点内容。

本文由作者按照 CC BY 4.0 进行授权。