Talking About Django App Deployment and Performance
Introduction As I've gone deeper into my work, I've increasingly found that there are many pitfalls in Python web development, and I've always envied the executable-file deployment and high performance of AspNetCore and Go, as well as the richness of the Spring ecosystem. But since work uses Django, life still has to go on—Django is at least a web framework with a large market share, and it's not that bad; at least in development speed it utterly beats a bunch of other frameworks~ In a previous article I introduced how to deploy a Django app with Docker, but that deployment approach is only suitable for going live
目录6 个章节
Introduction
As I've gone deeper into my work, I've increasingly found that there are many pitfalls in Python web development, and I've always envied the executable-file deployment and high performance of AspNetCore and Go, as well as the richness of the Spring ecosystem. But since work uses Django, life still has to go on—Django is at least a web framework with a large market share, and it's not that bad; at least in development speed it utterly beats a bunch of other frameworks~
In a previous article I introduced how to deploy a Django app with Docker, but that deployment approach is only suitable for going live for debugging: it serves with Django's built-in web server, single-threaded, with very low performance. For a real production service, you still need to deploy with uwsgi plus nginx.
Clarifying the Approach
I previously wrote an article introducing how to deploy a Django app directly on a Linux server with uwsgi+nginx. Now, for easier management, we'll use Docker to deploy the Django app.
I've seen many solutions online that also put nginx in Docker, which feels a bit off. We may have many projects on a server; one nginx per project not only uses extra resources, but is also quite a hassle in terms of configuration.
A single nginx installed on the system is completely sufficient and convenient to configure. For our team, the company's network security is quite strict, with restrictions on open ports on the server and domain names we can apply for. We can't open a port per application; we can only have nginx listen on one port and configure reverse proxies to serve multiple projects, so one nginx is very convenient.
Our approach is to put Django and uwsgi in Docker—the usual way—use docker-compose to orchestrate related dependent database, cache, and monitoring services, then use nginx for reverse proxy and static file serving. For static files and hot-reloading of code, we also need volume mapping.
Next I'll walk through, step by step, how to build a convenient uwsgi+django image.
Writing the Dockerfile
First is our image file. Compared to the previous development image, it adds a uwsgi module; everything else stays the same.
FROM python:3.7
# 设置 python 环境变量
ENV PYTHONUNBUFFERED 1
# 创建 code 文件夹并将其设置为工作目录
RUN mkdir /code
WORKDIR /code
# 更新 pip
RUN pip install -i https://mirrors.aliyun.com/pypi/simple pip -U
# 设置国内源
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple
# 将 requirements.txt 复制到容器的 code 目录
ADD requirements.txt /code/
# 安装库
RUN pip install -r requirements.txt
# 将当前目录复制到容器的 code 目录
ADD . /code
# 安装uwsgi
RUN pip install uwsgi
uwsgi ini Configuration
For uwsgi configuration I chose the ini format; the file contents are as follows:
[uwsgi]
socket = :8000
# the base directory (full path)
chdir = /code
# Django s wsgi file
module = config.wsgi
# 启用主进程,当进程挂掉会再spawn一个(以下三个指标,实现“并发”)
master = true
# 进程个数
processes = 4
# 每个进程中的线程个数/workers
threads = 2
# 一个工作进程最大请求数
max-requests = 5000
vacuum = true
# pid文件,用于脚本启动、停止该进程
pidfile = /code/uwsgi.pid
# 当文件改变时,优雅的重启uWSGI
touch-reload = /code/readme.md
One thing to note: in the previous configuration we used daemonize = log/uwsgi.log to set the log file path so uwsgi would write logs, but if you do that with Docker, docker-compose will treat the container as finished and exit automatically when it starts. So as a workaround we can only skip writing a log file and output to the console, which keeps the container from shutting down (if I find a better approach later I'll update the article to record it)
I also used touch-reload to watch for file changes and automatically restart uwsgi, which gives us hot-reloading of code (strictly speaking it's not real hot-reloading haha. But it's good enough) Every time you update the code, also tweak the readme file—you can record what changed, and the service restarts automatically. Super convenient~
However, this kind of hot-reload will cause brief bad requests, so try to update the service during low-traffic periods~
The current system is still fairly simple, so my approach is: once testing on the test server looks fine, sync the code to the production server~ Since the frontend is a mini program and the user base isn't large yet, users basically don't notice~
Docker-Compose
Not much to say here—just changed the command~
version: "3"
services:
redis:
image: redis
expose:
- 6379
web:
restart: always # 除正常工作外,容器会在任何时候重启,比如遭遇 bug、进程崩溃、docker 重启等情况。
build: .
environment:
- ENVIRONMENT=docker
command: uwsgi uwsgi.ini
volumes:
- .:/code
ports:
- "19001:8000"
depends_on:
- redis
After the steps above, a simple docker-compose up command gets our service running. Then configure reverse proxy and static files in nginx and you're all set. As for nginx configuration, I plan to cover that in the next article.
Finally, about performance: after the above deployment, tests show this system can currently handle up to 1800 QPS with no optimizations at all. Pros might think that's too low—yeah, Python's performance is indeed… but for our current product it's more than enough (overkill). If not, have the company throw more hardware at it; it's cloud anyway, easy to scale (the company has plenty of servers and money haha) Optimize as needed later… I'm out haha
Feel Free to Discuss
I've put together a series of technical articles and materials. Reply with linux, flutter, c#, netcore, android, kotlin, java, python, etc. in the backend of the WeChat official account 「程序设计实验室」 to get related technical articles and materials. If you have any questions you can also leave a message in the official account backend~
_EOF_
Talking About Django App Deployment and Performance
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
告别单调,Django后台主页改造
前言 之前我做了个Django的项目,为了让管理后台更加美观,我对Django(应该说是SimpleUI的)默认的Admin后台主页进行改造,具体可以看这篇文章:项目完成 - 基于Django3.x版本 - 开发部署小结 之前的两篇关于Django3.x开发部署小结的文章介绍的都是数据库、接口、性能方面的,看到有朋友留言对后台主页的改造表示有兴趣,所以就写一篇来介绍一下~ 分析 改造基于我定制的
2022-06-28 · Django · 约 1 分钟
使用Django-Channels实现websocket通信+大模型对话
这个只是简单的demo,实际上生产还得考虑很多问题,本文就是为 channels 的应用开了个头,后续有新的研究成果会持续更新博客~
2024-08-14 · Django · 约 1 分钟
Django统一包装接口返回数据格式
前言 最近实在太忙了,开始了一个新的项目,为了快速形成产品,我选择了Django来实现后端,然后又拿起了之前我封装了项目脚手架「DjangoStarter」。 由于前段时间我写了不少.NetCore的后端代码,对CRUD的接口开发又有了一些新的理解,所以肯定也要把DjangoStarter改造一番,改得更加顺手~ 题外话:话说我在前端终于真正用上react,不得不说 ts + react 真好用,
2022-08-26 · Django · 约 1 分钟
Django的信号机制入门
概念 django自带一套信号机制来帮助我们在框架的不同位置之间传递信息。也就是说,当某一事件发生时,信号系统可以允许一个或多个发送者(senders)将通知或信号(signals)发送给一组接受者(receivers)。 (感觉就很像Qt的信号与槽机制) 信号系统包含以下三要素: 发送者-信号的发出方 信号-信号本身 接收者-信号的接受者 Django内置了一整套信号,下面是一些比较常用的: 在
2022-08-26 · Django · 约 1 分钟
讨论
共 0 条加载评论中…