Skip to content
Dokumentatsiya
Jenkins bilan Docker CI/CD

Koddan Servergacha: Jenkins bilan Docker CI/CD va Discord bilan integratsiya

Kirish

Jenkins - bu doimiy Continuous Integration/Continuous Deployment (CI/CD) pipelinei uchun ajoyib tool, ayniqsa Docker konteynerlari bilan ishlashda.

Jenkins CI/CD jarayonini osonlashtiradigan open-source avtomatlashtirish serveri bo'lib, ishlab chiquvchilarga applicationlarni build qilish, testdan o'tkazish va deploymentni avtomatlashtirish imkonini beradi. Docker bilan birlashganda, u konteynerlashtirilgan workflowlari uchun kuchli yechim taklif qiladi, bu turli muhitlarda izchillik(consistency) va portativlikni ta'minlaydi.

Jenkins Docker bilan birgalikda izchil va samarali development workflowni yaratish uchun konteynerlashtirishdan foydalangan holda CI/CD jarayonini soddalashtiradi. Jenkins pipelinelarida Docker imkoniyatlaridan foydalanish orqali jamoalar dasturiy ta'minotni tezroq va ishonchli yetkazib berishga erishishlari mumkin.

Bugun ushbu qo'llanmamizda konteynerlashtirilgan ilovalarni Jenkins orqali CI/CD avtomatizatsiya qilamiz.

Ushbu jarayon quyidagicha bosqichlarsan iborat bo'ladi. (qisqa tushuntirish)

  • CI CI qismida biz loyiha source kodlari joylashgan github yoki gitlab bilan ishlaymiz. Github yoki Gitlabda yangi kod qo'shilsa Jenkins CI pipeline avtomatik ishga tushadigan qilamiz. Keyingi qismida o'zgarish bo'lganinidan keyin Github yoki Gitlabdan loyiha repositoriyasi va belgilangan branchdan loyiha source kodlarini runner yuklab oladi. Loyiha source kodlari yuklab olinganidan keyin test uchun Docker image build qilinadi, agara Docker image build bo'lsa keyingi bosqichda CD o'tadi aks holda shu joyida jarayon to'xtaydi. Docker image muvaffaqiyatli build bo'lganidan keyin birorta Docker Registryga push qilinadi. Shu jarayonlar hammasi bajarilganida CI bosqichimiz tugab CD bosqichiga o'tiladi.

  • CD CI qismida applicationimiz testlardan o'tib Docker image build qilinib Docker Registyga push qilingan bo'ladi. CD qismida ssh orqali serverga kirib bundan oldingi docker containerni to'xtatib, o'chirib yangi docker imageni Docker Registydan pull qilib olib uni Docker container qilib ishga tushirib qo'yishimiz kerak.

  • Notification Agar CI/CD jarayoni muvaffaqiyatli yoki muvaffaqiyatsiz bo'lsa Discord serverga notification yuboradigan qilib Discord bilan integratsiya qilamiz.

Ishni boshlash

Ushbu amaliyotni amalga oshirish uchun bizga quyidagi minimum server talablaridagi server kerak bo'ladi.

Minimum Server talabi

OSRAMCPUXotiraStatic IP
Ubuntu 20.048GB4vCPU 2 core50GBHa kerak

Tavsiya qilinadigan mimimum server talabi

OSRAMCPUXotiraStatic IPServer nomi
Ubuntu 20.0412GB8vCPU 4 core50GBHa kerakJenkins Server
Ubuntu 20.048GB4vCPU 2 core50GBHa kerakApplication Server

Ushbu amaliyotda biz Jenkins, Docker, Container Registry (Dockerhub va GCR(Google Container Registry)), Github yoki Gitlab, Discord va Ubuntu server ishlatamiz

ESLATMA-> Ushbu amaliyotda biz devops-journey (opens in a new tab) platformasi manba kodlaridan foydalanamiz. Stars(yulduzcha) bosib qo'yish esdan chiqmasin :)

Jenkins o'rnatish

Amaliyotning birinchi qadami bu Serverga Jenkins o'rnatishdan boshlanadi. Bundan oldingi qo'llanmalarda Jenkins o'rnatish haqida yozilgan. Linux Serverlarga Jenkins o'rnatish (opens in a new tab) qo'llanmasidan foydalanib serveringizga jenkins o'rnatib olishingiz mumkin. Serveringizga Jenkins o'rnatib boshlang'ich sozlab olganingizdan keyin keyingi bosqichga o'tsak bo'ladi.

Docker o'rnatish

Biz ilovamizni Docker containerlarda ishga tushiramiz shuning uchun serverimizga Docker o'rnatib olishimiz kerak bo'ladi. Linux serverlarga Docker o'rnatish (opens in a new tab) qo'llanasidan foydalanib serveringizga Docker o'rnatib oling.

Git bilan ishlash

Applicationlarimiz kodlarini birorta VCS(Version Control System)da saqlanadi ushbu amaliyotda Github va Gitlab bilan ishlaydigan Jenkins CI pipeline yozamiz. Gitdan private repositoriyalarni klon qilib olish uchun ushbu repositoriyalarga ruxsati bor userlardan Personal access token tokenlar olinadi va Jenkins credendialsga qo'shib qo'yiladi. Bundan maqsad Jenkins pipeline ushbu Personal access token orqali Github yoki Gitlabdan private repositoriyalarni clon qila olishi va ishlata olishidir.

ESLATMA-> Ko'p startuplar kichik jamolar Github ishlatishadi, boshqalar esa enterprise Gitlabni o'z serverlarda ishga tushirib ishlatishadi. Bu amaliyotda ikkalasi bilan ham ishlash yozilgan. Sizning jamoyingiz qaysi birini ishlatsa shunga mosini qo'llaysiz.

Personal access token olish

Github uchun

Github profilingizga kirib Settings sozlamalar bo'limga kiring.

Keyin Developer settings bo'limga kiring.

Personal access tokens -> Tokens(classic)

->Generate new token(classic)

Personal access tokenga nom berib va ishlash mudatini belgilab admin accesslarni beramiz va Generate token bosib token generatsiya qilib olamiz.

Sizga Github Personal access token generatsiya qilib berganida uni nusxalab olib qo'yishingiz kerak bo'ladi. Bu Personal access tokenni Jenkinsda credentialsga qo'shib qo'yib Jenkins pipelineda ishlatamiz.

Gitlab uchun

Gitlab profilingizga kirib Pereferences bo'limga kirib Accsess Tokensga o'tamiz.

