پرش به محتوا
DevOps و پایداری

استقرار خودکار سایت با CI/CD؛ از push تا سرور بدون دست کاری

۱۳ دقیقه مطالعه

اگه هر انتشار سایتت دستی انجام می شه، هر بار یه شرط بندی کوچیکه. توی این راهنما می بینی استقرار خودکار از کجا شروع می شه و پنج ایستگاه مسیر push تا سرور چیه.

پنج ایستگاه استقرار خودکار از push تا بررسی سلامت، به شکل مسیری پیوسته که به یک سرور می رسد

استقرار خودکار سایت یعنی هر تغییری که وارد شاخه اصلی Git می شه، بدون دخالت دست ساخته بشه، آزمایش بشه و روی سرور بره. کوچک ترین نسخه این جریان پنج ایستگاه داره: push، build، تست، ارسال به سرور و بررسی سلامت. چون هر بار همین مراحل با همین ترتیب اجرا می شن، جای خطاهای دستی مثل جا موندن یه فایل یا اجرای یه دستور اشتباه خالی می شه.

اگه سایتت برات درآمد داره، هر انتشار دستی یه شرط بندی کوچیکه. بیشتر وقت ها می بری، ولی هزینه یه باخت، مثلاً دکمه پرداخت خراب در ساعت اوج خرید، از زمانی که برای ساختن این جریان می ذاری خیلی بیشتره. این راهنما قدم به قدم می گه از کجا شروع کنی و کجا زیاده روی نکنی.

استقرار خودکار سایت از کجا شروع می شه؟ از فهرست کار دستی فعلی

فرض کن یه فروشگاه اینترنتی داری و شب قبل از کمپین یلدا، ساعت ۱۱ شب، قراره آپدیت جدید بره روی سایت. (این یه سناریوی فرضیه، اسم هیچ مشتری ای در کار نیست.) نسخه جدید رو با FTP روی هاست می ریزی. یه فایل تنظیمات روی سرور جا می مونه، یا نسخه قدیمی یه فایل روی نسخه جدید می شینه. سایت بالاست، ولی یه جای سبد خرید درست کار نمی کنه. تا صبح تیم دنبال اینه که کدوم فایل کجا بوده.

تقصیر آدم ها نیست. کار تکراری و حساس رو سپردیم به حافظه آدمی که ساعت ۱۱ شب خسته است. قبل از انتخاب هر ابزاری، مسیر دستی فعلیت رو روی کاغذ بیار و این چهار سوال رو جواب بده:

  • چه کسی، چه فایل هایی رو از کجا به کجا می بره؟
  • روی سرور چه دستورهایی می زنه؟ مثلاً پاک کردن کش، ری استارت سرویس یا اجرای تغییرات پایگاه داده.
  • چه چیزهایی رو فقط «از حفظ» می دونه و هیچ جا ننوشته؟
  • بعد از انتشار چی رو چک می کنه تا مطمئن بشه سایت سالمه؟

هر خط این فهرست یکی از ورودی های pipeline آینده است. خط هایی که فقط توی ذهن یه نفرن، بزرگ ترین ریسک ان. اگه اون آدم مرخصی باشه یا بره، اون مرحله هم باهاش می ره.

تشبیهش ساده است. انتشار دستی مثل آشپزی بدون دستور پخته؛ یه روز عالی درمیاد و یه روز شور. CI/CD همون دستور پخت نوشته شده است که هر آشپز، با هر حال و هوایی، طبقش کار می کنه.

مسیر ساده از push تا سرور: پنج ایستگاه

نمای کلی اینه: یه نفر تغییر رو به شاخه اصلی push می کنه، ابزار CI خودش بیدار می شه و pipeline رو راه می ندازه. اول نسخه رو می سازه (build)، بعد تست می کنه، بعد به سرور می فرسته و آخر سر چک می کنه که سایت جواب می ده. اگه هر ایستگاه خطا بده، جریان همون جا می ایسته و چیزی به سرور نمی رسه.

دو اصطلاح رو جدا کنیم. CI یعنی هر تغییر خودکار ساخته و بررسی بشه. CD یعنی نسخه ای که بررسی ها رو رد کرد، خودکار یا با یه تایید به سرور هم برسه. همین، بحث تعریفی کافیه.

