Явное объявление и изоляция всех зависимостей приложения
Явное объявление и изоляция всех зависимостей приложения
Dependencies — второй фактор 12-Factor App. Принцип гласит:
Все зависимости приложения должны быть явно объявлены в файле зависимостей. Никаких неявных зависимостей «это уже установлено на сервере».
✅ Правильно:
# requirements.txt (Python)
flask==3.0.0
requests==2.31.0
psycopg2==2.9.9
# package.json (Node.js)
{
"dependencies": {
"express": "^4.18.0",
"pg": "^8.11.0"
}
}
# Gemfile (Ruby)
gem 'rails', '7.1.0'
gem 'pg', '1.5.4'
Представьте приложение, которое работает на машине разработчика, но падает на сервере:
# app.py
import requests # ← У разработчика установлен
import flask # ← У разработчика установлен
@app.route('/')
def index():
return 'Hello'На машине разработчика:
# Установлено глобально через pip install
pip install requests
pip install flask
python app.py # ✅ РаботаетНа сервере:
# Зависимости не установлены
python app.py # ❌ ModuleNotFoundError: No module named 'requests'Проблемы неявных зависимостей:
# requirements.txt
flask==3.0.0
requests==2.31.0
psycopg2==2.9.9
# Установка всех зависимостей одной командой
pip install -r requirements.txtПреимущества:
git clone && pip install -r requirements.txt && python app.py# requirements.txt
# Точные версии для воспроизводимости
flask==3.0.0
requests==2.31.0
gunicorn==21.2.0
psycopg2-binary==2.9.9
# Или с указанием минимальной версии
flask>=3.0.0
requests>=2.31.0
# Или диапазон версий
flask>=3.0.0,<4.0.0# pyproject.toml (современный стандарт)
[project]
name = "myapp"
version = "1.0.0"
dependencies = [
"flask>=3.0.0",
"requests>=2.31.0",
"gunicorn>=21.2.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.4.0",
"black>=23.0.0",
]// package.json
{
"name": "myapp",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.2",
"pg": "^8.11.3"
},
"devDependencies": {
"jest": "^29.7.0",
"eslint": "^8.56.0"
}
}# Установка зависимостей
npm install # Установить из package.json
npm install pkg # Добавить новую зависимость# Gemfile
source 'https://rubygems.org'
ruby '3.2.2'
gem 'rails', '7.1.0'
gem 'pg', '1.5.4'
gem 'puma', '6.4.0'
group :development, :test do
gem 'rspec-rails', '6.1.0'
gem 'rubocop', '1.60.0'
end# Установка зависимостей
bundle install// go.mod
module github.com/company/myapp
go 1.21
require (
github.com/gin-gonic/gin v1.9.1
github.com/lib/pq v1.10.9
)# Установка зависимостей
go mod download
go mod tidy// composer.json
{
"require": {
"php": ">=8.2",
"laravel/framework": "^10.0",
"doctrine/dbal": "^3.7"
},
"require-dev": {
"phpunit/phpunit": "^10.5"
}
}# Установка зависимостей
composer install# ❌ Неправильно: установка глобально
pip install flask
pip install requests
# Конфликт версий между проектами:
# Проект A требует flask==2.0.0
# Проект B требует flask==3.0.0
# ❌ Невозможно установить одновременно# Создание виртуального окружения
python -m venv venv
# Активация (Linux/macOS)
source venv/bin/activate
# Активация (Windows)
venv\Scripts\activate
# Установка зависимостей в изолированное окружение
pip install -r requirements.txt
# Проверка: пакеты установлены только в venv
which python # /path/to/project/venv/bin/python# Node.js автоматически изолирует зависимости
npm install
# Пакеты устанавливаются в локальную папку
ls node_modules/ # express, pg, ...
# Использование в коде
const express = require('express'); # Берётся из node_modules# Bundler устанавливает гемы в изолированное окружение
bundle install
# Запуск с изоляцией
bundle exec rails server# Go модули автоматически изолируют зависимости
go mod init myapp
go mod tidy
# Зависимости кэшируются в $GOPATH/pkg/mod# requirements.txt
flask==3.0.0
requests==2.31.0Преимущества:
Недостатки:
# requirements.txt
flask>=3.0.0,<4.0.0
requests>=2.31.0Преимущества:
Недостатки:
# Точные версий для production
# Диапазоны для разработки
# requirements.txt
flask==3.0.0
requests==2.31.0
# requirements-dev.txt
pytest>=7.4.0
black>=23.0.0# Production: точные версии
pip install -r requirements.txt
# Разработка: свежие инструменты
pip install -r requirements-dev.txt# requirements.txt (production)
flask==3.0.0
gunicorn==21.2.0
psycopg2==2.9.9
# requirements-dev.txt (разработка)
-r requirements.txt # Включает production-зависимости
pytest==7.4.0
black==23.12.0
flake8==7.0.0
mypy==1.8.0# Установка только production-зависимостей
pip install -r requirements.txt
# Установка всех зависимостей (включая dev)
pip install -r requirements-dev.txt{
"dependencies": {
"express": "^4.18.0"
},
"devDependencies": {
"jest": "^29.7.0",
"eslint": "^8.56.0"
}
}# Production: только dependencies
npm install --production
# Разработка: все зависимости
npm install# app.py
import requests # ← Предполагается, что установлен глобально
import flask # ← «На сервере же есть»# ❌ Нет файла зависимостей
# Разработчики устанавливают пакеты вручную
pip install requests
pip install flaskПроблема: на новом сервере приложение не запустится.
# ❌ Неправильно: попытка установить зависимости из кода
import subprocess
subprocess.run(['pip', 'install', 'requests'])
import requestsПроблема: установка зависимостей — задача системы развёртывания, а не кода.
# ❌ Неправильно: любая версия
flask
requestsПроблема: сегодня работает flask==3.0.0, завтра обновится до flask==4.0.0 с breaking changes.
# ❌ Неправильно: зависимости только в Dockerfile
FROM python:3.12
RUN pip install flask requests psycopg2
COPY app.py .
CMD ["python", "app.py"]Проблема: нет явного файла зависимостей, сложно обновлять пакеты, невозможно использовать вне Docker.
Правильно:
FROM python:3.12
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m venv venv
source venv/bin/activate
pip install -r requirements-dev.txt
- name: Run tests
run: |
source venv/bin/activate
pytestИспользуйте инструменты для автоматического обновления:
# Проверка устаревших пакетов
pip-review --local
# Интерактивное обновление
pip-review --interactive# Проверка на уязвимости (Python)
pip install safety
safety check
# Проверка на уязвимости (Node.js)
npm audit
# Проверка на уязвимости (Ruby)
bundle audit# Автоматическое обновление безопасных версий
pip install --upgrade requests # Если нет breaking changes
# Или через Dependabot:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"FROM python:3.12-slim
WORKDIR /app
# Копируем файл зависимостей
COPY requirements.txt .
# Устанавливаем зависимости (используем кэш)
RUN pip install --no-cache-dir -r requirements.txt
# Копируем код
COPY . .
CMD ["gunicorn", "app:app"]Преимущества:
# Stage 1: сборка зависимостей
FROM python:3.12 as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# Stage 2: финальный образ
FROM python:3.12-slim
WORKDIR /app
COPY /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["gunicorn", "app:app"]Задайте себе вопросы:
# На машине разработчика
pip list | grep flask
Flask 2.3.2
# На staging-сервере
pip list | grep flask
Flask 1.1.2 # ← Старая версия!
# На production-сервере
pip list | grep flask
# ← Flask не установлен, приложение падаетПроблемы:
# requirements.txt
flask==3.0.0
requests==2.31.0
gunicorn==21.2.0
psycopg2==2.9.9# Любой разработчик:
git clone repo
cd repo
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python app.py # ✅ Работает
# Любой сервер:
git pull
pip install -r requirements.txt
systemctl restart myapp # ✅ РаботаетРезультат:
Ключевой вывод: Зависимости должны быть явно объявлены, изолированы и зафиксированы. Это обеспечивает воспроизводимость, предсказуемость и лёгкое развёртывание на любом окружении.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.