Table of Contents
- Introduction to Koupleless
- Overview of Merged Deployment
- Environment Setup
- Base Refactoring
- Module Refactoring
- Module Slimming
- Deployment and Verification
- Common Issues and Solutions
- Best Practices
- Production Deployment
Introduction to Koupleless
Koupleless is a modular Serverless technology solution that enables conventional applications to evolve into the Serverless development model at a low cost. It decouples code from resources, making them easy to maintain independently, while supporting capabilities such as builds and deployments in seconds, merged deployment, and dynamic scaling to deliver an exceptional development and operations experience.
Use Cases
- Slow application builds and releases or cumbersome SDK upgrades: Traditional application builds and releases take 6–10 minutes, but Koupleless can reduce this to around 10 seconds.
- Resource waste from long-tail applications: CPU utilization is below 10% in 80% of enterprise applications. Merged deployment can significantly reduce infrastructure costs.
- Low development collaboration efficiency: When several developers work on the same application, releases must wait for a shared release window. Koupleless supports independent iteration of each module.
- Shared-platform applications struggle to accumulate business assets: Put shared capabilities in the base and implement specific business logic in modules.
- High cost of evolving to microservices: Supports smooth transitions among monolithic, modular, and microservice architectures.
Overview of Merged Deployment
What Is Merged Deployment?
Merged deployment means deploying multiple independent applications (called “modules” in Koupleless) into the same JVM process. They share the base’s resources and dependencies while retaining isolation in both code and runtime.
Advantages of Merged Deployment
- Resource savings: Multiple modules share the base’s memory (Metaspace and Heap), making better use of CPU.
- Fast startup: Slimming module build artifacts from hundreds of MB to tens of MB significantly reduces startup time.
- Simpler operations: Manage multiple modules in a unified way, reducing maintenance effort.
- Flexible expansion: Supports dynamically adding and removing modules without restarting the base.
Architecture Principles
Koupleless uses the SOFAArk framework to implement class isolation and merged deployment:
- Base: Provides shared dependencies and foundational capabilities, such as the Spring Boot framework and middleware SDKs.
- Module (Biz): A business feature module that relies on the shared capabilities provided by the base.
- Class-loading mechanism: A module first looks for a class in its own ClassLoader; if it cannot find the class, it delegates the lookup to the base ClassLoader.
Environment Setup
Prerequisites
- JDK 8 or later
- Maven 3.6+
- Spring Boot 2.x or 3.x
- An available IDE (IntelliJ IDEA, Eclipse, etc.)
Version Selection
The main versions currently used by Koupleless are:
<koupleless.runtime.version>0.5.6</koupleless.runtime.version>
<sofa.ark.version>2.2.14</sofa.ark.version>
Tool Setup
Download the Arkctl tool (used to deploy modules):
Base Refactoring
1. Add Dependencies
Add the Koupleless base dependency to the base application’s pom.xml:
2. Configure the Application Name
Configure the application name in application.properties or application.yml:
# application.properties
spring.application.name=base-application
3. Configure the Base Build Plugin
Add the base build plugin to the build section of pom.xml:
4. Generate the Dependency Starter
Run the following command to generate the base’s dependency starter:
mvn com.alipay.sofa.koupleless:koupleless-base-build-plugin::packageDependency -f pom.xml
This command generates a ${baseAppName}-dependencies-starter dependency package. Module projects can inherit all dependencies from the base by using it as their parent.
5. Start the Base
After completing the configuration, you can start the base directly from your IDE or the command line:
mvn spring-boot:run
Module Refactoring
1. Create a Module Project
A module can be an existing Spring Boot application or a newly created project. We recommend using a Maven multi-module structure to manage multiple modules.
2. Configure Module Dependencies
Add the module dependencies and packaging plugin to the module’s pom.xml:
3. Configure the Packaging Plugin
Add the SOFAArk packaging plugin to the module’s pom.xml:
4. Configure Module Slimming
Download the automatic packaging configuration file rules.txt and place it in the module’s conf/ark/ directory:
Example rules.txt contents:
5. Develop the Module Code
Create the module’s business code, such as a REST controller:
6. Configure the Application Name
Configure the application name in the module’s application.properties:
spring.application.name=module1
Module Slimming
Why Slim Modules?
Module slimming can:
- Reduce module package size: Cut the size from hundreds of MB to tens of MB.
- Reduce memory usage: Lower Metaspace consumption so the base can accommodate more modules.
- Speed up startup: Reduce module startup time from minutes to seconds.
- Improve deployment efficiency: Smaller packages are easier to transfer and deploy.
How Slimming Works
The core idea of module slimming is: Remove dependencies that duplicate those in the base so the module can reuse the base’s classes.
This is achieved in the following ways:
- Inherit the base dependency starter: Use
${baseAppName}-dependencies-starteras the module’s parent. - Automated packaging exclusions: Use the
rules.txtconfiguration file to exclude unnecessary dependencies. - Dependency delegation: Set module dependencies to the
providedscope and delegate their loading to the base.
Configuration Methods
Method 1: Inherit the Base Dependency Starter (Recommended)
Configure the module’s pom.xml as follows:
The module then automatically inherits all dependencies from the base without manually configuring versions.
Method 2: Manually Configure Dependency Scopes
In the module’s pom.xml, set dependencies already provided by the base to the provided scope:
Method 3: Use Exclusion Configuration
Configure exclusions in sofa-ark-maven-plugin:
Verify the Slimming Results
Build the module and check its package size:
As you can see, the ark-biz.jar shrinks from 180M to 15M after slimming, delivering a significant improvement.
Deployment and Verification
1. Start the Base
First, start the base application:
cd base-application
mvn spring-boot:run
Alternatively, launch the main class from your IDE.
2. Build the Module
Run the build in the module project:
cd module1
mvn clean package
After a successful build, two JAR files are generated:
module1-1.0.0.jar: A standard Spring Boot fat JAR that can run independently.module1-1.0.0-ark-biz.jar: A Koupleless module package for merged deployment.
3. Deploy the Module with Arkctl
Use the Arkctl tool to deploy the module to the base:
4. Verify the Deployment
After deployment succeeds, you can verify it using any of the following methods:
Check the Module Status
arkctl status
Example output:
Biz count: 1
bizName: module1
bizVersion: 1.0.0
bizState: ACTIVATED
mainClass: com.example.module1.Module1Application
webContextPath: /module1
Access the Module API
# 访问模块1的接口
curl http://localhost:8080/module1/
# 输出: hello to module1 deploy
curl http://localhost:8080/module1/health
# 输出: OK
View the Logs
You can see the module startup logs in the base console:
[INFO] Install biz: module1, version: 1.0.0, state: ACTIVATED
[INFO] Module module1 started successfully
5. Deploy Multiple Modules
Repeat the steps above to deploy module2:
cd module2
mvn clean package
arkctl deploy target/module2-1.0.0-ark-biz.jar
Verify the multi-module deployment:
# 查看所有模块
arkctl status
# 访问不同模块
curl http://localhost:8080/module1/
curl http://localhost:8080/module2/
6. Uninstall a Module
Common Issues and Solutions
1. Class Conflicts
Problem: The module and the base contain classes with the same name, causing conflicts.
Solution:
- Use the class isolation mechanism so modules load their own classes first.
- Move shared classes down to the base and reuse them through dependency delegation.
- Avoid defining classes with the same package and class names in both the module and the base.
2. Shared Static Fields
Problem: Multiple modules share static fields, leading to inconsistent data.
Solution:
Use the StaticFieldMapWrapper adapter for static fields:
3. Dubbo Compatibility Issues
Problem: Issues arise when using Dubbo generic invocation in a module.
Solution:
- Ensure all Dubbo-related dependencies are moved down to the base.
- Configure the correct ClassLoader switching for Dubbo.
- Refer to the solutions in the official adapter repository.
4. Too Many Nacos Threads
Problem: Each module creates a Nacos client at startup, resulting in too many threads.
Solution:
Cache the Nacos client in the base:
5. Module Startup Order
Problem: Modules depend on one another and must start in a specific order.
Solution:
- Configure the module startup order in the base.
- Use dependency relationships to control the startup order.
- Avoid circular dependencies.
6. Memory Leaks
Problem: Memory is not released after a module is unloaded, causing Metaspace to grow continuously.
Solution:
- Ensure all resources (thread pools, connections, etc.) are cleaned up when the module is unloaded.
- Use weak references to manage caches.
- Periodically restart the base or reclaim memory.
7. Spring Context Conflicts
Problem: Spring Bean conflicts occur between the module and the base.
Solution:
- Use different Bean names.
- Use the
@Primaryannotation in the module to make priority explicit. - Avoid defining the same Bean in both the module and the base.
Production Deployment
1. Deployment Architecture
The recommended production deployment architecture is:
2. K8s Deployment Configuration
Base Deployment
Module Release Pipeline
3. Dynamic Module Deployment
Option 1: OSS Monitoring Mode
The base watches the OSS directory for changes and automatically deploys new modules:
Option 2: Configuration Center Mode
Control module deployment through a configuration center:
The base listens for configuration changes:
The following sections cover the two topics that were not expanded on in the tutorial:
- Static Merged Deployment (Static Packaged Deployment) — package the base + N modules into a single executable fat JAR so that everything is ready as soon as it starts;
- Port isolation — let each module listen on its own port, enabling multiple WebServers within a single process.
------------------------------------------------
Option 3: Static Merged Deployment (Static Packaged Deployment)
Applicable Scenarios
- An extremely streamlined release process in which operations teams are not allowed to run arkctl deploy;
- The container image starts only one process, and K8s liveness/readiness probes can target the base directly;
- Module versions are fixed, with no need for hot unloading or hot loading.
Packaging approach
Add each module’s ark-biz.jar as a resource in the base pom and package it into the BOOT-INF/lib directory;
When the base starts, use SOFAArk’s “static-biz” mechanism to install and start these ark-biz.jar files together with the base’s lifecycle.Practical steps
step-1 On the module side, run mvn package as usual to produce moduleX-1.0.0-ark-biz.jar
step-2 Add a profile to the base pom that is activated only when building the static package
step-3 Add the @StaticBiz annotation to the base startup class (supported in 0.5.6+) to tell SOFAArk to statically install every ark-biz.jar on the classpath:
step-4 Build the static package
mvn clean package -Pstatic-pack
The generated base-application-1.0.0-exec.jar contains both module1-ark-biz.jar and module2-ark-biz.jar.
After you run java -jar base-application-1.0.0-exec.jar, arkctl status shows that both modules are already ACTIVATED, so there is no need to deploy them again.
Let Modules Listen on Their Own Ports
Default behavior
- All modules share the same embedded Tomcat (port 8080) with the base and are distinguished only by webContextPath;
- Benefit: saves threads and memory;
- Drawback: provides no “port-level” isolation and no way to expose a dedicated management port for an individual module.
Starting with Koupleless 0.5.6, “multi-WebServer mode” is supported, allowing each module to launch an independent Netty/Tomcat server and bind to its own port.
- Add the dependency on the module side
- Configure the port and context
# module1/application.yml
server:
port: 8081 # 模块独占
servlet:
context-path: / # 可以为 /
spring:
application:
name: module1
- Declare the packaging plugin
- Verify the deployment
The base still runs on 8080, while module1 additionally listens on 8081. You can configure module2 for 8082, with no interference between them;
curl http://localhost:8081/ directly returns module 1’s response, with no prefix required.
Caution
- Each additional module consumes another port, and memory and thread usage increase accordingly, so weigh the trade-offs;
- In production, declare ports 8081, 8082… in the K8s Service as well, or use multi-port Istio.
Reference Resources
- Official documentation: https://koupleless.io/docs/
- GitHub repository: https://github.com/koupleless/koupleless
- Sample code: https://github.com/koupleless/samples
- Community support: https://github.com/koupleless/koupleless/issues