diff --git a/CLAUDE.md b/CLAUDE.md index d1b452a..c33b9c6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -186,6 +186,383 @@ QA Engineer - **예시**: `kubectl exec -n phonebill-dev product-change-postgres-dev-postgresql-0 -c postgresql -- bash -c 'PGPASSWORD="$POSTGRES_POSTGRES_PASSWORD" psql -U postgres -d product_change_db -c "ALTER TABLE product_change.pc_product_change_history ALTER COLUMN customer_id TYPE VARCHAR(100);"'` [가이드] ``` -$(cat claude/guide.md) +# Clauding Guide +최종 수정일시: 2025-11-18 20:10 + +## 서비스기획 가이드 +- 서비스기획프롬프트 + - 설명: 유저스토리 작성 등 서비스 기획을 위한 프롬프트 예시 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/prompt/02.think-prompt.md + - 파일명: think-prompt.md + +- 서비스기획가이드 + - 설명: 서비스 기획 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/think/think-guide.md + - 파일명: think-guide.md + +--- + +## 설계 가이드 +- 설계실행프롬프트 + - 설명: 각 설계 단계 실행을 위한 프롬프트 모음 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/prompt/03.design-prompt.md + - 파일명: design-prompt.md + +- 공통설계원칙 + - 설명: 모든 설계 시 적용할 공통설계원칙 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/common-principles.md + - 파일명: common-principles.md + +- UI/UX설계가이드 + - 설명: UI/UX 설계 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/uiux-design.md + - 파일명: uiux-design.md + +- 프로토타입작성가이드 + - 설명: 프로토타입 작성 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/uiux-prototype.md + - 파일명: uiux-prototype.md + +- 클라우드아키텍처패턴선정가이드 + - 설명: 클라우드 아키텍처 패턴 선정 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/architecture-patterns.md + - 파일명: architecture-patterns.md + +- 논리아키텍처설계가이드 + - 설명: 논리 아키텍처 설계 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/logical-architecture-design.md + - 파일명: logical-architecture-design.md + +- API설계가이드 + - 설명: API 설계 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/api-design.md + - 파일명: api-design.md + +- 외부시퀀스설계가이드 + - 설명: 외부 시퀀스 설계 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/sequence-outer-design.md + - 파일명: sequence-outer-design.md + +- 내부시퀀스설계 가이드 + - 설명: 내부 시퀀스 설계 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/sequence-inner-design.md + - 파일명: sequence-inner-design.md + +- 클래스설계가이드 + - 설명: 클래스 설계 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/class-design.md + - 파일명: class-design.md + +- 데이터설계가이드 + - 설명: 데이터 설계 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/data-design.md + - 파일명: data-design.md + +- HighLevel아키텍처정의가이드 + - 설명: 상위수준 아키텍처 정의 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/architecture-highlevel.md + - 파일명: architecture-highlevel.md + +- 물리아키텍처설계가이드 + - 설명: 물리 아키텍처 설계 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/physical-architecture-design.md + - 파일명: physical-architecture-design.md + +- 프론트엔드설계가이드 + - 설명: 프론트엔드 설계 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/frontend-design.md + - 파일명: frontend-design.md + +--- + +## 개발 가이드 +- 개발실행프롬프트 + - 설명: 각 개발 단계 실행을 위한 프롬프트 모음 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/prompt/04.develop-prompt.md + - 파일명: develop-prompt.md + +- 데이터베이스설치계획서가이드 + - 설명: 데이터베이스 설치 방법 안내 요청 시 참조 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/develop/database-plan.md + - 파일명: database-plan.md + +- 데이터베이스설치가이드 + - 설명: 데이터베이스 설치 방법 안내 요청 시 참조 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/develop/database-install.md + - 파일명: database-install.md + +- MQ설치게획서가이드 + - 설명: Message Queue 설치 방법 안내 요청 시 참조 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/develop/mq-plan.md + - 파일명: mq-plan.md + +- MQ설치가이드 + - 설명: Message Queue 설치 방법 안내 요청 시 참조 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/develop/mq-install.md + - 파일명: mq-install.md + +- 백엔드개발가이드 + - 설명: 백엔드 개발 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/develop/dev-backend.md + - 파일명: dev-backend.md + +- GradleWrapper생성가이드 + - 설명: Gradle Wrapper 생성 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/develop/gradle-wrapper.md + - 파일명: gradle-wrapper.md + +- 서비스실행프로파일작성가이드 + - 설명: 백엔드 서비스 실행 프로파일 작성 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/develop/make-run-profile.md + - 파일명: make-run-profile.md + +- 백엔드테스트가이드 + - 설명: 백엔드 테스트 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/develop/test-backend.md + - 파일명: test-backend.md + +- 프론트엔드개발가이드 + - 설명: 프론트엔드 개발 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/develop/dev-frontend.md + - 파일명: dev-frontend.md + +--- + +## 배포 가이드 +- 배포실행프롬프트 + - 설명: 각 배포 단계 실행을 위한 프롬프트 모음 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/prompt/05.deploy-prompt.md + - 파일명: deploy-prompt.md +- 백엔드컨테이너이미지작성가이드 + - 설명: 백엔드 컨테이너 이미지 작성 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/deploy/build-image-back.md + - 파일명: build-image-back.md +- 프론트엔드컨테이너이미지작성가이드 + - 설명: 프론트엔드 컨테이너 이미지 작성 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/deploy/build-image-front.md + - 파일명: build-image-front.md +- 백엔드컨테이너실행방법가이드 + - 설명: 백엔드 컨테이너 실행방법 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/deploy/run-container-guide-back.md + - 파일명: run-container-guide-back.md +- 프론트엔드컨테이너실행방법가이드 + - 설명: 프론트엔드 컨테이너 실행방법 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/deploy/run-container-guide-front.md + - 파일명: run-container-guide-front.md +- 백엔드배포가이드 + - 설명: 백엔드 서비스를 쿠버네티스 클러스터에 배포하는 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/deploy/deploy-k8s-back.md + - 파일명: deploy-k8s-back.md +- 프론트엔드배포가이드 + - 설명: 프론트엔드 서비스를 쿠버네티스 클러스터에 배포하는 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/deploy/deploy-k8s-front.md + - 파일명: deploy-k8s-front.md +- 백엔드Jenkins파이프라인작성가이드 + - 설명: 백엔드 서비스를 Jenkins를 이용하여 CI/CD하는 배포 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/deploy/deploy-jenkins-cicd-back.md + - 파일명: deploy-jenkins-cicd-back.md +- 프론트엔드Jenkins파이프라인작성가이드 + - 설명: 프론트엔드 서비스를 Jenkins를 이용하여 CI/CD하는 배포 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/deploy/deploy-jenkins-cicd-front.md + - 파일명: deploy-jenkins-cicd-front.md +- 백엔드GitHubActions파이프라인작성가이드 + - 설명: 백엔드 서비스를 GitHub Actions를 이용하여 CI/CD하는 배포 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/deploy/deploy-actions-cicd-back.md + - 파일명: deploy-actions-cicd-back.md +- 프론트엔드GitHubActions파이프라인작성가이드 + - 설명: 프론트엔드 서비스를 GitHub Actions를 이용하여 CI/CD하는 배포 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/deploy/deploy-actions-cicd-front.md + - 파일명: deploy-actions-cicd-front.md +- ArgoCD파이프라인준비가이드 + - 설명: 프론트엔드/백엔드 서비스를 ArgoCD를 이용하여 CI와 CD를 분리하는 가이드 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/deploy/deploy-argocd-cicd.md + - 파일명: deploy-argocd-cicd.md + +## 참조 문서 +- 프로젝트지침템플릿 + - 설명: 프로젝트 지침인 CLAUDE.md 파일 템플릿 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/references/instruction-template.md + - 파일명: instruction-template.md + +- 유저스토리작성방법 + - 설명: 유저스토리 형식과 작성법 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/references/유저스토리작성방법.md + - 파일명: userstory-writing.md + +- 유저스토리예제 + - 설명: 유저스토리 예제 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/sample-%EC%9C%A0%EC%A0%80%EC%8A%A4%ED%86%A0%EB%A6%AC.md + - 파일명: sample-userstory.md + +- 클라우드아키텍처패턴요약표 + - 설명: 클라우드 디자인 패턴 요약표 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/references/Cloud%20Design%20Patterns(%EA%B0%9C%EC%9A%94).md + - 파일명: cloud-design-patterns.md + +- HighLevel아키텍처정의서템플릿 + - 설명: MSA 7대 컴포넌트별로 상위 수준의 아키텍처를 정의한 문서 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/references/highlevel-architecture-template.md + - 파일명: highlevel-architecture-template.md + +- 제품별버전가이드 + - 설명: 개발언어, 개발 프레임워크, AI제품 등의 버전 참조를 위한 페이지 링크 제공 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/references/제품버전참조.md + - 파일명: version-link.md + +- 백킹서비스설치방법 + - 설명: 데이터베이스, Message Queue 등 백킹서비스설치방법 설명 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/references/백킹서비스설치방법.md + - 파일명: backing-service-method.md + +--- + +## 표준 +- 개발주석표준 + - 설명: 개발 주석 표준 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/standards/standard_comment.md + - 파일명: standard_comment.md + +- 패키지구조표준 + - 설명: 패키지 구조 표준과 설계 아키텍처 패턴(Layered, Clean, Hexagonal)별 예시 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/standards/standard_package_structure.md + - 파일명: standard_package_structure.md + +- 테스트코드표준 + - 설명: 테스트 코드 작성 표준 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/standards/standard_testcode.md + - 파일명: standard_testcode.md + +--- + +## 기술 도구 +- PlantUML문법검사가이드 + - 설명: PlantUML 문법 검사하는 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/tools/plantuml-guide.md + - 파일명: plantuml-guide.md + +- Mermaid문법검사가이드 + - 설명: Mermaid 문법 검사하는 방법 안내 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/tools/mermaid-guide.md + - 파일명: mermaid-guide.md + +- MCP동기화도구 + - 설명: Window에서 Cloude Desktop의 MCP설정을 읽어 Claude Code에 MCP 서버를 동기화하는 툴 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/tools/sync-mcp.md + - 파일명: sync-mcp.md + +- PlantUML문법검사기(Window) + - 설명: Window용 PlantUML 스크립트 문법 검사기 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/tools/check-plantuml.ps1 + - 파일명: check-plantuml.ps1 + +- Mermaid문법검사기(Window) + - 설명: Window용 PlantUML 스크립트 문법 검사기 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/tools/check-mermaid.ps1 + - 파일명: check-mermaid.ps1 + +- PlantUML문법검사기(Linux/Mac) + - 설명: Linux/Mac용 PlantUML 스크립트 문법 검사기 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/tools/check-plantuml.sh + - 파일명: check-plantuml.sh + +- Mermaid문법검사기(Linux/Mac) + - 설명: Linux/Mac용 PlantUML 스크립트 문법 검사기 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/tools/check-mermaid.sh + - 파일명: check-mermaid.sh + +- IntelliJ서비스실행기 + - 설명: IntelliJ에 등록된 실행프로파일을 이용하여 서비스 실행 + - URL: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/tools/run-intellij-service-profile.py + - 파일명: run-intellij-service-profile.py + +--- + +## 산출물 디렉토리 +- 유저스토리: design/userstory.md +- UI/UX설계서: design/uiux/uiux.md +- 스타일가이드: design/uiux/style-guide.md +- 프로토타입: design/uiux/prototype/*.html +- 아키텍처패턴: design/pattern/architecture-pattern.md +- 논리아키텍처: design/backend/logical/* +- API설계서: design/backend/api/* +- API명세서: design/backend/api/spec/* +- 외부시퀀스설계서: design/backend/sequence/outer/{플로우명}.puml +- 내부시퀀스설계서: design/backend/sequence/inner/{service-name}-{flow-name}.puml +- 클래스설계서: design/backend/class/* +- 백엔드패키지구조도: 클래스설계 결과(design/backend/class/class.md)의 '패키지 구조도' 섹션 +- 데이터설계서: design/backend/database/* +- HighLevel아키텍처정의서: design/high-level-architecture.md +- 물리아키텍처: design/backend/physical/* +- 데이터베이스설치계획서 + - develop/database/plan/db-plan-{service-name}-dev.md + - develop/database/plan/db-plan-{service-name}-prod.md +- 캐시설치계획서: + - develop/mq/mq-plan-dev.md + - develop/mq/mq-plan-prod.md +- MQ설치계획서 + - develop/database/plan/mq-plan-{service-name}-dev.md + - develop/database/plan/mq-plan-{service-name}-prod.md +- 데이터베이스설치결과서 + - develop/database/exec/db-exec-dev.md + - develop/database/exec/db-exec-prod.md +- 캐시설치결과서 + - develop/database/exec/cache-exec-{service-name}-dev.md + - develop/database/exec/cache-exec-{service-name}-prod.md +- MQ설치결과서 + - develop/mq/mq-exec-dev.md + - develop/mq/mq-exec-prod.md +- 백엔드개발결과서: develop/dev/dev-backend.md +- 백엔드테스트결과서: develop/dev/test-backend.md +- 프론트엔드설계서: design/frontend/frontend-design.md + +## 프롬프트 약어 +### 역할 약어 +- "@archi": "--persona-architect" +- "@front": "--persona-front" +- "@back": "--persona-backend" +- "@secu": "--persona-security" +- "@qa": "--persona-qa" +- "@refact": "--persona-refactor" +- "@devops": "--persona-devops" +- "@scribe": "--persona-scriber" + +### 작업 약어 +- "@complex-flag": --seq --c7 --uc --wave-mode auto --wave-strategy systematic --delegate auto + +- "@userstory": /sc:document @scribe @archi --think --wave-strategy systematic +- "@uiux": /sc:design --think @front --uc --wave-mode auto --wave-strategy systematic +- "@prototype": /sc:implement @front --answer-only +- "@design-pattern": /sc:design @archi --think-hard @complex-flag +- "@architecture": /sc:design @archi @back @refact --think-hard @complex-flag +- "@plan": --plan --think +- "@backing-service": /sc:implement @devops @back --think-hard @complex-flag +- "@dev-backend": /sc:implement @back --think-hard @complex-flag +- "@dev-front": /sc:implement @front --think-hard @complex-flag +- "@test-backend": /sc:test @back @qa --think @complex-flag +- "@test-api": /sc:test @back @qa --think 1) 소스 수정 후 컴파일하고 서버 시작 요청. 2) API경로와 DTO를 분석하여 정확하게 요청하여 테스트 +- "@run-back": + - 'IntelliJ서비스실행기'를 'tools' 디렉토리에 다운로드 + - python 또는 python3 명령으로 백그라우드로 실행하고 결과 로그를 분석 + nohup python3 tools/run-intellij-service-profile.py {service-name} > logs/{service-name}.log 2>&1 & echo "Started {service-name} with PID: $!" +- "@test-front": /sc:test @front @qa --play --think @complex-flag +- "@cicd": /sc:implement @devops --think @complex-flag +- "@document": /sc:document --think @scribe @complex-flag +- "@fix": /sc:troubleshoot --think @complex-flag +- "@estimate": /sc:estimate --think-hard @complex-flag +- "@improve": /sc:improve --think @complex-flag +- "@analyze": /sc:analyze --think --seq +- "@explain": /sc:explain --think --seq --answer-only + +### 파일 약어 +- "@error": debug/error.png파일을 의미함 +- "@info": debug/info.png파일을 의미함 + +### 작업 단계 가이드 약어 +- "@think-help": "기획실행프롬프트 내용을 터미널에 출력" +- "@design-help": "설계실행프롬프트 내용을 터미널에 출력" +- "@develop-help": "개발실행프롬프트 내용을 터미널에 출력" +- "@deploy-help": "배포실행프롬프트 내용을 터미널에 출력" ``` + + diff --git a/bill-service/.run/bill-service.run.xml b/bill-service/.run/bill-service.run.xml index 5992e59..d09d59a 100644 --- a/bill-service/.run/bill-service.run.xml +++ b/bill-service/.run/bill-service.run.xml @@ -12,10 +12,10 @@ - - + + - + @@ -27,8 +27,8 @@ - - + + diff --git a/bill-service/src/main/resources/application.yml b/bill-service/src/main/resources/application.yml index cc84bc7..82c02d0 100644 --- a/bill-service/src/main/resources/application.yml +++ b/bill-service/src/main/resources/application.yml @@ -25,9 +25,11 @@ spring: leak-detection-threshold: 60000 # JPA 설정 jpa: + database-platform: org.hibernate.dialect.PostgreSQLDialect show-sql: ${SHOW_SQL:true} properties: hibernate: + dialect: org.hibernate.dialect.PostgreSQLDialect format_sql: true use_sql_comments: true connection: diff --git a/deployment/cicd/Jenkinsfile b/deployment/cicd/Jenkinsfile index 8ba01b8..75d2348 100644 --- a/deployment/cicd/Jenkinsfile +++ b/deployment/cicd/Jenkinsfile @@ -12,7 +12,7 @@ podTemplate( slaveConnectTimeout: 300, idleMinutes: 1, activeDeadlineSeconds: 3600, - podRetention: never(), // 파드 자동 정리 옵션: never(), onFailure(), always(), default() + podRetention: never(), yaml: ''' spec: terminationGracePeriodSeconds: 3 @@ -51,8 +51,8 @@ podTemplate( ] ), containerTemplate( - name: 'azure-cli', - image: 'hiondal/azure-kubectl:latest', + name: 'kubectl', + image: 'bitnami/kubectl:latest', command: 'cat', ttyEnabled: true, resourceRequestCpu: '200m', @@ -63,14 +63,12 @@ podTemplate( ], 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 imageTag = "dg0500" + 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'] @@ -81,15 +79,12 @@ podTemplate( 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-dg0500 --dry-run=client -o yaml | kubectl apply -f - - """ - } + stage("Setup Kubernetes") { + container('kubectl') { + sh """ + kubectl config use-context ${props.k8s_context} || echo "Context switch skipped" + kubectl create namespace ${props.namespace} --dry-run=client -o yaml | kubectl apply -f - + """ } } @@ -107,27 +102,36 @@ podTemplate( 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}-dg0500 \\ - -Dsonar.projectName=phonebill-${service}-dg0500 \\ - -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/** - """ - } + services.each { service -> + if (service != 'api-gateway') { + withSonarQubeEnv('SonarQube') { + echo "🔍 Starting SonarQube analysis for ${service}..." - // Quality Gate 확인 - timeout(time: 10, unit: 'MINUTES') { - def qg = waitForQualityGate() - if (qg.status != 'OK') { - error "Pipeline aborted due to quality gate failure: ${qg.status}" + 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/** + """ + + echo "✅ SonarQube analysis completed for ${service}" + } + + timeout(time: 5, unit: 'MINUTES') { + echo "⏳ Waiting for Quality Gate result for ${service}..." + def qg = waitForQualityGate() + if (qg.status != 'OK') { + error "❌ Quality Gate failed for ${service}: ${qg.status}" + } else { + echo "✅ Quality Gate passed for ${service}" + } } } } + + echo "🎉 All services passed SonarQube Quality Gates!" } } } @@ -136,32 +140,23 @@ podTemplate( 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} . + -t docker.io/hiondal/${service}:${environment}-${imageTag} . - podman push acrdigitalgarage01.azurecr.io/phonebill/${service}:${environment}-${imageTag} + podman push docker.io/hiondal/${service}:${environment}-${imageTag} """ } } @@ -170,9 +165,9 @@ podTemplate( } stage('Update Kustomize & Deploy') { - container('azure-cli') { + container('kubectl') { sh """ - # Kustomize 설치 (sudo 없이 사용자 디렉토리에 설치) + # Kustomize 설치 curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash mkdir -p \$HOME/bin mv kustomize \$HOME/bin/ @@ -181,12 +176,12 @@ podTemplate( # 환경별 디렉토리로 이동 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:${environment}-${imageTag} + \$HOME/bin/kustomize edit set image docker.io/hiondal/\$service:${environment}-${imageTag} done # 매니페스트 적용 @@ -195,17 +190,15 @@ podTemplate( # 배포 상태 확인 echo "Waiting for deployments to be ready..." for service in \$services; do - kubectl -n phonebill-dg0500 wait --for=condition=available deployment/\$service --timeout=300s + kubectl -n ${props.namespace} wait --for=condition=available deployment/\$service --timeout=300s || echo "Timeout waiting for \$service" 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 { @@ -222,4 +215,4 @@ podTemplate( echo "Pod will be terminated in 3 seconds due to terminationGracePeriodSeconds: 3" } } -} \ No newline at end of file +} diff --git a/deployment/cicd/Jenkinsfile_ArgoCD b/deployment/cicd/Jenkinsfile_ArgoCD deleted file mode 100644 index 7c37bfa..0000000 --- a/deployment/cicd/Jenkinsfile_ArgoCD +++ /dev/null @@ -1,240 +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 = "dg0500-"+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') { - // 각 서비스별로 개별적으로 SonarQube 분석 및 Quality Gate 확인 - services.each { service -> - withSonarQubeEnv('SonarQube') { - echo "🔍 Starting SonarQube analysis for ${service}..." - - // 서비스별 테스트 및 SonarQube 분석 - sh """ - ./gradlew :${service}:test :${service}:jacocoTestReport :${service}:sonar \\ - -Dsonar.projectKey=phonebill-${service}-dg0500 \\ - -Dsonar.projectName=phonebill-${service}-dg0500 \\ - -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/** - """ - - echo "✅ SonarQube analysis completed for ${service}" - } - - // 각 서비스별 Quality Gate 확인 - timeout(time: 5, unit: 'MINUTES') { - echo "⏳ Waiting for Quality Gate result for ${service}..." - def qg = waitForQualityGate() - if (qg.status != 'OK') { - error "❌ Quality Gate failed for ${service}: ${qg.status}" - } else { - echo "✅ Quality Gate passed for ${service}" - } - } - } - - echo "🎉 All services passed SonarQube Quality Gates!" - } - } - } - - 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!" - } 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" - } - } -} \ No newline at end of file diff --git a/deployment/cicd/config/deploy_env_vars_dev b/deployment/cicd/config/deploy_env_vars_dev index 5a49197..4ba0dc9 100644 --- a/deployment/cicd/config/deploy_env_vars_dev +++ b/deployment/cicd/config/deploy_env_vars_dev @@ -1,3 +1,4 @@ -# dev Environment Configuration -resource_group=rg-digitalgarage-01 -cluster_name=aks-digitalgarage-01 \ No newline at end of file +# Dev Environment Configuration +# Minikube Remote 환경 설정 +k8s_context=minikube-remote +namespace=phonebill diff --git a/deployment/cicd/config/deploy_env_vars_prod b/deployment/cicd/config/deploy_env_vars_prod index 53ee4a0..76bebbe 100644 --- a/deployment/cicd/config/deploy_env_vars_prod +++ b/deployment/cicd/config/deploy_env_vars_prod @@ -1,3 +1,4 @@ -# prod Environment Configuration -resource_group=rg-digitalgarage-01 -cluster_name=aks-digitalgarage-01 \ No newline at end of file +# Production Environment Configuration +# Minikube Remote 환경 설정 +k8s_context=minikube-remote +namespace=phonebill-prod diff --git a/deployment/cicd/config/deploy_env_vars_staging b/deployment/cicd/config/deploy_env_vars_staging index 5873b90..3ff8ab1 100644 --- a/deployment/cicd/config/deploy_env_vars_staging +++ b/deployment/cicd/config/deploy_env_vars_staging @@ -1,3 +1,4 @@ -# staging Environment Configuration -resource_group=rg-digitalgarage-01 -cluster_name=aks-digitalgarage-01 \ No newline at end of file +# Staging Environment Configuration +# Minikube Remote 환경 설정 +k8s_context=minikube-remote +namespace=phonebill-staging diff --git a/deployment/cicd/jenkins-pipeline-guide.md b/deployment/cicd/jenkins-pipeline-guide.md index 128112b..09c4841 100644 --- a/deployment/cicd/jenkins-pipeline-guide.md +++ b/deployment/cicd/jenkins-pipeline-guide.md @@ -1,181 +1,253 @@ -# phonebill Jenkins CI/CD 파이프라인 구축 가이드 +# Jenkins CI/CD 파이프라인 가이드 -## 📋 개요 +## 개요 +이 문서는 Phonebill 프로젝트의 Jenkins 기반 CI/CD 파이프라인 구축 및 운영 가이드입니다. -본 가이드는 phonebill 프로젝트를 위한 Jenkins + Kustomize 기반 CI/CD 파이프라인 구축 방법을 제공합니다. +## 프로젝트 정보 -### 프로젝트 정보 -- **시스템명**: phonebill -- **서비스 목록**: api-gateway, user-service, bill-service, product-service, kos-mock -- **JDK 버전**: 21 -- **Azure 환경**: - - ACR: acrdigitalgarage01 - - 리소스 그룹: rg-digitalgarage-01 - - AKS 클러스터: aks-digitalgarage-01 - - 네임스페이스: phonebill-dg0500 +| 항목 | 값 | +|------|-----| +| 시스템명 | phonebill | +| JDK 버전 | 21 | +| Image Registry | docker.io | +| Image Organization | hiondal | +| K8s Context | minikube-remote | +| Namespace | phonebill | -## 🏗️ 파이프라인 아키텍처 +### 서비스 목록 +- api-gateway (포트: 8080) +- user-service (포트: 8081) +- bill-service (포트: 8082) +- product-service (포트: 8083) +- kos-mock (포트: 8084) -### 주요 구성 요소 -- **빌드**: Gradle 기반 멀티모듈 빌드 -- **품질 검증**: SonarQube 분석 & Quality Gate -- **컨테이너화**: Podman 기반 이미지 빌드 -- **배포**: Kustomize를 통한 환경별 배포 -- **인프라**: AKS (Azure Kubernetes Service) +--- -### 환경별 설정 -| 환경 | Replicas | CPU Request | Memory Request | CPU Limit | Memory Limit | -|------|----------|-------------|----------------|-----------|--------------| -| dev | 1 | 256m | 256Mi | 1024m | 1024Mi | -| staging | 2 | 512m | 512Mi | 2048m | 2048Mi | -| prod | 3 | 1024m | 1024Mi | 4096m | 4096Mi | +## 1. 사전 준비사항 -## 🚀 Jenkins 서버 환경 구성 - -### 1. Jenkins 필수 플러그인 설치 - -Jenkins 관리 > Plugin Manager에서 다음 플러그인들을 설치해주세요: - -```bash -# Jenkins 필수 플러그인 목록 +### 1.1 Jenkins 필수 플러그인 +``` - Kubernetes - Pipeline Utility Steps - Docker Pipeline - GitHub - SonarQube Scanner -- Azure Credentials ``` -### 2. Jenkins Credentials 등록 +### 1.2 Jenkins Credentials 등록 -**Manage Jenkins > Credentials > Add Credentials**에서 다음 인증 정보들을 등록해주세요: +Jenkins 관리 > Credentials > Add Credentials에서 등록: -#### Azure Service Principal +#### Docker Hub Credentials ``` -Kind: Microsoft Azure Service Principal -ID: azure-credentials -Subscription ID: {구독ID} -Client ID: {클라이언트ID} -Client Secret: {클라이언트시크릿} -Tenant ID: {테넌트ID} -Azure Environment: Azure +- Kind: Username with password +- ID: dockerhub-credentials +- Username: {DOCKERHUB_USERNAME} +- Password: {DOCKERHUB_PASSWORD} ``` -#### ACR Credentials +#### SonarQube Token (선택사항) ``` -Kind: Username with password -ID: acr-credentials -Username: acrdigitalgarage01 -Password: {ACR_PASSWORD} +- Kind: Secret text +- ID: sonarqube-token +- Secret: {SonarQube 토큰} ``` -#### Docker Hub Credentials (Rate Limit 해결용) +### 1.3 SonarQube 설정 (선택사항) +Jenkins 관리 > Configure System > SonarQube servers: ``` -Kind: Username with password -ID: dockerhub-credentials -Username: {DOCKERHUB_USERNAME} -Password: {DOCKERHUB_PASSWORD} -참고: Docker Hub 무료 계정 생성 (https://hub.docker.com) +- Name: SonarQube +- Server URL: http://{SONARQUBE_URL} +- Server authentication token: sonarqube-token (위에서 등록한 credential) ``` -#### SonarQube Token -``` -Kind: Secret text -ID: sonarqube-token -Secret: {SonarQube토큰} -``` +--- -## 📂 Kustomize 구조 +## 2. Kustomize 구조 +### 2.1 디렉토리 구조 ``` -deployment/cicd/kustomize/ -├── base/ -│ ├── kustomization.yaml # Base 설정 -│ ├── common/ # 공통 리소스 -│ │ ├── cm-common.yaml -│ │ ├── secret-common.yaml -│ │ ├── secret-imagepull.yaml -│ │ └── ingress.yaml -│ ├── api-gateway/ # API Gateway 리소스 -│ │ ├── deployment.yaml -│ │ ├── service.yaml -│ │ └── cm-api-gateway.yaml -│ ├── user-service/ # User Service 리소스 -│ │ ├── deployment.yaml -│ │ ├── service.yaml -│ │ ├── cm-user-service.yaml -│ │ └── secret-user-service.yaml -│ ├── bill-service/ # Bill Service 리소스 -│ │ ├── deployment.yaml -│ │ ├── service.yaml -│ │ ├── cm-bill-service.yaml -│ │ └── secret-bill-service.yaml -│ ├── product-service/ # Product Service 리소스 -│ │ ├── deployment.yaml -│ │ ├── service.yaml -│ │ ├── cm-product-service.yaml -│ │ └── secret-product-service.yaml -│ └── kos-mock/ # KOS Mock 리소스 -│ ├── deployment.yaml -│ ├── service.yaml -│ └── cm-kos-mock.yaml -└── overlays/ - ├── dev/ # 개발 환경 +deployment/cicd/ +├── Jenkinsfile +├── config/ +│ ├── deploy_env_vars_dev +│ ├── deploy_env_vars_staging +│ └── deploy_env_vars_prod +├── scripts/ +│ ├── deploy.sh +│ └── validate-resources.sh +└── kustomize/ + ├── base/ │ ├── kustomization.yaml - │ ├── cm-common-patch.yaml - │ ├── secret-common-patch.yaml - │ ├── ingress-patch.yaml - │ ├── deployment-*-patch.yaml (각 서비스별) - │ └── secret-*-patch.yaml (Secret 보유 서비스) - ├── staging/ # 스테이징 환경 - │ └── (dev와 동일한 구조) - └── prod/ # 운영 환경 - └── (dev와 동일한 구조) + │ ├── common/ + │ │ ├── cm-common.yaml + │ │ ├── secret-common.yaml + │ │ └── ingress.yaml + │ ├── api-gateway/ + │ ├── user-service/ + │ ├── bill-service/ + │ ├── product-service/ + │ └── kos-mock/ + └── overlays/ + ├── dev/ + ├── staging/ + └── prod/ ``` -## 🔧 Jenkins Pipeline Job 생성 +### 2.2 환경별 설정 차이 -### 1. 새 Pipeline Job 생성 -1. Jenkins 대시보드에서 **New Item** 클릭 -2. **Pipeline** 선택하고 프로젝트명 입력 -3. **OK** 클릭 +| 항목 | dev | staging | prod | +|------|-----|---------|------| +| Namespace | phonebill | phonebill-staging | phonebill-prod | +| Replicas | 1 | 2 | 3 | +| CPU Requests | 256m | 512m | 1024m | +| Memory Requests | 256Mi | 512Mi | 1024Mi | +| CPU Limits | 1024m | 2048m | 4096m | +| Memory Limits | 1024Mi | 2048Mi | 4096Mi | +| DDL_AUTO | update | validate | validate | +| SHOW_SQL | true | false | false | +| SSL Redirect | false | true | true | -### 2. Pipeline 설정 -#### General 탭 -- **Build Parameters** 추가: - ``` - ENVIRONMENT: Choice Parameter - Choices: dev, staging, prod - Default: dev +--- - IMAGE_TAG: String Parameter - Default: latest - Description: 이미지 태그 (기본값: latest) +## 3. Jenkins Pipeline Job 생성 - SKIP_SONARQUBE: String Parameter - Default: true - Description: SonarQube 분석 건너뛰기 (true/false) - ``` +### 3.1 New Item > Pipeline 선택 -#### Pipeline 탭 +### 3.2 Pipeline 설정 ``` -Definition: Pipeline script from SCM SCM: Git -Repository URL: {Git저장소URL} +Repository URL: {Git 저장소 URL} Branch: main (또는 develop) Script Path: deployment/cicd/Jenkinsfile ``` -## 📊 SonarQube 프로젝트 설정 +### 3.3 Pipeline Parameters 설정 +| 파라미터 | 타입 | 기본값 | 설명 | +|----------|------|--------|------| +| ENVIRONMENT | Choice | dev | 배포 환경 (dev/staging/prod) | +| SKIP_SONARQUBE | String | true | SonarQube 분석 건너뛰기 (true/false) | -### 1. 각 서비스별 SonarQube 프로젝트 생성 -- phonebill-api-gateway-{환경} -- phonebill-user-service-{환경} -- phonebill-bill-service-{환경} -- phonebill-product-service-{환경} -- phonebill-kos-mock-{환경} +--- -### 2. Quality Gate 설정 +## 4. 파이프라인 스테이지 + +### 4.1 Get Source +- Git 저장소에서 소스 코드 체크아웃 +- 환경별 설정 파일 로드 + +### 4.2 Setup Kubernetes +- Kubernetes 컨텍스트 설정 +- 네임스페이스 생성 + +### 4.3 Build +- Gradle을 사용한 빌드 (테스트 제외) +- `./gradlew build -x test` + +### 4.4 SonarQube Analysis & Quality Gate (선택사항) +- SKIP_SONARQUBE=false일 때만 실행 +- 각 서비스별 테스트 및 코드 품질 분석 +- Quality Gate 통과 확인 + +### 4.5 Build & Push Images +- Podman을 사용한 컨테이너 이미지 빌드 +- Docker Hub로 이미지 푸시 +- 이미지 태그: `{환경}-{타임스탬프}` + +### 4.6 Update Kustomize & Deploy +- Kustomize를 사용한 이미지 태그 업데이트 +- Kubernetes 매니페스트 적용 +- 배포 상태 확인 + +--- + +## 5. 배포 실행 + +### 5.1 Jenkins 파이프라인 실행 +1. Jenkins > phonebill > Build with Parameters +2. ENVIRONMENT 선택 (dev/staging/prod) +3. SKIP_SONARQUBE 입력 (true 또는 false) +4. Build 클릭 + +### 5.2 수동 배포 (스크립트 사용) +```bash +# dev 환경 배포 +./deployment/cicd/scripts/deploy.sh dev latest + +# staging 환경 배포 +./deployment/cicd/scripts/deploy.sh staging v1.0.0 + +# prod 환경 배포 +./deployment/cicd/scripts/deploy.sh prod v1.0.0 +``` + +### 5.3 배포 상태 확인 +```bash +# Pod 상태 확인 +kubectl get pods -n phonebill + +# 서비스 상태 확인 +kubectl get services -n phonebill + +# Ingress 상태 확인 +kubectl get ingress -n phonebill + +# 특정 Pod 로그 확인 +kubectl logs -f deployment/api-gateway -n phonebill +``` + +--- + +## 6. 롤백 + +### 6.1 이전 버전으로 롤백 +```bash +# 특정 리비전으로 롤백 +kubectl rollout undo deployment/{서비스명} -n phonebill --to-revision=2 + +# 롤백 상태 확인 +kubectl rollout status deployment/{서비스명} -n phonebill + +# 롤백 히스토리 확인 +kubectl rollout history deployment/{서비스명} -n phonebill +``` + +### 6.2 이미지 태그 기반 롤백 +```bash +cd deployment/cicd/kustomize/overlays/{환경} + +# 이전 안정 버전 이미지 태그로 업데이트 +kustomize edit set image docker.io/hiondal/{서비스명}:{환경}-{이전태그} + +# 배포 +kubectl apply -k . +``` + +--- + +## 7. 리소스 검증 + +### 7.1 검증 스크립트 실행 +```bash +./deployment/cicd/scripts/validate-resources.sh +``` + +### 7.2 Kustomize 빌드 테스트 +```bash +# Base 빌드 테스트 +kubectl kustomize deployment/cicd/kustomize/base/ + +# 환경별 빌드 테스트 +kubectl kustomize deployment/cicd/kustomize/overlays/dev/ +kubectl kustomize deployment/cicd/kustomize/overlays/staging/ +kubectl kustomize deployment/cicd/kustomize/overlays/prod/ +``` + +--- + +## 8. SonarQube 설정 (선택사항) + +### 8.1 Quality Gate 권장 설정 ``` Coverage: >= 80% Duplicated Lines: <= 3% @@ -184,194 +256,56 @@ Reliability Rating: <= A Security Rating: <= A ``` -## 🚀 배포 실행 방법 - -### 1. Jenkins Pipeline 실행 -1. Jenkins > {프로젝트명} > **Build with Parameters** 클릭 -2. 파라미터 설정: - - **ENVIRONMENT**: dev/staging/prod 선택 - - **IMAGE_TAG**: 이미지 태그 입력 (선택사항) - - **SKIP_SONARQUBE**: SonarQube 분석 건너뛰려면 "true", 실행하려면 "false" -3. **Build** 클릭 - -### 2. 수동 배포 (스크립트 사용) -```bash -# 개발 환경 배포 -./deployment/cicd/scripts/deploy.sh dev 20241230120000 - -# 스테이징 환경 배포 -./deployment/cicd/scripts/deploy.sh staging 20241230120000 - -# 운영 환경 배포 -./deployment/cicd/scripts/deploy.sh prod 20241230120000 -``` - -### 3. 배포 상태 확인 -```bash -# Pod 상태 확인 -kubectl get pods -n phonebill-dg0500 - -# 서비스 상태 확인 -kubectl get services -n phonebill-dg0500 - -# Ingress 상태 확인 -kubectl get ingress -n phonebill-dg0500 - -# 특정 서비스 로그 확인 -kubectl logs -f deployment/user-service -n phonebill-dg0500 -``` - -## 🔍 리소스 검증 - -### 리소스 검증 스크립트 실행 -```bash -# 모든 Kustomize 리소스 검증 -./deployment/cicd/scripts/validate-resources.sh -``` - -### 수동 검증 명령어 -```bash -# Base 검증 -kubectl kustomize deployment/cicd/kustomize/base/ - -# 환경별 검증 -kubectl kustomize deployment/cicd/kustomize/overlays/dev/ -kubectl kustomize deployment/cicd/kustomize/overlays/staging/ -kubectl kustomize deployment/cicd/kustomize/overlays/prod/ -``` - -## 🔄 롤백 방법 - -### 1. Kubernetes 롤백 -```bash -# 특정 버전으로 롤백 -kubectl rollout undo deployment/{서비스명} -n phonebill-dg0500 --to-revision=2 - -# 롤백 상태 확인 -kubectl rollout status deployment/{서비스명} -n phonebill-dg0500 -``` - -### 2. 이미지 태그 기반 롤백 -```bash -# 이전 안정 버전 이미지 태그로 업데이트 -cd deployment/cicd/kustomize/overlays/{환경} -kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/{서비스명}:{환경}-{이전태그} -kubectl apply -k . -``` - -## 🎯 파이프라인 단계별 설명 - -### 1. Get Source -- Git 저장소에서 소스코드 체크아웃 -- 환경별 설정 파일 로드 - -### 2. Setup AKS -- Azure CLI를 통한 AKS 클러스터 연결 -- 네임스페이스 생성 (phonebill-dg0500) - -### 3. Build -- Gradle을 통한 멀티모듈 빌드 -- 테스트는 SonarQube 단계에서 실행 - -### 4. SonarQube Analysis & Quality Gate -- 각 서비스별 개별 테스트 실행 -- JaCoCo 코드 커버리지 리포트 생성 -- SonarQube 정적 분석 -- Quality Gate 통과 검증 - -### 5. Build & Push Images -- Podman을 통한 컨테이너 이미지 빌드 -- 환경별 태그로 ACR에 푸시 -- 30분 타임아웃 설정 - -### 6. Update Kustomize & Deploy -- Kustomize를 통한 이미지 태그 업데이트 -- 환경별 매니페스트 적용 -- 배포 완료까지 대기 (5분 타임아웃) - -### 7. Pipeline Complete -- 파이프라인 완료 상태 로깅 -- Pod 자동 정리 (3초 후 종료) - -## ⚠️ 주의사항 - -### 1. 파드 자동 정리 -- 파이프라인 완료 시 에이전트 파드 자동 삭제 -- `podRetention: never()` 설정으로 즉시 정리 -- 리소스 절약을 위한 필수 설정 - -### 2. 변수 참조 문법 -- Jenkins Groovy에서는 `${variable}` 사용 -- `\${variable}` 사용 시 "syntax error: bad substitution" 오류 발생 - -### 3. 쉘 호환성 -- Jenkins 컨테이너 기본 쉘이 `/bin/sh`인 경우 -- Bash 배열 문법 `()` 미지원으로 공백 구분 문자열 사용 - -### 4. 환경별 설정 차이점 -- **DEV**: SSL 리다이렉트 비활성화, 기본 도메인 사용 -- **STAGING/PROD**: SSL 리다이렉트 활성화, 사용자 정의 도메인, 인증서 설정 - -## 🛠️ 트러블슈팅 - -### 1. 빌드 실패 -```bash -# Gradle 캐시 클리어 -./gradlew clean build - -# JDK 버전 확인 -java -version -``` - -### 2. 이미지 푸시 실패 -```bash -# ACR 로그인 확인 -az acr login --name acrdigitalgarage01 - -# Docker Hub rate limit 확인 -docker info | grep -i rate -``` - -### 3. 배포 실패 -```bash -# 네임스페이스 확인 -kubectl get namespaces - -# 리소스 상태 확인 -kubectl describe deployment/{서비스명} -n phonebill-dg0500 -``` - -### 4. SonarQube 분석 실패 -- SonarQube 서버 연결 상태 확인 -- 토큰 유효성 검증 -- Quality Gate 설정 확인 - -## 📈 모니터링 및 로그 - -### 1. Jenkins 빌드 히스토리 -- 각 단계별 실행 시간 추적 -- 실패 단계 및 원인 분석 - -### 2. 애플리케이션 로그 -```bash -# 실시간 로그 모니터링 -kubectl logs -f deployment/{서비스명} -n phonebill-dg0500 - -# 특정 기간 로그 확인 -kubectl logs deployment/{서비스명} -n phonebill-dg0500 --since=1h -``` - -### 3. 성능 메트릭 -- Kubernetes 메트릭 서버 활용 -- 각 서비스별 리소스 사용량 모니터링 +### 8.2 SonarQube 프로젝트 생성 +각 서비스별로 다음 형식의 프로젝트 키로 생성: +- `phonebill-user-service-dev` +- `phonebill-user-service-staging` +- `phonebill-user-service-prod` --- -## 📞 지원 +## 9. 트러블슈팅 -문의사항이나 문제 발생 시: -1. Jenkins 빌드 로그 확인 -2. Kubernetes 이벤트 확인: `kubectl get events -n phonebill-dg0500` -3. 리소스 검증 스크립트 실행: `./deployment/cicd/scripts/validate-resources.sh` +### 9.1 이미지 푸시 실패 +- Docker Hub 인증 정보 확인 +- Rate Limit 확인 (무료 계정 제한) -**최운영/데옵스**: phonebill Jenkins CI/CD 파이프라인이 성공적으로 구축되었습니다! 🎉 \ No newline at end of file +### 9.2 배포 실패 +```bash +# Pod 이벤트 확인 +kubectl describe pod {POD_NAME} -n phonebill + +# Pod 로그 확인 +kubectl logs {POD_NAME} -n phonebill +``` + +### 9.3 Kustomize 빌드 실패 +- 리소스 파일 존재 여부 확인 +- YAML 문법 검증 +- `kubectl kustomize` 명령으로 디버깅 + +### 9.4 SonarQube 연결 실패 +- SonarQube 서버 URL 확인 +- 인증 토큰 유효성 확인 +- 네트워크 연결 확인 + +--- + +## 10. 체크리스트 + +### 사전 준비 +- [ ] Jenkins 필수 플러그인 설치 완료 +- [ ] Docker Hub Credentials 등록 완료 +- [ ] SonarQube Token 등록 완료 (선택) +- [ ] Kubernetes 컨텍스트 설정 완료 + +### 배포 전 +- [ ] 리소스 검증 스크립트 실행 완료 +- [ ] 환경별 설정 파일 확인 완료 +- [ ] Kustomize 빌드 테스트 완료 + +### 배포 후 +- [ ] Pod 상태 확인 (Running) +- [ ] 서비스 엔드포인트 확인 +- [ ] Ingress 접근 테스트 +- [ ] 로그 이상 여부 확인 diff --git a/deployment/cicd/kustomize/base/api-gateway/cm-api-gateway.yaml b/deployment/cicd/kustomize/base/api-gateway/cm-api-gateway.yaml index aa83a94..ecfe4a3 100644 --- a/deployment/cicd/kustomize/base/api-gateway/cm-api-gateway.yaml +++ b/deployment/cicd/kustomize/base/api-gateway/cm-api-gateway.yaml @@ -1,10 +1,14 @@ apiVersion: v1 kind: ConfigMap metadata: - name: cm-api-gateway + name: api-gateway-config + + labels: + app: api-gateway + app.kubernetes.io/part-of: phonebill 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" \ No newline at end of file + USER_SERVICE_URL: "http://user-service:8081" + BILL_SERVICE_URL: "http://bill-service:8082" + PRODUCT_SERVICE_URL: "http://product-service:8083" + KOS_MOCK_URL: "http://kos-mock:8084" diff --git a/deployment/cicd/kustomize/base/api-gateway/deployment.yaml b/deployment/cicd/kustomize/base/api-gateway/deployment.yaml index ceb7e8c..e6b92be 100644 --- a/deployment/cicd/kustomize/base/api-gateway/deployment.yaml +++ b/deployment/cicd/kustomize/base/api-gateway/deployment.yaml @@ -2,6 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: name: api-gateway + + labels: + app: api-gateway + app.kubernetes.io/part-of: phonebill spec: replicas: 1 selector: @@ -12,46 +16,40 @@ spec: labels: app: api-gateway spec: - imagePullSecrets: - - name: phonebill containers: - name: api-gateway - image: acrdigitalgarage01.azurecr.io/phonebill/api-gateway:latest + image: docker.io/hiondal/api-gateway:latest imagePullPolicy: Always ports: - containerPort: 8080 + name: http envFrom: - configMapRef: - name: cm-common + name: phonebill-common-config - configMapRef: - name: cm-api-gateway + name: api-gateway-config - secretRef: - name: secret-common + name: phonebill-common-secret resources: requests: - cpu: 256m - memory: 256Mi + 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 + cpu: "1024m" + memory: "1024Mi" livenessProbe: httpGet: - path: /health + path: /actuator/health/liveness + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness port: 8080 initialDelaySeconds: 30 periodSeconds: 10 - failureThreshold: 3 \ No newline at end of file + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/deployment/cicd/kustomize/base/api-gateway/service.yaml b/deployment/cicd/kustomize/base/api-gateway/service.yaml index e1f6460..9491512 100644 --- a/deployment/cicd/kustomize/base/api-gateway/service.yaml +++ b/deployment/cicd/kustomize/base/api-gateway/service.yaml @@ -2,10 +2,16 @@ apiVersion: v1 kind: Service metadata: name: api-gateway + + labels: + app: api-gateway + app.kubernetes.io/part-of: phonebill spec: + type: ClusterIP selector: app: api-gateway ports: - - port: 80 + - name: http + port: 8080 targetPort: 8080 - type: ClusterIP + protocol: TCP diff --git a/deployment/cicd/kustomize/base/bill-service/cm-bill-service.yaml b/deployment/cicd/kustomize/base/bill-service/cm-bill-service.yaml index 9281f36..21486ec 100644 --- a/deployment/cicd/kustomize/base/bill-service/cm-bill-service.yaml +++ b/deployment/cicd/kustomize/base/bill-service/cm-bill-service.yaml @@ -1,21 +1,16 @@ apiVersion: v1 kind: ConfigMap metadata: - name: cm-bill-service + name: bill-service-config + + labels: + app: bill-service + app.kubernetes.io/part-of: phonebill data: SERVER_PORT: "8082" - DB_KIND: "postgresql" + DB_HOST: "inquiry-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" + DB_NAME: "inquirydb" REDIS_DATABASE: "1" - REDIS_MAX_ACTIVE: "8" - REDIS_MAX_IDLE: "8" - REDIS_MAX_WAIT: "-1" - REDIS_MIN_IDLE: "0" - REDIS_TIMEOUT: "2000" \ No newline at end of file + KOS_BASE_URL: "http://kos-mock:8084" + DDL_AUTO: "update" diff --git a/deployment/cicd/kustomize/base/bill-service/deployment.yaml b/deployment/cicd/kustomize/base/bill-service/deployment.yaml index 78a42dd..53e2f71 100644 --- a/deployment/cicd/kustomize/base/bill-service/deployment.yaml +++ b/deployment/cicd/kustomize/base/bill-service/deployment.yaml @@ -2,6 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: name: bill-service + + labels: + app: bill-service + app.kubernetes.io/part-of: phonebill spec: replicas: 1 selector: @@ -12,48 +16,42 @@ spec: labels: app: bill-service spec: - imagePullSecrets: - - name: phonebill containers: - name: bill-service - image: acrdigitalgarage01.azurecr.io/phonebill/bill-service:latest + image: docker.io/hiondal/bill-service:latest imagePullPolicy: Always ports: - containerPort: 8082 + name: http envFrom: - configMapRef: - name: cm-common + name: phonebill-common-config - configMapRef: - name: cm-bill-service + name: bill-service-config - secretRef: - name: secret-common + name: phonebill-common-secret - secretRef: - name: secret-bill-service + name: bill-service-db-secret resources: requests: - cpu: 256m - memory: 256Mi + 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 + cpu: "1024m" + memory: "1024Mi" livenessProbe: httpGet: path: /actuator/health/liveness port: 8082 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8082 initialDelaySeconds: 30 periodSeconds: 10 - failureThreshold: 3 \ No newline at end of file + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/deployment/cicd/kustomize/base/bill-service/secret-bill-service.yaml b/deployment/cicd/kustomize/base/bill-service/secret-bill-service.yaml index caaa7cf..ebe83d7 100644 --- a/deployment/cicd/kustomize/base/bill-service/secret-bill-service.yaml +++ b/deployment/cicd/kustomize/base/bill-service/secret-bill-service.yaml @@ -1,10 +1,12 @@ apiVersion: v1 kind: Secret metadata: - name: secret-bill-service + name: bill-service-db-secret + + labels: + app: bill-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "bill-inquiry-postgres-dev-postgresql" - DB_NAME: "bill_inquiry_db" - DB_USERNAME: "bill_inquiry_user" - DB_PASSWORD: "BillUser2025@" + DB_USERNAME: "unicorn" + DB_PASSWORD: "P@ssw0rd$" diff --git a/deployment/cicd/kustomize/base/bill-service/service.yaml b/deployment/cicd/kustomize/base/bill-service/service.yaml index 1e6373b..e491d02 100644 --- a/deployment/cicd/kustomize/base/bill-service/service.yaml +++ b/deployment/cicd/kustomize/base/bill-service/service.yaml @@ -2,10 +2,16 @@ apiVersion: v1 kind: Service metadata: name: bill-service + + labels: + app: bill-service + app.kubernetes.io/part-of: phonebill spec: + type: ClusterIP selector: app: bill-service ports: - - port: 80 + - name: http + port: 8082 targetPort: 8082 - type: ClusterIP \ No newline at end of file + protocol: TCP diff --git a/deployment/cicd/kustomize/base/common/cm-common.yaml b/deployment/cicd/kustomize/base/common/cm-common.yaml index 3c16172..87fe3f6 100644 --- a/deployment/cicd/kustomize/base/common/cm-common.yaml +++ b/deployment/cicd/kustomize/base/common/cm-common.yaml @@ -1,11 +1,14 @@ apiVersion: v1 kind: ConfigMap metadata: - name: cm-common + name: phonebill-common-config + + labels: + app.kubernetes.io/part-of: phonebill 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" + SPRING_PROFILES_ACTIVE: "prod" + REDIS_HOST: "cache-redis-master" REDIS_PORT: "6379" - SPRING_PROFILES_ACTIVE: "dev" - DDL_AUTO: "update" \ No newline at end of file + CORS_ALLOWED_ORIGINS: "http://localhost:3000,http://phonebill.72.155.72.236.nip.io" + SHOW_SQL: "false" + DDL_AUTO: "none" diff --git a/deployment/cicd/kustomize/base/common/ingress.yaml b/deployment/cicd/kustomize/base/common/ingress.yaml index da97115..9424e50 100644 --- a/deployment/cicd/kustomize/base/common/ingress.yaml +++ b/deployment/cicd/kustomize/base/common/ingress.yaml @@ -1,49 +1,25 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: phonebill + name: phonebill-ingress + + labels: + app.kubernetes.io/part-of: phonebill annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/ssl-redirect: "false" + nginx.ingress.kubernetes.io/proxy-body-size: "50m" + nginx.ingress.kubernetes.io/proxy-read-timeout: "60" + nginx.ingress.kubernetes.io/proxy-send-timeout: "60" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "60" spec: ingressClassName: nginx rules: - - host: phonebill-dg0500-api.20.214.196.128.nip.io + - host: phonebill-api.72.155.72.236.nip.io http: paths: - - path: /api/v1/auth + - path: / pathType: Prefix backend: service: - name: user-service + name: api-gateway 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 - + number: 8080 diff --git a/deployment/cicd/kustomize/base/common/secret-common.yaml b/deployment/cicd/kustomize/base/common/secret-common.yaml index 53795ab..a23a737 100644 --- a/deployment/cicd/kustomize/base/common/secret-common.yaml +++ b/deployment/cicd/kustomize/base/common/secret-common.yaml @@ -1,9 +1,13 @@ apiVersion: v1 kind: Secret metadata: - name: secret-common + name: phonebill-common-secret + + labels: + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==" - REDIS_HOST: "redis-cache-dev-master" - REDIS_PASSWORD: "Redis2025Dev@" \ No newline at end of file + # JWT Secret (최소 256비트 이상, HS256 알고리즘용) + JWT_SECRET: "EK1ZV7vROOXREXbYe/BCISdQq0Yklk9JtoA2v88ux1DBDc0bDGiRRxHeDSb7GHkDP9IUYHMVsBi4/1rS4OhfRg==" + # Redis 비밀번호 (비밀번호 없는 경우 빈 값) + REDIS_PASSWORD: "P@ssw0rd$" diff --git a/deployment/cicd/kustomize/base/common/secret-imagepull.yaml b/deployment/cicd/kustomize/base/common/secret-imagepull.yaml deleted file mode 100644 index 6bd576e..0000000 --- a/deployment/cicd/kustomize/base/common/secret-imagepull.yaml +++ /dev/null @@ -1,16 +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=" - } - } - } \ No newline at end of file diff --git a/deployment/cicd/kustomize/base/kos-mock/cm-kos-mock.yaml b/deployment/cicd/kustomize/base/kos-mock/cm-kos-mock.yaml index 3e55476..fc3f37a 100644 --- a/deployment/cicd/kustomize/base/kos-mock/cm-kos-mock.yaml +++ b/deployment/cicd/kustomize/base/kos-mock/cm-kos-mock.yaml @@ -1,6 +1,10 @@ apiVersion: v1 kind: ConfigMap metadata: - name: cm-kos-mock + name: kos-mock-config + + labels: + app: kos-mock + app.kubernetes.io/part-of: phonebill data: - SERVER_PORT: "8084" \ No newline at end of file + SERVER_PORT: "8084" diff --git a/deployment/cicd/kustomize/base/kos-mock/deployment.yaml b/deployment/cicd/kustomize/base/kos-mock/deployment.yaml index bd588f4..6912cfd 100644 --- a/deployment/cicd/kustomize/base/kos-mock/deployment.yaml +++ b/deployment/cicd/kustomize/base/kos-mock/deployment.yaml @@ -2,6 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: name: kos-mock + + labels: + app: kos-mock + app.kubernetes.io/part-of: phonebill spec: replicas: 1 selector: @@ -12,46 +16,40 @@ spec: labels: app: kos-mock spec: - imagePullSecrets: - - name: phonebill containers: - name: kos-mock - image: acrdigitalgarage01.azurecr.io/phonebill/kos-mock:latest + image: docker.io/hiondal/kos-mock:latest imagePullPolicy: Always ports: - containerPort: 8084 + name: http envFrom: - configMapRef: - name: cm-common + name: phonebill-common-config - configMapRef: - name: cm-kos-mock + name: kos-mock-config - secretRef: - name: secret-common + name: phonebill-common-secret resources: requests: - cpu: 256m - memory: 256Mi + 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 + cpu: "1024m" + memory: "1024Mi" livenessProbe: httpGet: path: /actuator/health/liveness port: 8084 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8084 initialDelaySeconds: 30 periodSeconds: 10 - failureThreshold: 3 \ No newline at end of file + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/deployment/cicd/kustomize/base/kos-mock/service.yaml b/deployment/cicd/kustomize/base/kos-mock/service.yaml index fdb5336..16d71ab 100644 --- a/deployment/cicd/kustomize/base/kos-mock/service.yaml +++ b/deployment/cicd/kustomize/base/kos-mock/service.yaml @@ -2,10 +2,16 @@ apiVersion: v1 kind: Service metadata: name: kos-mock + + labels: + app: kos-mock + app.kubernetes.io/part-of: phonebill spec: + type: ClusterIP selector: app: kos-mock ports: - - port: 80 + - name: http + port: 8084 targetPort: 8084 - type: ClusterIP \ No newline at end of file + protocol: TCP diff --git a/deployment/cicd/kustomize/base/kustomization.yaml b/deployment/cicd/kustomize/base/kustomization.yaml index 0da6c87..67327a9 100644 --- a/deployment/cicd/kustomize/base/kustomization.yaml +++ b/deployment/cicd/kustomize/base/kustomization.yaml @@ -8,7 +8,6 @@ resources: # Common resources - common/cm-common.yaml - common/secret-common.yaml - - common/secret-imagepull.yaml - common/ingress.yaml # api-gateway @@ -44,13 +43,13 @@ commonLabels: version: v1 images: - - name: acrdigitalgarage01.azurecr.io/phonebill/api-gateway + - name: docker.io/hiondal/api-gateway newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/user-service + - name: docker.io/hiondal/user-service newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/bill-service + - name: docker.io/hiondal/bill-service newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/product-service + - name: docker.io/hiondal/product-service + newTag: latest + - name: docker.io/hiondal/kos-mock newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/kos-mock - newTag: latest \ No newline at end of file diff --git a/deployment/cicd/kustomize/base/product-service/cm-product-service.yaml b/deployment/cicd/kustomize/base/product-service/cm-product-service.yaml index 5a3893d..2c6db72 100644 --- a/deployment/cicd/kustomize/base/product-service/cm-product-service.yaml +++ b/deployment/cicd/kustomize/base/product-service/cm-product-service.yaml @@ -1,10 +1,16 @@ apiVersion: v1 kind: ConfigMap metadata: - name: cm-product-service + name: product-service-config + + labels: + app: product-service + app.kubernetes.io/part-of: phonebill data: SERVER_PORT: "8083" - DB_KIND: "postgresql" + DB_HOST: "change-postgresql" DB_PORT: "5432" - KOS_BASE_URL: "http://kos-mock" - REDIS_DATABASE: "2" \ No newline at end of file + DB_NAME: "changedb" + REDIS_DATABASE: "2" + KOS_BASE_URL: "http://kos-mock:8084" + DDL_AUTO: "update" diff --git a/deployment/cicd/kustomize/base/product-service/deployment.yaml b/deployment/cicd/kustomize/base/product-service/deployment.yaml index 0b463a3..b4ff532 100644 --- a/deployment/cicd/kustomize/base/product-service/deployment.yaml +++ b/deployment/cicd/kustomize/base/product-service/deployment.yaml @@ -2,6 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: name: product-service + + labels: + app: product-service + app.kubernetes.io/part-of: phonebill spec: replicas: 1 selector: @@ -12,48 +16,42 @@ spec: labels: app: product-service spec: - imagePullSecrets: - - name: phonebill containers: - name: product-service - image: acrdigitalgarage01.azurecr.io/phonebill/product-service:latest + image: docker.io/hiondal/product-service:latest imagePullPolicy: Always ports: - containerPort: 8083 + name: http envFrom: - configMapRef: - name: cm-common + name: phonebill-common-config - configMapRef: - name: cm-product-service + name: product-service-config - secretRef: - name: secret-common + name: phonebill-common-secret - secretRef: - name: secret-product-service + name: product-service-db-secret resources: requests: - cpu: 256m - memory: 256Mi + 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 + cpu: "1024m" + memory: "1024Mi" livenessProbe: httpGet: path: /actuator/health/liveness port: 8083 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8083 initialDelaySeconds: 30 periodSeconds: 10 - failureThreshold: 3 \ No newline at end of file + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/deployment/cicd/kustomize/base/product-service/secret-product-service.yaml b/deployment/cicd/kustomize/base/product-service/secret-product-service.yaml index e773ec9..e21ad4b 100644 --- a/deployment/cicd/kustomize/base/product-service/secret-product-service.yaml +++ b/deployment/cicd/kustomize/base/product-service/secret-product-service.yaml @@ -1,10 +1,12 @@ apiVersion: v1 kind: Secret metadata: - name: secret-product-service + name: product-service-db-secret + + labels: + app: product-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "product-change-postgres-dev-postgresql" - DB_NAME: "product_change_db" - DB_USERNAME: "product_change_user" - DB_PASSWORD: "ProductUser2025@" \ No newline at end of file + DB_USERNAME: "unicorn" + DB_PASSWORD: "P@ssw0rd$" diff --git a/deployment/cicd/kustomize/base/product-service/service.yaml b/deployment/cicd/kustomize/base/product-service/service.yaml index b784a5d..4a77c8b 100644 --- a/deployment/cicd/kustomize/base/product-service/service.yaml +++ b/deployment/cicd/kustomize/base/product-service/service.yaml @@ -2,10 +2,16 @@ apiVersion: v1 kind: Service metadata: name: product-service + + labels: + app: product-service + app.kubernetes.io/part-of: phonebill spec: + type: ClusterIP selector: app: product-service ports: - - port: 80 + - name: http + port: 8083 targetPort: 8083 - type: ClusterIP \ No newline at end of file + protocol: TCP diff --git a/deployment/cicd/kustomize/base/user-service/cm-user-service.yaml b/deployment/cicd/kustomize/base/user-service/cm-user-service.yaml index 4031913..ac4b8e4 100644 --- a/deployment/cicd/kustomize/base/user-service/cm-user-service.yaml +++ b/deployment/cicd/kustomize/base/user-service/cm-user-service.yaml @@ -1,11 +1,15 @@ apiVersion: v1 kind: ConfigMap metadata: - name: cm-user-service + name: user-service-config + + labels: + app: user-service + app.kubernetes.io/part-of: phonebill data: SERVER_PORT: "8081" - DB_KIND: "postgresql" + DB_HOST: "auth-postgresql" DB_PORT: "5432" - DDL_AUTO: "update" + DB_NAME: "authdb" REDIS_DATABASE: "0" - SHOW_SQL: "true" \ No newline at end of file + DDL_AUTO: "update" diff --git a/deployment/cicd/kustomize/base/user-service/deployment.yaml b/deployment/cicd/kustomize/base/user-service/deployment.yaml index 2dbd2d4..7f39e0b 100644 --- a/deployment/cicd/kustomize/base/user-service/deployment.yaml +++ b/deployment/cicd/kustomize/base/user-service/deployment.yaml @@ -2,6 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: name: user-service + + labels: + app: user-service + app.kubernetes.io/part-of: phonebill spec: replicas: 1 selector: @@ -12,48 +16,42 @@ spec: labels: app: user-service spec: - imagePullSecrets: - - name: phonebill containers: - name: user-service - image: acrdigitalgarage01.azurecr.io/phonebill/user-service:latest + image: docker.io/hiondal/user-service:latest imagePullPolicy: Always ports: - containerPort: 8081 + name: http envFrom: - configMapRef: - name: cm-common + name: phonebill-common-config - configMapRef: - name: cm-user-service + name: user-service-config - secretRef: - name: secret-common + name: phonebill-common-secret - secretRef: - name: secret-user-service + name: user-service-db-secret resources: requests: - cpu: 256m - memory: 256Mi + 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 + cpu: "1024m" + memory: "1024Mi" livenessProbe: httpGet: path: /actuator/health/liveness port: 8081 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8081 initialDelaySeconds: 30 periodSeconds: 10 - failureThreshold: 3 \ No newline at end of file + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/deployment/cicd/kustomize/base/user-service/secret-user-service.yaml b/deployment/cicd/kustomize/base/user-service/secret-user-service.yaml index 8424423..a75e1f9 100644 --- a/deployment/cicd/kustomize/base/user-service/secret-user-service.yaml +++ b/deployment/cicd/kustomize/base/user-service/secret-user-service.yaml @@ -1,10 +1,12 @@ apiVersion: v1 kind: Secret metadata: - name: secret-user-service + name: user-service-db-secret + + labels: + app: user-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "auth-postgres-dev-postgresql" - DB_NAME: "phonebill_auth" - DB_USERNAME: "auth_user" - DB_PASSWORD: "AuthUser2025@" \ No newline at end of file + DB_USERNAME: "unicorn" + DB_PASSWORD: "P@ssw0rd$" diff --git a/deployment/cicd/kustomize/base/user-service/service.yaml b/deployment/cicd/kustomize/base/user-service/service.yaml index c9fb9cf..029298f 100644 --- a/deployment/cicd/kustomize/base/user-service/service.yaml +++ b/deployment/cicd/kustomize/base/user-service/service.yaml @@ -2,10 +2,16 @@ apiVersion: v1 kind: Service metadata: name: user-service + + labels: + app: user-service + app.kubernetes.io/part-of: phonebill spec: + type: ClusterIP selector: app: user-service ports: - - port: 80 + - name: http + port: 8081 targetPort: 8081 - type: ClusterIP \ No newline at end of file + protocol: TCP diff --git a/deployment/cicd/kustomize/overlays/dev/cm-common-patch.yaml b/deployment/cicd/kustomize/overlays/dev/cm-common-patch.yaml index 3c16172..6336378 100644 --- a/deployment/cicd/kustomize/overlays/dev/cm-common-patch.yaml +++ b/deployment/cicd/kustomize/overlays/dev/cm-common-patch.yaml @@ -1,11 +1,13 @@ apiVersion: v1 kind: ConfigMap metadata: - name: cm-common + name: phonebill-common-config + labels: + app.kubernetes.io/part-of: phonebill 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" \ No newline at end of file + REDIS_HOST: "cache-redis-master" + REDIS_PORT: "6379" + CORS_ALLOWED_ORIGINS: "http://localhost:3000,http://phonebill.72.155.72.236.nip.io" + SHOW_SQL: "true" + DDL_AUTO: "update" diff --git a/deployment/cicd/kustomize/overlays/dev/deployment-api-gateway-patch.yaml b/deployment/cicd/kustomize/overlays/dev/deployment-api-gateway-patch.yaml index c5278b1..a9eb12e 100644 --- a/deployment/cicd/kustomize/overlays/dev/deployment-api-gateway-patch.yaml +++ b/deployment/cicd/kustomize/overlays/dev/deployment-api-gateway-patch.yaml @@ -14,4 +14,4 @@ spec: memory: "256Mi" limits: cpu: "1024m" - memory: "1024Mi" \ No newline at end of file + memory: "1024Mi" diff --git a/deployment/cicd/kustomize/overlays/dev/deployment-bill-service-patch.yaml b/deployment/cicd/kustomize/overlays/dev/deployment-bill-service-patch.yaml index c67ce77..c17f70a 100644 --- a/deployment/cicd/kustomize/overlays/dev/deployment-bill-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/dev/deployment-bill-service-patch.yaml @@ -14,4 +14,4 @@ spec: memory: "256Mi" limits: cpu: "1024m" - memory: "1024Mi" \ No newline at end of file + memory: "1024Mi" diff --git a/deployment/cicd/kustomize/overlays/dev/deployment-kos-mock-patch.yaml b/deployment/cicd/kustomize/overlays/dev/deployment-kos-mock-patch.yaml index 21a137c..2c36df1 100644 --- a/deployment/cicd/kustomize/overlays/dev/deployment-kos-mock-patch.yaml +++ b/deployment/cicd/kustomize/overlays/dev/deployment-kos-mock-patch.yaml @@ -14,4 +14,4 @@ spec: memory: "256Mi" limits: cpu: "1024m" - memory: "1024Mi" \ No newline at end of file + memory: "1024Mi" diff --git a/deployment/cicd/kustomize/overlays/dev/deployment-product-service-patch.yaml b/deployment/cicd/kustomize/overlays/dev/deployment-product-service-patch.yaml index 7290636..be48b62 100644 --- a/deployment/cicd/kustomize/overlays/dev/deployment-product-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/dev/deployment-product-service-patch.yaml @@ -14,4 +14,4 @@ spec: memory: "256Mi" limits: cpu: "1024m" - memory: "1024Mi" \ No newline at end of file + memory: "1024Mi" diff --git a/deployment/cicd/kustomize/overlays/dev/deployment-user-service-patch.yaml b/deployment/cicd/kustomize/overlays/dev/deployment-user-service-patch.yaml index c2fa8d4..903f0ad 100644 --- a/deployment/cicd/kustomize/overlays/dev/deployment-user-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/dev/deployment-user-service-patch.yaml @@ -14,4 +14,4 @@ spec: memory: "256Mi" limits: cpu: "1024m" - memory: "1024Mi" \ No newline at end of file + memory: "1024Mi" diff --git a/deployment/cicd/kustomize/overlays/dev/ingress-patch.yaml b/deployment/cicd/kustomize/overlays/dev/ingress-patch.yaml index 8fb360e..30cd224 100644 --- a/deployment/cicd/kustomize/overlays/dev/ingress-patch.yaml +++ b/deployment/cicd/kustomize/overlays/dev/ingress-patch.yaml @@ -1,48 +1,25 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: phonebill + name: phonebill-ingress + labels: + app.kubernetes.io/part-of: phonebill annotations: - kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/proxy-body-size: "50m" + nginx.ingress.kubernetes.io/proxy-read-timeout: "60" + nginx.ingress.kubernetes.io/proxy-send-timeout: "60" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "60" nginx.ingress.kubernetes.io/ssl-redirect: "false" spec: ingressClassName: nginx rules: - - host: phonebill-dg0500-api.20.214.196.128.nip.io + - host: phonebill-api.72.155.72.236.nip.io http: paths: - - path: /api/v1/auth + - path: / pathType: Prefix backend: service: - name: user-service + name: api-gateway 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 \ No newline at end of file + number: 8080 diff --git a/deployment/cicd/kustomize/overlays/dev/kustomization.yaml b/deployment/cicd/kustomize/overlays/dev/kustomization.yaml index 974e023..7e7e5d0 100644 --- a/deployment/cicd/kustomize/overlays/dev/kustomization.yaml +++ b/deployment/cicd/kustomize/overlays/dev/kustomization.yaml @@ -1,16 +1,27 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -namespace: phonebill-dg0500 +namespace: phonebill resources: - ../../base patches: + # Common patches - path: cm-common-patch.yaml target: kind: ConfigMap - name: cm-common + name: phonebill-common-config + - path: secret-common-patch.yaml + target: + kind: Secret + name: phonebill-common-secret + - path: ingress-patch.yaml + target: + kind: Ingress + name: phonebill-ingress + + # Deployment patches - path: deployment-api-gateway-patch.yaml target: kind: Deployment @@ -31,35 +42,29 @@ patches: 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 + + # Service Secret patches - path: secret-user-service-patch.yaml target: kind: Secret - name: secret-user-service + name: user-service-db-secret - path: secret-bill-service-patch.yaml target: kind: Secret - name: secret-bill-service + name: bill-service-db-secret - path: secret-product-service-patch.yaml target: kind: Secret - name: secret-product-service + name: product-service-db-secret images: - - name: acrdigitalgarage01.azurecr.io/phonebill/api-gateway + - name: docker.io/hiondal/api-gateway newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/user-service + - name: docker.io/hiondal/user-service newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/bill-service + - name: docker.io/hiondal/bill-service newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/product-service + - name: docker.io/hiondal/product-service + newTag: latest + - name: docker.io/hiondal/kos-mock newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/kos-mock - newTag: latest \ No newline at end of file diff --git a/deployment/cicd/kustomize/overlays/dev/secret-bill-service-patch.yaml b/deployment/cicd/kustomize/overlays/dev/secret-bill-service-patch.yaml index caaa7cf..b69a9d5 100644 --- a/deployment/cicd/kustomize/overlays/dev/secret-bill-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/dev/secret-bill-service-patch.yaml @@ -1,10 +1,11 @@ apiVersion: v1 kind: Secret metadata: - name: secret-bill-service + name: bill-service-db-secret + labels: + app: bill-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "bill-inquiry-postgres-dev-postgresql" - DB_NAME: "bill_inquiry_db" - DB_USERNAME: "bill_inquiry_user" - DB_PASSWORD: "BillUser2025@" + DB_USERNAME: "unicorn" + DB_PASSWORD: "P@ssw0rd$" diff --git a/deployment/cicd/kustomize/overlays/dev/secret-common-patch.yaml b/deployment/cicd/kustomize/overlays/dev/secret-common-patch.yaml index 53795ab..20bf82c 100644 --- a/deployment/cicd/kustomize/overlays/dev/secret-common-patch.yaml +++ b/deployment/cicd/kustomize/overlays/dev/secret-common-patch.yaml @@ -1,9 +1,10 @@ apiVersion: v1 kind: Secret metadata: - name: secret-common + name: phonebill-common-secret + labels: + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==" - REDIS_HOST: "redis-cache-dev-master" - REDIS_PASSWORD: "Redis2025Dev@" \ No newline at end of file + JWT_SECRET: "EK1ZV7vROOXREXbYe/BCISdQq0Yklk9JtoA2v88ux1DBDc0bDGiRRxHeDSb7GHkDP9IUYHMVsBi4/1rS4OhfRg==" + REDIS_PASSWORD: "P@ssw0rd$" diff --git a/deployment/cicd/kustomize/overlays/dev/secret-product-service-patch.yaml b/deployment/cicd/kustomize/overlays/dev/secret-product-service-patch.yaml index e773ec9..3ecf261 100644 --- a/deployment/cicd/kustomize/overlays/dev/secret-product-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/dev/secret-product-service-patch.yaml @@ -1,10 +1,11 @@ apiVersion: v1 kind: Secret metadata: - name: secret-product-service + name: product-service-db-secret + labels: + app: product-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "product-change-postgres-dev-postgresql" - DB_NAME: "product_change_db" - DB_USERNAME: "product_change_user" - DB_PASSWORD: "ProductUser2025@" \ No newline at end of file + DB_USERNAME: "unicorn" + DB_PASSWORD: "P@ssw0rd$" diff --git a/deployment/cicd/kustomize/overlays/dev/secret-user-service-patch.yaml b/deployment/cicd/kustomize/overlays/dev/secret-user-service-patch.yaml index 8424423..f0f984c 100644 --- a/deployment/cicd/kustomize/overlays/dev/secret-user-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/dev/secret-user-service-patch.yaml @@ -1,10 +1,11 @@ apiVersion: v1 kind: Secret metadata: - name: secret-user-service + name: user-service-db-secret + labels: + app: user-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "auth-postgres-dev-postgresql" - DB_NAME: "phonebill_auth" - DB_USERNAME: "auth_user" - DB_PASSWORD: "AuthUser2025@" \ No newline at end of file + DB_USERNAME: "unicorn" + DB_PASSWORD: "P@ssw0rd$" diff --git a/deployment/cicd/kustomize/overlays/prod/cm-common-patch.yaml b/deployment/cicd/kustomize/overlays/prod/cm-common-patch.yaml index eea116d..316f727 100644 --- a/deployment/cicd/kustomize/overlays/prod/cm-common-patch.yaml +++ b/deployment/cicd/kustomize/overlays/prod/cm-common-patch.yaml @@ -1,11 +1,13 @@ apiVersion: v1 kind: ConfigMap metadata: - name: cm-common + name: phonebill-common-config + labels: + app.kubernetes.io/part-of: phonebill 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: "3600000" - JWT_REFRESH_TOKEN_VALIDITY: "43200000" - REDIS_PORT: "6379" SPRING_PROFILES_ACTIVE: "prod" - DDL_AUTO: "validate" \ No newline at end of file + REDIS_HOST: "cache-redis-master" + REDIS_PORT: "6379" + CORS_ALLOWED_ORIGINS: "https://phonebill.example.com" + SHOW_SQL: "false" + DDL_AUTO: "validate" diff --git a/deployment/cicd/kustomize/overlays/prod/ingress-patch.yaml b/deployment/cicd/kustomize/overlays/prod/ingress-patch.yaml index 95ee1a4..551c926 100644 --- a/deployment/cicd/kustomize/overlays/prod/ingress-patch.yaml +++ b/deployment/cicd/kustomize/overlays/prod/ingress-patch.yaml @@ -1,9 +1,14 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: phonebill + name: phonebill-ingress + labels: + app.kubernetes.io/part-of: phonebill annotations: - kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/proxy-body-size: "50m" + nginx.ingress.kubernetes.io/proxy-read-timeout: "60" + nginx.ingress.kubernetes.io/proxy-send-timeout: "60" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "60" nginx.ingress.kubernetes.io/ssl-redirect: "true" cert-manager.io/cluster-issuer: "letsencrypt-prod" spec: @@ -16,38 +21,10 @@ spec: - host: phonebill.example.com http: paths: - - path: /api/v1/auth + - path: / pathType: Prefix backend: service: - name: user-service + name: api-gateway 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 \ No newline at end of file + number: 8080 diff --git a/deployment/cicd/kustomize/overlays/prod/kustomization.yaml b/deployment/cicd/kustomize/overlays/prod/kustomization.yaml index 974e023..28b0863 100644 --- a/deployment/cicd/kustomize/overlays/prod/kustomization.yaml +++ b/deployment/cicd/kustomize/overlays/prod/kustomization.yaml @@ -1,16 +1,27 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -namespace: phonebill-dg0500 +namespace: phonebill-prod resources: - ../../base patches: + # Common patches - path: cm-common-patch.yaml target: kind: ConfigMap - name: cm-common + name: phonebill-common-config + - path: secret-common-patch.yaml + target: + kind: Secret + name: phonebill-common-secret + - path: ingress-patch.yaml + target: + kind: Ingress + name: phonebill-ingress + + # Deployment patches - path: deployment-api-gateway-patch.yaml target: kind: Deployment @@ -31,35 +42,29 @@ patches: 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 + + # Service Secret patches - path: secret-user-service-patch.yaml target: kind: Secret - name: secret-user-service + name: user-service-db-secret - path: secret-bill-service-patch.yaml target: kind: Secret - name: secret-bill-service + name: bill-service-db-secret - path: secret-product-service-patch.yaml target: kind: Secret - name: secret-product-service + name: product-service-db-secret images: - - name: acrdigitalgarage01.azurecr.io/phonebill/api-gateway + - name: docker.io/hiondal/api-gateway newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/user-service + - name: docker.io/hiondal/user-service newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/bill-service + - name: docker.io/hiondal/bill-service newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/product-service + - name: docker.io/hiondal/product-service + newTag: latest + - name: docker.io/hiondal/kos-mock newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/kos-mock - newTag: latest \ No newline at end of file diff --git a/deployment/cicd/kustomize/overlays/prod/secret-bill-service-patch.yaml b/deployment/cicd/kustomize/overlays/prod/secret-bill-service-patch.yaml index caaa7cf..3d64645 100644 --- a/deployment/cicd/kustomize/overlays/prod/secret-bill-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/prod/secret-bill-service-patch.yaml @@ -1,10 +1,11 @@ apiVersion: v1 kind: Secret metadata: - name: secret-bill-service + name: bill-service-db-secret + labels: + app: bill-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "bill-inquiry-postgres-dev-postgresql" - DB_NAME: "bill_inquiry_db" - DB_USERNAME: "bill_inquiry_user" - DB_PASSWORD: "BillUser2025@" + DB_USERNAME: "unicorn" + DB_PASSWORD: "PROD_DB_PASSWORD" diff --git a/deployment/cicd/kustomize/overlays/prod/secret-common-patch.yaml b/deployment/cicd/kustomize/overlays/prod/secret-common-patch.yaml index 53795ab..c2e7edb 100644 --- a/deployment/cicd/kustomize/overlays/prod/secret-common-patch.yaml +++ b/deployment/cicd/kustomize/overlays/prod/secret-common-patch.yaml @@ -1,9 +1,10 @@ apiVersion: v1 kind: Secret metadata: - name: secret-common + name: phonebill-common-secret + labels: + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==" - REDIS_HOST: "redis-cache-dev-master" - REDIS_PASSWORD: "Redis2025Dev@" \ No newline at end of file + JWT_SECRET: "PROD_JWT_SECRET_REPLACE_WITH_SECURE_VALUE" + REDIS_PASSWORD: "PROD_REDIS_PASSWORD" diff --git a/deployment/cicd/kustomize/overlays/prod/secret-product-service-patch.yaml b/deployment/cicd/kustomize/overlays/prod/secret-product-service-patch.yaml index e773ec9..d571924 100644 --- a/deployment/cicd/kustomize/overlays/prod/secret-product-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/prod/secret-product-service-patch.yaml @@ -1,10 +1,11 @@ apiVersion: v1 kind: Secret metadata: - name: secret-product-service + name: product-service-db-secret + labels: + app: product-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "product-change-postgres-dev-postgresql" - DB_NAME: "product_change_db" - DB_USERNAME: "product_change_user" - DB_PASSWORD: "ProductUser2025@" \ No newline at end of file + DB_USERNAME: "unicorn" + DB_PASSWORD: "PROD_DB_PASSWORD" diff --git a/deployment/cicd/kustomize/overlays/prod/secret-user-service-patch.yaml b/deployment/cicd/kustomize/overlays/prod/secret-user-service-patch.yaml index 8424423..2cf1d3e 100644 --- a/deployment/cicd/kustomize/overlays/prod/secret-user-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/prod/secret-user-service-patch.yaml @@ -1,10 +1,11 @@ apiVersion: v1 kind: Secret metadata: - name: secret-user-service + name: user-service-db-secret + labels: + app: user-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "auth-postgres-dev-postgresql" - DB_NAME: "phonebill_auth" - DB_USERNAME: "auth_user" - DB_PASSWORD: "AuthUser2025@" \ No newline at end of file + DB_USERNAME: "unicorn" + DB_PASSWORD: "PROD_DB_PASSWORD" diff --git a/deployment/cicd/kustomize/overlays/staging/cm-common-patch.yaml b/deployment/cicd/kustomize/overlays/staging/cm-common-patch.yaml index d3604e0..97e1bda 100644 --- a/deployment/cicd/kustomize/overlays/staging/cm-common-patch.yaml +++ b/deployment/cicd/kustomize/overlays/staging/cm-common-patch.yaml @@ -1,11 +1,13 @@ apiVersion: v1 kind: ConfigMap metadata: - name: cm-common + name: phonebill-common-config + labels: + app.kubernetes.io/part-of: phonebill 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: "staging" - DDL_AUTO: "validate" \ No newline at end of file + REDIS_HOST: "cache-redis-master" + REDIS_PORT: "6379" + CORS_ALLOWED_ORIGINS: "https://phonebill-staging.example.com" + SHOW_SQL: "false" + DDL_AUTO: "validate" diff --git a/deployment/cicd/kustomize/overlays/staging/deployment-api-gateway-patch.yaml b/deployment/cicd/kustomize/overlays/staging/deployment-api-gateway-patch.yaml index b8f0f64..dac2bcf 100644 --- a/deployment/cicd/kustomize/overlays/staging/deployment-api-gateway-patch.yaml +++ b/deployment/cicd/kustomize/overlays/staging/deployment-api-gateway-patch.yaml @@ -14,4 +14,4 @@ spec: memory: "512Mi" limits: cpu: "2048m" - memory: "2048Mi" \ No newline at end of file + memory: "2048Mi" diff --git a/deployment/cicd/kustomize/overlays/staging/ingress-patch.yaml b/deployment/cicd/kustomize/overlays/staging/ingress-patch.yaml index ba8d07b..7cc3bf5 100644 --- a/deployment/cicd/kustomize/overlays/staging/ingress-patch.yaml +++ b/deployment/cicd/kustomize/overlays/staging/ingress-patch.yaml @@ -1,11 +1,16 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: phonebill + name: phonebill-ingress + labels: + app.kubernetes.io/part-of: phonebill annotations: - kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/proxy-body-size: "50m" + nginx.ingress.kubernetes.io/proxy-read-timeout: "60" + nginx.ingress.kubernetes.io/proxy-send-timeout: "60" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "60" nginx.ingress.kubernetes.io/ssl-redirect: "true" - cert-manager.io/cluster-issuer: "letsencrypt-prod" + cert-manager.io/cluster-issuer: "letsencrypt-staging" spec: ingressClassName: nginx tls: @@ -16,38 +21,10 @@ spec: - host: phonebill-staging.example.com http: paths: - - path: /api/v1/auth + - path: / pathType: Prefix backend: service: - name: user-service + name: api-gateway 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 \ No newline at end of file + number: 8080 diff --git a/deployment/cicd/kustomize/overlays/staging/kustomization.yaml b/deployment/cicd/kustomize/overlays/staging/kustomization.yaml index 974e023..bb8ae59 100644 --- a/deployment/cicd/kustomize/overlays/staging/kustomization.yaml +++ b/deployment/cicd/kustomize/overlays/staging/kustomization.yaml @@ -1,16 +1,27 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -namespace: phonebill-dg0500 +namespace: phonebill-staging resources: - ../../base patches: + # Common patches - path: cm-common-patch.yaml target: kind: ConfigMap - name: cm-common + name: phonebill-common-config + - path: secret-common-patch.yaml + target: + kind: Secret + name: phonebill-common-secret + - path: ingress-patch.yaml + target: + kind: Ingress + name: phonebill-ingress + + # Deployment patches - path: deployment-api-gateway-patch.yaml target: kind: Deployment @@ -31,35 +42,29 @@ patches: 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 + + # Service Secret patches - path: secret-user-service-patch.yaml target: kind: Secret - name: secret-user-service + name: user-service-db-secret - path: secret-bill-service-patch.yaml target: kind: Secret - name: secret-bill-service + name: bill-service-db-secret - path: secret-product-service-patch.yaml target: kind: Secret - name: secret-product-service + name: product-service-db-secret images: - - name: acrdigitalgarage01.azurecr.io/phonebill/api-gateway + - name: docker.io/hiondal/api-gateway newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/user-service + - name: docker.io/hiondal/user-service newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/bill-service + - name: docker.io/hiondal/bill-service newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/product-service + - name: docker.io/hiondal/product-service + newTag: latest + - name: docker.io/hiondal/kos-mock newTag: latest - - name: acrdigitalgarage01.azurecr.io/phonebill/kos-mock - newTag: latest \ No newline at end of file diff --git a/deployment/cicd/kustomize/overlays/staging/secret-bill-service-patch.yaml b/deployment/cicd/kustomize/overlays/staging/secret-bill-service-patch.yaml index caaa7cf..265eb46 100644 --- a/deployment/cicd/kustomize/overlays/staging/secret-bill-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/staging/secret-bill-service-patch.yaml @@ -1,10 +1,11 @@ apiVersion: v1 kind: Secret metadata: - name: secret-bill-service + name: bill-service-db-secret + labels: + app: bill-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "bill-inquiry-postgres-dev-postgresql" - DB_NAME: "bill_inquiry_db" - DB_USERNAME: "bill_inquiry_user" - DB_PASSWORD: "BillUser2025@" + DB_USERNAME: "unicorn" + DB_PASSWORD: "STAGING_DB_PASSWORD" diff --git a/deployment/cicd/kustomize/overlays/staging/secret-common-patch.yaml b/deployment/cicd/kustomize/overlays/staging/secret-common-patch.yaml index 53795ab..9143b04 100644 --- a/deployment/cicd/kustomize/overlays/staging/secret-common-patch.yaml +++ b/deployment/cicd/kustomize/overlays/staging/secret-common-patch.yaml @@ -1,9 +1,10 @@ apiVersion: v1 kind: Secret metadata: - name: secret-common + name: phonebill-common-secret + labels: + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - JWT_SECRET: "nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==" - REDIS_HOST: "redis-cache-dev-master" - REDIS_PASSWORD: "Redis2025Dev@" \ No newline at end of file + JWT_SECRET: "STAGING_JWT_SECRET_REPLACE_WITH_SECURE_VALUE" + REDIS_PASSWORD: "STAGING_REDIS_PASSWORD" diff --git a/deployment/cicd/kustomize/overlays/staging/secret-product-service-patch.yaml b/deployment/cicd/kustomize/overlays/staging/secret-product-service-patch.yaml index e773ec9..c4a5141 100644 --- a/deployment/cicd/kustomize/overlays/staging/secret-product-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/staging/secret-product-service-patch.yaml @@ -1,10 +1,11 @@ apiVersion: v1 kind: Secret metadata: - name: secret-product-service + name: product-service-db-secret + labels: + app: product-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "product-change-postgres-dev-postgresql" - DB_NAME: "product_change_db" - DB_USERNAME: "product_change_user" - DB_PASSWORD: "ProductUser2025@" \ No newline at end of file + DB_USERNAME: "unicorn" + DB_PASSWORD: "STAGING_DB_PASSWORD" diff --git a/deployment/cicd/kustomize/overlays/staging/secret-user-service-patch.yaml b/deployment/cicd/kustomize/overlays/staging/secret-user-service-patch.yaml index 8424423..8d3de11 100644 --- a/deployment/cicd/kustomize/overlays/staging/secret-user-service-patch.yaml +++ b/deployment/cicd/kustomize/overlays/staging/secret-user-service-patch.yaml @@ -1,10 +1,11 @@ apiVersion: v1 kind: Secret metadata: - name: secret-user-service + name: user-service-db-secret + labels: + app: user-service + app.kubernetes.io/part-of: phonebill type: Opaque stringData: - DB_HOST: "auth-postgres-dev-postgresql" - DB_NAME: "phonebill_auth" - DB_USERNAME: "auth_user" - DB_PASSWORD: "AuthUser2025@" \ No newline at end of file + DB_USERNAME: "unicorn" + DB_PASSWORD: "STAGING_DB_PASSWORD" diff --git a/deployment/cicd/scripts/deploy.sh b/deployment/cicd/scripts/deploy.sh index 14817da..c5863de 100755 --- a/deployment/cicd/scripts/deploy.sh +++ b/deployment/cicd/scripts/deploy.sh @@ -4,40 +4,35 @@ set -e ENVIRONMENT=${1:-dev} IMAGE_TAG=${2:-latest} -echo "🚀 Starting deployment for environment: $ENVIRONMENT with image tag: $IMAGE_TAG" +echo "🚀 Deploying to ${ENVIRONMENT} environment with tag ${IMAGE_TAG}..." + +# 환경별 설정 파일 로드 +source "$(dirname "$0")/../config/deploy_env_vars_${ENVIRONMENT}" # 환경별 이미지 태그 업데이트 -cd deployment/cicd/kustomize/overlays/${ENVIRONMENT} +cd "$(dirname "$0")/../kustomize/overlays/${ENVIRONMENT}" -# 서비스 목록 (공백으로 구분) +# 서비스 목록 services="api-gateway user-service bill-service product-service kos-mock" -echo "📦 Updating image tags for services: $services" - # 각 서비스 이미지 태그 업데이트 for service in $services; do - echo " - Updating $service to ${ENVIRONMENT}-${IMAGE_TAG}" - kustomize edit set image acrdigitalgarage01.azurecr.io/phonebill/$service:${ENVIRONMENT}-${IMAGE_TAG} + echo "📦 Updating image tag for ${service}..." + kustomize edit set image docker.io/hiondal/$service:${ENVIRONMENT}-${IMAGE_TAG} done -echo "🔧 Applying Kubernetes manifests..." # 배포 실행 +echo "📋 Applying Kustomize manifests..." kubectl apply -k . -echo "⏳ Waiting for deployments to be ready..." # 배포 상태 확인 +echo "⏳ Waiting for deployments to be ready..." for service in $services; do - echo " - Checking rollout status for $service" - kubectl rollout status deployment/$service -n phonebill-dg0500 --timeout=300s + echo " Checking ${service}..." + kubectl rollout status deployment/$service -n ${namespace} --timeout=300s || echo " ⚠️ Timeout waiting for ${service}" done echo "✅ Deployment completed successfully!" echo "" -echo "📊 Current deployment status:" -kubectl get pods -n phonebill-dg0500 -o wide -echo "" -echo "🌐 Service endpoints:" -kubectl get services -n phonebill-dg0500 -echo "" -echo "🔗 Ingress information:" -kubectl get ingress -n phonebill-dg0500 \ No newline at end of file +echo "📊 Current status:" +kubectl get pods -n ${namespace} diff --git a/deployment/cicd/scripts/validate-resources.sh b/deployment/cicd/scripts/validate-resources.sh index 76b95ae..9d710b8 100755 --- a/deployment/cicd/scripts/validate-resources.sh +++ b/deployment/cicd/scripts/validate-resources.sh @@ -1,14 +1,13 @@ #!/bin/bash -# Base 리소스 누락 검증 스크립트 (phonebill 전용) +# Base 리소스 누락 검증 스크립트 -echo "🔍 phonebill Base 리소스 누락 검증 시작..." +echo "🔍 Phonebill Base 리소스 누락 검증 시작..." BASE_DIR="deployment/cicd/kustomize/base" MISSING_RESOURCES=0 -REQUIRED_FILES=("deployment.yaml" "service.yaml") -OPTIONAL_FILES=("cm-" "secret-") # 1. 각 서비스 디렉토리의 파일 확인 +echo "" echo "1. 서비스 디렉토리별 파일 목록:" for dir in $BASE_DIR/*/; do if [ -d "$dir" ] && [[ $(basename "$dir") != "common" ]]; then @@ -16,24 +15,33 @@ for dir in $BASE_DIR/*/; do echo "=== $service ===" # 필수 파일 확인 - for required in "${REQUIRED_FILES[@]}"; do - if [ -f "$dir$required" ]; then - echo " ✅ $required" - else - echo " ❌ MISSING REQUIRED: $required" - ((MISSING_RESOURCES++)) - fi - done + if [ -f "$dir/deployment.yaml" ]; then + echo " ✅ deployment.yaml" + else + echo " ❌ MISSING REQUIRED: deployment.yaml" + ((MISSING_RESOURCES++)) + fi - # 선택적 파일 확인 - for optional in "${OPTIONAL_FILES[@]}"; do - files=($(ls "$dir"$optional*".yaml" 2>/dev/null)) - if [ ${#files[@]} -gt 0 ]; then - for file in "${files[@]}"; do - echo " ✅ $(basename "$file")" - done - fi - done + if [ -f "$dir/service.yaml" ]; then + echo " ✅ service.yaml" + else + echo " ❌ MISSING REQUIRED: service.yaml" + ((MISSING_RESOURCES++)) + fi + + # ConfigMap 확인 + if ls "$dir"cm-*.yaml 1> /dev/null 2>&1; then + for file in "$dir"cm-*.yaml; do + echo " ✅ $(basename "$file")" + done + fi + + # Secret 확인 + if ls "$dir"secret-*.yaml 1> /dev/null 2>&1; then + for file in "$dir"secret-*.yaml; do + echo " ✅ $(basename "$file")" + done + fi echo "" fi done @@ -42,15 +50,11 @@ done echo "2. Common 리소스 확인:" COMMON_DIR="$BASE_DIR/common" if [ -d "$COMMON_DIR" ]; then - common_files=($(ls "$COMMON_DIR"/*.yaml 2>/dev/null)) - if [ ${#common_files[@]} -gt 0 ]; then - for file in "${common_files[@]}"; do + for file in "$COMMON_DIR"/*.yaml; do + if [ -f "$file" ]; then echo " ✅ common/$(basename "$file")" - done - else - echo " ❌ Common 디렉토리에 YAML 파일이 없습니다" - ((MISSING_RESOURCES++)) - fi + fi + done else echo " ❌ Common 디렉토리가 없습니다" ((MISSING_RESOURCES++)) @@ -61,9 +65,8 @@ echo "" echo "3. kustomization.yaml 리소스 검증:" if [ -f "$BASE_DIR/kustomization.yaml" ]; then while IFS= read -r line; do - # resources 섹션의 YAML 파일 경로 추출 if [[ $line =~ ^[[:space:]]*-[[:space:]]*([^#]+\.yaml)[[:space:]]*$ ]]; then - resource_path=$(echo "${BASH_REMATCH[1]}" | xargs) # 공백 제거 + resource_path=$(echo "${BASH_REMATCH[1]}" | xargs) full_path="$BASE_DIR/$resource_path" if [ -f "$full_path" ]; then echo " ✅ $resource_path" @@ -125,4 +128,4 @@ else echo " - Secret: secret-{서비스명}.yaml" echo "4. 다시 검증: ./scripts/validate-resources.sh" exit 1 -fi \ No newline at end of file +fi diff --git a/deployment/container/Dockerfile-backend b/deployment/container/Dockerfile-backend index 66d7f45..b03a59a 100644 --- a/deployment/container/Dockerfile-backend +++ b/deployment/container/Dockerfile-backend @@ -5,13 +5,14 @@ ARG ARTIFACTORY_FILE COPY ${BUILD_LIB_DIR}/${ARTIFACTORY_FILE} app.jar # Run stage -FROM eclipse-temurin:21-jre +FROM eclipse-temurin:21-jre-alpine ENV USERNAME=k8s ENV ARTIFACTORY_HOME=/home/${USERNAME} ENV JAVA_OPTS="" # Add a non-root user -RUN adduser --system --group ${USERNAME} && \ +RUN addgroup -S ${USERNAME} && \ + adduser -S -G ${USERNAME} ${USERNAME} && \ mkdir -p ${ARTIFACTORY_HOME} && \ chown ${USERNAME}:${USERNAME} ${ARTIFACTORY_HOME} @@ -22,4 +23,4 @@ RUN chown ${USERNAME}:${USERNAME} app.jar USER ${USERNAME} ENTRYPOINT [ "sh", "-c" ] -CMD ["java ${JAVA_OPTS} -jar app.jar"] \ No newline at end of file +CMD ["java ${JAVA_OPTS} -jar app.jar"] diff --git a/deployment/container/build-image.md b/deployment/container/build-image.md index 952fbb2..b10d2e0 100644 --- a/deployment/container/build-image.md +++ b/deployment/container/build-image.md @@ -1,48 +1,37 @@ -# 백엔드 컨테이너 이미지 빌드 결과서 +# 백엔드 컨테이너 이미지 작성 결과서 -## 작업 개요 -- **작업일시**: 2025-01-10 -- **작업자**: 최운영/데옵스 -- **작업 목표**: 백엔드 마이크로서비스들의 컨테이너 이미지 생성 +## 개요 +본 문서는 phonebill 프로젝트의 백엔드 마이크로서비스들의 컨테이너 이미지 빌드 과정과 결과를 기록합니다. -## 빌드 대상 서비스 -총 5개의 백엔드 서비스에 대한 컨테이너 이미지를 생성했습니다. +## 대상 서비스 +| 서비스명 | 설명 | +|---------|------| +| api-gateway | API Gateway 서비스 | +| user-service | 사용자 인증/관리 서비스 | +| bill-service | 요금 조회 서비스 | +| product-service | 상품 변경 서비스 | +| kos-mock | KOS 목업 서비스 | -1. **api-gateway**: API Gateway 서비스 -2. **user-service**: 사용자 관리 서비스 -3. **bill-service**: 요금 조회 서비스 -4. **product-service**: 상품 변경 서비스 -5. **kos-mock**: KOS 시스템 목업 서비스 +## Dockerfile 구성 -## 사전 작업 - -### 1. 서비스별 bootJar 설정 추가 -각 서비스의 build.gradle 파일에 일관된 JAR 파일명 설정을 추가했습니다. - -```gradle -bootJar { - archiveFileName = '{서비스명}.jar' -} -``` - -### 2. Dockerfile 생성 -`deployment/container/Dockerfile-backend` 파일을 생성했습니다. +**파일 위치**: `deployment/container/Dockerfile-backend` ```dockerfile # Build stage -FROM openjdk:23-oraclelinux8 AS builder +FROM eclipse-temurin:21-jdk AS builder ARG BUILD_LIB_DIR ARG ARTIFACTORY_FILE COPY ${BUILD_LIB_DIR}/${ARTIFACTORY_FILE} app.jar # Run stage -FROM openjdk:23-slim +FROM eclipse-temurin:21-jre-alpine ENV USERNAME=k8s ENV ARTIFACTORY_HOME=/home/${USERNAME} ENV JAVA_OPTS="" # Add a non-root user -RUN adduser --system --group ${USERNAME} && \ +RUN addgroup -S ${USERNAME} && \ + adduser -S -G ${USERNAME} ${USERNAME} && \ mkdir -p ${ARTIFACTORY_HOME} && \ chown ${USERNAME}:${USERNAME} ${ARTIFACTORY_HOME} @@ -56,136 +45,99 @@ ENTRYPOINT [ "sh", "-c" ] CMD ["java ${JAVA_OPTS} -jar app.jar"] ``` -### 3. 서비스별 빌드 -모든 서비스에 대해 Gradle 빌드를 수행했습니다. +### Dockerfile 특징 +- **멀티스테이지 빌드**: 빌드와 실행 환경 분리로 이미지 크기 최적화 +- **베이스 이미지**: Eclipse Temurin 21 JRE Alpine (경량화) +- **보안**: 비루트 사용자(k8s)로 실행 +- **유연성**: `JAVA_OPTS` 환경변수로 JVM 옵션 설정 가능 +## 빌드 과정 + +### 1. Gradle 빌드 (JAR 파일 생성) ```bash -./gradlew api-gateway:clean api-gateway:bootJar -./gradlew user-service:clean user-service:bootJar -./gradlew bill-service:clean bill-service:bootJar -./gradlew product-service:clean product-service:bootJar -./gradlew kos-mock:clean kos-mock:bootJar +./gradlew clean build -x test ``` -## 컨테이너 이미지 빌드 +### 2. 컨테이너 이미지 빌드 +각 서비스별로 아래 명령을 실행합니다: -각 서비스별로 다음 명령어를 사용하여 컨테이너 이미지를 빌드했습니다. - -### API Gateway ```bash DOCKER_FILE=deployment/container/Dockerfile-backend -service=api-gateway +# api-gateway docker build \ --platform linux/amd64 \ - --build-arg BUILD_LIB_DIR="${service}/build/libs" \ - --build-arg ARTIFACTORY_FILE="${service}.jar" \ + --build-arg BUILD_LIB_DIR="api-gateway/build/libs" \ + --build-arg ARTIFACTORY_FILE="api-gateway.jar" \ -f ${DOCKER_FILE} \ - -t ${service}:latest . -``` - -### User Service -```bash -DOCKER_FILE=deployment/container/Dockerfile-backend -service=user-service + -t api-gateway:latest . +# user-service docker build \ --platform linux/amd64 \ - --build-arg BUILD_LIB_DIR="${service}/build/libs" \ - --build-arg ARTIFACTORY_FILE="${service}.jar" \ + --build-arg BUILD_LIB_DIR="user-service/build/libs" \ + --build-arg ARTIFACTORY_FILE="user-service.jar" \ -f ${DOCKER_FILE} \ - -t ${service}:latest . -``` - -### Bill Service -```bash -DOCKER_FILE=deployment/container/Dockerfile-backend -service=bill-service + -t user-service:latest . +# bill-service docker build \ --platform linux/amd64 \ - --build-arg BUILD_LIB_DIR="${service}/build/libs" \ - --build-arg ARTIFACTORY_FILE="${service}.jar" \ + --build-arg BUILD_LIB_DIR="bill-service/build/libs" \ + --build-arg ARTIFACTORY_FILE="bill-service.jar" \ -f ${DOCKER_FILE} \ - -t ${service}:latest . -``` - -### Product Service -```bash -DOCKER_FILE=deployment/container/Dockerfile-backend -service=product-service + -t bill-service:latest . +# product-service docker build \ --platform linux/amd64 \ - --build-arg BUILD_LIB_DIR="${service}/build/libs" \ - --build-arg ARTIFACTORY_FILE="${service}.jar" \ + --build-arg BUILD_LIB_DIR="product-service/build/libs" \ + --build-arg ARTIFACTORY_FILE="product-service.jar" \ -f ${DOCKER_FILE} \ - -t ${service}:latest . -``` - -### KOS Mock Service -```bash -DOCKER_FILE=deployment/container/Dockerfile-backend -service=kos-mock + -t product-service:latest . +# kos-mock docker build \ --platform linux/amd64 \ - --build-arg BUILD_LIB_DIR="${service}/build/libs" \ - --build-arg ARTIFACTORY_FILE="${service}.jar" \ + --build-arg BUILD_LIB_DIR="kos-mock/build/libs" \ + --build-arg ARTIFACTORY_FILE="kos-mock.jar" \ -f ${DOCKER_FILE} \ - -t ${service}:latest . + -t kos-mock:latest . ``` ## 빌드 결과 -### 성공적으로 생성된 이미지들 +### 생성된 이미지 목록 +| 이미지명 | 태그 | 크기 | 상태 | +|---------|------|------|------| +| api-gateway | latest | 158MB | ✅ 성공 | +| user-service | latest | 205MB | ✅ 성공 | +| bill-service | latest | 214MB | ✅ 성공 | +| product-service | latest | 220MB | ✅ 성공 | +| kos-mock | latest | 201MB | ✅ 성공 | -| 서비스명 | 이미지 태그 | 이미지 ID | 크기 | 생성 시간 | -|---------|------------|-----------|------|----------| -| api-gateway | latest | 5f4a2a5527b8 | 329MB | 3분 전 | -| user-service | latest | a8a85ba0b703 | 376MB | 2분 전 | -| bill-service | latest | b77190090a40 | 385MB | 1분 전 | -| product-service | latest | 5a6fba790ca3 | 392MB | 1분 전 | -| kos-mock | latest | 3f5878cf2f1e | 372MB | 35초 전 | - -### 이미지 검증 명령어 실행 결과 +### 이미지 확인 명령 ```bash -$ docker images | grep -E "(api-gateway|user-service|bill-service|product-service|kos-mock)" -kos-mock latest 3f5878cf2f1e 35 seconds ago 372MB -product-service latest 5a6fba790ca3 About a minute ago 392MB -bill-service latest b77190090a40 About a minute ago 385MB -user-service latest a8a85ba0b703 2 minutes ago 376MB -api-gateway latest 5f4a2a5527b8 3 minutes ago 329MB +docker images | grep -E "api-gateway|user-service|bill-service|product-service|kos-mock" ``` -## 빌드 특징 +## 이미지 레지스트리 푸시 (선택) -### 멀티 스테이지 빌드 -- **Build Stage**: OpenJDK 23-oraclelinux8 사용하여 JAR 파일 복사 -- **Runtime Stage**: OpenJDK 23-slim 사용하여 경량화된 실행 환경 구성 +Azure Container Registry에 푸시하는 경우: +```bash +REGISTRY=docker.io +PROJECT=hiondal +TAG=latest -### 보안 강화 -- 비루트 사용자 `k8s` 생성 및 사용 -- 적절한 파일 소유권 및 권한 설정 -- 최소 권한 원칙 적용 +# 로그인 +az acr login --name acrdigitalgarage01 -### 플랫폼 호환성 -- `--platform linux/amd64` 옵션으로 AMD64 아키텍처 지원 -- 쿠버네티스 클러스터 배포에 적합한 형태 +# 태그 및 푸시 +for service in api-gateway user-service bill-service product-service kos-mock; do + docker tag ${service}:latest ${REGISTRY}/${PROJECT}/${service}:${TAG} + docker push ${REGISTRY}/${PROJECT}/${service}:${TAG} +done +``` -## 다음 단계 - -1. **컨테이너 레지스트리 푸시**: ACR 또는 Docker Hub에 이미지 푸시 -2. **쿠버네티스 매니페스트 작성**: Deployment, Service 등 K8s 리소스 정의 -3. **헬름 차트 작성**: 패키지 관리를 위한 Helm 차트 구성 -4. **CI/CD 파이프라인 통합**: 자동화된 빌드 및 배포 파이프라인 구축 - -## 주요 성과 - -✅ **모든 백엔드 서비스 컨테이너화 완료** (5개 서비스) -✅ **멀티 스테이지 빌드로 최적화된 이미지** (평균 360MB) -✅ **보안 강화된 컨테이너 구성** (비루트 사용자) -✅ **일관된 빌드 프로세스** (표준화된 Dockerfile) -✅ **쿠버네티스 배포 준비 완료** - -모든 백엔드 서비스들이 성공적으로 컨테이너화되었으며, 프로덕션 환경 배포를 위한 준비가 완료되었습니다. \ No newline at end of file +## 작성일 +2025-11-27 diff --git a/deployment/container/run-container-guide.md b/deployment/container/run-container-guide.md index 173cfdd..050a03b 100644 --- a/deployment/container/run-container-guide.md +++ b/deployment/container/run-container-guide.md @@ -1,328 +1,326 @@ -# 백엔드 컨테이너 실행 가이드 +# 백엔드 컨테이너 실행 가이드 (로컬 환경) -## 1. 시스템 정보 -- **시스템명**: phonebill -- **서비스 목록**: - - api-gateway (포트: 8080) - - user-service (포트: 8081) - - bill-service (포트: 8082) - - product-service (포트: 8083) - - kos-mock (포트: 8084) +## 개요 +본 문서는 phonebill 프로젝트의 백엔드 마이크로서비스들을 로컬 환경에서 Docker 컨테이너로 실행하는 방법을 안내합니다. -## 2. VM 접속 방법 +## 실행 정보 +| 항목 | 값 | +|------|-----| +| Image Registry | docker.io | +| 시스템명 | phonebill | +| 실행 환경 | 로컬 (localhost) | -### Windows 사용자 -1. **Windows Terminal** 실행 -2. 아래 명령어 순서대로 실행: +## 대상 서비스 +| 서비스명 | 포트 | 설명 | +|---------|------|------| +| kos-mock | 8084 | KOS 목업 서비스 | +| user-service | 8081 | 사용자 인증/관리 서비스 | +| bill-service | 8082 | 요금 조회 서비스 | +| product-service | 8083 | 상품 변경 서비스 | +| api-gateway | 8080 | API Gateway | +## 사전 준비 사항 + +### 1. Docker 설치 확인 ```bash -# Private Key 파일 권한 설정 (최초 1회만) -chmod 400 ~/home/bastion-dg0500 - -# VM 접속 -ssh -i ~/home/bastion-dg0500 azureuser@4.230.5.6 +docker --version ``` -### Linux/Mac 사용자 -1. **터미널** 실행 -2. 아래 명령어 순서대로 실행: +### 2. 백킹 서비스 실행 확인 +컨테이너 실행 전 아래 백킹 서비스가 실행 중이어야 합니다: +- **PostgreSQL** (auth-db: 15432, bill-inquiry-db: 25432, product-change-db: 35432) +- **Redis** (16379) +백킹 서비스 실행 상태 확인: ```bash -# Private Key 파일 권한 설정 (최초 1회만) -chmod 400 ~/home/bastion-dg0500 - -# VM 접속 -ssh -i ~/home/bastion-dg0500 azureuser@4.230.5.6 +docker ps | grep -E "postgres|redis" ``` -## 3. 컨테이너 이미지 준비 +## 어플리케이션 빌드 및 컨테이너 이미지 생성 -### 3.1 이미지 빌드 (로컬에서 수행) -먼저 로컬에서 이미지를 빌드해야 합니다. 아래 가이드를 참고하여 수행하세요: -```bash -# 이미지 빌드 가이드 확인 -cat deployment/container/build-image.md -``` - -### 3.2 Azure Container Registry 로그인 -ACR 인증 정보를 확인하고 Docker 로그인을 수행합니다: +`deployment/container/build-image.md` 파일을 참조하여 이미지를 빌드합니다. ```bash -# ACR 인증 정보 확인 -az acr credential show --name acrdigitalgarage01 +# Gradle 빌드 +./gradlew clean build -x test -# 출력 예시: -# { -# "passwords": [ -# { -# "name": "password", -# "value": "실제암호" -# } -# ], -# "username": "acrdigitalgarage01" -# } - -# Docker 로그인 (위에서 확인한 username과 password 사용) -docker login acrdigitalgarage01.azurecr.io -u acrdigitalgarage01 -p 실제암호 +# 이미지 빌드 예시 (api-gateway) +docker build \ + --platform linux/amd64 \ + --build-arg BUILD_LIB_DIR="api-gateway/build/libs" \ + --build-arg ARTIFACTORY_FILE="api-gateway.jar" \ + -f deployment/container/Dockerfile-backend \ + -t api-gateway:latest . ``` -### 3.3 이미지 Push (로컬에서 수행) -각 서비스별로 이미지를 태깅하고 푸시합니다: +## Docker Hub 로그인 (선택) +이미지를 Docker Hub에 푸시하려면 로그인이 필요합니다: +```bash +docker login docker.io -u {Docker Hub ID} -p {암호} +``` + +## 이미지 태그 및 푸시 (선택) + +Docker Hub에 이미지를 푸시하는 경우: +```bash +# 태그 +docker tag api-gateway:latest docker.io/hiondal/api-gateway:latest + +# 푸시 +docker push docker.io/hiondal/api-gateway:latest +``` + +--- + +## 컨테이너 실행 + +> **중요**: 서비스 간 의존성이 있으므로 아래 순서대로 실행하세요. +> 1. kos-mock → 2. user-service → 3. bill-service → 4. product-service → 5. api-gateway + +### 1. kos-mock 실행 +```bash +docker run -d --name kos-mock --rm \ + -p 8084:8084 \ + -e SERVER_PORT=8084 \ + -e SPRING_PROFILES_ACTIVE=dev \ + kos-mock:latest +``` + +### 2. user-service 실행 +```bash +docker run -d --name user-service --rm \ + -p 8081:8081 \ + -e SERVER_PORT=8081 \ + -e SPRING_PROFILES_ACTIVE=dev \ + -e CORS_ALLOWED_ORIGINS=http://localhost:3000 \ + -e DB_HOST=localhost \ + -e DB_PORT=15432 \ + -e DB_NAME=authdb \ + -e DB_USERNAME=unicorn \ + -e DB_PASSWORD='P@ssw0rd$' \ + -e DB_KIND=postgresql \ + -e DDL_AUTO=update \ + -e SHOW_SQL=true \ + -e REDIS_HOST=host.docker.internal \ + -e REDIS_PORT=16379 \ + -e REDIS_PASSWORD='P@ssw0rd$' \ + -e REDIS_DATABASE=0 \ + -e JWT_SECRET='nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==' \ + -e JWT_ACCESS_TOKEN_VALIDITY=18000000 \ + -e JWT_REFRESH_TOKEN_VALIDITY=86400000 \ + user-service:latest +``` + +### 3. bill-service 실행 +```bash +docker run -d --name bill-service --rm \ + -p 8082:8082 \ + -e SERVER_PORT=8082 \ + -e SPRING_PROFILES_ACTIVE=dev \ + -e CORS_ALLOWED_ORIGINS=http://localhost:3000 \ + -e DB_HOST=host.docker.internal \ + -e DB_PORT=25432 \ + -e DB_NAME=inquirydb \ + -e DB_USERNAME=unicorn \ + -e DB_PASSWORD='P@ssw0rd$' \ + -e DB_KIND=postgresql \ + -e DB_MAX_POOL=20 \ + -e DB_MIN_IDLE=5 \ + -e DB_CONNECTION_TIMEOUT=30000 \ + -e DB_IDLE_TIMEOUT=600000 \ + -e DB_MAX_LIFETIME=1800000 \ + -e DB_LEAK_DETECTION=60000 \ + -e REDIS_HOST=host.docker.internal \ + -e REDIS_PORT=16379 \ + -e REDIS_PASSWORD='P@ssw0rd$' \ + -e REDIS_DATABASE=1 \ + -e REDIS_TIMEOUT=2000 \ + -e REDIS_MAX_ACTIVE=8 \ + -e REDIS_MAX_IDLE=8 \ + -e REDIS_MIN_IDLE=0 \ + -e REDIS_MAX_WAIT=-1 \ + -e KOS_BASE_URL=http://host.docker.internal:8084 \ + -e JWT_SECRET='nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==' \ + -e JWT_ACCESS_TOKEN_VALIDITY=18000000 \ + -e JWT_REFRESH_TOKEN_VALIDITY=86400000 \ + -e LOG_FILE_NAME=logs/bill-service.log \ + bill-service:latest +``` + +### 4. product-service 실행 +```bash +docker run -d --name product-service --rm \ + -p 8083:8083 \ + -e SERVER_PORT=8083 \ + -e SPRING_PROFILES_ACTIVE=dev \ + -e CORS_ALLOWED_ORIGINS=http://localhost:3000 \ + -e DB_HOST=host.docker.internal \ + -e DB_PORT=35432 \ + -e DB_NAME=changedb \ + -e DB_USERNAME=unicorn \ + -e DB_PASSWORD='P@ssw0rd$' \ + -e DB_KIND=postgresql \ + -e DDL_AUTO=update \ + -e REDIS_HOST=host.docker.internal \ + -e REDIS_PORT=16379 \ + -e REDIS_PASSWORD='P@ssw0rd$' \ + -e REDIS_DATABASE=2 \ + -e KOS_BASE_URL=http://host.docker.internal:8084 \ + -e KOS_MOCK_ENABLED=true \ + -e KOS_CLIENT_ID=product-service-dev \ + -e KOS_API_KEY=dev-api-key \ + -e JWT_SECRET='nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==' \ + -e JWT_ACCESS_TOKEN_VALIDITY=18000000 \ + -e JWT_REFRESH_TOKEN_VALIDITY=86400000 \ + product-service:latest +``` + +### 5. api-gateway 실행 +```bash +docker run -d --name api-gateway --rm \ + -p 8080:8080 \ + -e SERVER_PORT=8080 \ + -e SPRING_PROFILES_ACTIVE=dev \ + -e CORS_ALLOWED_ORIGINS=http://localhost:3000 \ + -e USER_SERVICE_URL=http://host.docker.internal:8081 \ + -e BILL_SERVICE_URL=http://host.docker.internal:8082 \ + -e PRODUCT_SERVICE_URL=http://host.docker.internal:8083 \ + -e KOS_MOCK_URL=http://host.docker.internal:8084 \ + -e JWT_SECRET='nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==' \ + -e JWT_ACCESS_TOKEN_VALIDITY=18000000 \ + -e JWT_REFRESH_TOKEN_VALIDITY=86400000 \ + api-gateway:latest +``` + +--- + +## 실행 확인 + +### 컨테이너 실행 상태 확인 +```bash +docker ps | grep -E "api-gateway|user-service|bill-service|product-service|kos-mock" +``` + +### 예상 결과 +``` +CONTAINER ID IMAGE PORTS NAMES +xxxxxxxxxxxx api-gateway:latest 0.0.0.0:8080->8080/tcp api-gateway +xxxxxxxxxxxx product-service:latest 0.0.0.0:8083->8083/tcp product-service +xxxxxxxxxxxx bill-service:latest 0.0.0.0:8082->8082/tcp bill-service +xxxxxxxxxxxx user-service:latest 0.0.0.0:8081->8081/tcp user-service +xxxxxxxxxxxx kos-mock:latest 0.0.0.0:8084->8084/tcp kos-mock +``` + +### 서비스 헬스체크 ```bash # API Gateway -docker tag api-gateway:latest acrdigitalgarage01.azurecr.io/phonebill/api-gateway:latest -docker push acrdigitalgarage01.azurecr.io/phonebill/api-gateway:latest +curl http://localhost:8080/actuator/health # User Service -docker tag user-service:latest acrdigitalgarage01.azurecr.io/phonebill/user-service:latest -docker push acrdigitalgarage01.azurecr.io/phonebill/user-service:latest +curl http://localhost:8081/actuator/health # Bill Service -docker tag bill-service:latest acrdigitalgarage01.azurecr.io/phonebill/bill-service:latest -docker push acrdigitalgarage01.azurecr.io/phonebill/bill-service:latest +curl http://localhost:8082/actuator/health # Product Service -docker tag product-service:latest acrdigitalgarage01.azurecr.io/phonebill/product-service:latest -docker push acrdigitalgarage01.azurecr.io/phonebill/product-service:latest +curl http://localhost:8083/actuator/health # KOS Mock -docker tag kos-mock:latest acrdigitalgarage01.azurecr.io/phonebill/kos-mock:latest -docker push acrdigitalgarage01.azurecr.io/phonebill/kos-mock:latest +curl http://localhost:8084/actuator/health ``` -## 4. 컨테이너 실행 (VM에서 수행) - -### 4.1 KOS Mock 서비스 실행 +### 로그 확인 ```bash -SERVER_PORT=8084 - -docker run -d --name kos-mock --rm -p ${SERVER_PORT}:${SERVER_PORT} \ --e SERVER_PORT=8084 \ --e SPRING_PROFILES_ACTIVE=dev \ -acrdigitalgarage01.azurecr.io/phonebill/kos-mock:latest +docker logs -f api-gateway +docker logs -f user-service +docker logs -f bill-service +docker logs -f product-service +docker logs -f kos-mock ``` -### 4.2 User Service 실행 +--- + +## 컨테이너 중지 + +### 개별 서비스 중지 ```bash -SERVER_PORT=8081 - -docker run -d --name user-service --rm -p ${SERVER_PORT}:${SERVER_PORT} \ --e CORS_ALLOWED_ORIGINS="http://localhost:3000,http://4.230.5.6:3000" \ --e DB_HOST=20.249.70.6 \ --e DB_KIND=postgresql \ --e DB_NAME=phonebill_auth \ --e DB_PASSWORD=AuthUser2025! \ --e DB_PORT=5432 \ --e DB_USERNAME=auth_user \ --e DDL_AUTO=update \ --e JWT_ACCESS_TOKEN_VALIDITY=18000000 \ --e JWT_REFRESH_TOKEN_VALIDITY=86400000 \ --e JWT_SECRET="nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==" \ --e REDIS_DATABASE=0 \ --e REDIS_HOST=20.249.193.103 \ --e REDIS_PASSWORD=Redis2025Dev! \ --e REDIS_PORT=6379 \ --e SERVER_PORT=8081 \ --e SHOW_SQL=true \ --e SPRING_PROFILES_ACTIVE=dev \ -acrdigitalgarage01.azurecr.io/phonebill/user-service:latest -``` - -### 4.3 Bill Service 실행 -```bash -SERVER_PORT=8082 - -docker run -d --name bill-service --rm -p ${SERVER_PORT}:${SERVER_PORT} \ --e CORS_ALLOWED_ORIGINS="http://localhost:3000,http://4.230.5.6:3000" \ --e DB_CONNECTION_TIMEOUT=30000 \ --e DB_HOST=20.249.175.46 \ --e DB_IDLE_TIMEOUT=600000 \ --e DB_KIND=postgresql \ --e DB_LEAK_DETECTION=60000 \ --e DB_MAX_LIFETIME=1800000 \ --e DB_MAX_POOL=20 \ --e DB_MIN_IDLE=5 \ --e DB_NAME=bill_inquiry_db \ --e DB_PASSWORD=BillUser2025! \ --e DB_PORT=5432 \ --e DB_USERNAME=bill_inquiry_user \ --e JWT_ACCESS_TOKEN_VALIDITY=18000000 \ --e JWT_REFRESH_TOKEN_VALIDITY=86400000 \ --e JWT_SECRET="nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==" \ --e KOS_BASE_URL=http://4.230.5.6:8084 \ --e LOG_FILE_NAME=logs/bill-service.log \ --e REDIS_DATABASE=1 \ --e REDIS_HOST=20.249.193.103 \ --e REDIS_MAX_ACTIVE=8 \ --e REDIS_MAX_IDLE=8 \ --e REDIS_MAX_WAIT=-1 \ --e REDIS_MIN_IDLE=0 \ --e REDIS_PASSWORD=Redis2025Dev! \ --e REDIS_PORT=6379 \ --e REDIS_TIMEOUT=2000 \ --e SERVER_PORT=8082 \ --e SPRING_PROFILES_ACTIVE=dev \ -acrdigitalgarage01.azurecr.io/phonebill/bill-service:latest -``` - -### 4.4 Product Service 실행 -```bash -SERVER_PORT=8083 - -docker run -d --name product-service --rm -p ${SERVER_PORT}:${SERVER_PORT} \ --e CORS_ALLOWED_ORIGINS="http://localhost:3000,http://4.230.5.6:3000" \ --e DB_HOST=20.249.107.185 \ --e DB_KIND=postgresql \ --e DB_NAME=product_change_db \ --e DB_PASSWORD=ProductUser2025! \ --e DB_PORT=5432 \ --e DB_USERNAME=product_change_user \ --e DDL_AUTO=update \ --e JWT_ACCESS_TOKEN_VALIDITY=18000000 \ --e JWT_REFRESH_TOKEN_VALIDITY=86400000 \ --e JWT_SECRET="nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==" \ --e KOS_API_KEY=dev-api-key \ --e KOS_BASE_URL=http://4.230.5.6:8084 \ --e KOS_CLIENT_ID=product-service-dev \ --e KOS_MOCK_ENABLED=true \ --e REDIS_DATABASE=2 \ --e REDIS_HOST=20.249.193.103 \ --e REDIS_PASSWORD=Redis2025Dev! \ --e REDIS_PORT=6379 \ --e SERVER_PORT=8083 \ --e SPRING_PROFILES_ACTIVE=dev \ -acrdigitalgarage01.azurecr.io/phonebill/product-service:latest -``` - -### 4.5 API Gateway 실행 -```bash -SERVER_PORT=8080 - -docker run -d --name api-gateway --rm -p ${SERVER_PORT}:${SERVER_PORT} \ --e BILL_SERVICE_URL=http://4.230.5.6:8082 \ --e CORS_ALLOWED_ORIGINS="http://localhost:3000,http://4.230.5.6:3000" \ --e JWT_ACCESS_TOKEN_VALIDITY=18000000 \ --e JWT_REFRESH_TOKEN_VALIDITY=86400000 \ --e JWT_SECRET="nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==" \ --e KOS_MOCK_URL=http://4.230.5.6:8084 \ --e PRODUCT_SERVICE_URL=http://4.230.5.6:8083 \ --e SERVER_PORT=8080 \ --e SPRING_PROFILES_ACTIVE=dev \ --e USER_SERVICE_URL=http://4.230.5.6:8081 \ -acrdigitalgarage01.azurecr.io/phonebill/api-gateway:latest -``` - -## 5. 컨테이너 실행 확인 - -전체 서비스가 정상적으로 실행되었는지 확인: - -```bash -# 모든 서비스 확인 -docker ps | grep -E "(api-gateway|user-service|bill-service|product-service|kos-mock)" - -# 개별 서비스 확인 -docker ps | grep api-gateway -docker ps | grep user-service -docker ps | grep bill-service -docker ps | grep product-service -docker ps | grep kos-mock -``` - -## 6. 서비스 접속 테스트 - -각 서비스의 헬스체크 엔드포인트로 정상 동작을 확인: - -```bash -# API Gateway -curl http://4.230.5.6:8080/actuator/health - -# User Service -curl http://4.230.5.6:8081/actuator/health - -# Bill Service -curl http://4.230.5.6:8082/actuator/health - -# Product Service -curl http://4.230.5.6:8083/actuator/health - -# KOS Mock -curl http://4.230.5.6:8084/actuator/health -``` - -## 7. 재배포 방법 - -### 7.1 컨테이너 이미지 재생성 (로컬에서 수행) -```bash -# 이미지 재빌드 -/deploy-build-image-back -``` - -### 7.2 컨테이너 이미지 푸시 (로컬에서 수행) -특정 서비스만 재배포하는 경우: -```bash -# 예: user-service 재배포 -docker tag user-service:latest acrdigitalgarage01.azurecr.io/phonebill/user-service:latest -docker push acrdigitalgarage01.azurecr.io/phonebill/user-service:latest -``` - -### 7.3 컨테이너 재시작 (VM에서 수행) -```bash -# 1. 기존 컨테이너 중지 +docker stop api-gateway +docker stop product-service +docker stop bill-service docker stop user-service - -# 2. 컨테이너 이미지 삭제 (캐시 갱신을 위해) -docker rmi acrdigitalgarage01.azurecr.io/phonebill/user-service:latest - -# 3. 새 이미지로 컨테이너 재실행 -SERVER_PORT=8081 - -docker run -d --name user-service --rm -p ${SERVER_PORT}:${SERVER_PORT} \ --e CORS_ALLOWED_ORIGINS="http://localhost:3000,http://4.230.5.6:3000" \ --e DB_HOST=20.249.70.6 \ --e DB_KIND=postgresql \ --e DB_NAME=phonebill_auth \ --e DB_PASSWORD=AuthUser2025! \ --e DB_PORT=5432 \ --e DB_USERNAME=auth_user \ --e DDL_AUTO=update \ --e JWT_ACCESS_TOKEN_VALIDITY=18000000 \ --e JWT_REFRESH_TOKEN_VALIDITY=86400000 \ --e JWT_SECRET="nwe5Yo9qaJ6FBD/Thl2/j6/SFAfNwUorAY1ZcWO2KI7uA4bmVLOCPxE9hYuUpRCOkgV2UF2DdHXtqHi3+BU/ecbz2zpHyf/720h48UbA3XOMYOX1sdM+dQ==" \ --e REDIS_DATABASE=0 \ --e REDIS_HOST=20.249.193.103 \ --e REDIS_PASSWORD=Redis2025Dev! \ --e REDIS_PORT=6379 \ --e SERVER_PORT=8081 \ --e SHOW_SQL=true \ --e SPRING_PROFILES_ACTIVE=dev \ -acrdigitalgarage01.azurecr.io/phonebill/user-service:latest +docker stop kos-mock ``` -## 8. 전체 서비스 재시작 스크립트 - -모든 서비스를 한번에 재시작하려면: - +### 전체 서비스 중지 ```bash -# 모든 컨테이너 중지 -docker stop api-gateway user-service bill-service product-service kos-mock - -# 모든 이미지 삭제 -docker rmi acrdigitalgarage01.azurecr.io/phonebill/api-gateway:latest -docker rmi acrdigitalgarage01.azurecr.io/phonebill/user-service:latest -docker rmi acrdigitalgarage01.azurecr.io/phonebill/bill-service:latest -docker rmi acrdigitalgarage01.azurecr.io/phonebill/product-service:latest -docker rmi acrdigitalgarage01.azurecr.io/phonebill/kos-mock:latest - -# 컨테이너 재실행 (위의 4.1 ~ 4.5 단계 순서대로 실행) +docker stop api-gateway product-service bill-service user-service kos-mock ``` -## 주의사항 +--- -1. **실행 순서**: KOS Mock → User Service → Bill Service → Product Service → API Gateway 순서로 실행하는 것을 권장합니다. +## 재배포 방법 -2. **CORS 설정**: 프론트엔드에서 접근할 수 있도록 VM IP(4.230.5.6:3000)가 CORS_ALLOWED_ORIGINS에 포함되어 있습니다. +### 1. 소스 수정 후 로컬에서 푸시 +```bash +git add . +git commit -m "수정 내용" +git push +``` -3. **로그 확인**: 컨테이너 로그는 `docker logs [컨테이너명]` 명령으로 확인할 수 있습니다. +### 2. 소스 내려받기 +```bash +cd ~/home/workspace/phonebill +git pull +``` -4. **네트워크**: 모든 서비스는 localhost로 서로 통신하도록 설정되어 있습니다. \ No newline at end of file +### 3. 컨테이너 이미지 재생성 +`deployment/container/build-image.md` 파일을 참조하여 이미지를 다시 빌드합니다. + +### 4. 이미지 푸시 (Docker Hub 사용 시) +```bash +docker tag api-gateway:latest docker.io/phonebill/api-gateway:latest +docker push docker.io/phonebill/api-gateway:latest +``` + +### 5. 컨테이너 중지 +```bash +docker stop api-gateway +``` + +### 6. 기존 이미지 삭제 (선택) +```bash +docker rmi api-gateway:latest +``` + +### 7. 컨테이너 재실행 +위의 "컨테이너 실행" 섹션의 명령어를 다시 실행합니다. + +--- + +## 문제 해결 + +### 1. 컨테이너가 시작되지 않는 경우 +```bash +# 로그 확인 +docker logs api-gateway + +# 컨테이너 상태 확인 +docker ps -a | grep api-gateway +``` + +### 2. 서비스 간 통신 오류 +- `host.docker.internal`이 올바르게 설정되었는지 확인 +- 방화벽 설정 확인 +- 백킹 서비스(DB, Redis) 실행 상태 확인 + +### 3. 포트 충돌 +```bash +# 사용 중인 포트 확인 +lsof -i :8080 +``` + +--- + +## 작성일 +2025-11-27 diff --git a/deployment/k8s/api-gateway/cm-api-gateway.yaml b/deployment/k8s/api-gateway/cm-api-gateway.yaml deleted file mode 100644 index aa83a94..0000000 --- a/deployment/k8s/api-gateway/cm-api-gateway.yaml +++ /dev/null @@ -1,10 +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" \ No newline at end of file diff --git a/deployment/k8s/bill-service/cm-bill-service.yaml b/deployment/k8s/bill-service/cm-bill-service.yaml deleted file mode 100644 index 9281f36..0000000 --- a/deployment/k8s/bill-service/cm-bill-service.yaml +++ /dev/null @@ -1,21 +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" \ No newline at end of file diff --git a/deployment/k8s/bill-service/secret-bill-service.yaml b/deployment/k8s/bill-service/secret-bill-service.yaml deleted file mode 100644 index caaa7cf..0000000 --- a/deployment/k8s/bill-service/secret-bill-service.yaml +++ /dev/null @@ -1,10 +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@" diff --git a/deployment/k8s/bill-service/service.yaml b/deployment/k8s/bill-service/service.yaml deleted file mode 100644 index 1e6373b..0000000 --- a/deployment/k8s/bill-service/service.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: bill-service -spec: - selector: - app: bill-service - ports: - - port: 80 - targetPort: 8082 - type: ClusterIP \ No newline at end of file diff --git a/deployment/k8s/common/cm-common.yaml b/deployment/k8s/common/cm-common.yaml deleted file mode 100644 index 3c16172..0000000 --- a/deployment/k8s/common/cm-common.yaml +++ /dev/null @@ -1,11 +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" \ No newline at end of file diff --git a/deployment/k8s/common/ingress.yaml b/deployment/k8s/common/ingress.yaml deleted file mode 100644 index da97115..0000000 --- a/deployment/k8s/common/ingress.yaml +++ /dev/null @@ -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-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 - diff --git a/deployment/k8s/common/secret-common.yaml b/deployment/k8s/common/secret-common.yaml deleted file mode 100644 index 53795ab..0000000 --- a/deployment/k8s/common/secret-common.yaml +++ /dev/null @@ -1,9 +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@" \ No newline at end of file diff --git a/deployment/k8s/common/secret-imagepull.yaml b/deployment/k8s/common/secret-imagepull.yaml deleted file mode 100644 index 6bd576e..0000000 --- a/deployment/k8s/common/secret-imagepull.yaml +++ /dev/null @@ -1,16 +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=" - } - } - } \ No newline at end of file diff --git a/deployment/k8s/configmaps/api-gateway-config.yaml b/deployment/k8s/configmaps/api-gateway-config.yaml new file mode 100644 index 0000000..feb3661 --- /dev/null +++ b/deployment/k8s/configmaps/api-gateway-config.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: api-gateway-config + namespace: phonebill + labels: + app: api-gateway + app.kubernetes.io/part-of: phonebill +data: + SERVER_PORT: "8080" + USER_SERVICE_URL: "http://user-service:8081" + BILL_SERVICE_URL: "http://bill-service:8082" + PRODUCT_SERVICE_URL: "http://product-service:8083" + KOS_MOCK_URL: "http://kos-mock:8084" diff --git a/deployment/k8s/configmaps/bill-service-config.yaml b/deployment/k8s/configmaps/bill-service-config.yaml new file mode 100644 index 0000000..5a2b8de --- /dev/null +++ b/deployment/k8s/configmaps/bill-service-config.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: bill-service-config + namespace: phonebill + labels: + app: bill-service + app.kubernetes.io/part-of: phonebill +data: + SERVER_PORT: "8082" + DB_HOST: "inquiry-postgresql" + DB_PORT: "5432" + DB_NAME: "inquirydb" + REDIS_DATABASE: "1" + KOS_BASE_URL: "http://kos-mock:8084" + DDL_AUTO: "update" diff --git a/deployment/k8s/configmaps/common-config.yaml b/deployment/k8s/configmaps/common-config.yaml new file mode 100644 index 0000000..d570940 --- /dev/null +++ b/deployment/k8s/configmaps/common-config.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: phonebill-common-config + namespace: phonebill + labels: + app.kubernetes.io/part-of: phonebill +data: + SPRING_PROFILES_ACTIVE: "prod" + REDIS_HOST: "cache-redis-master" + REDIS_PORT: "6379" + CORS_ALLOWED_ORIGINS: "http://localhost:3000,http://phonebill.72.155.72.236.nip.io" + SHOW_SQL: "false" + DDL_AUTO: "none" diff --git a/deployment/k8s/configmaps/kos-mock-config.yaml b/deployment/k8s/configmaps/kos-mock-config.yaml new file mode 100644 index 0000000..5f7a829 --- /dev/null +++ b/deployment/k8s/configmaps/kos-mock-config.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: kos-mock-config + namespace: phonebill + labels: + app: kos-mock + app.kubernetes.io/part-of: phonebill +data: + SERVER_PORT: "8084" diff --git a/deployment/k8s/configmaps/product-service-config.yaml b/deployment/k8s/configmaps/product-service-config.yaml new file mode 100644 index 0000000..ee82361 --- /dev/null +++ b/deployment/k8s/configmaps/product-service-config.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: product-service-config + namespace: phonebill + labels: + app: product-service + app.kubernetes.io/part-of: phonebill +data: + SERVER_PORT: "8083" + DB_HOST: "change-postgresql" + DB_PORT: "5432" + DB_NAME: "changedb" + REDIS_DATABASE: "2" + KOS_BASE_URL: "http://kos-mock:8084" + DDL_AUTO: "update" diff --git a/deployment/k8s/configmaps/user-service-config.yaml b/deployment/k8s/configmaps/user-service-config.yaml new file mode 100644 index 0000000..afbc488 --- /dev/null +++ b/deployment/k8s/configmaps/user-service-config.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: user-service-config + namespace: phonebill + labels: + app: user-service + app.kubernetes.io/part-of: phonebill +data: + SERVER_PORT: "8081" + DB_HOST: "auth-postgresql" + DB_PORT: "5432" + DB_NAME: "authdb" + REDIS_DATABASE: "0" + DDL_AUTO: "update" diff --git a/deployment/k8s/delete-all.sh b/deployment/k8s/delete-all.sh new file mode 100755 index 0000000..c728743 --- /dev/null +++ b/deployment/k8s/delete-all.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Minikube 백엔드 서비스 삭제 스크립트 +# 작성자: 최운영(데옵스) +# 작성일: 2025-11-29 + +set -e + +NAMESPACE=phonebill +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo "==============================================" +echo " PhoneBill Backend - 리소스 삭제" +echo "==============================================" +echo "" + +read -p "⚠️ $NAMESPACE 네임스페이스의 모든 리소스를 삭제하시겠습니까? (y/N): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "삭제를 취소합니다." + exit 0 +fi + +echo "" +echo "[1/5] Ingress 삭제..." +kubectl delete -f "$SCRIPT_DIR/ingress/" --ignore-not-found=true || true + +echo "" +echo "[2/5] Services 삭제..." +kubectl delete -f "$SCRIPT_DIR/services/" --ignore-not-found=true || true + +echo "" +echo "[3/5] Deployments 삭제..." +kubectl delete -f "$SCRIPT_DIR/deployments/" --ignore-not-found=true || true + +echo "" +echo "[4/5] ConfigMaps 삭제..." +kubectl delete -f "$SCRIPT_DIR/configmaps/" --ignore-not-found=true || true + +echo "" +echo "[5/5] Secrets 삭제..." +kubectl delete -f "$SCRIPT_DIR/secrets/" --ignore-not-found=true || true + +echo "" +echo "==============================================" +echo " 삭제 완료!" +echo "==============================================" +echo "" +echo "📦 남은 리소스 확인:" +kubectl get all -n $NAMESPACE 2>/dev/null || echo "리소스 없음" +echo "" diff --git a/deployment/k8s/deploy-all.sh b/deployment/k8s/deploy-all.sh new file mode 100755 index 0000000..30256a2 --- /dev/null +++ b/deployment/k8s/deploy-all.sh @@ -0,0 +1,103 @@ +#!/bin/bash +# Minikube 백엔드 서비스 원클릭 배포 스크립트 +# 작성자: 최운영(데옵스) +# 작성일: 2025-11-29 + +set -e + +NAMESPACE=phonebill +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo "==============================================" +echo " PhoneBill Backend - Minikube 배포 시작" +echo "==============================================" + +# 1. Kubernetes Context 확인 +echo "" +echo "[1/7] Kubernetes Context 확인..." +CURRENT_CONTEXT=$(kubectl config current-context) +echo "현재 Context: $CURRENT_CONTEXT" + +if [[ "$CURRENT_CONTEXT" != "minikube-remote" ]]; then + echo "⚠️ 경고: 현재 Context가 minikube-remote가 아닙니다." + read -p "계속 진행하시겠습니까? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "배포를 취소합니다." + exit 1 + fi +fi + +# 2. Namespace 생성 +echo "" +echo "[2/7] Namespace 생성..." +kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f - +kubectl config set-context --current --namespace=$NAMESPACE + +# 3. Secrets 적용 +echo "" +echo "[3/7] Secrets 적용..." +kubectl apply -f "$SCRIPT_DIR/secrets/" + +# 4. ConfigMaps 적용 +echo "" +echo "[4/7] ConfigMaps 적용..." +kubectl apply -f "$SCRIPT_DIR/configmaps/" + +# 5. Deployments 적용 +echo "" +echo "[5/7] Deployments 적용..." +kubectl apply -f "$SCRIPT_DIR/deployments/" + +# 6. Services 적용 +echo "" +echo "[6/7] Services 적용..." +kubectl apply -f "$SCRIPT_DIR/services/" + +# 7. Ingress 적용 +echo "" +echo "[7/7] Ingress 적용..." +kubectl apply -f "$SCRIPT_DIR/ingress/" + +# 배포 상태 대기 +echo "" +echo "==============================================" +echo " 배포 상태 확인 중..." +echo "==============================================" + +for service in api-gateway user-service bill-service product-service kos-mock; do + echo "" + echo "⏳ $service 배포 대기 중..." + kubectl rollout status deployment/$service -n $NAMESPACE --timeout=180s || { + echo "❌ $service 배포 실패" + kubectl describe deployment/$service -n $NAMESPACE + exit 1 + } + echo "✅ $service 배포 완료" +done + +# 최종 상태 출력 +echo "" +echo "==============================================" +echo " 배포 완료!" +echo "==============================================" +echo "" +echo "📦 Pods 상태:" +kubectl get pods -n $NAMESPACE +echo "" +echo "🔗 Services:" +kubectl get svc -n $NAMESPACE +echo "" +echo "🌐 Ingress:" +kubectl get ingress -n $NAMESPACE +echo "" +echo "==============================================" +echo " 접속 방법" +echo "==============================================" +echo "" +echo "API Gateway Ingress Host:" +echo " phonebill-api.72.155.72.236.nip.io" +echo "" +echo "Health Check:" +echo " curl http://phonebill-api.72.155.72.236.nip.io/actuator/health" +echo "" diff --git a/deployment/k8s/deploy-k8s-guide.md b/deployment/k8s/deploy-k8s-guide.md deleted file mode 100644 index c239b18..0000000 --- a/deployment/k8s/deploy-k8s-guide.md +++ /dev/null @@ -1,313 +0,0 @@ -# 통신요금 관리 서비스 백엔드 Kubernetes 배포 가이드 - -## 📋 배포 개요 - -**시스템명**: phonebill -**네임스페이스**: phonebill-dev -**ACR명**: acrdigitalgarage01 -**k8s명**: aks-digitalgarage-01 -**파드수**: 1개 (각 서비스) -**리소스**: CPU 256m/1024m, 메모리 256Mi/1024Mi - -## 🎯 배포 대상 서비스 - -| 서비스명 | 포트 | 엔드포인트 | -|---------|------|-----------| -| api-gateway | 8080 | Gateway 및 라우팅 | -| user-service | 8081 | /api/v1/auth, /api/v1/users | -| bill-service | 8082 | /api/v1/bills | -| product-service | 8083 | /api/v1/products | -| kos-mock | 8084 | /api/v1/kos | - -## 📋 배포 전 검증 결과 - -### ✅ 검증 완료 항목 -- 객체이름 네이밍룰 준수 -- Secret에서 stringData 사용 -- JWT_SECRET 실행 프로파일 값 적용 -- Image 경로 올바른 형식 -- Service/Ingress 포트 매핑 일치 (80번) -- Controller @RequestMapping 기반 path 설정 -- 보안 환경변수 Secret 분리 -- REDIS_DATABASE 서비스별 구분 (0,1,2) -- envFrom 사용으로 환경변수 주입 -- 실행 프로파일 전체 환경변수 매핑 완료 - -### ✅ 배포 전 확인 완료 사항 -모든 환경 정보가 확인되어 매니페스트 파일에 반영 완료되었습니다: - -1. **✅ Ingress Controller External IP**: `20.214.196.128` -2. **✅ ACR 인증 정보**: `acrdigitalgarage01` / 실제 패스워드 적용 -3. **✅ Redis Service**: `redis-cache-dev-master` -4. **✅ Database Services**: - - User Service: `auth-postgres-dev-postgresql` - - Bill Service: `bill-inquiry-postgres-dev-postgresql` - - Product Service: `product-change-postgres-dev-postgresql` - -## 🔧 사전 확인 방법 - -### 1. Azure 로그인 상태 확인 -```bash -az account show -``` - -### 2. AKS Credential 확인 -```bash -kubectl cluster-info -``` - -### 3. 네임스페이스 존재 확인 -```bash -kubectl get ns phonebill-dev -``` - -### 4. Ingress Controller External IP 확인 ✅ -```bash -kubectl get svc ingress-nginx-controller -n ingress-nginx -``` -**확인 완료**: EXTERNAL-IP = `20.214.196.128` - -### 5. ACR 인증 정보 확인 ✅ -```bash -# USERNAME 확인 -USERNAME=$(az acr credential show -n acrdigitalgarage01 --query "username" -o tsv) -echo $USERNAME - -# PASSWORD 확인 -PASSWORD=$(az acr credential show -n acrdigitalgarage01 --query "passwords[0].value" -o tsv) -echo $PASSWORD -``` -**확인 완료**: USERNAME = `acrdigitalgarage01`, PASSWORD = 실제 값 적용 - -### 6. Redis Service 이름 확인 ✅ -```bash -kubectl get svc -n phonebill-dev | grep redis -``` -**확인 완료**: `redis-cache-dev-master` (ClusterIP) - -### 7. Database Service 이름 확인 ✅ -```bash -# 각 서비스별 DB 확인 -kubectl get svc -n phonebill-dev | grep auth -kubectl get svc -n phonebill-dev | grep bill -kubectl get svc -n phonebill-dev | grep product -``` -**확인 완료**: -- User Service: `auth-postgres-dev-postgresql` -- Bill Service: `bill-inquiry-postgres-dev-postgresql` -- Product Service: `product-change-postgres-dev-postgresql` - -## ✅ 매니페스트 업데이트 완료 - -모든 매니페스트 파일이 실제 환경 정보로 업데이트 완료되었습니다: - -### 1. ✅ Ingress External IP 적용 -`deployment/k8s/common/ingress.yaml`: -```yaml -host: phonebill-api.20.214.196.128.nip.io -``` - -### 2. ✅ CORS Origins 적용 -`deployment/k8s/common/cm-common.yaml`: -```yaml -CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill.20.214.196.128.nip.io" -``` - -### 3. ✅ ACR 인증 정보 적용 -`deployment/k8s/common/secret-imagepull.yaml`: -```yaml -stringData: - .dockerconfigjson: | - { - "auths": { - "acrdigitalgarage01.azurecr.io": { - "username": "acrdigitalgarage01", - "password": "+OY+rmOagorjWvQe/tTk6oqvnZI8SmNbY/Y2o5EDcY+ACRDCDbYk", - "auth": "YWNyZGlnaXRhbGdhcmFnZTAxOitPWStybU9hZ29yald2UWUvdFRrNm9xdm5aSThTbU5iWS9ZMm81RURjWStBQ1JEQ0RiWWs=" - } - } - } -``` - -### 4. ✅ Redis Host 적용 -`deployment/k8s/common/secret-common.yaml`: -```yaml -REDIS_HOST: "redis-cache-dev-master" -``` - -### 5. ✅ Database Host 적용 - -**user-service**: `deployment/k8s/user-service/secret-user-service.yaml` -```yaml -DB_HOST: "auth-postgres-dev-postgresql" -``` - -**bill-service**: `deployment/k8s/bill-service/secret-bill-service.yaml` -```yaml -DB_HOST: "bill-inquiry-postgres-dev-postgresql" -``` - -**product-service**: `deployment/k8s/product-service/secret-product-service.yaml` -```yaml -DB_HOST: "product-change-postgres-dev-postgresql" -``` - -## 🚀 배포 실행 가이드 - -### 1. 공통 매니페스트 적용 -```bash -kubectl apply -f deployment/k8s/common/ -``` - -### 2. 서비스별 매니페스트 적용 -```bash -# 각 서비스 순차 적용 -kubectl apply -f deployment/k8s/api-gateway/ -kubectl apply -f deployment/k8s/user-service/ -kubectl apply -f deployment/k8s/bill-service/ -kubectl apply -f deployment/k8s/product-service/ -kubectl apply -f deployment/k8s/kos-mock/ -``` - -### 3. 배포 상태 확인 - -#### 전체 객체 확인 -```bash -kubectl get all -n phonebill-dev -``` - -#### Pod 상태 확인 -```bash -kubectl get pods -n phonebill-dev -``` - -#### Service 확인 -```bash -kubectl get svc -n phonebill-dev -``` - -#### Ingress 확인 -```bash -kubectl get ingress -n phonebill-dev -``` - -#### ConfigMap/Secret 확인 -```bash -kubectl get cm,secret -n phonebill-dev -``` - -### 4. 로그 확인 -```bash -# 특정 서비스 로그 확인 -kubectl logs -f deployment/user-service -n phonebill-dev -kubectl logs -f deployment/bill-service -n phonebill-dev -kubectl logs -f deployment/product-service -n phonebill-dev -kubectl logs -f deployment/api-gateway -n phonebill-dev -kubectl logs -f deployment/kos-mock -n phonebill-dev -``` - -### 5. Health Check 확인 -```bash -# 각 서비스 Health 상태 확인 (Pod 내부에서) -kubectl exec -n phonebill-dev deployment/user-service -- curl http://localhost:8081/actuator/health -kubectl exec -n phonebill-dev deployment/bill-service -- curl http://localhost:8082/actuator/health -kubectl exec -n phonebill-dev deployment/product-service -- curl http://localhost:8083/actuator/health -``` - -## 🔍 문제 해결 가이드 - -### Pod 시작 실패시 -```bash -# Pod 상세 정보 확인 -kubectl describe pod -n phonebill-dev - -# 이벤트 확인 -kubectl get events -n phonebill-dev --sort-by='.lastTimestamp' -``` - -### ConfigMap/Secret 변경시 -```bash -# 변경 후 Pod 재시작 -kubectl rollout restart deployment/ -n phonebill-dev -``` - -### 네트워크 연결 문제 -```bash -# Service DNS 해결 테스트 -kubectl exec -n phonebill-dev deployment/api-gateway -- nslookup user-service -``` - -## 📊 환경변수 매핑 테이블 - -| 서비스명 | 환경변수 | 지정 객체명 | 환경변수값 | -|---------|---------|-----------|-----------| -| api-gateway | SERVER_PORT | cm-api-gateway | 8080 | -| api-gateway | BILL_SERVICE_URL | cm-api-gateway | http://bill-service | -| api-gateway | PRODUCT_SERVICE_URL | cm-api-gateway | http://product-service | -| api-gateway | USER_SERVICE_URL | cm-api-gateway | http://user-service | -| api-gateway | KOS_MOCK_URL | cm-api-gateway | http://kos-mock | -| 공통 | CORS_ALLOWED_ORIGINS | cm-common | http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://phonebill.{EXTERNAL_IP}.nip.io | -| 공통 | JWT_ACCESS_TOKEN_VALIDITY | cm-common | 18000000 | -| 공통 | JWT_REFRESH_TOKEN_VALIDITY | cm-common | 86400000 | -| 공통 | JWT_SECRET | secret-common | (base64 encoded JWT secret) | -| 공통 | REDIS_HOST | secret-common | (Redis 서비스명) | -| 공통 | REDIS_PASSWORD | secret-common | Redis2025Dev! | -| 공통 | REDIS_PORT | cm-common | 6379 | -| 공통 | SPRING_PROFILES_ACTIVE | cm-common | dev | -| user-service | SERVER_PORT | cm-user-service | 8081 | -| user-service | DB_KIND | cm-user-service | postgresql | -| user-service | DB_PORT | cm-user-service | 5432 | -| user-service | DDL_AUTO | cm-user-service | update | -| user-service | REDIS_DATABASE | cm-user-service | 0 | -| user-service | SHOW_SQL | cm-user-service | true | -| user-service | DB_HOST | secret-user-service | (Auth DB 서비스명) | -| user-service | DB_NAME | secret-user-service | phonebill_auth | -| user-service | DB_USERNAME | secret-user-service | auth_user | -| user-service | DB_PASSWORD | secret-user-service | AuthUser2025! | -| bill-service | SERVER_PORT | cm-bill-service | 8082 | -| bill-service | DB_KIND | cm-bill-service | postgresql | -| bill-service | DB_PORT | cm-bill-service | 5432 | -| bill-service | DB_CONNECTION_TIMEOUT | cm-bill-service | 30000 | -| bill-service | DB_IDLE_TIMEOUT | cm-bill-service | 600000 | -| bill-service | DB_LEAK_DETECTION | cm-bill-service | 60000 | -| bill-service | DB_MAX_LIFETIME | cm-bill-service | 1800000 | -| bill-service | DB_MAX_POOL | cm-bill-service | 20 | -| bill-service | DB_MIN_IDLE | cm-bill-service | 5 | -| bill-service | KOS_BASE_URL | cm-bill-service | http://kos-mock | -| bill-service | LOG_FILE_NAME | cm-bill-service | logs/bill-service.log | -| bill-service | REDIS_DATABASE | cm-bill-service | 1 | -| bill-service | REDIS_MAX_ACTIVE | cm-bill-service | 8 | -| bill-service | REDIS_MAX_IDLE | cm-bill-service | 8 | -| bill-service | REDIS_MAX_WAIT | cm-bill-service | -1 | -| bill-service | REDIS_MIN_IDLE | cm-bill-service | 0 | -| bill-service | REDIS_TIMEOUT | cm-bill-service | 2000 | -| bill-service | DB_HOST | secret-bill-service | (Bill DB 서비스명) | -| bill-service | DB_NAME | secret-bill-service | bill_inquiry_db | -| bill-service | DB_USERNAME | secret-bill-service | bill_inquiry_user | -| bill-service | DB_PASSWORD | secret-bill-service | BillUser2025! | -| product-service | SERVER_PORT | cm-product-service | 8083 | -| product-service | DB_KIND | cm-product-service | postgresql | -| product-service | DB_PORT | cm-product-service | 5432 | -| product-service | DDL_AUTO | cm-product-service | update | -| product-service | KOS_BASE_URL | cm-product-service | http://kos-mock | -| product-service | KOS_CLIENT_ID | cm-product-service | product-service-dev | -| product-service | KOS_MOCK_ENABLED | cm-product-service | true | -| product-service | REDIS_DATABASE | cm-product-service | 2 | -| product-service | DB_HOST | secret-product-service | (Product DB 서비스명) | -| product-service | DB_NAME | secret-product-service | product_change_db | -| product-service | DB_USERNAME | secret-product-service | product_change_user | -| product-service | DB_PASSWORD | secret-product-service | ProductUser2025! | -| product-service | KOS_API_KEY | secret-product-service | dev-api-key | -| kos-mock | SERVER_PORT | cm-kos-mock | 8084 | - -## 🎯 배포 완료 후 접근 URL - -- **API Gateway**: http://phonebill-api.20.214.196.128.nip.io -- **Swagger UI**: http://phonebill-api.20.214.196.128.nip.io/swagger-ui/index.html -- **사용자 인증**: http://phonebill-api.20.214.196.128.nip.io/api/v1/auth -- **요금 조회**: http://phonebill-api.20.214.196.128.nip.io/api/v1/bills -- **상품 변경**: http://phonebill-api.20.214.196.128.nip.io/api/v1/products - ---- - -**✅ 배포 준비 완료**: 모든 환경 정보가 확인되어 매니페스트 파일에 반영되었습니다. 이제 바로 배포를 진행할 수 있습니다. \ No newline at end of file diff --git a/deployment/k8s/deploy-minikube-guide.md b/deployment/k8s/deploy-minikube-guide.md new file mode 100644 index 0000000..ea78388 --- /dev/null +++ b/deployment/k8s/deploy-minikube-guide.md @@ -0,0 +1,867 @@ +# Minikube 백엔드 서비스 배포 가이드 + +## 개요 +본 문서는 phonebill 프로젝트의 백엔드 마이크로서비스들을 Minikube 환경에 배포하는 방법을 설명합니다. + +## 배포 대상 서비스 + +| 서비스명 | 포트 | 설명 | +|---------|------|------| +| api-gateway | 8080 | API Gateway 서비스 | +| user-service | 8081 | 사용자 인증/관리 서비스 | +| bill-service | 8082 | 요금 조회 서비스 | +| product-service | 8083 | 상품 변경 서비스 | +| kos-mock | 8084 | KOS 목업 서비스 | + +## 배포 환경 정보 + +| 항목 | 값 | +|------|-----| +| Image Registry | docker.io | +| Image Organization | hiondal | +| Kubernetes Context | minikube-remote | +| Namespace | phonebill | +| Replicas | 1 | +| CPU Request/Limit | 256m/1024m | +| Memory Request/Limit | 256Mi/1024Mi | + +--- + +## 사전 준비 + +### 1. Minikube 상태 확인 +```bash +# context 전환 +kubectl config use-context minikube-remote + +# minikube 상태 확인 +minikube status + +# 노드 확인 +kubectl get nodes +``` + +### 2. Namespace 생성 +```bash +kubectl create namespace phonebill +kubectl config set-context --current --namespace=phonebill +``` + +### 3. Ingress Controller 활성화 +```bash +minikube addons enable ingress +``` + +### 4. 백킹 서비스 확인 +배포 전 PostgreSQL과 Redis가 실행 중인지 확인합니다: +```bash +# PostgreSQL 파드 확인 +kubectl get pods -n phonebill | grep postgres + +# Redis 파드 확인 +kubectl get pods -n phonebill | grep redis +``` + +--- + +## Step 1: 컨테이너 이미지 빌드 및 푸시 + +### 1.1 Gradle 빌드 +```bash +cd /Users/dreamondal/home/workspace/phonebill +./gradlew clean build -x test +``` + +### 1.2 Docker 이미지 빌드 +```bash +DOCKER_FILE=deployment/container/Dockerfile-backend +REGISTRY=docker.io +PROJECT=hiondal +TAG=latest + +# 각 서비스 이미지 빌드 +for service in api-gateway user-service bill-service product-service kos-mock; do + docker build \ + --platform linux/amd64 \ + --build-arg BUILD_LIB_DIR="${service}/build/libs" \ + --build-arg ARTIFACTORY_FILE="${service}.jar" \ + -f ${DOCKER_FILE} \ + -t ${REGISTRY}/${PROJECT}/${service}:${TAG} . +done +``` + +### 1.3 Docker Hub 푸시 +```bash +# Docker Hub 로그인 +docker login + +# 이미지 푸시 +for service in api-gateway user-service bill-service product-service kos-mock; do + docker push ${REGISTRY}/${PROJECT}/${service}:${TAG} +done +``` + +--- + +## Step 2: Secret 생성 + +### 2.1 공통 Secret (JWT, Redis) +```bash +kubectl create secret generic phonebill-common-secret \ + --namespace=phonebill \ + --from-literal=JWT_SECRET='your-jwt-secret-key-must-be-at-least-256-bits-long-for-hs256' \ + --from-literal=REDIS_PASSWORD='' +``` + +### 2.2 서비스별 DB Secret + +```bash +# user-service DB Secret +kubectl create secret generic user-service-db-secret \ + --namespace=phonebill \ + --from-literal=DB_USERNAME='postgres' \ + --from-literal=DB_PASSWORD='your-auth-db-password' + +# bill-service DB Secret +kubectl create secret generic bill-service-db-secret \ + --namespace=phonebill \ + --from-literal=DB_USERNAME='postgres' \ + --from-literal=DB_PASSWORD='your-bill-db-password' + +# product-service DB Secret +kubectl create secret generic product-service-db-secret \ + --namespace=phonebill \ + --from-literal=DB_USERNAME='postgres' \ + --from-literal=DB_PASSWORD='your-product-db-password' +``` + +--- + +## Step 3: ConfigMap 생성 + +### 3.1 공통 ConfigMap +```bash +kubectl apply -f - < **참고**: nip.io 도메인을 사용하므로 별도의 hosts 파일 설정이 필요하지 않습니다. + +--- + +## Step 7: 배포 확인 + +### 7.1 파드 상태 확인 +```bash +kubectl get pods -n phonebill -w +``` + +### 7.2 로그 확인 +```bash +# 각 서비스 로그 확인 +kubectl logs -f deployment/api-gateway -n phonebill +kubectl logs -f deployment/user-service -n phonebill +kubectl logs -f deployment/bill-service -n phonebill +kubectl logs -f deployment/product-service -n phonebill +kubectl logs -f deployment/kos-mock -n phonebill +``` + +### 7.3 서비스 엔드포인트 확인 +```bash +kubectl get endpoints -n phonebill +``` + +### 7.4 Ingress 상태 확인 +```bash +kubectl get ingress -n phonebill +``` + +--- + +## Step 8: API 테스트 + +### 8.1 Health Check +```bash +# API Gateway Health Check +curl http://phonebill-api.72.155.72.236.nip.io/actuator/health +``` + +### 8.2 서비스 API 테스트 +```bash +# 사용자 등록 API +curl -X POST http://phonebill-api.72.155.72.236.nip.io/api/users/register \ + -H "Content-Type: application/json" \ + -d '{ + "userId": "testuser", + "password": "Test1234!", + "name": "테스트사용자", + "email": "test@example.com", + "phoneNumber": "01012345678" + }' + +# 로그인 API +curl -X POST http://phonebill-api.72.155.72.236.nip.io/api/users/login \ + -H "Content-Type: application/json" \ + -d '{ + "userId": "testuser", + "password": "Test1234!" + }' +``` + +--- + +## 트러블슈팅 + +### 파드가 시작되지 않는 경우 +```bash +# 파드 상세 정보 확인 +kubectl describe pod -n phonebill + +# 이벤트 확인 +kubectl get events -n phonebill --sort-by='.lastTimestamp' +``` + +### 이미지 풀 실패 +```bash +# Docker Hub 인증 Secret 생성 (필요시) +kubectl create secret docker-registry regcred \ + --namespace=phonebill \ + --docker-server=https://index.docker.io/v1/ \ + --docker-username= \ + --docker-password= \ + --docker-email= + +# Deployment에 imagePullSecrets 추가 +kubectl patch deployment -n phonebill \ + -p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"regcred"}]}}}}' +``` + +### DB 연결 실패 +```bash +# PostgreSQL 서비스 확인 +kubectl get svc -n phonebill | grep postgres + +# DNS 해석 테스트 +kubectl run -it --rm debug --image=busybox --restart=Never -n phonebill \ + -- nslookup auth-postgres.phonebill.svc.cluster.local +``` + +### Redis 연결 실패 +```bash +# Redis 서비스 확인 +kubectl get svc -n phonebill | grep redis + +# Redis 연결 테스트 +kubectl run -it --rm redis-cli --image=redis:alpine --restart=Never -n phonebill \ + -- redis-cli -h redis-cache.phonebill.svc.cluster.local ping +``` + +--- + +## 리소스 정리 + +### 전체 삭제 +```bash +kubectl delete namespace phonebill +``` + +### 개별 삭제 +```bash +# Ingress 삭제 +kubectl delete ingress phonebill-ingress -n phonebill + +# 서비스 삭제 +kubectl delete svc api-gateway user-service bill-service product-service kos-mock -n phonebill + +# Deployment 삭제 +kubectl delete deployment api-gateway user-service bill-service product-service kos-mock -n phonebill + +# ConfigMap 삭제 +kubectl delete configmap phonebill-common-config api-gateway-config user-service-config \ + bill-service-config product-service-config kos-mock-config -n phonebill + +# Secret 삭제 +kubectl delete secret phonebill-common-secret user-service-db-secret \ + bill-service-db-secret product-service-db-secret -n phonebill +``` + +--- + +## 원클릭 배포 스크립트 + +전체 배포를 한 번에 수행하는 스크립트: + +```bash +#!/bin/bash +# deploy-all.sh + +NAMESPACE=phonebill +REGISTRY=docker.io +PROJECT=hiondal +TAG=latest + +echo "=== Minikube Backend 배포 시작 ===" + +# 1. Namespace 생성 +kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f - + +# 2. Secrets 생성 +echo "Creating secrets..." +kubectl create secret generic phonebill-common-secret \ + --namespace=$NAMESPACE \ + --from-literal=JWT_SECRET='your-jwt-secret-key-must-be-at-least-256-bits-long-for-hs256' \ + --from-literal=REDIS_PASSWORD='' \ + --dry-run=client -o yaml | kubectl apply -f - + +# 3. ConfigMaps 적용 +echo "Applying ConfigMaps..." +kubectl apply -f deployment/k8s/configmaps/ + +# 4. Deployments 적용 +echo "Applying Deployments..." +kubectl apply -f deployment/k8s/deployments/ + +# 5. Services 적용 +echo "Applying Services..." +kubectl apply -f deployment/k8s/services/ + +# 6. Ingress 적용 +echo "Applying Ingress..." +kubectl apply -f deployment/k8s/ingress/ + +# 7. 배포 상태 확인 +echo "Waiting for deployments..." +kubectl rollout status deployment/api-gateway -n $NAMESPACE --timeout=120s +kubectl rollout status deployment/user-service -n $NAMESPACE --timeout=120s +kubectl rollout status deployment/bill-service -n $NAMESPACE --timeout=120s +kubectl rollout status deployment/product-service -n $NAMESPACE --timeout=120s +kubectl rollout status deployment/kos-mock -n $NAMESPACE --timeout=120s + +echo "=== 배포 완료 ===" +kubectl get pods -n $NAMESPACE +``` + +--- + +## 작성자 +- **작성자**: 최운영(데옵스) +- **작성일**: 2025-11-29 +- **버전**: 1.0.0 diff --git a/deployment/k8s/api-gateway/deployment.yaml b/deployment/k8s/deployments/api-gateway.yaml similarity index 54% rename from deployment/k8s/api-gateway/deployment.yaml rename to deployment/k8s/deployments/api-gateway.yaml index ceb7e8c..469f151 100644 --- a/deployment/k8s/api-gateway/deployment.yaml +++ b/deployment/k8s/deployments/api-gateway.yaml @@ -2,6 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: name: api-gateway + namespace: phonebill + labels: + app: api-gateway + app.kubernetes.io/part-of: phonebill spec: replicas: 1 selector: @@ -12,46 +16,40 @@ spec: labels: app: api-gateway spec: - imagePullSecrets: - - name: phonebill containers: - name: api-gateway - image: acrdigitalgarage01.azurecr.io/phonebill/api-gateway:latest + image: docker.io/hiondal/api-gateway:latest imagePullPolicy: Always ports: - containerPort: 8080 + name: http envFrom: - configMapRef: - name: cm-common + name: phonebill-common-config - configMapRef: - name: cm-api-gateway + name: api-gateway-config - secretRef: - name: secret-common + name: phonebill-common-secret resources: requests: - cpu: 256m - memory: 256Mi + 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 + cpu: "1024m" + memory: "1024Mi" livenessProbe: httpGet: - path: /health + path: /actuator/health/liveness + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness port: 8080 initialDelaySeconds: 30 periodSeconds: 10 - failureThreshold: 3 \ No newline at end of file + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/deployment/k8s/bill-service/deployment.yaml b/deployment/k8s/deployments/bill-service.yaml similarity index 58% rename from deployment/k8s/bill-service/deployment.yaml rename to deployment/k8s/deployments/bill-service.yaml index 78a42dd..a57eff2 100644 --- a/deployment/k8s/bill-service/deployment.yaml +++ b/deployment/k8s/deployments/bill-service.yaml @@ -2,6 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: name: bill-service + namespace: phonebill + labels: + app: bill-service + app.kubernetes.io/part-of: phonebill spec: replicas: 1 selector: @@ -12,48 +16,42 @@ spec: labels: app: bill-service spec: - imagePullSecrets: - - name: phonebill containers: - name: bill-service - image: acrdigitalgarage01.azurecr.io/phonebill/bill-service:latest + image: docker.io/hiondal/bill-service:latest imagePullPolicy: Always ports: - containerPort: 8082 + name: http envFrom: - configMapRef: - name: cm-common + name: phonebill-common-config - configMapRef: - name: cm-bill-service + name: bill-service-config - secretRef: - name: secret-common + name: phonebill-common-secret - secretRef: - name: secret-bill-service + name: bill-service-db-secret resources: requests: - cpu: 256m - memory: 256Mi + 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 + cpu: "1024m" + memory: "1024Mi" livenessProbe: httpGet: path: /actuator/health/liveness port: 8082 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8082 initialDelaySeconds: 30 periodSeconds: 10 - failureThreshold: 3 \ No newline at end of file + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/deployment/k8s/kos-mock/deployment.yaml b/deployment/k8s/deployments/kos-mock.yaml similarity index 59% rename from deployment/k8s/kos-mock/deployment.yaml rename to deployment/k8s/deployments/kos-mock.yaml index bd588f4..6c9a7ee 100644 --- a/deployment/k8s/kos-mock/deployment.yaml +++ b/deployment/k8s/deployments/kos-mock.yaml @@ -2,6 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: name: kos-mock + namespace: phonebill + labels: + app: kos-mock + app.kubernetes.io/part-of: phonebill spec: replicas: 1 selector: @@ -12,46 +16,40 @@ spec: labels: app: kos-mock spec: - imagePullSecrets: - - name: phonebill containers: - name: kos-mock - image: acrdigitalgarage01.azurecr.io/phonebill/kos-mock:latest + image: docker.io/hiondal/kos-mock:latest imagePullPolicy: Always ports: - containerPort: 8084 + name: http envFrom: - configMapRef: - name: cm-common + name: phonebill-common-config - configMapRef: - name: cm-kos-mock + name: kos-mock-config - secretRef: - name: secret-common + name: phonebill-common-secret resources: requests: - cpu: 256m - memory: 256Mi + 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 + cpu: "1024m" + memory: "1024Mi" livenessProbe: httpGet: path: /actuator/health/liveness port: 8084 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8084 initialDelaySeconds: 30 periodSeconds: 10 - failureThreshold: 3 \ No newline at end of file + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/deployment/k8s/product-service/deployment.yaml b/deployment/k8s/deployments/product-service.yaml similarity index 58% rename from deployment/k8s/product-service/deployment.yaml rename to deployment/k8s/deployments/product-service.yaml index 0b463a3..e7ca7db 100644 --- a/deployment/k8s/product-service/deployment.yaml +++ b/deployment/k8s/deployments/product-service.yaml @@ -2,6 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: name: product-service + namespace: phonebill + labels: + app: product-service + app.kubernetes.io/part-of: phonebill spec: replicas: 1 selector: @@ -12,48 +16,42 @@ spec: labels: app: product-service spec: - imagePullSecrets: - - name: phonebill containers: - name: product-service - image: acrdigitalgarage01.azurecr.io/phonebill/product-service:latest + image: docker.io/hiondal/product-service:latest imagePullPolicy: Always ports: - containerPort: 8083 + name: http envFrom: - configMapRef: - name: cm-common + name: phonebill-common-config - configMapRef: - name: cm-product-service + name: product-service-config - secretRef: - name: secret-common + name: phonebill-common-secret - secretRef: - name: secret-product-service + name: product-service-db-secret resources: requests: - cpu: 256m - memory: 256Mi + 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 + cpu: "1024m" + memory: "1024Mi" livenessProbe: httpGet: path: /actuator/health/liveness port: 8083 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8083 initialDelaySeconds: 30 periodSeconds: 10 - failureThreshold: 3 \ No newline at end of file + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/deployment/k8s/user-service/deployment.yaml b/deployment/k8s/deployments/user-service.yaml similarity index 58% rename from deployment/k8s/user-service/deployment.yaml rename to deployment/k8s/deployments/user-service.yaml index 2dbd2d4..ddd9095 100644 --- a/deployment/k8s/user-service/deployment.yaml +++ b/deployment/k8s/deployments/user-service.yaml @@ -2,6 +2,10 @@ apiVersion: apps/v1 kind: Deployment metadata: name: user-service + namespace: phonebill + labels: + app: user-service + app.kubernetes.io/part-of: phonebill spec: replicas: 1 selector: @@ -12,48 +16,42 @@ spec: labels: app: user-service spec: - imagePullSecrets: - - name: phonebill containers: - name: user-service - image: acrdigitalgarage01.azurecr.io/phonebill/user-service:latest + image: docker.io/hiondal/user-service:latest imagePullPolicy: Always ports: - containerPort: 8081 + name: http envFrom: - configMapRef: - name: cm-common + name: phonebill-common-config - configMapRef: - name: cm-user-service + name: user-service-config - secretRef: - name: secret-common + name: phonebill-common-secret - secretRef: - name: secret-user-service + name: user-service-db-secret resources: requests: - cpu: 256m - memory: 256Mi + 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 + cpu: "1024m" + memory: "1024Mi" livenessProbe: httpGet: path: /actuator/health/liveness port: 8081 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8081 initialDelaySeconds: 30 periodSeconds: 10 - failureThreshold: 3 \ No newline at end of file + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/deployment/k8s/ingress/phonebill-ingress.yaml b/deployment/k8s/ingress/phonebill-ingress.yaml new file mode 100644 index 0000000..c05d65b --- /dev/null +++ b/deployment/k8s/ingress/phonebill-ingress.yaml @@ -0,0 +1,25 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: phonebill-ingress + namespace: phonebill + labels: + app.kubernetes.io/part-of: phonebill + annotations: + nginx.ingress.kubernetes.io/proxy-body-size: "50m" + nginx.ingress.kubernetes.io/proxy-read-timeout: "60" + nginx.ingress.kubernetes.io/proxy-send-timeout: "60" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "60" +spec: + ingressClassName: nginx + rules: + - host: phonebill-api.72.155.72.236.nip.io + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: api-gateway + port: + number: 8080 diff --git a/deployment/k8s/kos-mock/cm-kos-mock.yaml b/deployment/k8s/kos-mock/cm-kos-mock.yaml deleted file mode 100644 index 3e55476..0000000 --- a/deployment/k8s/kos-mock/cm-kos-mock.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: cm-kos-mock -data: - SERVER_PORT: "8084" \ No newline at end of file diff --git a/deployment/k8s/kos-mock/service.yaml b/deployment/k8s/kos-mock/service.yaml deleted file mode 100644 index fdb5336..0000000 --- a/deployment/k8s/kos-mock/service.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: kos-mock -spec: - selector: - app: kos-mock - ports: - - port: 80 - targetPort: 8084 - type: ClusterIP \ No newline at end of file diff --git a/deployment/k8s/product-service/cm-product-service.yaml b/deployment/k8s/product-service/cm-product-service.yaml deleted file mode 100644 index 5a3893d..0000000 --- a/deployment/k8s/product-service/cm-product-service.yaml +++ /dev/null @@ -1,10 +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" \ No newline at end of file diff --git a/deployment/k8s/product-service/secret-product-service.yaml b/deployment/k8s/product-service/secret-product-service.yaml deleted file mode 100644 index e773ec9..0000000 --- a/deployment/k8s/product-service/secret-product-service.yaml +++ /dev/null @@ -1,10 +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@" \ No newline at end of file diff --git a/deployment/k8s/product-service/service.yaml b/deployment/k8s/product-service/service.yaml deleted file mode 100644 index b784a5d..0000000 --- a/deployment/k8s/product-service/service.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: product-service -spec: - selector: - app: product-service - ports: - - port: 80 - targetPort: 8083 - type: ClusterIP \ No newline at end of file diff --git a/deployment/k8s/secrets/bill-service-db-secret.yaml b/deployment/k8s/secrets/bill-service-db-secret.yaml new file mode 100644 index 0000000..64245b9 --- /dev/null +++ b/deployment/k8s/secrets/bill-service-db-secret.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + name: bill-service-db-secret + namespace: phonebill + labels: + app: bill-service + app.kubernetes.io/part-of: phonebill +type: Opaque +stringData: + DB_USERNAME: "unicorn" + DB_PASSWORD: "P@ssw0rd$" diff --git a/deployment/k8s/secrets/common-secret.yaml b/deployment/k8s/secrets/common-secret.yaml new file mode 100644 index 0000000..6fc3772 --- /dev/null +++ b/deployment/k8s/secrets/common-secret.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Secret +metadata: + name: phonebill-common-secret + namespace: phonebill + labels: + app.kubernetes.io/part-of: phonebill +type: Opaque +stringData: + # JWT Secret (최소 256비트 이상, HS256 알고리즘용) + JWT_SECRET: "EK1ZV7vROOXREXbYe/BCISdQq0Yklk9JtoA2v88ux1DBDc0bDGiRRxHeDSb7GHkDP9IUYHMVsBi4/1rS4OhfRg==" + # Redis 비밀번호 (비밀번호 없는 경우 빈 값) + REDIS_PASSWORD: "P@ssw0rd$" diff --git a/deployment/k8s/secrets/product-service-db-secret.yaml b/deployment/k8s/secrets/product-service-db-secret.yaml new file mode 100644 index 0000000..aae620c --- /dev/null +++ b/deployment/k8s/secrets/product-service-db-secret.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + name: product-service-db-secret + namespace: phonebill + labels: + app: product-service + app.kubernetes.io/part-of: phonebill +type: Opaque +stringData: + DB_USERNAME: "unicorn" + DB_PASSWORD: "P@ssw0rd$" diff --git a/deployment/k8s/secrets/user-service-db-secret.yaml b/deployment/k8s/secrets/user-service-db-secret.yaml new file mode 100644 index 0000000..2464171 --- /dev/null +++ b/deployment/k8s/secrets/user-service-db-secret.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + name: user-service-db-secret + namespace: phonebill + labels: + app: user-service + app.kubernetes.io/part-of: phonebill +type: Opaque +stringData: + DB_USERNAME: "unicorn" + DB_PASSWORD: "P@ssw0rd$" diff --git a/deployment/k8s/api-gateway/service.yaml b/deployment/k8s/services/api-gateway.yaml similarity index 50% rename from deployment/k8s/api-gateway/service.yaml rename to deployment/k8s/services/api-gateway.yaml index e1f6460..21e6dba 100644 --- a/deployment/k8s/api-gateway/service.yaml +++ b/deployment/k8s/services/api-gateway.yaml @@ -2,10 +2,16 @@ apiVersion: v1 kind: Service metadata: name: api-gateway + namespace: phonebill + labels: + app: api-gateway + app.kubernetes.io/part-of: phonebill spec: + type: ClusterIP selector: app: api-gateway ports: - - port: 80 + - name: http + port: 8080 targetPort: 8080 - type: ClusterIP + protocol: TCP diff --git a/deployment/k8s/services/bill-service.yaml b/deployment/k8s/services/bill-service.yaml new file mode 100644 index 0000000..12dc0ef --- /dev/null +++ b/deployment/k8s/services/bill-service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: bill-service + namespace: phonebill + labels: + app: bill-service + app.kubernetes.io/part-of: phonebill +spec: + type: ClusterIP + selector: + app: bill-service + ports: + - name: http + port: 8082 + targetPort: 8082 + protocol: TCP diff --git a/deployment/k8s/services/kos-mock.yaml b/deployment/k8s/services/kos-mock.yaml new file mode 100644 index 0000000..b912ff2 --- /dev/null +++ b/deployment/k8s/services/kos-mock.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: kos-mock + namespace: phonebill + labels: + app: kos-mock + app.kubernetes.io/part-of: phonebill +spec: + type: ClusterIP + selector: + app: kos-mock + ports: + - name: http + port: 8084 + targetPort: 8084 + protocol: TCP diff --git a/deployment/k8s/services/product-service.yaml b/deployment/k8s/services/product-service.yaml new file mode 100644 index 0000000..10bc374 --- /dev/null +++ b/deployment/k8s/services/product-service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: product-service + namespace: phonebill + labels: + app: product-service + app.kubernetes.io/part-of: phonebill +spec: + type: ClusterIP + selector: + app: product-service + ports: + - name: http + port: 8083 + targetPort: 8083 + protocol: TCP diff --git a/deployment/k8s/services/user-service.yaml b/deployment/k8s/services/user-service.yaml new file mode 100644 index 0000000..f782c5d --- /dev/null +++ b/deployment/k8s/services/user-service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: user-service + namespace: phonebill + labels: + app: user-service + app.kubernetes.io/part-of: phonebill +spec: + type: ClusterIP + selector: + app: user-service + ports: + - name: http + port: 8081 + targetPort: 8081 + protocol: TCP diff --git a/deployment/k8s/user-service/cm-user-service.yaml b/deployment/k8s/user-service/cm-user-service.yaml deleted file mode 100644 index 4031913..0000000 --- a/deployment/k8s/user-service/cm-user-service.yaml +++ /dev/null @@ -1,11 +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" \ No newline at end of file diff --git a/deployment/k8s/user-service/secret-user-service.yaml b/deployment/k8s/user-service/secret-user-service.yaml deleted file mode 100644 index 8424423..0000000 --- a/deployment/k8s/user-service/secret-user-service.yaml +++ /dev/null @@ -1,10 +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@" \ No newline at end of file diff --git a/deployment/k8s/user-service/service.yaml b/deployment/k8s/user-service/service.yaml deleted file mode 100644 index c9fb9cf..0000000 --- a/deployment/k8s/user-service/service.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: user-service -spec: - selector: - app: user-service - ports: - - port: 80 - targetPort: 8081 - type: ClusterIP \ No newline at end of file diff --git a/develop/database/exec/auth-postgres-values.yaml b/develop/database/exec/auth-postgres-values.yaml index 9acf0d9..b5daa25 100644 --- a/develop/database/exec/auth-postgres-values.yaml +++ b/develop/database/exec/auth-postgres-values.yaml @@ -7,7 +7,7 @@ global: database: "phonebill_auth" username: "auth_user" password: "AuthUser2025@" - storageClass: "managed" + storageClass: "standard" # Primary 설정 (개발환경 단독 구성) architecture: standalone @@ -25,7 +25,7 @@ primary: # 스토리지 설정 persistence: enabled: true - storageClass: "managed" + storageClass: "standard" size: 20Gi # PostgreSQL 성능 설정 (개발환경 최적화) diff --git a/develop/database/exec/bill-inquiry-postgres-values.yaml b/develop/database/exec/bill-inquiry-postgres-values.yaml index 5dc4e26..5dd3aed 100644 --- a/develop/database/exec/bill-inquiry-postgres-values.yaml +++ b/develop/database/exec/bill-inquiry-postgres-values.yaml @@ -7,7 +7,7 @@ global: database: "bill_inquiry_db" username: "bill_inquiry_user" password: "BillUser2025@" - storageClass: "managed" + storageClass: "standard" # Primary 설정 (개발환경 단독 구성) architecture: standalone @@ -25,7 +25,7 @@ primary: # 스토리지 설정 persistence: enabled: true - storageClass: "managed" + storageClass: "standard" size: 20Gi # PostgreSQL 성능 설정 (개발환경 최적화) diff --git a/develop/database/exec/product-change-postgres-values.yaml b/develop/database/exec/product-change-postgres-values.yaml index 21db70c..8fc7b34 100644 --- a/develop/database/exec/product-change-postgres-values.yaml +++ b/develop/database/exec/product-change-postgres-values.yaml @@ -7,7 +7,7 @@ global: database: "product_change_db" username: "product_change_user" password: "ProductUser2025@" - storageClass: "managed" + storageClass: "standard" # Primary 설정 (개발환경 단독 구성) architecture: standalone @@ -25,7 +25,7 @@ primary: # 스토리지 설정 persistence: enabled: true - storageClass: "managed" + storageClass: "standard" size: 20Gi # PostgreSQL 성능 설정 (개발환경 최적화) diff --git a/develop/database/exec/redis-cache-values.yaml b/develop/database/exec/redis-cache-values.yaml index 6170e19..059d24c 100644 --- a/develop/database/exec/redis-cache-values.yaml +++ b/develop/database/exec/redis-cache-values.yaml @@ -1,7 +1,7 @@ # values.yaml - Redis Cache 개발환경 설정 # Redis 기본 설정 global: - storageClass: "managed" + storageClass: "standard" # 아키텍처 (개발환경 단일 구성) architecture: standalone diff --git a/kos-mock/data/kos_mock.mv.db b/kos-mock/data/kos_mock.mv.db index 1912b3a..974dd1a 100644 Binary files a/kos-mock/data/kos_mock.mv.db and b/kos-mock/data/kos_mock.mv.db differ diff --git a/kos-mock/data/kos_mock.trace.db b/kos-mock/data/kos_mock.trace.db deleted file mode 100644 index bd3eb23..0000000 --- a/kos-mock/data/kos_mock.trace.db +++ /dev/null @@ -1,1312 +0,0 @@ -2025-09-09 16:44:37.343275+09:00 database: flush -org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.get(DbException.java:212) - at org.h2.message.DbException.convert(DbException.java:407) - at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122) - at org.h2.mvstore.MVStore.handleException(MVStore.java:1546) - at org.h2.mvstore.MVStore.panic(MVStore.java:371) - at org.h2.mvstore.MVStore.(MVStore.java:291) - at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2035) - at org.h2.mvstore.db.Store.(Store.java:133) - at org.h2.engine.Database.(Database.java:326) - at org.h2.engine.Engine.openSession(Engine.java:92) - at org.h2.engine.Engine.openSession(Engine.java:222) - at org.h2.engine.Engine.createSession(Engine.java:201) - at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:343) - at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:125) - at org.h2.Driver.connect(Driver.java:59) - at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) - at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) - at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) - at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) - at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:550) - at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:98) - at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) - at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:437) - at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.delegateWork(JdbcIsolationDelegate.java:61) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.getJdbcEnvironmentUsingJdbcMetadata(JdbcEnvironmentInitiator.java:290) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:123) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:77) - at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:130) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:238) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:215) - at org.hibernate.boot.model.relational.Database.(Database.java:45) - at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.getDatabase(InFlightMetadataCollectorImpl.java:221) - at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.(InFlightMetadataCollectorImpl.java:189) - at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:171) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1431) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1502) - at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) - at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) - at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1463) - at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) - at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130) - at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191) - at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130) - at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) - at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:163) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:378) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:383) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:378) - at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) - at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) - at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) - at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) - at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) - at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734) - at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:377) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:290) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:289) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:279) - at java.base/java.util.Optional.orElseGet(Optional.java:364) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:278) - at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:106) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:105) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:69) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) - at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94) - at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) - at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) - at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92) - at jdk.proxy1/jdk.proxy1.$Proxy4.stop(Unknown Source) - at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63) - at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) -Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.getJdbcSQLException(DbException.java:566) - at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) - ... 149 more -Caused by: org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7] - at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996) - at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81) - at org.h2.mvstore.MVStore.(MVStore.java:286) - ... 143 more -2025-09-09 16:44:38.915218+09:00 database: flush -org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.get(DbException.java:212) - at org.h2.message.DbException.convert(DbException.java:407) - at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122) - at org.h2.mvstore.MVStore.handleException(MVStore.java:1546) - at org.h2.mvstore.MVStore.panic(MVStore.java:371) - at org.h2.mvstore.MVStore.(MVStore.java:291) - at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2035) - at org.h2.mvstore.db.Store.(Store.java:133) - at org.h2.engine.Database.(Database.java:326) - at org.h2.engine.Engine.openSession(Engine.java:92) - at org.h2.engine.Engine.openSession(Engine.java:222) - at org.h2.engine.Engine.createSession(Engine.java:201) - at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:343) - at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:125) - at org.h2.Driver.connect(Driver.java:59) - at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) - at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) - at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) - at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) - at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:550) - at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:98) - at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) - at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:437) - at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:46) - at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:39) - at org.hibernate.tool.schema.internal.exec.ImprovedExtractionContextImpl.getJdbcConnection(ImprovedExtractionContextImpl.java:63) - at org.hibernate.tool.schema.extract.spi.ExtractionContext.getQueryResults(ExtractionContext.java:43) - at org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl.extractMetadata(SequenceInformationExtractorLegacyImpl.java:39) - at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.initializeSequences(DatabaseInformationImpl.java:66) - at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.(DatabaseInformationImpl.java:60) - at org.hibernate.tool.schema.internal.Helper.buildDatabaseInformation(Helper.java:185) - at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:98) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:280) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:144) - at java.base/java.util.HashMap.forEach(HashMap.java:1429) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:141) - at org.hibernate.boot.internal.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:37) - at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) - at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:322) - at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:457) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1506) - at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) - at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) - at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1463) - at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) - at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130) - at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191) - at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130) - at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) - at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:163) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:378) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:383) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:378) - at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) - at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) - at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) - at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) - at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) - at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734) - at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:377) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:290) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:289) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:279) - at java.base/java.util.Optional.orElseGet(Optional.java:364) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:278) - at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:106) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:105) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:69) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) - at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94) - at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) - at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) - at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92) - at jdk.proxy1/jdk.proxy1.$Proxy4.stop(Unknown Source) - at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63) - at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) -Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.getJdbcSQLException(DbException.java:566) - at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) - ... 153 more -Caused by: org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7] - at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996) - at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81) - at org.h2.mvstore.MVStore.(MVStore.java:286) - ... 147 more -2025-09-09 16:44:40.401986+09:00 database: flush -org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.get(DbException.java:212) - at org.h2.message.DbException.convert(DbException.java:407) - at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122) - at org.h2.mvstore.MVStore.handleException(MVStore.java:1546) - at org.h2.mvstore.MVStore.panic(MVStore.java:371) - at org.h2.mvstore.MVStore.(MVStore.java:291) - at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2035) - at org.h2.mvstore.db.Store.(Store.java:133) - at org.h2.engine.Database.(Database.java:326) - at org.h2.engine.Engine.openSession(Engine.java:92) - at org.h2.engine.Engine.openSession(Engine.java:222) - at org.h2.engine.Engine.createSession(Engine.java:201) - at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:343) - at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:125) - at org.h2.Driver.connect(Driver.java:59) - at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) - at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) - at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) - at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) - at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:550) - at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:98) - at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) - at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:437) - at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.delegateWork(JdbcIsolationDelegate.java:61) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.getJdbcEnvironmentUsingJdbcMetadata(JdbcEnvironmentInitiator.java:290) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:123) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:77) - at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:130) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:238) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:215) - at org.hibernate.boot.model.relational.Database.(Database.java:45) - at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.getDatabase(InFlightMetadataCollectorImpl.java:221) - at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.(InFlightMetadataCollectorImpl.java:189) - at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:171) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1431) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1502) - at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) - at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) - at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1463) - at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) - at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130) - at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191) - at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130) - at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) - at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:163) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:378) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:383) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:378) - at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) - at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) - at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) - at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) - at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) - at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734) - at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:377) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:290) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:289) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:279) - at java.base/java.util.Optional.orElseGet(Optional.java:364) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:278) - at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:106) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:105) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:69) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) - at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94) - at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) - at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) - at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92) - at jdk.proxy1/jdk.proxy1.$Proxy4.stop(Unknown Source) - at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63) - at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) -Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.getJdbcSQLException(DbException.java:566) - at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) - ... 149 more -Caused by: org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7] - at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996) - at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81) - at org.h2.mvstore.MVStore.(MVStore.java:286) - ... 143 more -2025-09-09 16:44:41.479340+09:00 database: flush -org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.get(DbException.java:212) - at org.h2.message.DbException.convert(DbException.java:407) - at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122) - at org.h2.mvstore.MVStore.handleException(MVStore.java:1546) - at org.h2.mvstore.MVStore.panic(MVStore.java:371) - at org.h2.mvstore.MVStore.(MVStore.java:291) - at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2035) - at org.h2.mvstore.db.Store.(Store.java:133) - at org.h2.engine.Database.(Database.java:326) - at org.h2.engine.Engine.openSession(Engine.java:92) - at org.h2.engine.Engine.openSession(Engine.java:222) - at org.h2.engine.Engine.createSession(Engine.java:201) - at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:343) - at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:125) - at org.h2.Driver.connect(Driver.java:59) - at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) - at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) - at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) - at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) - at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:550) - at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:98) - at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) - at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:437) - at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:46) - at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:39) - at org.hibernate.tool.schema.internal.exec.ImprovedExtractionContextImpl.getJdbcConnection(ImprovedExtractionContextImpl.java:63) - at org.hibernate.tool.schema.extract.spi.ExtractionContext.getQueryResults(ExtractionContext.java:43) - at org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl.extractMetadata(SequenceInformationExtractorLegacyImpl.java:39) - at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.initializeSequences(DatabaseInformationImpl.java:66) - at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.(DatabaseInformationImpl.java:60) - at org.hibernate.tool.schema.internal.Helper.buildDatabaseInformation(Helper.java:185) - at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:98) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:280) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:144) - at java.base/java.util.HashMap.forEach(HashMap.java:1429) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:141) - at org.hibernate.boot.internal.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:37) - at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) - at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:322) - at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:457) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1506) - at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) - at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) - at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1463) - at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) - at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130) - at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191) - at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130) - at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) - at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:163) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:378) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:383) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:378) - at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) - at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) - at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) - at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) - at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) - at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734) - at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:377) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:290) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:289) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:279) - at java.base/java.util.Optional.orElseGet(Optional.java:364) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:278) - at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:106) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:105) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:69) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) - at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94) - at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) - at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) - at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92) - at jdk.proxy1/jdk.proxy1.$Proxy4.stop(Unknown Source) - at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63) - at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) -Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.getJdbcSQLException(DbException.java:566) - at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) - ... 153 more -Caused by: org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7] - at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996) - at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81) - at org.h2.mvstore.MVStore.(MVStore.java:286) - ... 147 more -2025-09-09 16:45:23.324975+09:00 database: flush -org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.get(DbException.java:212) - at org.h2.message.DbException.convert(DbException.java:407) - at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122) - at org.h2.mvstore.MVStore.handleException(MVStore.java:1546) - at org.h2.mvstore.MVStore.panic(MVStore.java:371) - at org.h2.mvstore.MVStore.(MVStore.java:291) - at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2035) - at org.h2.mvstore.db.Store.(Store.java:133) - at org.h2.engine.Database.(Database.java:326) - at org.h2.engine.Engine.openSession(Engine.java:92) - at org.h2.engine.Engine.openSession(Engine.java:222) - at org.h2.engine.Engine.createSession(Engine.java:201) - at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:343) - at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:125) - at org.h2.Driver.connect(Driver.java:59) - at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) - at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) - at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) - at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) - at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:550) - at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:98) - at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) - at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:437) - at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.delegateWork(JdbcIsolationDelegate.java:61) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.getJdbcEnvironmentUsingJdbcMetadata(JdbcEnvironmentInitiator.java:290) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:123) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:77) - at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:130) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:238) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:215) - at org.hibernate.boot.model.relational.Database.(Database.java:45) - at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.getDatabase(InFlightMetadataCollectorImpl.java:221) - at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.(InFlightMetadataCollectorImpl.java:189) - at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:171) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1431) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1502) - at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) - at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) - at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1463) - at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) - at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130) - at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191) - at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130) - at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) - at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:163) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:378) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:383) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:378) - at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) - at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) - at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) - at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) - at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) - at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734) - at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:377) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:290) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:289) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:279) - at java.base/java.util.Optional.orElseGet(Optional.java:364) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:278) - at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:106) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:105) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:69) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) - at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94) - at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) - at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) - at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92) - at jdk.proxy1/jdk.proxy1.$Proxy4.stop(Unknown Source) - at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63) - at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) -Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.getJdbcSQLException(DbException.java:566) - at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) - ... 149 more -Caused by: org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7] - at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996) - at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81) - at org.h2.mvstore.MVStore.(MVStore.java:286) - ... 143 more -2025-09-09 16:45:24.882303+09:00 database: flush -org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.get(DbException.java:212) - at org.h2.message.DbException.convert(DbException.java:407) - at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122) - at org.h2.mvstore.MVStore.handleException(MVStore.java:1546) - at org.h2.mvstore.MVStore.panic(MVStore.java:371) - at org.h2.mvstore.MVStore.(MVStore.java:291) - at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2035) - at org.h2.mvstore.db.Store.(Store.java:133) - at org.h2.engine.Database.(Database.java:326) - at org.h2.engine.Engine.openSession(Engine.java:92) - at org.h2.engine.Engine.openSession(Engine.java:222) - at org.h2.engine.Engine.createSession(Engine.java:201) - at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:343) - at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:125) - at org.h2.Driver.connect(Driver.java:59) - at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) - at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) - at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) - at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) - at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:550) - at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:98) - at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) - at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:437) - at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:46) - at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:39) - at org.hibernate.tool.schema.internal.exec.ImprovedExtractionContextImpl.getJdbcConnection(ImprovedExtractionContextImpl.java:63) - at org.hibernate.tool.schema.extract.spi.ExtractionContext.getQueryResults(ExtractionContext.java:43) - at org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl.extractMetadata(SequenceInformationExtractorLegacyImpl.java:39) - at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.initializeSequences(DatabaseInformationImpl.java:66) - at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.(DatabaseInformationImpl.java:60) - at org.hibernate.tool.schema.internal.Helper.buildDatabaseInformation(Helper.java:185) - at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:98) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:280) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:144) - at java.base/java.util.HashMap.forEach(HashMap.java:1429) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:141) - at org.hibernate.boot.internal.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:37) - at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) - at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:322) - at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:457) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1506) - at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) - at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) - at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1463) - at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) - at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130) - at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191) - at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130) - at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) - at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:163) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:378) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:383) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:378) - at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) - at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) - at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) - at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) - at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) - at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734) - at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:377) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:290) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:289) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:279) - at java.base/java.util.Optional.orElseGet(Optional.java:364) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:278) - at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:106) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:105) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:69) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) - at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94) - at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) - at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) - at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92) - at jdk.proxy1/jdk.proxy1.$Proxy4.stop(Unknown Source) - at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63) - at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) -Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.getJdbcSQLException(DbException.java:566) - at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) - ... 153 more -Caused by: org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7] - at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996) - at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81) - at org.h2.mvstore.MVStore.(MVStore.java:286) - ... 147 more -2025-09-09 16:45:26.315245+09:00 database: flush -org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.get(DbException.java:212) - at org.h2.message.DbException.convert(DbException.java:407) - at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122) - at org.h2.mvstore.MVStore.handleException(MVStore.java:1546) - at org.h2.mvstore.MVStore.panic(MVStore.java:371) - at org.h2.mvstore.MVStore.(MVStore.java:291) - at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2035) - at org.h2.mvstore.db.Store.(Store.java:133) - at org.h2.engine.Database.(Database.java:326) - at org.h2.engine.Engine.openSession(Engine.java:92) - at org.h2.engine.Engine.openSession(Engine.java:222) - at org.h2.engine.Engine.createSession(Engine.java:201) - at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:343) - at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:125) - at org.h2.Driver.connect(Driver.java:59) - at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) - at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) - at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) - at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) - at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:550) - at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:98) - at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) - at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:437) - at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.delegateWork(JdbcIsolationDelegate.java:61) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.getJdbcEnvironmentUsingJdbcMetadata(JdbcEnvironmentInitiator.java:290) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:123) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:77) - at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:130) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:238) - at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:215) - at org.hibernate.boot.model.relational.Database.(Database.java:45) - at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.getDatabase(InFlightMetadataCollectorImpl.java:221) - at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.(InFlightMetadataCollectorImpl.java:189) - at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:171) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1431) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1502) - at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) - at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) - at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1463) - at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) - at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130) - at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191) - at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130) - at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) - at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:163) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:378) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:383) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:378) - at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) - at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) - at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) - at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) - at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) - at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734) - at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:377) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:290) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:289) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:279) - at java.base/java.util.Optional.orElseGet(Optional.java:364) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:278) - at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:106) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:105) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:69) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) - at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94) - at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) - at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) - at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92) - at jdk.proxy1/jdk.proxy1.$Proxy4.stop(Unknown Source) - at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63) - at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) -Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.getJdbcSQLException(DbException.java:566) - at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) - ... 149 more -Caused by: org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7] - at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996) - at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81) - at org.h2.mvstore.MVStore.(MVStore.java:286) - ... 143 more -2025-09-09 16:45:27.401788+09:00 database: flush -org.h2.message.DbException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.get(DbException.java:212) - at org.h2.message.DbException.convert(DbException.java:407) - at org.h2.mvstore.db.Store.lambda$new$0(Store.java:122) - at org.h2.mvstore.MVStore.handleException(MVStore.java:1546) - at org.h2.mvstore.MVStore.panic(MVStore.java:371) - at org.h2.mvstore.MVStore.(MVStore.java:291) - at org.h2.mvstore.MVStore$Builder.open(MVStore.java:2035) - at org.h2.mvstore.db.Store.(Store.java:133) - at org.h2.engine.Database.(Database.java:326) - at org.h2.engine.Engine.openSession(Engine.java:92) - at org.h2.engine.Engine.openSession(Engine.java:222) - at org.h2.engine.Engine.createSession(Engine.java:201) - at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:343) - at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:125) - at org.h2.Driver.connect(Driver.java:59) - at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:137) - at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:360) - at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:202) - at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:461) - at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:550) - at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:98) - at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:111) - at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) - at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:437) - at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:46) - at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:39) - at org.hibernate.tool.schema.internal.exec.ImprovedExtractionContextImpl.getJdbcConnection(ImprovedExtractionContextImpl.java:63) - at org.hibernate.tool.schema.extract.spi.ExtractionContext.getQueryResults(ExtractionContext.java:43) - at org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl.extractMetadata(SequenceInformationExtractorLegacyImpl.java:39) - at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.initializeSequences(DatabaseInformationImpl.java:66) - at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.(DatabaseInformationImpl.java:60) - at org.hibernate.tool.schema.internal.Helper.buildDatabaseInformation(Helper.java:185) - at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:98) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:280) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.lambda$process$5(SchemaManagementToolCoordinator.java:144) - at java.base/java.util.HashMap.forEach(HashMap.java:1429) - at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:141) - at org.hibernate.boot.internal.SessionFactoryObserverForSchemaExport.sessionFactoryCreated(SessionFactoryObserverForSchemaExport.java:37) - at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) - at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:322) - at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:457) - at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1506) - at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) - at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) - at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1835) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) - at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) - at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) - at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) - at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) - at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205) - at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:952) - at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) - at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) - at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) - at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) - at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) - at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) - at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1463) - at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137) - at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225) - at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152) - at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130) - at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191) - at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130) - at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260) - at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:163) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:378) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:383) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:378) - at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) - at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) - at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) - at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) - at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) - at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) - at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734) - at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:377) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:290) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:289) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:279) - at java.base/java.util.Optional.orElseGet(Optional.java:364) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:278) - at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:106) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:105) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:69) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) - at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67) - at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) - at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) - at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:124) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:99) - at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:94) - at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) - at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) - at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) - at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) - at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92) - at jdk.proxy1/jdk.proxy1.$Proxy4.stop(Unknown Source) - at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:200) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:132) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:103) - at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:63) - at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:121) - at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) - at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) -Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7]" [50000-224] - at org.h2.message.DbException.getJdbcSQLException(DbException.java:566) - at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) - ... 153 more -Caused by: org.h2.mvstore.MVStoreException: The file is locked: /Users/dreamondal/home/workspace/phonebill/kos-mock/data/kos_mock.mv.db [2.2.224/7] - at org.h2.mvstore.DataUtils.newMVStoreException(DataUtils.java:996) - at org.h2.mvstore.SingleFileStore.lockFileChannel(SingleFileStore.java:143) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:117) - at org.h2.mvstore.SingleFileStore.open(SingleFileStore.java:81) - at org.h2.mvstore.MVStore.(MVStore.java:286) - ... 147 more diff --git a/product-service/.run/product-service.run.xml b/product-service/.run/product-service.run.xml index 86da7b1..48e3823 100644 --- a/product-service/.run/product-service.run.xml +++ b/product-service/.run/product-service.run.xml @@ -6,10 +6,10 @@ - - + + - + @@ -20,8 +20,8 @@ - - + + diff --git a/user-service/.run/user-service.run.xml b/user-service/.run/user-service.run.xml index a074ffa..254be8b 100644 --- a/user-service/.run/user-service.run.xml +++ b/user-service/.run/user-service.run.xml @@ -6,18 +6,18 @@ - - + + - + - - + +