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:
- ✅ venv - Built-in virtual environments
- ✅ virtualenv - Advanced virtual environments với nhiều features
- ✅ pyenv - Quản lý multiple Python versions
- ✅ pipenv - Modern dependency management với Pipfile
- ✅ Poetry - Advanced dependency management và packaging
- ✅ Docker - Container-based development environments
- ✅ 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! 📦