GitLab یک پلتفرم توسعه اشتراکی به صورت متن‌باز است که ابزارهایی فراتر از یک «میزبانی منبع کد» در اختیار قرار می‌دهد. شما در بستر GitLab می‌توانید مشکلات را ردیابی کنید، بسته‌ها و رجیستری‌ها را میزبانی کنید، به صفحات راهنمای ویکی رسیدگی کنید، شرایط هماهنگی همیشگی (CI) و شبکه توسعه مداوم (CD) را تنظیم نمایید و بسیاری از موارد دیگر.

در این آموزش، به ایجاد یک شبکه توسعه مداوم در بستر GitLab خواهیم پرداخت. خطوط شبکه یا pipeline با ساخت یک ایمیج Docker تنظیم می‌شوند. سپس این ایمیج به رجیستری کانتینر GitLab انتقال داده می‌شود و بعداً از طریق SSH در سرور استفاده می‌گردد.

در این آموزش از یک صفحه وب کوچک و استاتیک استفاده می‌شود و بیشتر تمرکز این آموزش بر روی تنظیم خطوط شبکه CD است. همچنین می‌توانید این تنظیمات برای ایمیج‌های دیگر Docker نیز استفاده کنید.

وقتی این آموزش را به پایان رساندید، می‌توانید از طریق مرورگرتان به آدرس http://your_server_IP و نتایج بکارگیری اتوماتیک را مشاهده نمایید.

پیش‌نیازها

برای تکمیل مراحل این آموزش به موارد زیر احتیاج خواهید داشت.

  • یک سرور اوبونتو 18.04 همراه با تنظیمات اولیه شامل یک کاربر غیر روت با دسترسی‌های sudo و فایروال. همچنین به حداقل یک گیگابایت حافظه رم و یک پردازنده احتیاج خواهید داشت.
  • نصب Docker در سرور
  • یک حساب کاربری در GitLab همراه با فعال‌بودن رجیستری کانتینر. نسخه رایگان رسمی GitLab تمام شرایط موردنیاز را برآورده می‌کند. در همین حال، می‌توانید از بستر GitLab در سرور خودتان نیز میزبانی کنید.

گام ۱) ساخت منبع GitLab

با ایجاد یک پروژه GitLab شروع می‌کنیم و یک فایل HTML به آن اضافه می‌کنیم. بعداً این فایل به یک ایمیج Nginx Docker کپی شده و برای سرور مورد استفاده قرار می‌گیرد.

به GitLab وارد شوید و گزینه New project را بزنید.

دکمه New project در بستر GitLab

دکمه New project در بستر GitLab

  • یک نام مناسب برای پروژه انتخاب کنید.
  • در صورت تمایل، توضیحاتی در مورد پروژه وارد نمایید.
  • بر حسب نیاز، سطح دسترسی یا Visibility Level را به “Private” یا “Public” (خصوصی یا عمومی) تنظیم کنید.
  • نهایتاً بر روی “Create project” کلیک کنید.
فرم پروژه جدید در بستر GitLab

فرم پروژه جدید در بستر GitLab

در نتیجه، به صفحه نمای کلی پروژه منتقل می‌شوید.

حالا فایل اچتمل را ایجاد می‌کنیم. در صفحه نمای کلی پروژه، بر روی دکمه “New file” کلیک کنید.

دکمه “New file” در صفحه نمای کلی پروژه

دکمه “New file” در صفحه نمای کلی پروژه

عنوان فایل را به صورت index.html انتخاب کرده و کد اچتمل زیر را در بدنه فایل وارد نمایید.

</pre>
<html>

<body>

<h1>My Personal Website</h1>

</body>

</html>

سپس روی “Commit changes” در پایین صفحه کلیک کنید تا فایل ایجاد شود.

این اچتمل موجب ایجاد یک صفحه خالی با تیتر “My Personal Website” در مرورگر می‌شود.

Dockerfile ها دستورالعمل‌هایی هستند که توسط Docker برای ساخت ایمیج‌های Docker مورد استفاده قرار می‌گیرند. در اینجا، یک Dockerfile برای کپی‌کردن فایل اچتمل به ایمیج Nginx ایجاد می‌کنیم.

بر این اساس، به صفحه نمای کلی پروژه برمی‌گردیم. روی دکمه + کلیک و گزینه “New file” را انتخاب می‌کنیم.

گزینه New file در صفحه نمای کلی پروژه در لیست دکمه  بعلاوه

گزینه New file در صفحه نمای کلی پروژه در لیست دکمه  بعلاوه

عنوان فایل را به صورت Dockerfile تنظیم کرده و دستورالعمل‌های زیر را به بدنه فایل وارد می‌کنیم.

FROM nginx:1.18

COPY index.html /usr/share/nginx/html

دستور FROM ایمیج موردنظر را مشخص می‌کند.همچنین در اینجا در nginx:1.18، منظور از 1.18 نسخه Nginx است. استفاده از تگ nginx:latest باعث ارجاع به آخرین نسخه Nginx می‌شود. امّا باید توجه داشته باشید که این موضوع ممکن است که درآینده موجب از کار افتادن اپلیکیشن شما شود. بر این اساس، استفاده از نسخه‌های ثابت توصیه می‌گردد.

دستور COPY فایل index.html را به دایرکتوری /usr/share/nginx/html در ایمیج Docker انتقال می‌دهد. این همان دایرکتوری‌ای است که Nginx محتوای استاتیک اچتمل را در آن نگهداری می‌کند.

سپس باید روی Commit changes در پایین صفحه کلیک کنید تا فایل ساخته شود.

در گام بعدی، یک GitLab runner را تنظیم می‌کنید تا به این وسیله بر افراد اجرا کننده برنامه کنترل داشته باشید.

گام ۲) ثبت یک GitLab runner

برای حفظ و رهگیری اقدامات محیطی که ارتباط آن از کلیدهای خصوصی SSH صورت می‌گیرد، سرور خود را به عنوان یک GitLab runner ثبت می‌کنید.

