EduFlow
أساسيات AWS السحابية
⁦0%⁩

الوحدة 4 · بناء شيء حقيقي

استضافة موقع ثابت على S3 وCloudFront

17 دقراءة 4 درتّب الخطوات

الدرس في لمحة

انشر الواجهة الأمامية لـ Lantern من bucket خاص في S3 عبر Amazon CloudFront مع origin access control، وHTTPS على نطاق مخصّص، وترويسات cache مدروسة، وعمليات إبطال (invalidations).

  1. 1الصورة العامة
  2. 2خطوة بخطوة
  3. 3التكلفة والأمان في لمحة

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

أتذكر مهرجان طعام الشارع الذي أسقط Lantern؟ معظم تلك الطلبات كانت لبضعة ملفات هي نفسها: الصفحة الرئيسية، وملف أنماط، وJavaScript، وملصق واحد. الواجهة الأمامية لـ Lantern تطبيق Vite يُبنى إلى ملفات ثابتة، فلا يحتاج إلى خادم أصلًا. إنه يحتاج إلى مكان يحفظ فيه الملفات، Amazon S3، وإلى cache عالمي أمامه، Amazon CloudFront.

الصورة العامة

يطلب المتصفّح lantern.example عبر HTTPS من موقع حافة في CloudFront. إذا وُجد الملف في الـ cache يجيب موقع الحافة مباشرة. وإذا لم يوجد، يوقّع CloudFront طلبًا عبر origin access control إلى الـ bucket الخاص في S3، الذي لا يسمح إلا لذلك الـ distribution. المتصفّح CloudFront edge cache S3 bucket خاص، BPA مفعّل HTTPS cache hit miss: طلب موقَّع سياسة الـ bucket: السماح بـ s3:GetObject لهذا الـ distribution فقط
يصل الزوّار إلى موقع حافة قريب؛ ولا يبلغ الـ bucket الخاص إلا ما لم يوجد في الـ cache

معظم الطلبات cache hits، يجيب عنها موقع حافة قريب من الزائر دون لمس S3. وعند الـ miss (عدم وجود الملف في الـ cache)، يجلب CloudFront الملف من الـ bucket، موقّعًا الطلب عبر origin access control (OAC). يبقى الـ bucket خاصًّا، مع تفعيل Block Public Access، وتسمح سياسته بالقراءة من ذلك الـ distribution وحده.

خطوة بخطوة

1. ابنِ التطبيق وأنشئ bucket خاصًّا

Terminal
npm run build                      # Vite writes dist/
aws s3 mb s3://lantern-site-prod --region eu-west-1

الـ bucket الجديد خاص افتراضيًا. لا تفعّل استضافة المواقع الثابتة: يحتاج OAC إلى نقطة النهاية العادية للـ bucket، أما نقطة نهاية الموقع فلا تتحدّث إلا HTTP وتتطلّب وصولًا عامًّا.

2. ارفع الملفات بترويسات الـ cache الصحيحة

يضع Vite قيمة hash للمحتوى في أسماء ملفات الأصول (assets/index-4f8a1c.js)، فيحصل الملف المتغيّر دائمًا على اسم جديد. يمكن تخزين هذه الملفات مؤقتًا لمدة سنة. أما index.html فيحتفظ باسمه، لذا يجب أن تتحقّق المتصفّحات وCloudFront دائمًا من وجود نسخة أحدث منه:

Terminal
aws s3 sync dist/ s3://lantern-site-prod/ --delete \
  --exclude "index.html" \
  --cache-control "public,max-age=31536000,immutable"

aws s3 cp dist/index.html s3://lantern-site-prod/index.html \
  --cache-control "no-cache"

يزيل --delete من الـ bucket الملفات التي لم تعد موجودة في dist/، كي لا تتراكم الحزم القديمة.

3. احصل على شهادة في us-east-1

لتفعيل HTTPS على lantern.example، اطلب شهادة عامة من AWS Certificate Manager في us-east-1. لا يستخدم CloudFront إلا الشهادات من تلك الـ Region، أيًّا كانت الـ Region التي فيها الـ bucket الخاص بك. تحقّق منها بسجل DNS الذي يعطيك إياه ACM.

Terminal
aws acm request-certificate --region us-east-1 \
  --domain-name lantern.example --validation-method DNS

4. أنشئ الـ distribution

في console الـ CloudFront، أنشئ distribution بالإعدادات التالية:

  • Origin: نقطة النهاية العادية للـ bucket، مع origin access control (توقيع الطلبات).
  • Default root object: index.html.
  • Viewer protocol policy: إعادة توجيه HTTP إلى HTTPS.
  • Alternate domain name هو lantern.example، مع شهادة us-east-1.
  • Cache policy هي CachingOptimized، وسياسة ترويسات الاستجابة المُدارة SecurityHeadersPolicy، التي تضيف HSTS والترويسات المرتبطة به.

إذا كانت الواجهة الأمامية تستخدم مسارات من جهة العميل مثل /events/street-food، فهذه المسارات لا توجد كملفات، فيجيب S3 عبر OAC بالرمز 403. أضف استجابة خطأ مخصّصة تقدّم /index.html بالرمز 200 لأخطاء 403، أو CloudFront Function صغيرة تعيد كتابة تلك المسارات، كي تفتح الروابط العميقة المتداولة على وسائل التواصل الصفحة الصحيحة بدلًا من صفحة خطأ.

