watchdog.observers.Observer 在 Windows 中工作,在 Linux 上的 docker 中工作,在 Windows 上的 docker 中不起作用

问题描述

我有一个有趣的问题让我发疯。我有一个使用 watchdog.observers.Observer 的 python 程序。该程序(又名观察程序)监视文件夹并在其中出现文件时做出响应。我有一个程序(又名解析器),它会定期用文件填充监视文件夹。

  1. 当观察程序在 Windows 中运行并且解析器在 Windows 上的 docker 容器中运行时,就有了幸福。
  2. 当观察程序在 Linux 机器上的 docker 容器中运行,而解析器在 Linux 机器上的另一个 docker 容器中运行时,就会很幸福。
  3. 当观察者程序在 Windows 上的一个 docker 容器中运行,而解析器在 Windows 上的另一个 docker 容器中运行时,幸福并没有实现。解析器用文件填充文件夹,但观察者从不观察它们。

这是我的观察者代码

import os
import sys
import time
   
from watchdog.observers import Observer
from event_handler import ImagesEventHandler
from constants import ROOT_FOLDER,IMAGES_FOLDER,CWD


class ImagesWatcher:
    def __init__(self,src_path):
        self.__src_path = src_path
        print(self.__src_path)
        self.__event_handler = ImagesEventHandler()
        self.__event_observer = Observer()
        print("********** Inside ImagesWatcher --init__ method just after instantiating ImagesEventHandler and Observer **************")

    def run(self):
        print("********** Inside ImagesWatcher run method **************")
        self.start()
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            self.stop()

    def start(self):
        print("********** Inside ImagesWatcher start method **************")
        self.__schedule()
        self.__event_observer.start()

    def stop(self):
        print("********** Inside ImagesWatcher stop method **************")
        self.__event_observer.stop()
        self.__event_observer.join()

    def __schedule(self):
        print("********** Inside ImagesWatcher __schedule method **************")
        print(self.__src_path)
        self.__event_observer.schedule(
            self.__event_handler,self.__src_path,recursive=True
        )

if __name__ == "__main__":
    src_path = sys.argv[1] if len(sys.argv) > 1 else CWD
    src_path = os.path.abspath(src_path)
    watch_path = os.path.join(src_path,ROOT_FOLDER)
    watch_path = os.path.join(watch_path,IMAGES_FOLDER)
    print('watch_path: ' + watch_path)

    if not os.path.exists(watch_path):
        os.makedirs(watch_path)
        print('just created: ' + watch_path)

    ImagesWatcher(watch_path).run()

这是相关的事件处理程序代码

import os
from PIL import Image
from watchdog.events import FileSystemEventHandler
from lambda_function import lambda_handler
from time import sleep
from os.path import dirname,abspath

class ImagesEventHandler(FileSystemEventHandler):

    def __init__(self,):
        print("********** Inside event handler __init__ method **************")
    
    def on_created(self,event):
        print("********** Inside event handler on_created method **************")
        self.process(event)

    def process(self,event):
        print("********** Inside event handler process method **************")
        sleep(2)
        image = Image.open(event.src_path)
        tracking_dir=os.path.join(dirname(dirname(abspath(event.src_path))),'Tracking')
        print("********************  tracking_dir: ' + tracking_dir + ' ********************")
        lambda_handler(image,tracking_dir)

watcher 的 stop 方法永远不会被执行。执行事件处理程序的 init 方法,但不执行 on_created 和 process 方法

以下是我构建和运行 docker 容器的方法

docker build -t watcher -f docker/watcher/Dockerfile . 
docker run -d --network onprem_network -v c:\My_MR:/code/My_MR --name watcher watcher 

docker build -t parser -f docker/parser/Dockerfile . 
docker run -d --network onprem_network -v c:\My_MR:/code/My_MR --name parser parser 

我的观察者 Dockerfile:

FROM python:3.7.9
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
copY requirements.txt /requirements.txt
RUN pip install --upgrade pip -r /requirements.txt && mkdir /code 
workdir /code
copY . /code/
RUN apt update && apt-get update && apt install tesseract-ocr -y && apt-get install ffmpeg libsm6 libxext6  -y
CMD ["python","/code/watcher.py"]

我的解析器 Dockerfile:

FROM python:3.7.9
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
copY requirements.txt /requirements.txt
RUN pip install --upgrade pip -r /requirements.txt && mkdir /code
workdir /code
copY . /code/
RUN apt update && apt-get update && apt-get install ffmpeg -y
CMD ["python","/code/parser.py"]

我的需求.txt:

Pillow == 5.4.1
gql == 3.0.0a5
matplotlib == 3.0.3
numpy == 1.16.2
opencv_python == 4.4.0.44
pandas == 0.24.2
PyTesseract == 0.2.6
python_ffmpeg_video_streaming == 0.1.14
watchdog == 2.0.2
requests
tesseract

任何帮助将不胜感激。

解决方法

看门狗用于监控 linux 文件系统事件的底层 API 称为 inotify。 Docker for Windows WSL 2 backend documentation 注释:

如果原始文件存储在 Linux 文件系统中,Linux 容器只会接收文件更改事件(“inotify 事件”)。

您正在挂载的目录 c:\My_MR 驻留在 Windows 文件系统上,因此观察者容器内的 inotify 不起作用。

相反,您可以从在您的 WSL 2 默认发行版中使用 linux 文件系统路径运行 docker,例如 ~/my_mr:

docker run -d --network onprem_network -v ~/my_mr:/code/My_MR --name watcher watcher 
docker run -d --network onprem_network -v ~/my_mr:/code/My_MR --name parser parser 

当 WSL 2 发行版使用 \\wsl$\ 网络路径运行时,可以从 Windows 访问该目录,即 \\wsl$\<Distro name>\home\<username>\my_mr(更多信息 here)。因此,我相信 docker run 也可以在 Windows 中使用带有 \\wsl$\-v 路径。