شما در نقاط شبکه توسعه به ورود از طریق SSH به سرور احتیاج خواهید داشت. برای این منظور، باید کلیدهای اختصاصی SSH خود را در یک متغیر GitLab CI/CD ذخیره کنید (این کار در گام ۵ صورت می‌گیرد). کلید خصوصی SSH داده بسیار حساسی محسوب می‌شود؛ چرا که در واقع، بلیت ورود به سرور شما خواهد بود. معمولاً یک کلید خصوصی هیچ‌گاه از سیستمی که در آن تولید شده، خارج نمی‌شود. شرایط معمول به این صورت است که شما یک کلید SSH در سیستم میزبان تولید می‌کنید و سپس نوبت به تأیید آن در سرور می‌رسد. در این حالت است که شما می‌توانید به صورت دستی به سرور وارد شده و از ‌آن استفاده نمایید.

امّا در اینجا، شرایط اندکی متفاوت است. شما در اینجا قصد دارید که یک حالت دسترسی خودکار (GitLab CI/CD) برای اتوماسیون روند توسعه ایجاد کنید. بنابراین، کلید اختصاصی ناگزیر به خروج از سیستمی که در آن تولید شده، خواهد بود و اینکه این کلید باید مورد اطمینان GitLab و سایر برنامه‌های مربوطه باشد. این در حالی است که هیچ‌کس نمی‌خواهد که کلید اختصاصی‌اش وارد محیطی شود که هیچ کنترل یا اعتمادی نسبت به آن نداشته باشد.

در کنار GitLab، GitLab runner سیستم دیگری است که کلید اختصاصی شما به آن وارد می‌شود. بستر GitLab  برای هر شبکه‌ای از runner ها برای انجام کارهای سنگین استفاده می‌کنید. runner ابزاری است که برای اجرای کارهای مشخص‌شده در تنظیمات CI/CD مورد استفاده قرار می‌گیرد. در نتیجه، باید گفت که «توسعه» عملاً و نهایتاً بر روی یک GitLab runner صورت می‌گیرد. اینجاست که کلید خصوصی به این runner کپی شده و در نتیجه، می‌تواند از طریق SSH به سرور وارد شود.

اگر از Runner های ناشناخته GitLab (به عنوان مثال Runner های اشتراکی) استفاده می‌کنید، به احتمال زیادی از سیستم‌هایی که با کلید اختصاصی ارتباط پیدا می‌کنند، اطلاع نخواهید داشت. البته GitLab runner ها پس از انجام کار، تمام داده‌ها را پاک می‌کنند. ولی باز هم برای احتیاط بیشتر و جلوگیری از انتقال کلیدهای خصوصی به سیستم‌های ناشناس، می‌توانید سرور خود را به عنوان یک GitLab runner ثبت کنید. با این کار، کلید اختصاصی به سروری منتقل می‌شود که کنترل آن در اختیار شماست.

ابتدا به سرور وارد می‌شویم.

ssh [email protected]_server_IP

برای نصب سرویس gitlab-runner، منبع رسمی بستر GitLab را اضافه می‌کنیم. بر این اساس، اسکریپت نصب آن را دریافت می‌کنیم.

curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh &amp;amp;amp;gt; script.deb.sh

less script.deb.sh

وقتی از امنیت اسکریپت مطمئن شدید، نصب‌کننده را اجرا نمایید.

 sudo bash script.deb.sh

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

 [sudo] password for sammy:   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

100  5945  100  5945    0     0   8742      0 --:--:-- --:--:-- --:--:--  8729

وقتی فرمان curl خاتمه یافت، پیغام زیر را دریافت خواهید کرد.

The repository is setup! You can now install packages.

سپس سرویس gitlab-runner را نصب کنید.

sudo apt install gitlab-runner

با بررسی وضعیت سرویس، از نصب این ابزار مطمئن شوید.

systemctl status gitlab-runner

در نتیجه، با عبارت active (running) در خروجی روبرو می‌شوید.

gitlab-runner.service - GitLab Runner

Loaded: loaded (/etc/systemd/system/gitlab-runner.service; enabled; vendor preset: enabled)

Active: active (running) since Mon 2020-06-01 09:01:49 UTC; 4s ago

Main PID: 16653 (gitlab-runner)

Tasks: 6 (limit: 1152)

CGroup: /system.slice/gitlab-runner.service

└─16653 /usr/lib/gitlab-runner/gitlab-runner run --working-directory /home/gitlab-runner --config /etc/gitla

برای ثبت runner، نیاز به دریافت توکن پروژه و GitLab URL خواهید داشت.

برای این منظور، در پروژه GitLab به مسیر Settings > CI/CD > Runners بروید.

در بخش Set up a specific Runner manually، گزینه registration token و آدرس GitLab را پیدا خواهید کرد. هر دوی اینها را به یک ویرایشگر متنی کپی کنید. چرا که در فرمان بعدی به آنها نیاز خواهید داشت و به صورت https://your_gitlab.com و project_token مورد استفاده قرار می‌گیرند.

بخش runners در تنظیمات ci/cd همراه با دکمه copy token

بخش runners در تنظیمات ci/cd همراه با دکمه copy token

به ترمینال برگردید و runner را برای پروژه خود ثبت کنید.

sudo gitlab-runner register -n --url https://your_gitlab.com --registration-token project_token --executor docker --description "Deployment Runner" --docker-image "docker:stable" --tag-list deployment --docker-privileged