Accsess Tokensga kirganimizdan keyin Token nomi ishlash muddatini belgilab kerakli ruxsatlarni berib Create personal accsess token bosib Gitlab token generatsiya qilib olamiz.

O'zingiz ishlatdigan VCSdan access token generatsiya qilib olganingizdan keyin Serverimizda ishga tushirilgan Jenkinsga kirib credentialsga ushbu tokenlarni qo'shib qo'yamiz.

-> Manage Jenkins -> Credentials

Username-> Gitlab yoki Github username

Password Gitlab yoki Gitlabdan generatsiya qilib olgan personal access tokenni joylashtiramiz.

Jenkinsga Gitlab yoki Githubdan private repositoriyalarni klon qilib ishlashi uchun accsess tokenni joylashtirib sozlaganimizdan keyin birinchi Jenkins CI pipelineni yozishni boshlasak bo'ladi.

Discord bilan integratsiya

Discordni CI/CD pipelinelari bilan integratsiyalash dastuchilar guruhlari ichida hamkorlik va aloqani yaxshilaydi. Discord, mashhur xabar almashish platformasi, real vaqt rejimida notificationlar uchun markazlashtirilgan markaz(hub) bo'lib xizmat qiladi, tezkor javob vaqtlarini oshiradi va dasturiy ta'minotni ishlab chiqishning lifesiklini yaxshilaydi. Jenkins CI yoki har qanday CI/CD tooli Discord bilan bog'lash orqali dasturchilar to'g'ridan-to'g'ri o'zlari tanlagan Discord kanallarida build holati, test natijalari va deployment natijalari bo'yicha tezkor yangilanishlarni oladi.

Qisqa qilib aytganda biz discordni CI/CD pipelinemiz muvaffaqiyatli ishini tugatgani yoki muvaffaqiyatsiz bo'lgan xabarlarni dasturchilar guruhlariga discord orqali yetkizish uchun ishlatamiz.

Jenkins uchun

Discordni Jenkins bilan integratsiya qilish uchun Discord Notifier plaginini Jenkinsga o'rnatib olishimiz kerak.

-> Manage Jenkins -> Plugins -> Available plugins

Discord Notifier plaginini o'rnatib jenkins restart bo'lganidan keyin Discord server ochib serverda channel ochib webhook yaratib olamiz.

Discord kirib Add a Server bosib Server yaratib olamiz

Serverga nom berib Create bosamiz.

Discord Server yaratib olganimzidan keyin Serverimizda channel yaratib olamiz. Create Channel

Channel nomini yozamiz masalan dev-jenkins -> Create Channel

Channel ochib olganimzidan keyin Integrations bo'limga kirib Webhooksdan Create Webhook bosamiz

Webhook yaratib olganimizdan keyin uni nusxalab Copy Webhook URL olib qo'yamiz va Jenkins pipelineda ishlatish uchun olib qo'yamiz.

Discordan olgan webhookni Jenkins credendialsga qo'shamiz.

-> Manage Jenkins -> Credentials

Gitlab Github uchun

Github bilan jenkinsni integratsiya qilish uchun ushbu video qo'llanmalardan foydalanishingiz mumkin. dev-jenkins channel ochganimizdek github yoki gitlab channel ochib webhook yaratib Gitlab yoki Github bilan integratsiya qilamiz.

Bu holda har bir repositoriyalarga discord webhook ulab chiqiladi.

Birinchi CI pipeline

Hozirchi hammasi tayyor birinchi Jenkins CI pipelinemizni yozishga tayyormiz. Jenkinsga kirib loyihamiz uchun job yaratib olamiz.

-> Creaet a job +

Enter an item name ga loyihamiz nomini yozib Pipelineni tanlab OK bosamiz

Keyingi qismda eng pastgi qatorga Pipeline qismiga quyidagi birinchi CI pipelinemizni joylashtiramiz Save qilamiz.

Jenkins CI pipeline

pipeline {
    agent any
 
    environment {
        DISCORD_WEBHOOK = credentials('discord-webhook')
        GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
        GIT_TOKEN = credentials('git-token')
        BRANCH_NAME = 'main'
    }
    stages {
        stage('Clean Workspace') {
            steps {
                cleanWs()
            }
        }
        stage('Clone Repository') {
            steps {
                git branch: BRANCH_NAME, url: GIT_URL, credentialsId: 'git-token'
            }
        }
    }
    post {
        always {
            discordSend(
                description: "Jenkins Pipeline Build ${currentBuild.currentResult}",
                link: env.GIT_URL,
                result: currentBuild.currentResult,
                title: JOB_NAME,
                webhookURL: env.DISCORD_WEBHOOK
            )
        }
    }
}

Keling pipelineni bo'laklarga bo'lib ko'rib chiqamiz. Bu Groovy-da yozilgan Jenkins Pipeline, Java Virtual Machine uchun skript tili. Keling, ushbu pipelinening asosiy qismlarini ajratamiz:

agent any

Ushbu pipeline Jenkins muhitida mavjud bo'lgan har qanday agentda ishlashi mumkin. Har qanday kalit so'z pipelineni istalgan mavjud executorda ishlashiga imkon beradi.

environment {
    DISCORD_WEBHOOK = credentials('discord-webhook')
    GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
    GIT_TOKEN = credentials('git-token')
    BRANCH_NAME = 'main'
}
  • DISCORD_WEBHOOK-> Discord webhook URL manzilini saqlaydigan credential.
  • GIT_URL-> Klonlanadigan Git repositoriyaning URL manzili.
  • GIT_TOKEN-> Autentifikatsiya uchun Git tokenini saqlaydigan credendial(private repositoriyalar bilan ishlash uchun).
  • BRANCH_NAME-> Git repositoriyasining foydalaniladigan branchi (bu holda main).
stages {
    stage('Clean Workspace') {
        steps {
            cleanWs()
        }
    }
    stage('Clone Repository') {
        steps {
            git branch: BRANCH_NAME, url: GIT_URL, credentialsId: 'git-token'
        }
    }
}
  • Clean Workspace-> Ushbu bosqich ishni boshlashdan oldin workspaceni tozalaydi.
  • Clone Repository-> Bu bosqich GIT_URL va BRANCH_NAME tomonidan belgilangan Git repositoriyani git-token tomonidan aniqlangan credendial yordamida klonlaydi.
post {
    always {
        discordSend(
            description: "Jenkins Pipeline Build ${currentBuild.currentResult}",
            link: env.GIT_URL,
            result: currentBuild.currentResult,
            title: JOB_NAME,
            webhookURL: env.DISCORD_WEBHOOK
        )
    }
}