برای شروع، یه خط راست بهتر از یه شبکه پیچیده است. هر شاخه اضافه توی pipeline یعنی یه جای دیگه برای خراب شدن که باید نگه داری بشه، و تیم کوچیک وقتش رو نداره. اول یه مسیر خطی بساز که کار کنه، بعد اگه لازم شد پیچیده اش کن.

یه پیشنهاد دو سطحی هم دارم. تغییرات شاخه اصلی خودکار برن روی محیط آزمایشی (staging). از اونجا، رفتن به محیط اصلی با تایید یه نفر انجام بشه. اگه سایتت ساده است و ماهی یکی دو بار تغییر می کنه، شاید staging رو لازم نداشته باشی. ولی برای فروشگاهی که پرداخت داره، همین یه سطح اضافه جلوی خیلی از ماجراهای شبانه رو می گیره.

نمودار پنج ایستگاه استقرار خودکار از مخزن Git تا سرور، با توقف جریان هنگام خطا

ایستگاه اول و دوم: مخزن منظم و build قابل تکرار

قواعد حداقلی مخزن

شاخه اصلی باید همیشه «قابل انتشار» باشه. یعنی هر لحظه که یکی بخواد اون شاخه رو منتشر کنه، نباید نصفه کاره باشه. تغییرات رو توی شاخه های جدا انجام بده و با merge وارد شاخه اصلی کن. دلیلش اینه که pipeline فقط شاخه اصلی رو منتشر می کنه، و اگه کار نیمه تموم اونجا بره، خودکار روی سایت می شینه.

فایل قفل وابستگی ها (مثل package-lock.json یا composer.lock) هم باید توی مخزن باشه. بدون اون، build امروز و build سه ماه بعد ممکنه نسخه های متفاوتی از کتابخونه ها بگیرن و یه سایت که دیروز سالم بود امروز بی دلیل خراب بشه.

رمزها و تنظیمات

رمز پایگاه داده، کلید API و هر اطلاعات محرمانه دیگه نباید داخل کد باشه، حتی توی مخزن خصوصی. از بخش متغیرهای محرمانه (secrets) خود ابزار CI استفاده کن. تنظیمات هر محیط هم از کد جدا باشه: آدرس پایگاه داده staging با محیط اصلی فرق داره و نباید با یه اشتباه توی کد جابه جا بشه.

یه نکته که زیاد می بینیم: فایل .env به مخزن می ره چون «مخزن که خصوصیه». ولی مخزن رو ممکنه فردا به یه برون سپار یا توسعه دهنده جدید بدی، و رمزها هم باهاش می رن.

ساخت یک بارمصرف

build باید توی یه محیط تمیز و با اسکریپت مشخص انجام بشه، نه روی لپ تاپ یه نفر. جمله «روی سیستم من کار می کرد» دقیقاً از همین جا درمیاد. خروجی build (به اسمش artifact می گن) یه بار ساخته می شه و همون نسخه به سرور می ره. اگه روی سرور دوباره build کنی، نسخه ای رو تست نکردی که اجرا می شه.

یه تبصره هست: ما خودمون، به خاطر محدودیت هایی که پایین تر می گم، ایمیج ها رو روی سیستم توسعه می سازیم. اگه تو هم همین کار رو می کنی، مهم اینه که ساخت با یه اسکریپت ثابت انجام بشه و به دست و تنظیمات اون سیستم وابسته نباشه.

ایستگاه سوم: تست های کم ولی کارآمد

لازم نیست از روز اول پوشش تست کامل داشته باشی. این سه چیز رو بسنج و شروع کن:

  1. build بدون خطا تموم می شه.
  2. بررسی سینتکس یا lint پاس می شه.
  3. چند مسیر حیاتی جواب می دن: صفحه اصلی، ورود، فرم تماس یا مرحله پرداخت.

سومی رو تست دودی (smoke test) می گن. اسمش از این اومده که دستگاه رو روشن می کنی ببینی دود می کنه یا نه. یه اسکریپت کوچیکه که چند آدرس مهم رو صدا می زنه و کد پاسخ رو چک می کنه. برای تیم کوچیک بیشترین بازده رو داره، چون با کمترین زحمت جلوی «سایت بالا نمی آد» رو می گیره.

