我在Docker容器中运行Django项目,并使用Visual Studio代码作为我的IDE。在本文中,我分享了我如何调试和自动加载Django和芹菜工人。该解决方案基于debugpy
,watchdog
和django.utils.autoreload
。
在本文中:
- Example docker-compose
- 🔄🐞 Debugging a Django app running in Docker
- 🐞 Debugging a Django celery worker running in Docker (no auto reload)
- 🔄 Auto-reloading a Django celery worker running in Docker (no debug)
- 🔄🐞 Debugging a Django celery worker running in Docker with auto-reload
(用bitdowntoc生成的TOC)
示例Docker-Compose
要更好地理解帖子的其余部分,让我们假设您的docker-compose.yml
看起来与此相似:
services:
web: # Django App
build: .
command: ./manage.py runserver 0.0.0.0:80
volumes:
- .:/app # mount the code inside the container
links:
- postgres
worker: # Celery Worker
build: .
command: >-
celery -A my.package.worker worker
-l info --concurrency=6 --queues a,b
volumes:
- .:/app
links:
- postgres
postgres: # Database
image: postgres:15-alpine
ports:
- '5432'
volumes:
- .data/postgres:/var/lib/postgresql/data
environment:
POSTGRES_DB: ...
POSTGRES_USER: ...
POSTGRES_PASSWORD: ...
ð - 在Docker运行的Django应用程序调试
运行manage.py runserver
时启用了实时重新加载,但是调试呢?
使断点工作的最简单方法是在容器中安装debugpy
,并打开一个远程端口,用于调试VSCODE可以连接到。
由于docker-compose文件和Dockerfile
通常是由版本控制的,所以让我们使用docker-compose.override.yml
进行调试设置。来自docker compose的文档:
如果您在命令行上不提供
-f
标志,请撰写trawers trawers working目录及其父目录,以寻找docker-compose.yml
和docker-compose.override.yml
文件。 [...]如果两个文件都在同一目录级别上,则将两个文件组合到单个配置中。(另请参见Merge and override)。
与您的docker-compose.yml
一起创建一个docker-compose.override.yml
与以下内容:
# in docker-compose.override.yml
services:
web:
command: >-
sh -c "pip install debugpy &&
python -m debugpy --listen 0.0.0.0:3000
manage.py runserver 0.0.0.0:80"
ports:
- 3003:3000 # set 3003 to anything you want
现在,运行docker compose up
时,在普通启动日志之前,您应该在web
容器的日志中看到以下内容:
web-1 | Collecting debugpy
web-1 | Downloading debugpy-1.6.7-py2.py3-none-any.whl (4.9 MB)
web-1 | Installing collected packages: debugpy
web-1 | Successfully installed debugpy-1.6.7
在VSCODE上,创建一个新的调试配置,使用UI( debug >>> 添加配置),或者通过创建文件.vscode/launch.json
:
{
"version": "0.2.0",
"configurations": [
{
"name": "CHANGEME",
"type": "python",
"request": "attach",
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app"
}
],
"port": 3003,
"host": "127.0.0.1",
"django": true,
"justMyCode": false,
}
]
}
重要的事情:
-
pathMappings.remoteRoot
应该匹配安装在容器中的代码的文件夹 -
port
应该匹配您映射到容器端口3000
的 -
,即端口debugpy lcked lise complugpy complugpy lcep
-
justMyCode
确定了代码之外的断点(例如,在您使用的库中)工作。
使用此配置,您可以在需要时启动调试会话(或点击F5
),并且所有断点都应该工作。调试完成后,只需使用分离图标“分离”调试器。
- ¹如果您需要调试只有在启动期间发生的事情,请通过-wait-for-client
进行调试。设置时,django应用在启动调试器之前不会启动。
ð调试在Docker跑步的Django芹菜工人(无自动加载)
对于芹菜工人,适用相同的原则。只需将docker-compose.override.yml
更改为:
services:
worker:
command: >-
sh -c "pip install debugpy &&
python -m debugpy --listen 0.0.0.0:3000
/usr/local/bin/celery -A my.package.worker worker
-l info -P solo --queues a,b"
ports:
- 3003:3000
与初始芹菜命令相比,最大的区别是:
-
必须使用绝对路径(例如
/usr/local/bin/celery
)调用芹菜,因为Debugpy在工作目录中查找脚本(在我的情况下,/app
)。仅使用celery
将增加:
No such file or directory: '/app/celery'
-
将
-P solo
传递到芹菜而不是--concurrency N
简化了调试,因为只使用了一个芹菜线。
- 这不会自动重新加载您的工人 - (继续阅读ð)
ð自动加油在Docker跑步的Django芹菜工人(无调试)
只要自动加载芹菜工人,就可以使用watchdog软件包中的Awesome Utility watchmedo auto-restart
。 watchmedo
观看一组文件和/或目录,并在文件更改时自动重新启动过程。
docker-compose.override.yml
变为:
services:
worker:
command: >-
sh -c "pip install "watchdog[watchmedo]" &&
python -m watchdog.watchmedo auto-restart
-d src/ -p '*.py' --recursive
celery -A my.package.worker worker -l info -P solo --queues a,b"
ports:
- 3003:3000
watchmedo
的常见选项是:
-
-d
或--directory
选项是要观看的目录。它可以重复。 -
-p
或--patterns
选项将手表限制为匹配的文件。使用;
列出多个模式,例如*.py;*.json
-
-R
或--recursive
选项递归监视目录。
请注意,我们不需要再指定celery
的绝对路径。
â€â€debugger不起作用 - (继续读取ð)
ðð调试与自动校长在Docker跑步的Django芹菜工人
要进行现场重新加载和调试器工作,将debugpy
和watchmedo
组合在一起是不够的。我相信这与每次重新启动(感谢WatchMedo)有关,因此失去了连接和上下文。换句话说,我们需要在调试的上下文中重新启动。
django如何进行自动弹奏?查看源代码,我们可以看到runserver
命令使用koude2下hood(请参阅runserver.py::run)。很酷的是,该实用程序也可以用于运行其他过程!
这是一个简单的python文件,它使用autoreload
运行芹菜工人:
import django
# ❗ needs to be called *before* importing autoreload
django.setup()
from django.utils import autoreload
def run_celery():
# ↓ import the Celery app object from your code
from my_package.celery.app import app as celery_app
# ↓ usual celery arguments
args = "-A my.package.worker worker -l info -P solo --queues a,b"
celery_app.worker_main(args.split(" "))
print("Starting celery worker with autoreload...")
autoreload.run_with_reloader(run_celery)
不要忘记调整args
和Celery
对象导入以适合您的需求。
假设此文件保存在项目的根部,则docker-compose.override.yml
变为:
services:
worker:
command: >-
sh -c "pip install debugpy &&
python -m debugpy --listen 0.0.0.0:3000 worker.py"
ports:
- 3003:3000
有关其他实现这一目标的方法,请看一下[StackOverflow] Celery auto reload on ANY changes。