如何自动重新加载和调试Django和在Docker中运行的芹菜工人(VS代码)
#编程 #生产率 #python #todayilearned

我在Docker容器中运行Django项目,并使用Visual Studio代码作为我的IDE。在本文中,我分享了我如何调试和自动加载Django和芹菜工人。该解决方案基于debugpywatchdogdjango.utils.autoreload


在本文中:

(用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.ymldocker-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-restartwatchmedo观看一组文件和/或目录,并在文件更改时自动重新启动过程。

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芹菜工人

要进行现场重新加载和调试器工作,将debugpywatchmedo组合在一起是不够的。我相信这与每次重新启动(感谢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)

不要忘记调整argsCelery对象导入以适合您的需求。

假设此文件保存在项目的根部,则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