EKS – Spring 프로젝트 ECR 업로드

Spring 프로파일 설정 이해

프로파일 구조

Spring 프로젝트에는 환경별로 다른 설정을 사용하기 위한 프로파일 시스템이 있다

src/main/resources/
├── application.yml          # 기본 설정 (어떤 프로파일을 사용할지 지정)
├── application-local.yml    # 로컬 개발 환경 설정
└── application-prod.yml     # 프로덕션(Kubernetes) 환경 설정

프로파일 활성화 메커니즘

application.yml
spring:
  profiles:
    default: local  # 기본값: local 프로파일 사용
이 설정을 prod로 변경하면
spring:
  profiles:
    default: prod  # prod 프로파일 사용
application-local.yml
spring:
  config:
    activate:
      on-profile: local  # 이 파일은 'local' 프로파일일 때 활성화
application-prod.yml
spring:
  config:
    activate:
      on-profile: prod  # 이 파일은 'prod' 프로파일일 때 활성화

핵심: application.yml의 default 값과 각 프로파일 파일의 on-profile 값이 일치하면 해당 파일이 적용된다

Kubernetes 환경을 위한 설정 (application-prod.yml)

Redis 연결 설정

로컬 환경 (application-local.yml)
spring:
  redis:
    host: localhost  # 로컬 PC에 설치된 Redis
    port: 6379
Kubernetes 환경 (application-prod.yml)
spring:
  redis:
    host: redis-service  # Kubernetes Service 이름
    port: 6379
Kubernetes 클러스터 내부
┌─────────────────┐         ┌─────────────────┐
│  Spring Pod     │         │   Redis Pod     │
│                 │         │                 │
│  redis:         │         │   port: 6379    │
│   host:         │─────────│                 │
│   redis-service │   ↓     │                 │
└─────────────────┘   │     └─────────────────┘
                      │
                ┌─────┴─────────┐
                │   Service     │
                │ redis-service │ 
                │  port: 6379   │
                └───────────────┘
redis.yml
apiVersion: v1
kind: Service
metadata:
  name: redis-service  # ← Spring에서 이 이름으로 접근
  namespace: hyeok
spec:
  ports:
  - port: 6379        # ← Service 포트
    targetPort: 6379  # ← Pod의 containerPort와 일치
  selector:
    app: redis
통신 흐름
  • Spring이 http://redis-service:6379
  • Kubernetes DNS가 redis-service를 Service IP로 변환
  • Service가 요청을 Redis Pod로 전달

RDS 연결 설정 및 보안 관리

로컬 환경 (application-local.yml)
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/ordersystem?useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: 1234
Kubernetes 환경 (application-prod.yml)
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${DB_HOST}:3306/ordersystem?useSSL=false&allowPublicKeyRetrieval=true
    username: admin
    password: ${DB_PW}

변수로 처리하는 이유

  • redis-service: 직접 입력 – 클러스터 내부에서만 접근 가능, 외부 노출 안 됨
  • DB_HOST: 변수 처리 – RDS는 퍼블릭 엑세스 허용, 호스트 주소 노출 시 보안 위험
  • DB_PW: 변수 처리 – 암호 노출 시 누구나 DB 접근 가능

Kubernetes Secret을 통한 정보 관리

보안 정보 관리 전략
코드 작성 단계
├── application-prod.yml (변수만 정의)
│   ├── ${DB_HOST}
│   └── ${DB_PW}
└── GitHub에 Push (안전)

이미지 빌드 단계
└── Docker 이미지 생성 (변수 상태 유지)

Pod 실행 단계
├── Kubernetes Secret에서 값 조회
│   ├── DB_HOST: database-1.xxxxx.ap-northeast-2.rds.amazonaws.com
│   └── DB_PW: 실제암호
└── Pod 환경 변수로 주입 → Spring 실행 시 값 대체
이렇게 관리하는 이유
시나리오위험도해결 방법
GitHub Public 저장소에 올림높음Secret 사용으로 코드에는 변수만 존재
private 저장소에 올림중간일반 개발자도 암호를 볼 수 있음
Kubernetes Secret 사용낮음권한 있는 사람만 Secret 접근 가능
실무 보안 관리
  • 보안 관리자 또는 팀 리더가 Kubernetes Secret 생성 및 관리
  • 일반 개발자는 암호를 모르는 상태로 개발 가능
  • 포트폴리오로 GitHub 공개해도 안전

JWT Secret Key 관리

application-prod.yml
jwt:
  expiration: 1000
  secretKey: aaa       # 학습 단계에서는 직접 입력 (선택)
  expirationRt: 288000
  secretKeyRt: bbb     # 실무에서는 Secret으로 관리 권장
JWT Secret Key 노출 시 위험
  • 토큰 조작 가능 (권한 변조, 만료 기간 연장 등)
  • 서비스 무단 사용 가능

