Bài 14: Virtual Environments Deep Dive

Mục Tiêu Bài Học

Sau khi hoàn thành bài này, bạn sẽ:

  • ✅ Hiểu venv vs virtualenv vs conda
  • ✅ Sử dụng pyenv cho multiple Python versions
  • ✅ Làm việc với pipenv và Pipfile
  • ✅ Sử dụng poetry dependency management
  • ✅ Áp dụng Docker containers

Tại Sao Cần Virtual Environments?

Virtual environments giải quyết các vấn đề:

1. Dependency Conflicts

# Project A cần Django 3.2pip install Django==3.2 # Project B cần Django 4.0pip install Django==4.0  # ❌ Conflict!

2. System Python Pollution

# Cài packages vào system Pythonsudo pip install requests  # ❌ Không nên làm!# Có thể làm hỏng system tools

3. Reproducible Environments

# Dễ dàng chia sẻ dependenciespip freeze > requirements.txtpip install -r requirements.txt

1. venv - Built-in Virtual Environments

venv là module có sẵn từ Python 3.3+.

Tạo và Kích Hoạt venv

# Tạo virtual environmentpython -m venv myenv # Kích hoạt (macOS/Linux)source myenv/bin/activate # Kích hoạt (Windows)myenv\Scripts\activate # Kiểm tra Python locationwhich python  # myenv/bin/python # Thoátdeactivate

Cài Đặt Packages

# Sau khi activatepip install requests pandas numpy # List installed packagespip list # Freeze dependenciespip freeze > requirements.txt # Install từ requirementspip install -r requirements.txt

Tự Động Hóa venv

setup_env.sh

#!/bin/bash # Tạo venv nếu chưa cóif [ ! -d "venv" ]; then    python3 -m venv venv    echo "✅ Created virtual environment"fi # Kích hoạtsource venv/bin/activate # Cài đặt dependenciespip install -r requirements.txt echo "✅ Environment ready!"

2. virtualenv - Advanced Virtual Environments

virtualenv mạnh hơn venv với nhiều tính năng.

Cài Đặt và Sử Dụng

# Cài virtualenvpip install virtualenv # Tạo virtualenvvirtualenv myenv # Tạo với Python version cụ thểvirtualenv -p python3.9 myenv # Không copy packages từ systemvirtualenv --no-site-packages myenv # Tạo với system site packagesvirtualenv --system-site-packages myenv

virtualenvwrapper

Tool quản lý nhiều virtualenvs:

# Cài đặtpip install virtualenvwrapper # Thêm vào ~/.bashrc hoặc ~/.zshrcexport WORKON_HOME=$HOME/.virtualenvssource /usr/local/bin/virtualenvwrapper.sh # Tạo virtualenvmkvirtualenv myproject # List tất cả virtualenvslsvirtualenv # Chuyển đổi virtualenvworkon myproject # Xóa virtualenvrmvirtualenv myproject # Copy virtualenvcpvirtualenv old_env new_env

3. pyenv - Python Version Management

pyenv quản lý nhiều Python versions.

Cài Đặt pyenv

# macOS (với Homebrew)brew install pyenv # Linuxcurl https://pyenv.run | bash # Thêm vào ~/.bashrc hoặc ~/.zshrcexport PATH="$HOME/.pyenv/bin:$PATH"eval "$(pyenv init -)"eval "$(pyenv virtualenv-init -)"

Sử Dụng pyenv

# List Python versions có sẵnpyenv install --list # Cài Python versionpyenv install 3.9.7pyenv install 3.10.4 # List installed versionspyenv versions # Set global Python versionpyenv global 3.10.4 # Set local Python version (cho project)pyenv local 3.9.7 # Set shell Python version (session hiện tại)pyenv shell 3.10.4

pyenv-virtualenv

# Tạo virtualenv với pyenvpyenv virtualenv 3.9.7 myproject-env # Kích hoạtpyenv activate myproject-env # Deactivatepyenv deactivate # Xóapyenv uninstall myproject-env # Auto-activate khi cd vào projectecho "myproject-env" > .python-version