گزینه‌های این فرمان می‌توانند به صورت زیر تفسیر شوند.

  • -n باعث می‌شود که فرمان register به صورت غیرمحسوس فعال شود.
  • –url آدرس GitLab است که قبلاً ان را از صفحه runners در بستر GitLab کپی کرده بودید.
  • –registration-token همان توکن موجود در صفحه runners است.
  • –executor نوع اجرا کننده است. Docker هر کدام از کارهای CI/CD را در یک کانتینر اجرا می‌کند.
  • –description توضیحات مربوط به runner است که بعداً در GitLab نمایش داده می‌شود.
  • –docker-image ایمیج Docker پیش‌فرض برای کارهای CI/CD است . در صورت وارد نکردن یک ایمیج خاص، این ایمیج مورد استفاده قرار خواهد گرفت.
  • –tag-list لیستی از برچسب‌های اختصاص‌یافته به runner است. این برچسب‌ها می‌توانند برای انتخاب برخی runner های خاص در تنظیمات شبکه به کار گرفته شوند. برچسب deployment به شما اجازه می‌دهد که این runner را برای اقدامات توسعه‌ای استفاده کنید.
  • –docker-privileged باعث می‌شود که کانتینر Docker ساخته شده برای هر کدام از کارهای CI/CD در یک حالت ممتاز (privileged) اجرا شود. یک کانتینرممتاز دارای دسترسی به تمام دستگاه در سیستم هاست است و تقریباً‌ همان دسترسی فرآیندهای خارج از کانتینرها را دارد دلیل اجرای کانتینر در حالت «ممتاز» این است که شما می‌توانید به این وسیله از قابلیت Docker-in-Docker (dind) برای ساخت یک ایمیج داکر در شبکه CI/CD استفاده کنید. راهکار مناسب برای این منظور، تأمین حداقل‌ها برای اجرای کانتینر و مخصوصاً برای استفاده از Docker-in-Docker است. به خاطر داشته باشید که شما runner را برای این پروژه خاص ثبت کرده‌اید؛ جایی که شما کنترل فرمان‌هایی را که در کانتینر ممتاز اجرا می‌شوند، در اختیار دارید.

پس از اجرای gitlab-runner register، خروجی زیر را دریافت خواهید کرد.

Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

حالا با رفتن به مسیر Settings > CI/CD > Runners، از ثبت runner مطمئن شوید.

runner ثبت‌شده در بخش  runners در تنظیمات ci/cd

runner ثبت‌شده در بخش  runners در تنظیمات ci/cd

در مرحله بعدی، یک کاربر Deployment ایجاد خواهید کرد.

گام ۳) ایجاد یک کاربر Deployment

در اینجا می‌خواهیم یک کاربر مختص انجام کارهای توسعه بسازیم. این کاربر بعداً برای تنظیم شبکه CI/CD برای ورود به سرور مورد استفاده قرار خواهد گرفت.

برای این منظور، در سرور خودتان یک کاربر جدید ایجاد کنید.

sudo adduser deployer

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

حالا کاربر را به گروه Docker اضافه کنید.

 sudo usermod -aG docker deployer

این کار موجب می‌شود که کاربر deployer  بتواند فرمان docker را اجرا نماید.

هشدار: اضافه‌کردن یک کاربر به گروه Docker باعث می‌شود که دسترسی‌هایی در سطح کاربر روت داشته باشد. بنابراین، حتماً جنبه امنیتی این موضوع را مدنظر داشته باشید.

در مرحله بعدی، یک کلید SSH برای ورود به سرور به عنوان توسعه‌دهنده ایجاد خواهیم کرد.

گام ۴)  تنظیم کلید SSH

در اینجا می‌خواهیم یک کلید SSH برای کاربر توسعه‌دهنده بسازیم. در ادامه، GitLab CI/CD از این کلید برای ورود به سرور و انجام فرآیندهای توسعه استفاده خواهد کرد.

ابتدا به کاربر deployer که کلید SSH قرار است برای آن تولید شود، سوئیچ می‌کنیم.

su deployer

در اینجا از شما خواسته می‌شود که کلمه عبور deployer را وارد کنید تا ورود کاربری تکمیل شود.

سپس یک کلید SSH به صورت 4096-bit ایجاد کنید. حتماً دقت کنید که به پرسش‌های فرمان ssh-keygen پاسخ مناسب بدهید.

۱) اولین پرسش: این پرسش را با کلید Enter پاسخ دهید. در نتیجه، کلید در موقعیت پیش‌فرض ذخیره می‌شود. درنظر داشته باشید که سایر قسمت‌های این آموزش با این پیش‌فرض هماهنگ شده‌اند.

۲) دومین پرسش: یک کلمه عبور برای محافظت از کلید خصوصی SSH تنظیم کنید. در صورتی که یک عبارت کلمه عبور تنظیم کنید، در هر بار استفاده از کلید خصوصی باید آن را وارد کنید. در حالت کلی، عبارت کلمه عبور باعث می‌شود که یک لایه امنیتی دیگر به کلیدهای SSH اضافه شود. امّا برای پیش بردن اهداف این آموزش، بهتر است عبارت کلمه عبور را خالی بگذاریم. چرا که شبکه CI/CD به صورت غیرهوشمند قرار است اجرا شود و بنابراین اجازه ورود یک کلمه عبور را نخواهد داد.

به طور خلاصه باید گفت که پس از اجرای فرمان زیر و تأیید دو پرسش با زدن Enter و یک کلید SSH به صورت 4096-bit، آن را در موقعیت پیش‌فرض و بدون عبارت کلمه عبور ذخیره می‌کنیم.

ssh-keygen -b 4096

برای تأیید کلید SSH برای کاربر deployer، باید کلید عمومی را به فایل authorized_keys اضافه کنید.

cat ~/.ssh/id_rsa.pub &amp;amp;amp;gt;&amp;amp;amp;gt; ~/.ssh/authorized_keys

علامت ~ در لینوکس مخفف دایرکتوری خانگی کاربر است. برنامه cat محتوای یک فایل را چاپ می‌کند. همچنین در اینجا با استفاده از عملگر >>، خروجی cat را به فایل authorized_keys منتقل می‌کنیم.

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

گام ۵) ذخیره کلید خصوصی در یک متغیر GitLab CI/CD

در اینجا قرار است که کلید خصوصی SSH را در یک متغیر فایل GitLab CI/CD ذخیره کنید. بر این اساس، شبکه قادر خواهد بود که با این کلید به سرور وارد شود.

وقتی بستر GitLab یک شبکه CI/CD ایجاد می‌کند، تمام متغیرها را به runner مربوطه ارسال می‌کند و این متغیرها نیز به عنوان متغیرهای محیطی در طول فرآیند تنظیم می‌شوند. مقادیر این متغیرها در یک فایل ذخیره می‌شوند و متغیرهای محیطی حاوی مسیری به این فایل خواهند بود.

