Why We Dropped Nacos for Apollo: A Hands‑On Guide to Apollo Configuration Center
This article walks through the reasons for abandoning Nacos in favor of Apollo and provides a step‑by‑step tutorial that covers Apollo’s core concepts, architecture, client integration with Spring Boot, dynamic updates, environment/cluster/namespace handling, and deployment on Kubernetes.
Overview
Apollo is an open‑source distributed configuration center from Ctrip that provides feature parity with Nacos and is designed for complex micro‑service environments.
Core Concepts
When applications grow, static files or databases cannot meet the need for real‑time configuration changes, feature flags, and environment‑specific settings. A centralized configuration center solves these problems.
Key Features
Simple deployment
Gray (canary) release
Version management
Open API
Client configuration monitoring
Native Java and .NET clients
Hot‑update of configurations
Permission, audit and release workflow
Core Model
User modifies a configuration in Apollo and publishes it.
The Config Service notifies connected Apollo clients.
Clients pull the latest configuration, update the in‑memory cache and notify the application.
Four Management Dimensions
Application – identified by app.id Environment – DEV, FAT, UAT, PRO
Cluster – a logical group of instances (e.g., Beijing, Shanghai)
Namespace – logical grouping of keys (public, private, inherited)
Local Cache
Clients cache configuration files under /opt/data/{appId}/config-cache on Linux/macOS or C:\opt\data\{appId}\config-cache on Windows. The cache file name follows the pattern {appId}+{cluster}+{namespace}.properties.
Client Design
Each client maintains a long‑polling HTTP connection (default 60 s) with the Config Service. If a change occurs, the server returns the affected namespace; otherwise it returns HTTP 304. A fallback pull runs every apollo.refreshInterval minutes (default 5 min) and reports the local version to the server.
Overall Architecture
Config Service – provides configuration read and push APIs; stateless and accessed directly by applications.
Admin Service – handles configuration creation, modification and publishing; used by the Apollo Portal.
Meta Server – registers Config Service and Admin Service with Eureka and exposes a unified meta endpoint.
Portal – UI for managing projects, environments, clusters and namespaces; discovers services via the Meta Server.
Availability Scenarios
One Config Service down – no impact; clients reconnect to other instances.
All Config Services down – clients cannot fetch new configs; they continue to use the local cache.
One Admin Service down – no impact; Portal reconnects to other instances.
All Admin Services down – Portal cannot publish, but clients keep working.
One Portal down – no impact; load balancer redirects to a healthy instance.
All Portals down – clients unaffected; Portal unavailable.
One data‑center down – no impact; multi‑DC deployment with automatic failover.
Step‑by‑Step Tutorial
1. Create a Project in Apollo Portal
Log in (default user: apollo, password: admin), create a project with app.id = apollo-test and app.name = apollo-demo.
2. Add a Configuration
Create a key test with value 123456, add a remark and publish.
3. Build a Spring Boot Client
Maven dependency (Apollo client 1.4.0):
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.4.0</version>
</dependency>application.yml (essential properties):
server:
port: 8080
spring:
application:
name: apollo-demo
app:
id: apollo-test
apollo:
cacheDir: /opt/data/
cluster: default
meta: http://192.168.2.11:30002
autoUpdateInjectedSpringProperties: true
bootstrap:
enabled: true
namespaces: application
eagerLoad:
enabled: falseTestController :
@RestController
public class TestController {
@Value("${test:默认值}")
private String test;
@GetMapping("/test")
public String test() {
return "test的值为:" + test;
}
}Application entry point :
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}Run the jar with JVM arguments, e.g.:
-Dapollo.configService=http://192.168.2.11:30002 -Denv=DEV4. Verify
Access http://localhost:8080/test. The response should be test的值为:123456, confirming that the value comes from Apollo.
5. Dynamic Update
Change the test value in the Portal to 666666 and publish. The endpoint immediately returns the new value.
6. Rollback
Rolling back restores the previous value ( 123456) without redeploying the client.
7. Deletion
Deleting the key makes the controller fall back to the default value defined in @Value ( 默认值).
8. Cache Behaviour
If the Config Service is unreachable, the client reads the locally cached file and returns the last known value. Deleting the cache file forces the default value.
Environment / Cluster / Namespace Switch
Environment Switch
Set apollo.meta to the PRO address and start the JVM with -Denv=PRO. The same key now returns the value defined in the PRO environment.
Cluster Switch
Define apollo.cluster: beijing (or shanghai) in application.yml. After publishing different values for each cluster, the endpoint returns the cluster‑specific value.
Namespace Switch
Create private namespaces (e.g., dev-1, dev-2) and select the desired namespace via bootstrap.namespaces. The endpoint returns the value belonging to the selected namespace.
Kubernetes Deployment
Docker Image
FROM openjdk:8u222-jre-slim
VOLUME /tmp
ADD target/*.jar app.jar
ENV JAVA_OPTS="-XX:MaxRAMPercentage=80.0 -Duser.timezone=Asia/Shanghai"
ENV APP_OPTS=""
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS"]Build the image:
docker build -t mydlqclub/springboot-apollo:0.0.1 .Kubernetes Manifest (springboot‑apollo.yaml)
apiVersion: v1
kind: Service
metadata:
name: springboot-apollo
spec:
type: NodePort
ports:
- name: server
nodePort: 31080
port: 8080
targetPort: 8080
- name: management
nodePort: 31081
port: 8081
targetPort: 8081
selector:
app: springboot-apollo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: springboot-apollo
spec:
replicas: 1
selector:
matchLabels:
app: springboot-apollo
template:
metadata:
labels:
app: springboot-apollo
spec:
containers:
- name: springboot-apollo
image: mydlqclub/springboot-apollo:0.0.1
imagePullPolicy: Always
ports:
- containerPort: 8080
name: server
env:
- name: JAVA_OPTS
value: "-Denv=DEV"
- name: APP_OPTS
value: " \
--app.id=apollo-demo \
--apollo.bootstrap.enabled=true \
--apollo.bootstrap.eagerLoad.enabled=false \
--apollo.cacheDir=/opt/data/ \
--apollo.cluster=default \
--apollo.bootstrap.namespaces=application \
--apollo.autoUpdateInjectedSpringProperties=true \
--apollo.meta=http://service-apollo-config-server-dev.mydlqcloud:8080"
resources:
limits:
memory: 1000Mi
cpu: 1000m
requests:
memory: 500Mi
cpu: 500mDeploy to the desired namespace (e.g., mydlqcloud) with:
kubectl apply -f springboot-apollo.yaml -n mydlqcloudTest the Deployed Service
Assuming the cluster node IP is 192.168.2.11 and the NodePort is 31080, access http://192.168.2.11:31080/test. The response should be the value stored in Apollo (e.g., 123456).
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