4. pipenv - Modern Dependency Management

pipenv kết hợp virtualenv và dependency management.

Cài Đặt và Sử Dụng

# Cài pipenvpip install pipenv # Tạo project với pipenvcd myprojectpipenv install  # Tạo Pipfile và virtualenv # Cài packagespipenv install requestspipenv install pytest --dev  # Dev dependency # Kích hoạt shellpipenv shell # Run command trong virtualenvpipenv run python script.pypipenv run pytest # Lock dependenciespipenv lock # Install từ Pipfile.lockpipenv install --deploy

Pipfile Example

Pipfile

[[source]]url = "https://pypi.org/simple"verify_ssl = truename = "pypi" [packages]django = ">=4.0,<5.0"requests = "*"psycopg2-binary = "==2.9.3" [dev-packages]pytest = "*"black = "*"mypy = "*" [requires]python_version = "3.10" [scripts]dev = "python manage.py runserver"test = "pytest tests/"format = "black ."

pipenv Best Practices

# Chỉ commit Pipfile và Pipfile.lockgit add Pipfile Pipfile.lockgit commit -m "Add dependencies" # Trên productionpipenv install --deploy --ignore-pipfile # Update tất cả dependenciespipenv update # Check security vulnerabilitiespipenv check # Graph dependenciespipenv graph # Remove unused packagespipenv clean

5. Poetry - Advanced Dependency Management

poetry là tool hiện đại nhất cho dependency management.

Cài Đặt Poetry

# macOS/Linuxcurl -sSL https://install.python-poetry.org | python3 - # Windows PowerShell(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python - # Kiểm trapoetry --version

Tạo Project với Poetry

# Tạo project mớipoetry new myproject # Structuremyproject/├── pyproject.toml├── README.md├── myproject/│   └── __init__.py└── tests/    └── __init__.py # Hoặc init trong existing projectcd existing_projectpoetry init

Quản Lý Dependencies với Poetry

# Thêm dependencypoetry add requestspoetry add pytest --group dev # Xóa dependencypoetry remove requests # Update dependenciespoetry update # Install tất cả dependenciespoetry install # Install không có dev dependenciespoetry install --only main # Show dependenciespoetry showpoetry show --tree

pyproject.toml Example

pyproject.toml

[tool.poetry]name = "myproject"version = "0.1.0"description = "My awesome project"authors = ["Your Name <[email protected]>"]readme = "README.md"packages = [{include = "myproject"}] [tool.poetry.dependencies]python = "^3.9"django = "^4.0"requests = "^2.28"psycopg2-binary = "^2.9" [tool.poetry.group.dev.dependencies]pytest = "^7.0"black = "^22.0"mypy = "^0.950"flake8 = "^4.0" [tool.poetry.scripts]dev = "myproject.cli:dev"test = "pytest" [build-system]requires = ["poetry-core"]build-backend = "poetry.core.masonry.api"

Poetry Virtual Environments

# Poetry tự động tạo virtualenvpoetry install  # Tạo venv và install deps # Run command trong venvpoetry run python script.pypoetry run pytest # Activate shellpoetry shell # Kiểm tra venv locationpoetry env info # List tất cả venvspoetry env list # Xóa venvpoetry env remove python3.9 # Sử dụng system Pythonpoetry config virtualenvs.create false

5 Ứng Dụng Thực Tế

1. Multi-Project Development Environment

project_manager.py