یه قانون ساده برای تست ها: اگه تستی ناپایداره و گاهی بی دلیل رد می شه، یا درستش کن یا موقتاً از مسیر اصلی بردارش. تستی که کسی بهش اعتماد نداره، تیم رو عادت می ده به رنگ قرمز بی توجه باشه، و روزی که یه قرمز واقعی بیاد هم نادیده گرفته می شه.

اگه سایتت درگاه پرداخت داره، روی محیط آزمایشی تراکنش واقعی نزن. برای انتخاب و تنظیم درگاه، درگاه پرداخت شاپرکی برای سایت؛ تفاوت ها و نکته های انتخاب رو ببین و محیط آزمایشی رو از همون اول از محیط اصلی جدا نگه دار.

ایستگاه چهارم: رسوندن نسخه به سرور بدون دست زدن به فایل ها

حالا نوبت اینه که خروجی ساخته شده برسه به سرور. ساده ترین راه برای سایت های معمولی rsync از طریق SSH است. ولی یه نکته مهمه: فایل ها رو مستقیم روی پوشه زنده نریز. اگه وسط انتقال یه فایل نصفه برسه یا اتصال قطع بشه، کاربر یه سایت نیمه نو و نیمه کهنه می بینه، مخصوصاً وقتی اینترنت بین الملل و مسیرها نوسان دارن.

راه بهتر اینه که هر نسخه توی یه پوشه جدا (releases) بره و فقط یه اشاره گر به اسم current عوض بشه. یه اسکریپت نمونه:

#!/usr/bin/env bash
set -euo pipefail

APP=/var/www/mysite
RELEASE=$(date +%Y%m%d%H%M%S)

# upload the build output into a new release folder
rsync -az ./dist/ deploy@SERVER_IP:$APP/releases/$RELEASE/

# switch the live pointer, then reload the web server config
ssh deploy@SERVER_IP "ln -sfn $APP/releases/$RELEASE $APP/current.tmp \
  && mv -Tf $APP/current.tmp $APP/current \
  && sudo systemctl reload nginx"

چند نکته درباره این اسکریپت:

  • نشانی سرور و مسیرها نمونه است؛ با وب سرور و ساختار خودت جایگزینش کن.
  • جابه جا کردن اشاره گر با mv یک لحظه است، پس کاربر هیچ وقت نسخه نصفه نمی بینه.
  • کاربر deploy فقط با کلید SSH وارد بشه و دسترسی اش محدود به همین پوشه و همین چند دستور باشه، نه root.

خط آخر، یعنی بارگذاری دوباره تنظیمات وب سرور، تصادفی نیست. یه بار بعد از یه انتشار، فایل های صوتی مقاله هامون خطای ۴۰۴ می دادن، چون تنظیمات تازه وب سرور بارگذاری نشده بود. از اون به بعد، reload جزو مراحل ثابت هر انتشار شد. دستورش بسته به وب سرورت فرق می کنه؛ اگه هنوز بین وب سرورها مردد هستی، لایت اسپید یا آپاچی؛ کدام وب سرور برای سایت وردپرسی مناسب تر است؟ کمکت می کنه. اگه PHP با کش opcode داری، پاک کردن یا reload اون هم باید جزو همین مرحله باشه.

درباره پایگاه داده هم صادق باشم: تغییر ساختار جداول بزرگ ترین نقطه خطر انتشار خودکاره. قبل از اجرای هر تغییر، یه پشتیبان بگیر. اگه می شه، تغییرها رو جوری بنویس که نسخه قبلی کد هم با ساختار جدید کار کنه. اون وقت برگشتن به نسخه قبل بدون دردسره.

ایستگاه پنجم: سایت بالاست؟ بررسی سلامت و راه برگشت

اجرای موفق اسکریپت یعنی فقط فایل ها رسیدن. سالم بودن سایت یه چیز دیگه است. بعد از سوییچ، pipeline باید چند آدرس مهم رو صدا بزنه و اگه جواب نگرفت، خودش اعلام خطا کنه. ساده ترین شکلش یه آدرس /health است که پایگاه داده و سرویس های اصلی رو چک می کنه:

# health check after the switch
curl --fail --silent --max-time 10 https://example.ir/health > /dev/null

# rollback: point back to the previous release
ssh deploy@SERVER_IP "ln -sfn $APP/releases/PREVIOUS $APP/current.tmp \
  && mv -Tf $APP/current.tmp $APP/current \
  && sudo systemctl reload nginx"

