KodeKloud 100 Days of MLOps – Day 1: Setting Up a Standardized Python Environment

Step-by-step walkthrough of Day 1 task in KodeKloud 100 Days of MLOps challenge: setting up a virtual environment, installing ML dependencies, and generating requirements.txt on controlplane.

Welcome to Day 1 of the KodeKloud 100 Days of MLOps Challenge!

In today’s challenge, we lay down the foundational infrastructure for xFusionCorp Industries’ data science team. Standardizing development environments is a core MLOps best practice—it guarantees reproducibility, prevents library dependency conflicts across teams, and makes CI/CD pipelines smooth and predictable.


Task Scenario & Requirements

The data science team at xFusionCorp Industries requires a standardized Python virtual environment on the controlplane host for a new machine learning initiative.

Requirements:

  1. Working Directory: All operations must be performed under /root/code/.
  2. Virtual Environment: Create a Python virtual environment named ml-env inside /root/code/.
  3. Packages: Install core ML dependencies: numpy, pandas, scikit-learn, and matplotlib.
  4. Reproducibility: Save a complete dependency freeze as requirements.txt at /root/code/requirements.txt.

Step-by-Step Implementation Guide

Step 1: Navigate to the Project Directory

Actually the terminal in given space is kinda styled and I’m not used to that style and by the look of it working directory was correct but since i don’t want to mess something up I decided to navigate to the given directory since it won’t do any harm.

First, change your directory to the project workspace:

cd /root/code/

Step 2: Create the Virtual Environment

To create the Virtual enviornment we can use 3’s native venv module.If python3-venv is missing, run apt-get update && apt-get install -y python3-venv before running this step.

Use Python 3’s native venv module to isolate dependencies from the global system environment. We name our environment ml-env:

python3 -m venv ml-env

Why we’re creating a venv here Virtual environments encapsulate Python binaries and installed libraries, ensuring isolated package management per project.


Step 3: Activate the Virtual Environment

Activate ml-env so that all subsequent pip operations target this isolated environment rather than system Python:

source /root/code/ml-env/bin/activate

Upon successful activation, your shell prompt will update to show (ml-env).


Step 4: Install Required Machine Learning Libraries

With the environment active, install the necessary libraries (numpy, pandas, scikit-learn, matplotlib):

pip install numpy pandas scikit-learn matplotlib

Step 5: Freeze Environment Dependencies

To make the environment fully reproducible for production deployments and team members, output all exact package versions into a requirements.txt file:

pip freeze > /root/code/requirements.txt

Step 6: Verify and Deactivate

Verify that the files exist in /root/code/ and deactivate the virtual environment when done:

ls -l /root/code
cat /root/code/requirements.txt
deactivate

💻 Full Terminal Session Output

Below is the complete terminal log from executing Day 1’s task on the controlplane host:

root@controlplane ~/code ✖ cd /root/code/

root@controlplane ~/code ➜  python3 -m venv ml-env

root@controlplane ~/code ➜  source /root/code/ml-env/bin/activate

root@controlplane ~/code via 🐍 v3.12.3 (ml-env) ➜  pip install numpy pandas scikit-learn matplotlib
Collecting numpy
  Downloading numpy-2.5.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (6.6 kB)
