vscode远程调试docker中的python服务

vscode远程调试docker中的python服务

环境:windows Docker Desktop , Vscode

阅读前,默认你对以下内容有基本了解:docker-compose.yml, Dockerfile, Vscode python 调试配置, flask 服务

原本想使用python pdb来调试,但是在容器环境中使用pdb,需要直接在容器终端中启动python程序,不方便 然后找到一个远程调试工具:debugpy,它将在容器中运行,等待vscode的连接(可配置等待模式),然后启动python程序。 配置好后就可以在vscode中调试容器中的python程序了。

远程调试flask服务

在docker 中调试flask服务时不要开启debug模式

例如:

if __name__ == '__main__':
    # 本机访问:http://127.0.0.1:5000/
    app.run(host="0.0.0.0", port=5000, debug=False)

原因是,开启debug后意味着开启了flask的重新加载机制,由于使用debugpy远程调试的某种原因,flask的重载机制触发了,这会导致Python 会创建一个新的进程来运行新的代码。但是,原来的进程还在监听 debug 调试端口,所以当新进程尝试绑定同一个端口时会报 “Address already in use” 错误。

远程调试flask服务时,Dockerfile的示例

Dockerfile

# Use the Python 3.10 image as the base image
FROM python:3.10

# Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE=1

# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1

# Set the working directory to /app
WORKDIR /app

# Copy all files in the current directory to /app
COPY . /app

# Dependencies required by the installation script
RUN pip install -r requirements.txt -i https://mirrors.cloud.tencent.com/pypi/simple

# Expose the port of the container
EXPOSE 5000
EXPOSE 5678

# run script
CMD ["python", "server.py"]

远程调试flask服务时,docker-compose.debug.yml 配置

这里使用的是docker-compose.debug.yml,是为了区别于docker-compose.yml

启动docker-compose.debug.yml的方式是:docker-compose -f docker-compose.debug.yml up

version: "3.9"

services:
  pic_server:
    build: ./
    # tty: true
    # stdin_open: true
    # image: web_dl-main_server:latest
    ports:
     - "5000:5000"
     - "5678:5678"
    restart: always
    volumes:
      - ".:/app"
    command: ["python", "-m", "debugpy", "--listen", "0.0.0.0:5678", "--wait-for-client", "server.py"]

容器启动后,会自动执行command的设置的命令

这个命令会覆盖Dockerfile中的CMD命令,例如会覆盖Dockerfile原来的命令:CMD ["python", "server.py"]

远程调试flask服务时,vscode 的调试客户端配置

.vscode\launch.json 见后文:vscode 的调试客户端配置

调试我的web-dl服务

web-dl service github address: https://github.com/qyzhizi/web_dl

docker-compose.debug.yml 配置

这里使用的是docker-compose.debug.yml,是为了区别于docker-compose.yml

启动docker-compose.debug.yml的方式是:docker-compose -f docker-compose.debug.yml up

docker-compose.debug.yml的内容是:

version: "3.9"

services:
  main_server:
    depends_on:
      - redis
    build: ./
    env_file:
      - .env
    ports:
     - "9000:9000"
     - "5678:5678"
    restart: always
    volumes:
      - ".:/app"
    command: >
      sh -c "python web_dl/cmd/celery_work > celery_work.log 2>&1 &
             python -m debugpy --listen 0.0.0.0:5678 --wait-for-client  web_dl/cmd/main"
  redis:
    image: "redis/redis-stack-server:latest"

容器启动后,会自动执行command的设置的命令

command: >
  sh -c "python web_dl/cmd/celery_work > celery_work.log 2>&1 &
          python -m debugpy --listen 0.0.0.0:5678 --wait-for-client  web_dl/cmd/main"

这个命令会覆盖Dockerfile中的CMD命令,例如会覆盖Dockerfile原来的命令:CMD ["bash", "-c", "web_dl/cmd/run.sh"]

第一个命令:python web_dl/cmd/celery_work > celery_work.log 2>&1 &,它是在后台执行一个python脚本web_dl/cmd/celery_work,最后的&表示后台执行 这里一定要后台执行,因为这个脚本不会终止。否者第二个命令就无法被终端执行,而是一直等第一个命令执行结束。

第二个命令:python -m debugpy --listen 0.0.0.0:5678 --wait-for-client web_dl/cmd/main", 它是启动docker远端调试服务的关键,python 启动debugpy, 并且监听0.0.0.0:5678, --wait-for-client表示等待客户端的连接接(docker这边启动的是服务端),客户端本文采用的vscode的python调试模块(后文再介绍相关配置)。

最后启动python 脚本web_dl/cmd/main,这个脚本是python web服务的入口点。

vscode 的调试客户端配置

安装python 扩展插件 打开vscode的调试界面,如果你之前没有创建过launch.json, 那就选择创建launch.json 文件,然后选择调试器Docker: Debug in Container,然后生成一个文件launch.json 接下来配置这个launch.json,如下:

{
    // Use IntelliSense to understand related attributes。 
    // Hover to see descriptions of existing properties。
    // For more information, please visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: remote attach",
            "type": "python",
            "request": "attach",
            "connect": {
                "host": "localhost",
                "port": 5678
            },
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}",
                    "remoteRoot": "/app"
                }
            ],
            "justMyCode": true
        }
    ]
}

其中:

"connect": {
                "host": "localhost",
                "port": 5678
            },

这个表示连接到本地的5678端口,因为调试的服务端是本地, 如果你的docker服务在其他地方,比如云端,那么这里需要填写机器的ip地址,可能还会有密码需要配置, 目前笔者还没有试过远程连接docker服务,先就不管了。 "justMyCode": true 表示只是调试你写的代码,第三方库的代码就不调试了,这里笔者也没有试过,先记录一下。

另外:

"pathMappings": [
                {
                    "localRoot": "${workspaceFolder}",
                    "remoteRoot": "/app"
                }
            ],

这个表示本地代码到docker容器的代码映射,因为在Dockerfile中,代码的根目录是/app,所以"remoteRoot": "/app" ${workspaceFolder}表示vscode的工作目录,这里是把当前工作目录与容器的/app进行映射。 之所以要做映射,是因为在调试时,可以准确找到对应的代码。

参考

https://blog.hipolabs.com/remote-debugging-with-vscode-docker-and-pico-fde11f0e5f1c https://github.com/Microsoft/ptvsd/issues/1131