← Назад к вопросам

Зачем нужны переменные окружения в Python?

1.2 Junior🔥 241 комментариев
#DevOps и инфраструктура#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Переменные окружения в Python

Переменные окружения — это критически важный механизм для конфигурирования приложений. Давайте разберём зачем они нужны и как их использовать.

Что такое переменные окружения

Переменные окружения — это пары ключ-значение, которые передаются программе от операционной системы. Они видны всем процессам, запущенным в этой системе.

# В shell (bash, zsh)
export DATABASE_URL="postgresql://user:pass@localhost/db"
export API_KEY="secret-key-12345"
export DEBUG="True"
export LOG_LEVEL="INFO"

# Проверить
echo $DATABASE_URL  # postgresql://user:pass@localhost/db
# В Python
import os

database_url = os.environ.get('DATABASE_URL')
api_key = os.environ.get('API_KEY')
debug = os.environ.get('DEBUG', 'False')  # Default value

print(database_url)  # postgresql://user:pass@localhost/db

Проблема без переменных окружения

Плохой подход: Хардкод в коде

# settings.py (НИКОГДА ТАК НЕ ДЕЛАЙ!)
DATABASE_URL = 'postgresql://user:pass@localhost/mydb'
API_KEY = 'sk-1234567890abcdef'
SECRET_KEY = 'my-super-secret-key'
DEBUG = True
ALLOWED_HOSTS = ['192.168.1.100']

# Проблемы:
# 1. Секреты в git репозитории (утечка данных!)
# 2. Разная конфигурация для разработки/staging/production
# 3. Нельзя менять конфиг без перегрузки кода
# 4. Все разработчики и вся команда видит настоящие пароли

Хороший подход: Переменные окружения

# settings.py
import os

# Конфигурация автоматически подбирается из окружения
DATABASE_URL = os.environ.get('DATABASE_URL')
API_KEY = os.environ.get('API_KEY')
SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost').split(',')

# Код не меняется, конфиг меняется через переменные окружения

Использование в реальных приложениях

1. Flask/Django приложение

# app.py
from flask import Flask
import os

app = Flask(__name__)

# Конфигурация из окружения
app.config['DATABASE_URL'] = os.environ.get('DATABASE_URL')
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
app.config['DEBUG'] = os.environ.get('DEBUG', 'False') == 'True'
app.config['API_KEY'] = os.environ.get('API_KEY')

@app.route('/api/data')
def get_data():
    api_key = app.config['API_KEY']
    # Используем api_key для запросов
    return {'status': 'ok'}

if __name__ == '__main__':
    app.run(debug=app.config['DEBUG'])
# Запуск с разными конфигурациями

# Разработка
DATABASE_URL="sqlite:///dev.db" DEBUG="True" python app.py

# Production
DATABASE_URL="postgresql://prod_user:prod_pass@prod.db.com/prod_db" \
DEBUG="False" \
API_KEY="secret-prod-key" \
python app.py

2. Docker с переменными окружения

# Dockerfile
FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .

# Переменные окружения с дефолтными значениями
ENV DATABASE_URL="sqlite:///default.db"
ENV DEBUG="False"
ENV LOG_LEVEL="INFO"

CMD ["python", "app.py"]
# Запуск контейнера с переменными
docker run \
  -e DATABASE_URL="postgresql://localhost/mydb" \
  -e API_KEY="secret-key" \
  -e DEBUG="True" \
  my-python-app

# Или из файла
docker run --env-file .env my-python-app

3. .env файл (для локальной разработки)

Либиотека python-dotenv загружает переменные из файла:

# .env (не коммитить в git!)
DATABASE_URL=sqlite:///dev.db
API_KEY=local-dev-key-12345
DEBUG=True
LOG_LEVEL=DEBUG
REDIS_URL=redis://localhost:6379/0
SMTP_PASSWORD=my-secret-password
# settings.py
from dotenv import load_dotenv
import os
from pathlib import Path

# Загрузить из .env
env_path = Path(__file__).parent.parent / '.env'
load_dotenv(dotenv_path=env_path)

# Теперь все значения из .env доступны
DATABASE_URL = os.environ.get('DATABASE_URL')
API_KEY = os.environ.get('API_KEY')
DEBUG = os.environ.get('DEBUG') == 'True'
# Установить библиотеку
pip install python-dotenv

# .gitignore
.env  # Никогда не коммитим!
.env.local

Реальный пример: Production-grade приложение

# config.py
from functools import lru_cache
from pydantic import BaseSettings, Field

