Python Packaging & Dependency Management – Vì sao code chạy trên máy bạn nhưng lại lỗi trên Production?
Một trong những câu nói quen thuộc nhất của giới DevOps:
Phần lớn các sự cố kiểu này không đến từ logic của ứng dụng mà đến từ môi trường thực thi (Environment) và Dependency Management. Trong Python, việc quản lý package, phiên bản thư viện và đóng gói ứng dụng là kỹ năng bắt buộc đối với Developer, DevOps và Automation Engineer. Môi trường thực thi quan trọng không kém source code
Một dự án Python hiếm khi chỉ sử dụng thư viện chuẩn (Standard Library). Phần lớn ứng dụng là sự kết hợp giữa:
Mỗi khi import một thư viện, bạn phải đảm bảo:
Nếu Production sử dụng phiên bản thư viện mới hơn hoặc cũ hơn môi trường Development, application có thể gặp lỗi do API của thư viện đã thay đổi.
Ví dụ:
import requests
Code có thể chạy tốt với:
requests==2.22.0
Nhưng phát sinh lỗi với:
requests==3.x
do API đã thay đổi. Hậu quả của việc quản lý dependency không tốt
Các dependency bị thiếu, lỗi thời hoặc không tương thích có thể gây ra:
Đó là lý do Python khuyến nghị sử dụng:
Package Manager phổ biến nhất: pip
Ngày nay, hầu hết việc cài đặt package Python được tự động hóa thông qua:
pip install requests
Kiểm tra package:
pip show requests
Ví dụ:
$ pip show requests
Name: requests
Version: 2.22.0
Summary: Python HTTP for Humans.
Requires: chardet, urllib3, idna, certifi
PyPI – Kho package lớn nhất của Python
Python Package Index (PyPI) là nơi lưu trữ:
Khác với Linux Repository thường chỉ cung cấp một phiên bản package, PyPI lưu giữ nhiều phiên bản khác nhau, giúp các ứng dụng cũ vẫn có thể hoạt động ổn định. Wheel (.whl) – Bí mật giúp cài package nhanh hơn
Python có khả năng tích hợp với:
Việc build các thư viện này khá phức tạp. Vì vậy Python tạo ra định dạng:
.whl
(Wheel Package)
Wheel chứa các thành phần đã được biên dịch sẵn (Precompiled Binary), giúp:
Tạo Python Package với setuptools
Mỗi project Python có thể được đóng gói thông qua:
from setuptools import setup, find_packages
setup(
name='my_package',
version='1.0.0',
packages=find_packages(),
)
Một số tham số quan trọng:
install_requires
python_requires
include_package_data
exclude_package_data
dependency_links
Các tham số này giúp kiểm soát:
Build Package
Trước tiên cập nhật công cụ:
python3 -m pip install --user --upgrade setuptools wheel
Build package:
python3 setup.py sdist bdist_wheel
Kết quả:
dist/
my_package-0.1.0-py3-none-any.whl
my_package-0.1.0.tar.gz
Trong đó:
Dependency Control – Kỹ năng sống còn của DevOps
Mặc định:
pip install package
sẽ cài phiên bản mới nhất.
Điều này có thể phá vỡ:
Python giải quyết vấn đề này bằng:
pip freeze > requirements.txt
Ví dụ:
certifi==2019.9.11
chardet==3.0.4
idna==2.8
requests==2.22.0
urllib3==1.25.6
Sau đó cài lại:
pip install -r requirements.txt
Version Pinning linh hoạt
Requirements file hỗ trợ:
requests>=2.22.0,<3.0
urllib3>=1.25.1
Điều này cho phép:
Góc nhìn DevSecOps
Trong môi trường CI/CD hiện đại, quản lý dependency không còn là công việc của Developer riêng lẻ mà là một phần của Software Supply Chain Security.
Best Practice hiện nay:
Bởi vì trong rất nhiều sự cố Production, nguyên nhân không phải là code sai mà đơn giản chỉ là:
Một trong những câu nói quen thuộc nhất của giới DevOps:
"It works on my machine."
Phần lớn các sự cố kiểu này không đến từ logic của ứng dụng mà đến từ môi trường thực thi (Environment) và Dependency Management. Trong Python, việc quản lý package, phiên bản thư viện và đóng gói ứng dụng là kỹ năng bắt buộc đối với Developer, DevOps và Automation Engineer. Môi trường thực thi quan trọng không kém source code
Một dự án Python hiếm khi chỉ sử dụng thư viện chuẩn (Standard Library). Phần lớn ứng dụng là sự kết hợp giữa:
- Source code tự phát triển
- Các thư viện (Libraries/Modules)
- Các ứng dụng hoặc dịch vụ bên ngoài
- Resource files
Mỗi khi import một thư viện, bạn phải đảm bảo:
- Thư viện đã được cài đặt
- Đúng phiên bản yêu cầu
- Tương thích với application hiện tại
Nếu Production sử dụng phiên bản thư viện mới hơn hoặc cũ hơn môi trường Development, application có thể gặp lỗi do API của thư viện đã thay đổi.
Ví dụ:
import requests
Code có thể chạy tốt với:
requests==2.22.0
Nhưng phát sinh lỗi với:
requests==3.x
do API đã thay đổi. Hậu quả của việc quản lý dependency không tốt
Các dependency bị thiếu, lỗi thời hoặc không tương thích có thể gây ra:
- Installation failure
- Unexpected Exception
- Application Crash
- Performance degradation
- Production outage
Đó là lý do Python khuyến nghị sử dụng:
- Virtual Environment
- Requirements File
- Version Pinning
Package Manager phổ biến nhất: pip
Ngày nay, hầu hết việc cài đặt package Python được tự động hóa thông qua:
pip install requests
Kiểm tra package:
pip show requests
Ví dụ:
$ pip show requests
Name: requests
Version: 2.22.0
Summary: Python HTTP for Humans.
Requires: chardet, urllib3, idna, certifi
PyPI – Kho package lớn nhất của Python
Python Package Index (PyPI) là nơi lưu trữ:
- Hàng trăm nghìn package
- Nhiều phiên bản lịch sử
- Hỗ trợ giải quyết dependency conflict
Khác với Linux Repository thường chỉ cung cấp một phiên bản package, PyPI lưu giữ nhiều phiên bản khác nhau, giúp các ứng dụng cũ vẫn có thể hoạt động ổn định. Wheel (.whl) – Bí mật giúp cài package nhanh hơn
Python có khả năng tích hợp với:
- C
- C++
- Fortran
- Rust
- Các ngôn ngữ khác
Việc build các thư viện này khá phức tạp. Vì vậy Python tạo ra định dạng:
.whl
(Wheel Package)
Wheel chứa các thành phần đã được biên dịch sẵn (Precompiled Binary), giúp:
- Giảm thời gian cài đặt
- Không cần compiler trên máy đích
- Giảm rủi ro build lỗi
Tạo Python Package với setuptools
Mỗi project Python có thể được đóng gói thông qua:
from setuptools import setup, find_packages
setup(
name='my_package',
version='1.0.0',
packages=find_packages(),
)
Một số tham số quan trọng:
install_requires
python_requires
include_package_data
exclude_package_data
dependency_links
Các tham số này giúp kiểm soát:
- Dependency cần cài
- Phiên bản Python được hỗ trợ
- Resource cần đóng gói
- Vị trí lấy package phụ thuộc
Build Package
Trước tiên cập nhật công cụ:
python3 -m pip install --user --upgrade setuptools wheel
Build package:
python3 setup.py sdist bdist_wheel
Kết quả:
dist/
my_package-0.1.0-py3-none-any.whl
my_package-0.1.0.tar.gz
Trong đó:
- .tar.gz → Source Distribution
- .whl → Binary Distribution (Wheel)
Dependency Control – Kỹ năng sống còn của DevOps
Mặc định:
pip install package
sẽ cài phiên bản mới nhất.
Điều này có thể phá vỡ:
- Project hiện tại
- CI/CD Pipeline
- Các ứng dụng Python khác trên cùng hệ thống
Python giải quyết vấn đề này bằng:
pip freeze > requirements.txt
Ví dụ:
certifi==2019.9.11
chardet==3.0.4
idna==2.8
requests==2.22.0
urllib3==1.25.6
Sau đó cài lại:
pip install -r requirements.txt
Version Pinning linh hoạt
Requirements file hỗ trợ:
requests>=2.22.0,<3.0
urllib3>=1.25.1
Điều này cho phép:
- Update bản vá bảo mật
- Vẫn đảm bảo tương thích ứng dụng
- Giảm dependency conflict
Góc nhìn DevSecOps
Trong môi trường CI/CD hiện đại, quản lý dependency không còn là công việc của Developer riêng lẻ mà là một phần của Software Supply Chain Security.
Best Practice hiện nay:
- Sử dụng Virtual Environment
- Pin phiên bản package
- Lưu requirements.txt trong Git
- Build bằng wheel khi có thể
- Scan dependency bằng:
- pip-audit
- Safety
- Snyk
- Dependabot
- Trivy
Bởi vì trong rất nhiều sự cố Production, nguyên nhân không phải là code sai mà đơn giản chỉ là:
"Máy Production đang chạy một phiên bản thư viện khác với máy Development."