Tech Blog

การออกแบบการสลับ Active Profile ของ Spring Boot ระหว่าง docker-compose และ ECS

by y104
Spring Boot Docker AWS ECS

บทนำ

เมื่อรัน Spring Boot app ในหลายสภาพแวดล้อม สิ่งสำคัญคือต้องเข้าใจอย่างแม่นยำว่า application.yml ไหนกำลังถูกใช้งานอยู่

  • Local development: application-local.yml
  • Docker Compose: application-docker.yml
  • AWS ECS (production): application-prod.yml

บทความนี้บันทึกการออกแบบการสลับ profile และวิธีการส่ง configuration บน ECS


พื้นฐาน Spring Boot Profile

เมื่อสร้างไฟล์ที่ชื่อ application-{profile}.yml ไฟล์นั้นจะถูก load เฉพาะเมื่อ profile นั้น active อยู่

src/main/resources/
  application.yml          ← ใช้ร่วมกันทุกสภาพแวดล้อม
  application-local.yml    ← สำหรับ local development
  application-docker.yml   ← สำหรับ Docker Compose
  application-prod.yml     ← สำหรับ production (ECS)

วิธีระบุ profile:

# ระบุตอน startup
java -jar app.jar --spring.profiles.active=prod

# ระบุผ่าน environment variable (Docker-friendly)
SPRING_PROFILES_ACTIVE=prod java -jar app.jar

Configuration Files แต่ละสภาพแวดล้อม

application.yml (ใช้ร่วมกันทุกสภาพแวดล้อม)

spring:
  application:
    name: dvd-rental-customer-backend
  datasource:
    driver-class-name: org.postgresql.Driver
  jpa:
    open-in-view: false

server:
  port: 8082

application-local.yml

spring:
  datasource:
    url: jdbc:postgresql://localhost:15433/dvdrental
    username: postgres
    password: postgres
  
  thymeleaf:
    cache: false

logging:
  level:
    com.example: DEBUG

application-docker.yml

spring:
  datasource:
    # เชื่อมต่อ DB โดยใช้ Docker Compose service name
    url: jdbc:postgresql://postgres:5432/dvdrental
    username: postgres
    password: postgres

application-prod.yml

spring:
  datasource:
    # Production DB URL รับจาก environment variables
    url: ${DATABASE_URL}
    username: ${DATABASE_USERNAME}
    password: ${DATABASE_PASSWORD}

logging:
  level:
    root: WARN
    com.example: INFO

สำคัญ: อย่า hard-code ข้อมูลสำคัญของ production ใน yaml ต้องส่งผ่าน environment variables เสมอ


การระบุ Profile ใน Docker Compose

# compose.yml
services:
  app:
    image: dvd-rental-customer-backend:latest
    environment:
      SPRING_PROFILES_ACTIVE: docker  # ← ระบุที่นี่
      TZ: Asia/Tokyo
    ports:
      - "8082:8082"
    depends_on:
      postgres:
        condition: service_healthy
  
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: dvdrental
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

การระบุ Profile บน ECS (AWS Fargate)

ตั้งค่า environment variables ของ container ใน ECS task definition

ตัวอย่างการตั้งค่าด้วย CDK

// cdk/lib/app-stack.ts
const taskDefinition = new ecs.FargateTaskDefinition(this, 'AppTask', {
  memoryLimitMiB: 512,
  cpu: 256,
});

const container = taskDefinition.addContainer('AppContainer', {
  image: ecs.ContainerImage.fromEcrRepository(ecrRepo, 'latest'),
  
  environment: {
    // Environment variables (ข้อความธรรมดา)
    SPRING_PROFILES_ACTIVE: 'prod',
    TZ: 'Asia/Tokyo',
  },
  
  secrets: {
    // ข้อมูลสำคัญรับจาก SSM Parameter Store
    DATABASE_URL: ecs.Secret.fromSsmParameter(
      ssm.StringParameter.fromStringParameterName(this, 'DbUrl', '/dvd-rental/db-url')
    ),
    DATABASE_USERNAME: ecs.Secret.fromSsmParameter(
      ssm.StringParameter.fromStringParameterName(this, 'DbUser', '/dvd-rental/db-username')
    ),
    DATABASE_PASSWORD: ecs.Secret.fromSsmParameter(
      ssm.StringParameter.fromStringParameterName(this, 'DbPassword', '/dvd-rental/db-password')
    ),
  },
  
  logging: ecs.LogDrivers.awsLogs({
    streamPrefix: 'dvd-rental',
    logGroup: logGroup,
  }),
});