import subprocessimport osfrom pathlib import Path class ProjectManager:    """Quản lý nhiều projects với different Python versions"""        def __init__(self, workspace: str):        self.workspace = Path(workspace)        def create_project(self, name: str, python_version: str = "3.10"):        """Tạo project với Poetry và pyenv"""        project_path = self.workspace / name        project_path.mkdir(exist_ok=True)                os.chdir(project_path)                # Set Python version với pyenv        subprocess.run(["pyenv", "local", python_version])                # Tạo project với Poetry        subprocess.run(["poetry", "init", "--no-interaction"])                print(f"✅ Created {name} with Python {python_version}")        def list_projects(self):        """List tất cả projects và Python versions"""        for project in self.workspace.iterdir():            if project.is_dir():                version_file = project / ".python-version"                version = "unknown"                if version_file.exists():                    version = version_file.read_text().strip()                                print(f"📦 {project.name}: Python {version}")        def activate_project(self, name: str):        """Generate activation script"""        project_path = self.workspace / name                script = f"""#!/bin/bashcd {project_path}pyenv activatepoetry shell"""                script_path = project_path / "activate.sh"        script_path.write_text(script)        script_path.chmod(0o755)                print(f"✅ Run: {script_path}") # Sử dụngmanager = ProjectManager("/Users/dev/projects")manager.create_project("api-service", "3.10")manager.create_project("ml-pipeline", "3.9")manager.list_projects()

2. CI/CD Environment Manager

ci_env.py

import yamlfrom typing import Dict, List class CIEnvironment:    """Quản lý environments cho CI/CD"""        def __init__(self, project_path: str):        self.project_path = project_path        def generate_github_actions(self, python_versions: List[str]):        """Generate GitHub Actions workflow"""        workflow = {            "name": "Tests",            "on": ["push", "pull_request"],            "jobs": {                "test": {                    "runs-on": "ubuntu-latest",                    "strategy": {                        "matrix": {                            "python-version": python_versions                        }                    },                    "steps": [                        {                            "uses": "actions/checkout@v3"                        },                        {                            "name": "Set up Python",                            "uses": "actions/setup-python@v4",                            "with": {                                "python-version": "${{ matrix.python-version }}"                            }                        },                        {                            "name": "Install Poetry",                            "run": "pip install poetry"                        },                        {                            "name": "Install dependencies",                            "run": "poetry install"                        },                        {                            "name": "Run tests",                            "run": "poetry run pytest"                        }                    ]                }            }        }                path = f"{self.project_path}/.github/workflows/test.yml"        with open(path, "w") as f:            yaml.dump(workflow, f, default_flow_style=False)                print(f"✅ Generated GitHub Actions workflow")        def generate_dockerfile(self, python_version: str):        """Generate Dockerfile với Poetry"""        dockerfile = f"""FROM python:{python_version}-slim WORKDIR /app # Install PoetryRUN pip install poetry # Copy dependency filesCOPY pyproject.toml poetry.lock ./ # Install dependenciesRUN poetry config virtualenvs.create false \\    && poetry install --only main --no-interaction # Copy applicationCOPY . . CMD ["poetry", "run", "python", "main.py"]"""                path = f"{self.project_path}/Dockerfile"        with open(path, "w") as f:            f.write(dockerfile)                print(f"✅ Generated Dockerfile") # Sử dụngci = CIEnvironment("./myproject")ci.generate_github_actions(["3.9", "3.10", "3.11"])ci.generate_dockerfile("3.10")

3. Dependency Security Scanner

security_checker.py

import subprocessimport jsonfrom typing import List, Dict class DependencyScanner:    """Scan dependencies cho security vulnerabilities"""        def __init__(self, project_path: str):        self.project_path = project_path        def check_pipenv(self) -> List[Dict]:        """Check Pipenv dependencies"""        result = subprocess.run(            ["pipenv", "check", "--json"],            cwd=self.project_path,            capture_output=True,            text=True        )                if result.returncode != 0:            data = json.loads(result.stdout)            return data.get("vulnerabilities", [])                return []        def check_poetry(self) -> List[Dict]:        """Check Poetry dependencies với safety"""        # Install safety        subprocess.run(["pip", "install", "safety"])                # Export requirements        subprocess.run(            ["poetry", "export", "-f", "requirements.txt",              "-o", "requirements.txt"],            cwd=self.project_path        )                # Check với safety        result = subprocess.run(            ["safety", "check", "--json",              "-r", "requirements.txt"],            cwd=self.project_path,            capture_output=True,            text=True        )                if result.returncode != 0:            return json.loads(result.stdout)                return []        def generate_report(self):        """Generate security report"""        vulnerabilities = self.check_poetry()                if not vulnerabilities:            print("✅ No vulnerabilities found!")            return                print(f"⚠️  Found {len(vulnerabilities)} vulnerabilities:")                for vuln in vulnerabilities:            print(f"""Package: {vuln['package']}Installed: {vuln['installed_version']}Affected: {vuln['vulnerable_spec']}Description: {vuln['advisory']}Severity: {vuln['severity']}Fix: Upgrade to {vuln['analyzed_version']}""") # Sử dụngscanner = DependencyScanner("./myproject")scanner.generate_report()

