بروزرسانی پروژه با CI/CD - قسمت اول

یکی از مشکلاتی که خیلی از ماها، مخصوصا frontend Developer ها باهاش درگیریم، زمانی هست که میخوایم یه ورژن جدیدی از اپلیکیشن رو ریلیز کنیم. روال متداول و سنتی قضیه، اینه که شما در تنظیمات وب سرورتون (Node, Nginx, Apache, ...) یه فولدر رو اختصاص میدید به پروژه ی فرانت اندتون، هر زمانی که خواستید ورژن جدیدی رو ریلیز کنید، پروژه تون رو روی سرور آپلود میکنید و فایل های بیلد شده رو منتقل می کنید روی اون فولدر.

این مساله چند تا عیب داره. اولیش اینه که شما به عنوان دولوپر سمت Client شاید دسترسی مستقیم به سرور نداشته باشید و هر سری بروزرسانی، باید توسط SysAdmin یا امثالهم صورت بگیره. مساله دیگه باز نبودن دستتون برای اعمال تغییرات روی وب سرور هست، مثلا شما اجازه ندارید تنظیمات جدیدی برای Caching روی Nginx انجام بدید، هرچند که فقط مربوط به پروژه ی خودتونه، ولی دسترسی میخواد که شما ندارید. و یک سری مشکلات روتینی که همه تون بهش برخوردید.

خب، ببینیم چجوری میشه از شر این قضیه راحت شد.

گام اول برای رهایی از این داستان، Dockerize کردن پروژه تون هست. اما قبلش بگم، من نمی خوام داکر توضیح بدم یا از اول بگم داکر چطوری کار میکنه. فقط در حدی که یه Frontend Developer باید آشنا باشه، توضیح میدم.

حالا بزارین خیلی ساده بگم Docker چیکار میکنه. شما وقتی پروژه تون رو با استفاده از Docker بیلد و ران میکنید، دارید یه محیط ایزوله ی جدید که مثل یه ماشین کامل عمل میکنه، میسازید. فرض کنید روی nginx سرور، پورت ۸۰۸۰ به پروژه ی شما اختصاص پیدا کرده. مدیر سیستم، روی دامنه (ساب دامنه ای) که اپ شما روی اون Serve میشه، یک Proxy Path مینویسه که همه ی درخواست ها ارسال میشه به پورت ۸۰۸۰. از اینجا به بعد کافیه شما به این پورت Listen کنید و پروژه تون رو براش Serve کنید.

وقتی دارین از Docker استفاده می کنید، کافیه پروژه تون رو بیلد کنید و روی پورت ۸۰۸۰ ران کنید. از این به بعد تمام درخواست هایی که به این پورت میرسه، توسط داکر قابل دریافته.

خب بعدش چی میشه؟

از اینجا به بعد داکر کاملا مثل یه ماشین مجازی با پروژه ی شما برخورد میکنه. هر Image داکر، یه ماشین مجازیه که می تونید به صورت مستقل برای تنظیمات ست کنید. مثلا میتونید برای خودتون Nginx مستقل بنویسید، می تونید روی این ماشین Module هایی که می خواید رو نصب کنید. خلاصه آزادی عمل دارین.

حالا بزارین یه نمونه از یه Dockerfile رو با هم ببینیم و خط به خط با هم بریم جلو:

FROM node:10.16.0 as build

WORKDIR /app
COPY ./ /app/

RUN yarn install
RUN yarn build

FROM nginx:alpine
COPY --from=build /app/build/ /usr/share/nginx/html/
ADD ./deploy/nginx-default.conf /etc/nginx/conf.d/default.conf

ابتدای ماجرا، Node ورژن 10.16.0 رو به عنوان سورس برای نصب Node Module ها معرفی می کنیم. در خط بعد، فولدر /app رو به عنوان فولدر اصلی، ساخت پروژه در Docker معرفی میکنیم و میگیم میخوایم کارامون رو توی این فولدر انجام بدیم.
دستور COPY ، هر فایلی رو که بگید، از فایل های لوکال روی سیستمتون، به Docker منتقل میکنه. مثلا ما اینجا نوشتیم که تمام فایل های روت پروژه رو به فولدر /app/ که برای خودمون ساخته بودیم، منتقل کنه.

حالا که فایل های خام پروژه رو منتقل کردیم، برای راه اندازی پروژه ، باید Dependency ها رو راه بندازیم. برای این کار،‌ باید yarn install بزنیم.
بعد از اینکه ماژول هامون نصب شد، دستور build رو میزنیم که از پروژه مون build production گرفته بشه.

