راه اندازی Gitlab CI/CD از صفر تا صد

این روز ها با داغ شدن فضای startup ها خیلی از developer ها دور هم جمع شدند و دارند روی یک ایده کار میکنند تا در نهایت به یک اپلیکیشن برسند . یکی از معضلاتی که سال ها پیش برای developer هایی که به صورت تیمی کار می کردند وجود داشت این بود که مدیریت source پروژه کار بسیار سختی بود و حتی source ها رو با flash جا به جا میکردند اما با بالا رفتن سرعت اینترنت و رشد تکنولوژی ، source control ها مطرح شدند و همون طور که امروز نگاه میکنیم سایت هایی مثل Gitlab و Github این روزها با استفاده از version control گیت (git) به راحتی این مشکل رو برای همه حل کردند ، مخصوصا این روز ها که Github هم با اضافه کردن امکان ساخت private repository برای کاربران غیر premium فضای کار رو رقابتی تر کرده و developer ها در هرجای دنیا میتونن بدون هیچ مشکلی source ها رو در اختیار داشته باشن و روشون کار کنند بدون اینکه هیچ نگرانی وجود داشته باشه.اما یکی دیگه از مشکلات تیم ها روند نسخه گذاری هست که یه جورایی کار روتین و وقت گیری محسوب میشه . اینجاست که کمپانی های بزرگ به این فکر افتادن که چجوری میشه روال نسخه گذاری رو راحت تر کرد ، که اینجا نقطه تولد Continuous Integration and Continuous Delivery یا همون CI/CD هست.در واقع CI/CD میاد source و روال build شما رو با هم integrate می کنه و در نهایت اون رو به محیط production شما deliver می کنهبه زبان ساده اگر بخوام بگم CI/CD روال نسخه گذاری رو automatic میکنه . فرض کنید اپلیکیشنی دارید که به تازگی بالا اومده و یک سری مشتری دارن باهاش کار میکنند . حالا اگه روال نسخه گذاریتون وقت گیر باشه و یه باگی هم توی سیستم به وجود بیاد ، چقدر روال توسعه و پشتیبانی سیستم برای رفع مشکلات به وجود آمده میتونه ترسناک باشهیا این طور در نظر بگیرید که اون نفری از تیمتون که عملیات نسخه گذاری رو بلد بوده و همیشه انجام میداده نیست و شما هم بهش دسترسی ندارید حالا باید چی کار کنیم؟با استفاده از CI/CD به راحتی میتونید این کار رو انجام بدید . فرض کنید مشکل رو درست کنید و commit کنید و تنها برای نسخه گذاری ، داخل source control خودتون login کنید و یک دکمه رو فشار بدید و چند دقیقه بعدش اپلیکیشنتون با آخرین تغییرات روی سرور آماده بهره برداری باشهتوی این نوشته من نحوه راه اندازی gitlab Ci/CD ، روی یک پروژه جاوایی رو آموزش میدم و امیدوارم که بتونید ازش استفاده کنیدتوجه : برای متوجه شدن مفاهیم این متن ، آشنایی با داکر و مفاهیم آن الزامی است و برای کمک میتونید به این پست مراجعه کنیدآشنایی با Docker از نصب تا راه اندازیبا یک مثال سعی میکنم مفاهیم رو شفاف تر توضیح بدم تا بهتر متوجه بشید.مثالمن یک اپلیکیشن جاوایی دارم که در نهایت قراره یک war فایل بشه . پس اولین قدم ما ، ساختن یک image داکری از روی war فالمون محسوب میشه.قطعا اپلیکیشن ما یک دیتابیس داره که به کمکش داریم اطلاعات مورد نیازمون رو در اون ذخیره میکنیم. من توی این مثال از دیتابیس mysql استفاده میکنم ، پس قدم بعدی ساخت یک image از دیتابیس محسوب میشه که این بخش هم کار راحتی هست و خود mysql توی dockerhub ایمیج و نحوه استفاده اش رو قرار داده که لینک اش رو اینجا قرار میدمایمیج mysqlحالا برای اجرا کردن این image های ساخته شده نیازمند یک فایل docker-compose.yml هستیم که بتونیم داخل اون image های اپلیکیشن و دیتابیسی که ساختیم رو اجرا کنیم.بهتر این است که خود mysql هم توی یک mysql-compse.yml ای باشه به صورت جداگانه و توی docker-compose اصلی مون docker-compose مربوط به mysql رو extend کنیم ، که در ادامه نحوه این کار رو توضیح میدم.پس همون طور که گفتم دو تا container خواهیم داشت یکی برای application اصلی و یکی هم برای mysqlحالا اینطور در نظر بگیرید که اگر به دنبال build کردن اپ خودتون باشید ، قطعا باید آخرین نسخه اپلیکیشن رو از سورس کنترل خودمون که کدها رو اونجا کامیت کردیم ، دریافت کنیم.پس در ادامه به این شکل میریم جلو که شما از gitlab به عنوان source control استفاده میکنید و یه سرور cloud یا vps هم گرفتید که میخواین اپکلیکیشن خودتون رو اونجا اجرا کنید.اگر بخوام دقیق تر بگم باید دستور docker-compose up -d که از روی یک image یک container میسازده و اون رو start میکنه باید توی سرورمون اجرا بشه . پس اولین سوالی که مطرح میشه اینه که سرورمون قراره این ایمیج رو از کجا بیاره ؟ در واقع همون طور که وقتی میخوایم یه داکر ایمیج از روی mysql بسازیم ، اون رو از dockerhub میگیریم یا اصطلاحا pull کنیم. اینجا هم باید بپذیریم که image مربوط به اپلیکیشن ما باید یه جایی push بشه تا ما روی سرورمون بتونیم اون رو pull کنیم و اجراش کنیم.توضیحات بالا سناریو کلی بود که برای انجام automatic نسخه گذاری باید انجام بدیمبرمیگردیم به gitlab که source countrol ماست . gitlab میگه که من میتونم به شما CI/CD ارائه بدم و در قالب یک سری stage بیام source شما رو بخونم و بسته به نوع پروژه ، مثلا اگر جاوایی هست با maven بیلد رو انجام بدم .خود گیت لب برای اینکه بتونه این کار رو انجام بده داره از مکانیزم داکر استفاده میکنه ، یعنی داخل خودش یک سری container های داکری رو داره اجرا میکنه پس برای اینکار از یک فایل به اسم 'gitlab-ci.yml.' استفاده میکنه که در اون stage های مختلف رو میخونه و اجرا میکنه .این stage ها در واقع مراحل انجام کار هستند مثلا اولین مرحله build کردن ، دومین مرحله ساخت image و سومین مرحله اجرا ، روی سرور ما است.مکانیزم گیت لب به این شکل هست که وقتی یک stage اجرا میشه و complete میشه همه چیزش پاک میشه یعنی یه جورایی هر stage یک temp محسوب میشه. که در ادامه میگم که این به چه معنی هست و برای ارتباط برقرار کردن بین این stage ها و دل و بدل کردن دیتا چه کار هایی رو باید انجام بدیم.پایپ لاین ها (Pipelines)یک pipeline مجموعه ای از job هایی هست که در قالب چندین stage اجرا میشن و از طریق منو pipeline قابل دسترسی هستند. اگر تمامی job ها با موفقیت اجرا بشن ، pipeline به stage بعدی میره اما اگه یکی از job ها fail بشه ، pipeline همونجا متوقف میشه در عکس زیر عکس یک pipeline رو مشاهده میکنید که متشکل از چهار stage هست که هر کدوم حداقل یکی یا بیش از یک job دارنلایبراریاگر library ندارید لازم به خوندن این بخش نیستخب بزارین قبل شروع ساختار پروژه رو باز کنم . فرض کنید که ما یک library داریم که یک پروژه maven ای هست و کانفیگ های اصلی پروژمون و یک سری مدل های base که ممکنه توی تمامی اپ هامون مشترک باشه رو اونجا قرار دادیم . مثل تنظیمات spring boot و spring security و مدل هایی مثل user و …خب تا قبل از این چی کار میکردیم ؟لایبراریمون رو با maven goal زیر اجرا میکردیم تا بره و توی پوشه m2. قرار بگیره و اون رو به عنوان یک dependency به فایل pom.xml پروژه مون اضافه میکردیم تا ازش استفاده کنیم.clean package installاما حالا که قراره تمام این کار ها رو gitlab برامون انجام بده پس باید لایبراریمون رو توی یک maven repository که به صورت private باشه قرار بدیم تا gitlab هم بتونه به اون دسترسی داشته باشه.پس توی پروژه لایبراریمون یه فایل gitlab-ci.yml. داریم که تنها یک stage داره و اون هم build هست و قراره که وقتی تغییری روی library انجام میدیم و کامیت میکنیم ، gitlab اون رو build کنه و روی mymavenrepo که یک maven repository شخصی به ما ارائه میده deploy کنه.پس میریم توی mymavenrepo یک اکانت درست میکنم. برای اینکه بتونیم توی یه repository عملیات deploy انجام بدیم باید یه url و یک user,pass داشته باشیم تا ازش استفاده کنیمحالا برای بیلد کردن پروژه نیازمند maven هستم و اینجا هم base image من maven ای هست و دستورات maven رو میشناسه . حالا میخوام deploy کنم ولی سوال اینجاست که به کدوم آدرس؟یا باید برم داخل pom.xml بزارم یا توی settings.xml مربوط به m2 توی mavenاما بحث اینجاست که m2 باید توی محیط اجرا باشه ( یعنی توی سرورم ) پس میتونم کنار source خودم یه directory بسازم به اسم m2. و به maven از طریق settings.xml میگم که دایرکتوری maven من اینجاست . در واقع من آدرس default رو تغییر میدم به این دایرکتوی که تو روت پروژه ام ساختم.Settings.xml<settings> <profiles> <profile> <id>myMavenRepoRead</id> <activation> <property> <name>!doNotUseMyMavenRepo</name> </property> </activation> <properties> <myMavenRepoReadUrl>https://mymavenrepo.com/repo/sampleUrl/</myMavenRepoReadUrl> </properties> </profile> <profile> <id>myMavenRepoWrite</id> <activation> <property> <name>!doNotUseMyMavenRepo</name> </property> </activation> <properties> <myMavenRepoWriteUrl>https://mymavenrepo.com/repo/sampleUrl/</myMavenRepoWriteUrl> </properties> </profile> </profiles> <servers> <server> <id>myMavenRepoRead</id> <username>test</username> <password>test</password> </server> <server> <id>myMavenRepoWrite</id> <username>test</username> <password>test</password> </server> </servers> </settings>حالا برای اجرا کردن دستورات maven یا باید بریم اون رو توی ماشینمون نصب و config کنیم ، یا میتونیم دو تا فایل mvnw.sh و bat رو به root پروژه اضافه کنیم.کار این دو فایل این هست که دستورات maven رو میشناسند ، پس این دو تا فایل هم به root پروژه library اضافه میکنیم که این نگرانی رو نداشته باشیم که توی ماشینمون maven نصب هست یا نهخب پس حالا باید library رو بیلد کنیم که در نهایت میشه یه jar file و اون رو توی mavenrepo که بالاتر گفتم قرار میده و میتونیم ازش استفاده کنیم.این هم کد مربوط به gitlab-ci.yml. فریم ورک.gitlab-ci.ymlimage: maven:latest variables: MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode" MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" cache: paths: - .m2/repository/ # - target/ build: stage: build when: manual script: - mvn $MAVEN_CLI_OPTS deploy وقتی when:manual رو مینویسیم این قابلیت رو به شما میده که برید داخل gitlab و از منوی CI/CD>Pipelines خودتون به صورت دستی دکمه استارت رو بزنید ، که توی عکس زیر مشخصهخب الان مرحله library تموم شد پس میرسیم به اپلیکیشن خودمون . خب قطعا توی اولین مرحله build به dependency لایبراری که ساختیم ، نیاز داریم ، پس باید بهش بفهمونم که باید از کجا dependency پروژه رو بگیرهپس توی پروژه مون دوباره دایرکتوری m2. و settings.xml رو قرار میدم تا متوجه بشه که از کجا باید dependency رو دانلود کنه.بخش مربوط به library تموم شد میتونید از اینجا به بعد رو ادامه بدیداستیج هاحالا برمیگردیم روی اپ خودمون ، که در اینجا ما قرار بود ۳ تا stage داشته باشیم BuildBuild docker imageDeployیه نکته ای که باید بدونیم اینه که gitlab به ازای هر کدوم از این stage ها یک container میسازه ، یعنی توی مثال ما ، ۳ تا container داریم که به ترتیب اجرا میشن.همونطور که بالا تر گفتم هر کدوم از این container های داکری وقتی کارشون تموم میشه dispose میشن و ما دیگه بهشون دسترسی نداریم.پس سوال اصلی اینجاست که ما توی stage اول که داریم پروژه رو build میکنیم و به یک war file میرسیم و بعدش قراره توی stage دوم از روش یه docker image بسازیم ، چجوری قراره توی stage دوم از war فایلی که توی stage اول ساخته شده استفاده کنیم ؟ اون که dispose شده ؟؟؟اینجا gitlab از یک مفهوم به اسم artifact استفاده میکنه و میگه اگر قراره بین این stage ها فایلی رد و بدل بشه باید از طریق artifact به من بگی نحوه کارش اینجوری هست که gitlab برای خودش یک دایرکتوری داره که artifact ها رو به ازای jobId ها store میکنه و قبل از اینکه کار stage تموم بشه توی log میبینید که میزنه upload artifact و بعد از success شدن stage میبیند که وقتی میخواد stage بعدی رو شروع کنه اول میزنه download artifact و بعد ادامه مراحل رو پیش میگیره.استیج اول (build)بخش اول فایل gitlab-ci.yml..gitlab-ci.ymlimage: openjdk:8-jdk-alpine variables: DOCKER_HOST: tcp://docker:2375 MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode" MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" services: - docker:dind stages: - build - build-image - deploy-ssh build: stage: build when: manual before_script: - apk update - apk add --nocache git - apk add --update nodejs nodejs-npm && node -v - apk add maven && mvn --version - npm install -g yarn - yarn --version - npm install -g @angular/cli@1.7.3 script: - './mvnw $MAVEN_CLI_OPTS package ' only: - master artifacts: expire_in: 10 mins 0 sec paths: - target/app.war - target/Dockerfile خب من از openjdk alpine به عنوان base image استفاده کردم که خودش یک لینوکس light weight داره و jdk روش کانفیگ شده که دستور java -jar رو برای اجرای پروژه بشناسهتوی بخش variables متغیر های مربوط به maven و docker host رو اضافه کردم که بسته به نوع تکنولوژی ها و زبان برنامه نویسی که استفاده میکنید این کانفیگ ها میتونه متفاوت باشهتوی بخش services گفتم که از سرویس docker in docker استفاده کنه چون در حقیقت این container هایی که داره توسط gitlab ساخته میشه باید وقتی اجرا میشن و میان بالا به صورت پیشفرض docker داخل container اش نصب باشه تا بتونن دستوراتی مثل docker-compose up رو اجرا کنندتوی بخش stages ابتدا مشخص میکنیم که چند تا stage داریم که در اینجا ۳ تا هست و بعد به ترتیب اسم stage ها رو میگیم و مشخص میکنیم که توی هر stage باید چه کاری رو انجام بده.خب در ابتدا stage : build رو توضیح میدم.توی قسمت beforeScripts میتونم بگم که قبل از اینکه script من رو اجرا کنی یه سری کار ها رو انجام بده که در اینجا من node رو update کردم و yarn رو نصب کردم چون فریم ورک UI که استفاده میکردم angular بودهتوی بخش script گفتم که دستور maven build رو اجرا کنه تا war فایل پروژه ام ساخته بشه توی قسمت only گفتم که دستور رو روی کدوم branch ای که روی گیت لب دارم اجرا کنهتوی قسمت artifacts توی بخش expire_in گفتم که حداکثر زمانی که این container اجازه داره زنده بمونه چقدرهو در نهایت توی قسمت paths بهش گفتم که دو فایل app.war و Dockerfile رو از مسیر های که مشخص کردم بردار و به عنوان artifact به stage بعدی بفرستاستیج دوم (build-image)حالا وقتشه که از war file ای که ساختیم یک docker image بسازیم شاید بپرسید که چرا باید این کار رو بکنیم در جواب چرا باید بگم که راحتی کار کردن با dockerبزارید با یه مثال بهتر توضیح بدم فرض کنید که یه سرور لینوکسی گرفتین و میخواین روش یه war file رو اجرا کنید. اولین کار به روش سنتی اینه که روی لینوکس jdk نصب کنید بعد براش java home ست کنید و کلی داستانای دیگه یا مثلا قراره یه mysql نصب کنید که کار طاقت فرسایی محسوب میشه.اما با داکر شما فقط کافیه که روی سرورتون یک بار داکر رو نصب کنید و دیگه همه این ها رو با دستورات کوتاهی از docker hub بگیرید (pull کنید) و استفاده کنید خب در مرحله بعد سوال اینجاست که image ای که از روی war فایل من ساخته میشه باید کجا آپلود بشه تا توی سرورم بتونم اون رو pull کنم و از روش یه container بسازم و اجراش کنم؟برای اینکار خود gitlab دارای یک docker registery مثل docker hub هست و image شما رو اونجا آپلود میکنه و توسط یک آدرس و user/pass گیت لب با اون ارتباط برقرار میکنید و اون رو pull میکنیدخب برای ساخت image از war file پروژه اول نیاز یه یک DockerFile هستDockerfileFROM openjdk:8-jre-alpine ENV SPRING_OUTPUT_ANSI_ENABLED=ALWAYS \ JHIPSTER_SLEEP=0 \ JAVA_OPTS="" ADD *.war /app.war EXPOSE 8080 CMD echo "The application will start in ${JHIPSTER_SLEEP}s..." && \ sleep ${JHIPSTER_SLEEP} && \ java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar /app.war توی این فایل گفتم که base image من openjdk هست و یه سری متغیر داشتم که توی ENV مشخص کردم که میخواستم توی اجرا این متغیر ها رو بشناسهتوی add گفتم که war file من رو کپی کن توی root ایمیجی که داری میسازی و port 8080 رو هم باز کن برام و در نهایت این war فایل رو با دستور java -jar اجرا کنبعد از اینکه image ما ساخته شد برای اینکه بتونیم اجراش کنیم نیاز به یه فایل docker-compose.yml داریم Mysql.ymlversion: '2' services: mysql: image: mysql:5.7.22 volumes: - /Users/hostDirectoryUrl/:/var/lib/mysql/ environment: - MYSQL_ROOT_PASSWORD=root - MYSQL_DATABASE=testDB ports: - 3306:3306 command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8 --explicit_defaults_for_timestampاینجا گفتیم که base image ما mysql هست و توی volume گفتیم که آدرس var/lib/mysql که دیتا هامون داره اونجا ذخیره میشه رو توی سرورمون به یه آدرسی volume کنه تا هر بار که container ما stop میشه به هر دلیلی ، دیتا های DB رو از دست ندیم و وقتی دوباره container رو start میکنیم ، دیتا ها سر جای خودش باشهتوی environment رمز عبور دیتابیس رو دادم و گفتم که وقتی یک container میسازی از این دیتابیس ، یک دیتابیس به اسم testDB هم توش بسازتوی قسمت port گفتم که پورت default دیتابیس که 3306 هست رو به پورت 3306 توی سرور من (هاست من) map کن تا از این طریق به دیتابیس ام دسترسی داشته باشمو در نهایت یک سری تنظیمات utf-8 و اینکه نام جداول با نام کوچیک باشه و …docker-compose.ymlversion: '2' services: myApplication: image: registry.gitlab.com/testProject/testApp:latest environment: - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/testDB?useUnicode=yes&characterEncoding=UTF-8&characterSetResults=UTF-8&useSSL=false - JHIPSTER_SLEEP=120 # gives time for the database to boot before the application ports: - 80:8087 mysql: extends: file: mysql.yml service: mysqlاینجا توی docker-compose اصلی هم گفتم که base image من اون image ای هست که بالاتر ساختیم و توی gitlab-registery آپلودش کردیم.توی environments مهم ترین متغیری که استفاده شده spring_datasource_url هست که درواقع کارش اینه که کانتینر mysql رو که بالا ساختیم رو به کانتینر app ما وصل میکنه و اجازه میده که app ما توی پورت 3306 بتونه به دیتابیس ما دسترسی داشته باشه . در واقع یه راه ارتباطی بین کانتینر دیتابیس و کانتینر اپلیکیشن ایجاد میکنهبرمیگردیم به تنظیمات stage دومbuild-image: image: docker:latest stage: build-image when: manual script: - cd target - docker login registry.gitlab.com -u "yourGitlabUsername" -p "yourPrivateKey" - docker build --pull -t registry.gitlab.com/testProject/testApp:latest . - docker push registry.gitlab.com/testProject/testApp:latest only: - master artifacts: expire_in: 10 mins 0 sec paths: - target/app.war - target/Dockerfile اینجا گفتیم که توی استیج build-image اول برو به مسیر targetبعد لاگین کن توی gitlab registery با username و key که gitlab در اختیارتون میزاره برای گرفتن این token روی اکانتتون توی gitlab کلیک کنید و برید توی settings و بعد به قسمت access tokens برید و برای خودتون یه token جدید بسازید در نهایت یه کدی بهتون میده که ازش میتونید اینجا استفاده کنیدلینک راهنما برای ساخت private keyبعد image رو میسازه و درنهایت push میکنه توی gitlab registery و دوباره دو تا فایل war , Dockerfile رو میفرسته به stage بعدیاستیج سوم (deploy)خب حالا میریم سراغ سرورمون اولین کاری که باید انجام بدیم اینه که باید روش docker نصب کنیم که این کار رو انجام میدیمدر مرحله بعد باید به یک شکلی وصل بشیم به gitlab و اون image ای که برامون ساخته رو بگیریم یا اصطلاحا pull کنیم.خب برای اینکه درگیر مسائل versioning توی image نشیم بهترین کار اینه که توی این مرحله بگیم که اول برو image فعلی که روی سرور ازش یه container ساخته بودیم رو پاک کنیم پس مراحل میشه به شکل زیرلاگین کنم به gitlabکانتینر فعلی روی سرور رو پاک کنمایمیج فعلی روی سرور رو پاک کنمدوباره ایمیج جدید رو دانلود کنمو توسط docker-compose.yml اجراش کنمحالا توی مرحله deploy اجرای دستور docker-compose up -d باید داخل سرور من اجرا بشه پس باید با یه مکانیزمی به gitlab بفهمونیم که وصل شو به سرور من و این دستور رو اونجا برای من اجرا کنگیت لب یک امکان داره به اسم gitlab runner که درواقع میگه میتونی یه agent هایی برای من توی ماشینت نصب کنی و اون رو به من بشناسونی و بگی از جایی که من هستم (منظور داخل stage هست) به کدوم agent وصل بشم و دستوراتت رو اجرا کنم.پس برمیگردیم دوباره به سرور خودمون و اونجا gitlab runner رو نصب میکنیم که نحوه نصب اش رو اینجا قرار میدمآموزش نصب gitlab runnerبعد از نصب حالا باید وصل بشیم به gitlab runner که نحوه انجام این کار در لینک زیر هست ولی من خلاصه ای از اون رو در پایین توضیح میدم.نحوه ساخت یک runner و وصل شدن به اوندر اونجا میگیم gitlab-runner registerبعد باید token مربوط به runner ای که ساختیم رو بهش بدیم بعد میپرسه از چه نوعی هست که ما چون قراره فقط یه ssh بزنیم به ماشینمون ، پس نوع runner رو shell در نظر میگیریم.چون اینا همه دارن توی ماشین ما اجرا میشه و توی سرور ما هم همون طور که بالاتر گفتیم docker نصب هست ، پس در واقع فقط قراره یه سری shell script برای ما اجرا کنهحالا gitlab-ci باید از یه جایی بفهمه که چجوری بره این stage رو حتما تو ماشین من اجرا کنه ، اینجا جایی که من وقتی داشتم runner تعریف میکردم یه tag بهش دادم (یعنی یک اسم برای runner ام تعریف کردم) تا از طریق اون tag گیت لب این مسئله رو متوجه بشهاین هم کد مربوط به stage سومdeploy-ssh: stage: deploy-ssh when: manual script: - sudo docker login registry.gitlab.com -u "yourGitlabUsername" -p "yourPrivateKey" - sudo docker rm -f yourApplicationContainerName || true - sudo docker rmi registry.gitlab.com/testProject/testApp || true - sudo docker-compose -f /usr/testProject/docker-compose.yml up -d || true only: - master tags: - testTagNameاگر براتون سوال شده که فایل docker-compose.yml از کجا پیداش شد باید بگم که توی فایل pom.xml پروژه ام تعریف کردم که هرچی فایل yml زیر پوشه rousource ام وجود داره رو موقع build کردن به زیر پوشه target کپی کنه .اگر همه چیز به درستی config شده باشه به راحتی عملیات نسخه گذاریتون انجام میشه و دیگه لازم نیست نگران هیچ چیز باشیدامیدوارم این مطلب براتون مفید بوده باشهدر نهایت اگر به دنبال درک بهتر این مسائل هستید میتونید به لینک های زیر مراجعه کنید و همچنین اگر پروژه شما هم جاوایی هست ، برای کمک میتونید به jhipster هم سر بزنیدhttps://docs.gitlab.com/ee/ci/quick_start/https://docs.gitlab.com/ee/ci/yamlhttps://docs.gitlab.com/runner/install/linux-manually.htmlhttps://docs.gitlab.com/runner/register/https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html