Collecting pandas
  Downloading pandas-3.0.5-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.metadata (79 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 79.5/79.5 kB 5.8 MB/s eta 0:00:00
Collecting scikit-learn
  Downloading scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (11 kB)
Collecting matplotlib
  Downloading matplotlib-3.11.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (80 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 80.3/80.3 kB 64.7 MB/s eta 0:00:00
Collecting python-dateutil>=2.8.2 (from pandas)
  Downloading python_dateutil-2.9.0.post0-py2.py3-none-any.whl.metadata (8.4 kB)
Collecting scipy>=1.10.0 (from scikit-learn)
  Downloading scipy-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (62 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.3/62.3 kB 62.4 MB/s eta 0:00:00
Collecting joblib>=1.4.0 (from scikit-learn)
  Downloading joblib-1.5.3-py3-none-any.whl.metadata (5.5 kB)
Collecting narwhals>=2.0.1 (from scikit-learn)
  Downloading narwhals-2.24.0-py3-none-any.whl.metadata (15 kB)
Collecting threadpoolctl>=3.5.0 (from scikit-learn)
  Downloading threadpoolctl-3.6.0-py3-none-any.whl.metadata (13 kB)
Collecting contourpy>=1.0.1 (from matplotlib)
  Downloading contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (5.5 kB)
Collecting cycler>=0.10 (from matplotlib)
  Downloading cycler-0.12.1-py3-none-any.whl.metadata (3.8 kB)
Collecting fonttools>=4.28.2 (from matplotlib)
  Downloading fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (118 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 118.7/118.7 kB 81.0 MB/s eta 0:00:00
Collecting kiwisolver>=1.3.1 (from matplotlib)
  Downloading kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.metadata (5.1 kB)
Collecting packaging>=20.0 (from matplotlib)
  Downloading packaging-26.2-py3-none-any.whl.metadata (3.5 kB)
Collecting pillow>=9 (from matplotlib)
  Downloading pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (9.1 kB)
Collecting pyparsing>=3 (from matplotlib)
  Downloading pyparsing-3.3.2-py3-none-any.whl.metadata (5.8 kB)
Collecting six>=1.5 (from python-dateutil>=2.8.2->pandas)
  Downloading six-1.17.0-py2.py3-none-any.whl.metadata (1.7 kB)
Downloading numpy-2.5.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (16.7 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 16.7/16.7 MB 97.2 MB/s eta 0:00:00
Downloading pandas-3.0.5-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (11.0 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 11.0/11.0 MB 102.3 MB/s eta 0:00:00
Downloading scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (9.1 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.1/9.1 MB 106.7 MB/s eta 0:00:00
Downloading matplotlib-3.11.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (10.0 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.0/10.0 MB 103.8 MB/s eta 0:00:00
Downloading contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (362 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 362.6/362.6 kB 186.5 MB/s eta 0:00:00
Downloading cycler-0.12.1-py3-none-any.whl (8.3 kB)
Downloading fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (5.0 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.0/5.0 MB 90.8 MB/s eta 0:00:00
Downloading joblib-1.5.3-py3-none-any.whl (309 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 309.1/309.1 kB 104.5 MB/s eta 0:00:00
Downloading kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (1.5 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.5/1.5 MB 111.2 MB/s eta 0:00:00
Downloading narwhals-2.24.0-py3-none-any.whl (461 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 461.0/461.0 kB 114.5 MB/s eta 0:00:00
Downloading packaging-26.2-py3-none-any.whl (100 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.2/100.2 kB 91.3 MB/s eta 0:00:00
Downloading pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (6.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.9/6.9 MB 107.5 MB/s eta 0:00:00
Downloading pyparsing-3.3.2-py3-none-any.whl (122 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 122.8/122.8 kB 110.0 MB/s eta 0:00:00
Downloading python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 229.9/229.9 kB 124.2 MB/s eta 0:00:00
Downloading scipy-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (35.3 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 35.3/35.3 MB 88.2 MB/s eta 0:00:00
Downloading threadpoolctl-3.6.0-py3-none-any.whl (18 kB)
Downloading six-1.17.0-py2.py3-none-any.whl (11 kB)
Installing collected packages: threadpoolctl, six, pyparsing, pillow, packaging, numpy, narwhals, kiwisolver, joblib, fonttools, cycler, scipy, python-dateutil, contourpy, scikit-learn, pandas, matplotlib
Successfully installed contourpy-1.3.3 cycler-0.12.1 fonttools-4.63.0 joblib-1.5.3 kiwisolver-1.5.0 matplotlib-3.11.1 narwhals-2.24.0 numpy-2.5.1 packaging-26.2 pandas-3.0.5 pillow-12.3.0 pyparsing-3.3.2 python-dateutil-2.9.0.post0 scikit-learn-1.9.0 scipy-1.18.0 six-1.17.0 threadpoolctl-3.6.0

root@controlplane ~/code via 🐍 v3.12.3 (ml-env) ➜  pip freeze > /root/code/requirements.txt

root@controlplane ~/code via 🐍 v3.12.3 (ml-env) ➜  ls -l /root/code
total 12
-rw-r--r-- 1 root root  725 Jul 26 07:22 README.md
drwxr-xr-x 6 root root 4096 Jul 26 08:05 ml-env
-rw-r--r-- 1 root root  289 Jul 26 08:06 requirements.txt

root@controlplane ~/code via 🐍 v3.12.3 (ml-env) ➜  cat /root/code/requirements.txt
contourpy==1.3.3
cycler==0.12.1
fonttools==4.63.0
joblib==1.5.3
kiwisolver==1.5.0
matplotlib==3.11.1
narwhals==2.24.0
numpy==2.5.1
packaging==26.2
pandas==3.0.5
pillow==12.3.0
pyparsing==3.3.2
python-dateutil==2.9.0.post0
scikit-learn==1.9.0
scipy==1.18.0
six==1.17.0
threadpoolctl==3.6.0

root@controlplane ~/code via 🐍 v3.12.3 (ml-env) ➜  deactivate

root@controlplane ~/code via 🐍 v3.12.3 ➜  

What to learn here.

  • Isolation Matters: Always run python3 -m venv <env_name> to create isolated environments for separate projects, preventing package collision.
  • Lock Down Dependencies: Running pip freeze > requirements.txt captures explicit version numbers (e.g., numpy==2.5.1, pandas==3.0.5), ensuring identical environments across staging and production.
  • Transitive Dependencies: Notice how installing 4 top-level packages (numpy, pandas, scikit-learn, matplotlib) pulled in transitive dependencies like scipy, joblib, kiwisolver, and pillow. pip freeze records all of them for 100% fidelity.

Stay tuned for Day 2 of the 100 Days of MLOps challenge!