학습 단계에서는 YAML에 직접 입력 가능하다. 그 이유는 중요한 데이터가 없고 누구나 가입 가능한 테스트 서비스일 확률이 높다. 실무에서는 Kubernetes Secret를 활용해서 실제 사용자 데이터 보호가 필요하여 필수이다

Docker 이미지 빌드 및 ECR 업로드

ECR 로그인

ECR은 권한 있는 사용자만 이미지를 업로드할 수 있도록 로그인 절차가 필요하다

명령어 구조
aws ecr get-login-password --region ap-northeast-2 | \
docker login --username AWS --password-stdin <ECR-URI>
실제 명령어 예시
aws ecr get-login-password --region ap-northeast-2 | \
docker login --username AWS --password-stdin \
123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/order-backend
ECR URI 확인 방법
  • AWS Console → ECR → 리포지토리
  • 생성한 리포지토리 클릭
  • URI 복사 (예: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/order-backend)

성공 메시지: Login Succeeded

Docker 이미지 빌드

Docker 이미지를 ECR에 업로드하려면 이미지 이름이 ECR URI를 포함해야 한다

형식
<ECR-URI>:<태그>

예시
123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/order-backend:latest
│                                                              │ │
└───────────────────────── 이미지 이름 ───────────────────────────┘ └─태그

빌드 명령어

기본 형식
docker build -t <이미지명:태그> -f <Dockerfile 경로> <빌드 컨텍스트>
프로젝트 루트에서 실행 시
docker build -t 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/order-backend:latest \
  -f ./ordersystem/Dockerfile ./ordersystem
프로젝트 폴더 내부에서 실행 시
cd ordersystem
docker build -t 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/order-backend:latest .
💀 pod status ErrImagePull
  • 다음 강의에서 pod의 status ErrImagePull가 발생하였다
  • 이미지의 CPU 아키텍처(Platform)와 서버의 CPU 아키텍처가 달라서 실행할 수 없기 때문에 발생한 에러이다.
# 이미지 다시 빌드
docker build --platform linux/amd64 -t 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/order-backend:latest .

# 이미지 다시 푸시 (ECR)
docker push 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/order-backend:latest

참고: 현재 디렉토리에 Dockerfile 이 있으면 -f 옵션 생략 가능

Dockerfile 구조 (멀티스테이지 빌드)
# Stage 1: 빌드 단계
# FROM openjdk:17-jdk-alpine 기존 명령어 에러로 인해서 아래 명령어로 수정
FROM eclipse-temurin:17-jdk-jammy as stage1



WORKDIR /app
# 소스 코드 및 빌드 파일 복사
COPY gradle gradle
COPY src src
COPY build.gradle .
COPY settings.gradle .
COPY gradlew .

# Gradle 빌드 실행
RUN chmod +x gradlew
RUN ./gradlew bootJar

# Stage 2: 실행 단계
# FROM openjdk:17-jdk-alpine 기존 명령어 에러로 인해서 아래 명령어로 수정
FROM eclipse-temurin:17-jdk-jammy

WORKDIR /app
# Stage 1에서 빌드한 JAR 파일만 복사
COPY --from=stage1 /app/build/libs/*.jar app.jar

# 컨테이너 실행 시 JAR 실행
ENTRYPOINT ["java", "-jar", "app.jar"]
오류 발생시
  • Cannot connect to the Docker daemon 에러가 나면 Docker Desktop을 실행하지 않은 것이다. Docker Desktop을 실행한 후 다시 다시 시도

멀티스테이지 빌드의 장점

  • Stage 1: 빌드에 필요한 도구 포함 (용량 큼)
  • Stage 2: 실행에 필요한 파일만 포함 (용량 작음)
  • 최종 이미지 크기 최소화
이미지 확인
docker images


출력 예시
REPOSITORY                                  TAG       IMAGE ID       CREATED         SIZE
123456789012.d.e.ap.a/order-backend        latest    99cbe9e9de9b   3 minutes ago   750MB
redis                                      latest    cc2dfb8f9999   3 months ago    222MB
mysql                                      latest    999bfb9044dc   4 months ago    1.27GB

ECR에 이미지 Push

docker push 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/order-backend:latest
Push 진행 상황
Pushing [==============>                    ] 45.2MB/150MB
...
Pushed
ECR에서 확인
  • AWS Console → ECR → 리포지토리
  • order-backend 클릭 (생성한 ecr 이나 이미지 push한 ecr)
  • 이미지 목록에 latest 태그가 보이면 성공
Push 전 ECR
Push 후 ECR

수동 작업의 한계

지금까지 프로세스는 수동으로 진행

  • 소스 코드 수정 → Docker 빌드 → ECR Push
문제점
  • 소스 코드가 변경될 때 마다 반복
  • 명령어가 복잡하고 실수하기 쉬움
  • 시간 소모적

이러한 문제점으로 인해 GitGub Actions(혹은 다른 툴)를 통한 CI/CD 자동화가 필요하다

출처 – eks를 활용한 spring 운영서버 배포(feat. devops의 모든것)