البيئات والإعدادات والأسرار
درس 2 من 3
2البيئات والإعدادات والأسرار
الصورة الكبيرةرابط قاعدة البيانات في جهازك ليس رابطها في الإنتاج. لو كتبته في الكود ستعدّل الكود لكل بيئة، وستنشر كلمة المرور مع أول push. الحل: الكود يقرأ الإعدادات من البيئة التي يعمل فيها.
ماذا تشاهد في الرسم: بناء واحد يُنشر في كل البيئات. الفرق كله في المتغيرات التي تأتي من الخارج.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
مثل جهاز تكييف واحد يُركّب في بيوت مختلفة: الجهاز نفسه، لكن كل بيت يضبط درجة الحرارة على جهاز التحكم. لا تفتح الجهاز لتغيّر الحرارة. والمفتاح السري للبيت لا تلصقه على الجهاز.
مبدأ Twelve-Factor: الإعدادات في متغيرات البيئة، لا في الكود. محليًا ملف .env (في .gitignore) مع .env.example بلا قيم حقيقية في المستودع. اقرأ الإعدادات في مكان واحد (كائن Settings)، حوّل الأنواع، وتحقق من الإلزامي عند البدء (fail fast) بدل الانهيار عند أول طلب. Pydantic Settings يفعل هذا تلقائيًا. الأسرار في الإنتاج من مدير أسرار (أسرار GitHub Actions، AWS Secrets Manager، Vault، أسرار المنصة)، تُدوّر دوريًا، ولا تُطبع في السجلات. لو تسرّب سر: ألغِه فورًا، فحذفه من التاريخ لا يكفي.
قبل أن تبدأ
🐳Docker
الدرس السابق.
⌨️الطرفية
تعرف export وecho $VAR.
الفكرة الجوهرية
1إعدادات في مكان واحد
نقرأ كل الإعدادات مرة واحدة، نحوّل أنواعها، ونفشل بوضوح إن نقص الإلزامي. هنا نمرّر قاموسًا بدل البيئة الحقيقية لنرى الحالتين:
from dataclasses import dataclass
@dataclass(frozen=True)
class Settings:
database_url: str
debug: bool
port: int
REQUIRED = ["DATABASE_URL"]
def load_settings(env):
missing = [name for name in REQUIRED if not env.get(name)]
if missing:
raise SystemExit(f"missing settings: {', '.join(missing)}")
return Settings(
database_url=env["DATABASE_URL"],
debug=env.get("DEBUG", "false").lower() == "true",
port=int(env.get("PORT", "8000")),
)
dev = {"DATABASE_URL": "postgresql://localhost/cafe", "DEBUG": "true"}
print(load_settings(dev))
try:
load_settings({"PORT": "9000"})
except SystemExit as e:
print("startup refused ->", e)os.environ، أو استخدم pydantic-settings الذي يقرأ البيئة و.env ويتحقق من الأنواع.2ملفات .env
.env لجهازك فقط وفي .gitignore. و.env.example في المستودع يوثّق الأسماء بقيم وهمية، فيعرف الزميل الجديد ما يحتاجه:
# .env.example (committed) DATABASE_URL=postgresql://cafe:change-me@localhost:5432/cafe DEBUG=false PAYMENT_API_KEY= # .gitignore .env
.env للمستودع ثم حذفه في commit لاحق. السر ما زال في التاريخ، وبوتات تمسح GitHub تلتقطه خلال دقائق. ألغِ المفتاح وأصدر غيره.3الأسرار في الإنتاج
منصات النشر وأدوات CI تخزّن الأسرار مشفرة وتحقنها كمتغيرات بيئة وقت التشغيل. لا تطبعها في السجلات، ولا ترسلها في رسائل الخطأ، وامنح كل خدمة أقل الصلاحيات التي تحتاجها.
مثال محلول
المسألة: التطبيق يعمل في الإنتاج ساعة ثم ينهار عند أول طلب دفع: KeyError: PAYMENT_API_KEY. التحليل: المتغير يُقرأ داخل دالة الدفع فقط، فلم يلاحظ أحد نقصه عند النشر. الحل: قراءة كل الإعدادات في Settings عند البدء. الآن النشر نفسه يفشل برسالة «missing settings: PAYMENT_API_KEY» قبل أن يصل أي مستخدم.
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| المستودع خاص فالأسرار فيه آمنة. | كل من يصل للمستودع (والأدوات المربوطة به) يرى السر، والخاص قد يصبح عامًا. |
| ملف إعدادات لكل بيئة داخل الكود فكرة جيدة. | يخلط الأسرار بالكود. الإعدادات من البيئة، والكود واحد. |
اختبر فهمك
أين تضع رابط قاعدة بيانات الإنتاج؟
الأسرار خارج المستودع وتُحقن وقت التشغيل.
رفعت مفتاح API بالخطأ. أول خطوة؟
المفتاح قد يكون نُسخ فعلًا، فالإلغاء هو الحماية.
لماذا نتحقق من الإعدادات عند البدء؟
الفشل المبكر أرخص وأوضح.
بطاقات مراجعة سريعة
تحقق من فهمك
ما الفرق بين .env و.env.example؟
الأول قيم حقيقية خارج المستودع، والثاني أسماء بقيم وهمية داخله.
كيف تمرّر الإعدادات لحاوية؟
بـ -e أو --env-file في docker run، أو environment/env_file في Compose.
ما الخطر في طباعة الإعدادات كاملة عند البدء؟
تظهر الأسرار في السجلات التي يقرؤها كثيرون.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
توثيق رسمي2
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.