Understanding and Fixing the ModuleNotFoundError
Now that we've encountered the ModuleNotFoundError, let's understand why it happened and how to fix it.
Why Does ModuleNotFoundError Occur in Docker?
The ModuleNotFoundError occurs in Docker for several common reasons:
- Missing dependency installation: We didn't install the required Python packages in the Docker image.
- Incorrect PYTHONPATH: The Python interpreter can't find the modules in the expected locations.
- File structure issues: The application code structure doesn't match how imports are being done.
In our case, the error occurred because we didn't install the requests
package in our Docker image. Unlike our local development environment where we might have this package installed globally, Docker containers are isolated environments.
Method 1: Installing Dependencies Using pip in the Dockerfile
Let's modify our Dockerfile to install the required dependencies:
nano Dockerfile
Update the Dockerfile with the following content:
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"]
Let's build and run this updated image:
docker build -t python-app-fixed-1 .
You should see output that includes the package installation:
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
Now let's run the fixed container:
docker run python-app-fixed-1
You should see output similar to this:
Status code: 200
Content length: 1256 characters
Great! The application now runs successfully because we installed the required dependency.
Method 2: Using requirements.txt for Dependency Management
While directly installing packages works, it's better practice to use a requirements.txt file for more organized dependency management. Let's update our Dockerfile:
nano Dockerfile
Update the Dockerfile with the following content:
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"]
This approach has several advantages:
- It separates dependency management from code
- It makes it easier to update dependencies
- It follows best practices for Docker image layer caching
Let's build and run this updated image:
docker build -t python-app-fixed-2 .
You should see output similar to the previous build, but this time it's using 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
Now let's run the container:
docker run python-app-fixed-2
You should see the same successful output:
Status code: 200
Content length: 1256 characters
You've successfully fixed the ModuleNotFoundError using two different methods!