در حالی که در بخش variables هستید، یک متغیر نیز برای IP سرور و کاربر سرور ایجاد می‌کنید. این متغیر برای اطلاع‌رسانی شبکه در مورد سرور مقصد و کاربر مربوطه استفاده می‌شود.

با نمایش کلید خصوصی SSH شروع می‌کنیم.

cat ~/.ssh/id_rsa

خروجی را در کلیپ‌بورد کپی کنید. حتماً دقت کنید که یک خط فاصله بعد از —–END RSA PRIVATE KEY—– قرار دهید.

-----BEGIN RSA PRIVATE KEY-----

...

-----END RSA PRIVATE KEY-----

حالا به مسیر Settings > CI / CD > Variables در پروژه GitLab بروید و روی “Add Variable” کلیک کنید. سپس فرم را مطابق زیر تکمیل کنید.

  • Key: ID_RSA
  • Value: کلید خصوصی SSH خود را از کلیپ‌بورد بچسبانید (شامل یک خط فاصله در انتها)
  • Type: File
  • Environment Scope: All (default)
  • Protect variable: Checked
  • Mask variable: Unchecked

نکته: متغیر نمی‌تواند پوشش‌گذاری یا masked شود؛ چرا که معیارهای ارائه لازم را ندارد. با این وجود، کلید خصوص هیچ‌گاه در تاریخچه کنسول ظاهر نمی‌شود و همین موضوع باعث می‌شود که پوشش‌گذاری عملاً تفاوتی ایجاد نکند.

در نتیجه، یک فایل حاوی کلید خصوصی در runner ایجاد می‌شود که برای هر کدام از کارهای CI/CD مورد استفاده قرار می‌گیرد. همچنین مسیر آن در متغیر محیطی $ID_RSA ذخیره می‌گردد.

حالا یک متغیر دیگر با IP سرور ایجاد کنید. روی Add Variable کلیک کنید و فرم را مطابق زیر پر نمایید.

  • Key: SERVER_IP
  • Value: your_server_IP
  • Type: Variable
  • Environment scope: All (default)
  • Protect variable: Checked
  • Mask variable: Checked

نهایتاً یک متغیر همراه با ورود کاربر ایجاد کنید. برای این منظور روی Add Variable کلیک کرده و فرم را مطابق زیر تکمیل کنید.

  • Key: SERVER_USER
  • Value: deployer
  • Environment scope: All (default)
  • Protect variable: Checked
  • Mask variable: Checked

اکنون کلید خصوصی را در یک متغیر GitLab CI/CD ذخیره کرده‌ایم؛ موضوعی که باعث می‌شود که کلید در طول اجرای فرآیند شبکه در دسترس قرار گیرد. در مرحله بعدی، به سراغ تنظیم شبکه CI/CD می‌رویم.

گام ۶) تنظیمات فایل .gitlab-ci.yml

در اینجا می‌خواهیم شبکه the GitLab CI/CD را تنظیم کنیم. درنظر داشته باشید که شبکه یک ایمیج docker ایجاد کرده و آن را به رجیستری کانتینر انتقال می‌دهد. GitLab برای هر کدام از پروژه‌ها یک رجیستری کانتینر می‌سازد. در همین حال، امکان بررسی این رجیستری از مسیر Packages & Registries > Container Registry در پروژه GitLab وجود دارد. گام نهایی در شبکه، ورود به سرور، دریافت آ‌خرین نسخه داکر ایمیج، حذف کانتینر قدیمی و شروع یک کانتینر جدید است.

حالا فایل .gitlab-ci.yml را می‌سازیم که حاوی تنظیمات شبکه یا pipeline خواهد بود. در بستر GitLab، به صفحه نمای کلی پروژه رفته و روی دکمه + کلید کنید و سپس New file را انتخاب نمایید. اکنون عنوان فایل را به .gitlab-ci.yml تنظیم کنید.

برای شروع، موارد زیر را اضافه کنید.

stages:

  - publish

 - deploy

هر کدام از وظایف به یک stage اختصاص داده می‌شود. وظایفی که دارای stage یکسان باشند، به صورت موازی اجرا می‌شوند (در صورتی که runner به تعداد کافی در دسترس باشد). stage ها به ترتیبی که برای آنها مشخص کرده‌ایم، اجرا می‌شوند. در اینجا، ابتدا publish stage و سپس deploy stage اجرا می‌شوند. stage های بعدی تنها در صورتی آغاز به کار می‌کنند که stage قبلی به پایان رسیده باشد. همچنین عناوین stage ها به صورت اختیاری انتخاب می‌شوند.

وقتی می‌خواهید این تنظیمات CD را با شبکه CI کنونی ترکیب نمایید (برای تست و ساخت اپلیکیشن)، شاید نیاز داشته باشید که stage‌های publish و deploy را بعد از stage های کنونی تنظیم کنید. به این دلیل که مثلاً توسعه تنها زمانی انجام گیرد که معیارهای تست برآورده شده باشند.

بر این اساس، موارد زیر را به فایل .gitlab-ci.yml اضافه کنید.

. . .

variables:

  TAG_LATEST: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:latest

  TAG_COMMIT: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA

بخش variables متغیرهای محیطی‌ای را تعریف می‌کند که بعداً در بخش script هر وظیفه در دسترس خواهند بود. این متغیرها به صورت متغیرهای محیطی معمول لینوکس در دسترس قرار می‌‌گیرند. بنابراین می‌توانید با اضافه علامت دلار ($) در ابتدایشان، آنها را در اسکریپت استفاده کنید. بستر GitLab برخی متغیرهای پیش‌فرض را برای هر کدام از کارها ایجاد می‌کند که حاوی برخی اطلاعات خاص در مورد کار مربوطه هستند. در اینجا، دو متغیر محیطی از متغیرهای پیش‌فرض و از پیش تعریف شده را بررسی می‌کنیم.

  • CI_REGISTRY_IMAGE: بیانگر آدرس رجیستری کانتینر مطابق با پروژه خاص. این آدرس به برنامه GitLab بستگی خواهد داشت. به عنوان مثال، آدرس‌های رجیستری برای پروژه‌های com از الگوی registry.gitlab.com/your_user/your_project پیروی می‌کنند. ولی از آنجایی که خودِ بستر GitLab این متغیر را ارائه می‌کند، نیازی به آدرس دقیق آن نخواهید داشت.
  • CI_COMMIT_REF_NAME: رشته یا عنوان برچسب مربوط به پروژه‌ای که قرار است ساخته شود.
  • CI_COMMIT_SHORT_SHA: هشت کاراکتر اول commit revision پروژه.