Jarayon tugallangandan so'ng (muvaffaqiyatsiz bo'lsa ham), discordSend qadami bajariladi. U DISCORD_WEBHOOK da saqlangan webhook URL manzilidan foydalanib, Discord channelga notification(xabar) yuboradi. Xabarda natija, Git repositoriyaga link va job nomi kabi ma'lumotlar mavjud.

Qisqa qilib aytganda ushbu boshlang'ich sodda pipeline ishga tushganida biinchi Clean Workspace bosqichida cleanWs() bilan worskspaceni tozalaydi keyingi Clone Repository bosqichida berilgan GIT_URL, GIT_TOKEN va BRANCH_NAME bilan Git repositoriyani klon qilib oladi. Oxirgi bosqichda pipeline muvaffaqiyatli(SUCCSES) yoki muvaffaqiyatsiz(FAILED) bo'lgani haqida Discord channelga notification(xabar) yuboradi.

Keling pipelineni ishga tushiramiz. -> Build Now

SIzda quyidagi natijha bilan muvaffaqiyatli ishga tushishi va discordga notification yuborilishi kerak.

Git repositoriyalar bilan ishlash (mono repo va multi repo)

Bizda har xil vaziyat bo'lishi mumkin loyihamiz mono repo yoki multi repo bo'lishi mumkin.

Monorepo (Monolithic Repository)-> monolithic repository so'zining qisqartmasi monorepo - bu bir nechta loyihalar, applicationlar yoki servicelar bitta repositoriyada saqlanadigan version control system (VCS) strategiyasidir. Monorepoda barcha kodlar, kutubxonalar(library) va turli loyihalar uchun dependensilar(bog'liqliklar) bitta markaziy repositoriyada birgalikda boshqariladi. Ushbu yondashuv har bir loyiha yoki servicening o'z repositoriyasiga ega bo'lgan ko'p repositoriyali(multi-repo) sturukturasidan farq qiladi. Qisqa qilib aytganda butun bir tashkilot loyihalari bitta repositoriyada bo'ladi.

Multirepo (Multi-Repository)-> multi-repo yondashuvida har bir loyiha, application yoki service o'zining alohida repositoriyasiga ega. Har bir repositoriya ma'lum bir kod bazasi yoki servicega bag'ishlangan va loyihalar o'rtasidagi dependensilar versiya va paketlarni boshqarish orqali boshqariladi. Qisqa qilib ayganda har bir loyiha, application, service va boshqalar alohida alohida repositoriyalarda bo'ladi va bir-biriga bo'g'liklari bo'ladi.

Monorepo va multirepo yondashuvlarining kuchli va zaif tomonlari bor va ular orasidagi tanlov ko'pincha loyiha yoki tashkilotning o'ziga xos ehtiyojlari, ko'lami va rivojlanish ish oqimlariga bog'liq.

Agar biz CI/CD yozayotgan loyiha monorepo bo'lsa, bitta monoreponi o'zini clon qilib uni build qilib ishga tushira olamiz, Bu soddaroq bo'ladi chunki loyihalar, applicationlar, servicelar va boshqalar o'rtasidagi bo'gliklik(reference) bitta shu monorepo ichida bo'ladi.

Monorepo uchun Jenkins pipeline, faqat kerakli monorepo clon qilinadi holos.

pipeline {
    agent any
 
    environment {
        DISCORD_WEBHOOK = credentials('discord-webhook')
        GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
        GIT_TOKEN = credentials('git-token')
        BRANCH_NAME = 'main'
    }
    stages {
        stage('Clean Workspace') {
            steps {
                cleanWs()
            }
        }
        stage('Clone Repository') {
            steps {
                git branch: BRANCH_NAME, url: GIT_URL, credentialsId: 'git-token'
            }
        }
    }
    post {
        always {
            discordSend(
                description: "Jenkins Pipeline Build ${currentBuild.currentResult}",
                link: env.GIT_URL,
                result: currentBuild.currentResult,
                title: JOB_NAME,
                webhookURL: env.DISCORD_WEBHOOK
            )
        }
    }
}

Tashkilot loyihalari multirepo bo'lsa va biz loyihalarga CI/CD yozayotgan bo'lsak, har bir loyihada bir nechta boshqa repositoriyalarga bo'glikligi(reference) bo'ladi, shuning uchun CI/CD yozayotgan loyihamiz ishlashiga kerak bo'lgan loyiha, application,service va boshqalar repositoriyalarni ham klon qilib olishimiz va yig'ishimiz kerak bo'ladi.

Multirepo uchun Jenkins pipeline.

pipeline {
    agent any
 
    environment {
        DISCORD_WEBHOOK = credentials('discord-webhook')
        API_GIT_URL ='https://github.com/ismoilovdevml/devops-journey-api.git'
        UI_GIT_URL = 'https://github.com/ismoilovdevml/devops-journey-ui.git'
        SERVICE_GIT_URL = 'https://github.com/ismoilovdevml/devops-journey-service.git'
        CONFIGURATIONS_GIT_URL = 'https://github.com/ismoilovdevml/devops-journey-configurations.git'
        GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
        GIT_TOKEN = credentials('git-token')
        BRANCH_NAME = 'main'
    }
    stages {
        stage('Clean Workspace') {
            steps {
                cleanWs()
            }
        }
        stage('Setup Environment') {
            steps{
                    dir('configurations'){
                        git branch: BRANCH_NAME, url: CONFIGURATIONS_GIT_URL, credentialsId: 'git-token'
                    }
                    dir('devops-journey/devops-journey-ui'){
                        git branch: BRANCH_NAME, url: UI_GIT_URL, credentialsId: 'git-token'
                    }
                    dir('devops-journey/devops-journey-api'){
                        git branch: BRANCH_NAME, url: API_GIT_URL, credentialsId: 'git-token'
                    }
                    dir('devops-journey/devops-journey-service'){
                        git branch: BRANCH_NAME, url: SERVICE_GIT_URL, credentialsId: 'git-token'
                    }
                    sh "cp configurations/devops-journey/${BUILD_BRANCH}/MAIN.Dockerfile ./MAIN.Dockerfile"
                    sh "cp configurations/devops-journey/${BUILD_BRANCH}/API.Dockerfile ./API.Dockerfile"
                    sh "cp configurations/devops-journey/${BUILD_BRANCH}/UI.Dockerfile ./UI.Dockerfile" 
                    sh "cp configurations/devops-journey/${BUILD_BRANCH}/SERVICE.Dockerfile ./SERVICE.Dockerfile" 
            }
        }
    }
    post {
        always {
            discordSend(
                description: "Jenkins Pipeline Build ${currentBuild.currentResult}",
                link: env.GIT_URL,
                result: currentBuild.currentResult,
                title: JOB_NAME,
                webhookURL: env.DISCORD_WEBHOOK
            )
        }
    }
}