4. Environment Synchronization Tool

env_sync.py

import subprocessimport jsonfrom pathlib import Pathfrom typing import Dict, Set class EnvironmentSync:    """Đồng bộ environments giữa các máy"""        def __init__(self, project_path: str):        self.project_path = Path(project_path)        def export_environment(self) -> Dict:        """Export environment configuration"""        config = {}                # Python version từ pyenv        version_file = self.project_path / ".python-version"        if version_file.exists():            config["python_version"] = version_file.read_text().strip()                # Dependencies từ Poetry        result = subprocess.run(            ["poetry", "export", "-f", "requirements.txt"],            cwd=self.project_path,            capture_output=True,            text=True        )        config["dependencies"] = result.stdout                # Environment variables        env_file = self.project_path / ".env"        if env_file.exists():            config["env_vars"] = env_file.read_text()                # Save config        config_file = self.project_path / "env_config.json"        config_file.write_text(json.dumps(config, indent=2))                print("✅ Exported environment configuration")        return config        def import_environment(self, config_path: str):        """Import và setup environment"""        config = json.loads(Path(config_path).read_text())                # Setup Python version        if "python_version" in config:            subprocess.run([                "pyenv", "install", "-s", config["python_version"]            ])            subprocess.run([                "pyenv", "local", config["python_version"]            ], cwd=self.project_path)                # Setup Poetry        subprocess.run(["poetry", "install"], cwd=self.project_path)                # Restore environment variables        if "env_vars" in config:            env_file = self.project_path / ".env"            env_file.write_text(config["env_vars"])                print("✅ Imported environment configuration")        def compare_environments(self, other_project: str) -> Dict:        """So sánh dependencies giữa 2 projects"""        def get_packages(path: Path) -> Set[str]:            result = subprocess.run(                ["poetry", "show"],                cwd=path,                capture_output=True,                text=True            )            return set(line.split()[0] for line in result.stdout.splitlines())                current = get_packages(self.project_path)        other = get_packages(Path(other_project))                return {            "only_in_current": current - other,            "only_in_other": other - current,            "common": current & other        } # Sử dụngsync = EnvironmentSync("./project-a")config = sync.export_environment() # Trên máy khácsync2 = EnvironmentSync("./project-a")sync2.import_environment("env_config.json") # So sánhdiff = sync.compare_environments("./project-b")print(f"Common packages: {len(diff['common'])}")

5. Docker Development Environment

docker_env.py

