mirror of
https://github.com/cna-bootcamp/lifesub.git
synced 2026-01-21 11:06:23 +00:00
release
This commit is contained in:
commit
6e36841dbc
297
.github/workflows/cicd.yaml
vendored
297
.github/workflows/cicd.yaml
vendored
@ -1,143 +1,274 @@
|
||||
name: Backend CI/CD Pipeline
|
||||
name: Backend Services CI/CD
|
||||
|
||||
# Temporarily disabled
|
||||
# on:
|
||||
# push:
|
||||
# branches: [ k8s ]
|
||||
|
||||
env:
|
||||
JAVA_VERSION: '21'
|
||||
GRADLE_VERSION: '8.5'
|
||||
on:
|
||||
push:
|
||||
#branches: [ cicd ]
|
||||
paths:
|
||||
- 'member/**'
|
||||
- 'mysub/**'
|
||||
- 'recommend/**'
|
||||
- 'common/**'
|
||||
- 'deployment/**'
|
||||
- '.github/workflows/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Applications
|
||||
name: Build and Test
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
image_tag: ${{ steps.set_outputs.outputs.image_tag }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
gradle-version: ${{ env.GRADLE_VERSION }}
|
||||
- name: Load environment variables
|
||||
id: env_vars
|
||||
run: |
|
||||
# Read environment variables from file
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# Skip comments and empty lines
|
||||
[[ "$line" =~ ^#.*$ ]] && continue
|
||||
[[ -z "$line" ]] && continue
|
||||
|
||||
# Extract key-value pairs
|
||||
key=$(echo "$line" | cut -d '=' -f1)
|
||||
value=$(echo "$line" | cut -d '=' -f2-)
|
||||
|
||||
# Set GitHub environment variables
|
||||
echo "$key=$value" >> $GITHUB_ENV
|
||||
done < deployment/deploy_env_vars
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew clean :member:build :mysub-infra:build :recommend:build
|
||||
./gradlew :member:build :mysub-infra:build :recommend:build -x test
|
||||
|
||||
- name: Upload Build Artifacts
|
||||
- name: Test with Gradle
|
||||
run: |
|
||||
./gradlew :member:test :member:jacocoTestReport
|
||||
./gradlew :mysub-infra:test :mysub-infra:jacocoTestReport
|
||||
./gradlew :recommend:test :recommend:jacocoTestReport
|
||||
|
||||
- name: SonarQube Analysis
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
|
||||
run: |
|
||||
./gradlew :member:sonar \
|
||||
-Dsonar.projectKey=lifesub-member \
|
||||
-Dsonar.projectName=lifesub-member \
|
||||
-Dsonar.host.url=$SONAR_HOST_URL \
|
||||
-Dsonar.token=$SONAR_TOKEN \
|
||||
-Dsonar.java.binaries=build/classes/java/main \
|
||||
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml
|
||||
|
||||
./gradlew :mysub-infra:sonar \
|
||||
-Dsonar.projectKey=lifesub-mysub \
|
||||
-Dsonar.projectName=lifesub-mysub \
|
||||
-Dsonar.host.url=$SONAR_HOST_URL \
|
||||
-Dsonar.token=$SONAR_TOKEN \
|
||||
-Dsonar.java.binaries=build/classes/java/main \
|
||||
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml
|
||||
|
||||
./gradlew :recommend:sonar \
|
||||
-Dsonar.projectKey=lifesub-recommend \
|
||||
-Dsonar.projectName=lifesub-recommend \
|
||||
-Dsonar.host.url=$SONAR_HOST_URL \
|
||||
-Dsonar.token=$SONAR_TOKEN \
|
||||
-Dsonar.java.binaries=build/classes/java/main \
|
||||
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-artifacts
|
||||
name: app-builds
|
||||
path: |
|
||||
member/build/libs/*.jar
|
||||
mysub-infra/build/libs/*.jar
|
||||
recommend/build/libs/*.jar
|
||||
|
||||
- name: Set outputs
|
||||
id: set_outputs
|
||||
run: |
|
||||
# Generate timestamp for image tag
|
||||
IMAGE_TAG=$(date +%Y%m%d%H%M%S)
|
||||
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
release:
|
||||
name: Build and Push Images
|
||||
name: Build and Push Docker Images
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
image_tag: ${{ steps.set_tag.outputs.tag }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download Build Artifacts
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: build-artifacts
|
||||
name: app-builds
|
||||
|
||||
- name: Load Environment Variables
|
||||
run: source deployment/deploy_env_vars
|
||||
|
||||
- name: Generate Image Tag
|
||||
id: set_tag
|
||||
- name: Load environment variables
|
||||
run: |
|
||||
tag=$(date +'%Y%m%d%H%M%S')
|
||||
echo "tag=${tag}" >> $GITHUB_OUTPUT
|
||||
# Read environment variables from file
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# Skip comments and empty lines
|
||||
[[ "$line" =~ ^#.*$ ]] && continue
|
||||
[[ -z "$line" ]] && continue
|
||||
|
||||
# Extract key-value pairs
|
||||
key=$(echo "$line" | cut -d '=' -f1)
|
||||
value=$(echo "$line" | cut -d '=' -f2-)
|
||||
|
||||
# Set GitHub environment variables
|
||||
echo "$key=$value" >> $GITHUB_ENV
|
||||
done < deployment/deploy_env_vars
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Azure Container Registry
|
||||
uses: azure/docker-login@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
login-server: ${{ env.registry }}
|
||||
registry: ${{ env.registry }}
|
||||
username: ${{ secrets.ACR_USERNAME }}
|
||||
password: ${{ secrets.ACR_PASSWORD }}
|
||||
|
||||
- name: Build and Push Images
|
||||
run: |
|
||||
for service in member mysub recommend; do
|
||||
build_dir=$([[ "$service" == "mysub" ]] && echo "mysub-infra" || echo "$service")
|
||||
jar_file=$([[ "$service" == "mysub" ]] && echo "mysub.jar" || echo "${service}.jar")
|
||||
|
||||
docker build \
|
||||
--build-arg BUILD_LIB_DIR="${build_dir}/build/libs" \
|
||||
--build-arg ARTIFACTORY_FILE="${jar_file}" \
|
||||
-f deployment/Dockerfile \
|
||||
-t ${registry}/${image_org}/${service}:${{ steps.set_tag.outputs.tag }} .
|
||||
|
||||
docker push ${registry}/${image_org}/${service}:${{ steps.set_tag.outputs.tag }}
|
||||
done
|
||||
- name: Build and push Member service image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: deployment/Dockerfile
|
||||
push: true
|
||||
tags: ${{ env.registry }}/${{ env.image_org }}/member:${{ needs.build.outputs.image_tag }}
|
||||
build-args: |
|
||||
BUILD_LIB_DIR=member/build/libs
|
||||
ARTIFACTORY_FILE=member.jar
|
||||
|
||||
- name: Build and push MySub service image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: deployment/Dockerfile
|
||||
push: true
|
||||
tags: ${{ env.registry }}/${{ env.image_org }}/mysub:${{ needs.build.outputs.image_tag }}
|
||||
build-args: |
|
||||
BUILD_LIB_DIR=mysub-infra/build/libs
|
||||
ARTIFACTORY_FILE=mysub.jar
|
||||
|
||||
- name: Build and push Recommend service image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: deployment/Dockerfile
|
||||
push: true
|
||||
tags: ${{ env.registry }}/${{ env.image_org }}/recommend:${{ needs.build.outputs.image_tag }}
|
||||
build-args: |
|
||||
BUILD_LIB_DIR=recommend/build/libs
|
||||
ARTIFACTORY_FILE=recommend.jar
|
||||
|
||||
deploy:
|
||||
name: Deploy to AKS
|
||||
needs: release
|
||||
name: Deploy to Kubernetes
|
||||
needs: [build, release]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Load Environment Variables
|
||||
run: source deployment/deploy_env_vars
|
||||
- name: Load environment variables
|
||||
run: |
|
||||
# Read environment variables from file
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# Skip comments and empty lines
|
||||
[[ "$line" =~ ^#.*$ ]] && continue
|
||||
[[ -z "$line" ]] && continue
|
||||
|
||||
# Extract key-value pairs
|
||||
key=$(echo "$line" | cut -d '=' -f1)
|
||||
value=$(echo "$line" | cut -d '=' -f2-)
|
||||
|
||||
# Set GitHub environment variables
|
||||
echo "$key=$value" >> $GITHUB_ENV
|
||||
done < deployment/deploy_env_vars
|
||||
|
||||
- name: Set image tag environment variable
|
||||
run: |
|
||||
echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV
|
||||
|
||||
# Azure CLI 설치 단계 수정
|
||||
- name: Install Azure CLI
|
||||
run: |
|
||||
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
|
||||
|
||||
- name: Azure Login
|
||||
uses: azure/login@v1
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
||||
|
||||
- name: Set AKS Context
|
||||
uses: azure/aks-set-context@v1
|
||||
with:
|
||||
resource-group: ictcoe-edu
|
||||
cluster-name: ${{ env.teamid }}-aks
|
||||
- name: Setup kubectl
|
||||
uses: azure/setup-kubectl@v3
|
||||
|
||||
- name: Create Namespace
|
||||
- name: Get AKS Credentials
|
||||
run: |
|
||||
kubectl create namespace ${teamid}-${root_project}-ns --dry-run=client -o yaml | kubectl apply -f -
|
||||
az aks get-credentials --resource-group ictcoe-edu --name ${{ env.teamid }}-aks --overwrite-existing
|
||||
|
||||
- name: Generate Manifest
|
||||
- name: Create namespace
|
||||
run: |
|
||||
export namespace=${teamid}-${root_project}-ns
|
||||
export allowed_origins=${allowed_origins}
|
||||
export jwt_secret_key=${jwt_secret_key}
|
||||
export postgres_user=${postgres_user}
|
||||
export postgres_password=${postgres_password}
|
||||
export replicas=${replicas}
|
||||
export resources_requests_cpu=${resources_requests_cpu}
|
||||
export resources_requests_memory=${resources_requests_memory}
|
||||
export resources_limits_cpu=${resources_limits_cpu}
|
||||
export resources_limits_memory=${resources_limits_memory}
|
||||
export member_image_path=${registry}/${image_org}/member:${{ needs.release.outputs.image_tag }}
|
||||
export mysub_image_path=${registry}/${image_org}/mysub:${{ needs.release.outputs.image_tag }}
|
||||
export recommend_image_path=${registry}/${image_org}/recommend:${{ needs.release.outputs.image_tag }}
|
||||
kubectl create namespace ${{ env.namespace }} --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
- name: Install envsubst
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gettext-base
|
||||
|
||||
- name: Generate Kubernetes manifest
|
||||
run: |
|
||||
# Set environment variables for the deployment template
|
||||
export namespace=${{ env.namespace }}
|
||||
export allowed_origins=${{ env.allowed_origins }}
|
||||
export jwt_secret_key=${{ env.jwt_secret_key }}
|
||||
export postgres_user=${{ env.postgres_user }}
|
||||
export postgres_password=${{ env.postgres_password }}
|
||||
export replicas=${{ env.replicas }}
|
||||
export resources_requests_cpu=${{ env.resources_requests_cpu }}
|
||||
export resources_requests_memory=${{ env.resources_requests_memory }}
|
||||
export resources_limits_cpu=${{ env.resources_limits_cpu }}
|
||||
export resources_limits_memory=${{ env.resources_limits_memory }}
|
||||
|
||||
# Set image paths with the dynamic tag
|
||||
export member_image_path=${{ env.registry }}/${{ env.image_org }}/member:${{ env.IMAGE_TAG }}
|
||||
export mysub_image_path=${{ env.registry }}/${{ env.image_org }}/mysub:${{ env.IMAGE_TAG }}
|
||||
export recommend_image_path=${{ env.registry }}/${{ env.image_org }}/recommend:${{ env.IMAGE_TAG }}
|
||||
|
||||
# Generate the manifest file using envsubst
|
||||
envsubst < deployment/deploy.yaml.template > deployment/deploy.yaml
|
||||
|
||||
# Print manifest for debugging
|
||||
echo "Generated Kubernetes manifest:"
|
||||
cat deployment/deploy.yaml
|
||||
|
||||
- name: Deploy to AKS
|
||||
- name: Apply Kubernetes manifest
|
||||
run: |
|
||||
kubectl apply -f deployment/deploy.yaml
|
||||
|
||||
echo "Waiting for deployments to be ready..."
|
||||
kubectl -n ${namespace} wait --for=condition=available deployment/member --timeout=300s
|
||||
kubectl -n ${namespace} wait --for=condition=available deployment/mysub --timeout=300s
|
||||
kubectl -n ${namespace} wait --for=condition=available deployment/recommend --timeout=300s
|
||||
|
||||
- name: Wait for deployments to be ready
|
||||
run: |
|
||||
kubectl -n ${{ env.namespace }} wait --for=condition=available deployment/member --timeout=300s
|
||||
kubectl -n ${{ env.namespace }} wait --for=condition=available deployment/mysub --timeout=300s
|
||||
kubectl -n ${{ env.namespace }} wait --for=condition=available deployment/recommend --timeout=300s
|
||||
|
||||
- name: Get service information
|
||||
run: |
|
||||
echo "Ingress IP: $(kubectl -n ${{ env.namespace }} get ingress lifesub -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"
|
||||
|
||||
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
.gradle
|
||||
build/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
Binary file not shown.
Binary file not shown.
12
.idea/modules.xml
generated
12
.idea/modules.xml
generated
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/common/lifesub.common.main.iml" filepath="$PROJECT_DIR$/.idea/modules/common/lifesub.common.main.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/member/lifesub.member.main.iml" filepath="$PROJECT_DIR$/.idea/modules/member/lifesub.member.main.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/mysub-biz/lifesub.mysub-biz.main.iml" filepath="$PROJECT_DIR$/.idea/modules/mysub-biz/lifesub.mysub-biz.main.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/mysub-infra/lifesub.mysub-infra.main.iml" filepath="$PROJECT_DIR$/.idea/modules/mysub-infra/lifesub.mysub-infra.main.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/recommend/lifesub.recommend.main.iml" filepath="$PROJECT_DIR$/.idea/modules/recommend/lifesub.recommend.main.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules/common/lifesub.common.main.iml
generated
8
.idea/modules/common/lifesub.common.main.iml
generated
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="AdditionalModuleElements">
|
||||
<content url="file://$MODULE_DIR$/../../../common/build/generated/sources/annotationProcessor/java/main">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../common/build/generated/sources/annotationProcessor/java/main" isTestSource="false" generated="true" />
|
||||
</content>
|
||||
</component>
|
||||
</module>
|
||||
8
.idea/modules/member/lifesub.member.main.iml
generated
8
.idea/modules/member/lifesub.member.main.iml
generated
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="AdditionalModuleElements">
|
||||
<content url="file://$MODULE_DIR$/../../../member/build/generated/sources/annotationProcessor/java/main">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../member/build/generated/sources/annotationProcessor/java/main" isTestSource="false" generated="true" />
|
||||
</content>
|
||||
</component>
|
||||
</module>
|
||||
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="AdditionalModuleElements">
|
||||
<content url="file://$MODULE_DIR$/../../../mysub-biz/build/generated/sources/annotationProcessor/java/main">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../mysub-biz/build/generated/sources/annotationProcessor/java/main" isTestSource="false" generated="true" />
|
||||
</content>
|
||||
</component>
|
||||
</module>
|
||||
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="AdditionalModuleElements">
|
||||
<content url="file://$MODULE_DIR$/../../../mysub-infra/build/generated/sources/annotationProcessor/java/main">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../mysub-infra/build/generated/sources/annotationProcessor/java/main" isTestSource="false" generated="true" />
|
||||
</content>
|
||||
</component>
|
||||
</module>
|
||||
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="AdditionalModuleElements">
|
||||
<content url="file://$MODULE_DIR$/../../../recommend/build/generated/sources/annotationProcessor/java/main">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../recommend/build/generated/sources/annotationProcessor/java/main" isTestSource="false" generated="true" />
|
||||
</content>
|
||||
</component>
|
||||
</module>
|
||||
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -92,6 +92,7 @@ subprojects {
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-- Biz와 common 모듈이 아닌 경우 인프라 관련 라이브러리 추가
|
||||
@ -133,3 +134,4 @@ configure(subprojects.findAll { it.name.endsWith('-biz') || it.name == 'common'
|
||||
bootJar.enabled = false
|
||||
jar.enabled = true
|
||||
}
|
||||
|
||||
|
||||
59
deployment/Jenkinsfile
vendored
59
deployment/Jenkinsfile
vendored
@ -54,16 +54,16 @@ podTemplate(
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build Applications') {
|
||||
stage('Build Applications & SonarQube Analysis') {
|
||||
container('podman') {
|
||||
sh 'podman system service -t 0 unix:///run/podman/podman.sock & sleep 2'
|
||||
}
|
||||
|
||||
container('gradle') {
|
||||
def testContainersConfig = '''docker.client.strategy=org.testcontainers.dockerclient.UnixSocketClientProviderStrategy
|
||||
docker.host=unix:///run/podman/podman.sock
|
||||
ryuk.container.privileged=true
|
||||
testcontainers.reuse.enable=true'''
|
||||
docker.host=unix:///run/podman/podman.sock
|
||||
ryuk.container.privileged=true
|
||||
testcontainers.reuse.enable=true'''
|
||||
|
||||
sh """
|
||||
# TestContainers 설정
|
||||
@ -71,11 +71,51 @@ podTemplate(
|
||||
echo '${testContainersConfig}' > member/src/test/resources/testcontainers.properties
|
||||
echo '${testContainersConfig}' > mysub-infra/src/test/resources/testcontainers.properties
|
||||
echo '${testContainersConfig}' > recommend/src/test/resources/testcontainers.properties
|
||||
|
||||
# 빌드 실행
|
||||
chmod +x gradlew
|
||||
./gradlew clean :member:build :mysub-infra:build :recommend:build
|
||||
"""
|
||||
|
||||
// Member 서비스 빌드 및 SonarQube 분석
|
||||
withSonarQubeEnv('SonarQube') {
|
||||
sh """
|
||||
chmod +x gradlew
|
||||
|
||||
# 빌드 실행
|
||||
./gradlew :member:build :mysub-infra:build :recommend:build -x test
|
||||
|
||||
# Member 서비스
|
||||
./gradlew :member:test :member:jacocoTestReport :member:sonar \
|
||||
-Dsonar.projectKey=lifesub-member \
|
||||
-Dsonar.projectName=lifesub-member \
|
||||
-Dsonar.java.binaries=build/classes/java/main \
|
||||
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \
|
||||
-Dsonar.exclusions=**/config/**,**/entity/**,**/dto/**,**/*Application.class,**/exception/**
|
||||
|
||||
# Recommend 서비스
|
||||
./gradlew :recommend:test :recommend:jacocoTestReport :recommend:sonar \
|
||||
-Dsonar.projectKey=lifesub-recommend \
|
||||
-Dsonar.projectName=lifesub-recommend \
|
||||
-Dsonar.java.binaries=build/classes/java/main \
|
||||
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \
|
||||
-Dsonar.exclusions=**/config/**,**/entity/**,**/dto/**,**/*Application.class,**/exception/**
|
||||
|
||||
# Mysub 서비스 (biz & infra 구조)
|
||||
./gradlew :mysub-infra:test :mysub-infra:jacocoTestReport :mysub-infra:sonar \
|
||||
-Dsonar.projectKey=lifesub-mysub \
|
||||
-Dsonar.projectName=lifesub-mysub \
|
||||
-Dsonar.java.binaries=build/classes/java/main \
|
||||
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \
|
||||
-Dsonar.exclusions=**/config/**,**/entity/**,**/dto/**,**/*Application.class,**/exception/**
|
||||
"""
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
stage('Quality Gate') {
|
||||
timeout(time: 10, unit: 'MINUTES') {
|
||||
def qg = waitForQualityGate()
|
||||
if (qg.status != 'OK') {
|
||||
error "Pipeline aborted due to quality gate failure: ${qg.status}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,4 +183,5 @@ podTemplate(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,43 +10,54 @@ podTemplate(
|
||||
label: "${PIPELINE_ID}",
|
||||
serviceAccount: 'jenkins',
|
||||
containers: [
|
||||
containerTemplate(name: 'podman',
|
||||
image: "mgoltzsche/podman",
|
||||
ttyEnabled: true,
|
||||
command: 'cat',
|
||||
privileged: true),
|
||||
containerTemplate(name: 'podman', image: "mgoltzsche/podman", ttyEnabled: true, command: 'cat', privileged: true),
|
||||
containerTemplate(name: 'gradle',
|
||||
image: 'gradle:jdk17',
|
||||
ttyEnabled: true,
|
||||
command: 'cat',
|
||||
envVars: [
|
||||
envVar(key: 'DOCKER_HOST', value: 'unix:///run/podman/podman.sock'),
|
||||
envVar(key: 'TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE', value: '/run/podman/podman.sock'),
|
||||
envVar(key: 'TESTCONTAINERS_RYUK_DISABLED', value: 'true')
|
||||
]),
|
||||
containerTemplate(name: 'git',
|
||||
image: 'alpine:3.19',
|
||||
command: 'cat',
|
||||
ttyEnabled: true)
|
||||
image: 'gradle:jdk17',
|
||||
ttyEnabled: true,
|
||||
command: 'cat',
|
||||
envVars: [
|
||||
envVar(key: 'DOCKER_HOST', value: 'unix:///run/podman/podman.sock'),
|
||||
envVar(key: 'TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE', value: '/run/podman/podman.sock'),
|
||||
envVar(key: 'TESTCONTAINERS_RYUK_DISABLED', value: 'true')
|
||||
]),
|
||||
containerTemplate(name: 'azure-cli', image: 'hiondal/azure-kubectl:latest', command: 'cat', ttyEnabled: true),
|
||||
containerTemplate(name: 'envsubst', image: "hiondal/envsubst", command: 'sleep', args: '1h'),
|
||||
containerTemplate(name: 'git', image: 'alpine/git:latest', command: 'cat', ttyEnabled: true)
|
||||
],
|
||||
volumes: [
|
||||
emptyDirVolume(mountPath: '/home/gradle/.gradle', memory: false),
|
||||
emptyDirVolume(mountPath: '/root/.azure', memory: false),
|
||||
emptyDirVolume(mountPath: '/run/podman', memory: false)
|
||||
]
|
||||
) {
|
||||
node(PIPELINE_ID) {
|
||||
def props
|
||||
def imageTag = getImageTag()
|
||||
def manifest = "deploy.yaml"
|
||||
def namespace
|
||||
def services = ['member', 'mysub', 'recommend']
|
||||
def manifestRepo = 'cna-bootcamp/lifesub-manifest'
|
||||
def manifestBranch = 'main'
|
||||
def MANIFEST_REPO = "https://github.com/cna-bootcamp/lifesub-manifest.git"
|
||||
def MANIFEST_BRANCH = "main"
|
||||
|
||||
stage("Get Source") {
|
||||
checkout scm
|
||||
props = readProperties file: "deployment/deploy_env_vars"
|
||||
namespace = "${props.namespace}"
|
||||
}
|
||||
|
||||
stage('Build Applications') {
|
||||
stage("Setup AKS") {
|
||||
container('azure-cli') {
|
||||
withCredentials([azureServicePrincipal('azure-credentials')]) {
|
||||
sh """
|
||||
az login --service-principal -u \$AZURE_CLIENT_ID -p \$AZURE_CLIENT_SECRET -t \$AZURE_TENANT_ID
|
||||
az aks get-credentials --resource-group ictcoe-edu --name ${props.teamid}-aks --overwrite-existing
|
||||
kubectl create namespace ${namespace} --dry-run=client -o yaml | kubectl apply -f -
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build Applications & SonarQube Analysis') {
|
||||
container('podman') {
|
||||
sh 'podman system service -t 0 unix:///run/podman/podman.sock & sleep 2'
|
||||
}
|
||||
@ -63,11 +74,50 @@ testcontainers.reuse.enable=true'''
|
||||
echo '${testContainersConfig}' > member/src/test/resources/testcontainers.properties
|
||||
echo '${testContainersConfig}' > mysub-infra/src/test/resources/testcontainers.properties
|
||||
echo '${testContainersConfig}' > recommend/src/test/resources/testcontainers.properties
|
||||
|
||||
# 빌드 실행
|
||||
chmod +x gradlew
|
||||
./gradlew clean :member:build :mysub-infra:build :recommend:build
|
||||
"""
|
||||
|
||||
// Member 서비스 빌드 및 SonarQube 분석
|
||||
withSonarQubeEnv('SonarQube') {
|
||||
sh """
|
||||
chmod +x gradlew
|
||||
|
||||
# 빌드 실행
|
||||
./gradlew :member:build :mysub-infra:build :recommend:build -x test
|
||||
|
||||
# Member 서비스
|
||||
./gradlew :member:test :member:jacocoTestReport :member:sonar \
|
||||
-Dsonar.projectKey=lifesub-member \
|
||||
-Dsonar.projectName=lifesub-member \
|
||||
-Dsonar.java.binaries=build/classes/java/main \
|
||||
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \
|
||||
-Dsonar.exclusions=**/config/**,**/entity/**,**/dto/**,**/*Application.class,**/exception/**
|
||||
|
||||
# Recommend 서비스
|
||||
./gradlew :recommend:test :recommend:jacocoTestReport :recommend:sonar \
|
||||
-Dsonar.projectKey=lifesub-recommend \
|
||||
-Dsonar.projectName=lifesub-recommend \
|
||||
-Dsonar.java.binaries=build/classes/java/main \
|
||||
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \
|
||||
-Dsonar.exclusions=**/config/**,**/entity/**,**/dto/**,**/*Application.class,**/exception/**
|
||||
|
||||
# Mysub 서비스 (biz & infra 구조)
|
||||
./gradlew :mysub-infra:test :mysub-infra:jacocoTestReport :mysub-infra:sonar \
|
||||
-Dsonar.projectKey=lifesub-mysub \
|
||||
-Dsonar.projectName=lifesub-mysub \
|
||||
-Dsonar.java.binaries=build/classes/java/main \
|
||||
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \
|
||||
-Dsonar.exclusions=**/config/**,**/entity/**,**/dto/**,**/*Application.class,**/exception/**
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Quality Gate') {
|
||||
timeout(time: 10, unit: 'MINUTES') {
|
||||
def qg = waitForQualityGate()
|
||||
if (qg.status != 'OK') {
|
||||
error "Pipeline aborted due to quality gate failure: ${qg.status}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,10 +135,10 @@ testcontainers.reuse.enable=true'''
|
||||
def jarFile = service == 'mysub' ? 'mysub.jar' : "${service}.jar"
|
||||
|
||||
sh """
|
||||
podman build \\
|
||||
--build-arg BUILD_LIB_DIR="${buildDir}/build/libs" \\
|
||||
--build-arg ARTIFACTORY_FILE="${jarFile}" \\
|
||||
-f deployment/Dockerfile \\
|
||||
podman build \
|
||||
--build-arg BUILD_LIB_DIR="${buildDir}/build/libs" \
|
||||
--build-arg ARTIFACTORY_FILE="${jarFile}" \
|
||||
-f deployment/Dockerfile \
|
||||
-t ${props.registry}/${props.image_org}/${service}:${imageTag} .
|
||||
|
||||
podman push ${props.registry}/${props.image_org}/${service}:${imageTag}
|
||||
@ -98,47 +148,35 @@ testcontainers.reuse.enable=true'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Update Manifests') {
|
||||
stage('Update Manifest Repository') {
|
||||
container('git') {
|
||||
// git과 yq 설치
|
||||
sh '''
|
||||
apk add --no-cache git curl
|
||||
curl -L https://github.com/mikefarah/yq/releases/download/v4.40.5/yq_linux_amd64 -o /usr/local/bin/yq
|
||||
chmod +x /usr/local/bin/yq
|
||||
'''
|
||||
|
||||
withCredentials([usernamePassword(
|
||||
credentialsId: 'github-credentials',
|
||||
usernameVariable: 'GIT_USERNAME',
|
||||
passwordVariable: 'GIT_PASSWORD'
|
||||
)]) {
|
||||
sh '''
|
||||
git config --global user.email "jenkins@example.com"
|
||||
git config --global user.name "Jenkins"
|
||||
'''
|
||||
// 임시 디렉토리 생성
|
||||
sh "mkdir -p /tmp/manifests"
|
||||
|
||||
// Clone manifest repository
|
||||
withCredentials([usernamePassword(credentialsId: 'github-credentials', usernameVariable: 'GIT_USERNAME', passwordVariable: 'GIT_PASSWORD')]) {
|
||||
sh """
|
||||
rm -rf lifesub-manifest
|
||||
#git clone https://oauth2:${GIT_PASSWORD}@github.com/${manifestRepo}.git
|
||||
git clone https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/${manifestRepo}.git
|
||||
git config --global user.email "jenkins@example.com"
|
||||
git config --global user.name "Jenkins Pipeline"
|
||||
|
||||
git clone https://${GIT_USERNAME}:${GIT_PASSWORD}@github.com/cna-bootcamp/lifesub-manifest.git /tmp/manifests
|
||||
cd /tmp/manifests
|
||||
|
||||
# Update image tags in the appropriate YAML files
|
||||
for service in ${services.join(' ')}; do
|
||||
# 백엔드 서비스 이미지 태그 업데이트
|
||||
if [ -f lifesub/deployments/\${service}-deployment.yaml ]; then
|
||||
sed -i "s|image: ${props.registry}/${props.image_org}/\${service}:.*|image: ${props.registry}/${props.image_org}/\${service}:${imageTag}|g" lifesub/deployments/\${service}-deployment.yaml
|
||||
fi
|
||||
done
|
||||
|
||||
# Commit and push changes
|
||||
git add .
|
||||
git commit -m "Update backend service image tags to ${imageTag}" || true
|
||||
git push
|
||||
"""
|
||||
|
||||
dir('lifesub-manifest') {
|
||||
services.each { service ->
|
||||
def imagePath = "${props.registry}/${props.image_org}/${service}:${imageTag}"
|
||||
sh """
|
||||
yq -i '.spec.template.spec.containers[0].image = "${imagePath}"' lifesub/deployments/${service}-deployment.yaml
|
||||
"""
|
||||
}
|
||||
|
||||
sh """
|
||||
git add .
|
||||
git diff-index --quiet HEAD || git commit -m "Update backend services images to ${imageTag}"
|
||||
git push origin ${manifestBranch}
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,4 +306,41 @@ spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8083
|
||||
type: ClusterIP
|
||||
type: ClusterIP
|
||||
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: lifesub
|
||||
namespace: ${namespace}
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /$2
|
||||
nginx.ingress.kubernetes.io/use-regex: "true"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /member(/|$)(.*)
|
||||
pathType: ImplementationSpecific
|
||||
backend:
|
||||
service:
|
||||
name: member
|
||||
port:
|
||||
number: 80
|
||||
- path: /mysub(/|$)(.*)
|
||||
pathType: ImplementationSpecific
|
||||
backend:
|
||||
service:
|
||||
name: mysub
|
||||
port:
|
||||
number: 80
|
||||
- path: /recommend(/|$)(.*)
|
||||
pathType: ImplementationSpecific
|
||||
backend:
|
||||
service:
|
||||
name: recommend
|
||||
port:
|
||||
number: 80
|
||||
|
||||
@ -8,8 +8,8 @@ registry=unicorncr.azurecr.io
|
||||
image_org=lifesub
|
||||
|
||||
# Application Settings
|
||||
replicas=2
|
||||
allowed_origins=http://4.230.156.229
|
||||
replicas=1
|
||||
allowed_origins=http://20.214.113.12
|
||||
|
||||
# Security Settings
|
||||
jwt_secret_key=8O2HQ13etL2BWZvYOiWsJ5uWFoLi6NBUG8divYVoCgtHVvlk3dqRksMl16toztDUeBTSIuOOPvHIrYq11G2BwQ
|
||||
|
||||
@ -5,4 +5,8 @@ metadata:
|
||||
data:
|
||||
JPA_DDL_AUTO: update
|
||||
JPA_SHOW_SQL: "true"
|
||||
<<<<<<< HEAD
|
||||
ALLOWED_ORIGINS: "http://localhost:18080,http://localhost:18081,http://20.249.184.6"
|
||||
=======
|
||||
ALLOWED_ORIGINS: "http://localhost:18080,http://localhost:18081,http://20.214.113.12"
|
||||
>>>>>>> 1ea06faa5d92eae36ec9d161158eb1314eaff732
|
||||
|
||||
@ -55,4 +55,4 @@ spec:
|
||||
path: /actuator/health/readiness
|
||||
port: 8082
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 15
|
||||
periodSeconds: 15
|
||||
|
||||
@ -18,7 +18,6 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@ -40,7 +39,7 @@ public class MemberServiceImpl implements MemberService {
|
||||
// 사용자의 권한 정보 생성
|
||||
Collection<? extends GrantedAuthority> authorities = member.getRoles().stream()
|
||||
.map(SimpleGrantedAuthority::new)
|
||||
.collect(Collectors.toList());
|
||||
.toList();
|
||||
|
||||
return jwtTokenProvider.createToken(member, authorities);
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ spring:
|
||||
datasource:
|
||||
url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:member}
|
||||
username: ${POSTGRES_USER:admin}
|
||||
password: ${POSTGRES_PASSWORD:Passw0rd}
|
||||
password: ${POSTGRES_PASSWORD:Hi5Jessica!}
|
||||
driver-class-name: org.postgresql.Driver
|
||||
|
||||
# JPA 설정
|
||||
|
||||
@ -7,7 +7,7 @@ spring:
|
||||
datasource:
|
||||
url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:mysub}
|
||||
username: ${POSTGRES_USER:admin}
|
||||
password: ${POSTGRES_PASSWORD:Passw0rd}
|
||||
password: ${POSTGRES_PASSWORD:Hi5Jessica!}
|
||||
driver-class-name: org.postgresql.Driver
|
||||
# JPA 설정
|
||||
jpa:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +0,0 @@
|
||||
com.unicorn.lifesub.recommend.RecommendApplication
|
||||
@ -1,12 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: org.springframework.boot.loader.launch.JarLauncher
|
||||
Start-Class: com.unicorn.lifesub.recommend.RecommendApplication
|
||||
Spring-Boot-Version: 3.4.0
|
||||
Spring-Boot-Classes: BOOT-INF/classes/
|
||||
Spring-Boot-Lib: BOOT-INF/lib/
|
||||
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
|
||||
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
|
||||
Build-Jdk-Spec: 21
|
||||
Implementation-Title: recommend
|
||||
Implementation-Version: 1.0.0
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
|
||||
@ -7,7 +7,7 @@ spring:
|
||||
datasource:
|
||||
url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:recommend}
|
||||
username: ${POSTGRES_USER:admin}
|
||||
password: ${POSTGRES_PASSWORD:Passw0rd}
|
||||
password: ${POSTGRES_PASSWORD:Hi5Jessica!}
|
||||
driver-class-name: org.postgresql.Driver
|
||||
# JPA 설정
|
||||
jpa:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user