وقتی کار بیلد گرفتن پروژه تموم بشه، اگر در محیط داکر هم نباشیم، فایل های بیلد شده رو به آدرسی که در nginx تعریف کردیم، منتقل میکنیم، اینجا هم به همین صورت:
FROM nginx:alpine
یعنی می خوایم از یه ورژن light برای nginx استفاده کنیم. در خط بعد،‌ میگیم فایل های بیلد شده که الان در فولدر /app/build/ روی داکر هستن رو به فولدر /usr/share/nginx/html/ که همون روت nginx هست منتقل بشه.
یک مساله ی اساسی که باقی میمونه اینه که هر nginx ای نیاز به یه سری تنظیمات کاستوم داره که شما باید در داکر اون رو ست کنید. این تنظیمات در لینوکس در فایل /etc/nginx/conf.d/default.conf قرار داره. خب شما میتونید یه فایل conf به صورت کاستوم در پروژه ی خودتون درست کنید. مثل:

server {
    listen 80 default_server;
    root /usr/share/nginx/html;
    server_name  _;
    server_tokens off;
    charset utf-8;
    index index.html index.htm;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    client_max_body_size 20M;


    location / {
        index index.html index.htm;
        try_files $uri $uri/ /index.html =404;
        include mime.types;
        default_type application/octet-stream;

        gzip_static on;
        gzip on;
        gzip_vary on;
        gzip_min_length 0;
        gzip_proxied expired no-cache no-store private auth;
        gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
        gzip_disable &quotMSIE [1-6]\.&quot
    }

    location ~ /assets/fonts/ {
        root /usr/share/nginx/html/assets/fonts/;
    }

    location ~ /\. {
        deny all;
    }


    location ~* \.(?:html)$ {
        sendfile off;
        add_header Last-Modified $date_gmt;
        add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
        if_modified_since off;
        expires off;
        etag off;
    }

    location ~ \.html$ {
        sendfile off;
        add_header Last-Modified $date_gmt;
        add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
        if_modified_since off;
        expires off;
        etag off;
    }

    location ~* \.(?:ico|gif|jpg|jpeg|png|woff|woff2|ttf|js)$ {
        if_modified_since off;
        gzip_static on;
        expires 365d;
        add_header Pragma public;
        add_header Cache-Control &quotpublic&quot
        access_log off;
    }
}

و در یه فولدری در پروژه تون به آدرس/deploy/nginx-default.conf ذخیره کنید. در پایان می تونید همین تنظیمات رو با دستور ADD ./deploy/nginx-default.conf /etc/nginx/conf.d/default.conf به عنوان تنظیمات nginx در داکر ست کنید.

در نظر داشته باشید که اگر محتوایی که برای docker توضیح دادم رو در فایل ای با اسم Dockerfile ذخیره کنید، به عنوان فایل پیشفرض شناخته میشه و در دستور بیلدتون نیاز نیست اسم فایل رو ذکر کنید.

خب حالا که محتوای Dockerfile آماده شد، باید دستور بیلدتون رو وارد کنید:

docker build . -t <image_name>

به جای <image_name> اسمی که برای image داکر خودتون میخواید بزارید رو بنویسید. مثلا:

docker build . -t javad_production

بعد از اینکه این دستور رو زدین، بیلد شما شروع میشه. در نظر داشته باشید که تحریم باعث میشه همون اول نتونید یه سری ماژول ها رو دانلود کنید، پس حتما از یه چیزی مثل shecan استفاده کنید.

بعد از اینکه بیلدتون تموم شد دستور docker images رو وارد کنید. یه لیست از ایمیج هایی که ساخته شده رو بهتون نشون میده که اگه بیلدتون موفقیت آمیز باشه، javad_production هم درش هست.

حالا برای اجرا کردن این image، دستور

docker run -it -p 8080:80 javad_production

رو وارد کنید. با -p پورتی که میخواید image روی اون ران بشه رو معرفی میکنید. اول پورتی که در سرور برای پروژه ی شما تعریف شده (۸۰۸۰) و بعد از : ، پورتی که در داخل داکر پروژه ی خودتون رو ران کردین که اینجا فرض کردیم اون پورت ۸۰ هست. در انتها هم اسم image.

این یه مقدمه برای اینکه بدونیم CI/CD با استفاده از داکر قراره چطور کار کنه. در قسمت بعد اون رو کامل توضیح میدم

شب خوش...