Profiles — dev/staging/prod, profile-specific bean, profile groups
Profile là cơ chế Boot toggle config + bean theo môi trường. Bài này bóc cách profile activate, @Profile trên @Bean và @Component, profile groups, default profile, multi-document YAML, profile expression (! && ||), profile inheritance, và pattern multi-tenant với profile.
Bài 04 đã giới thiệu application-\{profile\}.yml. Bài này đào sâu profile như cơ chế cross-cutting: không chỉ override property file, mà còn toggle bean theo môi trường. Pattern Spring profile là cách clean nhất để có 1 codebase chạy đúng trên 5 môi trường (local/dev/staging/prod/test) mà không phải if (env == "prod") rải rác trong code.
1. Profile là gì — về mặt khái niệm
Profile là named tag gắn vào bean hoặc config. Khi 1 profile được "active", Boot:
- Load file
application-\{profile\}.yml(đã thấy bài 04). - Register bean có
@Profile("\{profile\}")matching, skip bean không match. - Activate
@PropertySourceprofile-specific.
Multiple profile có thể active cùng lúc. App có thể không có profile nào active (= "default" profile).
flowchart LR
Start[App start]
Active["spring.profiles.active=prod,monitoring"]
Filter1["application-prod.yml<br/>application-monitoring.yml<br/>load + override"]
Filter2["@Profile('prod') beans → register<br/>@Profile('monitoring') beans → register<br/>@Profile('dev') beans → SKIP"]
Final[Container with prod + monitoring config]
Start --> Active --> Filter1 --> Filter2 --> Final
style Active fill:#fef3c72. Activate profile — 5 cách
2.1 Property spring.profiles.active
# application.yml — set default profile
spring:
profiles:
active: dev
# Override khi run
java -jar app.jar --spring.profiles.active=prod
# Hoac qua env var (relax binding)
SPRING_PROFILES_ACTIVE=prod java -jar app.jar
2.2 JVM system property
java -Dspring.profiles.active=prod -jar app.jar
2.3 Programmatic
public static void main(String[] args) {
SpringApplication app = new SpringApplication(App.class);
app.setAdditionalProfiles("prod"); // them, khong replace
app.run(args);
}
Hoặc:
ConfigurableEnvironment env = ctx.getEnvironment();
env.setActiveProfiles("prod"); // replace
env.addActiveProfile("monitoring"); // them
2.4 Test annotation
@SpringBootTest
@ActiveProfiles("test")
class OrderServiceTest { ... }
2.5 Multiple profile cùng lúc
spring:
profiles:
active: prod,monitoring,asia-east # 3 profile cung active
Boot apply theo thứ tự — profile sau override profile trước. Pattern phổ biến: 1 profile cho env (prod), 1 cho region (asia-east), 1 cho feature (monitoring).
3. @Profile trên bean
3.1 @Profile cơ bản
@Configuration
public class CacheConfig {
@Bean
@Profile("dev")
public CacheManager devCache() {
return new ConcurrentMapCacheManager(); // in-memory cho dev
}
@Bean
@Profile("prod")
public CacheManager prodCache(RedisConnectionFactory factory) {
return new RedisCacheManager(factory); // Redis cho prod
}
}
Active profile dev → register devCache, skip prodCache. Active prod → ngược lại. 1 bean type (CacheManager), 2 implementation tuỳ profile — không cần if-else trong code business.
3.2 @Profile trên @Component
@Component
@Profile("prod")
public class CloudWatchMetricsExporter implements MetricsExporter { ... }
@Component
@Profile("dev")
public class ConsoleMetricsExporter implements MetricsExporter { ... }
Service inject MetricsExporter — Boot pick implementation theo profile. Strategy pattern + profile = clean.
3.3 @Profile trên @Configuration class
@Configuration
@Profile("prod")
public class ProductionConfig {
@Bean public DataSource dataSource() { ... }
@Bean public CacheManager cache() { ... }
@Bean public MetricsExporter metrics() { ... }
}
Cả class @Configuration chỉ register khi profile active. Pattern này gom config theo profile — clean cho team biết "muốn xem prod config, mở ProductionConfig".
3.4 Profile expression — ! && ||
Boot 2.4+ support expression:
@Profile("!dev") // moi profile TRU dev
@Profile("dev | test") // dev HOAC test
@Profile("prod & monitoring") // prod VA monitoring (Boot 5.1+ syntax)
@Profile({"dev", "test"}) // OR (legacy syntax)
Operator:
!— NOT|(Boot 2.4+) hoặc array — OR&(Boot 5.1+, Spring 6) — AND
Use case:
@Component
@Profile("!prod") // dev, test, staging tat ca run, tru prod
public class DebugInterceptor { ... }
@Component
@Profile("prod & asia-east")
public class ChinaSpecificFilter { ... }
4. Profile groups — gom logic
Boot 2.4+ support profile groups — gom nhiều profile thành 1 logic:
spring:
profiles:
group:
production: prod, prod-db, prod-cache, prod-tracing
staging: staging, staging-db, staging-cache
Activate production:
java -jar app.jar --spring.profiles.active=production
Boot tự include 4 profile: prod, prod-db, prod-cache, prod-tracing. Tách thành 4 file YAML → mỗi concern 1 file:
application-prod.yml # Spring config common cho prod
application-prod-db.yml # DataSource config
application-prod-cache.yml # Redis config
application-prod-tracing.yml # Observability config
Lợi ích: file nhỏ, single concern, easy review. Khi application-prod.yml lên 200 dòng, group là cách clean.
5. Default profile
Bean không có @Profile annotation → register ở mọi profile. Bean với @Profile("default") chỉ register khi không profile nào active:
@Component
public class CommonService { ... } // moi profile
@Component
@Profile("default") // CHI khi khong profile active
public class FallbackService { ... }
@Component
@Profile({"!dev", "!test"}) // moi profile tru dev va test
public class ProductionLogger { ... }
spring.profiles.default — profile dùng khi spring.profiles.active empty:
spring:
profiles:
default: dev # neu khong active gi, dung 'dev'
Pattern: development team chạy local không set SPRING_PROFILES_ACTIVE → fall back dev profile. CI/CD set explicit profile.
6. Multi-document YAML — nhiều profile trong 1 file
Bài 04 đã giới thiệu — đây là chi tiết:
# application.yml — single file, multi document
spring:
application:
name: order-service
datasource:
hikari:
maximum-pool-size: 10 # default
---
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:postgresql://localhost:5432/dev
logging:
level:
com.olhub: DEBUG
---
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:postgresql://prod-db.internal:5432/app
hikari:
maximum-pool-size: 50 # override default
logging:
level:
com.olhub: INFO
---
spring:
config:
activate:
on-profile: prod & monitoring
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
Boot pick document có on-profile match. Document đầu (no profile filter) luôn apply như default. Document sau override.
Khi nào dùng multi-document vs separate files:
| Scenario | Approach |
|---|---|
| File ngắn (dưới 100 dòng), ít profile (2-3) | Multi-document |
| File dài, nhiều profile, nhiều team chỉnh | Separate files |
| Dùng profile groups | Separate files |
| Quick prototype | Multi-document |
7. Inheritance giữa profile
Boot không có cơ chế "profile A extends profile B" trực tiếp. Pattern mô phỏng inheritance:
7.1 Common base + override
application.yml # base — moi profile inherit
application-prod-base.yml # base prod (DB, cache config)
application-prod.yml # prod-specific override
application-prod-canary.yml # canary deployment override
# spring.profiles.active=prod-base,prod-canary
# Order: application.yml → prod-base → prod-canary
Profile sau override profile trước → cascade behavior như inheritance.
7.2 Include via property
# application-prod.yml
spring:
profiles:
include: prod-base, prod-monitoring # auto include khi prod active
datasource:
url: ${PROD_DB_URL}
Activate prod → Boot tự include prod-base và prod-monitoring. Đây là cách viết explicit dependency giữa profile.
8. Pattern thực tế
8.1 Local + Docker Compose dev
# application.yml
spring:
profiles:
default: local
---
spring:
config:
activate:
on-profile: local
datasource:
url: jdbc:postgresql://localhost:5432/dev
username: dev_user
password: dev_pass
---
spring:
config:
activate:
on-profile: docker
datasource:
url: jdbc:postgresql://postgres:5432/app # docker service name
username: app
password: app
Run local: mvn spring-boot:run → local profile active.
Run Docker Compose: SPRING_PROFILES_ACTIVE=docker docker-compose up.
8.2 Multi-region deployment
spring:
profiles:
group:
asia-east: prod-base, prod-asia-east, prod-monitoring
eu-west: prod-base, prod-eu-west, prod-monitoring
us-east: prod-base, prod-us-east, prod-monitoring
3 region — mỗi region group 3 profile. application-prod-asia-east.yml chứa endpoint server Singapore, prod-eu-west.yml chứa endpoint Frankfurt. Common config (prod-base.yml) shared.
K8s deployment per region:
env:
- name: SPRING_PROFILES_ACTIVE
value: "asia-east" # region-specific
8.3 Test profile cho integration test
@SpringBootTest
@ActiveProfiles("test")
class OrderServiceIntegrationTest {
@Autowired OrderService service;
@Test
void placeOrderSuccess() { ... }
}
# application-test.yml — load tu src/test/resources
spring:
datasource:
url: jdbc:tc:postgresql:16:///testdb # Testcontainers
jpa:
hibernate:
ddl-auto: create-drop
logging:
level:
com.olhub: DEBUG
Test profile dùng Testcontainers (DB ephemeral), DEBUG log, ddl-auto=create-drop. Không lẫn với prod config.
8.4 Feature flag via profile
@Service
@Profile("feature-recommendation-v2")
public class RecommendationServiceV2 implements RecommendationService { ... }
@Service
@Profile("!feature-recommendation-v2")
public class RecommendationServiceV1 implements RecommendationService { ... }
Rollout v2: K8s deployment với SPRING_PROFILES_ACTIVE=prod,feature-recommendation-v2 cho 10% pod. 90% pod giữ v1. Spring Cloud Config + Kubernetes namespace label làm routing.
Pattern này stop-gap; LaunchDarkly/Unleash là feature flag platform purpose-built. Nhưng profile + @ConditionalOnProperty cũng đủ cho on/off feature đơn giản.
9. Vận hành production — secret management, blue/green, profile validation
Profile sai = config sai = behavior khác production. Section này cover safeguards.
9.1 Secret management — không hardcode
Anti-pattern: secret trong application-prod.yml:
# application-prod.yml — DO NOT
spring:
datasource:
password: prod-secret-123 # COMMIT GIT?!
Pattern enterprise:
- HashiCorp Vault:
spring-cloud-starter-vault-config. - AWS Secrets Manager: Spring Cloud AWS.
- K8s Secret: mount as env var.
- Sealed Secrets (Bitnami): encrypted in Git.
Property reference env var:
spring:
datasource:
password: ${DB_PASSWORD} # tu env var, fail-fast neu missing
K8s manifest mount:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: db-password
9.2 Profile validation startup — fail-fast
Force fail nếu thiếu critical property:
@Component
public class StartupValidator {
@EventListener(ApplicationReadyEvent.class)
public void validate(ApplicationReadyEvent event) {
Environment env = event.getApplicationContext().getEnvironment();
Profiles allowed = Profiles.of("dev", "test", "staging", "prod");
if (!env.acceptsProfiles(allowed)) {
throw new IllegalStateException(
"Active profiles must include one of: dev/test/staging/prod. Got: " +
Arrays.toString(env.getActiveProfiles())
);
}
if (env.acceptsProfiles(Profiles.of("prod"))) {
require(env, "spring.datasource.url");
require(env, "spring.datasource.password");
}
}
private void require(Environment env, String key) {
if (env.getProperty(key) == null) {
throw new IllegalStateException("Missing required property: " + key);
}
}
}
App refuse start nếu config invalid → fail fast tại deploy thay vì runtime bug ngầm.
9.3 Blue/green deployment
Pattern: deploy v2 với profile prod-v2, route 10% traffic, monitor metrics.
K8s manifest canary deployment:
spec:
containers:
- name: app
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod,canary" # canary = 10% pod
Service mesh (Istio/Linkerd) route theo label. Profile canary enable extra logging, sampling rate cao hơn → so sánh metric giữa canary và prod ổn định.
9.4 Failure runbook
Mode 1 — Wrong profile active production:
- Triệu chứng: app dùng config dev, connect DB localhost (down).
- Diagnose: log "The following profiles are active: dev".
- Remediate: re-deploy với
SPRING_PROFILES_ACTIVE=prod. Monitor: alert nếu profile string không match expected pattern.
Mode 2 — Profile typo (vd prdo):
- Triệu chứng: silent fall back default profile, bug ngầm.
- Remediate: validation startup (section 9.2).
Mode 3 — Multiple profile config conflict:
- Triệu chứng: config X có 2 value khác nhau giữa 2 profile cùng active.
- Remediate: profile order intentional. Document priority. Verify với
/actuator/configprops.
Mode 4 — Secret rotated, app vẫn dùng old:
- Cause: K8s Secret update không trigger pod restart.
- Remediate: rolling restart pod hoặc Spring Cloud Config refresh endpoint.
10. Pitfall tổng hợp
❌ Nhầm 1: Profile name typo, app vẫn start.
java -jar app.jar --spring.profiles.active=prdo # typo
Boot không có profile prdo → 0 profile active → fallback default → bug ngầm.
✅ Validate strict: kiểm tra log startup The following profiles are active: prdo — nếu thấy typo thì fix. Hoặc dùng Actuator /actuator/env check spring.profiles.active.
❌ Nhầm 2: Bean không có @Profile nhưng giả định "chỉ active prod".
@Component // KHONG @Profile → register o moi profile
public class CloudWatchExporter { ... }
Test profile cũng register → fail vì AWS credential không có local.
✅ Thêm @Profile("prod") explicit. Hoặc @ConditionalOnProperty cho fine-grained control.
❌ Nhầm 3: Multiple profile active nhưng config conflict.
SPRING_PROFILES_ACTIVE=dev,prod
2 profile cùng define db.url → profile sau win (prod). Có thể intended hoặc accidental. Confusing.
✅ Tránh active conflicting profile. Hoặc rõ ràng "prod override dev" — đặt thứ tự intentional.
❌ Nhầm 4: Quên application-\{profile\}.yml không tự load qua @PropertySource.
@Configuration
@PropertySource("classpath:custom.properties")
@Profile("prod")
public class ProdConfig { ... }
File custom.properties không phải convention application-X.yml → không tự profile-specific. Phải nhân bản: custom-prod.properties, custom-dev.properties.
✅ Dùng convention application-\{profile\}.yml + Boot tự handle. Tránh @PropertySource custom trừ khi cần.
❌ Nhầm 5: @Profile("prod") @Component nhưng inject vào singleton common.
@Component @Profile("prod") class ProdHelper { ... }
@Service
public class CommonService {
@Autowired ProdHelper helper; // FAIL khi profile=dev (no bean)
}
✅ Inject qua interface + 2 implementation profile-specific:
@Component @Profile("prod") class ProdHelper implements Helper { ... }
@Component @Profile("!prod") class DevHelper implements Helper { ... }
@Autowired Helper helper; // moi profile co implementation
❌ Nhầm 6: Hardcode profile name strings rải rác code.
if (env.acceptsProfiles("prod")) { ... } // hardcode string nhieu noi
✅ Define constant: public static final String PROD = "prod"; hoặc dùng enum. Refactor friendly.
❌ Nhầm 7: @ActiveProfiles("test") trong test nhưng quên override application-test.yml.
✅ Profile test chỉ tag — phải có file application-test.yml (trong src/test/resources) hoặc bean @Profile("test") để có effect.
11. 📚 Deep Dive Spring Reference
Spring Boot Reference docs:
- Spring Boot Reference — Profiles — overview chính thức.
- Spring Boot Reference — Profile Groups — gom profile.
- Spring Boot Reference — Profile Activation — 5 cách activate.
Spring Framework docs:
- Spring Framework Reference — Bean Definition Profiles —
@Profileannotation chi tiết. - Spring Framework Reference — Profile Expression —
!,|,&operator.
Source:
Profiles— interface định nghĩa profile expression.ProfileCondition— implement@Profile.
Tool:
/actuator/env— runtime check active profile + property source.- IntelliJ "Spring Boot Profiles" tool window — quick switch profile.
--spring.profiles.active=...— override at run time.
Bài viết:
- Stéphane Nicoll — Bean profile in Spring Boot 2.4+ — multi-document, profile group.
Ghi chú: profile là cơ chế đơn giản nhưng power. Pattern phổ biến là 1 profile cho env (prod), 1 cho region (asia-east), 1 cho feature (monitoring). Multi-active profile cho phép orthogonal concern. Đừng over-engineer — bắt đầu với 3-4 profile (dev, test, staging, prod), thêm khi cần.
12. Tóm tắt
- Profile là named tag gắn vào bean/config — toggle theo môi trường.
- 5 cách activate: property
spring.profiles.active, JVM-D, env varSPRING_PROFILES_ACTIVE, programmaticsetAdditionalProfiles, test@ActiveProfiles. - Multi-active:
dev,monitoring,asia-east— Boot apply theo thứ tự, sau override trước. @Profileđặt được trên@Bean,@Component,@Configurationclass.- Profile expression (
!,|,&):@Profile("!dev"),@Profile("prod | staging"),@Profile("prod & monitoring"). - Profile groups (Boot 2.4+) gom logic:
production = prod + prod-db + prod-cache. Tách concern thành nhiều file. - Default profile
default: bean register khi không profile nào active.spring.profiles.defaultset fallback. - Multi-document YAML (Boot 2.4+): nhiều profile trong 1 file qua
---+spring.config.activate.on-profile. - Inheritance mô phỏng qua
spring.profiles.includehoặc cascade activation order. - Pattern thực tế: 1 profile cho env, 1 cho region, 1 cho feature. Multi-tenant qua profile group.
- Test profile + Testcontainers:
@ActiveProfiles("test")+application-test.ymlchứa Testcontainers JDBC URL. - Feature flag đơn giản qua
@Profile("feature-X"). Production rollout dùng LaunchDarkly/Unleash purpose-built.
13. Tự kiểm tra
Q1Bean @Component @Profile("prod") được inject vào @Service không có annotation profile. Active profile = "dev". Điều gì xảy ra tại startup?▸
@Component @Profile("prod") được inject vào @Service không có annotation profile. Active profile = "dev". Điều gì xảy ra tại startup?App fail to start với NoSuchBeanDefinitionException.
Lý do:
@Servicekhông có@Profile→ register ở mọi profile (bao gồm dev).- Service constructor cần inject 1 bean
@Profile("prod")— nhưng profile dev active, bean prod skip. - Spring resolve dependency: tìm bean type → không có → throw.
Cách fix — 3 lựa chọn:
- Inject qua interface + 2 implementation:
public interface MetricsExporter { ... } @Component @Profile("prod") class CloudWatchExporter implements MetricsExporter { ... } @Component @Profile("!prod") class ConsoleExporter implements MetricsExporter { ... } @Service public class App { @Autowired MetricsExporter exporter; // mọi profile có implementation } - Optional dependency:Code phải null-check.
@Autowired(required = false) private MetricsExporter exporter; // null nếu profile != prod - ObjectProvider:
@Autowired ObjectProvider<MetricsExporter> exporterProvider; public void exportIfAvailable() { exporterProvider.ifAvailable(e -> e.export(...)); }
Best practice: cách 1 — luôn có implementation cho mọi profile. App không cần null-check, code business clean.
Q2Profile expression @Profile("prod & monitoring") — bean register khi nào? Khi nào không?▸
@Profile("prod & monitoring") — bean register khi nào? Khi nào không?Bean register khi cả 2 profile prod và monitoring đều active.
Operator profile expression:
!X: NOT — profile X không active.X | Y: OR — X HOẶC Y active.X & Y: AND — X VÀ Y cùng active (Spring 6 / Boot 5.1+).{X, Y}: array = OR (legacy syntax).
Bảng truth table cho "prod & monitoring":
| Active profiles | Bean register? |
|---|---|
prod | ❌ Không (thiếu monitoring) |
monitoring | ❌ Không (thiếu prod) |
prod, monitoring | ✅ Có |
prod, monitoring, asia-east | ✅ Có (đủ 2 profile cần) |
dev, monitoring | ❌ Không (thiếu prod) |
| (không profile) | ❌ Không |
Use case thực tế: Prometheus exporter chỉ cần khi prod + có monitoring stack. Dev profile không cần (overhead). Profile expression cho phép logic AND mà không phải tách 2 bean.
Cách activate prod + monitoring:
# Cach 1: list trong env
SPRING_PROFILES_ACTIVE=prod,monitoring
# Cach 2: profile group
spring:
profiles:
group:
production: prod, monitoringQ3Đoạn YAML sau có gì hay? Khi nào nên dùng pattern này thay vì tách thành nhiều file?spring:
application:
name: order-service
---
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:postgresql://localhost/dev
---
spring:
config:
activate:
on-profile: prod
datasource:
url: ${PROD_DB_URL}
▸
spring:
application:
name: order-service
---
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:postgresql://localhost/dev
---
spring:
config:
activate:
on-profile: prod
datasource:
url: ${PROD_DB_URL}Cái hay: multi-document YAML (Boot 2.4+) — 3 document trong 1 file ngăn cách bằng ---.
Cách hoạt động:
- Document 1 (no profile filter): apply mọi profile — common config.
- Document 2 (
on-profile: dev): apply chỉ khidevactive. - Document 3 (
on-profile: prod): apply chỉ khiprodactive.
Boot pick document có on-profile match active profile, merge với document 1.
Khi nào dùng multi-document vs separate files:
| Tình huống | Approach |
|---|---|
| File ngắn (<100 dòng), 2-3 profile | Multi-document — 1 file, dễ thấy overview |
| File dài, 5+ profile, nhiều team chỉnh | Separate files — tách concern, merge conflict ít hơn |
| Dùng profile groups | Separate files — group reference file |
| Quick prototype / demo | Multi-document — không phải tạo nhiều file |
| Production app stable | Separate files — review per-profile dễ |
Pros multi-document:
- Single source of truth — open 1 file thấy toàn cảnh.
- Đảm bảo cấu trúc nhất quán (cùng key parent
spring.config.activate.on-profile). - Dễ diff giữa profile (cùng file → IDE highlight diff).
Cons multi-document:
- File phình to khi nhiều profile.
- Merge conflict khi nhiều dev cùng sửa profile khác nhau.
- Search/grep khó hơn (cần match profile context).
Pattern lai: giữ application.yml với common config + multi-document cho profile nhỏ. File lớn (vd application-prod.yml với 200 dòng) tách riêng.
Q4Bạn deploy app ở 3 region: asia-east, eu-west, us-east. Mỗi region có DB endpoint riêng + S3 bucket riêng + tracing endpoint riêng. Common config (security, jackson) giống nhau. Thiết kế profile layout thế nào để DRY?▸
Profile group + region-specific files:
# application.yml — common cho moi profile
spring:
application:
name: order-service
jackson:
serialization:
write-dates-as-timestamps: false
security:
oauth2:
resource-server:
jwt:
issuer-uri: ${JWT_ISSUER}
profiles:
group:
asia-east: prod-base, prod-asia-east
eu-west: prod-base, prod-eu-west
us-east: prod-base, prod-us-east# application-prod-base.yml — config common cho prod (moi region)
spring:
datasource:
hikari:
maximum-pool-size: 50
jpa:
hibernate:
ddl-auto: validate
logging:
level:
com.olhub: INFO# application-prod-asia-east.yml — region-specific
spring:
datasource:
url: jdbc:postgresql://prod-db.asia-east.internal/app
cloud:
aws:
s3:
endpoint: https://s3.ap-southeast-1.amazonaws.com
management:
tracing:
sampling:
probability: 0.1
zipkin:
tracing:
endpoint: https://tracing.asia-east.internal/api/v2/spans# application-prod-eu-west.yml
spring:
datasource:
url: jdbc:postgresql://prod-db.eu-west.internal/app
cloud:
aws:
s3:
endpoint: https://s3.eu-west-1.amazonaws.com
management:
zipkin:
tracing:
endpoint: https://tracing.eu-west.internal/api/v2/spansK8s deployment per region:
# K8s asia-east cluster
env:
- name: SPRING_PROFILES_ACTIVE
value: "asia-east"
# K8s eu-west cluster
env:
- name: SPRING_PROFILES_ACTIVE
value: "eu-west"Lợi ích:
- DRY: common (security, jackson) trong
application.yml. Common prod (pool, JPA) trongprod-base.yml. Chỉ region-specific (DB URL, S3 endpoint, tracing) trong file region. - Easy review: mở
prod-eu-west.ymlthấy ngay khác biệt vs region khác. - Add region mới: tạo
prod-ap-south.yml+ thêm vào groupap-south. Không phải duplicate base config. - Rollback dễ: bug in 1 region không affect region khác (file riêng biệt).
Anti-pattern (đừng làm): 3 file application-asia-east.yml, application-eu-west.yml, application-us-east.yml mỗi file 200 dòng duplicate 80%. Khi cần update common rule → sửa 3 chỗ → dễ inconsistent.
Q5Profile default khác profile không có annotation ở chỗ nào? Cho ví dụ minh hoạ.▸
default khác profile không có annotation ở chỗ nào? Cho ví dụ minh hoạ.Khác biệt:
- Bean không
@Profile: register ở mọi profile (bao gồm khi không có active profile). - Bean
@Profile("default"): register CHỈ KHI không có active profile nào.
Ví dụ minh hoạ:
@Component
public class CommonService { ... } // case 1
@Component
@Profile("default")
public class FallbackService { ... } // case 2
@Component
@Profile("dev")
public class DevHelper { ... } // case 3Bảng register theo profile active:
| spring.profiles.active | CommonService | FallbackService | DevHelper |
|---|---|---|---|
| (empty) | ✅ | ✅ | ❌ |
| dev | ✅ | ❌ | ✅ |
| prod | ✅ | ❌ | ❌ |
| dev,prod | ✅ | ❌ | ✅ |
Use case @Profile("default"):
- Fallback config khi developer chạy local không set profile: 1 instance H2 in-memory thay vì kết nối real DB.
- Demo mode: app chạy với mock data khi không config gì.
- Initial setup wizard: bean tạo data sample khi app lần đầu chạy fresh.
Pattern phổ biến:
# application.yml
spring:
profiles:
default: dev # neu khong active gi, fallback 'dev'Set spring.profiles.default = profile dùng khi spring.profiles.active empty. Lúc này @Profile("default") bean **không register** (vì đã có dev active).
Q6Bạn rollout feature mới qua profile feature-recommendation-v2 cho 10% pod ở K8s. Strategy nào setup K8s deployment + Spring profile?▸
feature-recommendation-v2 cho 10% pod ở K8s. Strategy nào setup K8s deployment + Spring profile?Approach: Canary deployment với 2 K8s deployment chia traffic.
# K8s deployment chinh - 90% pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 9 # 90% capacity
template:
spec:
containers:
- name: app
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"# K8s canary deployment - 10% pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service-canary
spec:
replicas: 1 # 10% capacity
template:
spec:
containers:
- name: app
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod,feature-recommendation-v2" # them feature flag profile# Service span ca 2 deployment qua label
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service # match ca 2 deployment
ports:
- port: 80
targetPort: 8080Spring code:
public interface RecommendationService {
List<Product> recommend(User user);
}
@Service
@Profile("feature-recommendation-v2")
public class RecommendationServiceV2 implements RecommendationService {
// ML-based, expensive
}
@Service
@Profile("!feature-recommendation-v2")
public class RecommendationServiceV1 implements RecommendationService {
// Rule-based, fast
}Service inject RecommendationService — Boot pick V1 hoặc V2 tuỳ profile của pod.
Rollout dần:
- Day 1: Canary 1 pod (10%). Monitor metrics, error rate.
- Day 3: Tăng canary lên 3 pod (30%). Tiếp tục monitor.
- Day 7: Tăng lên 5 pod (50%). A/B test result.
- Day 14: Tăng lên 9 pod (90%). Bỏ deployment chính.
- Day 21: 100% pod chạy V2. Xoá
feature-recommendation-v2profile, mark V1 deprecated.
Cảnh báo: profile-based feature flag là stop-gap. Production-grade feature flag dùng LaunchDarkly/Unleash/FeatureHub:
- Toggle runtime, không restart pod.
- User segmentation (10% user thấy V2, không phải 10% pod).
- A/B test với metrics tracking.
- Kill switch nhanh chóng.
Profile chỉ phù hợp cho rollout simple (vd toàn site dùng V2 sau 1 tuần). Phức tạp hơn → tool chuyên dụng.
Bài tiếp theo: Logging — Logback default, structured logging, log level dynamic
Bài này có giúp bạn hiểu bản chất không?
Bình luận (0)
Đang tải...