mirror of
https://github.com/cna-bootcamp/phonebill.git
synced 2025-12-06 08:06:24 +00:00
GitHub Actions CI/CD 파이프라인 구축 완료
- GitHub Actions 전용 Kustomize 매니페스트 구조 생성 - 환경별(dev/staging/prod) Overlay 및 Patch 파일 작성 - SonarQube 코드 품질 분석 통합 - Docker 이미지 빌드 및 Azure Container Registry 푸시 - Kubernetes 자동 배포 워크플로우 구성 - 수동 배포 스크립트 및 구축 가이드 문서 추가 주요 기능: - 자동 배포: Push/PR 시 dev 환경 자동 배포 - 수동 배포: 환경 선택 가능한 워크플로우 트리거 - 환경별 최적화: dev(1 replica), staging(2 replicas), prod(3 replicas) - 보안 설정: staging/prod HTTPS, JWT 토큰 유효시간 조정 - 롤백 지원: GitHub Actions, kubectl, 수동 스크립트 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
04dd8c9643
commit
f5543bf0ef
314
.github/actions-pipeline-guide.md
vendored
314
.github/actions-pipeline-guide.md
vendored
@ -1,188 +1,188 @@
|
|||||||
# 백엔드 GitHub Actions CI/CD 파이프라인 가이드
|
# GitHub Actions CI/CD 파이프라인 구축 가이드
|
||||||
|
|
||||||
## 📋 개요
|
## 개요
|
||||||
|
|
||||||
통신요금 관리 서비스(phonebill)의 GitHub Actions 기반 CI/CD 파이프라인 구축 가이드입니다.
|
phonebill 시스템을 위한 GitHub Actions 기반 CI/CD 파이프라인이 성공적으로 구축되었습니다.
|
||||||
|
|
||||||
**실행정보**:
|
### 프로젝트 정보
|
||||||
- ACR_NAME: acrdigitalgarage01
|
- **시스템명**: phonebill
|
||||||
- RESOURCE_GROUP: rg-digitalgarage-01
|
- **서비스**: api-gateway, user-service, bill-service, product-service, kos-mock
|
||||||
- AKS_CLUSTER: aks-digitalgarage-01
|
- **JDK 버전**: 21
|
||||||
|
- **Azure 환경**: ACR(acrdigitalgarage01), AKS(aks-digitalgarage-01), RG(rg-digitalgarage-01)
|
||||||
|
- **네임스페이스**: phonebill-dg0500
|
||||||
|
|
||||||
**시스템 정보**:
|
## 구축된 파일 구조
|
||||||
- 시스템명: phonebill
|
|
||||||
- JDK 버전: 21
|
|
||||||
- 서비스: api-gateway, user-service, bill-service, product-service, kos-mock
|
|
||||||
|
|
||||||
## 🏗️ 구축된 파이프라인 구조
|
|
||||||
|
|
||||||
### 디렉토리 구조
|
|
||||||
```
|
```
|
||||||
.github/
|
.github/
|
||||||
├── kustomize/
|
├── kustomize/
|
||||||
│ ├── base/ # 기본 매니페스트
|
│ ├── base/
|
||||||
│ │ ├── kustomization.yaml
|
│ │ ├── kustomization.yaml
|
||||||
│ │ ├── namespace.yaml
|
│ │ ├── common/
|
||||||
│ │ ├── common/ # 공통 리소스
|
│ │ │ ├── cm-common.yaml
|
||||||
│ │ └── {서비스명}/ # 각 서비스별 매니페스트
|
│ │ │ ├── secret-common.yaml
|
||||||
│ └── overlays/ # 환경별 오버레이
|
│ │ │ ├── secret-imagepull.yaml
|
||||||
|
│ │ │ └── ingress.yaml
|
||||||
|
│ │ └── {서비스명}/
|
||||||
|
│ │ ├── deployment.yaml
|
||||||
|
│ │ ├── service.yaml
|
||||||
|
│ │ ├── cm-{서비스명}.yaml
|
||||||
|
│ │ └── secret-{서비스명}.yaml (해당되는 경우)
|
||||||
|
│ └── overlays/
|
||||||
│ ├── dev/
|
│ ├── dev/
|
||||||
|
│ │ ├── kustomization.yaml
|
||||||
|
│ │ ├── cm-common-patch.yaml
|
||||||
|
│ │ ├── ingress-patch.yaml
|
||||||
|
│ │ ├── deployment-{서비스명}-patch.yaml
|
||||||
|
│ │ ├── secret-common-patch.yaml
|
||||||
|
│ │ └── secret-{서비스명}-patch.yaml
|
||||||
│ ├── staging/
|
│ ├── staging/
|
||||||
|
│ │ └── (dev와 동일한 구조, staging 환경 설정)
|
||||||
│ └── prod/
|
│ └── prod/
|
||||||
├── config/ # 환경별 설정
|
│ └── (dev와 동일한 구조, prod 환경 설정)
|
||||||
|
├── config/
|
||||||
│ ├── deploy_env_vars_dev
|
│ ├── deploy_env_vars_dev
|
||||||
│ ├── deploy_env_vars_staging
|
│ ├── deploy_env_vars_staging
|
||||||
│ └── deploy_env_vars_prod
|
│ └── deploy_env_vars_prod
|
||||||
├── scripts/ # 배포 스크립트
|
├── scripts/
|
||||||
│ └── deploy-actions.sh
|
│ └── deploy-actions.sh
|
||||||
└── workflows/ # GitHub Actions 워크플로우
|
└── workflows/
|
||||||
└── backend-cicd.yaml
|
└── backend-cicd.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
### 파이프라인 워크플로우
|
## GitHub Repository 설정
|
||||||
1. **Build & Test**: Gradle 빌드 및 단위 테스트
|
|
||||||
2. **SonarQube Analysis**: 코드 품질 분석 (선택적)
|
|
||||||
3. **Container Build & Push**: 환경별 이미지 빌드 및 ACR 푸시
|
|
||||||
4. **Kustomize Deploy**: 환경별 매니페스트 적용
|
|
||||||
|
|
||||||
## ⚙️ GitHub Repository 설정
|
|
||||||
|
|
||||||
### 1. Repository Secrets 설정
|
### 1. Repository Secrets 설정
|
||||||
|
|
||||||
GitHub Repository > Settings > Secrets and variables > Actions > Repository secrets에 다음 항목들을 등록하세요:
|
GitHub Repository → Settings → Secrets and variables → Actions → Repository secrets에 다음 설정:
|
||||||
|
|
||||||
#### Azure 인증 정보
|
|
||||||
```json
|
|
||||||
AZURE_CREDENTIALS:
|
|
||||||
{
|
|
||||||
"clientId": "5e4b5b41-7208-48b7-b821-d6d5acf50ecf",
|
|
||||||
"clientSecret": "ldu8Q~GQEzFYU.dJX7_QsahR7n7C2xqkIM6hqbV8",
|
|
||||||
"subscriptionId": "2513dd36-7978-48e3-9a7c-b221d4874f66",
|
|
||||||
"tenantId": "4f0a3bfd-1156-4cce-8dc2-a049a13dba23"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### ACR Credentials
|
|
||||||
```bash
|
```bash
|
||||||
# ACR 자격 증명 확인 명령어
|
# Azure Service Principal
|
||||||
az acr credential show --name acrdigitalgarage01
|
AZURE_CREDENTIALS
|
||||||
```
|
{
|
||||||
```
|
"clientId": "{클라이언트ID}",
|
||||||
|
"clientSecret": "{클라이언트시크릿}",
|
||||||
|
"subscriptionId": "{구독ID}",
|
||||||
|
"tenantId": "{테넌트ID}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ACR Credentials (az acr credential show --name acrdigitalgarage01)
|
||||||
ACR_USERNAME: acrdigitalgarage01
|
ACR_USERNAME: acrdigitalgarage01
|
||||||
ACR_PASSWORD: {ACR패스워드}
|
ACR_PASSWORD: {ACR패스워드}
|
||||||
```
|
|
||||||
|
|
||||||
#### SonarQube 설정
|
# SonarQube 설정
|
||||||
```bash
|
SONAR_HOST_URL: http://{External IP} # k get svc -n sonarqube로 확인
|
||||||
# SonarQube URL 확인
|
SONAR_TOKEN: {SonarQube토큰} # SonarQube > My Account > Security에서 생성
|
||||||
kubectl get svc -n sonarqube
|
|
||||||
```
|
|
||||||
```
|
|
||||||
SONAR_HOST_URL: http://{External IP}
|
|
||||||
SONAR_TOKEN: {SonarQube토큰}
|
|
||||||
```
|
|
||||||
|
|
||||||
**SonarQube 토큰 생성 방법**:
|
# Docker Hub (Rate Limit 방지)
|
||||||
1. SonarQube 로그인 후 우측 상단 'Administrator' > My Account 클릭
|
|
||||||
2. Security 탭 선택 후 토큰 생성
|
|
||||||
|
|
||||||
#### Docker Hub 설정 (Rate Limit 해결)
|
|
||||||
```
|
|
||||||
DOCKERHUB_USERNAME: {Docker Hub 사용자명}
|
DOCKERHUB_USERNAME: {Docker Hub 사용자명}
|
||||||
DOCKERHUB_PASSWORD: {Docker Hub 패스워드}
|
DOCKERHUB_PASSWORD: {Docker Hub 패스워드}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Repository Variables 설정
|
### 2. Repository Variables 설정
|
||||||
|
|
||||||
GitHub Repository > Settings > Secrets and variables > Actions > Variables > Repository variables에 등록:
|
GitHub Repository → Settings → Secrets and variables → Actions → Variables → Repository variables에 다음 설정:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
ENVIRONMENT: dev
|
ENVIRONMENT: dev (기본값)
|
||||||
SKIP_SONARQUBE: true
|
SKIP_SONARQUBE: true (기본값)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🚀 파이프라인 실행 방법
|
## 파이프라인 실행 방법
|
||||||
|
|
||||||
### 자동 실행
|
### 1. 자동 실행
|
||||||
- **Push/PR 트리거**: main, develop 브랜치로 push시 자동 실행
|
- **트리거**: main/develop 브랜치에 push 또는 main 브랜치에 PR
|
||||||
- **기본 설정**: ENVIRONMENT=dev, SKIP_SONARQUBE=true
|
- **환경**: dev (기본값)
|
||||||
|
- **SonarQube**: 스킵 (기본값)
|
||||||
|
|
||||||
### 수동 실행
|
### 2. 수동 실행
|
||||||
1. GitHub Repository > Actions 탭
|
1. GitHub → Actions 탭 이동
|
||||||
2. "Backend Services CI/CD" 워크플로우 선택
|
2. "Backend Services CI/CD" 워크플로우 선택
|
||||||
3. "Run workflow" 버튼 클릭
|
3. "Run workflow" 버튼 클릭
|
||||||
4. 환경 선택 (dev/staging/prod)
|
4. 환경 선택 (dev/staging/prod)
|
||||||
5. SonarQube 분석 여부 선택 (true/false)
|
5. SonarQube 분석 여부 선택 (true/false)
|
||||||
|
|
||||||
## 📦 배포 환경별 설정
|
## 파이프라인 단계
|
||||||
|
|
||||||
|
### 1. Build and Test
|
||||||
|
- Gradle 빌드 (테스트 제외)
|
||||||
|
- SonarQube 코드 품질 분석 (선택사항)
|
||||||
|
- 빌드 아티팩트 업로드
|
||||||
|
|
||||||
|
### 2. Build and Push Docker Images
|
||||||
|
- 각 서비스별 Docker 이미지 빌드
|
||||||
|
- Azure Container Registry에 푸시
|
||||||
|
- 이미지 태그: `{환경}-{타임스탬프}`
|
||||||
|
|
||||||
|
### 3. Deploy to Kubernetes
|
||||||
|
- Kustomize를 사용한 환경별 매니페스트 생성
|
||||||
|
- AKS 클러스터에 배포
|
||||||
|
- 배포 상태 확인
|
||||||
|
|
||||||
|
## 환경별 설정
|
||||||
|
|
||||||
### DEV 환경
|
### DEV 환경
|
||||||
- **Namespace**: phonebill-dev
|
- **Replicas**: 1
|
||||||
- **Replicas**: 모든 서비스 1개
|
- **Resources**: requests(256Mi/256m), limits(1024Mi/1024m)
|
||||||
- **Resources**: CPU 256m/1024m, Memory 256Mi/1024Mi
|
- **DDL**: update
|
||||||
- **Profile**: dev (DDL_AUTO: update)
|
|
||||||
- **SSL**: 비활성화
|
- **SSL**: 비활성화
|
||||||
|
- **Host**: phonebill-dg0500-api.20.214.196.128.nip.io
|
||||||
|
|
||||||
### STAGING 환경
|
### STAGING 환경
|
||||||
- **Namespace**: phonebill-staging
|
- **Replicas**: 2
|
||||||
- **Replicas**: 모든 서비스 2개
|
- **Resources**: requests(512Mi/512m), limits(2048Mi/2048m)
|
||||||
- **Resources**: CPU 512m/2048m, Memory 512Mi/2048Mi
|
- **DDL**: validate
|
||||||
- **Profile**: staging (DDL_AUTO: validate)
|
|
||||||
- **SSL**: 활성화
|
- **SSL**: 활성화
|
||||||
|
- **Host**: phonebill-staging.digitalgarage.com
|
||||||
|
- **JWT**: 운영 환경 토큰 유효시간
|
||||||
|
|
||||||
### PROD 환경
|
### PROD 환경
|
||||||
- **Namespace**: phonebill-prod
|
- **Replicas**: 3
|
||||||
- **Replicas**: 모든 서비스 3개
|
- **Resources**: requests(1024Mi/1024m), limits(4096Mi/4096m)
|
||||||
- **Resources**: CPU 1024m/4096m, Memory 1024Mi/4096Mi
|
- **DDL**: validate
|
||||||
- **Profile**: prod (DDL_AUTO: validate, 짧은 JWT)
|
|
||||||
- **SSL**: 활성화
|
- **SSL**: 활성화
|
||||||
|
- **Host**: phonebill.digitalgarage.com
|
||||||
|
- **JWT**: 보안 강화된 짧은 토큰 유효시간
|
||||||
|
|
||||||
## 🔧 수동 배포 방법
|
## 수동 배포 방법
|
||||||
|
|
||||||
|
로컬에서 수동 배포를 수행하려면:
|
||||||
|
|
||||||
### 스크립트를 이용한 배포
|
|
||||||
```bash
|
```bash
|
||||||
# 기본 (dev 환경, latest 태그)
|
# 기본 dev 환경으로 배포
|
||||||
./.github/scripts/deploy-actions.sh
|
./.github/scripts/deploy-actions.sh
|
||||||
|
|
||||||
# 특정 환경과 태그 지정
|
# 특정 환경과 이미지 태그로 배포
|
||||||
./.github/scripts/deploy-actions.sh staging 20241215143022
|
./.github/scripts/deploy-actions.sh staging 20241001123000
|
||||||
|
|
||||||
|
# 권한 오류 시
|
||||||
|
chmod +x .github/scripts/deploy-actions.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### kubectl을 이용한 직접 배포
|
## 롤백 방법
|
||||||
```bash
|
|
||||||
# Kustomize 설치
|
|
||||||
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
|
|
||||||
sudo mv kustomize /usr/local/bin/
|
|
||||||
|
|
||||||
# 환경별 배포
|
|
||||||
cd .github/kustomize/overlays/dev
|
|
||||||
kubectl apply -k .
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔄 롤백 방법
|
|
||||||
|
|
||||||
### 1. GitHub Actions를 통한 롤백
|
### 1. GitHub Actions를 통한 롤백
|
||||||
1. GitHub > Actions > 성공한 이전 워크플로우 선택
|
1. GitHub → Actions → 성공한 이전 워크플로우 선택
|
||||||
2. "Re-run all jobs" 클릭
|
2. "Re-run all jobs" 클릭
|
||||||
|
|
||||||
### 2. kubectl을 이용한 롤백
|
### 2. kubectl을 이용한 롤백
|
||||||
```bash
|
```bash
|
||||||
# 이전 버전으로 롤백
|
# 이전 버전으로 롤백
|
||||||
kubectl rollout undo deployment/user-service -n phonebill-dev --to-revision=2
|
kubectl rollout undo deployment/{서비스명} -n phonebill-dg0500 --to-revision=2
|
||||||
|
|
||||||
# 롤백 상태 확인
|
# 롤백 상태 확인
|
||||||
kubectl rollout status deployment/user-service -n phonebill-dev
|
kubectl rollout status deployment/{서비스명} -n phonebill-dg0500
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 수동 스크립트를 이용한 롤백
|
### 3. 수동 스크립트를 이용한 롤백
|
||||||
```bash
|
```bash
|
||||||
# 이전 안정 버전 태그로 배포
|
# 이전 안정 버전 이미지 태그로 배포
|
||||||
./.github/scripts/deploy-actions.sh dev 20241215140000
|
./.github/scripts/deploy-actions.sh {환경} {이전태그}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📊 SonarQube Quality Gate 설정
|
## SonarQube Quality Gate 설정
|
||||||
|
|
||||||
각 서비스별 SonarQube 프로젝트에서 다음 Quality Gate 설정:
|
각 서비스별 프로젝트 생성 후 다음 Quality Gate 설정:
|
||||||
|
|
||||||
```
|
```
|
||||||
Coverage: >= 80%
|
Coverage: >= 80%
|
||||||
@ -192,66 +192,62 @@ Reliability Rating: <= A
|
|||||||
Security Rating: <= A
|
Security Rating: <= A
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🐛 트러블슈팅
|
## 모니터링 및 확인
|
||||||
|
|
||||||
### 1. Kustomize 빌드 실패
|
### 배포 상태 확인
|
||||||
```bash
|
```bash
|
||||||
# Base 매니페스트 검증
|
# Pod 상태 확인
|
||||||
kubectl kustomize .github/kustomize/base/
|
kubectl get pods -n phonebill-dg0500
|
||||||
|
|
||||||
# 환경별 오버레이 검증
|
# 서비스 상태 확인
|
||||||
kubectl kustomize .github/kustomize/overlays/dev/
|
kubectl get services -n phonebill-dg0500
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 이미지 Pull 실패
|
# Ingress 상태 확인
|
||||||
- ACR 자격 증명 확인
|
kubectl get ingress -n phonebill-dg0500
|
||||||
- ImagePullSecret 설정 확인
|
|
||||||
|
|
||||||
### 3. 배포 타임아웃
|
|
||||||
```bash
|
|
||||||
# 배포 상태 확인
|
|
||||||
kubectl get pods -n phonebill-dev
|
|
||||||
|
|
||||||
# 로그 확인
|
# 로그 확인
|
||||||
kubectl logs -f deployment/user-service -n phonebill-dev
|
kubectl logs -f deployment/{서비스명} -n phonebill-dg0500
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. SonarQube 연결 실패
|
### 헬스 체크
|
||||||
- SONAR_HOST_URL과 SONAR_TOKEN 확인
|
|
||||||
- SonarQube 서버 상태 확인
|
|
||||||
|
|
||||||
## 📋 체크리스트
|
|
||||||
|
|
||||||
### 배포 전 확인사항
|
|
||||||
- [ ] GitHub Secrets 모든 항목 설정 완료
|
|
||||||
- [ ] Repository Variables 설정 완료
|
|
||||||
- [ ] ACR 접근 권한 확인
|
|
||||||
- [ ] AKS 클러스터 접근 권한 확인
|
|
||||||
- [ ] SonarQube 서버 상태 확인 (분석 수행시)
|
|
||||||
|
|
||||||
### 배포 후 확인사항
|
|
||||||
- [ ] 모든 Pod가 Running 상태인지 확인
|
|
||||||
- [ ] Service와 Ingress가 정상적으로 생성되었는지 확인
|
|
||||||
- [ ] Health Check 엔드포인트 응답 확인
|
|
||||||
- [ ] 로그에 에러가 없는지 확인
|
|
||||||
|
|
||||||
## 📞 지원 및 연락처
|
|
||||||
|
|
||||||
문제 발생시 다음 명령어로 디버깅 정보를 수집하여 지원팀에 문의하세요:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 시스템 상태 확인
|
# API Gateway 헬스 체크
|
||||||
kubectl get all -n phonebill-{환경}
|
curl -f http://phonebill-dg0500-api.20.214.196.128.nip.io/actuator/health
|
||||||
|
|
||||||
# 로그 수집
|
|
||||||
kubectl logs -l app.kubernetes.io/name=user-service -n phonebill-{환경}
|
|
||||||
|
|
||||||
# 이벤트 확인
|
|
||||||
kubectl get events -n phonebill-{환경} --sort-by='.lastTimestamp'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 주요 특징
|
||||||
|
|
||||||
|
1. **환경별 분리**: dev, staging, prod 환경별 독립적인 설정
|
||||||
|
2. **Kustomize 사용**: 환경별 매니페스트 관리 자동화
|
||||||
|
3. **SonarQube 통합**: 코드 품질 분석 및 Quality Gate
|
||||||
|
4. **Docker 최적화**: Multi-stage 빌드 및 Rate Limit 방지
|
||||||
|
5. **자동 배포**: Push/PR 시 자동 빌드 및 배포
|
||||||
|
6. **수동 배포**: 운영진이 필요 시 수동 실행 가능
|
||||||
|
7. **롤백 지원**: 다양한 방법의 롤백 기능
|
||||||
|
|
||||||
|
## 문제 해결
|
||||||
|
|
||||||
|
### 일반적인 오류
|
||||||
|
|
||||||
|
1. **Azure 인증 실패**
|
||||||
|
- AZURE_CREDENTIALS 설정 확인
|
||||||
|
- Service Principal 권한 확인
|
||||||
|
|
||||||
|
2. **ACR 접근 실패**
|
||||||
|
- ACR_USERNAME, ACR_PASSWORD 확인
|
||||||
|
- ACR 권한 설정 확인
|
||||||
|
|
||||||
|
3. **SonarQube 분석 실패**
|
||||||
|
- SONAR_TOKEN, SONAR_HOST_URL 확인
|
||||||
|
- SonarQube 서버 접근성 확인
|
||||||
|
|
||||||
|
4. **Kustomize 오류**
|
||||||
|
- patch 파일 경로 및 target 확인
|
||||||
|
- YAML 문법 오류 확인
|
||||||
|
|
||||||
|
### 연락처
|
||||||
|
문제 발생 시 DevOps 팀에 문의하거나 GitHub Issues를 통해 보고해 주세요.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
✅ **GitHub Actions CI/CD 파이프라인 구축이 완료되었습니다!**
|
**최운영/데옵스**: GitHub Actions CI/CD 파이프라인 구축이 완료되었습니다! 🎉
|
||||||
|
|
||||||
이제 코드를 푸시하거나 수동으로 워크플로우를 실행하여 자동 배포를 테스트할 수 있습니다.
|
|
||||||
@ -2,7 +2,6 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-api-gateway
|
name: cm-api-gateway
|
||||||
|
|
||||||
data:
|
data:
|
||||||
SERVER_PORT: "8080"
|
SERVER_PORT: "8080"
|
||||||
BILL_SERVICE_URL: "http://bill-service"
|
BILL_SERVICE_URL: "http://bill-service"
|
||||||
|
|||||||
@ -2,7 +2,6 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: api-gateway
|
name: api-gateway
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
@ -2,11 +2,10 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: api-gateway
|
name: api-gateway
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: api-gateway
|
app: api-gateway
|
||||||
ports:
|
ports:
|
||||||
- port: 80
|
- port: 80
|
||||||
targetPort: 8080
|
targetPort: 8080
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
|
|||||||
@ -2,7 +2,6 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-bill-service
|
name: cm-bill-service
|
||||||
|
|
||||||
data:
|
data:
|
||||||
SERVER_PORT: "8082"
|
SERVER_PORT: "8082"
|
||||||
DB_KIND: "postgresql"
|
DB_KIND: "postgresql"
|
||||||
|
|||||||
@ -2,7 +2,6 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: bill-service
|
name: bill-service
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-bill-service
|
name: secret-bill-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
||||||
DB_NAME: "bill_inquiry_db"
|
DB_NAME: "bill_inquiry_db"
|
||||||
DB_USERNAME: "bill_inquiry_user"
|
DB_USERNAME: "bill_inquiry_user"
|
||||||
DB_PASSWORD: "BillUser2025!"
|
DB_PASSWORD: "BillUser2025@"
|
||||||
|
|||||||
@ -2,7 +2,6 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: bill-service
|
name: bill-service
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: bill-service
|
app: bill-service
|
||||||
|
|||||||
3
.github/kustomize/base/common/cm-common.yaml
vendored
3
.github/kustomize/base/common/cm-common.yaml
vendored
@ -2,9 +2,8 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-common
|
name: cm-common
|
||||||
|
|
||||||
data:
|
data:
|
||||||
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill.20.214.196.128.nip.io"
|
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill-dg0500.20.214.196.128.nip.io"
|
||||||
JWT_ACCESS_TOKEN_VALIDITY: "18000000"
|
JWT_ACCESS_TOKEN_VALIDITY: "18000000"
|
||||||
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
||||||
REDIS_PORT: "6379"
|
REDIS_PORT: "6379"
|
||||||
|
|||||||
6
.github/kustomize/base/common/ingress.yaml
vendored
6
.github/kustomize/base/common/ingress.yaml
vendored
@ -2,14 +2,13 @@ apiVersion: networking.k8s.io/v1
|
|||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: phonebill
|
name: phonebill
|
||||||
|
|
||||||
annotations:
|
annotations:
|
||||||
kubernetes.io/ingress.class: nginx
|
kubernetes.io/ingress.class: nginx
|
||||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
||||||
spec:
|
spec:
|
||||||
ingressClassName: nginx
|
ingressClassName: nginx
|
||||||
rules:
|
rules:
|
||||||
- host: phonebill-api.20.214.196.128.nip.io
|
- host: phonebill-dg0500-api.20.214.196.128.nip.io
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /api/v1/auth
|
- path: /api/v1/auth
|
||||||
@ -46,4 +45,5 @@ spec:
|
|||||||
service:
|
service:
|
||||||
name: kos-mock
|
name: kos-mock
|
||||||
port:
|
port:
|
||||||
number: 80
|
number: 80
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,8 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-common
|
name: secret-common
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
||||||
REDIS_HOST: "redis-cache-dev-master"
|
REDIS_HOST: "redis-cache-dev-master"
|
||||||
REDIS_PASSWORD: "Redis2025Dev!"
|
REDIS_PASSWORD: "Redis2025Dev@"
|
||||||
@ -2,7 +2,6 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: phonebill
|
name: phonebill
|
||||||
|
|
||||||
type: kubernetes.io/dockerconfigjson
|
type: kubernetes.io/dockerconfigjson
|
||||||
stringData:
|
stringData:
|
||||||
.dockerconfigjson: |
|
.dockerconfigjson: |
|
||||||
|
|||||||
@ -2,6 +2,5 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-kos-mock
|
name: cm-kos-mock
|
||||||
|
|
||||||
data:
|
data:
|
||||||
SERVER_PORT: "8084"
|
SERVER_PORT: "8084"
|
||||||
@ -2,7 +2,6 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: kos-mock
|
name: kos-mock
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
1
.github/kustomize/base/kos-mock/service.yaml
vendored
1
.github/kustomize/base/kos-mock/service.yaml
vendored
@ -2,7 +2,6 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: kos-mock
|
name: kos-mock
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: kos-mock
|
app: kos-mock
|
||||||
|
|||||||
2
.github/kustomize/base/kustomization.yaml
vendored
2
.github/kustomize/base/kustomization.yaml
vendored
@ -6,10 +6,10 @@ metadata:
|
|||||||
|
|
||||||
resources:
|
resources:
|
||||||
# Common resources
|
# Common resources
|
||||||
- common/ingress.yaml
|
|
||||||
- common/cm-common.yaml
|
- common/cm-common.yaml
|
||||||
- common/secret-common.yaml
|
- common/secret-common.yaml
|
||||||
- common/secret-imagepull.yaml
|
- common/secret-imagepull.yaml
|
||||||
|
- common/ingress.yaml
|
||||||
|
|
||||||
# API Gateway
|
# API Gateway
|
||||||
- api-gateway/deployment.yaml
|
- api-gateway/deployment.yaml
|
||||||
|
|||||||
@ -2,7 +2,6 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-product-service
|
name: cm-product-service
|
||||||
|
|
||||||
data:
|
data:
|
||||||
SERVER_PORT: "8083"
|
SERVER_PORT: "8083"
|
||||||
DB_KIND: "postgresql"
|
DB_KIND: "postgresql"
|
||||||
|
|||||||
@ -2,7 +2,6 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: product-service
|
name: product-service
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-product-service
|
name: secret-product-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "product-change-postgres-dev-postgresql"
|
DB_HOST: "product-change-postgres-dev-postgresql"
|
||||||
DB_NAME: "product_change_db"
|
DB_NAME: "product_change_db"
|
||||||
DB_USERNAME: "product_change_user"
|
DB_USERNAME: "product_change_user"
|
||||||
DB_PASSWORD: "ProductUser2025!"
|
DB_PASSWORD: "ProductUser2025@"
|
||||||
@ -2,7 +2,6 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: product-service
|
name: product-service
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: product-service
|
app: product-service
|
||||||
|
|||||||
@ -2,7 +2,6 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: cm-user-service
|
name: cm-user-service
|
||||||
|
|
||||||
data:
|
data:
|
||||||
SERVER_PORT: "8081"
|
SERVER_PORT: "8081"
|
||||||
DB_KIND: "postgresql"
|
DB_KIND: "postgresql"
|
||||||
|
|||||||
@ -2,7 +2,6 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: user-service
|
name: user-service
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-user-service
|
name: secret-user-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "auth-postgres-dev-postgresql"
|
DB_HOST: "auth-postgres-dev-postgresql"
|
||||||
DB_NAME: "phonebill_auth"
|
DB_NAME: "phonebill_auth"
|
||||||
DB_USERNAME: "auth_user"
|
DB_USERNAME: "auth_user"
|
||||||
DB_PASSWORD: "AuthUser2025!"
|
DB_PASSWORD: "AuthUser2025@"
|
||||||
@ -2,7 +2,6 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: user-service
|
name: user-service
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: user-service
|
app: user-service
|
||||||
|
|||||||
@ -3,7 +3,7 @@ kind: ConfigMap
|
|||||||
metadata:
|
metadata:
|
||||||
name: cm-common
|
name: cm-common
|
||||||
data:
|
data:
|
||||||
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill.20.214.196.128.nip.io"
|
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill-dg0500-api.20.214.196.128.nip.io"
|
||||||
JWT_ACCESS_TOKEN_VALIDITY: "18000000"
|
JWT_ACCESS_TOKEN_VALIDITY: "18000000"
|
||||||
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
||||||
REDIS_PORT: "6379"
|
REDIS_PORT: "6379"
|
||||||
|
|||||||
@ -8,7 +8,7 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
ingressClassName: nginx
|
ingressClassName: nginx
|
||||||
rules:
|
rules:
|
||||||
- host: phonebill-api.20.214.196.128.nip.io
|
- host: phonebill-dg0500-api.20.214.196.128.nip.io
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /api/v1/auth
|
- path: /api/v1/auth
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
|
|
||||||
namespace: phonebill-dev
|
namespace: phonebill-dg0500
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
- ../../base
|
- ../../base
|
||||||
@ -11,14 +11,6 @@ patches:
|
|||||||
target:
|
target:
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
name: cm-common
|
name: cm-common
|
||||||
- path: secret-common-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Secret
|
|
||||||
name: secret-common
|
|
||||||
- path: ingress-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Ingress
|
|
||||||
name: phonebill
|
|
||||||
- path: deployment-api-gateway-patch.yaml
|
- path: deployment-api-gateway-patch.yaml
|
||||||
target:
|
target:
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@ -39,6 +31,14 @@ patches:
|
|||||||
target:
|
target:
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
name: kos-mock
|
name: kos-mock
|
||||||
|
- path: ingress-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Ingress
|
||||||
|
name: phonebill
|
||||||
|
- path: secret-common-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: secret-common
|
||||||
- path: secret-user-service-patch.yaml
|
- path: secret-user-service-patch.yaml
|
||||||
target:
|
target:
|
||||||
kind: Secret
|
kind: Secret
|
||||||
|
|||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-bill-service
|
name: secret-bill-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
||||||
DB_NAME: "bill_inquiry_db"
|
DB_NAME: "bill_inquiry_db"
|
||||||
DB_USERNAME: "bill_inquiry_user"
|
DB_USERNAME: "bill_inquiry_user"
|
||||||
DB_PASSWORD: "BillUser2025!"
|
DB_PASSWORD: "BillUser2025@"
|
||||||
|
|||||||
@ -6,4 +6,4 @@ type: Opaque
|
|||||||
stringData:
|
stringData:
|
||||||
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
||||||
REDIS_HOST: "redis-cache-dev-master"
|
REDIS_HOST: "redis-cache-dev-master"
|
||||||
REDIS_PASSWORD: "Redis2025Dev!"
|
REDIS_PASSWORD: "Redis2025Dev@"
|
||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-product-service
|
name: secret-product-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "product-change-postgres-dev-postgresql"
|
DB_HOST: "product-change-postgres-dev-postgresql"
|
||||||
DB_NAME: "product_change_db"
|
DB_NAME: "product_change_db"
|
||||||
DB_USERNAME: "product_change_user"
|
DB_USERNAME: "product_change_user"
|
||||||
DB_PASSWORD: "ProductUser2025!"
|
DB_PASSWORD: "ProductUser2025@"
|
||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-user-service
|
name: secret-user-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "auth-postgres-dev-postgresql"
|
DB_HOST: "auth-postgres-dev-postgresql"
|
||||||
DB_NAME: "phonebill_auth"
|
DB_NAME: "phonebill_auth"
|
||||||
DB_USERNAME: "auth_user"
|
DB_USERNAME: "auth_user"
|
||||||
DB_PASSWORD: "AuthUser2025!"
|
DB_PASSWORD: "AuthUser2025@"
|
||||||
@ -3,9 +3,9 @@ kind: ConfigMap
|
|||||||
metadata:
|
metadata:
|
||||||
name: cm-common
|
name: cm-common
|
||||||
data:
|
data:
|
||||||
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill.20.214.196.128.nip.io"
|
CORS_ALLOWED_ORIGINS: "https://phonebill.digitalgarage.com,https://phonebill-prod.digitalgarage.com"
|
||||||
JWT_ACCESS_TOKEN_VALIDITY: "18000000"
|
JWT_ACCESS_TOKEN_VALIDITY: "3600000"
|
||||||
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
JWT_REFRESH_TOKEN_VALIDITY: "43200000"
|
||||||
REDIS_PORT: "6379"
|
REDIS_PORT: "6379"
|
||||||
SPRING_PROFILES_ACTIVE: "dev"
|
SPRING_PROFILES_ACTIVE: "prod"
|
||||||
DDL_AUTO: "update"
|
DDL_AUTO: "validate"
|
||||||
@ -3,15 +3,15 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: api-gateway
|
name: api-gateway
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 3
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: api-gateway
|
- name: api-gateway
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "256Mi"
|
|
||||||
cpu: "256m"
|
|
||||||
limits:
|
|
||||||
memory: "1024Mi"
|
memory: "1024Mi"
|
||||||
cpu: "1024m"
|
cpu: "1024m"
|
||||||
|
limits:
|
||||||
|
memory: "4096Mi"
|
||||||
|
cpu: "4096m"
|
||||||
|
|||||||
@ -3,15 +3,15 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: bill-service
|
name: bill-service
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 3
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: bill-service
|
- name: bill-service
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "256Mi"
|
|
||||||
cpu: "256m"
|
|
||||||
limits:
|
|
||||||
memory: "1024Mi"
|
memory: "1024Mi"
|
||||||
cpu: "1024m"
|
cpu: "1024m"
|
||||||
|
limits:
|
||||||
|
memory: "4096Mi"
|
||||||
|
cpu: "4096m"
|
||||||
|
|||||||
@ -3,15 +3,15 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: kos-mock
|
name: kos-mock
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 3
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: kos-mock
|
- name: kos-mock
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "256Mi"
|
|
||||||
cpu: "256m"
|
|
||||||
limits:
|
|
||||||
memory: "1024Mi"
|
memory: "1024Mi"
|
||||||
cpu: "1024m"
|
cpu: "1024m"
|
||||||
|
limits:
|
||||||
|
memory: "4096Mi"
|
||||||
|
cpu: "4096m"
|
||||||
|
|||||||
@ -3,15 +3,15 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: product-service
|
name: product-service
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 3
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: product-service
|
- name: product-service
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "256Mi"
|
|
||||||
cpu: "256m"
|
|
||||||
limits:
|
|
||||||
memory: "1024Mi"
|
memory: "1024Mi"
|
||||||
cpu: "1024m"
|
cpu: "1024m"
|
||||||
|
limits:
|
||||||
|
memory: "4096Mi"
|
||||||
|
cpu: "4096m"
|
||||||
|
|||||||
@ -3,15 +3,15 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: user-service
|
name: user-service
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 3
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: user-service
|
- name: user-service
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "256Mi"
|
|
||||||
cpu: "256m"
|
|
||||||
limits:
|
|
||||||
memory: "1024Mi"
|
memory: "1024Mi"
|
||||||
cpu: "1024m"
|
cpu: "1024m"
|
||||||
|
limits:
|
||||||
|
memory: "4096Mi"
|
||||||
|
cpu: "4096m"
|
||||||
|
|||||||
@ -4,11 +4,16 @@ metadata:
|
|||||||
name: phonebill
|
name: phonebill
|
||||||
annotations:
|
annotations:
|
||||||
kubernetes.io/ingress.class: nginx
|
kubernetes.io/ingress.class: nginx
|
||||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||||
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||||
spec:
|
spec:
|
||||||
ingressClassName: nginx
|
ingressClassName: nginx
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- phonebill.digitalgarage.com
|
||||||
|
secretName: phonebill-prod-tls
|
||||||
rules:
|
rules:
|
||||||
- host: phonebill-api.20.214.196.128.nip.io
|
- host: phonebill.digitalgarage.com
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /api/v1/auth
|
- path: /api/v1/auth
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
|
|
||||||
namespace: phonebill-prod
|
namespace: phonebill-dg0500
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
- ../../base
|
- ../../base
|
||||||
@ -11,14 +11,6 @@ patches:
|
|||||||
target:
|
target:
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
name: cm-common
|
name: cm-common
|
||||||
- path: secret-common-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Secret
|
|
||||||
name: secret-common
|
|
||||||
- path: ingress-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Ingress
|
|
||||||
name: phonebill
|
|
||||||
- path: deployment-api-gateway-patch.yaml
|
- path: deployment-api-gateway-patch.yaml
|
||||||
target:
|
target:
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@ -39,6 +31,14 @@ patches:
|
|||||||
target:
|
target:
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
name: kos-mock
|
name: kos-mock
|
||||||
|
- path: ingress-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Ingress
|
||||||
|
name: phonebill
|
||||||
|
- path: secret-common-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: secret-common
|
||||||
- path: secret-user-service-patch.yaml
|
- path: secret-user-service-patch.yaml
|
||||||
target:
|
target:
|
||||||
kind: Secret
|
kind: Secret
|
||||||
|
|||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-bill-service
|
name: secret-bill-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
||||||
DB_NAME: "bill_inquiry_db"
|
DB_NAME: "bill_inquiry_db"
|
||||||
DB_USERNAME: "bill_inquiry_user"
|
DB_USERNAME: "bill_inquiry_user"
|
||||||
DB_PASSWORD: "BillUser2025!"
|
DB_PASSWORD: "BillUser2025@"
|
||||||
|
|||||||
@ -6,4 +6,4 @@ type: Opaque
|
|||||||
stringData:
|
stringData:
|
||||||
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
||||||
REDIS_HOST: "redis-cache-dev-master"
|
REDIS_HOST: "redis-cache-dev-master"
|
||||||
REDIS_PASSWORD: "Redis2025Dev!"
|
REDIS_PASSWORD: "Redis2025Dev@"
|
||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-product-service
|
name: secret-product-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "product-change-postgres-dev-postgresql"
|
DB_HOST: "product-change-postgres-dev-postgresql"
|
||||||
DB_NAME: "product_change_db"
|
DB_NAME: "product_change_db"
|
||||||
DB_USERNAME: "product_change_user"
|
DB_USERNAME: "product_change_user"
|
||||||
DB_PASSWORD: "ProductUser2025!"
|
DB_PASSWORD: "ProductUser2025@"
|
||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-user-service
|
name: secret-user-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "auth-postgres-dev-postgresql"
|
DB_HOST: "auth-postgres-dev-postgresql"
|
||||||
DB_NAME: "phonebill_auth"
|
DB_NAME: "phonebill_auth"
|
||||||
DB_USERNAME: "auth_user"
|
DB_USERNAME: "auth_user"
|
||||||
DB_PASSWORD: "AuthUser2025!"
|
DB_PASSWORD: "AuthUser2025@"
|
||||||
@ -3,9 +3,9 @@ kind: ConfigMap
|
|||||||
metadata:
|
metadata:
|
||||||
name: cm-common
|
name: cm-common
|
||||||
data:
|
data:
|
||||||
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill.20.214.196.128.nip.io"
|
CORS_ALLOWED_ORIGINS: "https://phonebill.staging.digitalgarage.com,https://phonebill-staging.digitalgarage.com"
|
||||||
JWT_ACCESS_TOKEN_VALIDITY: "18000000"
|
JWT_ACCESS_TOKEN_VALIDITY: "18000000"
|
||||||
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
||||||
REDIS_PORT: "6379"
|
REDIS_PORT: "6379"
|
||||||
SPRING_PROFILES_ACTIVE: "dev"
|
SPRING_PROFILES_ACTIVE: "staging"
|
||||||
DDL_AUTO: "update"
|
DDL_AUTO: "validate"
|
||||||
@ -3,15 +3,15 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: api-gateway
|
name: api-gateway
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 2
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: api-gateway
|
- name: api-gateway
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "256Mi"
|
memory: "512Mi"
|
||||||
cpu: "256m"
|
cpu: "512m"
|
||||||
limits:
|
limits:
|
||||||
memory: "1024Mi"
|
memory: "2048Mi"
|
||||||
cpu: "1024m"
|
cpu: "2048m"
|
||||||
|
|||||||
@ -3,15 +3,15 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: bill-service
|
name: bill-service
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 2
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: bill-service
|
- name: bill-service
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "256Mi"
|
memory: "512Mi"
|
||||||
cpu: "256m"
|
cpu: "512m"
|
||||||
limits:
|
limits:
|
||||||
memory: "1024Mi"
|
memory: "2048Mi"
|
||||||
cpu: "1024m"
|
cpu: "2048m"
|
||||||
|
|||||||
@ -3,15 +3,15 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: kos-mock
|
name: kos-mock
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 2
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: kos-mock
|
- name: kos-mock
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "256Mi"
|
memory: "512Mi"
|
||||||
cpu: "256m"
|
cpu: "512m"
|
||||||
limits:
|
limits:
|
||||||
memory: "1024Mi"
|
memory: "2048Mi"
|
||||||
cpu: "1024m"
|
cpu: "2048m"
|
||||||
|
|||||||
@ -3,15 +3,15 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: product-service
|
name: product-service
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 2
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: product-service
|
- name: product-service
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "256Mi"
|
memory: "512Mi"
|
||||||
cpu: "256m"
|
cpu: "512m"
|
||||||
limits:
|
limits:
|
||||||
memory: "1024Mi"
|
memory: "2048Mi"
|
||||||
cpu: "1024m"
|
cpu: "2048m"
|
||||||
|
|||||||
@ -3,15 +3,15 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: user-service
|
name: user-service
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 2
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: user-service
|
- name: user-service
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
memory: "256Mi"
|
memory: "512Mi"
|
||||||
cpu: "256m"
|
cpu: "512m"
|
||||||
limits:
|
limits:
|
||||||
memory: "1024Mi"
|
memory: "2048Mi"
|
||||||
cpu: "1024m"
|
cpu: "2048m"
|
||||||
|
|||||||
@ -4,11 +4,16 @@ metadata:
|
|||||||
name: phonebill
|
name: phonebill
|
||||||
annotations:
|
annotations:
|
||||||
kubernetes.io/ingress.class: nginx
|
kubernetes.io/ingress.class: nginx
|
||||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||||
|
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||||
spec:
|
spec:
|
||||||
ingressClassName: nginx
|
ingressClassName: nginx
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- phonebill-staging.digitalgarage.com
|
||||||
|
secretName: phonebill-staging-tls
|
||||||
rules:
|
rules:
|
||||||
- host: phonebill-api.20.214.196.128.nip.io
|
- host: phonebill-staging.digitalgarage.com
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /api/v1/auth
|
- path: /api/v1/auth
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
|
|
||||||
namespace: phonebill-staging
|
namespace: phonebill-dg0500
|
||||||
|
|
||||||
resources:
|
resources:
|
||||||
- ../../base
|
- ../../base
|
||||||
@ -11,14 +11,6 @@ patches:
|
|||||||
target:
|
target:
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
name: cm-common
|
name: cm-common
|
||||||
- path: secret-common-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Secret
|
|
||||||
name: secret-common
|
|
||||||
- path: ingress-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Ingress
|
|
||||||
name: phonebill
|
|
||||||
- path: deployment-api-gateway-patch.yaml
|
- path: deployment-api-gateway-patch.yaml
|
||||||
target:
|
target:
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@ -39,6 +31,14 @@ patches:
|
|||||||
target:
|
target:
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
name: kos-mock
|
name: kos-mock
|
||||||
|
- path: ingress-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Ingress
|
||||||
|
name: phonebill
|
||||||
|
- path: secret-common-patch.yaml
|
||||||
|
target:
|
||||||
|
kind: Secret
|
||||||
|
name: secret-common
|
||||||
- path: secret-user-service-patch.yaml
|
- path: secret-user-service-patch.yaml
|
||||||
target:
|
target:
|
||||||
kind: Secret
|
kind: Secret
|
||||||
|
|||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-bill-service
|
name: secret-bill-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
||||||
DB_NAME: "bill_inquiry_db"
|
DB_NAME: "bill_inquiry_db"
|
||||||
DB_USERNAME: "bill_inquiry_user"
|
DB_USERNAME: "bill_inquiry_user"
|
||||||
DB_PASSWORD: "BillUser2025!"
|
DB_PASSWORD: "BillUser2025@"
|
||||||
|
|||||||
@ -6,4 +6,4 @@ type: Opaque
|
|||||||
stringData:
|
stringData:
|
||||||
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
||||||
REDIS_HOST: "redis-cache-dev-master"
|
REDIS_HOST: "redis-cache-dev-master"
|
||||||
REDIS_PASSWORD: "Redis2025Dev!"
|
REDIS_PASSWORD: "Redis2025Dev@"
|
||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-product-service
|
name: secret-product-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "product-change-postgres-dev-postgresql"
|
DB_HOST: "product-change-postgres-dev-postgresql"
|
||||||
DB_NAME: "product_change_db"
|
DB_NAME: "product_change_db"
|
||||||
DB_USERNAME: "product_change_user"
|
DB_USERNAME: "product_change_user"
|
||||||
DB_PASSWORD: "ProductUser2025!"
|
DB_PASSWORD: "ProductUser2025@"
|
||||||
@ -2,10 +2,9 @@ apiVersion: v1
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: secret-user-service
|
name: secret-user-service
|
||||||
|
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
DB_HOST: "auth-postgres-dev-postgresql"
|
DB_HOST: "auth-postgres-dev-postgresql"
|
||||||
DB_NAME: "phonebill_auth"
|
DB_NAME: "phonebill_auth"
|
||||||
DB_USERNAME: "auth_user"
|
DB_USERNAME: "auth_user"
|
||||||
DB_PASSWORD: "AuthUser2025!"
|
DB_PASSWORD: "AuthUser2025@"
|
||||||
16
.github/scripts/deploy-actions.sh
vendored
Normal file → Executable file
16
.github/scripts/deploy-actions.sh
vendored
Normal file → Executable file
@ -25,8 +25,8 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Create namespace
|
# Create namespace
|
||||||
echo "📝 Creating namespace phonebill-${ENVIRONMENT}..."
|
echo "📝 Creating namespace phonebill-dg0500..."
|
||||||
kubectl create namespace phonebill-${ENVIRONMENT} --dry-run=client -o yaml | kubectl apply -f -
|
kubectl create namespace phonebill-dg0500 --dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
# 환경별 이미지 태그 업데이트 (.github/kustomize 사용)
|
# 환경별 이미지 태그 업데이트 (.github/kustomize 사용)
|
||||||
cd .github/kustomize/overlays/${ENVIRONMENT}
|
cd .github/kustomize/overlays/${ENVIRONMENT}
|
||||||
@ -47,18 +47,18 @@ kubectl apply -k .
|
|||||||
echo "⏳ Waiting for deployments to be ready..."
|
echo "⏳ Waiting for deployments to be ready..."
|
||||||
# 서비스별 배포 상태 확인
|
# 서비스별 배포 상태 확인
|
||||||
for service in "${services[@]}"; do
|
for service in "${services[@]}"; do
|
||||||
kubectl rollout status deployment/$service -n phonebill-${ENVIRONMENT} --timeout=300s
|
kubectl rollout status deployment/$service -n phonebill-dg0500 --timeout=300s
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "🔍 Health check..."
|
echo "🔍 Health check..."
|
||||||
# API Gateway Health Check (첫 번째 서비스가 API Gateway라고 가정)
|
# API Gateway Health Check (첫 번째 서비스가 API Gateway라고 가정)
|
||||||
GATEWAY_SERVICE=${services[0]}
|
GATEWAY_SERVICE=${services[0]}
|
||||||
GATEWAY_POD=$(kubectl get pod -n phonebill-${ENVIRONMENT} -l app.kubernetes.io/name=$GATEWAY_SERVICE -o jsonpath='{.items[0].metadata.name}')
|
GATEWAY_POD=$(kubectl get pod -n phonebill-dg0500 -l app.kubernetes.io/name=$GATEWAY_SERVICE -o jsonpath='{.items[0].metadata.name}')
|
||||||
kubectl -n phonebill-${ENVIRONMENT} exec $GATEWAY_POD -- curl -f http://localhost:8080/actuator/health || echo "Health check failed, but deployment completed"
|
kubectl -n phonebill-dg0500 exec $GATEWAY_POD -- curl -f http://localhost:8080/actuator/health || echo "Health check failed, but deployment completed"
|
||||||
|
|
||||||
echo "📋 Service Information:"
|
echo "📋 Service Information:"
|
||||||
kubectl get pods -n phonebill-${ENVIRONMENT}
|
kubectl get pods -n phonebill-dg0500
|
||||||
kubectl get services -n phonebill-${ENVIRONMENT}
|
kubectl get services -n phonebill-dg0500
|
||||||
kubectl get ingress -n phonebill-${ENVIRONMENT}
|
kubectl get ingress -n phonebill-dg0500
|
||||||
|
|
||||||
echo "✅ GitHub Actions deployment completed successfully!"
|
echo "✅ GitHub Actions deployment completed successfully!"
|
||||||
21
.github/workflows/backend-cicd.yaml
vendored
21
.github/workflows/backend-cicd.yaml
vendored
@ -1,4 +1,5 @@
|
|||||||
name: Backend Services CI/CD
|
name: Backend Services CI/CD
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main, develop ]
|
branches: [ main, develop ]
|
||||||
@ -74,6 +75,7 @@ jobs:
|
|||||||
IMAGE_ORG="phonebill"
|
IMAGE_ORG="phonebill"
|
||||||
RESOURCE_GROUP="rg-digitalgarage-01"
|
RESOURCE_GROUP="rg-digitalgarage-01"
|
||||||
AKS_CLUSTER="aks-digitalgarage-01"
|
AKS_CLUSTER="aks-digitalgarage-01"
|
||||||
|
NAMESPACE="phonebill-dg0500"
|
||||||
|
|
||||||
# Read environment variables from .github/config file
|
# Read environment variables from .github/config file
|
||||||
if [[ -f ".github/config/deploy_env_vars_${ENV}" ]]; then
|
if [[ -f ".github/config/deploy_env_vars_${ENV}" ]]; then
|
||||||
@ -127,8 +129,8 @@ jobs:
|
|||||||
# Run tests, coverage reports, and SonarQube analysis for each service
|
# Run tests, coverage reports, and SonarQube analysis for each service
|
||||||
for service in "${services[@]}"; do
|
for service in "${services[@]}"; do
|
||||||
./gradlew :$service:test :$service:jacocoTestReport :$service:sonar \
|
./gradlew :$service:test :$service:jacocoTestReport :$service:sonar \
|
||||||
-Dsonar.projectKey=phonebill-$service-${{ steps.determine_env.outputs.environment }} \
|
-Dsonar.projectKey=phonebill-$service-dg0500 \
|
||||||
-Dsonar.projectName=phonebill-$service-${{ steps.determine_env.outputs.environment }} \
|
-Dsonar.projectName=phonebill-$service-dg0500 \
|
||||||
-Dsonar.host.url=$SONAR_HOST_URL \
|
-Dsonar.host.url=$SONAR_HOST_URL \
|
||||||
-Dsonar.token=$SONAR_TOKEN \
|
-Dsonar.token=$SONAR_TOKEN \
|
||||||
-Dsonar.java.binaries=build/classes/java/main \
|
-Dsonar.java.binaries=build/classes/java/main \
|
||||||
@ -151,7 +153,8 @@ jobs:
|
|||||||
id: set_outputs
|
id: set_outputs
|
||||||
run: |
|
run: |
|
||||||
# Generate timestamp for image tag
|
# Generate timestamp for image tag
|
||||||
IMAGE_TAG=$(date +%Y%m%d%H%M%S)
|
#IMAGE_TAG=$(date +%Y%m%d%H%M%S)
|
||||||
|
IMAGE_TAG=dg0500
|
||||||
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
|
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
|
||||||
echo "environment=${{ steps.determine_env.outputs.environment }}" >> $GITHUB_OUTPUT
|
echo "environment=${{ steps.determine_env.outputs.environment }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
@ -241,7 +244,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create namespace
|
- name: Create namespace
|
||||||
run: |
|
run: |
|
||||||
kubectl create namespace phonebill-${{ env.ENVIRONMENT }} --dry-run=client -o yaml | kubectl apply -f -
|
kubectl create namespace phonebill-dg0500 --dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
- name: Install Kustomize
|
- name: Install Kustomize
|
||||||
run: |
|
run: |
|
||||||
@ -266,8 +269,8 @@ jobs:
|
|||||||
- name: Wait for deployments to be ready
|
- name: Wait for deployments to be ready
|
||||||
run: |
|
run: |
|
||||||
echo "Waiting for deployments to be ready..."
|
echo "Waiting for deployments to be ready..."
|
||||||
kubectl -n phonebill-${{ env.ENVIRONMENT }} wait --for=condition=available deployment/api-gateway --timeout=300s
|
kubectl -n phonebill-dg0500 wait --for=condition=available deployment/api-gateway --timeout=300s
|
||||||
kubectl -n phonebill-${{ env.ENVIRONMENT }} wait --for=condition=available deployment/user-service --timeout=300s
|
kubectl -n phonebill-dg0500 wait --for=condition=available deployment/user-service --timeout=300s
|
||||||
kubectl -n phonebill-${{ env.ENVIRONMENT }} wait --for=condition=available deployment/bill-service --timeout=300s
|
kubectl -n phonebill-dg0500 wait --for=condition=available deployment/bill-service --timeout=300s
|
||||||
kubectl -n phonebill-${{ env.ENVIRONMENT }} wait --for=condition=available deployment/product-service --timeout=300s
|
kubectl -n phonebill-dg0500 wait --for=condition=available deployment/product-service --timeout=300s
|
||||||
kubectl -n phonebill-${{ env.ENVIRONMENT }} wait --for=condition=available deployment/kos-mock --timeout=300s
|
kubectl -n phonebill-dg0500 wait --for=condition=available deployment/kos-mock --timeout=300s
|
||||||
251
.github/workflows/backend-cicd_ArgoCD.yaml
vendored
251
.github/workflows/backend-cicd_ArgoCD.yaml
vendored
@ -1,251 +0,0 @@
|
|||||||
name: Backend Services CI/CD (ArgoCD)
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main, develop ]
|
|
||||||
paths:
|
|
||||||
- 'api-gateway/**'
|
|
||||||
- 'user-service/**'
|
|
||||||
- 'bill-service/**'
|
|
||||||
- 'product-service/**'
|
|
||||||
- 'kos-mock/**'
|
|
||||||
- 'common/**'
|
|
||||||
- '.github/**'
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
ENVIRONMENT:
|
|
||||||
description: 'Target environment'
|
|
||||||
required: true
|
|
||||||
default: 'dev'
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- dev
|
|
||||||
- staging
|
|
||||||
- prod
|
|
||||||
SKIP_SONARQUBE:
|
|
||||||
description: 'Skip SonarQube Analysis'
|
|
||||||
required: false
|
|
||||||
default: 'true'
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- 'true'
|
|
||||||
- 'false'
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: acrdigitalgarage01.azurecr.io
|
|
||||||
IMAGE_ORG: phonebill
|
|
||||||
RESOURCE_GROUP: rg-digitalgarage-01
|
|
||||||
AKS_CLUSTER: aks-digitalgarage-01
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build and Test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
image_tag: ${{ steps.set_outputs.outputs.image_tag }}
|
|
||||||
environment: ${{ steps.set_outputs.outputs.environment }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up JDK 21
|
|
||||||
uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
java-version: '21'
|
|
||||||
distribution: 'temurin'
|
|
||||||
cache: 'gradle'
|
|
||||||
|
|
||||||
- name: Determine environment
|
|
||||||
id: determine_env
|
|
||||||
run: |
|
|
||||||
# Use input parameter or default to 'dev'
|
|
||||||
ENVIRONMENT="${{ github.event.inputs.ENVIRONMENT || 'dev' }}"
|
|
||||||
echo "environment=$ENVIRONMENT" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Load environment variables
|
|
||||||
id: env_vars
|
|
||||||
run: |
|
|
||||||
ENV=${{ steps.determine_env.outputs.environment }}
|
|
||||||
|
|
||||||
# Initialize variables with defaults
|
|
||||||
REGISTRY="acrdigitalgarage01.azurecr.io"
|
|
||||||
IMAGE_ORG="phonebill"
|
|
||||||
RESOURCE_GROUP="rg-digitalgarage-01"
|
|
||||||
AKS_CLUSTER="aks-digitalgarage-01"
|
|
||||||
|
|
||||||
# Read environment variables from .github/config file
|
|
||||||
if [[ -f ".github/config/deploy_env_vars_${ENV}" ]]; then
|
|
||||||
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-)
|
|
||||||
|
|
||||||
# Override defaults if found in config
|
|
||||||
case "$key" in
|
|
||||||
"resource_group") RESOURCE_GROUP="$value" ;;
|
|
||||||
"cluster_name") AKS_CLUSTER="$value" ;;
|
|
||||||
esac
|
|
||||||
done < ".github/config/deploy_env_vars_${ENV}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Export for other jobs
|
|
||||||
echo "REGISTRY=$REGISTRY" >> $GITHUB_ENV
|
|
||||||
echo "IMAGE_ORG=$IMAGE_ORG" >> $GITHUB_ENV
|
|
||||||
echo "RESOURCE_GROUP=$RESOURCE_GROUP" >> $GITHUB_ENV
|
|
||||||
echo "AKS_CLUSTER=$AKS_CLUSTER" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Grant execute permission for gradlew
|
|
||||||
run: chmod +x gradlew
|
|
||||||
|
|
||||||
- name: Build with Gradle
|
|
||||||
run: |
|
|
||||||
./gradlew build -x test
|
|
||||||
|
|
||||||
- name: SonarQube Analysis & Quality Gate
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
||||||
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
|
|
||||||
run: |
|
|
||||||
# Check if SonarQube should be skipped
|
|
||||||
SKIP_SONARQUBE="${{ github.event.inputs.SKIP_SONARQUBE || 'true' }}"
|
|
||||||
|
|
||||||
if [[ "$SKIP_SONARQUBE" == "true" ]]; then
|
|
||||||
echo "⏭️ Skipping SonarQube Analysis (SKIP_SONARQUBE=$SKIP_SONARQUBE)"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Define services array
|
|
||||||
services=(api-gateway user-service bill-service product-service kos-mock)
|
|
||||||
|
|
||||||
# Run tests, coverage reports, and SonarQube analysis for each service
|
|
||||||
for service in "${services[@]}"; do
|
|
||||||
./gradlew :$service:test :$service:jacocoTestReport :$service:sonar \
|
|
||||||
-Dsonar.projectKey=phonebill-$service-${{ steps.determine_env.outputs.environment }} \
|
|
||||||
-Dsonar.projectName=phonebill-$service-${{ steps.determine_env.outputs.environment }} \
|
|
||||||
-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 \
|
|
||||||
-Dsonar.exclusions=**/config/**,**/entity/**,**/dto/**,**/*Application.class,**/exception/**
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Upload build artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: app-builds
|
|
||||||
path: |
|
|
||||||
api-gateway/build/libs/*.jar
|
|
||||||
user-service/build/libs/*.jar
|
|
||||||
bill-service/build/libs/*.jar
|
|
||||||
product-service/build/libs/*.jar
|
|
||||||
kos-mock/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
|
|
||||||
echo "environment=${{ steps.determine_env.outputs.environment }}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
release:
|
|
||||||
name: Build and Push Docker Images
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Download build artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: app-builds
|
|
||||||
|
|
||||||
- name: Set environment variables from build job
|
|
||||||
run: |
|
|
||||||
echo "REGISTRY=${{ env.REGISTRY }}" >> $GITHUB_ENV
|
|
||||||
echo "IMAGE_ORG=${{ env.IMAGE_ORG }}" >> $GITHUB_ENV
|
|
||||||
echo "ENVIRONMENT=${{ needs.build.outputs.environment }}" >> $GITHUB_ENV
|
|
||||||
echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Login to Docker Hub (prevent rate limit)
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Login to Azure Container Registry
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ secrets.ACR_USERNAME }}
|
|
||||||
password: ${{ secrets.ACR_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Build and push Docker images for all services
|
|
||||||
run: |
|
|
||||||
# Define services array
|
|
||||||
services=(api-gateway user-service bill-service product-service kos-mock)
|
|
||||||
|
|
||||||
# Build and push each service image
|
|
||||||
for service in "${services[@]}"; do
|
|
||||||
echo "Building and pushing $service..."
|
|
||||||
docker build \
|
|
||||||
--build-arg BUILD_LIB_DIR="$service/build/libs" \
|
|
||||||
--build-arg ARTIFACTORY_FILE="$service.jar" \
|
|
||||||
-f deployment/container/Dockerfile-backend \
|
|
||||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} .
|
|
||||||
|
|
||||||
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }}
|
|
||||||
done
|
|
||||||
|
|
||||||
update-manifest:
|
|
||||||
name: Update Manifest Repository
|
|
||||||
needs: [build, release]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Set image tag environment variable
|
|
||||||
run: |
|
|
||||||
echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV
|
|
||||||
echo "ENVIRONMENT=${{ needs.build.outputs.environment }}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Update Manifest Repository
|
|
||||||
run: |
|
|
||||||
# 매니페스트 레포지토리 클론
|
|
||||||
REPO_URL=$(echo "https://github.com/cna-bootcamp/phonebill-manifest.git" | sed 's|https://||')
|
|
||||||
git clone https://${{ secrets.GIT_USERNAME }}:${{ secrets.GIT_PASSWORD }}@${REPO_URL} manifest-repo
|
|
||||||
cd manifest-repo
|
|
||||||
|
|
||||||
# Kustomize 설치
|
|
||||||
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
|
|
||||||
sudo mv kustomize /usr/local/bin/
|
|
||||||
|
|
||||||
# 매니페스트 업데이트
|
|
||||||
cd phonebill/kustomize/overlays/${{ env.ENVIRONMENT }}
|
|
||||||
|
|
||||||
# 각 서비스별 이미지 태그 업데이트
|
|
||||||
services="api-gateway user-service bill-service product-service kos-mock"
|
|
||||||
for service in $services; do
|
|
||||||
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/$service:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}
|
|
||||||
done
|
|
||||||
|
|
||||||
# Git 설정 및 푸시
|
|
||||||
cd ../../../..
|
|
||||||
git config user.name "GitHub Actions"
|
|
||||||
git config user.email "actions@github.com"
|
|
||||||
git add .
|
|
||||||
git commit -m "🚀 Update phonebill ${{ env.ENVIRONMENT }} images to ${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}"
|
|
||||||
git push origin main
|
|
||||||
|
|
||||||
echo "✅ 매니페스트 업데이트 완료. ArgoCD가 자동으로 배포합니다."
|
|
||||||
225
deployment/cicd/Jenkinsfile
vendored
225
deployment/cicd/Jenkinsfile
vendored
@ -1,225 +0,0 @@
|
|||||||
def PIPELINE_ID = "${env.BUILD_NUMBER}"
|
|
||||||
|
|
||||||
def getImageTag() {
|
|
||||||
def dateFormat = new java.text.SimpleDateFormat('yyyyMMddHHmmss')
|
|
||||||
def currentDate = new Date()
|
|
||||||
return dateFormat.format(currentDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
podTemplate(
|
|
||||||
label: "${PIPELINE_ID}",
|
|
||||||
serviceAccount: 'jenkins',
|
|
||||||
slaveConnectTimeout: 300,
|
|
||||||
idleMinutes: 1,
|
|
||||||
activeDeadlineSeconds: 3600,
|
|
||||||
podRetention: never(), // 파드 자동 정리 옵션: never(), onFailure(), always(), default()
|
|
||||||
yaml: '''
|
|
||||||
spec:
|
|
||||||
terminationGracePeriodSeconds: 3
|
|
||||||
restartPolicy: Never
|
|
||||||
tolerations:
|
|
||||||
- effect: NoSchedule
|
|
||||||
key: dedicated
|
|
||||||
operator: Equal
|
|
||||||
value: cicd
|
|
||||||
''',
|
|
||||||
containers: [
|
|
||||||
containerTemplate(
|
|
||||||
name: 'podman',
|
|
||||||
image: "mgoltzsche/podman",
|
|
||||||
ttyEnabled: true,
|
|
||||||
command: 'cat',
|
|
||||||
privileged: true,
|
|
||||||
resourceRequestCpu: '500m',
|
|
||||||
resourceRequestMemory: '2Gi',
|
|
||||||
resourceLimitCpu: '2000m',
|
|
||||||
resourceLimitMemory: '4Gi'
|
|
||||||
),
|
|
||||||
containerTemplate(
|
|
||||||
name: 'gradle',
|
|
||||||
image: 'gradle:jdk21',
|
|
||||||
ttyEnabled: true,
|
|
||||||
command: 'cat',
|
|
||||||
resourceRequestCpu: '500m',
|
|
||||||
resourceRequestMemory: '1Gi',
|
|
||||||
resourceLimitCpu: '1000m',
|
|
||||||
resourceLimitMemory: '2Gi',
|
|
||||||
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,
|
|
||||||
resourceRequestCpu: '200m',
|
|
||||||
resourceRequestMemory: '512Mi',
|
|
||||||
resourceLimitCpu: '500m',
|
|
||||||
resourceLimitMemory: '1Gi'
|
|
||||||
)
|
|
||||||
],
|
|
||||||
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 environment = params.ENVIRONMENT ?: 'dev'
|
|
||||||
def id = params.ID ?: 'dg0500'
|
|
||||||
def skipSonarQube = (params.SKIP_SONARQUBE?.toLowerCase() == 'true')
|
|
||||||
def services = ['api-gateway', 'user-service', 'bill-service', 'product-service', 'kos-mock']
|
|
||||||
|
|
||||||
try {
|
|
||||||
stage("Get Source") {
|
|
||||||
checkout scm
|
|
||||||
props = readProperties file: "deployment/cicd/config/deploy_env_vars_${environment}"
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ${props.resource_group} --name ${props.cluster_name} --overwrite-existing
|
|
||||||
kubectl create namespace phonebill-${id} --dry-run=client -o yaml | kubectl apply -f -
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('Build') {
|
|
||||||
container('gradle') {
|
|
||||||
sh """
|
|
||||||
chmod +x gradlew
|
|
||||||
./gradlew build -x test
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('SonarQube Analysis & Quality Gate') {
|
|
||||||
if (skipSonarQube) {
|
|
||||||
echo "⏭️ Skipping SonarQube Analysis (SKIP_SONARQUBE=${params.SKIP_SONARQUBE})"
|
|
||||||
} else {
|
|
||||||
container('gradle') {
|
|
||||||
withSonarQubeEnv('SonarQube') {
|
|
||||||
// 각 서비스별 테스트 및 SonarQube 분석
|
|
||||||
services.each { service ->
|
|
||||||
sh """
|
|
||||||
./gradlew :${service}:test :${service}:jacocoTestReport :${service}:sonar \\
|
|
||||||
-Dsonar.projectKey=phonebill-${service}-${id} \\
|
|
||||||
-Dsonar.projectName=phonebill-${service}-${id} \\
|
|
||||||
-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/**
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quality Gate 확인
|
|
||||||
timeout(time: 10, unit: 'MINUTES') {
|
|
||||||
def qg = waitForQualityGate()
|
|
||||||
if (qg.status != 'OK') {
|
|
||||||
error "Pipeline aborted due to quality gate failure: ${qg.status}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('Build & Push Images') {
|
|
||||||
timeout(time: 30, unit: 'MINUTES') {
|
|
||||||
container('podman') {
|
|
||||||
withCredentials([
|
|
||||||
usernamePassword(
|
|
||||||
credentialsId: 'acr-credentials',
|
|
||||||
usernameVariable: 'ACR_USERNAME',
|
|
||||||
passwordVariable: 'ACR_PASSWORD'
|
|
||||||
),
|
|
||||||
usernamePassword(
|
|
||||||
credentialsId: 'dockerhub-credentials',
|
|
||||||
usernameVariable: 'DOCKERHUB_USERNAME',
|
|
||||||
passwordVariable: 'DOCKERHUB_PASSWORD'
|
|
||||||
)
|
|
||||||
]) {
|
|
||||||
// Docker Hub 로그인 (rate limit 해결)
|
|
||||||
sh "podman login docker.io --username \$DOCKERHUB_USERNAME --password \$DOCKERHUB_PASSWORD"
|
|
||||||
|
|
||||||
// ACR 로그인
|
|
||||||
sh "podman login acrdigitalgarage01.azurecr.io --username \$ACR_USERNAME --password \$ACR_PASSWORD"
|
|
||||||
|
|
||||||
services.each { service ->
|
|
||||||
sh """
|
|
||||||
podman build \\
|
|
||||||
--build-arg BUILD_LIB_DIR="${service}/build/libs" \\
|
|
||||||
--build-arg ARTIFACTORY_FILE="${service}.jar" \\
|
|
||||||
-f deployment/container/Dockerfile-backend \\
|
|
||||||
-t acrdigitalgarage01.azurecr.io/phonebill/${service}:${id}-${imageTag} .
|
|
||||||
|
|
||||||
podman push acrdigitalgarage01.azurecr.io/phonebill/${service}:${id}-${imageTag}
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('Update Kustomize & Deploy') {
|
|
||||||
container('azure-cli') {
|
|
||||||
sh """
|
|
||||||
# Kustomize 설치 (sudo 없이 사용자 디렉토리에 설치)
|
|
||||||
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
|
|
||||||
mkdir -p \$HOME/bin
|
|
||||||
mv kustomize \$HOME/bin/
|
|
||||||
export PATH=\$PATH:\$HOME/bin
|
|
||||||
|
|
||||||
# 환경별 디렉토리로 이동
|
|
||||||
cd deployment/cicd/kustomize/overlays/${environment}
|
|
||||||
|
|
||||||
# 서비스 목록 정의 (공백으로 구분)
|
|
||||||
services="api-gateway user-service bill-service product-service kos-mock"
|
|
||||||
|
|
||||||
# 이미지 태그 업데이트
|
|
||||||
for service in \$services; do
|
|
||||||
\$HOME/bin/kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/\$service:${id}-${imageTag}
|
|
||||||
done
|
|
||||||
|
|
||||||
# 매니페스트 적용
|
|
||||||
kubectl apply -k .
|
|
||||||
|
|
||||||
# 배포 상태 확인
|
|
||||||
echo "Waiting for deployments to be ready..."
|
|
||||||
for service in \$services; do
|
|
||||||
kubectl -n phonebill-${id} wait --for=condition=available deployment/\$service --timeout=300s
|
|
||||||
done
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 파이프라인 완료 로그 (Scripted Pipeline 방식)
|
|
||||||
stage('Pipeline Complete') {
|
|
||||||
echo "🧹 Pipeline completed. Pod cleanup handled by Jenkins Kubernetes Plugin."
|
|
||||||
|
|
||||||
// 성공/실패 여부 로깅
|
|
||||||
if (currentBuild.result == null || currentBuild.result == 'SUCCESS') {
|
|
||||||
echo "✅ Pipeline completed successfully!"
|
|
||||||
} else {
|
|
||||||
echo "❌ Pipeline failed with result: ${currentBuild.result}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
currentBuild.result = 'FAILURE'
|
|
||||||
echo "❌ Pipeline failed with exception: ${e.getMessage()}"
|
|
||||||
throw e
|
|
||||||
} finally {
|
|
||||||
echo "🧹 Cleaning up resources and preparing for pod termination..."
|
|
||||||
echo "Pod will be terminated in 3 seconds due to terminationGracePeriodSeconds: 3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,229 +0,0 @@
|
|||||||
def PIPELINE_ID = "${env.BUILD_NUMBER}"
|
|
||||||
|
|
||||||
def getImageTag() {
|
|
||||||
def dateFormat = new java.text.SimpleDateFormat('yyyyMMddHHmmss')
|
|
||||||
def currentDate = new Date()
|
|
||||||
return dateFormat.format(currentDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
podTemplate(
|
|
||||||
label: "${PIPELINE_ID}",
|
|
||||||
serviceAccount: 'jenkins',
|
|
||||||
slaveConnectTimeout: 300,
|
|
||||||
idleMinutes: 1,
|
|
||||||
activeDeadlineSeconds: 3600,
|
|
||||||
podRetention: never(), // 파드 자동 정리 옵션: never(), onFailure(), always(), default()
|
|
||||||
yaml: '''
|
|
||||||
spec:
|
|
||||||
terminationGracePeriodSeconds: 3
|
|
||||||
restartPolicy: Never
|
|
||||||
tolerations:
|
|
||||||
- effect: NoSchedule
|
|
||||||
key: dedicated
|
|
||||||
operator: Equal
|
|
||||||
value: cicd
|
|
||||||
''',
|
|
||||||
containers: [
|
|
||||||
containerTemplate(
|
|
||||||
name: 'podman',
|
|
||||||
image: "mgoltzsche/podman",
|
|
||||||
ttyEnabled: true,
|
|
||||||
command: 'cat',
|
|
||||||
privileged: true,
|
|
||||||
resourceRequestCpu: '500m',
|
|
||||||
resourceRequestMemory: '2Gi',
|
|
||||||
resourceLimitCpu: '2000m',
|
|
||||||
resourceLimitMemory: '4Gi'
|
|
||||||
),
|
|
||||||
containerTemplate(
|
|
||||||
name: 'gradle',
|
|
||||||
image: 'gradle:jdk21',
|
|
||||||
ttyEnabled: true,
|
|
||||||
command: 'cat',
|
|
||||||
resourceRequestCpu: '500m',
|
|
||||||
resourceRequestMemory: '1Gi',
|
|
||||||
resourceLimitCpu: '1000m',
|
|
||||||
resourceLimitMemory: '2Gi',
|
|
||||||
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,
|
|
||||||
resourceRequestCpu: '200m',
|
|
||||||
resourceRequestMemory: '512Mi',
|
|
||||||
resourceLimitCpu: '500m',
|
|
||||||
resourceLimitMemory: '1Gi'
|
|
||||||
),
|
|
||||||
containerTemplate(
|
|
||||||
name: 'git',
|
|
||||||
image: 'alpine/git:latest',
|
|
||||||
command: 'cat',
|
|
||||||
ttyEnabled: true,
|
|
||||||
resourceRequestCpu: '100m',
|
|
||||||
resourceRequestMemory: '256Mi',
|
|
||||||
resourceLimitCpu: '300m',
|
|
||||||
resourceLimitMemory: '512Mi'
|
|
||||||
)
|
|
||||||
],
|
|
||||||
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 environment = params.ENVIRONMENT ?: 'dev'
|
|
||||||
def skipSonarQube = (params.SKIP_SONARQUBE?.toLowerCase() == 'true')
|
|
||||||
def services = ['api-gateway', 'user-service', 'bill-service', 'product-service', 'kos-mock']
|
|
||||||
|
|
||||||
try {
|
|
||||||
stage("Get Source") {
|
|
||||||
checkout scm
|
|
||||||
props = readProperties file: "deployment/cicd/config/deploy_env_vars_${environment}"
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('Build') {
|
|
||||||
container('gradle') {
|
|
||||||
sh """
|
|
||||||
chmod +x gradlew
|
|
||||||
./gradlew build -x test
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('SonarQube Analysis & Quality Gate') {
|
|
||||||
if (skipSonarQube) {
|
|
||||||
echo "⏭️ Skipping SonarQube Analysis (SKIP_SONARQUBE=${params.SKIP_SONARQUBE})"
|
|
||||||
} else {
|
|
||||||
container('gradle') {
|
|
||||||
withSonarQubeEnv('SonarQube') {
|
|
||||||
// 각 서비스별 테스트 및 SonarQube 분석
|
|
||||||
services.each { service ->
|
|
||||||
sh """
|
|
||||||
./gradlew :${service}:test :${service}:jacocoTestReport :${service}:sonar \\
|
|
||||||
-Dsonar.projectKey=phonebill-${service}-${environment} \\
|
|
||||||
-Dsonar.projectName=phonebill-${service}-${environment} \\
|
|
||||||
-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/**
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quality Gate 확인
|
|
||||||
timeout(time: 10, unit: 'MINUTES') {
|
|
||||||
def qg = waitForQualityGate()
|
|
||||||
if (qg.status != 'OK') {
|
|
||||||
error "Pipeline aborted due to quality gate failure: ${qg.status}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('Build & Push Images') {
|
|
||||||
timeout(time: 30, unit: 'MINUTES') {
|
|
||||||
container('podman') {
|
|
||||||
withCredentials([
|
|
||||||
usernamePassword(
|
|
||||||
credentialsId: 'acr-credentials',
|
|
||||||
usernameVariable: 'ACR_USERNAME',
|
|
||||||
passwordVariable: 'ACR_PASSWORD'
|
|
||||||
),
|
|
||||||
usernamePassword(
|
|
||||||
credentialsId: 'dockerhub-credentials',
|
|
||||||
usernameVariable: 'DOCKERHUB_USERNAME',
|
|
||||||
passwordVariable: 'DOCKERHUB_PASSWORD'
|
|
||||||
)
|
|
||||||
]) {
|
|
||||||
// Docker Hub 로그인 (rate limit 해결)
|
|
||||||
sh "podman login docker.io --username \$DOCKERHUB_USERNAME --password \$DOCKERHUB_PASSWORD"
|
|
||||||
|
|
||||||
// ACR 로그인
|
|
||||||
sh "podman login acrdigitalgarage01.azurecr.io --username \$ACR_USERNAME --password \$ACR_PASSWORD"
|
|
||||||
|
|
||||||
services.each { service ->
|
|
||||||
sh """
|
|
||||||
podman build \\
|
|
||||||
--build-arg BUILD_LIB_DIR="${service}/build/libs" \\
|
|
||||||
--build-arg ARTIFACTORY_FILE="${service}.jar" \\
|
|
||||||
-f deployment/container/Dockerfile-backend \\
|
|
||||||
-t acrdigitalgarage01.azurecr.io/phonebill/${service}:${environment}-${imageTag} .
|
|
||||||
|
|
||||||
podman push acrdigitalgarage01.azurecr.io/phonebill/${service}:${environment}-${imageTag}
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('Update Manifest Repository') {
|
|
||||||
container('git') {
|
|
||||||
withCredentials([usernamePassword(
|
|
||||||
credentialsId: 'github-credentials-dg0500',
|
|
||||||
usernameVariable: 'GIT_USERNAME',
|
|
||||||
passwordVariable: 'GIT_TOKEN'
|
|
||||||
)]) {
|
|
||||||
sh """
|
|
||||||
# 매니페스트 레포지토리 클론
|
|
||||||
REPO_URL=\$(echo "https://github.com/cna-bootcamp/phonebill-manifest.git" | sed 's|https://||')
|
|
||||||
git clone https://\${GIT_USERNAME}:\${GIT_TOKEN}@\${REPO_URL} manifest-repo
|
|
||||||
cd manifest-repo
|
|
||||||
|
|
||||||
# 각 서비스별 이미지 태그 업데이트 (sed 명령 사용)
|
|
||||||
services="api-gateway user-service bill-service product-service kos-mock"
|
|
||||||
for service in \$services; do
|
|
||||||
echo "Updating \$service image tag..."
|
|
||||||
sed -i "s|image: acrdigitalgarage01.azurecr.io/phonebill/\$service:.*|image: acrdigitalgarage01.azurecr.io/phonebill/\$service:${environment}-${imageTag}|g" \\
|
|
||||||
phonebill/kustomize/base/\$service/deployment.yaml
|
|
||||||
|
|
||||||
# 변경 사항 확인
|
|
||||||
echo "Updated \$service deployment.yaml:"
|
|
||||||
grep "image: acrdigitalgarage01.azurecr.io/phonebill/\$service" phonebill/kustomize/base/\$service/deployment.yaml
|
|
||||||
done
|
|
||||||
|
|
||||||
# Git 설정 및 푸시
|
|
||||||
git config user.name "Jenkins CI"
|
|
||||||
git config user.email "jenkins@example.com"
|
|
||||||
git add .
|
|
||||||
git commit -m "🚀 Update phonebill ${environment} images to ${environment}-${imageTag}"
|
|
||||||
git push origin main
|
|
||||||
|
|
||||||
echo "✅ 매니페스트 업데이트 완료. ArgoCD가 자동으로 배포합니다."
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 파이프라인 완료 로그 (Scripted Pipeline 방식)
|
|
||||||
stage('Pipeline Complete') {
|
|
||||||
echo "🧹 Pipeline completed. Pod cleanup handled by Jenkins Kubernetes Plugin."
|
|
||||||
|
|
||||||
// 성공/실패 여부 로깅
|
|
||||||
if (currentBuild.result == null || currentBuild.result == 'SUCCESS') {
|
|
||||||
echo "✅ Pipeline completed successfully!"
|
|
||||||
echo "✅ 매니페스트가 업데이트되었습니다. ArgoCD에서 배포를 확인하세요."
|
|
||||||
} else {
|
|
||||||
echo "❌ Pipeline failed with result: ${currentBuild.result}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
currentBuild.result = 'FAILURE'
|
|
||||||
echo "❌ Pipeline failed with exception: ${e.getMessage()}"
|
|
||||||
throw e
|
|
||||||
} finally {
|
|
||||||
echo "🧹 Cleaning up resources and preparing for pod termination..."
|
|
||||||
echo "Pod will be terminated in 3 seconds due to terminationGracePeriodSeconds: 3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# dev Environment Configuration
|
|
||||||
resource_group=rg-digitalgarage-01
|
|
||||||
cluster_name=aks-digitalgarage-01
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# prod Environment Configuration
|
|
||||||
resource_group=rg-digitalgarage-01
|
|
||||||
cluster_name=aks-digitalgarage-01
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# staging Environment Configuration
|
|
||||||
resource_group=rg-digitalgarage-01
|
|
||||||
cluster_name=aks-digitalgarage-01
|
|
||||||
@ -1,350 +0,0 @@
|
|||||||
# Jenkins CI/CD 파이프라인 구축 가이드
|
|
||||||
|
|
||||||
**최운영/데옵스**가 작성한 통신요금 관리 서비스 Jenkins CI/CD 파이프라인 구축 가이드입니다.
|
|
||||||
|
|
||||||
## 📋 프로젝트 정보
|
|
||||||
|
|
||||||
### 시스템 정보
|
|
||||||
- **시스템명**: phonebill
|
|
||||||
- **서비스**: api-gateway, user-service, bill-service, product-service, kos-mock
|
|
||||||
- **JDK 버전**: 21
|
|
||||||
- **Container Registry**: acrdigitalgarage01.azurecr.io
|
|
||||||
- **Resource Group**: rg-digitalgarage-01
|
|
||||||
- **AKS Cluster**: aks-digitalgarage-01
|
|
||||||
|
|
||||||
## 🏗️ 아키텍처 개요
|
|
||||||
|
|
||||||
본 CI/CD 파이프라인은 다음 구성 요소들로 이루어져 있습니다:
|
|
||||||
|
|
||||||
- **Jenkins**: 파이프라인 오케스트레이션
|
|
||||||
- **Kustomize**: 환경별 Kubernetes 매니페스트 관리
|
|
||||||
- **SonarQube**: 코드 품질 분석 및 Quality Gate
|
|
||||||
- **Azure Container Registry (ACR)**: 컨테이너 이미지 저장소
|
|
||||||
- **Azure Kubernetes Service (AKS)**: 배포 대상 클러스터
|
|
||||||
|
|
||||||
## 🔧 사전 준비사항
|
|
||||||
|
|
||||||
### 1. Jenkins 서버 환경 구성
|
|
||||||
|
|
||||||
#### 필수 플러그인 설치
|
|
||||||
```
|
|
||||||
- Kubernetes
|
|
||||||
- Pipeline Utility Steps
|
|
||||||
- Docker Pipeline
|
|
||||||
- GitHub
|
|
||||||
- SonarQube Scanner
|
|
||||||
- Azure Credentials
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Jenkins Credentials 등록
|
|
||||||
|
|
||||||
**Azure Service Principal**
|
|
||||||
```
|
|
||||||
Manage Jenkins > Credentials > Add Credentials
|
|
||||||
- Kind: Microsoft Azure Service Principal
|
|
||||||
- ID: azure-credentials
|
|
||||||
- Subscription ID: {구독ID}
|
|
||||||
- Client ID: {클라이언트ID}
|
|
||||||
- Client Secret: {클라이언트시크릿}
|
|
||||||
- Tenant ID: {테넌트ID}
|
|
||||||
- Azure Environment: Azure
|
|
||||||
```
|
|
||||||
|
|
||||||
**ACR Credentials**
|
|
||||||
```
|
|
||||||
- Kind: Username with password
|
|
||||||
- ID: acr-credentials
|
|
||||||
- Username: acrdigitalgarage01
|
|
||||||
- Password: {ACR_PASSWORD}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Docker Hub Credentials** (Rate Limit 해결용)
|
|
||||||
```
|
|
||||||
- Kind: Username with password
|
|
||||||
- ID: dockerhub-credentials
|
|
||||||
- Username: {DOCKERHUB_USERNAME}
|
|
||||||
- Password: {DOCKERHUB_PASSWORD}
|
|
||||||
참고: Docker Hub 무료 계정 생성 (https://hub.docker.com)
|
|
||||||
```
|
|
||||||
|
|
||||||
**SonarQube Token**
|
|
||||||
```
|
|
||||||
- Kind: Secret text
|
|
||||||
- ID: sonarqube-token
|
|
||||||
- Secret: {SonarQube토큰}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. SonarQube 프로젝트 설정
|
|
||||||
|
|
||||||
각 서비스별 프로젝트 생성 및 Quality Gate 설정:
|
|
||||||
```
|
|
||||||
Coverage: >= 80%
|
|
||||||
Duplicated Lines: <= 3%
|
|
||||||
Maintainability Rating: <= A
|
|
||||||
Reliability Rating: <= A
|
|
||||||
Security Rating: <= A
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📁 디렉토리 구조
|
|
||||||
|
|
||||||
구축 완료된 디렉토리 구조:
|
|
||||||
```
|
|
||||||
deployment/cicd/
|
|
||||||
├── kustomize/
|
|
||||||
│ ├── base/
|
|
||||||
│ │ ├── common/
|
|
||||||
│ │ │ ├── cm-common.yaml
|
|
||||||
│ │ │ ├── secret-common.yaml
|
|
||||||
│ │ │ ├── secret-imagepull.yaml
|
|
||||||
│ │ │ └── ingress.yaml
|
|
||||||
│ │ ├── api-gateway/
|
|
||||||
│ │ ├── user-service/
|
|
||||||
│ │ ├── bill-service/
|
|
||||||
│ │ ├── product-service/
|
|
||||||
│ │ ├── kos-mock/
|
|
||||||
│ │ ├── namespace.yaml
|
|
||||||
│ │ └── kustomization.yaml
|
|
||||||
│ └── overlays/
|
|
||||||
│ ├── dev/
|
|
||||||
│ ├── staging/
|
|
||||||
│ └── prod/
|
|
||||||
├── config/
|
|
||||||
│ ├── deploy_env_vars_dev
|
|
||||||
│ ├── deploy_env_vars_staging
|
|
||||||
│ └── deploy_env_vars_prod
|
|
||||||
├── scripts/
|
|
||||||
│ ├── deploy.sh
|
|
||||||
│ └── validate-cicd-setup.sh
|
|
||||||
└── Jenkinsfile
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 파이프라인 단계
|
|
||||||
|
|
||||||
### 1. Get Source
|
|
||||||
- GitHub에서 소스코드 체크아웃
|
|
||||||
- 환경별 설정 파일 읽기
|
|
||||||
|
|
||||||
### 2. Setup AKS
|
|
||||||
- Azure CLI로 인증
|
|
||||||
- AKS 클러스터 연결
|
|
||||||
- 환경별 네임스페이스 생성
|
|
||||||
|
|
||||||
### 3. Build & SonarQube Analysis
|
|
||||||
- Gradle 빌드 (테스트 제외)
|
|
||||||
- 각 서비스별 단위 테스트 실행
|
|
||||||
- JaCoCo 커버리지 리포트 생성
|
|
||||||
- SonarQube 코드 품질 분석
|
|
||||||
|
|
||||||
### 4. Quality Gate
|
|
||||||
- SonarQube Quality Gate 대기 (10분 타임아웃)
|
|
||||||
- 품질 기준 미달 시 파이프라인 중단
|
|
||||||
|
|
||||||
### 5. Build & Push Images
|
|
||||||
- Podman을 사용한 컨테이너 이미지 빌드
|
|
||||||
- 환경별 이미지 태그로 ACR에 푸시
|
|
||||||
- 30분 타임아웃 설정
|
|
||||||
|
|
||||||
### 6. Update Kustomize & Deploy
|
|
||||||
- Kustomize를 사용한 이미지 태그 업데이트
|
|
||||||
- Kubernetes 매니페스트 적용
|
|
||||||
- 배포 상태 확인 (5분 타임아웃)
|
|
||||||
|
|
||||||
### 7. Pipeline Complete
|
|
||||||
- 성공/실패 로깅
|
|
||||||
- 자동 파드 정리
|
|
||||||
|
|
||||||
## 🔄 파이프라인 실행 방법
|
|
||||||
|
|
||||||
### Jenkins 파이프라인 Job 생성
|
|
||||||
|
|
||||||
1. Jenkins 웹 UI에서 **New Item > Pipeline** 선택
|
|
||||||
2. **Pipeline script from SCM** 설정:
|
|
||||||
```
|
|
||||||
SCM: Git
|
|
||||||
Repository URL: {Git저장소URL}
|
|
||||||
Branch: main
|
|
||||||
Script Path: deployment/cicd/Jenkinsfile
|
|
||||||
```
|
|
||||||
3. **Pipeline Parameters** 설정:
|
|
||||||
```
|
|
||||||
ENVIRONMENT: Choice Parameter (dev, staging, prod)
|
|
||||||
IMAGE_TAG: String Parameter (default: latest)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 배포 실행
|
|
||||||
|
|
||||||
1. Jenkins > {프로젝트명} > **Build with Parameters**
|
|
||||||
2. **ENVIRONMENT** 선택 (dev/staging/prod)
|
|
||||||
3. **IMAGE_TAG** 입력 (선택사항)
|
|
||||||
4. **Build** 클릭
|
|
||||||
|
|
||||||
## 📊 환경별 설정
|
|
||||||
|
|
||||||
### DEV 환경
|
|
||||||
- **네임스페이스**: phonebill-dev
|
|
||||||
- **Replicas**: 1
|
|
||||||
- **Resources**: 256m CPU/256Mi Memory → 1024m CPU/1024Mi Memory
|
|
||||||
- **Database**: DDL update 모드
|
|
||||||
- **Ingress**: HTTP, SSL 리다이렉션 비활성화
|
|
||||||
|
|
||||||
### STAGING 환경
|
|
||||||
- **네임스페이스**: phonebill-staging
|
|
||||||
- **Replicas**: 2
|
|
||||||
- **Resources**: 512m CPU/512Mi Memory → 2048m CPU/2048Mi Memory
|
|
||||||
- **Database**: DDL validate 모드
|
|
||||||
- **Ingress**: HTTPS, SSL 리다이렉션 활성화
|
|
||||||
|
|
||||||
### PROD 환경
|
|
||||||
- **네임스페이스**: phonebill-prod
|
|
||||||
- **Replicas**: 3
|
|
||||||
- **Resources**: 1024m CPU/1024Mi Memory → 4096m CPU/4096Mi Memory
|
|
||||||
- **Database**: DDL validate 모드, 짧은 JWT 토큰 (1시간)
|
|
||||||
- **Ingress**: HTTPS, SSL 리다이렉션 활성화, Let's Encrypt 인증서
|
|
||||||
|
|
||||||
## 🛠️ 수동 배포 방법
|
|
||||||
|
|
||||||
스크립트를 사용한 수동 배포:
|
|
||||||
```bash
|
|
||||||
# DEV 환경 배포
|
|
||||||
./deployment/cicd/scripts/deploy.sh dev latest
|
|
||||||
|
|
||||||
# STAGING 환경 배포
|
|
||||||
./deployment/cicd/scripts/deploy.sh staging 20241213151500
|
|
||||||
|
|
||||||
# PROD 환경 배포
|
|
||||||
./deployment/cicd/scripts/deploy.sh prod 20241213151500
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📋 배포 상태 확인
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 파드 상태 확인
|
|
||||||
kubectl get pods -n phonebill-{환경}
|
|
||||||
|
|
||||||
# 서비스 확인
|
|
||||||
kubectl get services -n phonebill-{환경}
|
|
||||||
|
|
||||||
# Ingress 확인
|
|
||||||
kubectl get ingress -n phonebill-{환경}
|
|
||||||
|
|
||||||
# 배포 히스토리 확인
|
|
||||||
kubectl rollout history deployment/{서비스명} -n phonebill-{환경}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔄 롤백 방법
|
|
||||||
|
|
||||||
### 이전 리비전으로 롤백
|
|
||||||
```bash
|
|
||||||
# 특정 버전으로 롤백
|
|
||||||
kubectl rollout undo deployment/{서비스명} -n phonebill-{환경} --to-revision=2
|
|
||||||
|
|
||||||
# 롤백 상태 확인
|
|
||||||
kubectl rollout status deployment/{서비스명} -n phonebill-{환경}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 이미지 태그 기반 롤백
|
|
||||||
```bash
|
|
||||||
# 이전 안정 버전 이미지 태그로 업데이트
|
|
||||||
cd deployment/cicd/kustomize/overlays/{환경}
|
|
||||||
kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/{서비스명}:{환경}-{이전태그}
|
|
||||||
kubectl apply -k .
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔍 트러블슈팅
|
|
||||||
|
|
||||||
### 일반적인 문제 해결
|
|
||||||
|
|
||||||
**1. SonarQube Quality Gate 실패**
|
|
||||||
- 코드 커버리지 확인 (80% 이상)
|
|
||||||
- 코드 중복도 확인 (3% 이하)
|
|
||||||
- 보안/신뢰성 등급 확인 (A등급)
|
|
||||||
|
|
||||||
**2. 컨테이너 이미지 빌드 실패**
|
|
||||||
- Dockerfile 경로 확인: `deployment/container/Dockerfile-backend`
|
|
||||||
- JAR 파일 경로 확인: `{서비스명}/build/libs/{서비스명}.jar`
|
|
||||||
- ACR 인증 상태 확인
|
|
||||||
|
|
||||||
**3. 배포 실패**
|
|
||||||
- Kubernetes 매니페스트 문법 확인
|
|
||||||
- 네임스페이스 존재 확인
|
|
||||||
- 리소스 할당량 확인
|
|
||||||
|
|
||||||
**4. 파드 시작 실패**
|
|
||||||
- 환경변수 설정 확인
|
|
||||||
- Secret/ConfigMap 존재 확인
|
|
||||||
- 이미지 태그 정확성 확인
|
|
||||||
|
|
||||||
### 검증 스크립트
|
|
||||||
|
|
||||||
리소스 누락 검증:
|
|
||||||
```bash
|
|
||||||
./deployment/cicd/scripts/validate-cicd-setup.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔐 보안 고려사항
|
|
||||||
|
|
||||||
### Jenkins 보안
|
|
||||||
- **Service Account**: jenkins 전용 계정 사용
|
|
||||||
- **Pod Security**: 최소 권한 원칙 적용
|
|
||||||
- **Credential 관리**: Jenkins Credential Store 사용
|
|
||||||
|
|
||||||
### 컨테이너 보안
|
|
||||||
- **Base Image**: 공식 이미지 사용
|
|
||||||
- **Image Scanning**: ACR 취약점 스캔 활용
|
|
||||||
- **Secrets 관리**: Kubernetes Secret으로 관리
|
|
||||||
|
|
||||||
### 네트워크 보안
|
|
||||||
- **TLS**: HTTPS 강제 적용 (Staging/Prod)
|
|
||||||
- **Network Policy**: 네임스페이스 격리
|
|
||||||
- **Ingress**: 인증서 자동 갱신
|
|
||||||
|
|
||||||
## 📈 성능 최적화
|
|
||||||
|
|
||||||
### 빌드 최적화
|
|
||||||
- **Gradle Daemon**: 빌드 속도 향상
|
|
||||||
- **Docker Layer Caching**: 이미지 빌드 최적화
|
|
||||||
- **Parallel Build**: 병렬 빌드 활용
|
|
||||||
|
|
||||||
### 배포 최적화
|
|
||||||
- **Rolling Update**: 무중단 배포
|
|
||||||
- **Health Check**: 정확한 상태 확인
|
|
||||||
- **Resource Limit**: 적절한 리소스 할당
|
|
||||||
|
|
||||||
## 🔧 유지보수 가이드
|
|
||||||
|
|
||||||
### 정기 점검 항목
|
|
||||||
- [ ] Jenkins 플러그인 업데이트
|
|
||||||
- [ ] SonarQube 룰 세트 검토
|
|
||||||
- [ ] ACR 이미지 정리
|
|
||||||
- [ ] 인증서 만료일 확인
|
|
||||||
|
|
||||||
### 모니터링 권장사항
|
|
||||||
- 빌드 실패율 모니터링
|
|
||||||
- 배포 소요시간 추적
|
|
||||||
- Quality Gate 통과율 확인
|
|
||||||
- 리소스 사용률 모니터링
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ 체크리스트
|
|
||||||
|
|
||||||
### 사전 준비 완료
|
|
||||||
- [x] Jenkins 필수 플러그인 설치
|
|
||||||
- [x] Jenkins Credentials 등록
|
|
||||||
- [x] SonarQube 프로젝트 설정
|
|
||||||
- [x] ACR 접근 권한 설정
|
|
||||||
- [x] AKS 클러스터 연결 설정
|
|
||||||
|
|
||||||
### Kustomize 구성 완료
|
|
||||||
- [x] Base 매니페스트 생성
|
|
||||||
- [x] 환경별 Overlay 생성
|
|
||||||
- [x] Patch 파일 작성
|
|
||||||
- [x] 매니페스트 검증 완료
|
|
||||||
|
|
||||||
### 파이프라인 구성 완료
|
|
||||||
- [x] Jenkinsfile 작성
|
|
||||||
- [x] 환경별 설정 파일 생성
|
|
||||||
- [x] 배포 스크립트 작성
|
|
||||||
- [x] 검증 스크립트 작성
|
|
||||||
|
|
||||||
**🎯 모든 구성이 완료되어 Jenkins CI/CD 파이프라인을 실행할 준비가 완료되었습니다!**
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: cm-api-gateway
|
|
||||||
|
|
||||||
data:
|
|
||||||
SERVER_PORT: "8080"
|
|
||||||
BILL_SERVICE_URL: "http://bill-service"
|
|
||||||
PRODUCT_SERVICE_URL: "http://product-service"
|
|
||||||
USER_SERVICE_URL: "http://user-service"
|
|
||||||
KOS_MOCK_URL: "http://kos-mock"
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: api-gateway
|
|
||||||
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: api-gateway
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: api-gateway
|
|
||||||
spec:
|
|
||||||
imagePullSecrets:
|
|
||||||
- name: phonebill
|
|
||||||
containers:
|
|
||||||
- name: api-gateway
|
|
||||||
image: acrdigitalgarage01.azurecr.io/phonebill/api-gateway:latest
|
|
||||||
imagePullPolicy: Always
|
|
||||||
ports:
|
|
||||||
- containerPort: 8080
|
|
||||||
envFrom:
|
|
||||||
- configMapRef:
|
|
||||||
name: cm-common
|
|
||||||
- configMapRef:
|
|
||||||
name: cm-api-gateway
|
|
||||||
- secretRef:
|
|
||||||
name: secret-common
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 256m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1024m
|
|
||||||
memory: 1024Mi
|
|
||||||
startupProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 6
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 5
|
|
||||||
failureThreshold: 3
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 3
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: api-gateway
|
|
||||||
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: api-gateway
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
targetPort: 8080
|
|
||||||
type: ClusterIP
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: cm-bill-service
|
|
||||||
|
|
||||||
data:
|
|
||||||
SERVER_PORT: "8082"
|
|
||||||
DB_KIND: "postgresql"
|
|
||||||
DB_PORT: "5432"
|
|
||||||
DB_CONNECTION_TIMEOUT: "30000"
|
|
||||||
DB_IDLE_TIMEOUT: "600000"
|
|
||||||
DB_LEAK_DETECTION: "60000"
|
|
||||||
DB_MAX_LIFETIME: "1800000"
|
|
||||||
DB_MAX_POOL: "20"
|
|
||||||
DB_MIN_IDLE: "5"
|
|
||||||
KOS_BASE_URL: "http://kos-mock"
|
|
||||||
REDIS_DATABASE: "1"
|
|
||||||
REDIS_MAX_ACTIVE: "8"
|
|
||||||
REDIS_MAX_IDLE: "8"
|
|
||||||
REDIS_MAX_WAIT: "-1"
|
|
||||||
REDIS_MIN_IDLE: "0"
|
|
||||||
REDIS_TIMEOUT: "2000"
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: bill-service
|
|
||||||
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: bill-service
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: bill-service
|
|
||||||
spec:
|
|
||||||
imagePullSecrets:
|
|
||||||
- name: phonebill
|
|
||||||
containers:
|
|
||||||
- name: bill-service
|
|
||||||
image: acrdigitalgarage01.azurecr.io/phonebill/bill-service:latest
|
|
||||||
imagePullPolicy: Always
|
|
||||||
ports:
|
|
||||||
- containerPort: 8082
|
|
||||||
envFrom:
|
|
||||||
- configMapRef:
|
|
||||||
name: cm-common
|
|
||||||
- configMapRef:
|
|
||||||
name: cm-bill-service
|
|
||||||
- secretRef:
|
|
||||||
name: secret-common
|
|
||||||
- secretRef:
|
|
||||||
name: secret-bill-service
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 256m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1024m
|
|
||||||
memory: 1024Mi
|
|
||||||
startupProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health
|
|
||||||
port: 8082
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 6
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health/readiness
|
|
||||||
port: 8082
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 5
|
|
||||||
failureThreshold: 3
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health/liveness
|
|
||||||
port: 8082
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 3
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: secret-bill-service
|
|
||||||
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
|
||||||
DB_NAME: "bill_inquiry_db"
|
|
||||||
DB_USERNAME: "bill_inquiry_user"
|
|
||||||
DB_PASSWORD: "BillUser2025!"
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: bill-service
|
|
||||||
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: bill-service
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
targetPort: 8082
|
|
||||||
type: ClusterIP
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: cm-common
|
|
||||||
|
|
||||||
data:
|
|
||||||
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill.20.214.196.128.nip.io"
|
|
||||||
JWT_ACCESS_TOKEN_VALIDITY: "18000000"
|
|
||||||
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
|
||||||
REDIS_PORT: "6379"
|
|
||||||
SPRING_PROFILES_ACTIVE: "dev"
|
|
||||||
DDL_AUTO: "update"
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: phonebill
|
|
||||||
|
|
||||||
annotations:
|
|
||||||
kubernetes.io/ingress.class: nginx
|
|
||||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
|
||||||
spec:
|
|
||||||
ingressClassName: nginx
|
|
||||||
rules:
|
|
||||||
- host: phonebill-api.20.214.196.128.nip.io
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /api/v1/auth
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: user-service
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
- path: /api/v1/users
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: user-service
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
- path: /api/v1/bills
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: bill-service
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
- path: /api/v1/products
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: product-service
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
- path: /api/v1/kos
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: kos-mock
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: secret-common
|
|
||||||
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
|
||||||
REDIS_HOST: "redis-cache-dev-master"
|
|
||||||
REDIS_PASSWORD: "Redis2025Dev!"
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: phonebill
|
|
||||||
|
|
||||||
type: kubernetes.io/dockerconfigjson
|
|
||||||
stringData:
|
|
||||||
.dockerconfigjson: |
|
|
||||||
{
|
|
||||||
"auths": {
|
|
||||||
"acrdigitalgarage01.azurecr.io": {
|
|
||||||
"username": "acrdigitalgarage01",
|
|
||||||
"password": "+OY+rmOagorjWvQe/tTk6oqvnZI8SmNbY/Y2o5EDcY+ACRDCDbYk",
|
|
||||||
"auth": "YWNyZGlnaXRhbGdhcmFnZTAxOitPWStybU9hZ29yald2UWUvdFRrNm9xdm5aSThTbU5iWS9ZMm81RURjWStBQ1JEQ0RiWWs="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: cm-kos-mock
|
|
||||||
|
|
||||||
data:
|
|
||||||
SERVER_PORT: "8084"
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: kos-mock
|
|
||||||
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: kos-mock
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: kos-mock
|
|
||||||
spec:
|
|
||||||
imagePullSecrets:
|
|
||||||
- name: phonebill
|
|
||||||
containers:
|
|
||||||
- name: kos-mock
|
|
||||||
image: acrdigitalgarage01.azurecr.io/phonebill/kos-mock:latest
|
|
||||||
imagePullPolicy: Always
|
|
||||||
ports:
|
|
||||||
- containerPort: 8084
|
|
||||||
envFrom:
|
|
||||||
- configMapRef:
|
|
||||||
name: cm-common
|
|
||||||
- configMapRef:
|
|
||||||
name: cm-kos-mock
|
|
||||||
- secretRef:
|
|
||||||
name: secret-common
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 256m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1024m
|
|
||||||
memory: 1024Mi
|
|
||||||
startupProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health
|
|
||||||
port: 8084
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 6
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health/readiness
|
|
||||||
port: 8084
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 5
|
|
||||||
failureThreshold: 3
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health/liveness
|
|
||||||
port: 8084
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 3
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: kos-mock
|
|
||||||
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: kos-mock
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
targetPort: 8084
|
|
||||||
type: ClusterIP
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
|
|
||||||
metadata:
|
|
||||||
name: phonebill-base
|
|
||||||
|
|
||||||
resources:
|
|
||||||
|
|
||||||
# Common resources
|
|
||||||
- common/cm-common.yaml
|
|
||||||
- common/secret-common.yaml
|
|
||||||
- common/secret-imagepull.yaml
|
|
||||||
- common/ingress.yaml
|
|
||||||
|
|
||||||
# api-gateway
|
|
||||||
- api-gateway/deployment.yaml
|
|
||||||
- api-gateway/service.yaml
|
|
||||||
- api-gateway/cm-api-gateway.yaml
|
|
||||||
|
|
||||||
# user-service
|
|
||||||
- user-service/deployment.yaml
|
|
||||||
- user-service/service.yaml
|
|
||||||
- user-service/cm-user-service.yaml
|
|
||||||
- user-service/secret-user-service.yaml
|
|
||||||
|
|
||||||
# bill-service
|
|
||||||
- bill-service/deployment.yaml
|
|
||||||
- bill-service/service.yaml
|
|
||||||
- bill-service/cm-bill-service.yaml
|
|
||||||
- bill-service/secret-bill-service.yaml
|
|
||||||
|
|
||||||
# product-service
|
|
||||||
- product-service/deployment.yaml
|
|
||||||
- product-service/service.yaml
|
|
||||||
- product-service/cm-product-service.yaml
|
|
||||||
- product-service/secret-product-service.yaml
|
|
||||||
|
|
||||||
# kos-mock
|
|
||||||
- kos-mock/deployment.yaml
|
|
||||||
- kos-mock/service.yaml
|
|
||||||
- kos-mock/cm-kos-mock.yaml
|
|
||||||
|
|
||||||
images:
|
|
||||||
- name: acrdigitalgarage01.azurecr.io/phonebill/api-gateway
|
|
||||||
newTag: latest
|
|
||||||
- name: acrdigitalgarage01.azurecr.io/phonebill/user-service
|
|
||||||
newTag: latest
|
|
||||||
- name: acrdigitalgarage01.azurecr.io/phonebill/bill-service
|
|
||||||
newTag: latest
|
|
||||||
- name: acrdigitalgarage01.azurecr.io/phonebill/product-service
|
|
||||||
newTag: latest
|
|
||||||
- name: acrdigitalgarage01.azurecr.io/phonebill/kos-mock
|
|
||||||
newTag: latest
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: cm-product-service
|
|
||||||
|
|
||||||
data:
|
|
||||||
SERVER_PORT: "8083"
|
|
||||||
DB_KIND: "postgresql"
|
|
||||||
DB_PORT: "5432"
|
|
||||||
KOS_BASE_URL: "http://kos-mock"
|
|
||||||
REDIS_DATABASE: "2"
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: product-service
|
|
||||||
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: product-service
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: product-service
|
|
||||||
spec:
|
|
||||||
imagePullSecrets:
|
|
||||||
- name: phonebill
|
|
||||||
containers:
|
|
||||||
- name: product-service
|
|
||||||
image: acrdigitalgarage01.azurecr.io/phonebill/product-service:latest
|
|
||||||
imagePullPolicy: Always
|
|
||||||
ports:
|
|
||||||
- containerPort: 8083
|
|
||||||
envFrom:
|
|
||||||
- configMapRef:
|
|
||||||
name: cm-common
|
|
||||||
- configMapRef:
|
|
||||||
name: cm-product-service
|
|
||||||
- secretRef:
|
|
||||||
name: secret-common
|
|
||||||
- secretRef:
|
|
||||||
name: secret-product-service
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 256m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1024m
|
|
||||||
memory: 1024Mi
|
|
||||||
startupProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health
|
|
||||||
port: 8083
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 6
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health/readiness
|
|
||||||
port: 8083
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 5
|
|
||||||
failureThreshold: 3
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health/liveness
|
|
||||||
port: 8083
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 3
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: secret-product-service
|
|
||||||
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
DB_HOST: "product-change-postgres-dev-postgresql"
|
|
||||||
DB_NAME: "product_change_db"
|
|
||||||
DB_USERNAME: "product_change_user"
|
|
||||||
DB_PASSWORD: "ProductUser2025!"
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: product-service
|
|
||||||
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: product-service
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
targetPort: 8083
|
|
||||||
type: ClusterIP
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: cm-user-service
|
|
||||||
|
|
||||||
data:
|
|
||||||
SERVER_PORT: "8081"
|
|
||||||
DB_KIND: "postgresql"
|
|
||||||
DB_PORT: "5432"
|
|
||||||
DDL_AUTO: "update"
|
|
||||||
REDIS_DATABASE: "0"
|
|
||||||
SHOW_SQL: "true"
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: user-service
|
|
||||||
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: user-service
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: user-service
|
|
||||||
spec:
|
|
||||||
imagePullSecrets:
|
|
||||||
- name: phonebill
|
|
||||||
containers:
|
|
||||||
- name: user-service
|
|
||||||
image: acrdigitalgarage01.azurecr.io/phonebill/user-service:latest
|
|
||||||
imagePullPolicy: Always
|
|
||||||
ports:
|
|
||||||
- containerPort: 8081
|
|
||||||
envFrom:
|
|
||||||
- configMapRef:
|
|
||||||
name: cm-common
|
|
||||||
- configMapRef:
|
|
||||||
name: cm-user-service
|
|
||||||
- secretRef:
|
|
||||||
name: secret-common
|
|
||||||
- secretRef:
|
|
||||||
name: secret-user-service
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 256m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1024m
|
|
||||||
memory: 1024Mi
|
|
||||||
startupProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health
|
|
||||||
port: 8081
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 6
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health/readiness
|
|
||||||
port: 8081
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 5
|
|
||||||
failureThreshold: 3
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /actuator/health/liveness
|
|
||||||
port: 8081
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 3
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: secret-user-service
|
|
||||||
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
DB_HOST: "auth-postgres-dev-postgresql"
|
|
||||||
DB_NAME: "phonebill_auth"
|
|
||||||
DB_USERNAME: "auth_user"
|
|
||||||
DB_PASSWORD: "AuthUser2025!"
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: user-service
|
|
||||||
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: user-service
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
targetPort: 8081
|
|
||||||
type: ClusterIP
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: cm-common
|
|
||||||
|
|
||||||
data:
|
|
||||||
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill-dg0500.20.214.196.128.nip.io"
|
|
||||||
JWT_ACCESS_TOKEN_VALIDITY: "18000000"
|
|
||||||
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
|
||||||
REDIS_PORT: "6379"
|
|
||||||
SPRING_PROFILES_ACTIVE: "dev"
|
|
||||||
DDL_AUTO: "update"
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: api-gateway
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: api-gateway
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 256m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1024m
|
|
||||||
memory: 1024Mi
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: bill-service
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: bill-service
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 256m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1024m
|
|
||||||
memory: 1024Mi
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: kos-mock
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: kos-mock
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 256m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1024m
|
|
||||||
memory: 1024Mi
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: product-service
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: product-service
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 256m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1024m
|
|
||||||
memory: 1024Mi
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: user-service
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: user-service
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 256m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1024m
|
|
||||||
memory: 1024Mi
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: phonebill-ingress
|
|
||||||
annotations:
|
|
||||||
kubernetes.io/ingress.class: nginx
|
|
||||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
|
||||||
spec:
|
|
||||||
ingressClassName: nginx
|
|
||||||
rules:
|
|
||||||
- host: phonebill-dg0500-api.20.214.196.128.nip.io
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /api/v1/auth
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: user-service
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
- path: /api/v1/users
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: user-service
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
- path: /api/v1/bills
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: bill-service
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
- path: /api/v1/products
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: product-service
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
- path: /api/v1/kos
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: kos-mock
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
|
|
||||||
namespace: phonebill-dg0500
|
|
||||||
|
|
||||||
resources:
|
|
||||||
- ../../base
|
|
||||||
|
|
||||||
patches:
|
|
||||||
- path: cm-common-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: ConfigMap
|
|
||||||
name: cm-common
|
|
||||||
- path: deployment-api-gateway-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Deployment
|
|
||||||
name: api-gateway
|
|
||||||
- path: deployment-user-service-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Deployment
|
|
||||||
name: user-service
|
|
||||||
- path: deployment-bill-service-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Deployment
|
|
||||||
name: bill-service
|
|
||||||
- path: deployment-product-service-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Deployment
|
|
||||||
name: product-service
|
|
||||||
- path: deployment-kos-mock-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Deployment
|
|
||||||
name: kos-mock
|
|
||||||
- path: ingress-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Ingress
|
|
||||||
name: phonebill
|
|
||||||
- path: secret-common-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Secret
|
|
||||||
name: secret-common
|
|
||||||
- path: secret-user-service-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Secret
|
|
||||||
name: secret-user-service
|
|
||||||
- path: secret-bill-service-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Secret
|
|
||||||
name: secret-bill-service
|
|
||||||
- path: secret-product-service-patch.yaml
|
|
||||||
target:
|
|
||||||
kind: Secret
|
|
||||||
name: secret-product-service
|
|
||||||
|
|
||||||
images:
|
|
||||||
- name: acrdigitalgarage01.azurecr.io/phonebill/api-gateway
|
|
||||||
newTag: dev-latest
|
|
||||||
- name: acrdigitalgarage01.azurecr.io/phonebill/user-service
|
|
||||||
newTag: dev-latest
|
|
||||||
- name: acrdigitalgarage01.azurecr.io/phonebill/bill-service
|
|
||||||
newTag: dev-latest
|
|
||||||
- name: acrdigitalgarage01.azurecr.io/phonebill/product-service
|
|
||||||
newTag: dev-latest
|
|
||||||
- name: acrdigitalgarage01.azurecr.io/phonebill/kos-mock
|
|
||||||
newTag: dev-latest
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: secret-bill-service
|
|
||||||
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
DB_HOST: "bill-inquiry-postgres-dev-postgresql"
|
|
||||||
DB_NAME: "bill_inquiry_db"
|
|
||||||
DB_USERNAME: "bill_inquiry_user"
|
|
||||||
DB_PASSWORD: "BillUser2025!"
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: secret-common
|
|
||||||
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ=="
|
|
||||||
REDIS_HOST: "redis-cache-dev-master"
|
|
||||||
REDIS_PASSWORD: "Redis2025Dev!"
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: secret-product-service
|
|
||||||
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
DB_HOST: "product-change-postgres-dev-postgresql"
|
|
||||||
DB_NAME: "product_change_db"
|
|
||||||
DB_USERNAME: "product_change_user"
|
|
||||||
DB_PASSWORD: "ProductUser2025!"
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: secret-user-service
|
|
||||||
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
DB_HOST: "auth-postgres-dev-postgresql"
|
|
||||||
DB_NAME: "phonebill_auth"
|
|
||||||
DB_USERNAME: "auth_user"
|
|
||||||
DB_PASSWORD: "AuthUser2025!"
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: cm-common
|
|
||||||
|
|
||||||
data:
|
|
||||||
CORS_ALLOWED_ORIGINS: "https://phonebill.example.com"
|
|
||||||
JWT_ACCESS_TOKEN_VALIDITY: "3600000"
|
|
||||||
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
|
|
||||||
REDIS_PORT: "6379"
|
|
||||||
SPRING_PROFILES_ACTIVE: "prod"
|
|
||||||
DDL_AUTO: "validate"
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user