PREVIOUS اسم پوشه نسخه قبلی است. چون هر نسخه توی پوشه خودش مونده، برگشتن یعنی فقط عوض کردن اشاره گر، و چند ثانیه بیشتر طول نمی کشه. چند نسخه آخر رو نگه دار و قدیمی ها رو پاک کن تا دیسک سرور پر نشه.

بررسی سلامت فقط «کد ۲۰۰» نیست. یه اشتباه رایج اینه که تنظیم staging، مثل noindex برای موتورهای جستجو، همراه انتشار به محیط اصلی می ره و کم کم صفحه ها از نتایج گوگل می افتن. یه چک ساده برای robots و canonical جلوی این اتفاق رو می گیره. فهرست کامل اش رو توی چک لیست سئو فنی سایت؛ از sitemap تا canonical آوردیم.

زمان انتشار هم مهمه. حتی با pipeline خوب، درست قبل از ساعت های پرتردد سایتت، که برای خیلی از فروشگاه ها عصر تا شبه، یا دقیقاً قبل از رفتن تیم، انتشار نده. با خودکار بودن، می تونی تغییرهای کوچیک رو وسط صبح و با آرامش منتشر کنی، و دیگه لازم نیست منتظر «نیمه شب» بمونی.

ساختار پوشه های نسخه و اشاره گر نسخه فعال با امکان برگشت به نسخه قبلی

توی ایران چه چیزهایی رو جدا حساب کنیم؟

بعضی مشکل ها مال شرایط خود ما هستن و توی آموزش های خارجی پیدا نمی شن.

دسترسی به سرویس ها. روی سرور داخل ایران، گرفتن ایمیج از Docker Hub و بعضی بسته ها به خاطر محدودیت IP ایران ممکن نیست. برای همین ما ایمیج ها رو روی سیستم توسعه می سازیم و با SSH به سرور منتقل می کنیم. اگه از Docker استفاده می کنی، قبل از طراحی pipeline همین رو روی سرور خودت امتحان کن؛ بعد از ساخت pipeline فهمیدنش گرون تموم می شه.

ابزار CI. بعضی سرویس های خارجی از ایران محدود یا ناپایدارن و شرایطشون هم عوض می شه. قبل از انتخاب، دسترسی و پایداری اش رو خودت بررسی کن. یه گزینه معمول، ابزار خودمیزبان روی سرور داخلیه که به اینترنت بین الملل وابسته نباشه. اگه می خوای از دیگران کمک بگیری، برای راه اندازی سریع تر DevOps رو ببین.

قابل پیش بینی بودن. اینترنت بین الملل می تونه کند یا قطع بشه. pipeline ای که وسط انتقال قطع شد، نباید سایت رو نصفه بذاره. به همین خاطر اشاره گر فقط بعد از رسیدن کامل نسخه عوض می شه و یه تلاش دوباره (retry) ساده توی مرحله ارسال کمک می کنه.

نمادها و دامنه. انتشار خودکار نباید فایل تایید نماد یا تنظیمات دامنه رو پاک کنه. فایل های تاییدیه رو توی مخزن نگه دار تا با هر انتشار از بین نرن. اگه با اینماد درگیری، دریافت اینماد برای فروشگاه اینترنتی؛ پرسش های رایج پیش از درخواست می تونه کمکت کنه.

هزینه، ریسک و تصمیم: از کدوم سطح شروع کنیم؟

هزینه اصلی خود ابزار نیست. خیلی از ابزارهای CI پلن رایگان یا نسخه خودمیزبان دارن؛ شرایط و قیمت شون رو پیش از تصمیم از منبع رسمی بررسی کن. هزینه واقعی، وقت یه نفره برای نوشتن pipeline و نگه داریش. پس باید تصمیم بگیری ریسک انتشار دستی برات گرون تره یا این وقت.

سطحچی گیرت می آدریسک باقی موندهمناسب کی
انتشار دستی با چک لیستثبت مراحل، بدون ابزارخطای انسانی، جا انداختن مرحلهسایت ساده با تغییر خیلی کم
اسکریپت یک دکمه ایهر بار همان مراحلفراموش کردن تست و بررسیتیم کوچیک، شروع کار
pipeline کامل با stagingتست، تایید و برگشت خودکارخرابی کل ابزار CIفروشگاه یا سایت پرتغییر با پرداخت

