3.1 Лабораторийн зорилго
Энэ лабораторид та:
- GitHub Actions ашиглан Java/Spring Boot CI pipeline үүсгэх
- Автомат тест, код чанарын шалгалт нэмэх
- Docker image бүтээх pipeline нэмэх
- Multi-environment deploy тохируулах
Хэл: Java 17+ / Spring Boot 3.x | CI/CD: GitHub Actions | Container: Docker
3.2 Лаб 1: GitHub Actions CI Pipeline үүсгэх
Алхам 1: Хавтасын бүтэц
student-api/
├── .github/
│ └── workflows/
│ └── ci.yml ← CI Pipeline
├── src/
│ ├── main/java/...
│ └── test/java/...
├── Dockerfile ← Docker image
├── docker-compose.yml ← Олон container
├── pom.xml
└── README.md
Алхам 2: CI Workflow файл үүсгэх
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
JAVA_VERSION: '17'
jobs:
# ────────────────────────────────────────
# Job 1: Build + Unit Test
# ────────────────────────────────────────
build:
name: Build & Unit Test
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK ${{ env.JAVA_VERSION }}
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Build and Run Unit Tests
run: mvn clean verify -DskipITs
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: target/surefire-reports/
- name: Upload JAR Artifact
uses: actions/upload-artifact@v4
with:
name: app-jar
path: target/*.jar
Алхам 3: Push хийж Pipeline ажиллуулах
# 1. Хавтас үүсгэх
mkdir -p .github/workflows
# 2. ci.yml файл нэмэх (дээрх агуулга)
# 3. Commit + Push
git add .github/workflows/ci.yml
git commit -m "ci: add GitHub Actions CI pipeline"
git push origin main
# 4. GitHub → Actions tab → Pipeline ажиллаж буйг харах
3.3 Лаб 2: Тест + Code Coverage нэмэх
Алхам 1: JaCoCo нэмэх (pom.xml)
<!-- pom.xml дотор plugins хэсэгт -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
Алхам 2: CI Pipeline-д Coverage нэмэх
# ci.yml-д нэмэх (build job-ийн steps дотор)
- name: Generate Code Coverage Report
run: mvn jacoco:report
- name: Upload Coverage Report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: target/site/jacoco/
- name: Check Coverage Threshold
run: |
COVERAGE=$(grep -oP 'Total.*?(\d+)%' target/site/jacoco/index.html | grep -oP '\d+' | head -1)
echo "Code Coverage: ${COVERAGE}%"
if [ "$COVERAGE" -lt 70 ]; then
echo "❌ Coverage ${COVERAGE}% is below 70% threshold!"
exit 1
fi
echo "✅ Coverage ${COVERAGE}% meets threshold"
3.4 Лаб 3: Docker Image бүтээх Pipeline
Алхам 1: Dockerfile үүсгэх
# Dockerfile
# Multi-stage build — Жижиг image
FROM eclipse-temurin:17-jdk-alpine AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN apk add --no-cache maven && mvn clean package -DskipTests
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Алхам 2: Docker Build Pipeline нэмэх
# .github/workflows/ci.yml дотор шинэ job нэмэх
docker:
name: Build Docker Image
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push Docker Image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/student-api:latest
${{ secrets.DOCKER_USERNAME }}/student-api:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
Алхам 3: Docker Compose (Локал тест)
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: dev
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/studentdb
SPRING_DATASOURCE_USERNAME: admin
SPRING_DATASOURCE_PASSWORD: secret
depends_on:
db:
condition: service_healthy
db:
image: postgres:15-alpine
ports:
- "5432:5432"
environment:
POSTGRES_DB: studentdb
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U admin -d studentdb"]
interval: 5s
timeout: 5s
retries: 5
volumes:
postgres_data:
# Ажиллуулах
docker-compose up -d
# Шалгах
curl http://localhost:8080/actuator/health
# Зогсоох
docker-compose down
3.5 Лаб 4: Multi-Environment Deploy Pipeline
Бүрэн CI/CD Workflow
# .github/workflows/deploy.yml
name: Deploy Pipeline
on:
push:
branches: [ main ]
jobs:
# ──── 1. Build + Test ────
build:
name: Build & Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Build & Test
run: mvn clean verify
- uses: actions/upload-artifact@v4
with:
name: app-jar
path: target/*.jar
# ──── 2. Docker Build ────
docker:
name: Docker Build & Push
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/student-api:${{ github.sha }}
# ──── 3. Deploy to Staging ────
deploy-staging:
name: Deploy to Staging
needs: docker
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to Staging Server
run: |
echo "Deploying to Staging..."
echo "Image: student-api:${{ github.sha }}"
# kubectl set image deployment/student-api \
# student-api=${{ secrets.DOCKER_USERNAME }}/student-api:${{ github.sha }} \
# --namespace=staging
# ──── 4. Deploy to Production (Manual Approval) ────
deploy-production:
name: Deploy to Production
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to Production Server
run: |
echo "Deploying to Production..."
echo "Image: student-api:${{ github.sha }}"
# kubectl set image deployment/student-api \
# student-api=${{ secrets.DOCKER_USERNAME }}/student-api:${{ github.sha }} \
# --namespace=production
Pipeline визуал:
Push to main
│
▼
┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌──────────────────┐
│ Build │───►│ Docker │───►│ Staging │───►│ Production │
│ & Test │ │ Build │ │ Deploy │ │ Deploy │
│ (auto) │ │ (auto) │ │ (auto) │ │ (manual ✋) │
└──────────┘ └──────────┘ └──────────────┘ └──────────────────┘
mvn verify docker push kubectl apply Approve → Deploy