هر دوی این متغیرها متشکل از متغیرهای «از پیش تعریف شده» هستند و برای برچسب‌زدن به داکر ایمیج استفاده می‌شوند.

TAG_LATEST آخرین برچسب را به ایمیج اضافه می‌کند. این یک استراتژی معمول برای ایجاد یک برچسب همیشگی برای شناساندن «آخرین نسخه» است. برای هر کدام از کارهای توسعه‌ای، آخرین ایمیج با آخرین داکر ایمیجی که به‌تازگی ساخته شده، در رجیستری کانتینر جایگزین می‌شود.

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

البته همان‌طور که در ادامه این مطلب خواهید دید، برگشت به مراحل قبلی Git مستقیماً می‌تواند در بستر GitLab صورت گیرد.

$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME نام پایه داکر ایمیج را مشخص می‌کند. مطابق اسناد راهنمای GitLab، عنوان یک داکر ایمیج باید از الگوی زیر پیروی کند.

image name scheme

&amp;amp;amp;lt;registry URL&amp;amp;amp;gt;/&amp;amp;amp;lt;namespace&amp;amp;amp;gt;/&amp;amp;amp;lt;project&amp;amp;amp;gt;/&amp;amp;amp;lt;image&amp;amp;amp;gt;

$CI_REGISTRY_IMAGE بیانگر بخش <registry URL>/<namespace>/<project> است و به صورت اجباری است. چرا که ریشه رجیستری پروژه محسوب می‌شود. $CI_COMMIT_REF_NAME اختیاری است، امّا می‌توانید برای میزبانی ایمیج‌های داکر برای بخش‌های مختلف بسیار مفید باشد. البته در این آموزش، تنها با یک بخش و شاخه شبکه کار می‌کنیم، ولی همیشه بهتر است که یک ساختار گسترش‌پذیر را درنظر بگیرید. در حالت کلی، سه سطح عنوان منبع ایمیج توسط بستر GitLab پشتیبانی می‌شود.

repository name levels

registry.example.com/group/project:some-tag

registry.example.com/group/project/image:latest

registry.example.com/group/project/my/image:rc1

برای متغیر TAG_COMMIT از گزینه دوم استفاده می‌کنید و این در حالی است که عبارت image با عنوان بخش (branch) جایگزین می‌شود.

سپس باید موارد زیر را به فایل .gitlab-ci.yml اضافه کنید.

. . .

publish:

  image: docker:latest

  stage: publish

  services:

    - docker:dind

  script:

    - docker build -t $TAG_COMMIT -t $TAG_LATEST .

    - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY

    - docker push $TAG_COMMIT

    - docker push $TAG_LATEST

بخش publish اولین وظیفه در تنظیمات CI/CD شما خواهد بود. بیایید جزئیات آ‌ن را با هم بررسی کنیم.

  • image داکر ایمیج مورد استفاده برای این کار است. GitLab runner یک کانتینر داکر برای هر کدام از وظایف تولید می‌کند و اسکریپت را درون این کانتینر اجرا خواهد کرد. گزینه docker:latest موجب اطمینان از دسترسی فرمان docker می‌شود.
  • stage وظیفه را به publish stage مرتبط می‌کند.
  • services سرویس Docker-in-Docker یا dind را مشخص می‌کند. به همین دلیل بود که قبلاً GitLab runner را در حالت ممتاز یا privileged ثبت کردیم.
  • بخش script از وظیفه publish مشخص می‌کند که فرمان‌های shell برای این وظیفه اجرا شوند. پس از اجرای این فرمان‌ها، دایرکتوری کاری به عنوان ریشه منبع تنظیم می‌شود.
  • docker build …: ساخت داکر ایمیج بر اساس Dockerfile و برچسب زدن آن مطابق آخرین تگ تعریف‌شده در بخش variables
  • docker login …: وارد کردن داکر به رجیستری کانتینر پروژه. در اینجا از متغیر پیش‌فرض $CI_BUILD_TOKEN به عنوان توکن تأییدیه ورودی استفاده می‌شود. بستر GitLab خودش توکن را تولید و برای مدت زمان انجام وظیفه در دسترس باقی می‌ماند.
  • docker push …: انتقال دو برچسب ایمیج به رجیستری کانتینر

در ادامه باید کار توسعه‌ای خود را به فایل .gitlab-ci.yml اضافه کنید.

. . .

deploy:

  image: alpine:latest

  stage: deploy

  tags:

    - deployment

  script:

    - chmod og= $ID_RSA

    - apk update &amp;amp;amp;amp;&amp;amp;amp;amp; apk add openssh-client

    - ssh -i $ID_RSA -o StrictHostKeyChecking=no [email protected]$SERVER_IP "docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY"

    - ssh -i $ID_RSA -o StrictHostKeyChecking=no [email protected]$SERVER_IP "docker pull $TAG_COMMIT"

    - ssh -i $ID_RSA -o StrictHostKeyChecking=no [email protected]$SERVER_IP "docker container rm -f my-app || true"

    - ssh -i $ID_RSA -o StrictHostKeyChecking=no [email protected]$SERVER_IP "docker run -d -p 80:80 --name my-app $TAG_COMMIT"

«آلپاین» یک توزیع سبک از لینوکس محسوب می‌شود و گزینه‌ای مناسب به عنوان یک داکر ایمیج در اینجاست. شما «وظیفه» را به deploy stage اختصاص می‌دهید. برچسب deployment باعث می‌شود که این وظیفه در runner‌های با این برچسب، نظیر آنچه در گام دوم تنظیم کردیم، اجرا گردد.