วิธีตรวจสอบ Profile บน ECS

หลัง deploy ตรวจสอบว่า profile ที่ถูกต้องถูก apply ผ่าน CloudWatch Logs

Spring Boot จะ output active profile ไปยัง logs ตอน startup:

INFO  o.s.b.SpringApplication - The following 1 profile is active: "prod"
INFO  o.s.b.w.embedded.tomcat.TomcatWebServer - Tomcat initialized with port 8082 (http)

ยืนยันว่าแสดง "prod" ก่อนถือว่า deployment เสร็จสมบูรณ์

# ตรวจสอบ profile ใน CloudWatch Logs
aws logs filter-log-events \
    --log-group-name /dvd-rental/app \
    --filter-pattern "profiles is active" \
    --region ap-northeast-1

ข้อผิดพลาดที่พบบ่อย

① ลืมระบุ profile ใน Docker Compose

หากไม่ระบุ profile จะใช้ default profile
แม้ว่า application-docker.yml จะมีอยู่ก็จะไม่ถูกอ่าน

# NG (ไม่ระบุ profile)
services:
  app:
    environment:
      TZ: Asia/Tokyo

# OK
services:
  app:
    environment:
      SPRING_PROFILES_ACTIVE: docker  # ← จำเป็น
      TZ: Asia/Tokyo

② นำ application-local.yml ในเครื่องเข้าไปใน Docker image

หาก production image มีข้อมูลการเชื่อมต่อ local เช่น localhost:15433 อาจถูกอ้างอิงโดยไม่ตั้งใจ

# ยกเว้นใน .dockerignore
src/main/resources/application-local.yml

③ ใช้ตัวพิมพ์ใหญ่/เล็กผิดสำหรับ environment variables

Spring Boot รองรับทั้ง SPRING_PROFILES_ACTIVE (ตัวพิมพ์ใหญ่ทั้งหมด คั่นด้วย underscore) และ spring.profiles.active (คั่นด้วยจุด)
สำหรับ Docker environment variables ให้ใช้รูปแบบตัวพิมพ์ใหญ่ underscore


สรุป

สภาพแวดล้อมProfileวิธีตั้งค่า
Local developmentlocalIDE Run config หรือ JVM args
Docker Composedockerenvironment: SPRING_PROFILES_ACTIVE: docker
ECS (production)prodTask definition environment
  • อย่า hard-code ข้อมูลสำคัญใน application-prod.yml ให้ส่งผ่าน environment variables หรือ SSM
  • หลัง deploy ต้องตรวจสอบ “active profile” ใน CloudWatch Logs เสมอ
  • รักษา profile names ให้เรียบง่าย ประมาณสามประเภท: local, Docker และ production แทนที่จะสร้างหนึ่งต่อสภาพแวดล้อม

แผนที่บทความสำหรับซีรีส์นี้

สร้างแอป DVD Rental สำหรับผู้ใช้ปลายทาง — โครงสร้าง Vue 3 + Spring Boot คู่กับแอปผู้ดูแลระบบ พร้อมแผนที่บทความ
สร้างแอปจัดการ DVD Rental ด้วย Spring Boot + Thymeleaf โดยใช้ฐานข้อมูลตัวอย่าง dvdrental

ส่งข้อความได้ตามสบาย

กรุณาส่งข้อความ หากมีคำปรึกษาด้านเทคนิค ความคิดเห็น หรือคำถาม