class Settings(BaseSettings):
    """Конфигурация приложения из переменных окружения"""
    
    # Базовые
    APP_NAME: str = Field(default="MyApp", env="APP_NAME")
    DEBUG: bool = Field(default=False, env="DEBUG")
    ENV: str = Field(default="development", env="ENV")  # dev/staging/prod
    
    # База данных
    DATABASE_URL: str = Field(default="sqlite:///db.sqlite3", env="DATABASE_URL")
    DATABASE_POOL_SIZE: int = Field(default=5, env="DATABASE_POOL_SIZE")
    
    # API ключи
    API_KEY: str = Field(env="API_KEY")  # Обязательный
    SECRET_KEY: str = Field(env="SECRET_KEY")  # Обязательный
    
    # Redis
    REDIS_URL: str = Field(default="redis://localhost:6379/0", env="REDIS_URL")
    
    # CORS
    ALLOWED_ORIGINS: list = Field(
        default=["http://localhost:3000"],
        env="ALLOWED_ORIGINS"
    )
    
    # Логирование
    LOG_LEVEL: str = Field(default="INFO", env="LOG_LEVEL")
    
    class Config:
        env_file = ".env"  # Загружает из .env если существует
        case_sensitive = True

# Кэшируем конфигурацию
@lru_cache()
def get_settings():
    return Settings()

# app.py
from fastapi import FastAPI
from config import get_settings
import logging

settings = get_settings()

# Конфигурируем приложение
app = FastAPI(
    title=settings.APP_NAME,
    debug=settings.DEBUG,
)

# Конфигурируем логирование
logging.basicConfig(level=settings.LOG_LEVEL)
logger = logging.getLogger(__name__)

# Используем конфигурацию
logger.info(f"Starting {settings.APP_NAME} in {settings.ENV} mode")

@app.get("/health")
async def health_check():
    return {"status": "ok", "env": settings.ENV}

Типичные переменные окружения

# Базовые
DEBUG=True|False
ENV=development|staging|production
PORT=8000
HOST=0.0.0.0

# База данных
DATABASE_URL=postgresql://user:pass@host:5432/db
DATABASE_POOL_SIZE=10
DATABASE_MAX_OVERFLOW=20

# Кэширование
REDIS_URL=redis://localhost:6379/0
CACHE_TTL=3600

# API ключи и секреты
SECRET_KEY=random-secret-key-here
API_KEY=api-key-from-external-service
JWT_SECRET=jwt-secret-key
OAUTH_CLIENT_ID=...
OAUTH_CLIENT_SECRET=...

# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=...
SMTP_PASSWORD=...
FROM_EMAIL=noreply@example.com

# Логирование
LOG_LEVEL=INFO|DEBUG|WARNING|ERROR
LOG_FILE=/var/log/app.log

# CORS и безопасность
ALLOWED_HOSTS=localhost,127.0.0.1,example.com
ALLOWED_ORIGINS=http://localhost:3000,https://example.com
CSRF_TRUSTED_ORIGINS=https://example.com

# Внешние сервисы
SENTRY_DSN=https://...
SLACK_WEBHOOK_URL=https://hooks.slack.com/...
TWILIO_ACCOUNT_SID=...
TWILIO_AUTH_TOKEN=...

# Cloud
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
GCP_PROJECT_ID=...
AZURE_STORAGE_ACCOUNT=...

Безопасность переменных окружения

✅ Делай так

# Проверяй обязательные переменные
required_vars = ['SECRET_KEY', 'API_KEY', 'DATABASE_URL']
for var in required_vars:
    if not os.environ.get(var):
        raise ValueError(f"Required environment variable not set: {var}")

# Используй .env для разработки
# НО не коммитай в git
# Создай .env.example с пустыми значениями

# Используй системные конфигурации (systemd, upstart) для production
# Или cloud secret management (AWS Secrets Manager, Google Secret Manager)

❌ Не делай так

# ❌ Хардкодирование
SECRET_KEY = "my-secret-key-123"

# ❌ Печать в логах
logger.info(f"API_KEY: {os.environ.get('API_KEY')}")

# ❌ Коммит .env файла
# Никогда не коммитай в git содержащие секреты файлы

# ❌ Использование слабых дефолтов
SECRET_KEY = os.environ.get('SECRET_KEY', 'default-secret')  # Плохо!
SECRET_KEY = os.environ.get('SECRET_KEY')  # Хорошо! (исключение если не задана)

Cloud Secret Management

# Для production используй специализированные сервисы

# AWS Secrets Manager
import boto3

def get_secret(secret_name):
    client = boto3.client('secretsmanager')
    response = client.get_secret_value(SecretId=secret_name)
    return response['SecretString']

api_key = get_secret('prod/api-key')

# Google Secret Manager
from google.cloud import secretmanager

def access_secret_version(secret_id, version_id="latest"):
    client = secretmanager.SecretManagerServiceClient()
    name = f"projects/my-project/secrets/{secret_id}/versions/{version_id}"
    response = client.access_secret_version(request={"name": name})
    return response.payload.data.decode("UTF-8")

api_key = access_secret_version('api-key')

Вывод

Переменные окружения нужны потому что:

  1. Безопасность — секреты не в коде и не в git
  2. Гибкость — один код работает в dev/staging/prod
  3. Простота — легко менять конфиг без перегрузки
  4. Стандарт — все приложения используют env variables
  5. DevOps — Docker, Kubernetes, CI/CD работают с env vars

Золотое правило:

  • Вся конфигурация из переменных окружения
  • Никаких хардкодированных секретов в коде
  • .env файл для разработки, но не в git
  • Cloud secret management для production
Зачем нужны переменные окружения в Python? | PrepBro