بخش script از وظیفه deploy با دو فرمان قابل‌تنظیم مطابق زیر آغاز می‌شود:

  • chmod og= $ID_RSA: فراخوانی تمام مجوّزها از group و سایر از طریق کلید خصوصی. در نتیجه، تنها «مالک» می‌تواند از آن استفاده کند. این یکی از موارد ضروری است؛ در غیر این صورت SSH با کلید خصوصی عمل نخواهد کرد.
  • apk update && apk add openssh-client: بروزرسانی مدیریت بسته آلپاین (apk) و نصب openssh-client که فرمان ssh را برای کاربر فراهم می‌کند.

چهار فرمان ssh در ادامه می‌آیند. الگوی هر کدام از آنها به صورت زیر است:

ssh connect pattern for all deployment commands

ssh -i $ID_RSA -o StrictHostKeyChecking=no [email protected]$SERVER_IP "command"

در هر کدام از عبارت‌های ssh شما فرمان را در سرور ریموت اجرا می‌کنید. برای این منظور، تأییدیه را با استفاده از کلید خصوصی خودتان انجام می‌دهید.

گزینه‌ها در این رابطه از قرار زیرند:

  • -i مخفف فایل شناسایی است و $ID_RSA متغیری در بستر GitLab است که شامل مسیر فایل کلید خصوصی می‌شود.
  • -o StrictHostKeyChecking=no برای اطمینان از عبور از پرسش، فارغ از میزان اعتماد شما به سرور میزبان ریموت است. این پرسش در هر صورت، نمی‌تواند در یک وضعیت غیرتعاملی و غیرهوشمند مانند شبکه یا pipeline پاسخ داده شود.
  • $SERVER_USER و $SERVER_IP همان متغیرهایی از Gitlab هستند که شما در گام پنجم ایجاد کرده بودید. این متغیرها هاست ریموت و ورود کاربر به اتصال SSH را مشخص می‌کنند.
  • command در سرور ریموت اجرا خواهد شد.

نهایتاً روند توسعه با اجرای چهار فرمان زیر در سرور شکل خواهد گرفت:

۱) docker login …: وارد کردن docker به رجیستری کانتینر.

۲) docker pull …: دریافت آخرین نسخه ایمیج از رجیستری کانتینر.

۳) docker container rm …: حذف کانتینر موجود. عبارت || true برای اطمینان از موفقیت‌‌آمیز بودن همیشگی کد exit است؛ حتی اگر هیچ کانتینری با نام my-app در حال اجرا نباشد.

۴)docker run …: شروع یک کانتینر جدید با استفاده از آخرین ایمیج در رجیستری. کانتینر در اینجا با نام my-app خواهد بود. پورت شماره 80 در هاست منحصر به پورت 80 کانتینر خواهد بود (ترتیب به صورت -p host:container). گزینه -d باعث می‌شود که کانتینر در حالت detached اجرا شود. در غیر این صورت، شبکه باید در هر صورت، منتظر خاتمه و نتیجه فرمان بماند تا بتواند کار خود را ادامه دهد.

نکته: با توجه به این موضوع که GitLab runner فرمان‌ها را در همان سرور خودش اجرا می‌کند، شاید استفاده از SSH برای اجرای این فرمان‌ها در سرور ریموت اندکی عجیب باشد. با این وجود، هنوز هم استفاده از GitLab runner در اینجا ضروری است. چرا که  runner فرمان‌ها را در یک کانتینر داکر اجرا می‌کند و بنابراین، شما در صورتی که فرمان‌ها را بدون SSH بخواهید اجرا کنید، کار توسعه را درون کانتینر به جای سرور انجام خواهدید داد.

شاید هم به عنوان یک راه‌حل جایگزین استفاده از docker به عنوان اجراکننده runner، استفاده از اجرا‌کننده shell برای اجرای این فرمان‌ها در هاست پیشنهاد شود. امّا باید توجه داشته باشید که این کار موجب محدودیت شبکه شما شده و خصوصاً اینکه، runner باید در همان سرور «توسعه» باشد. چنین کاری نمی‌تواند چندان منطقی و قابل‌اتکا باشد؛ چرا که ممکن بخواهید اپلیکیشن را به یک سرور دیگر انتقال دهید و از یک سرور runner متفاوت استفاده کنید. در هر کدام از این حالت‌ها، کاربرد SSH برای اجرای فرمان‌های توسعه منطقی خواهد بود.

کار را با اضافه کردن این مورد به وظیفه توسعه در فایل .gitlab-ci.yml ادامه می‌دهیم.

. . .

deploy:

. . .

  environment:

    name: production

    url: http://your_server_IP

  only:

- master

محیط‌های GitLab  به شما این امکان را می‌دهند که روند توسعه را درون بستر GitLab کنترل کنید. شما با رفتن به مسیر Operations > Environments در پروژه GitLab می‌توانید محیط‌ها را بررسی کنید. اگر هنوز کار شبکه به اتمام نرسیده باشد، هیچگونه محیطی در دسترس شما نخواهد بود. چرا که هنوز روند توسعه آغاز نشده است.

با تعریف بخش محیط برای یک وظیفه شبکه توسعه، GitLab در هر زمان که این وظیفه با موفقیت انجام می‌شود، یک deployment برای این محیط  تعریف می‌کند. در نتیجه امکان رهگیری تمام فعالیت‌های توسعه‌ای توسط بستر GitLab CI/CD وجود خواهد داشت.

همچنین یک دکمه re-deployment برای شما پیش‌بینی شده که می‌توانید در هر زمان به ورژن قبلی نرم‌افزار برگردید. همچنین آدرسی که در بخش  environment مشخص شده، در هنگام کلیک بر روی دکمه View deployment برایتان باز می‌شود.

در بخش only، عناوین شاخه‌ها و برچسب‌های وظیفه در حال اجرا تعریف می‌شوند. بستر GitLab به صورت پیش‌فرض برای هر انتقال به منبع، یک pipeline ایجاد کرده و تمام وظایف را اجرا می‌کند. بخش only گزینه‌ای در اختیار شماست که بتوانید اجرای وظیفه را برای برچسب‌ها یا شاخه‌های مشخص محدود کنید. به عنوان مثال، دراینجا می‌خواهیم که وظیفه deployment تنها برای شاخه master انجام پذیرد.