from typing import List, Dict class DockerEnvironment:    """Quản lý Docker development environments"""        def __init__(self, project_name: str):        self.project_name = project_name        def generate_dockerfile_multistage(self, python_version: str):        """Generate multi-stage Dockerfile"""        dockerfile = f"""# Stage 1: BuilderFROM python:{python_version} as builder WORKDIR /app # Install PoetryRUN pip install poetry # Copy dependency filesCOPY pyproject.toml poetry.lock ./ # Install dependenciesRUN poetry export -f requirements.txt -o requirements.txt \\    && pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt # Stage 2: RuntimeFROM python:{python_version}-slim WORKDIR /app # Copy wheels từ builderCOPY --from=builder /app/wheels /wheelsCOPY --from=builder /app/requirements.txt . # Install dependenciesRUN pip install --no-cache /wheels/* # Copy applicationCOPY . . # Run as non-root userRUN useradd -m appuser && chown -R appuser:appuser /appUSER appuser CMD ["python", "main.py"]"""                with open("Dockerfile", "w") as f:            f.write(dockerfile)                print("✅ Generated multi-stage Dockerfile")        def generate_docker_compose(self, services: List[Dict]):        """Generate docker-compose.yml"""        compose = {            "version": "3.8",            "services": {}        }                for service in services:            compose["services"][service["name"]] = {                "build": ".",                "volumes": [                    f"./{service['name']}:/app"                ],                "environment": service.get("env", {}),                "ports": service.get("ports", []),                "depends_on": service.get("depends_on", [])            }                # Add development service        compose["services"]["dev"] = {            "build": ".",            "volumes": [".:/app"],            "command": "poetry run python -m debugpy --listen 0.0.0.0:5678 main.py",            "ports": ["5678:5678"],            "stdin_open": True,            "tty": True        }                import yaml        with open("docker-compose.yml", "w") as f:            yaml.dump(compose, f, default_flow_style=False)                print("✅ Generated docker-compose.yml") # Sử dụngdocker_env = DockerEnvironment("myproject")docker_env.generate_dockerfile_multistage("3.10")docker_env.generate_docker_compose([    {        "name": "app",        "ports": ["8000:8000"],        "env": {"DEBUG": "1"}    }])

Best Practices

1. Project Structure

myproject/├── .python-version      # pyenv version├── pyproject.toml       # Poetry config├── poetry.lock          # Locked dependencies├── .env.example         # Environment variables template├── .gitignore           # Git ignore (include venv/, .env)├── README.md            # Setup instructions├── src/│   └── myproject/│       └── __init__.py└── tests/    └── test_*.py

2. Dependencies Management

# ✅ Pin important versionsDjango==4.0.4 # ✅ Allow patch updatesrequests~=2.28.0 # ✅ Allow minor updates (Poetry)django = "^4.0" # ❌ Avoid wildcardpackage = "*"

3. Environment Variables

# .env.example - Commit to gitDATABASE_URL=postgresql://localhost/mydbSECRET_KEY=change-me-in-productionDEBUG=True # .env - Don't commit!DATABASE_URL=postgresql://user:pass@localhost/mydbSECRET_KEY=actual-secret-keyDEBUG=False

4. Documentation

README.md

## Setup Development Environment ### Prerequisites- Python 3.10+- Poetry ### Installation1. Clone repository2. Install dependencies:   \`\`\`bash   poetry install   \`\`\`3. Copy environment variables:   \`\`\`bash   cp .env.example .env   \`\`\`4. Run application:   \`\`\`bash   poetry run python main.py   \`\`\`

5. Tool Selection Guide

Tool Use Case
venv Simple projects, learning
virtualenv More features than venv
pyenv Multiple Python versions
pipenv Simple dependency management
poetry Modern projects, publishing packages
conda Data science, scientific computing
Docker Production, microservices

Bài Tập Thực Hành

Bài 1: Setup Multi-Version Environment

Tạo 3 projects với Python 3.9, 3.10, 3.11 sử dụng pyenv và Poetry.

Bài 2: Migrate từ requirements.txt sang Poetry

Migrate một existing project từ requirements.txt sang Poetry với pyproject.toml.

Bài 3: CI/CD Pipeline

Tạo GitHub Actions workflow test code trên Python 3.9, 3.10, 3.11.

Bài 4: Docker Development Environment

Tạo Dockerfile và docker-compose.yml cho development với hot-reload.

Bài 5: Dependency Audit

Scan một project cho security vulnerabilities và update dependencies.

Tóm Tắt

Trong bài này chúng ta đã học:

  1. venv - Built-in virtual environments
  2. virtualenv - Advanced virtual environments với nhiều features
  3. pyenv - Quản lý multiple Python versions
  4. pipenv - Modern dependency management với Pipfile
  5. Poetry - Advanced dependency management và packaging
  6. Docker - Container-based development environments
  7. Best Practices - Project structure, dependencies, CI/CD

Virtual environments là foundation của Python development, giúp isolate dependencies và maintain reproducible environments.


Bài tiếp theo: Bài 15: Package Structure & Distribution - Học cách structure và distribute Python packages! 📦