5. اسمح لذلك الـ distribution وحده بقراءة الـ bucket

site-bucket-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontServicePrincipalReadOnly",
      "Effect": "Allow",
      "Principal": { "Service": "cloudfront.amazonaws.com" },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::lantern-site-prod/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/E2QWRUHEXAMPLE"
        }
      }
    }
  ]
}

الشرط هو ما يجعل هذا آمنًا: من دونه، يستطيع أي distribution في CloudFront في أي حساب قراءة الـ bucket.

6. وجّه DNS واختبر

أنشئ سجلات alias من نوع A وAAAA لـ lantern.example تشير إلى الـ distribution (في Amazon Route 53، أو كسجل CNAME لدى مزوّد DNS آخر إن كان نطاقًا فرعيًا). ثم تحقّق من أن https://lantern.example يُحمَّل، وأن رابط الـ bucket نفسه يعيد 403 Access Denied: هذا الـ 403 يثبت أن الـ bucket خاص.

7. في كل عملية نشر

كرّر الخطوة 2، ثم أبطل الملف الوحيد الذي يحتفظ باسمه:

Terminal
aws cloudfront create-invalidation \
  --distribution-id E2QWRUHEXAMPLE --paths "/index.html"

التكلفة والأمان في لمحة

تدفع لـ S3 مقابل التخزين والطلبات، ومع وجود CloudFront في الأمام، لا يرى S3 إلا حالات الـ miss. النقل من S3 إلى CloudFront لا يُحتسب؛ ويحتسب CloudFront الطلبات والبيانات المقدَّمة للزوّار بنظام الدفع حسب الاستخدام، أو يمكنك اختيار إحدى خططه ذات السعر الثابت التي تجمع الـ CDN مع AWS WAF وDNS وأرصدة S3 بسعر شهري ثابت دون رسوم تجاوز. وفي الحالتين، تهبط ذروة المهرجان الآن على مواقع حافة بُنيت لتحمّلها.

من ناحية الأمان: bucket خاص واحد، وسياسة bucket واحدة بشرط SourceArn، وHTTPS فقط، وترويسات الأمان مفعّلة. في الدرس التالي تضيف الجزء الديناميكي: الـ API الخاص بتأكيد الحضور.

أهم النقاط

  • يبقى الـ bucket خاصًّا؛ يقرؤه CloudFront عبر origin access control، وتسمح سياسة الـ bucket لذلك الـ distribution وحده.
  • يعمل origin access control مع نقطة النهاية العادية للـ bucket، لا مع نقطة النهاية الخاصة باستضافة المواقع الثابتة في S3.
  • شهادة النطاق المخصّص في CloudFront يجب أن تكون في AWS Certificate Manager في us-east-1، مطلوبةً هناك أو مستوردةً إليها.
  • امنح ملفات الأصول ذات الأسماء المحتوية على hash عمر cache طويلًا، وامنح index.html عمرًا قصيرًا، كي تظهر عمليات النشر دون إبطال جماعي.
  • التقديم من مواقع الحافة يمتصّ ذروات الزيارات ويقلّل نقل البيانات من الـ origin.

قراءات إضافية

تحقّق من فهمك

3 أسئلة

السؤال 1 من 3

لماذا يُبقي Lantern الـ bucket الخاص بالموقع خاصًّا بدلًا من تفعيل استضافة المواقع الثابتة في S3 وجعله عامًّا؟

السؤال 2 من 3

طلبت شهادة لـ lantern.example من AWS Certificate Manager في eu-west-1، لكن CloudFront لا يعرضها. لماذا؟

السؤال 3 من 3

بعد عملية نشر، ما زال الزوّار يرون النسخة القديمة من الصفحة الرئيسية لساعات، بينما تُحمَّل ملفات JavaScript الجديدة بشكل سليم. ما السبب الأرجح؟

تدريب

رتّب الخطوات

انشر الموقع الثابت

رتّب خطوات أول عملية نشر للواجهة الأمامية لـ Lantern في الإنتاج ترتيبًا يعمل. يبقى الـ bucket خاصًّا، ولا يُقدَّم الموقع إلا عبر CloudFront على https://lantern.example. أنهِ أعمال البناء وS3 أولًا، ثم انتقل إلى الشهادة وCloudFront وDNS.

استخدم أزرار الأسهم لإعادة الترتيب.

  1. 1أضف سياسة الـ bucket التي تسمح بـ s3:GetObject لـ cloudfront.amazonaws.com مع شرط AWS:SourceArn لهذا الـ distribution.
  2. 2أنشئ الـ distribution في CloudFront مع origin access control، وindex.html ككائن جذر افتراضي، والشهادة مرفقة.
  3. 3اطلب شهادة ACM لـ lantern.example في us-east-1 وتحقّق منها عبر DNS.
  4. 4أنشئ سجلات alias من نوع A وAAAA لـ lantern.example تشير إلى الـ distribution.
  5. 5أنشئ الـ bucket الخاص lantern-site-prod في الـ Region الخاصة بـ Lantern، مع إبقاء Block Public Access مفعّلًا.
  6. 6ارفع dist/ باستخدام aws s3 sync، مع ترويسات cache طويلة للأصول ذات الـ hash وno-cache لـ index.html.
  7. 7تحقّق من أن https://lantern.example يُحمَّل وأن الرابط المباشر للـ bucket يعيد 403.
  8. 8شغّل npm run build لإنتاج المجلّد dist/.