Entendiendo y Solucionando el ModuleNotFoundError
Ahora que nos hemos encontrado con el ModuleNotFoundError, entendamos por qué ocurrió y cómo solucionarlo.
¿Por qué ocurre el ModuleNotFoundError en Docker?
El ModuleNotFoundError ocurre en Docker por varias razones comunes:
- Instalación de dependencias faltante: No instalamos los paquetes Python requeridos en la imagen Docker.
- PYTHONPATH incorrecto: El intérprete de Python no puede encontrar los módulos en las ubicaciones esperadas.
- Problemas de estructura de archivos: La estructura del código de la aplicación no coincide con la forma en que se están haciendo las importaciones.
En nuestro caso, el error ocurrió porque no instalamos el paquete requests en nuestra imagen Docker. A diferencia de nuestro entorno de desarrollo local donde podríamos tener este paquete instalado globalmente, los contenedores Docker son entornos aislados.
Método 1: Instalación de Dependencias Usando pip en el Dockerfile
Modifiquemos nuestro Dockerfile para instalar las dependencias requeridas:
nano Dockerfile
Actualice el Dockerfile con el siguiente contenido:
FROM python:3.9-slim
WORKDIR /app
COPY app.py .
## Fix Method 1: Directly install the required package
RUN pip install requests==2.28.1
CMD ["python", "app.py"]
Construyamos y ejecutemos esta imagen actualizada:
docker build -t python-app-fixed-1 .
Debería ver una salida que incluye la instalación del paquete:
Sending build context to Docker daemon 3.072kB
Step 1/5 : FROM python:3.9-slim
---> 3a4bac80b3ea
Step 2/5 : WORKDIR /app
---> Using cache
---> a8a4f574dbf5
Step 3/5 : COPY app.py .
---> Using cache
---> 7d5ae315f84b
Step 4/5 : RUN pip install requests==2.28.1
---> Running in 5a6d7e8f9b0c
Collecting requests==2.28.1
Downloading requests-2.28.1-py3-none-any.whl (62 kB)
Collecting charset-normalizer<3,>=2
Downloading charset_normalizer-2.1.1-py3-none-any.whl (39 kB)
Collecting certifi>=2017.4.17
Downloading certifi-2022.9.24-py3-none-any.whl (161 kB)
Collecting idna<4,>=2.5
Downloading idna-3.4-py3-none-any.whl (61 kB)
Collecting urllib3<1.27,>=1.21.1
Downloading urllib3-1.26.12-py2.py3-none-any.whl (140 kB)
Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests
Successfully installed certifi-2022.9.24 charset-normalizer-2.1.1 idna-3.4 requests-2.28.1 urllib3-1.26.12
---> 2b3c4d5e6f7g
Removing intermediate container 5a6d7e8f9b0c
Step 5/5 : CMD ["python", "app.py"]
---> Running in 8h9i0j1k2l3m
---> 3n4o5p6q7r8s
Removing intermediate container 8h9i0j1k2l3m
Successfully built 3n4o5p6q7r8s
Successfully tagged python-app-fixed-1:latest
Ahora ejecutemos el contenedor corregido:
docker run python-app-fixed-1
Debería ver una salida similar a esta:
Status code: 200
Content length: 1256 characters
¡Genial! La aplicación ahora se ejecuta con éxito porque instalamos la dependencia requerida.
Método 2: Usando requirements.txt para la Gestión de Dependencias
Si bien la instalación directa de paquetes funciona, es mejor práctica usar un archivo requirements.txt para una gestión de dependencias más organizada. Actualicemos nuestro Dockerfile:
nano Dockerfile
Actualice el Dockerfile con el siguiente contenido:
FROM python:3.9-slim
WORKDIR /app
## Copy requirements first to leverage Docker cache
COPY requirements.txt .
## Fix Method 2: Use requirements.txt
RUN pip install -r requirements.txt
## Copy the rest of the application
COPY app.py .
CMD ["python", "app.py"]
Este enfoque tiene varias ventajas:
- Separa la gestión de dependencias del código
- Facilita la actualización de dependencias
- Sigue las mejores prácticas para el almacenamiento en caché de capas de imágenes de Docker
Construyamos y ejecutemos esta imagen actualizada:
docker build -t python-app-fixed-2 .
Debería ver una salida similar a la compilación anterior, pero esta vez está usando requirements.txt:
Sending build context to Docker daemon 4.096kB
Step 1/5 : FROM python:3.9-slim
---> 3a4bac80b3ea
Step 2/5 : WORKDIR /app
---> Using cache
---> a8a4f574dbf5
Step 3/5 : COPY requirements.txt .
---> Using cache
---> b2c3d4e5f6g7
Step 4/5 : RUN pip install -r requirements.txt
---> Running in h8i9j0k1l2m3
Collecting requests==2.28.1
Using cached requests-2.28.1-py3-none-any.whl (62 kB)
Collecting charset-normalizer<3,>=2
Using cached charset_normalizer-2.1.1-py3-none-any.whl (39 kB)
Collecting idna<4,>=2.5
Using cached idna-3.4-py3-none-any.whl (61 kB)
Collecting certifi>=2017.4.17
Using cached certifi-2022.9.24-py3-none-any.whl (161 kB)
Collecting urllib3<1.27,>=1.21.1
Using cached urllib3-1.26.12-py2.py3-none-any.whl (140 kB)
Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests
Successfully installed certifi-2022.9.24 charset-normalizer-2.1.1 idna-3.4 requests-2.28.1 urllib3-1.26.12
---> n4o5p6q7r8s9
Removing intermediate container h8i9j0k1l2m3
Step 5/5 : COPY app.py .
---> t0u1v2w3x4y5
Step 6/6 : CMD ["python", "app.py"]
---> Running in z5a6b7c8d9e0
---> f1g2h3i4j5k6
Removing intermediate container z5a6b7c8d9e0
Successfully built f1g2h3i4j5k6
Successfully tagged python-app-fixed-2:latest
Ahora ejecutemos el contenedor:
docker run python-app-fixed-2
Debería ver la misma salida exitosa:
Status code: 200
Content length: 1256 characters
¡Ha solucionado con éxito el ModuleNotFoundError utilizando dos métodos diferentes!