← Назад к вопросам
Зачем нужны переменные окружения в 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')
Вывод
Переменные окружения нужны потому что:
- ✅ Безопасность — секреты не в коде и не в git
- ✅ Гибкость — один код работает в dev/staging/prod
- ✅ Простота — легко менять конфиг без перегрузки
- ✅ Стандарт — все приложения используют env variables
- ✅ DevOps — Docker, Kubernetes, CI/CD работают с env vars
Золотое правило:
- Вся конфигурация из переменных окружения
- Никаких хардкодированных секретов в коде
- .env файл для разработки, но не в git
- Cloud secret management для production