اگه هنوز مطمئن نیستی، از وسط شروع کن: اسکریپت یک دکمه ای. یه هفته با اون کار کن، بعد تست دودی رو اضافه کن، بعد staging. هر سطح خودش جلوی بخشی از خطاها رو می گیره و لازم نیست همه رو یک جا بخری.

اشتباه هایی که زیاد می بینیم

  • ساختن pipeline پیچیده در روز اول. به جای خودکار کردن یه مسیر ساده، یه ساختار بزرگ می سازن که هیچ کس جرئت دست زدن بهش رو نداره.
  • نداشتن راه برگشت. انتشار خودکار بدون rollback یعنی خطاها رو سریع تر پخش می کنی.
  • نادیده گرفتن پایگاه داده. کد خودکار منتشر می شه ولی تغییر جدول ها دستی می مونه و دو طرف از هم جدا می افتن.
  • دسترسی زیاد برای کاربر استقرار. اگه کلید SSH اون کاربر لو بره، باید فقط به همون پوشه دسترسی داشته باشه.
  • نگه نداشتن مستندات. اگه فقط یه نفر pipeline رو می فهمه، همون ریسک «از حفظ» تو شکل جدید برگشته.

سوالات متداول

استقرار خودکار سایت برای یه سایت وردپرسی کوچیک هم می ارزه؟

بستگی به دفعات تغییر داره. اگه فقط افزونه ها رو ماهی یه بار آپدیت می کنی، یه چک لیست دقیق کافیه. ولی اگه قالب یا کد سفارشی داری که مدام تغییر می کنه، حتی یه اسکریپت ساده که فایل ها رو به نسخه جدید می بره و کش رو پاک می کنه، از خطاهای تکراری نجات می ده.

باید حتماً Docker یاد بگیرم؟

نه. برای خیلی از سایت ها، rsync با پوشه های نسخه و اشاره گر کافیه و ساده تر نگه داری می شه. Docker وقتی به درد می خوره که چند سرویس داری یا می خوای محیط توسعه و سرور دقیقاً یکی باشه. فقط یادت باشه محدودیت دریافت ایمیج روی سرور ایرانی رو از قبل بررسی کنی.

اگه خود pipeline خراب شد و نتونستم سایت رو منتشر کنم چی؟

برای همین، اسکریپت استقرار رو جوری بنویس که با دست هم قابل اجرا باشه. pipeline در اصل همون اسکریپت رو صدا می زنه، پس اگه ابزار CI از کار افتاد، همون اسکریپت رو از یه سیستم قابل اعتماد اجرا می کنی. این همون چیزیه که اتکا به یه ابزار رو کم می کنه.

پایگاه داده رو هم می شه خودکار به روز کرد؟

می شه، ولی با احتیاط بیشتر. قبل از اجرا پشتیبان بگیر و تغییرها رو طوری بنویس که نسخه قبلی کد هم با ساختار جدید کار کنه. برای تغییرهای حساس یا حذف ستون ها، تایید دستی توی pipeline بذار.

جمع بندی: این هفته چه کاری انجام بدیم؟

لازم نیست همه چیز رو یک جا بسازی. اول مسیر دستی فعلیت رو فهرست کن، بعد تبدیلش کن به یه اسکریپت که با دست هم اجرا بشه. یه تست دودی و یه آدرس سلامت اضافه کن و برگشت به نسخه قبل رو یه بار واقعاً امتحان کن؛ اینکه راه برگشت کار می کنه نباید روز بحران معلوم بشه. بعدش اگه تیم و سایت رشد کرد، staging و تایید دستی رو اضافه کن.

اگه به یه نفر برای مرور مسیر انتشارت یا راه اندازی اولین pipeline نیاز داشتی، مشاوره و معماری ما همین کار رو انجام می ده.

  • استقرار خودکار
  • CI/CD
  • Git
  • دیپلوی
  • مدیریت سرور

مقاله‌های مرتبط

ادامه همین موضوع در وبلاگ.

همه مقاله‌ها
کمک فنی

برای اجرای این کار کمک می‌خواهید؟

تیم فنی Wbweb از طراحی سایت تا هاست و سرور کنار شماست؛ درخواستتان را ثبت کنید یا تیکت بزنید.