← Назад к вопросам

Как конфигурируется различное окружение для каждого стенда

2.0 Middle🔥 201 комментариев
#Docker, Kubernetes и DevOps

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как конфигурируется различное окружение для каждого стенда

Управление конфигурацией для разных сред (dev, staging, prod) — критическая задача в Spring Boot приложениях. Существует несколько стандартных подходов.

1. Spring Profiles — основной механизм

Это встроенное решение Spring для управления конфигурацией по окружению:

# application.yml (базовая конфигурация)
spring:
  application:
    name: my-app
  jpa:
    hibernate:
      ddl-auto: validate

app:
  feature-flags:
    analytics-enabled: true
# application-dev.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/myapp_dev
    username: dev
    password: dev123
  jpa:
    show-sql: true
    properties:
      hibernate.format_sql: true

app:
  debug: true
  log-level: DEBUG
# application-staging.yml
spring:
  datasource:
    url: jdbc:mysql://staging-db.internal:3306/myapp_staging
    username: ${DB_USER}
    password: ${DB_PASSWORD}
  jpa:
    show-sql: false

app:
  debug: false
  log-level: INFO
# application-prod.yml
spring:
  datasource:
    url: jdbc:mysql://${PROD_DB_HOST}:3306/myapp
    username: ${PROD_DB_USER}
    password: ${PROD_DB_PASSWORD}
  jpa:
    show-sql: false
    properties:
      hibernate.generate_statistics: false

app:
  debug: false
  log-level: WARN
  cache-ttl: 3600

2. Активация профилей

# Через системное свойство при запуске
java -jar app.jar --spring.profiles.active=dev

# Через переменную окружения
export SPRING_PROFILES_ACTIVE=staging
java -jar app.jar

# Через application.properties
# application.properties
spring.profiles.active=prod

# Через Docker переменную
DOCKER_OPTS="-e SPRING_PROFILES_ACTIVE=staging"

3. Конфигурация через @Configuration классы

@Configuration
@Profile("dev")
public class DevConfig {
    
    @Bean
    public DataSource devDataSource() {
        return DataSourceBuilder.create()
            .driverClassName("org.h2.Driver")
            .url("jdbc:h2:mem:testdb")
            .username("sa")
            .password("")
            .build();
    }
    
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    @Bean
    public EmailService emailService() {
        // Mock email service для разработки
        return new MockEmailService();
    }
}

@Configuration
@Profile("prod")
public class ProdConfig {
    
    @Bean
    public DataSource prodDataSource(
        @Value("${prod.db.host}") String host,
        @Value("${prod.db.port}") int port,
        @Value("${prod.db.name}") String database) {
        return DataSourceBuilder.create()
            .driverClassName("org.postgresql.Driver")
            .url("jdbc:postgresql://" + host + ":" + port + "/" + database)
            .username(System.getenv("DB_USER"))
            .password(System.getenv("DB_PASSWORD"))
            .hikari(HikariConfig.withConnectionPoolSize(20))
            .build();
    }
    
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
            .setConnectTimeout(Duration.ofSeconds(5))
            .setReadTimeout(Duration.ofSeconds(10))
            .build();
    }
    
    @Bean
    public EmailService emailService(EmailProperties properties) {
        return new SmtpEmailService(properties);
    }
}

4. Управляемые свойства через @ConfigurationProperties

@Configuration
@ConfigurationProperties(prefix = "app")
public class AppProperties {
    
    private Database database = new Database();
    private Cache cache = new Cache();
    private Security security = new Security();
    private String environment;
    
    public static class Database {
        private String host;
        private int port;
        private String name;
        private int maxConnections;
        
        // getters/setters
    }
    
    public static class Cache {
        private long ttl;
        private boolean enabled;
        private String implementation; // redis, memcached, local
        
        // getters/setters
    }
    
    public static class Security {
        private String jwtSecret;
        private long jwtExpiration;
        private List<String> allowedOrigins;
        
        // getters/setters
    }
}

// Использование
@Service
public class AuthService {
    
    private final AppProperties appProperties;
    
    public AuthService(AppProperties appProperties) {
        this.appProperties = appProperties;
    }
    
    public String createToken(User user) {
        return Jwts.builder()
            .subject(user.getId().toString())
            .issuedAt(new Date())
            .expiration(new Date(System.currentTimeMillis() + 
                appProperties.getSecurity().getJwtExpiration()))
            .signWith(Keys.hmacShaKeyFor(
                appProperties.getSecurity().getJwtSecret().getBytes()))
            .compact();
    }
}

