목차
Koupleless 소개
Koupleless는 모듈형 Serverless 기술 솔루션으로, 일반 애플리케이션을 비교적 적은 비용으로 Serverless 개발 모델로 전환할 수 있게 합니다. 코드와 리소스를 분리해 각각 독립적으로 관리하기 쉽게 하는 동시에, 초 단위 빌드·배포, 병합 배포, 동적 확장 등의 기능을 지원해 탁월한 개발·운영 경험을 제공합니다.
적용 시나리오
- 애플리케이션 빌드·배포가 느리거나 SDK 업그레이드가 번거로운 경우: 기존 애플리케이션의 빌드·배포에는 6-10분이 걸리지만, Koupleless를 사용하면 10초 수준으로 줄일 수 있습니다.
- 롱테일 애플리케이션의 리소스 낭비: 기업의 80% 애플리케이션은 CPU 사용율이 10% 미만이며, 병합 배포를 통해 리소스 비용을 크게 줄일 수 있습니다.
- 개발 협업 효율 저하: 여러 사람이 한 애플리케이션을 개발할 때는 릴리스 시간대를 맞춰야 하지만, Koupleless는 모듈을 독립적으로 반복 배포할 수 있습니다.
- 공통 플랫폼 애플리케이션에서 비즈니스 자산을 축적하기 어려운 경우: 베이스에 공통 역량을 모으고, 모듈에서 구체적인 비즈니스 로직을 구현합니다.
- 마이크로서비스 전환 비용이 높은 경우: 애플리케이션이 모놀리식, 모듈형, 마이크로서비스 아키텍처 사이를 원활하게 전환할 수 있습니다.
병합 배포 개요
병합배포란 무엇인가요
병합 배포는 여러 개의 독립적인 애플리케이션(Koupleless에서는 “모듈”이라고 함)을 동일한 JVM 프로세스에 배포해, 베이스의 리소스와 의존성을 공유하면서도 코드와 런타임은 격리된 상태로 유지하는 방식입니다.
병합 배포의 장점
- 리소스 절약: 여러 모듈이 베이스의 메모리(Metaspace와 Heap)를 공유해 CPU 사용률을 효과적으로 높일 수 있습니다.
- 빠른 시작: 모듈 빌드 산출물을 수백 MB에서 수십 MB로 줄여 시작 시간을 크게 단축할 수 있습니다.
- 간소화된 운영: 여러 모듈을 한곳에서 관리해 유지보수 비용을 줄일 수 있습니다.
- 유연한 확장: 베이스를 다시 시작하지 않고도 모듈을 동적으로 추가하거나 제거할 수 있습니다.
아키텍처 원리
Koupleless는 SOFAArk 프레임워크를 기반으로 클래스 격리와 병합 배포를 구현합니다.
- 베이스(Base): SpringBoot 프레임워크, 중간件 SDK 등 공통 의존성과 기본 역량을 제공합니다.
- 모듈(Biz): 베이스가 제공하는 공통 역량을 사용하는 업무 기능 모듈입니다.
- 클래스 로딩 메커니즘: 모듈은 우선 자체 ClassLoader에서 클래스를 찾고, 찾지 못하면 베이스 ClassLoader에 위임합니다.
환경 준비
사전 요구 사항
- JDK 8 이상
- Maven 3.6+
- SpringBoot 2.x 또는 3.x
- 사용할 수 있는 IDE(IntelliJ IDEA, Eclipse 등)
버전 선택
Koupleless의 현재 주요 버전은 다음과 같습니다.
<koupleless.runtime.version>0.5.6</koupleless.runtime.version>
<sofa.ark.version>2.2.14</sofa.ark.version>
도구 준비
Arkctl 도구(모듈 배포용)를 다운로드합니다.
베이스 애플리케이션 수정
1. 의존성 추가
베이스 애플리케이션의 pom.xml에 Koupleless 베이스 의존성을 추가합니다.
2. 애플리케이션 이름 구성
application.properties 또는 application.yml에서 애플리케이션 이름을 구성합니다.
# application.properties
spring.application.name=base-application
3. 베이스 빌드 플러그인 구성
pom.xml의 build 섹션에 베이스 빌드 플러그인을 추가합니다.
4. 의존성 Starter 생성
다음 명령을 실행해 베이스의 의존성 starter를 생성합니다.
mvn com.alipay.sofa.koupleless:koupleless-base-build-plugin::packageDependency -f pom.xml
이 명령은 ${baseAppName}-dependencies-starter 의존성 패키지를 생성합니다. 모듈 프로젝트에서는 이를 parent로 지정해 베이스의 모든 의존성을 상속할 수 있습니다.
5. 베이스 시작
위 구성을 완료하면 IDE나 명령줄에서 베이스를 바로 시작할 수 있습니다.
mvn spring-boot:run
모듈 수정
1. 모듈 프로젝트 생성
모듈은 기존 SpringBoot 애플리케이션이어도 새로 만든 프로젝트여도 됩니다. 여러 모듈은 Maven 멀티 모듈 구조로 관리하는 것이 좋습니다.
2. 모듈 의존성 구성
모듈의 pom.xml에 모듈 의존성과 패키징 플러그인을 추가합니다.
3. 패키징 플러그인 구성
모듈의 pom.xml에 SOFAArk 패키징 플러그인을 추가합니다.
4. 모듈 경량화 구성
자동 패키징 구성 파일 rules.txt를 다운로드해 모듈의 conf/ark/ 디렉터리에 넣습니다.
rules.txt 내용 예시는 다음과 같습니다.
5. 모듈 코드 개발
예를 들어 REST Controller와 같은 모듈 업무 코드를 작성합니다.
6. 애플리케이션 이름 구성
모듈의 application.properties에서 애플리케이션 이름을 구성합니다.
spring.application.name=module1
모듈 경량화
모듈을 경량화해야 하는 이유
모듈 경량화를 적용하면 다음과 같은 이점을 얻을 수 있습니다.
- 모듈 패키지 크기 감소: 수백 MB에서 수십 MB로 줄일 수 있습니다.
- 메모리 사용량 감소: Metaspace 사용량을 줄여 베이스에 더 많은 모듈을 설치할 수 있습니다.
- 시작 속도 향상: 모듈 시작 시간이 분 단위에서 초 단위로 줄어듭니다.
- 배포 효율성 향상: 작은 패키지는 전송과 배포가 더 쉽습니다.
경량화 원리
모듈 경량화의 핵심은 베이스와 중복되는 의존성을 모듈에서 제거해 모듈이 베이스의 클래스를 재사용하도록 하는 것입니다.
다음과 같은 방법으로 구현합니다.
- 베이스 의존성 starter 상속: 모듈이
${baseAppName}-dependencies-starter를 parent로 사용합니다. - 자동 패키징:
rules.txt구성 파일을 사용해 필요하지 않은 의존성을 제외합니다. - 의존성 위임: 모듈 의존성을
provided범위로 설정해 베이스가 로드하도록 합니다.
구성 방법
방법 1: 베이스 의존성 starter 상속(권장)
모듈의 pom.xml에 다음을 구성합니다.
이렇게 하면 모듈이 베이스의 모든 의존성을 자동으로 상속하므로 버전을 수동으로 설정할 필요가 없습니다.
방법 2: 의존성 범위 수동 설정
모듈의 pom.xml에서 베이스에 이미 있는 의존성을 provided 범위로 설정합니다.
방법 3: 제외 구성 사용
sofa-ark-maven-plugin에서 제외 항목을 구성합니다.
경량화 효과 확인
모듈을 빌드하고 패키지 크기를 확인합니다.
결과적으로 ark-biz.jar가 180M에서 15M로 줄어 눈에 띄는 효과를 확인할 수 있습니다.
배포 및 검증
1. 베이스 시작
먼저 베이스 애플리케이션을 시작합니다.
cd base-application
mvn spring-boot:run
또는 IDE에서 메인 클래스를 실행할 수 있습니다.
2. 모듈 빌드
모듈 프로젝트에서 빌드를 실행합니다.
cd module1
mvn clean package
빌드가 성공하면 두 개의 jar 패키지가 생성됩니다.
module1-1.0.0.jar: 일반 SpringBoot fat jar로, 단독 실행 가능module1-1.0.0-ark-biz.jar: 병합 배포에 사용하는 Koupleless 모듈 패키지
3. Arkctl로 모듈 배포
Arkctl 도구를 사용해 모듈을 베이스에 배포합니다.
4. 배포 검증
배포가 성공하면 다음 방법으로 검증할 수 있습니다.
모듈 상태 조회
arkctl status
출력 예시는 다음과 같습니다.
Biz count: 1
bizName: module1
bizVersion: 1.0.0
bizState: ACTIVATED
mainClass: com.example.module1.Module1Application
webContextPath: /module1
모듈 엔드포인트 접근
# 访问模块1的接口
curl http://localhost:8080/module1/
# 输出: hello to module1 deploy
curl http://localhost:8080/module1/health
# 输出: OK
로그 확인
베이스 콘솔에서 모듈 시작 로그를 확인할 수 있습니다.
[INFO] Install biz: module1, version: 1.0.0, state: ACTIVATED
[INFO] Module module1 started successfully
5. 여러 모듈 배포
위 절차를 반복해 module2를 배포합니다.
cd module2
mvn clean package
arkctl deploy target/module2-1.0.0-ark-biz.jar
다중 모듈 배포를 검증합니다.
# 查看所有模块
arkctl status
# 访问不同模块
curl http://localhost:8080/module1/
curl http://localhost:8080/module2/
6. 모듈卸载
자주 하는 문제 및 해결책
1. 클래스 충돌 문제
문제 설명: 모듈과 베이스에 동일한 클래스명의 클래스가 존재해 클래스 충돌이 발생합니다.
해결책:
- 클래스 격리 메커니즘을 사용해 모듈이 자체 클래스를 우선 로드하도록 합니다.
- 공통 클래스는 베이스로 내려 모듈이 의존성 위임을 통해 재사용하도록 합니다.
- 모듈과 베이스에 동일한 패키지 이름과 클래스 이름을 정의하지 않도록 합니다.
2. 정적 필드 공유 문제
문제 설명: 여러 모듈이 정적 필드를 공유해 데이터가 혼란스러워집니다.
해결책:
StaticFieldMapWrapper 어댑터로 정적 필드를 처리합니다.
3. Dubbo 호환성 문제
문제 설명: 모듈에서 Dubbo 범용 호출을 사용할 때 문제가 발생합니다.
해결책:
- Dubbo 관련 의존성이 베이스로 내려가도록 구성합니다.
- Dubbo에 올바른 ClassLoader 전환을 구성합니다.
- 공식 adapter 저장소의 해결책을 참고합니다.
4. Nacos 스레드 수 과다
문제 설명: 각 모듈을 시작할 때 Nacos 클라이언트를 생성해 스레드 수가 지나치게 많아집니다.
해결책:
베이스에서 Nacos 클라이언트를 캐시합니다.
5. 모듈 시작 순서 문제
문제 설명: 모듈 간에 의존 관계가 있어 순서대로 시작해야 합니다.
해결책:
- 베이스에서 모듈 시작 순서를 구성합니다.
- 의존 관계를 사용해 시작 순서를 제어합니다.
- 순환 의존 관계를 피합니다.
6. 메모리 누수
문제 설명: 모듈을卸载한 후에도 메모리가 해제되지 않아 Metaspace가 계속 증가합니다.
해결책:
- 모듈을卸载할 때 모든 리소스(스레드 풀, 연결 등)를 정리하도록 합니다.
- 약한 참조를 사용해 캐시를 관리합니다.
- 주기적으로 베이스를 재시작하거나 메모리를 정리합니다.
7. Spring 컨텍스트 충돌
문제 설명: 모듈과 베이스의 Spring Bean이 충돌합니다.
해결책:
- 서로 다른 Bean 이름을 사용합니다.
- 모듈에서
@Primary어노테이션으로 우선순위를 명확히 지정합니다. - 모듈과 베이스에 동일한 Bean을 정의하지 않도록 합니다.
프로덕션 환경 배포
1. 배포 아키텍처
프로덕션 환경에서 권장하는 배포 아키텍처는 다음과 같습니다.
2. K8s 배포 구성
베이스 Deployment
모듈 릴리스 파이프라인
3. 모듈 동적 배포
방안 1: OSS 감시 방식
베이스가 OSS 디렉터리 변경을 감시하며 새 모듈을 자동으로 배포합니다.
방안 2: 구성 센터 방식
구성 센터를 통해 모듈 배포를 제어합니다.
베이스가 구성 변경을 감시합니다.
아래에서는 튜토리얼에서 자세히 다루지 않은 두 가지를 한 번에 명확히 설명하겠습니다:
- 정적 병합 배포(Static Packaged Deployment)——베이스 애플리케이션과 N개의 모듈을 한 번에 실행 가능한 Fat Jar로 묶어, 시작하자마자 “전원 준비 완료” 상태가 됩니다.
- 포트 격리——각 모듈이 실제로 고유한 포트를 사용하도록 하여 “프로세스 내 다중 WebServer”를 구현합니다.
------------------------------------------------
방안 3: 정적 병합 배포(Static Packaged Deployment)
적용 시나리오
- 릴리스 절차를 최대한 단순하게 만들고 운영자가 arkctl deploy를 직접 실행하지 않도록 해야 하는 경우;
- 컨테이너 이미지에서 프로세스 하나만 시작하고 K8s liveness/readiness 프로브가 베이스를 직접 검사하도록 구성하는 경우;
- 모듈 버전이 고정되어 있고 핫 언로드/핫 로드가 필요하지 않은 경우.
패키징 방식
베이스 pom에서 각 모듈의 ark-biz.jar를 리소스로 함께 패키징해 BOOT-INF/lib 디렉터리에 넣습니다;
베이스를 시작할 때 SOFAArk의 “static-biz” 메커니즘을 사용해 해당 ark-biz.jar들을 베이스의 수명 주기와 함께 install + start합니다.실습 단계
step-1 모듈에서 정상적으로 mvn package를 실행해 moduleX-1.0.0-ark-biz.jar를 생성합니다
step-2 베이스 pom에 profile을 추가합니다(정적 패키지를 만들 때만 활성화됩니다.)
step-3 베이스 시작 클래스에 @StaticBiz 어노테이션(0.5.6+ 지원)을 추가해 SOFAArk가 classpath 아래의 모든 ark-biz.jar를 정적으로 설치하도록 합니다.
step-4 정적 패키지 빌드
mvn clean package -Pstatic-pack
생성된 base-application-1.0.0-exec.jar에는 module1-ark-biz.jar와 module2-ark-biz.jar가 모두 포함됩니다;java -jar base-application-1.0.0-exec.jar로 시작한 뒤 arkctl status에서 두 모듈이 이미 ACTIVATED 상태인 것을 확인할 수 있으며, 다시 deploy할 필요가 없습니다.
모듈이 자체 포트를 사용하도록 설정하기
기본 동작
- 모든 모듈과 베이스가 동일한 임베디드 Tomcat(포트 8080)을 공유하며 webContextPath로만 구분됩니다;
- 장점: 스레드와 메모리를 절약할 수 있음;
- 단점: “포트 수준” 격리가 불가능하고 “모듈별 관리 포트 노출”도 불가능합니다.
Koupleless 0.5.6부터 “다중 WebServer 모드”를 지원하므로, 각 모듈이 독립적인 Netty/Tomcat을 시작해 실제로 자체 포트에 바인딩할 수 있습니다.
- 모듈 의존성 추가
- 포트 및 컨텍스트 구성
# module1/application.yml
server:
port: 8081 # 模块独占
servlet:
context-path: / # 可以为 /
spring:
application:
name: module1
- 패키징 플러그인 선언
- 배포 검증
베이스는 계속 8080을 사용하고 module1은 8081을 추가로 사용하며, module2는 8082로 설정할 수 있어 서로 영향을 주지 않습니다;curl http://localhost:8081/을 호출하면 모듈 1의 응답이 바로 반환되므로 접두사를 추가할 필요가 없습니다.
주의
- 포트 n개를 추가로 점유하므로 메모리와 스레드도 그만큼 증가합니다. 필요에 따라 절충하세요;
- 프로덕션 환경에서는 K8s Service에 8081, 8082… 포트도 선언하거나 Istio 멀티 포트를 사용해야 합니다.