Multirepo uchun yozgan pipelinemizda o'zgarishlar quyidagicha devops-journey loyihamizda bir nechta boshqa repositoriyalarga bo'gliklari mavjuda maslaan UI, API, Service va Configurations repositoriyalarga. Ushbu loyihani Jenkins CI/CD ishga tushirishimiz uchun ushbu repositoriyalar clon qilinb yig'ishimiz va birga ishga tushirishimiz kerak. Setup Environment qadami ushbu vazifani bajaradi yani Environmentni sozlaydi kerak repositoriyalarni bir joyga yig'adi.

Ushbu namunada konfiguratsiyalar ham alohida repositoriyada saqlangani ko'rsatilgan yani configurations repositoriyada barcha loyihalar servicelar applicationlar konfiglari(masalan Dockerfayllar) branchlar nomi bilan joylashtirilgan bo'ladi. configurations repositoriyasi tuzulishiga namuna.

├── configurations
│   └── devops-journey
│       ├── main
│       │   ├── API.Dockerfile
│       │   ├── MAIN.Dockerfile
│       │   ├── SERVICE.Dockerfile
│       │   └── UI.Dockerfile
│       ├── dev
│       │   ├── API.Dockerfile
│       │   ├── MAIN.Dockerfile
│       │   ├── SERVICE.Dockerfile
│       │   └── UI.Dockerfile
│       ├── stage
│       │   ├── API.Dockerfile
│       │   ├── MAIN.Dockerfile
│       │   ├── SERVICE.Dockerfile
│       │   └── UI.Dockerfile
│       └── prod
│           ├── API.Dockerfile
│           ├── MAIN.Dockerfile
│           ├── SERVICE.Dockerfile
│           └── UI.Dockerfile

CI pipeline

Hammasi tayyor endi to'liq CI pipeline yozib ishga tushirsak bo'ladi. Bundan oldin monorepo va multi-repolar bilan ishlash ko'rsatildi loyihangizga qarab buni tanlaysiz. devops-journey (opens in a new tab) monorepo bo'lgani uchun monorepo CI/CD pipeline yozamiz lekin yuqorida multi-repo uchun ham namuna ko'rsatilgan.

Hozirgacha yozgan CI pipelinemiz quyidagicha

pipeline {
    agent any
 
    environment {
        DISCORD_WEBHOOK = credentials('discord-webhook')
        GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
        GIT_TOKEN = credentials('git-token')
        BRANCH_NAME = 'main'
    }
    stages {
        stage('Clean Workspace') {
            steps {
                cleanWs()
            }
        }
        stage('Clone Repository') {
            steps {
                git branch: BRANCH_NAME, url: GIT_URL, credentialsId: 'git-token'
            }
        }
    }
    post {
        always {
            discordSend(
                description: "Jenkins Pipeline Build ${currentBuild.currentResult}",
                link: env.GIT_URL,
                result: currentBuild.currentResult,
                title: JOB_NAME,
                webhookURL: env.DISCORD_WEBHOOK
            )
        }
    }
}

CI pipelinemizga Docker bilan ishlash bosqichini qo'shimiz kerak. Buning uchun Jenkinsga kerakli plaginlarni o'rnatib olishimiz kerak.

Pipelinemizda Docker bilan ishlash uchun quyidagi plaginlarni o'rnatib olishimiz kerak.

Docker, Docker Common, Docker Pipeline, Docker API, docker-build-step

Plaginlarni o'rnatib bo'lganingizdan keyin sozlab chiqish kerak.

-> Manage Jenkins -> Tools -> Docker installations

ESLATMA-> Docker bilan ishlash uchun Jenkins o'rnatilgan serverda docker ham o'rnatilgan bo'lishi kerak!

Docker bilan ishlashni sozlab chiqganimizdan keyin Jenkins pipelineda build bo'lgan docker imagelarni push qilish saqlash uchun Docker Registry ishlatamiz. Ushbu amaliyotda Dockerhub va GCR(Google Container Registry)dan foydalanish ko'rsatiladi. Dockerhub bepul versiyasida bitta private docker imagega ruxsat bor undan keyingi imagelar avtomatik public bo'ladi, private qilish uchun Dockerhub pullik obunaga a'zo bo'lish kerak. GCR Google Cloudga tegishli.

Dockerhub

Amaliyotni ushbu qismida Docker Registry uchun Dockerhub ishlatamiz shuning uchun Dockerhubdan ro'yxatdan o'tgan bo'lishingiz va Access token olgan bo'lishingiz kerak. Agar Ro'yxatdan o'tmagan bo'lsangiz ro'yxatdan o'tishingiz kerak

Sign up bosib ro'yxatdan o'tasiz

Ro'yxatdan o'tib olganingizdan keyin Email Verification talab qilinadi. Email verifatsiya qilganingizdan keyin

-> Account Settings -> Security -> bo'limga o'tib Access Token yaratib olamiz.

Access Tokenga nom berib unga Read,Write,Delete permisonlarni beramiz Generate qilamiz.

Sizga access token generatsiya qilib berialdi va siz undan nusxa olib qo'yishingiz kerak, aks holda uni qaytib ololmaysiz. Sizga docker login qilish uchun namuna CLI command ham beriladi

docker login -u devsecopsuser4732

Jenkins pipelineda DockerHub Registrydan foydalanishimiz uchun Jenkins crdentialsga DockerHub user va tokenni qo'shib qo'yamiz.

-> Manage Jenkins -> Credentials-ga kirib Add credentialsga o'tamiz.

Usernamega DockerHub usernamengizni yozasiz Passwordga esa yuqorida yaratib olgan access tokenimizni joylashtiramiz.

Dockerhubdan ro'yxatdan o'tdik, access token oldik hammasini Jenkins credentialsga dockerhub ID bilan saqladik endi Jenkins pipelinega CD(continuous delivery)qismini qo'shsak bo'ladi.

Jenkins pipelenimizga quyidagi bosqichni qo'shamiz. Bu bosqich ilovamizdan docker image yaratib, build qilib, Docker Registryga push qiladi ya'ni DockerHubga.

Pipelinega yangi environmentlar qo'shib olamiz:

environment {
    DISCORD_WEBHOOK = credentials('discord-webhook')
    GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
    DOCKERHUB_CREDENTIALS = credentials('dockerhub')
    CONTAINER_NAME = 'devops-journey'
    REGISTRY_URL = 'devsecopsuser732'
    GIT_TOKEN = credentials('git-token')
    BRANCH_NAME = 'main'
}
  • DOCKERHUB_CREDENTIALS -> credentialsdandan DockerHub username va access tokenni olib keladi.
  • CONTAINER_NAME -> Bu loyihamizni containerda ishga tushirishda unga beriladigan nom
  • REGISTRY_URL -> Dokcer Registry URL. Bizni holatimizda yani Dockerhubda bu yerda Dockerhub username yoziladi.

Dockerhub bilan ishlaydigan Jenkins CI pipelinemizni quyidagicha yangilaymiz.

pipeline {
    agent any
 
    environment {
        DISCORD_WEBHOOK = credentials('discord-webhook')
        GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
        DOCKERHUB_CREDENTIALS = credentials('dockerhub')
        CONTAINER_NAME = 'devops-journey'
        REGISTRY_URL = 'devsecopsuser732'
        GIT_TOKEN = credentials('git-token')
        BRANCH_NAME = 'main'
    }
    stages {
        stage('Clean Workspace') {
            steps {
                cleanWs()
            }
        }
        stage('Clone Repository') {
            steps {
                git branch: BRANCH_NAME, url: GIT_URL, credentialsId: 'git-token'
            }
        }
        stage('Build Application') {
            steps {
                withCredentials([usernamePassword(credentialsId: 'dockerhub', usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
                    script {
                        def dockerlogin = "docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD}"
                        sh dockerlogin
                        sh """
                            docker build . -t ${REGISTRY_URL}/${CONTAINER_NAME}:${BUILD_NUMBER} -f Dockerfile
                            docker tag ${REGISTRY_URL}/${CONTAINER_NAME}:${BUILD_NUMBER} ${REGISTRY_URL}/${CONTAINER_NAME}:latest
                            docker push ${REGISTRY_URL}/${CONTAINER_NAME}:latest
                            docker push ${REGISTRY_URL}/${CONTAINER_NAME}:${BUILD_NUMBER}
                            docker image rm -f ${REGISTRY_URL}/${CONTAINER_NAME}:latest
                            docker image rm -f ${REGISTRY_URL}/${CONTAINER_NAME}:${BUILD_NUMBER}
                        """
                    }
                }    
            }
        }
    }
    post {
        always {
            discordSend(
                description: "Jenkins Pipeline Build ${currentBuild.currentResult}",
                link: env.GIT_URL,
                result: currentBuild.currentResult,
                title: JOB_NAME,
                webhookURL: env.DISCORD_WEBHOOK
            )
        }
    }
}

GCR(Google Container Registry)

Docker Registry sifatida GCR ishlatish uchun Google Cloud IAMdan Service Account ochib olishimiz va unga Storage Admin ruxsatini berishimiz kerak, undan keyin service accountga kirib service account key generatsiya qilib olishimiz kerak. Credentiallar Jenkins pipeline Google cloudga GCR ga kira olishi va ishlashi uchun kerak.

1-> Google Cloudga kirib Service Accountsdan service account yaratib olamiz.

-> Google Cloud -> IAM & Admin -> Service Accounts

2 -> + CREATE SERVICE ACCOUNT bosib service account yaratib olamiz.

3 -> Service account namega service accountimiz uchun nom yozamiz masalan gcr. Service account descriptionga esa description yozib DONE bosganimizdan keyin keyingi bosqichga o'tadi

4 -> Rolega Storage Admin huquqini beramiz va DONE bosib yaratib olamiz.

5 -> Service Account yaratib olganimizdan keyin Service Accountimizga(gcr) kirib KEYS qismidan ADD KEY bosib JSON formatda private key generatsiya qilib olamiz va uni yuklab olamiz.

-> KEYS -> ADD KEY -> JSON -> CREATE

7-> Yuklab olgan Service account private keyimizni Jenkins credentialsga qo'shib qo'yamiz.

-> Manage Jenkins -> Credentials-ga kirib Add credentialsga o'tamiz.

8-> Google Clouddan yuklab olgan service account private keyimizni ochib ko'rsak 2chi qatorida project_id ko'rsatilgan bo'ladi uni nusxalab olib uni ham Jenkins credentialsga qo'shib qo'yamiz.

-> Manage Jenkins -> Credentials-ga kirib Add credentialsga o'tamiz.

9-> Hamma kerakli keylarni Jenkins credentialsga qo'shagnimizdan keyin bizni GCR bilan ishlaydigan Jenkins pipelinemizda quyidagicha o'zgarishlar bo'ladi.

pipeline {
    agent any
 
    environment {
        DISCORD_WEBHOOK = credentials('discord-webhook')
        GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
        GCR_CREDENTIALS = credentials('gcr-key')
        GCP_PROJECT_ID = credentials('gcp-project-id')
        CONTAINER_NAME = 'devops-journey'
        REGISTRY_URL = 'gcr.io'
        GIT_TOKEN = credentials('git-token')
        BRANCH_NAME = 'main'
    }
 
    stages {
        stage('Clean Workspace') {
            steps {
                cleanWs()
            }
        }
 
        stage('Clone Repository') {
            steps {
                git branch: BRANCH_NAME, url: GIT_URL, credentialsId: GIT_TOKEN
            }
        }
 
        stage('Build and Push Docker Image') {
            steps {
                script {
                    def dockerImage = "${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:${BUILD_NUMBER}"
 
                    sh """
                        cat ${GCR_CREDENTIALS} | docker login -u _json_key --password-stdin https://gcr.io
                        docker build . -t ${dockerImage} -f Dockerfile
                        docker push ${dockerImage}
                        docker tag ${dockerImage} ${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:latest
                        docker push ${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:latest
                        docker image rm -f ${dockerImage}
                        docker image rm -f ${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:latest
                    """
                }
            }
        }
    }
    post {
        always {
            discordSend(
                description: "Jenkins Pipeline Build ${currentBuild.currentResult}",
                link: GIT_URL,
                result: currentBuild.currentResult,
                title: JOB_NAME,
                webhookURL: DISCORD_WEBHOOK
            )
        }
    }
}

Dockerhub va GCR Container registrylar uchun Jenkins CI pipelien yozdik va muvaffaqiyatli ishladi va endi CD bosqichini yani serverga deploy qilish boqichini qo'shsak bo'ladi.

CD pipeline

CI pipelineni muvaffaqiyatli yozib ishga tushirganimizdan keyin CD pipeline yozishni boshlasak bo'ladi. CD pipeline quyidagi bosqichlardan iborat Jenkins pipeline ssh-agent orqali serverga bo'glanadi va u birinchi navbatda Jenkins crdentialsda ko'rsatilgan Docker registry secretlari bilan docker login qiladi va undan keyin belgilangan docker image va belgilangan tag bilan docker registrydan pull qilib oladi agar Jenkins pipelineda belgilangan docker container nomi bilan ishlab turgan docker container ishlab turgan bo'lsa uni to'xtatib, o'chirib tozalab yangi containerni belgilangan nom va belgilangan port bilan ishga tushiradi.

Loyihamizni serverimizda ishga tushirishimiz uchun Jenkins pipeline serverga kira olishi kerak. Biz buning uchun SSH(ssh-agent) dan foydalanamiz. ssh-agent plagini Jenkinsga o'rnatib olishimiz kerak.

-> Manage Jenkins -> Plugins

SSH Agent plagini Jenkinsga o'rnatganimizdan keyin Serverimizda ssh key generatsiya qilib Jenkins credentialsga qo'shib qo'yishimiz kerak.

Hozir esa serverimizga kirib ssh-key generatsiya qilib olamiz.

ESLATMA-> Bu yurda netflix nomli ssh key generatsiya qilinib ishtilyapti. Bu nom ixtiyoriy xoxlagan nomizni berib ishlatavering!

ssh-keygen -f ~/.ssh/netflix

ssh-keygen buyrug'i tizimlar orasidagi xavfsiz aloqa uchun SSH (Secure Shell) kalit juftlarini yaratish uchun Unix-ga o'xshash operatsion tizimlarda qo'llaniladigan tooldir. ssh-keygen-ni ishga tushirganingizda, siz bir juft kalit yaratasiz: public key va private key.

  • ssh-keygen Bu SSH kalitlarini yaratish uchun ishlatiladigan buyruq.
  • -f ~/.ssh/netflix Bu yerda -f yaratilgan kalit faylning fayl nomini bildiradi. ~/.ssh/netflix kalitlar saqlanadigan jild va fayl nomini bildiradi. ~ foydalanuvchining home jildini ifodalaydi (masalan, Linuxda /home/username), ~/.ssh esa SSH kalitlarini saqlash uchun umumiy jilddir. Bu buyruq ssh-keygen-ga yangi SSH kalit juftligini yaratish va uni foydalanuvchining home jildidagi .ssh jildida netflix sifatida saqlashni aytadi.

Buyruq bajarilgandan so'ng siz odatda ikkita faylni olasiz:

  • netflix Bu boshqa tizimlarga ulanishda o'zingizni autentifikatsiya qilish uchun foydalaniladigan shaxsiy(private) kalit fayli.
  • netflix.pub Bu public kalit fayli. Siz ushbu faylni ushbu kalitdan foydalanishga ruxsat bermoqchi bo'lgan boshqa tizimlar/xizmatlar bilan baham ko'rishingiz mumkin.

cat netflix.pub >> authorized_keys

cat netflix.pub >> authorized_keys Ushbu buyruq netflix.pub faylining mazmunini authorized_keys faylining oxiriga qo'shadi.

  • cat - fayl mazmunini ko'rsatish uchun ishlatiladigan buyruq.
  • >> - faylga chiqish qo'shish uchun ishlatiladigan qayta yo'naltirish operatori.

authorized_keys public kalit autentifikatsiyasi uchun SSH da qo'llaniladigan fayldir. SSH serveri ulanish so'rovini olganida, kirish public kaliti u yerda sanab o'tilgan kalitlardan biriga mos kelishini tekshirish uchun authorized_keys faylini ko'rib chiqadi. Agar shunday bo'lsa, kirish huquqi beriladi. Ushbu buyruqlar ketma-ketligining maqsadi netflix.pub faylida saqlangan public kalitni avtorizatsiya qilingan kalitlar ro'yxatiga (avtorized_keys) qo'shishi. Kimda tegishli private kalit (netflix) bo'lsa, endi ushbu private kalit yordamida ushbu tizim bilan autentifikatsiya qilish mumkin. U autentifikatsiya qilish uchun o'rnatilgan public-private kalit juftligidan foydalanib, parol talab qilmasdan kirish imkonini beradi. Bu pipeline orqali serverga kirishga imkon beradi.

cat ~/.ssh/netflix

Ushbu buyruq netflix nomli ssh keyimizni private keyni ko'rsatadi biz undan nusxa olib qo'yamiz.

Serverda ssh-key generatsiya qilib oldik endi Jenkins credentialsga qo'shsak bo'ladi.

-> Manage Jenkins -> Credentials

  • Kind -> SSH Userame with private key
  • ID-> server-ssh(ssh keyni pipelineda xavfsiz quyidagi ID bilan ishlatamiz)
  • Username-> server username kiritiladi(server usernameni bilish uchun buyruq)
whoami

Private Key-> netflix private keyni joylashtiramiz.

SSh key generatsiya qilib sozlab oldik endi Serverga kirib Docker registrydan imageni pull qilib olib uni run qiladigan bosqich qo'shishimiz kerak.

Jenkins pipeleni Serverga kira olishi uchun 2 ta Jenkins credentials yaratish kerak bular: Server username'si va Server IP manzilidir.

-> Manage Jenkins -> Credentials

Bu qismda Secretga serverimiz username'si yoziladi(whoami buyrug'i bilan bilib olishingiz mumkin)

Bu qismda esa Secretga Serverimiz IP manzilini yozishimiz kerak.

Dockerhub

ESLATMA-> Ushbu CD qism asosan Dockerhub va boshqa container registrylarda ishlash uchun moslashtirilgan! GCR uchun quyidagi qismga o'ting

1-> Endi Serverimizga kerakli environmentlarni qo'shib chiqishimiz kerak.

environment {
    DISCORD_WEBHOOK = credentials('discord-webhook')
    GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
    DOCKERHUB_CREDENTIALS = credentials('dockerhub')
    CONTAINER_NAME = 'devops-journey'
    REGISTRY_URL = 'devsecopsuser732'
    GIT_TOKEN = credentials('git-token')
    SERVER_USERNAME = credentials('server-username')
    SERVER_IP = credentials('server-ip')
    SSH_CREDENTIALS = credentials('server-ssh')
    BRANCH_NAME = 'main'
}

2-> Deploy Server bosqichi

stage('Deploy Server') {
    steps {
        withCredentials([usernamePassword(credentialsId: 'dockerhub', usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
            script {
                sshagent (credentials: ['server-ssh']) {
                    sh """
                        ssh -o StrictHostKeyChecking=no ${SERVER_USERNAME}@${SERVER_IP} '\
                        docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD} && \
                        docker pull ${REGISTRY_URL}/${CONTAINER_NAME}:latest && \
                        docker stop ${CONTAINER_NAME} || true && \
                        docker rm ${CONTAINER_NAME} || true && \
                        docker run -d -p 3000:3000 --name ${CONTAINER_NAME} --restart always ${REGISTRY_URL}/${CONTAINER_NAME}:latest '
                    """
                }
            }
        }    
    }
}

ushbu Deploy Server bosqichi quyidagicha ishlaydi.

withCredentials([usernamePassword(credentialsId: 'dockerhub', usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) { }

withCredentials orqali Jenkinsdagi dockerhub crdentialidan docker username va docker password ajratib olinib DOCKER_USERNAME va DOCKER_PASSWORD biriktiriladi bu docker login qilib Dockerhubga kira olish uchun. sshagent (credentials: ['server-ssh']) { } qismida Jenkinsga o'rnatgan ssh-agent plagini orqali serverga ulanib kiramiz va berilgan buyruqlarni ketma-ket ishga tushurishni boshlaydi.

  • ssh -o StrictHostKeyChecking=no ${SERVER_USERNAME}@${SERVER_IP} ushbu buyruq bilan serverga kiramiz.
  • docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD} Berilgan crdentials orqali Docker registry yani Dockerhubga login qiladi.
  • docker pull ${REGISTRY_URL}/${CONTAINER_NAME}:latest yangi docker imageni pull qilib serverga tortib oladi.
  • docker stop ${CONTAINER_NAME} || true berilgan container nomi bilan ishlab turgan container bo'lsa uni to'xtadi.
  • docker rm ${CONTAINER_NAME} || true berilgan container nomi bilan ishlab turgan container bo'lsa uni o'chiradi.(yangi docker imageni ishga tushirish uchun)
  • docker run -d -p 4001:4001 --name ${CONTAINER_NAME} --restart always ${REGISTRY_URL}/${CONTAINER_NAME}:latest berilgan containerni belgilangan port va nom bilan docker registrydan oxirgi marta pull qilib olingan docker imageni run qilib ishga tushirib qo'yadi.

Qisqa qilib aytganda asosiy ish quyidagicha ssh-agent orqali serverga kiradi va docker login qilib berilgan imageni pull qilib tortib oladi shu nom bilan ishlab turgan eski containerni to'xtatib o'chiradi va yangi containerni berilgan port berilgan nom bilan ishga tushirib qo'yadi.

GCR

1-> GCR uchun environment qismi.

environment {
    DISCORD_WEBHOOK = credentials('discord-webhook')
    GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
    GCR_CREDENTIALS = credentials('gcr-key')
    GCP_PROJECT_ID = credentials('gcp-project-id')
    CONTAINER_NAME = 'devops-journey'
    REGISTRY_URL = 'gcr.io'
    SERVER_USERNAME = credentials('server-username')
    SERVER_IP = credentials('server-ip')
    SSH_CREDENTIALS = credentials('server-ssh')
    GIT_TOKEN = credentials('git-token')
    BRANCH_NAME = 'main'
}

2-> GCR uchun Deploy Server bosqichi.

stage('Deploy to Server') {
    steps {
        script {
            sshagent(credentials: ['server-ssh']) {
                // Copy the GCR credentials to the server
                sh "scp -o StrictHostKeyChecking=no ${GCR_CREDENTIALS} ${SERVER_USERNAME}@${SERVER_IP}:/tmp/gcr-key.json"
                // Log in to GCR on the server using the copied credentials
                sh """
                    ssh -o StrictHostKeyChecking=no ${SERVER_USERNAME}@${SERVER_IP} \
                    'cat /tmp/gcr-key.json | docker login -u _json_key --password-stdin https://gcr.io'
                """
                // Continue with the rest of the deployment steps
                sh """
                    ssh -o StrictHostKeyChecking=no ${SERVER_USERNAME}@${SERVER_IP} '\
                    docker pull ${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:latest && \
                    docker stop ${CONTAINER_NAME} || true && \
                    docker rm ${CONTAINER_NAME} || true && \
                    docker run -d -p 3000:3000 --name ${CONTAINER_NAME} --restart always ${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:latest '
                """
            }
        }
    }
}

GCRga moslab yozilgan CD pipeline quyidagicha ishlaydi. Esingizda bo'lsa bizda Google Cloud IAMdan Service Account ochib undan private .json key generatsiya qilib olgandik va uni Jenkins crdentialsga fayl sifatida qo'shgandik. GCR registryga faqat shu berilgann .json private key fayl bilan docker login qila olamiz shuning shu secret faylni scp orqali serverga olib o'tamiz va shu private .json fayl bilan login qilamiz undan keyin docker imageni pull qilib olib eski containerni to'xtatib,o'chirib yangi containerni run qilib ishga tushirib qo'yamiz. Hammasi bajarilganidan keyin serverga ko'chirib o'tkazgan private .json faylimizni xavfsizlik uchun o'chirib tashlaymiz va natija haqida discord serverga notification yuboramiz.

Xulosa

Dockerhub bilan ishlaydigan to'liq CI/CD Jenkins pipeline

pipeline {
    agent any
 
    environment {
        DISCORD_WEBHOOK = credentials('discord-webhook')
        GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
        DOCKERHUB_CREDENTIALS = credentials('dockerhub')
        CONTAINER_NAME = 'devops-journey'
        REGISTRY_URL = 'devsecopsuser732'
        GIT_TOKEN = credentials('git-token')
        SERVER_USERNAME = credentials('server-username')
        SERVER_IP = credentials('server-ip')
        SSH_CREDENTIALS = credentials('server-ssh')
        BRANCH_NAME = 'main'
    }
    stages {
        stage('Clean Workspace') {
            steps {
                cleanWs()
            }
        }
        stage('Clone Repository') {
            steps {
                git branch: BRANCH_NAME, url: GIT_URL, credentialsId: 'git-token'
            }
        }
        stage('Build Application') {
            steps {
                withCredentials([usernamePassword(credentialsId: 'dockerhub', usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
                    script {
                        def dockerlogin = "docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD}"
                        sh dockerlogin
                        sh """
                            docker build . -t ${REGISTRY_URL}/${CONTAINER_NAME}:${BUILD_NUMBER} -f Dockerfile
                            docker tag ${REGISTRY_URL}/${CONTAINER_NAME}:${BUILD_NUMBER} ${REGISTRY_URL}/${CONTAINER_NAME}:latest
                            docker push ${REGISTRY_URL}/${CONTAINER_NAME}:latest
                            docker push ${REGISTRY_URL}/${CONTAINER_NAME}:${BUILD_NUMBER}
                            docker image rm -f ${REGISTRY_URL}/${CONTAINER_NAME}:latest
                            docker image rm -f ${REGISTRY_URL}/${CONTAINER_NAME}:${BUILD_NUMBER}
                        """
                    }
                }    
            }
        }
        stage('Deploy Server') {
            steps {
                withCredentials([usernamePassword(credentialsId: 'dockerhub', usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
                    script {
                        sshagent (credentials: ['server-ssh']) {
                            sh """
                                ssh -o StrictHostKeyChecking=no ${SERVER_USERNAME}@${SERVER_IP} '\
                                docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD} && \
                                docker pull ${REGISTRY_URL}/${CONTAINER_NAME}:latest && \
                                docker stop ${CONTAINER_NAME} || true && \
                                docker rm ${CONTAINER_NAME} || true && \
                                docker run -d -p 3000:3000 --name ${CONTAINER_NAME} --restart always ${REGISTRY_URL}/${CONTAINER_NAME}:latest '
                            """
                        }
                    }
                }    
            }
        }
    }
    post {
        always {
            discordSend(
                description: "Jenkins Pipeline Build ${currentBuild.currentResult}",
                link: env.GIT_URL,
                result: currentBuild.currentResult,
                title: JOB_NAME,
                webhookURL: env.DISCORD_WEBHOOK
            )
        }
    }
}

GCR uchun

pipeline {
    agent any
 
    environment {
        DISCORD_WEBHOOK = credentials('discord-webhook')
        GIT_URL = 'https://github.com/ismoilovdevml/devops-journey.git'
        GCR_CREDENTIALS = credentials('gcr-key')
        GCP_PROJECT_ID = credentials('gcp-project-id')
        CONTAINER_NAME = 'devops-journey'
        REGISTRY_URL = 'gcr.io'
        SERVER_USERNAME = credentials('server-username')
        SERVER_IP = credentials('server-ip')
        SSH_CREDENTIALS = credentials('server-ssh')
        GIT_TOKEN = credentials('git-token')
        BRANCH_NAME = 'main'
    }
 
    stages {
        stage('Clean Workspace') {
            steps {
                cleanWs()
            }
        }
 
        stage('Clone Repository') {
            steps {
                git branch: BRANCH_NAME, url: GIT_URL, credentialsId: 'git-token'
            }
        }
 
        stage('Build and Push Docker Image') {
            steps {
                script {
                    def dockerImage = "${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:${BUILD_NUMBER}"
 
                    sh """
                        cat ${GCR_CREDENTIALS} | docker login -u _json_key --password-stdin https://gcr.io
                        docker build . -t ${dockerImage} -f Dockerfile
                        docker push ${dockerImage}
                        docker tag ${dockerImage} ${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:latest
                        docker push ${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:latest
                        docker image rm -f ${dockerImage}
                        docker image rm -f ${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:latest
                    """
                }
            }
        }
        stage('Deploy to Server') {
            steps {
                script {
                    sshagent(credentials: ['server-ssh']) {
                        // Copy the GCR credentials to the server
                        sh "scp -o StrictHostKeyChecking=no ${GCR_CREDENTIALS} ${SERVER_USERNAME}@${SERVER_IP}:/tmp/gcr-key.json"
                        // Log in to GCR on the server using the copied credentials
                        sh """
                            ssh -o StrictHostKeyChecking=no ${SERVER_USERNAME}@${SERVER_IP} \
                            'cat /tmp/gcr-key.json | docker login -u _json_key --password-stdin https://gcr.io'
                        """
                        // Continue with the rest of the deployment steps
                        sh """
                            ssh -o StrictHostKeyChecking=no ${SERVER_USERNAME}@${SERVER_IP} '\
                            docker pull ${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:latest && \
                            docker stop ${CONTAINER_NAME} || true && \
                            docker rm ${CONTAINER_NAME} || true && \
                            docker run -d -p 3000:3000 --name ${CONTAINER_NAME} --restart always ${REGISTRY_URL}/${GCP_PROJECT_ID}/${CONTAINER_NAME}:latest '
                        """
                    }
                }
            }
        }
    }
    post {
        always {
            script {
                try {
                    // Delete the copied GCR credentials from the server
                    sshagent(credentials: ['server-ssh']) {
                        sh """
                            ssh -o StrictHostKeyChecking=no ${SERVER_USERNAME}@${SERVER_IP} \
                            'rm /tmp/gcr-key.json'
                        """
                    }
                } catch (Exception e) {
                    echo "Error cleaning up GCR credentials: ${e.message}"
                    currentBuild.result = 'FAILURE' // Set build result to FAILURE in case of errors
                } finally {
                    // Send Discord notification regardless of the build result
                    discordSend(
                        description: "Jenkins Pipeline Build ${currentBuild.currentResult}",
                        link: GIT_URL,
                        result: currentBuild.currentResult,
                        title: JOB_NAME,
                        webhookURL: DISCORD_WEBHOOK
                    )
                }
            }
        }
    }
}

DEV, STAGE, PROD

Loyihani uch bosqichda DEV, STAGE, PROD bosqichli qilish uchun quyidagi usuldan foydalanishimiz mumkin, yani jenkinsda loyiha nomli jild(folder) ochib olamiz va dev, stage, prod nomli joblar ochamiz dev pipelineda dev serverlar va gitdagi dev branch stage ham shunday gitdagi stage branch va stage serverlar va prod main branch va yoki prod branchdan kodlarni olib prod serverlarga deploy qilib qo'yadi.

1-> Jenkinsda loyiha nomli folder ochib olamiz masalan devops-journey -> Create a job -> Folder

loyiha nomini yozamiz va Folderni tanlab OK bosamiz.

Undan keyingi ochilgan oynada Folderga Description yozib yaratib olamiz

Keyin bizda quyidagi oyna ochiladi

Endi dev,sateg,prod nomli joblar yaratib olamiz va shunga moslab configuratsiya qilib olamiz. Masalan dev jobda dev nomli branchdagi kodlarni olib uni build qilib dev serverda ishga tushiradi, qolgan stage va prod ham xuddi shunday ishlaydi.

Qo'shimcha