نکته: در اکتبر ۲۰۲۰، GitHub قاعده نامگذاری خود برای شاخه پیش‌فرض را از master به main تغییر داد. بر این اساس، سایر پروایدرها مانند GitLab و در کل، جامعه توسعه‌دهندگان شروع به پیروی از این رویکرد کردند. در هر صورت، از اصطلاح شاخه master برای عنوان شاخه پیش‌فرض در این آموزش استفاده شده و این در حالی است که ممکن است شما با یک نام متفاوت در این زمینه سر و کار داشته باشید.

فایل کامل .gitlab-ci.yml چیزی شبیه به زیر خواهد بود.

stages:

  - publish

  - deploy

variables:

  TAG_LATEST: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:latest

  TAG_COMMIT: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA

publish:

  image: docker:latest

  stage: publish

  services:

    - docker:dind

  script:

    - docker build -t $TAG_COMMIT -t $TAG_LATEST .

    - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY

    - docker push $TAG_COMMIT

    - docker push $TAG_LATEST

deploy:

  image: alpine:latest

  stage: deploy

  tags:

    - deployment

  script:

    - chmod og= $ID_RSA

    - apk update &amp;amp;amp;amp;&amp;amp;amp;amp; apk add openssh-client

    - ssh -i $ID_RSA -o StrictHostKeyChecking=no [email protected]$SERVER_IP "docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY"

    - ssh -i $ID_RSA -o StrictHostKeyChecking=no [email protected]$SERVER_IP "docker pull $TAG_COMMIT"

    - ssh -i $ID_RSA -o StrictHostKeyChecking=no [email protected]$SERVER_IP "docker container rm -f my-app || true"

    - ssh -i $ID_RSA -o StrictHostKeyChecking=no [email protected]$SERVER_IP "docker run -d -p 80:80 --name my-app $TAG_COMMIT"

  environment:

    name: production

    url: http://your_server_IP

  only:

    - master

نهایتاً با کلیک بر روی Commit changes در پایین صفحه GitLab، فایل .gitlab-ci.yml را ایجاد می‌کنید. البته به جای این کار می‌توانید در هنگام تهیه نسخه کلون و مشابه از منبه Git به صورت لوکال، فایل را به سرور ریموت منتقل نمایید.

گام ۷) تأیید فرآیند توسعه

در اینجا فرآیند توسعه را در نقاط مختلف شبکه GitLab و همین طور در سرور خودتان و در مرورگر تأیید خواهید کرد.

وقتی یک فایل .gitlab-ci.yml به منبع انتقال پیدا می‌کند، بستر GitLab به‌طور اتوماتیک آن را شناسایی کرده و یک شبکه CI/CD را شروع می‌کند. بنابراین، در زمانی که فایل .gitlab-ci.yml را ایجاد کردیم، GitLab اولین pipeline را آغاز کرده است.

به آدرس CI/CD > Pipelines در پروژه GitLab بروید تا وضعیت شبکه توسعه را مشاهده کنید. اگر وظایف هنوز در مرحله running/pending هستند، منتظر تکمیل آنها بمانید. در انتها یک گزینه Passed و دو علامت تیک سبز خواهید دید که نشانه به پایان رسیدن وظایف publish و deploy هستند.

نمای کلی شبکه توسعه با وضعیت خاتمه یافته

نمای کلی شبکه توسعه با وضعیت خاتمه یافته

حالا قصد داریم که شبکه توسعه را آزمایش کنیم. روی دکمه passed در ستون “Status” کلیک کنید تا صفحه نمای کلی شبکه توسعه برایتان باز شود. در اینجا، اطلاعات کلی شامل موارد زیر در اختیار شما قرار می‌گیرد.

  • زمان اجرای کلی شبکه توسعه
  • commit و شاخه مربوط به اجرای شبکه توسعه
  • درخواست‌های merge مربوطه. در صورتی که یک درخواست merge برای یک شاخه در وضعیت باز قرار داشته باشد، در اینجا برایتان نمایش داده می‌شود.
  • تمام وظایف اجرا شده در این شبکه و همین‌طور وضعیت آنها.

سپس روی دکمه deploy کلیک کنید تا صفحه نتایج کار توسعه برایتان باز شود.

صفحه نتایج کار توسعه

صفحه نتایج کار توسعه

در این صفحه می‌توانید خروجی اسکریپت وظیفه را مشاهده کنید. این صفحه همان جایی است که در هنگام عیب‌یابی شبکه توسعه باید به آن مراجعه کنید. در نوار کناری سمت راست، برچسب deployment را می‌بینید که به این وظیفه اضافه شده و در Deployment Runner نیز اجرا شده است.

اگر به سراغ بالای صفحه بروید، پیغام “This job is deployed to production” را می‌بینید. به این معنا که بستر GitLab متوجه فرآیند توسعه شده و این به خاطر بخش environment وظیفه است. روی لینک production کلیک کنید تا به محیط پروداکشن وارد شوید.

محیط پروداکشن در بستر GitLab

محیط پروداکشن در بستر GitLab

در اینجا، نمایی کلی از تمام فرآیندهای توسعه پروداکشن مشاهده می‌کنید. تا به اینجا تنها یک deployment انجام گرفته است. برای هر کدام از این فعالیت‌های توسعه‌ای، یک دکمه re-deploy در سمت راست درنظر گرفته می‌شود. با زدن این دکمه، تمام فعالیت توسعه‌ای دوباره در شبکه توسعه انجام می‌شود.

کارآیی عمل re-deployment بستگی به تنظیمات شبکه توسعه دارد؛  چرا که در این حالت، چیزی خارج از کار قبلی در شرایط یکسان انجام نمی‌شود. از آنجایی که از قبل برای استفاده یک داکر اینیج در کنار commit SHA به عنوان برچسب تنظیم کرده‌ایم، عمل re-deployment برای شبکه توسعه ما کارآیی خواهد داشت.