5. YAML структура для разных сред

# application.yml
spring:
  config:
    import:
      - optional:file:./config/application-${spring.profiles.active}.yml
  
  application:
    name: my-app
  
  profiles:
    active: dev
    include:
      - common
      - features
📁 resources/
  ├── application.yml
  ├── application-common.yml (общие свойства)
  ├── application-dev.yml
  ├── application-staging.yml  
  ├── application-prod.yml
  ├── application-features.yml (feature flags)
  └── logback-spring.xml (различные уровни логирования)

6. Environment переменные и Secrets

@Service
public class EnvironmentService {
    
    private final Environment env;
    
    public EnvironmentService(Environment env) {
        this.env = env;
    }
    
    public String getDatabaseUrl() {
        // Приоритет: системное свойство → переменная окружения → default
        return env.getProperty(
            "spring.datasource.url",
            "jdbc:mysql://localhost:3306/myapp"
        );
    }
    
    public String getApiKey() {
        // Для production secrets используй переменные окружения
        String key = env.getProperty("API_KEY");
        if (key == null && !isProduction()) {
            key = "dev-key-12345"; // Fallback только для dev
        }
        return key;
    }
    
    private boolean isProduction() {
        return "prod".equals(env.getActiveProfiles());
    }
}

7. Conditional beans по профилю

@Configuration
public class ServiceConfig {
    
    @Bean
    @ConditionalOnProfile("dev")
    public LoggingAspect devLoggingAspect() {
        return new VerboseLoggingAspect(); // Логирует всё подряд
    }
    
    @Bean
    @ConditionalOnProfile("prod")
    public LoggingAspect prodLoggingAspect() {
        return new MinimalLoggingAspect(); // Логирует только ошибки
    }
    
    @Bean
    @ConditionalOnProfile({"dev", "staging"})
    public MockExternalService mockService() {
        return new MockExternalService();
    }
    
    @Bean
    @ConditionalOnProfile("prod")
    public RealExternalService realService(ExternalApiClient client) {
        return new RealExternalService(client);
    }
}

8. Пример Dockerfile с профилями

# Dockerfile
FROM openjdk:17-slim

ARG SPRING_PROFILE=dev
ENV SPRING_PROFILES_ACTIVE=${SPRING_PROFILE}

COPY target/app.jar app.jar

ENTRYPOINT ["java", "-jar", "app.jar"]

# Использование:
# docker build --build-arg SPRING_PROFILE=prod -t myapp:prod .
# docker run -e SPRING_PROFILES_ACTIVE=staging myapp:prod

9. Docker Compose с разными конфигурациями

version: '3.8'

services:
  app-dev:
    build:
      context: .
      args:
        SPRING_PROFILE: dev
    environment:
      - SPRING_PROFILES_ACTIVE=dev
      - DATABASE_URL=jdbc:mysql://mysql-dev:3306/myapp
    ports:
      - "8080:8080"

  app-prod:
    build:
      context: .
      args:
        SPRING_PROFILE: prod
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - DATABASE_URL=jdbc:postgresql://postgres-prod:5432/myapp
      - DB_USER=${PROD_DB_USER}
      - DB_PASSWORD=${PROD_DB_PASSWORD}
    ports:
      - "8443:8443"
    depends_on:
      - postgres-prod

  mysql-dev:
    image: mysql:8
    environment:
      - MYSQL_DATABASE=myapp
      - MYSQL_ROOT_PASSWORD=dev123

10. Kubernetes ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-prod
data:
  application-prod.yml: |
    spring:
      datasource:
        url: jdbc:postgresql://postgres:5432/myapp
        username: ${POSTGRES_USER}
        password: ${POSTGRES_PASSWORD}
      jpa:
        show-sql: false
    app:
      log-level: WARN
      cache-ttl: 3600
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: myapp:latest
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: POSTGRES_USER
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: username
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password
        volumeMounts:
        - name: config
          mountPath: /app/config
      volumes:
      - name: config
        configMap:
          name: app-config-prod

Итоговая таблица управления конфигурацией

МеханизмDevStagingProdПример
application-*.ymlDB хосты, лог-уровень
Переменные окруженияПароли, API ключи
@Profile классыMock vs Real сервисы
@ConfigurationPropertiesТипизированные свойства
Secrets (K8s/Docker)Ключи, пароли БД
Консоль при запускеПереопределение на лету

Знание этих подходов позволяет строить масштабируемые приложения, которые легко развертываются в разных окружениях.

Как конфигурируется различное окружение для каждого стенда | PrepBro