Java Spring Boot Deployment: Gitlab CI va Github Actions
Ushbu qo'llanma Java Spring Boot applicationlarnni GitLab CI/CD va GitHub Actions yordamida avtomatlashtirilgan tarzda deploy qilishni o'rganib chiqamiz. Bu amaliyot DevOps Engineerlar va Java dasturchilar uchun zarur bo'lgan bilim va ko'nikmalarni qamrab oladi. Qo'llanmada Java Spring Boot applicationining tuzilishini tushunish va konfiguratsiya qilish jarayonlari ko'rib chiqamiz.
Shuningdek, biz Dockerfile yozishni o'rganamiz va uni samarali ishlashi uchun optimallashtiramiz. Docker asosida konteynerlash jarayonlarini o'zlashtirgach, GitLab CI/CD va GitHub Actions yordamida CI/CD pipeline'larini sozlash va avtomatlashtirish usullarini ko'rib chiqamiz.
Ushbu qo'llanma orqali siz Java Spring Boot applicationlari uchun CI/CD jarayonlarini muvaffaqiyatli amalga oshirish bo'yicha amaliy yondashuvga ega bo'lasiz va real loyihalarda bu usullarni qo'llash imkoniyatiga ega bo'lasiz.
Ushbu amaliyotda quyidagi Java Spring Boot application kodlaridan foydalanamiz Gitlab-> gitlab.com/ismoilovdev/waifulist (opens in a new tab), Github-> github.com/devops-journey-uz/waifulist (opens in a new tab)
Loyihani tushunish
Spring Boot loyihasini muvaffaqiyatli deploy qilish uchun dastlab uning tuzilishini, ishlash prinsipini va muhim konfiguratsiyalarini to'liq tushunish lozim. Spring Boot loyihasi asosan quyidagi tarkibiy qismlardan iborat:
Masalan bizning loyihamiz tuzulishi:
/waifulist
├── Dockerfile
├── HELP.md
├── LICENSE
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── zawkin
│ │ └── me
│ │ └── asuna
│ │ ├── config
│ │ │ └── SwaggerConfig.java
│ │ ├── controller
│ │ │ └── WaifuController.java
│ │ ├── dto
│ │ │ └── WaifuDTO.java
│ │ ├── entity
│ │ │ └── WaifuEntity.java
│ │ ├── repository
│ │ │ └── WaifuRepository.java
│ │ ├── service
│ │ │ └── WaifuService.java
│ │ └── WaifulistApplication.java
│ └── resources
│ └── application.properties
└── test
└── java
└── zawkin
└── me
└── asuna
└── WaifulistApplicationTests.java
src/main/java/zawkin/me/asuna
katalogida loyihaning asosiy Java fayllari joylashgan. WaifulistApplication.java
fayli Spring Boot applicationning asosiy dastur kodi hisoblanadi.
package zawkin.me.asuna;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WaifulistApplication {
public static void main(String[] args) {
SpringApplication.run(WaifulistApplication.class, args);
}
}
@SpringBootApplication
anotatsiyasi Spring Boot applicationning asosiy konfiguratsiyalarini o'z ichiga oladi. main
metodida esa application ishga tushiriladi.
src/main/resources
katalogida esa applicationning konfiguratsiyalarini saqlash uchun fayllar joylashgan. Bu katalogda application.properties
yoki application.yml
joylashadi. Bizning loyihamizda application.properties
fayli mavjud.
spring.application.name=waifulist
# DATABASE
spring.datasource.url=jdbc:postgresql://134.209.217.179:5432/waifulist
spring.datasource.username=postgres
spring.datasource.password=lwfjljqwotpreqwt2
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
logging.level.org.springframework.web=DEBUG
logging.level.org.springdoc=DEBUG
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always
application.properties
faylida applicationning konfiguratsiyalarini saqlash uchun kerak bo'lgan sozlamalar joylashgan. Masalan, spring.datasource.url
sozlamasi PostgreSQL bazasiga ulanish uchun kerak bo'lgan ma'lumotlarni saqlaydi.
pom.xml
yoki build.gradle
fayllari dependencylar, pluginlar va boshqa loyihaning konfiguratsiyalarini saqlaydi. Bizning loyihamizda pom.xml
fayli mavjud, ya'ni Maven loyihasi agar build.gradle
fayli mavjud bo'lsa Gradle loyihasi deb hisoblanadi.
src/test/java/zawkin/me/asuna
katalogida loyihaning test fayllari joylashgan. WaifulistApplicationTests.java
fayli Spring Boot applicationning test kodi hisoblanadi.
package zawkin.me.asuna;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class WaifulistApplicationTests {
@Test
void contextLoads() {
}
}
Spring Boot loyihasini muvaffaqiyatli ishlashi uchun application.properties
yoki application.yml
fayllari muhim ahamiyatga egadir. Applicationning barcha konfiguratsiyalarini bu fayllar orqali o'zgartirish mumkin. Masalan bizning loyihamizdagi properties
konfiguratsiya faylini ko'rib chiqamiz.
spring.application.name=waifulist
# DATABASE
spring.datasource.url=jdbc:postgresql://134.209.217.179:5432/waifulist
spring.datasource.username=postgres
spring.datasource.password=lwfjljqwotpreqwt2
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
logging.level.org.springframework.web=DEBUG
logging.level.org.springdoc=DEBUG
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always
Bu xavsfzilik uchun yaxshi yechim emas chunki secretlar ochiq holda turibdi buni Gitlab CI variable yoki Github Secretsga qo'shib ishlatishni ko'rib chiqamiz bu amaliyotda.
Qiziq savol tu'giladi nega ba'zi loyihalarda application.properties
fayli bor ba'zilarida esa application.yml
fayli bor?
application.properties
va application.yml
o'rtasidagi far quyidagicha:
Xusuysatlar | application.properties | application.yml |
---|---|---|
Format | Oddiy key=value formatida yoziladi. | YAML formatida, ierarxik struktura ko'rinishida. |
O'qilish qulayligi | Kichik loyihalar uchun qulay. | Murakkab konfiguratsiyalarda o'qish oson. |
Profil boshqaruvi | Har bir profil uchun alohida fayl yaratiladi. | Profil boshqaruvi oson va YAML ichida qilinadi. |
Bu misolda application-dev.properties
va application-prod.properties
fayllarini alohida yaratish kerak bo'lsa, application-dev.yml
va application-prod.yml
fayllarini alohida yaratish kerak emas. YAML formati ierarxik struktura ko'rinishida yoziladi, shuning uchun profil boshqaruvi oson va qulaydir.
spring:
profiles:
active: dev
---
spring:
profiles: dev
datasource:
url: jdbc:postgresql://dev-db-url
---
spring:
profiles: prod
datasource:
url: jdbc:postgresql://prod-db-url
Multi environment boshqaruvda application.properties
va application.yml
fayllarini quyidagicha ishlatamiz.
Masalan bizda dev
, stage
, prod
environmentlar uchun alohida konfiguratsiyalar mavjud bo'lsa, application-dev.properties
, application-stage.properties
, application-prod.properties
fayllarini yaratamiz. Bu fayllar alohida environmentlar uchun kerak bo'lgan konfiguratsiyalarni saqlaydi, agar application.yml
faylini ishlatishni xohlasak, uni application-dev.yml
, application-stage.yml
, application-prod.yml
yoki bitta application.yml
faylida yozishimiz mumkin.
CI/CD'da ishga tushirishda esa docker run -e SPRING_PROFILES_ACTIVE=dev
yoki docker run -e SPRING_PROFILES_ACTIVE=prod
kabi environmentlar orqali alohida environmentni tanlashimiz mumkin.
Dockerfile yozish
Agar siz Docker bilan tanish bo'lmasangiz quyidagi qo'llanmalar orqali Docker bilan tanishingiz mumkin: Dockerga Kirish (opens in a new tab), Mastering Docker (opens in a new tab), Dockerfile yozish (opens in a new tab), Docker o'rnatish (opens in a new tab)
Spring Boot applicationni Docker konteyneriga joylash uchun Dockerfile
yozish kerak. Dockerfile
fayli konteynerning tuzilishini, ishlash prinsiplarini va muhim konfiguratsiyalarini saqlaydi. Docker orqali applicationlarimizni tezroq va xavfsiz tarzda deploy qilishimiz mumkin.
FROM maven:3.9.5-eclipse-temurin-17-alpine AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B
FROM eclipse-temurin:17-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
RUN chown -R appuser:appgroup /app
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget --spider --quiet http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]
Dockerfileni ko'rib chiqadigan bo'lsa, u quyidagi qadamlardan iborat:
Dockerfile ikki qismga bo'linadi, build bosqichi(build stage) va runtime bosqichi(runtime stage). Build stageda Maven asosida loyihani build qilish va kerakli librarylarni yuklab olish uchun ishlatiladi va bunda biz yengil va kichik alpine imagedan foydalanamiz. runtime stageda esa yengil eclipse-temurin:17-alpine
imagedan foydalanamiz bu Spring Boot uchun eng optimal JDK imagedir. Birinchi bosqich 1-6 qadamlar Maven asosida loyihani build qilish uchun kerakli jar faylini yaratadi. Ikkinchi bosqich 7-14 qadamlar esa jar faylini ishga tushiradi.
FROM maven:3.9.5-eclipse-temurin-17-alpine AS builder
- Maven va Java 17 asosida Docker imageni yaratamiz bu bizningbuilder
imagemiz bo'ladi.WORKDIR /app
-/app
papkasiga o'tamiz yani barcha ishlarimizni bu papkada amalga oshiramiz.COPY pom.xml .
-pom.xml
faylini/app
papkasiga ko'chiramiz bu faylda Maven dependencylar saqlanadi.RUN mvn dependency:go-offline -B
- Maven dependencylarini yuklab olamiz.COPY src ./src
-src
katalogini/app/src
papkasiga ko'chiramiz bu katalogda Java fayllar joylashgan.RUN mvn package -DskipTests -B
- Spring Boot applicationni jar faylini yaratamiz va testlarni ishga tushirmasdan ishga tushiramiz.FROM eclipse-temurin:17-alpine
- Java 17 asosida Docker imageni yaratamiz bu bizning asosiy imagemiz bo'ladi.RUN addgroup -S appgroup && adduser -S appuser -G appgroup
-appgroup
vaappuser
guruhlarini yaratamiz bu xavfsizlik uchun kerak bo'ladi.WORKDIR /app
-/app
papkasiga o'tamiz.COPY --from=builder /app/target/*.jar app.jar
-builder
imagedan jar faylini/app
papkasiga ko'chiramiz bu jar fayl Spring Boot applicationni ishga tushirish uchun kerak bo'ladi.RUN chown -R appuser:appgroup /app
-/app
papkasigaappuser
vaappgroup
guruhlariga ega bo'lgan permissionlarni beramizUSER appuser
-appuser
foydalanuvchiga o'tamiz.EXPOSE 8080
- 8080 portni ochamiz.HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD wget --spider --quiet http://localhost:8080/actuator/health || exit 1
- Spring Boot applicationningactuator/health
endpointiga HTTP so'rov yuborishni tekshiramiz va 3 marta urinishdan keyin xatolik chiqsa konteynerni qayta ishga tushiramiz, bu helatchek deyiladi.ENTRYPOINT ["java", "-jar", "app.jar"]
- Spring Boot applicationni ishga tushiramiz.
Eslatma: Ushbu helatchekni ishlatish uchun Spring Boot applicationda spring-boot-starter-actuator
dependencyni qo'shishingiz kerak bo'ladi.
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget --spider --quiet http://localhost:8080/actuator/health || exit 1
pom.xml
faylida spring-boot-starter-actuator
dependencyni qo'shamiz.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
.properties
yoki .yml
fayllariga quyidagi konfiguratsiyalarni ham qo'shish kerak bo'ladi.
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always
Konfiguratsiya fayllari bilan ishlash
Spring Boot applicationni Docker konteyneriga joylashda application.properties
yoki application.yml
fayllarini ishlatish kerak bo'ladi. Bu fayllar applicationning konfiguratsiyalarini saqlash uchun kerak bo'ladi. Bizning loyihamizda application.properties
fayli mavjud lekin unda secretlar ochiq holda shuning uchun secretlarni Gitlab CI variable yoki Github Secretsga qo'shib multi-environment qilib sozlashimiz kerak bo'ladi. src/main/resources
katalogida application-dev.properties
va application-prod.properties
fayllarini yaratamiz va ularni alohida environmentlar uchun kerakli konfiguratsiyalarni saqlaymiz.
application-dev.properties
faylida dev environment uchun kerakli konfiguratsiyalar saqlanadi.
spring.application.name=waifulist
# DATABASE
spring.datasource.url=${DEV_DATABASE_URL}
spring.datasource.username=${DEV_DATABASE_USERNAME}
spring.datasource.password=${DEV_DATABASE_PASSWORD}
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
logging.level.org.springframework.web=DEBUG
logging.level.org.springdoc=DEBUG
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always
application-prod.properties
faylida esa prod environment uchun kerakli konfiguratsiyalar saqlanadi.
spring.application.name=waifulist
# DATABASE
spring.datasource.url=${PROD_DATABASE_URL}
spring.datasource.username=${PROD_DATABASE_USERNAME}
spring.datasource.password=${PROD_DATABASE_PASSWORD}
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
logging.level.org.springframework.web=DEBUG
logging.level.org.springdoc=DEBUG
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always
VM Tayyorlash
CI/CD deployment bosqichida biz applicationimizni vm(virtual machine)ga deploy qilamiz bu uchun vm tayyorlashimiz kerak bo'ladi, yani pipeline serverga ssh bilan ulanib ishlaydigan qismi bor shu qismi uchun ssh sozlashimiz va docker o'rnatishimiz kerak bo'ladi chuni applicationimizni dockerda ishga tushiramiz.
Docker o'rnatish uchun quyidagi qo'llanmadan foydalanishingiz mumkin - Linux serverlarga Docker o'rnatish (opens in a new tab)
CD pipelien serverga ssh orqali kira olishi kerak buning uchun serverda ssh-key generatsiya qilishimiz kerak bo'ladi.
ssh-keygen
Yuqoridagi buyruqni yozib ENTERni bosib key generatsiya qilib oling
yuqorida buyruq ikkita key generatsiya qiladi public(id_rsa.pub
) va private(id_rsa
) keylar, bu keylar ~/.ssh
papkasiga saqlanadi.
CD pipeline serverga kira olishi uchun private(id_rsa
) keyni authorized_keys
fayliga qo'shib chiqishimiz kerak bo'ladi.
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
Keyin esa private(id_rsa
) keyni Gitlab CI variable yoki Github Secretsga SSH_PRIVATE_KEY
nomi bilan qo'shamiz private keyni cat ~/.ssh/id_rsa
buyruqini ishga tushirib olishimiz mumkin.
cat ~/.ssh/id_rsa
Gitlab CI/CD pipeline
Okey yuqorida biz loyihani tushundik, Dockerfile yozdik va konfiguratsiyalarimizni sozladik endi Gitlab CI/CD pipeline'larimizni yozamiz. Gitlab CI/CD pipeline'larini yozish uchun .gitlab-ci.yml
faylini loyihaning asosiy katalogiga yaratamiz.
CI pipeline
Birinchi navbatda loyihani test qilish uchun CI pipeline'ni yozamiz.
stages:
- build_and_push
variables:
IMAGE_NAME: waifulist
REPO_NAME: $CI_PROJECT_PATH
REGISTRY: "registry.gitlab.com"
build_and_push:
stage: build_and_push
image: docker:stable
services:
- docker:dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $REGISTRY/$REPO_NAME/$IMAGE_NAME:$CI_COMMIT_SHA .
- docker push $REGISTRY/$REPO_NAME/$IMAGE_NAME:$CI_COMMIT_SHA
Ushbu CI pipeline'ni tushunish uchun quyidagi qadamlarni ko'rib chiqamiz:
Agar siz Gitlab CI/CD bilan tanish bo'lmasangiz quyidagi qo'llanmalar orqali Gitlab CI/CD bilan tanishingiz mumkin: Gitlab CI bilan CI/CD (opens in a new tab), Gitlab CI | Releaselar va Integrationlar (opens in a new tab).
stages
- Pipeline'ning bosqichlarini bildiradi bunda faqatbuild_and_push
bosqich bor.variables
- Pipeline'ning ishlatadigan o'zgaruvchilar belgilanadi bu yerdaIMAGE_NAME
,REPO_NAME
,REGISTRY
o'zgaruvchilari belgilangan container registry sifatida gitlab container registry ishlatilgan.build_and_push
- Pipeline'ning birinchi bosqichidir bu bosqichda loyihani build qilish va Gitlab registryga imageni push qilish amalga oshiriladi.image
- Docker imageni ishlatish uchundocker:stable
imageni ishlatamiz.services
- Docker daemonni ishlatish uchundocker:dind
servisini ishlatamiz.before_script
- Pipeline boshlanishida Gitlab registryga kirish uchun bajariladigan amallar belgilanadi bu holdadocker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
amalini bajaradi yani Gitlab registryga kiradi.script
- Docker imageni build qilish va Gitlab registryga push qilish amallari belgilanadi bu holdadocker build -t $REGISTRY/$REPO_NAME/$IMAGE_NAME:$CI_COMMIT_SHA .
vadocker push $REGISTRY/$REPO_NAME/$IMAGE_NAME:$CI_COMMIT_SHA
amallari bajariladi.
Buni qisqacha qilib tushuntiradigan bo'lsam CI pipeline belgilangan branchda o'zgarish bo'lsa vatomatik ishga tushadi va docker login
qilib Gitlab Container Registrga kiradi va docker yordamida loyihani build qilib uni Gitlab Container Registryga imageni push qiladi.
.gilab-ci.yml
faylini yuqoridagidek konfiguratsiya yozib gitlabga push qilamiz va bizda avtomatik CI pipeline ishga tushadi.
Ko'k rang bilan ishlab turgan job ustiga bosib CI pipeline'ni ko'rishimiz mumkin va u ochilganda biz build_and_push
jobini ko'rishimiz mumkin. Joblar ko'k rangda pipeline ishga tushayotganini bildiradi.
Okey bizning CI pipeline muvaffaqiyatli ishga tushdi va Gitlab registryga imageni push qildik endi uni tekshirib ko'rishimiz kerak bo'ladi.
Repositoriyadagi -> Deploy -> Container Registry bo'limiga o'tib CI pipeline orqali build qilingan imageni ko'rishimiz mumkin.
Mana bizning waifulist
docker imagemiz.
Biz CI bosqichini muvaffaqiyatli ishga tushirdik endi esa CD pipeline'ni yozamiz.
CD pipeline
CD pipelineda biz CI bosqichida build bo'lgan docker imageni Gitlab registrydan olib, ssh orqali vm'ga kirib docker konteynerini ishga tushiramiz. Bu amaliyotda deploy qilish uchun vm(virtual mashina) ishlatamiz, CD pipelineda serverga kirib docker konteynerini ishga tushiramiz.
Buning uchun .gitlab-ci.yml
faylini quyidagicha o'zgartiramiz.
stages:
- build_and_push
- deploy
variables:
IMAGE_NAME: waifulist
CONTAINER_NAME: waifulist
PORT: "8080:8080"
REPO_NAME: $CI_PROJECT_PATH
REGISTRY: "registry.gitlab.com"
SSH_HOST: $SERVER_IP
SSH_USER: $SERVER_USERNAME
SSH_KEY: $SSH_PRIVATE_KEY
SPRING_PROFILES_ACTIVE: dev
DEV_DATABASE_URL: $DEV_DATABASE_URL
DEV_DATABASE_USERNAME: $DEV_DATABASE_USERNAME
DEV_DATABASE_PASSWORD: $DEV_DATABASE_PASSWORD
build_and_push:
stage: build_and_push
image: docker:stable
services:
- docker:dind
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script:
- docker build -t "$REGISTRY/$REPO_NAME/$IMAGE_NAME:$CI_COMMIT_SHA" .
- docker push "$REGISTRY/$REPO_NAME/$IMAGE_NAME:$CI_COMMIT_SHA"
deploy:
stage: deploy
image: alpine:latest
before_script:
- apk add --update --no-cache openssh-client
script:
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan -H $SSH_HOST >> ~/.ssh/known_hosts
- ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST "echo "$CI_JOB_TOKEN" | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY"
- ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST "docker pull $REGISTRY/$REPO_NAME/$IMAGE_NAME:$CI_COMMIT_SHA"
- ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST "docker stop $CONTAINER_NAME || true"
- ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST "docker rm $CONTAINER_NAME || true"
- ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no "$SSH_USER@$SSH_HOST" "docker run -d --name $CONTAINER_NAME --restart=always -p $PORT -e SPRING_PROFILES_ACTIVE=$SPRING_PROFILES_ACTIVE -e DEV_DATABASE_URL=$DEV_DATABASE_URL -e DEV_DATABASE_USERNAME=$DEV_DATABASE_USERNAME -e DEV_DATABASE_PASSWORD=$DEV_DATABASE_PASSWORD $REGISTRY/$REPO_NAME/$IMAGE_NAME:$CI_COMMIT_SHA"
Ushbu CD pipeline'ni tushunish uchun quyidagi qadamlarni ko'rib chiqamiz:
stages
- gadeploy
bosqichini qo'shamiz bu bosqichda loyihani deploy qilish amalga oshiriladi.variables
- ga yangi o'zgaruvchilar qo'shamiz bu o'zgaruvchilarCONTAINER_NAME
,PORT
,SSH_HOST
,SSH_USER
,SSH_KEY
,SPRING_PROFILES_ACTIVE
,DEV_DATABASE_URL
,DEV_DATABASE_USERNAME
,DEV_DATABASE_PASSWORD
o'zgaruvchilari belgilangan.deploy
- Pipeline'ning ikkinchi bosqichidir bu bosqichda loyihani deploy qilish amalga oshiriladi bu bosqichda ssh orqali serverga kirib docker konteynerini ishga tushiramiz.image
- Docker imageni ishlatish uchunalpine:latest
imageni ishlatamiz.before_script
- Pipeline boshlanishidaopenssh-client
ni o'rnatish uchun bajariladigan amallar belgilanadi.script
- SSH orqali serverga kirib docker konteynerini ishga tushirish amallari belgilanadi.mkdir -p ~/.ssh
-~/.ssh
papkasini yaratamiz bu papkada ssh keylar saqlanadi.echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- SSH private keyni~/.ssh/id_rsa
fayliga yozamiz bu key serverga kirish uchun kerak bo'ladi.chmod 600 ~/.ssh/id_rsa
-~/.ssh/id_rsa
faylining permissionlarni belgilaymiz bu keyni faqat foydalanuvchi o'qishi mumkin.ssh-keyscan -H $SSH_HOST >> ~/.ssh/known_hosts
- SSH hostning public keyini~/.ssh/known_hosts
fayliga yozamiz bu hostga birinchi marta ulanishda xatolik chiqmasligi uchun kerak bo'ladi.ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST "
bu amal SSH orqali serverga kirish amaliyotini bajaradi.echo "$CI_JOB_TOKEN" | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY"
- Gitlab registryga kirish uchun bajariladigan amal buning sababi Gitlab container registryga kirish va CI pipelineda yaratilgan imageni olish uchun kerak bo'ladi.docker pull $REGISTRY/$REPO_NAME/$IMAGE_NAME:$CI_COMMIT_SHA
- Gitlab registrydan imageni pull qilib oladi.docker stop $CONTAINER_NAME || true
- Bu eski Docker konteynerini to'xtatadi.docker rm $CONTAINER_NAME || true
- Bu eski Docker konteynerini o'chiradi.docker run -d --name $CONTAINER_NAME -p $PORT -e SPRING_PROFILES_ACTIVE=$SPRING_PROFILES_ACTIVE -e DEV_DATABASE_URL=$DEV_DATABASE_URL -e DEV_DATABASE_USERNAME=$DEV_DATABASE_USERNAME -e DEV_DATABASE_PASSWORD=$DEV_DATABASE_PASSWORD $REGISTRY/$REPO_NAME/$IMAGE_NAME:$CI_COMMIT_SHA
- Bu buyruq esa Gitlab CI pipelineda yaratilgan imageni belgilangan variablelar bilan ishga tushiradi, yani belgilangan portda, belgilangan nomda, belgilangan environment variablelar bilan ishga tushiradi.
Qisqacha qilib CD pipeline tushuntiradigan bo'lsam u alpine:latest
imagedan foydalanib unga openssh-clientni o'rnatadi bu serverga ssh orqali kirish uchun kerak va SSH_HOST
, SSH_USER
, SSH_KEY
o'zgaruvchilardan foydalanib serverga ssh bilan kiradi va docker login
qilib Gitlab Container Registryga kiradi va CI pipelineda build qilinib yaratilgan docker imageni pull qilib oladi va eski ishlab turgan containerni to'xtatib o'chirib yangi containerni belgilangan o'zgaruvchilar va secretlar bilan ishga tushiradi.
Bu CD pipelineni ishga tushirish uchun kerakli secretlarni Gitlab CI variablega qo'shib chiqamiz.
Yuqorida biz VM tayyorlash bosqichida serverga docker install qilib ssh-key generatsiya qilgandik endi shu keylarni Gitlab CI variablega qo'shib chiqishimiz kerak bo'ladi.
-> Settings -> CI/CD -> Variables bo'limiga o'tamiz va SERVER_IP
nomli variablega server ip manzilini(Gitlab bo'glana oladigan IP) yozamiz.
SERVER_USERNAME
nomli variablega serverga kirish uchun user nomini yozamiz, userni whoami
buyrug'ini ishga tushirib olishimiz mumkin.
whoami
SSH_PRIVATE_KEY
nomli variablega esa yuqorida yaratib olgan private keyni yozamiz.
Environment variablelarni Gitlab CI variablega qo'shib chiqishimiz kerak bo'ladi. Repositoriyaga o'tib -> Settings -> CI/CD -> Variables bo'limiga o'tamiz va kerakli environment variablelarni qo'shib chiqamiz.
DEV_DATABASE_URL
ga jdbc:postgresql://134.209.217.179:5432/waifulist
formatda secret keyni yozamiz buyerda IP manzil PostgreSQL server manzili va porti waifulist
esa database nomi bo'ladi.
DEV_DATABASE_USERNAME
ga postgres
secret keyni yozamiz buyerda PostgreSQL serverga ulanish uchun kerak bo'lgan foydalanuvchi nomi.
DEV_DATABASE_PASSWORD
ga lwfjljqwotpreqwt2
secret keyni yozamiz bu PostgreSQL serverga yuqoria belgilangan user bilan ulanish uchun kerak user paroli(yani postgres
user paroli).
Gitlab CI variablega secret keylarni qo'shib chiqdik endi Gitlab CI/CD pipeline'larimizda bu secret keylarni ishlatishimiz mumkin.
Gitlab CI variablelarni to'gri qo'yib chiqganimizdan keyin yangilangan .gitlab-ci.yml
faylini gitlabga push qilamiz va avtomatik CI/CD pipeline ishga tushadi.
Joblar ro'yxatini ko'rsak bu safar biz build_and_push
va deploy
joblarini ko'ramiz.
Okeey pipeliene muvaffaqiyatli ishga tushdi endi esa uni tekshirib ko'rishimiz kerak bo'ladi.
Serverga kirib docker containerlar ro'yxatini ko'ramiz.
docker ps
Docker containerlar ro'yxatida waifulist
nomli containerni ko'rib turibmiz porti 8080
va statusi healthy bo'lganini ko'ramiz, keling endi bu API'ning swagger interfeysiga kirib ko'ramiz, bunig uchun brauzerdan http://server-ip::8080/swagger-ui/index.html
manziliga kirib ko'ramiz bizda API swagger interfeysi ochilishi kerak.
Keling endi PostgreSQL databazani tekshirib ko'ramiz.
Github Actions CI/CD
Bu amaliyotimizda biz ham Gitlab CI bilan CI/CD pipeline va Github Actions bilan CI/CD pipeline yozamiz. Yuqorida biz Gitlab CI bilan pipeline yozishni ko'rib chiqdik bu qismda biz Github Actions bilan CI/CD pipeline yozamiz.
Agar siz Github Actions bilan tanish bo'lmasangiz quyidagi qo'llanma orqali o'rganib chiqishingiz mumkin: Github Actions CI/CD (opens in a new tab).
CI pipeline
Github Actions CI pipeline'ni yozish uchun birinchi navbatda .github/workflows
papkasini yaratib olamiz va ichiga ci-cd.yml
faylini yaratamiz ba CI pipeline'ni quyidagicha yozamiz.
name: Github Actions CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
REPO_NAME: ${{ github.repository }}
REGISTRY: ghcr.io
jobs:
build_and_push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: "${{ env.REGISTRY }}/${{ env.REPO_NAME }}:${{ github.sha }}"
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.REPO_NAME }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.REPO_NAME }}:buildcache,mode=max
Agar siz Github Actions CI/CD (opens in a new tab) qo'llanmasini o'qib chiqgan bo'lsangiz bu CI pipeline'ni oson tushunasiz. Qisqa qilib aytganda bu CI juda oddiy pipeline bo'lib Github Actions orqali main
branchga push qilganda yoki pull request ochganda avtomatik ishga tushadi va Docker imageni build qilib Github Container registryga push qiladi.
CI pipelineni sinash uchub Githubdagi repositoriyamiz main
branchiga push qilamiz pipeline avtomatik ishga tushishi kerak.
Rasmda Github Actions ishga tushayotganini ko'rishingiz mumkin.
Repositoriyadan -> Actions bo'limiga o'tib pipelinedagi joblarni ko'rishimiz mumkin.
Bu qismda faqat bizda CI pipelineda build_and_push
jobi ishlayotanini ko'rishimiz mumkin.
Bu rasmda esa CI pipeliendagi build_and_push
jobi muvaffaqiyatli ishga tushganini ko'rishimiz mumkin.
CI pipeline muvaffaqiyatli ishga tushdi endi esa uni teklshirib ko'rishimiz kerak bo'ladi. Biz container registry sifatida Github Container registryni ishlatdik keling container registryda CI pipelienda yaratilgan imageni tekshirib ko'ramiz, buning uchun -> Packages bo'limiga o'tib container registryda yaratilgan imageni ko'ramiz.
Ko'rib turganingizdek bizda waifulist
nomli docker image mavjud.
Biz Github Actionsda CI pipeline muvaffaqiyatli yozib ishga tushirdik endi esa CD pipeline'ni yozamiz.
CD pipeline
CD pipelineda biz CI pipeline'da build qilingan docker imageni Github Actions orqali serverga kirib docker imageni Github Container registrydan pull qilib belgilan qiymatlar bilan uni ishga tushiramiz. Buning uchun ci-cd.yml
faylini o'zgartirishlar kiritishimiz kerak bo'ladi.
name: Github Actions CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
REPO_NAME: ${{ github.repository }}
CONTAINER_NAME: waifulist
REGISTRY: ghcr.io
SSH_HOST: ${{ secrets.SERVER_IP }}
SSH_USER: ${{ secrets.SERVER_USERNAME }}
SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
PORT: 8080:8080
SPRING_PROFILES_ACTIVE: dev
jobs:
build_and_push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: "${{ env.REGISTRY }}/${{ env.REPO_NAME }}:${{ github.sha }}"
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.REPO_NAME }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.REPO_NAME }}:buildcache,mode=max
deploy:
needs: build_and_push
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Executing remote SSH commands to deploy
uses: appleboy/ssh-action@master
with:
host: "${{ env.SSH_HOST }}"
username: "${{ env.SSH_USER }}"
key: "${{ env.SSH_KEY }}"
script: |
echo ${{ secrets.GITHUB_TOKEN }} | docker login -u ${{ github.actor }} --password-stdin ${{ env.REGISTRY }}
docker pull "${{ env.REGISTRY }}/${{ env.REPO_NAME }}:${{ github.sha }}"
docker stop "${{ env.CONTAINER_NAME }}" || true
docker rm "${{ env.CONTAINER_NAME }}" || true
docker run -d --name ${{ env.CONTAINER_NAME }} --restart=always -p ${{ env.PORT }} \
-e SPRING_PROFILES_ACTIVE=${{ env.SPRING_PROFILES_ACTIVE }} \
-e DEV_DATABASE_URL=${{ secrets.DEV_DATABASE_URL }} \
-e DEV_DATABASE_USERNAME=${{ secrets.DEV_DATABASE_USERNAME }} \
-e DEV_DATABASE_PASSWORD=${{ secrets.DEV_DATABASE_PASSWORD }} \
${{ env.REGISTRY }}/${{ env.REPO_NAME }}:${{ github.sha }}
Ushbu CD pipelineni qisqacha tushintiradigan bo'lsam cd-cd.yml
ga CONTAINER_NAME
, PORT
, SSH_HOST
, SSH_USER
, SSH_KEY
, SPRING_PROFILES_ACTIVE
, DEV_DATABASE_URL
, DEV_DATABASE_USERNAME
, DEV_DATABASE_PASSWORD
nomli yangi o'zgaruvchilari qo'shildi. deploy uchun deploy
nomli job yozdik bu job faqat build_and_push
jobi tugagandan keyin ishlaydi va berilgan SSH_HOST
, SSH_USER
, SSH_KEY
secretlarini Github Secretsdan olib serverga ssh orqali kiradi va birinchi navbatda Github Container Registyrga login qiladi va build_and_push
jobida build qilingan imageni pull qilib oladi va hozir ishlab turgan eski containerni to'xtatatib belgilangan variablelar(SPRING_PROFILES_ACTIVE
, PORT
, CONTAINER_NAME
, DEV_DATABASE_URL
, DEV_DATABASE_USERNAME
, DEV_DATABASE_PASSWORD
) orqali yangi containerni ishga tushiradi. application-dev.properties
faylida dev environment uchun kerakli secretlarni esa Github Secretsdan olib ishlatadi.
Ushbu yangilangan to'liq CI/CD pipelineni ishga tushirishdan oldin kerakli secretlarni Github Secretga qo'shib chiqishimiz kerak bo'ladi.
Buning uchun -> Settings -> Secrets and variable -> New repository secret bo'limiga o'tib kerakli secretlarni qo'shib chiqamiz.
DEV_DATABASE_URL
ga jdbc:postgresql://134.209.217.179:5432/waifulist
formatda secret keyni yozamiz buyerda IP manzil PostgreSQL server manzili va porti waifulist
esa database nomi bo'ladi.
DEV_DATABASE_USERNAME
ga postgres secret keyni yozamiz buyerda PostgreSQL serverga ulanish uchun kerak bo'lgan foydalanuvchi nomi.
DEV_DATABASE_PASSWORD
ga lwfjljqwotpreqwt2
secret keyni yozamiz bu PostgreSQL serverga yuqoria belgilangan user bilan ulanish uchun kerak user paroli(yani postgres
user paroli).
Yuqorida biz VM tayyorlash bosqichida serverga docker install qilib ssh-key generatsiya qilgandik endi shu keylarni Github Actions Secretga qo'shib chiqishimiz kerak bo'ladi.
SERVER_IP
nomli variablega server ip manzilini(Github bo'glana oladigan IP) yozamiz.
SERVER_USERNAME
nomli variablega serverga kirish uchun user nomini yozamiz, userni whoami
buyruqini ishga tushirib olishimiz mumkin.
whoami
SSH_PRIVATE_KEY
nomli variablega esa yuqorida yaratib olgan private keyni yozamiz.
Bizda secretlar umumiy ro'yxati.
Okeyy biz barcha secret va variablelarni to'g'ri qo'shib chiqdik endi esa to'liq CI/CD pipelineni ishga tushirish uchun o'zgarishlarni main
branchga push qilamiz. CI/CD avtomatik ishga tushishi kerak.
Ko'rib turganingizdek bu safar bizda joblar ikkita build_and_push
va deploy
joblarini ko'ramiz va ular ketma-ketlikda ishlaydi.
Okey bizda to'liq CI/CD muvaffaqiyatli ishladi endi uni tekshirib ko'rishimiz kerak bo'ladi.
Serverga kirib docker containerlar ro'yxatini ko'ramiz.
docker ps
Bizda waifulist
nomli docker containerimiz mavjud va statusi healthy bo'lganini ko'ramiz, keling endi bu API'ning swagger interfeysiga kirib ko'ramiz, bunig uchun brauzerdan http://server-ip::8080/swagger-ui/index.html
manziliga kirib ko'ramiz bizda API swagger interfeysi ochilishi kerak.
Keling endi PostgreSQL databazani tekshirib ko'ramiz.
Niyohat biz bugun Spring Boot applicationni Docker konteynerida ishga tushirishni o'rgandik, Gitlab CI/CD va Github Actions CI/CD pipelinelarini yozdik va loyihamiz jarayonlarni avtomatlashtirdik, Spring Bootda multi-environment konfiguratsiyalar bilan ishlashni o'rgandik va secretlarimizni Gitlab CI variablega/Github Actionsga qo'shib ishladik. Bugungi amaliyot boshlang'ich darajada hisoblanadi bu orqali siz ko'p narsalarni o'rganib chiqasiz, qolganlari esa keyingi boqichlarda.
Qo'shimcha
Qo'shimcha Resurslar
- Gitlab Server o'rnatish va sozlash (opens in a new tab)
- Gitlab CI | Releaselar va Integrationlar (opens in a new tab)
- Github Actions bilan Flutter CI/CD (opens in a new tab)
- Github Actions CI/CD (opens in a new tab)
- Linux Serverlarga Jenkins o'rnatish (opens in a new tab)
- Koddan Servergacha: Jenkins bilan Docker CI/CD va Discord bilan integratsiya (opens in a new tab)
- Kubernetes CI/CD | GitHub Actions + Argo CD | GitOps (opens in a new tab)
Sana: 2025.01.26(2025-yil 26-yanvar)
Oxirgi yangilanish: 2025.01.26(2025-yil 26-yanvar)
Muallif: Otabek Ismoilov
Telegram (opens in a new tab) | GitHub (opens in a new tab) | LinkedIn (opens in a new tab) |
---|