نکته: رجیستری کانتینر Gitlab ممکن است دارای یک قاعده تاریخ انقضا باشد. سیاست منقضی‌شدن باعث می‌شود که ایمیج‌ها و برچسب‌های قدیمی مرتباً از رجیستری کانتینر حذف شوند. در نتیجه، یک عمل توسعه‌ای که قدیمی‌تر از تاریخ انقضا باشد، در هنگام انجام دوباره و re-deploy ممکن است با شکست روبرو شود؛ به این دلیل که داکر ایمیج مربوط به این commit قبلاً از رجیستری حذف شده است. امکان مدیریت این سیاست تاریخ انقاض از مسیر Settings > CI/CD > Container Registry tag expiration policy برای شما وجود خواهد داشت. معمولاً بازه‌های تاریخ انقضا به صورت طولانی، مثلاً ۹۰ روز تنظیم می‌شوند. امّا در صورتی که بخواهید از ایمیجی که قبلاً به دلیل رسیدن تاریخ انقضا از رجیستری حذف شده، استفاده کنید، می‌توانید با اجرای دوباره وظیفه publish مربوط به pipeline، دوباره ایمیج مرتبط با commit را بسازید و به رجیستری منتقل نمایید.

روی دکمه View deployment کلیک کنید. در نتیجه، آدرس http://your_server_IP در یک مرورگر برای شما باز می شود. در اینجا با تیتر “My Personal Website” روبرو می‌شوید.

حالا می‌خواهیم کانتینر به کار گرفته شده در سرور را بررسی کنیم. به ترمینال رفته و حتماً دقت کنید که دوباره به سرور وارد شوید.

ssh [email protected]_server_IP

حالا یک لیست از کانتینرهای در حال اجرا تهیه کنید.

docker container ls

در نتیجه، کانتینر  my-app برایتان لیست می‌شود.

CONTAINER ID        IMAGE                                                          COMMAND                  CREATED             STATUS              PORTS                NAMES

5b64df4b37f8        registry.your_gitlab.com/your_gitlab_user/your_project/master:your_commit_sha   "nginx -g 'daemon of…"   4 hours ago         Up 4 hours          0.0.0.0:80-&amp;amp;amp;gt;80/tcp   my-app

برای اطلاعات بیشتر درباره مدیریت کانتینرهای داکر می‌توانید به مطلب دیگر وبلاگ آریانت در این رابطه سر بزنید.

گام ۸) بازگشت از یک توسعه یا Deployment

در ادامه، صفحه وب را بروزرسانی می‌کنیم. در نتیجه، یک فعالیت توسعه‌ای جدید ایجاد می‌کنیم و دوباره با استفاده از محیط‌های Gitlab، توسعه قبلی را دوباره انجام می‌دهیم و یا در اصطلاح re-deploy می‌کنیم. در هنگام بروز مشکل و بازگشت از یک فعالیت توسعه‌ای نیز همین رویکرد را استفاده خواهیم کرد.

در ابتدا، یک سری تغییرات کوچک در فایل index.html موردنیاز خواهد بود.

۱) در بستر GitLab به سراغ صفحه نمای کلی پروژه یا Project overview بروید و فایل index.html را باز کنید.

۲) بر روی دکمه Edit کلیک کنید تا ویرایشگر آنلاین برایتان باز شود.

۳) محتوای فایل را به صورت زیر تغییر دهید.

</pre>
<html>

<body>

<h1>My Enhanced Personal Website</h1>

</body>

</html>
<pre>

اکنون با کلیک بر روی گزینه “Commit changes” در پایین صفحه، تغییرات را ذخیره کنید.

در نتیجه، یک pipeline جدید برای بکارگیری تغییرات ایجاد خواهد شد. حالا در GitLab به مسیر CI/CD > Pipelines بروید. وقتی pipeline  تکمیل شد، می‌توانید در مرورگر به آدرس http://your_server_IP بروید و صفحه وب بروزرسانی‌شده را ببینید. در اینجا مشاهده می‌کنید که پیغام “My Enhanced Personal Website” به جای “My Personal Website” نمایش داده می‌شود.

اگر به مسیر Operations > Environments > production بروید، می‌توانید فعالیت توسعه‌ای جدید را ببینید. حالا روی دکمه re-deploy برای فعالیت توسعه اولیه و قدیمی‌تر کلیک کنید.

دکمه re-deploy برای فعالیت توسعه اولیه و قدیمی‌تر

دکمه re-deploy برای فعالیت توسعه اولیه و قدیمی‌تر

برای تأیید در پیغام ظاهر شده، روی دکمه Rollback کلیک کنید.

در نتیجه، وظیفه توسعه pipeline قدیمی‌تر دوباره راه‌اندازی شده و صفحه نمای کلی وظیفه هدایت می‌شوید. باید منتظر شوید تا این وظیفه به اتمام برسد و سپس آ‌درس http://your_server_IP را در یک مرورگر باز کنید. در این حالت، دوباره تیتر اولیه My Personal Website را مشاهده خواهید کرد.

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

جمع‌بندی

در این آموزش، یک شبکه یا pipeline توسعه مداوم را در بستر GitLab CI/CD تنظیم کردیم. برای این منظور، یک پروژه وب کوچک متشکل از یک فایل اچتمال و یک Dockerfile ایجاد شد. سپس تنظیمات شبکه توسعه .gitlab-ci.yml به صورت زیر انجام شدند:

  • ساخت یک داکر ایمیج
  • انتقال داکر ایمیج به رجیستری کانتینر
  • ورود به سرور، انتقال آخرین نسخه ایمیج، توقف کانتینر کنونی و شروع یک کانتینر جدید.

به علاوه، یک فعال توسعه‌ای را در Gitlab و در سرور به تأیید رساندیم. همچنین، یک توسعه جدید ایجاد کردیم و دوباره با استفاده از محیط‌های GitLab، به مرحله توسعه قبلی برگشتیم. این موضوع، نحوه برخورد با شکست در مراحل توسعه را به ما نشان داد.

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