ما تو این آموزش ابتدا مقدماتی درباره ی devOps ارائه دادیم و سپس سعی کردیم یک دید کلی نسبت به CI/CD پیدا کنیم و در نهایت بیشتر عمیق شدیم.
با توجه به سرفصل هر مبحث راحت میتوانید به آن قسمت دسترسی داشته باشید و مسلط بشین.
DevOps مخفف عملیات توسعه است. این مجموعه ای از اقدامات است که توسعه نرم افزار (Dev) و عملیات فناوری اطلاعات (Ops) را ترکیب می کند. هدف آن کوتاه کردن چرخه عمر توسعه سیستم ها و ارائه تحویل مداوم با کیفیت نرم افزار بالا است.
عملکرد فناوری اطلاعات را می توان از نظر توان عملیاتی و پایداری اندازه گیری کرد. توان عملیاتی را می توان با فرکانس استقرار و زمان انجام تغییرات اندازه گیری کرد. ثبات را می توان با میانگین زمان بهبودی اندازه گیری کرد. گزارشهای State of DevOps دریافتند که سرمایهگذاری در روشهایی که این اقدامات را افزایش میدهند و معیارهای ثبات را افزایش میدهند، عملکرد فناوری اطلاعات را افزایش میدهند.
اهداف DevOps کل خط لوله تحویل را در بر می گیرد. آنها عبارتند از:
1: فرکانس استقرار بهبود یافته
2: زمان سریعتر برای بازاریابی
3: نرخ شکست کمتر نسخه های جدید
4: زمان بین اصلاحات کوتاه شد
5: میانگین زمان بازیابی سریعتر (در صورت خراب شدن نسخه جدید یا غیرفعال کردن سیستم فعلی).
فرآیندهای imple با استفاده از رویکرد DevOps به طور فزاینده ای قابل برنامه ریزی اند و پویا می شوند. هدف DevOps به حداکثر رساندن قابلیت پیش بینی، کارایی، امنیت و قابلیت نگهداری فرآیندهای عملیاتی است. اغلب اوقات، اتوماسیون از این هدف پشتیبانی می کند.
ادغام DevOps تحویل محصول، آزمایش مداوم، تست کیفیت، توسعه ویژگیها و انتشارات نگهداری را هدف قرار میدهد تا قابلیت اطمینان و امنیت را بهبود بخشد و چرخههای توسعه و استقرار سریعتر را ارائه دهد.
اقداماتی که با فرکانس استقرار مرتبط هستند عبارتند از:
1: تحویل مستمر
2: استفاده از کنترل نسخه برای تمام مصنوعات تولید
اقداماتی که با زمان انجام تغییر مرتبط هستند عبارتند از:
1: استفاده از کنترل نسخه برای تمام مصنوعات تولید
2: تست خودکار
اقداماتی که با میانگین زمان بهبودی برای تغییر مرتبط هستند عبارتند از:
1: استفاده از کنترل نسخه برای تمام مصنوعات تولید
2: سیستم مانیتورینگ و سلامت برنامه
در حالی که DevOps به جای یک نقش متمایز (مانند مدیر سیستم) رویکردی به کار را توصیف می کند، تبلیغات شغلی به طور فزاینده ای از عباراتی مانند "DevOps Engineer" استفاده می کنند.
هیچ گواهی یا عنوانی در مورد "مهندسی DevOps" وجود ندارد، اما درست است که هر توسعهدهندهای میتواند و باید بداند که چگونه قطعهای را در فرآیند DevOps پیادهسازی، آزمایش و ارائه کند، حداقل قطعاتی که ما در آنها مشارکت داریم.
اگر چیزی در مورد آن نمیدانید و برای شرکتی درخواست میکنید که DevOps را در کل فرآیند توسعه پیادهسازی میکند، ممکن است به یک شوک فرهنگی و یک سوء تفاهم بزرگ در مورد چگونگی کار کردن و چرایی کارکرد آنها تبدیل شود.
من در مورد عباراتی مانند IaC (زیرساخت به عنوان کد)، Containerization، Orchestration یا اندازهگیری نرمافزار در این آموزش بررسی عمیق تر انجام نمیدم زیرا هر یک از آن نقاط به یک آموزش کامل نیاز دارند، و برای هدف این آموزش نیازی به آن نیست.
فرض کنید یک پروژهای داریم که سه تا برنامه نویس دارن روش کار میکنن. از گیت هم به عنوان ابزار version control امون استفاده میکنیم. هر برنامه نویس موظف هست که چند فیچر برنامهی مارو توسعه بده. برای همین برنامه نویس اول شروع میکنه به پیاده سازی فیچر ها. به صورت تک تک فیچر هارو توسعه میده و توی یه برنچ جدید کامیت میکنه. بعدش کدش تغییراتی رو روی کد میده و اون هارو کامیت میکنه. برنامه نویس دوم و سوم هم مستقلا شروع میکنن به توسعه فیچرهاشون و هر کدوم کدی رو اضافه میکنن و یا کد قبلی رو تغییر میدن و هر کدوم توی یک برنچ جدا پوش میکنن. ممکنه هست این روند دو یا سه هفته طول بکشه و حجم زیادی از کد مرج نشده روی برنچ اصلی داخل هر کدوم از برنچ های سه تا برنامه نویسمون باشه. در نهایت فیچر ها توسعه داده میشه و برنچ های کناری باید توی برنچ اصلی مرج بشن. اما زمان مرج کردن این برنچ ها توی برنچ اصلی به مشکلات زیادی میخوریم. باید حجم زیادی کد به برنچ اصلی اضافه بشه و تغییرات زیادی روی اون اعمال بشه. و بدتر این که این برنچ ها با هم ممکنه کانفلیکت (conflict) های زیادی داشته باشن.
بالاخره بعد از رد کردن دردسر مرج کردن برنچ ها، کد به دست تیم کنترل یا کیفیت یا QA میفته که باگ های برنامه رو پیدا بکنن. باگ های پیدا شده توی این مرحله باید به برطرف بشن اما مشکل این جاست که مدت زیادی از توسعه بعضی از اون کدها گذشته و حتی برنامه برنامه نویس توسعه دهنده هم یادش نیست که چه کدی زده!
در نهایت با رد کردن دو مرحله سنگین مرج کردن کد ها و مرحله تست کیفیت، میتونیم کدمون رو ارائه کنیم.
حالا بیاین کل این پروسه رو یه جور دیگه طی کنیم! این دفعه هر برنامه نویسمون شروع به کار میکنه و اگر تغییری در کدش ایجاد کرد، اون تغییر رو با منبع مرج میکنه و کد مرج شده رو تست میکنه. اینجوری تغییرات کم کم به برنچ اصلی اضافه میشن و موقع اضافه شدن تست میشن. پس دیگه دردسر مرج کردن کد ها و حل کردن کانفلیکت هارو نخواهیم داشت. علاوه بر این تیم QA هم میتونی موازی تیم development، تغییرات کوچیک اضافه شده به کد اصلی رو تست کنه و تا آماده شدن تمام فیچر ها بیکار نمونه! به این پروسه تجمیع مدام یا continuous integration میگیم.
حالا CI/CD چجوری کار میکنه؟
برنامه نویس میاد و تغییراتی رو توی کد میده و کد هایی رو اضافه میکنه. بعد از این میاد و unit test هارو اجرا میکنه. اگر کدش نمیتونست این تست هارو رد کنه، دوباره میاد و کدش رو تغییر میده تا این مرحله رو رد کنه. بعدش کدش رو با منبع ادغام میکنه و یه pull request به برنچ اصلی میده. در این جا ما روی یک سرور، integration test هارو روی کد برنامه نویس انجام میدیم و اگر کد این مرحله رو رد نکرد، دوباره برنامه نویس کدش رو تغییر میده تا این که بالاخره این مرحله رو هم رد کنه. در نهایت کد برنامه نویس روی سرور های تست deploy میشه و باز هم با رد کردن تست های این مرحله، کد آماده انتشار میشه. در واقع تمام این عملیات ها به صورت اتوماتیک و پشت سر هم انجام میگیرند و برای همین هم از عبارت CI/CD Pipeline یا خط لوله CI/CD استفاده میشه.
اگر تفاوت unit test رو با integration test نمیدونین، این لینک رو نگاه کنید.
https://www.geeksforgeeks.org/difference-between-unit-testing-and-integration-testing/
اگر شما برنامه نویسی باشید که بخواین کدتون رو روی یک سرور اجرا کنین، اول تغییرات رو توی مخزن گیتتون push میکنید و بعد اون تغییرات رو از سرورتون pull میکنید و مثلا سرویسی رو ریستارت میکنید یا یه اسکریپت رو ران میکنید که دوباره برنامه تون رو با تغییرات جدید اجرا کنه. تا این جا کار مشکلی نداشت اما اگر بخوایم این کارو برای چند سرور انجام بدیم، دیگه این کار حوصله سر بر و زمان بر میشه. علاوه بر این گاها اجرا کردن و بیلد کردن دوباره خودش پروسه ای سخته، پس چرا این کارو اتوماتیک انجام ندیم؟
در این جا continuous deployment یا deployment مداوم به ما کمک میکنه که کارهای روتین مون رو اتوماتیک کنیم و وقت و انرژی ما هدر نره! به این صورت که توی خط لولهی CI/CD مون، پروسه های لازم برای deploy شدن کدمون رو تعریف میکنیم و از این به بعد، این کار خود به خود و بعد از مرج شدن توی برنچ اصلی انجام میشه.
حال میخواهیم به صورت عملی و دقیق تری نحوه ی عملکرد CI/CD را بررسی کنیم:
یکپارچه سازی مداوم با push کردن تکه های کوچک کد به پایگاه کد برنامه شما که در یک مخزن Git میزبانی شده است، کار می کند، و در هر push ، خط لوله ای از اسکریپت ها را اجرا می کند تا تغییرات کد را قبل از ادغام آنها در شاخه اصلی ایجاد، آزمایش و اعتبار سنجی کند.
Continuous Delivery and Deployment شامل یک مرحله بیشتر از CI است که برنامه شما را برای تولید در هر push به شعبه پیشفرض مخزن گسترش میدهد.
این روشها به شما امکان میدهند باگها و خطاها را در اوایل چرخه توسعه پیدا کنید و اطمینان حاصل کنید که همه کدهای مستقر در تولید با استانداردهای کدی که برای برنامه خود ایجاد کردهاید مطابقت دارد.
من از GitLab به عنوان مخزن اصلی استفاده می کنم اما این اسکریپت باید روی همه مخازن git کار کند. در GitLab این اسکریپت باید به صورت .gitlab-ci.yml نامگذاری شود (باید بسته به مخزن انتخابی خود نام آن را تغییر دهید).
Simple script:
stages:
- deploy
deploy:
stage: deploy
image: debian:stretch-slim
only:
- master
script:
- apt-get update && apt install -y --no-install-recommends lftp
- lftp -e "set ftp:ssl-allow no; set ssl:verify-certificate no; mirror -R ./ "$REMOTE_ROUTE -p 21 -u $USER,$PSWD $HOST
** حروف بزرگ قبل از نماد دلار متغیرهایی هستند که میتوانید در تنظیمات/ترجیحات مخزن خود تنظیم کنید.
این یک تصویر داکر را با کد شما بالا میآورد، برخی از اقدامات را در داخل این داکر انجام میدهد و سپس این تصویر داکر نابود میشود.
Line by Line:
مرحله ای به نام deploy را به لیست مرحله اضافه می کنیم، سپس این مرحله deploy را با یک تصویر داکر debian:stretch-slim تعریف می کنیم. تنها زمانی افزایش می یابد که merge/push به شاخه Master اتفاق بیفتد. سپس اسکریپتی را تعریف می کنیم که در این نقطه از مرحله deploy اجرا می شود.
در این حالت به سادگی یک به روز رسانی و ارتقاء انجام می دهد (=y- برای همه سوالات بله فرض می شود)، سپس باید lfpt را نصب کنیم و در نهایت از lftp برای انتقال محتوای مخزن (./) به دایرکتوری مورد نظر سرور (REMOTE_ROUTE$) با استفاده از FTP استفاده می کند. (این دایرکتوری سرور، راهنمای ریشه برای یک دامنه معین خواهد بود).
این می تواند Continuous Deployment باشد زیرا هر بار که push کد به شاخه Master انجام می شود، فعال می شود.
همچنین میتواند Continuous Delivery باشد، در صورتی که push به Master را مسدود کنیم، مرحله دیگری را اضافه کنیم که به جای Master کد را از Develop میگیرد و کد را به یک ماشین آزمایشی تحویل میدهد. سپس باید به صورت دستی یک ادغام را در master انجام دهیم تا بتوانیم Deploy را در خط لوله تولید اجرا کنیم.
حال خواهیم دید که میتواند اقدامات دیگری هم انجام دهد:
خوب اولین کاری که باید انجام دهیم این است که امنیت را به آن اضافه کنیم. این یک تراکنش ساده FTP است که می تواند به راحتی به یک تراکنش FTP از طریق SSL تبدیل شود.
stages:
- deploy
deploy:
stage: deploy
image: debian:stretch-slim
only:
- master
script:
- apt-get update && apt install -y --no-install-recommends lftp
- lftp -e "set ftp:ssl-protect-data true; set ftp:ssl-force true; set ssl:verify-certificate no; set ftp:ssl-allow no; mirror -R ./ "$REMOTE_ROUTE -p 21 -u $API_USER,$API_PSWD $HOST
اکنون ;ftp:ssl-protect-data true; set ftp:ssl-force true را اضافه کردیم.
بنابراین روی SSL اجرا می شود (که باید روی دستگاه مورد نظر نصب شود). بهترین راه برای انجام این اقدامات استفاده از SSH به جای آن است.
خوب، حالا ما این فرآیند رمزگذاری تراکنش های فایل را با SSL ایمن کرده ایم.
Adding jobs:
تصور کنید پوشهای به نام /app در repository root dir دارم که در آن یک پروژه react/angular/svelte/preact (هر چیزی) یا یک دسته html ساده، css و js از فایلها برای فرانتاند دارم.
امروزه استفاده از (bundler (webpack, rollup, parcel رایج است.
می توانید dist/folder (خروجی bundler) را به مخزن خود اضافه کنید تا بتوانید پوشه dist/ را مستقیماً از مخزن به پوشه تولید منتقل کنید:
stages:
- deploy
deploy:
stage: deploy
image: debian:stretch-slim
only:
- master
script:
- apt-get update && apt install -y --no-install-recommends lftp
- lftp -e "set ftp:ssl-protect-data true; set ftp:ssl-force true; set ssl:verify-certificate no; set ftp:ssl-allow no; mirror -R ./app/dist/ "$REMOTE_ROUTE -p 21 -u $API_USER,$API_PSWD $HOST
این یک عمل بد است. پس از همه اینها نتیجه یک فرآیند انتقال/کامپایل روی کد اصلی شماست.
ما میتوانیم این مشکل را با اجرای build در داخل این ماشین در فرآیند deploy به صورت زیر حل کنیم:
stages:
- deploy
deploy:
stage: deploy
image: debian:stretch-slim
only:
- master
script:
- apt-get update && apt install -y --no-install-recommends lftp
- cd ./app/
- apt-get install -y gnupg2
- apt-get install curl -y
- apt-get install apt-transport-https ca-certificates
- curl -sL https://deb.nodesource.com/setup_12.x | bash -
- apt-get install nodejs -y
- curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
- echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
- apt update && apt install yarn -y
- yarn install
- yarn build-prod
- lftp -e "set ftp:ssl-protect-data true; set ftp:ssl-force true; set ssl:verify-certificate no; set ftp:ssl-allow no; mirror -R ./dist/ "$REMOTE_ROUTE -p 21 -u $USER,$PSWD $HOST
در ابتدا به دایرکتوری برنامه /app می روم، جایی که front-end من است. سپس dependencies را نصب میکنم تا بتوانم دستورات yarn install و yarn build-prod را انجام دهم.
yarn install بسته.json را که داخل پوشه برنامه است می خواند، وابستگی های مشخص شده روی آن را دانلود و نصب می کند و سپس node_modules را تولید می کند.
yarn build-prod یک اسکریپت سفارشی است که از parcle برای bundle کردن برنامه من استفاده می کند، جزئیات در این زمینه اهمیتی ندارد، تنها چیزی که باید بدانید این است که این یک dist/پوشه در داخل برنامه تولید می کند که حاوی کد front-end من برای production است.
در پایان (به یاد داشته باشید که ما داخل دایرکتوری /app/. هستیم) من محتوای پوشه dist/ را به REMOTE_ROUTE$ که دایرکتوری اصلی تولید است push می کنم.
در نتیجه:
1: از push کردن dist/ folder به داخل مخزن اجتناب کردیم.
2: از push کردن node_modules به مخزن خودداری کردیم.
3: هر بار که برخی از کدها در شاخهpush ،Git Master می شوند یا ادغام می شوند، فرآیند deploy را خودکار می کند.
4: اگر برخی از فرآیندها با شکست مواجه شوند (چون یک وابستگی جدید اضافه کرده ایم اما به عنوان مثال در package.json بیان نشده است) یک خطا ایجاد می کند و فرآیند را لغو می کند، بنابراین قابلیت اطمینان را اضافه کردیم.
میتوانید triggerهایی را برای تست هایتان در این فرآیند اضافه کنید که بسته به ابزارهایی که برای تست استفاده میکنید میتواند بسیار متفاوت باشد. هر ابزار دارای documentation در مورد اضافه کردن تست ها به خطوط لوله CI خواهد بود.
افزودن تست ها به خط لولهتان و افزودن مرحلهای دیگر برای استقرار توسعه در محیط پیشتولید/تست، اسکریپت شما را کامل میکند.
اگر مشکلی ایجاد نشود و خط لوله CI/CD شما به درستی تکمیل شود، قبل از deploy با یک گزارش قابل مشاهده، تست های اتوماسیون را دریافت خواهید کرد.
در این مرحله تنها چیزی که باقی میماند نظارت است.
مانیتورینگ ضربان قلب عملکرد برنامه های شما را در حین deploy نسخه های جدید کد خود در محیط های مختلف ارائه می دهد. تشخیص مشکلات در مراحل اولیه به تیم ها این امکان را می دهد که به سرعت مشکل را برطرف کنند و به تست و نظارت بر تغییرات بعدی ادامه دهند.
ابزارهای زیادی برای این منظور وجود دارد که برخی از آنها Datadog, Nagios, Zabbix, Sensu, Prometheus, SysDig... هستند.
هر کدام رویکرد متفاوتی برای کار دارند و ابزارهای مختلفی را ارائه میدهند (نه تنها برای نظارت بر برنامههای شما، بلکه برای نظارت بر وضعیت سرور، نمودارهای داده و غیره) بنابراین باید کمی جستجو کنید تا بدانید کدام یک برای شما مناسبتر است.
اگر برای کار در یک شرکت مدرن (یا یک شرکت قدیمی با نرمافزار مدرن) درخواست میدهید، آنها معمولاً از خط لوله مانند این استفاده میکنند (ممکن است بسته به فناوریهایی که استفاده میکنند بسیار متفاوت باشد، اما اصول اولیه و فرآیند اساساً یکی خواهد بود. همان: برنامه ریزی، کدگذاری، ساخت، آزمایش، انتشار، استقرار، بهره برداری، نظارت و تکرار و تکرار).
در انتها خوبه یه نگاه به مزیت های این متدلوژی داشته باشیم:
1: حذف پروسه زمان بر مرج کردن و صرفه جویی در وقت
2: ایجاد امکان کار موازی برای تیم کنترل کیفیت
3: پیدا کردن سریع باگ ها
4: امکان دیدن بازخورد تغییرات اجرا شده به صورت آنی
5: کوتاه شدن پروسه ریلیز نسخه جدید
6: امکان گرفتن بازخورد ها از کاربر در مدت کوتاه
7: بهبود کیفیت کد نهایی
...
امیدوارم این به شما کمک کرده باشد و همه این آموزش را درک کرده باشید.