EKS 배포 프로세스 한 장 요약
2026. 06. 22.
KubernetesHelmECRDockerCI/CD
이 글의 목표: 백엔드를 EKS에 띄우기까지의 일련의 작업을 단계별로 정리한다. 다음 서비스(다른 모듈)도 같은 패턴으로 반복하면 된다.
전체 흐름 한 장 요약
[코드 작성]
↓
[Dockerfile 작성] ─── (1)
↓
[ECR 리포지토리 생성] ─── (2)
↓
[Docker 이미지 빌드] ─── (3) ← linux/amd64 크로스빌드
↓
[ECR에 push] ─── (4)
↓
[Helm 차트 작성/업데이트] ─── (5)
↓
[helm install / upgrade] ─── (6)
↓
[Ingress로 외부 노출] ─── (7)
↓
[curl로 헬스체크] ─── (8)
(1) Dockerfile 작성
위치: backend/Dockerfile
# Build stage
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY gradlew gradlew
COPY gradle gradle
COPY build.gradle settings.gradle ./
COPY src src
RUN chmod +x gradlew && ./gradlew bootJar --no-daemon
# Runtime stage
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
COPY --from=builder /app/build/libs/*-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-XX:+UseContainerSupport","-XX:MaxRAMPercentage=75.0","-jar","/app/app.jar"]핵심 결정 포인트
| 항목 | 선택 | 이유 |
|---|---|---|
| multi-stage | builder + runtime | 최종 이미지에 JDK/Gradle 캐시 안 남김. 크기 절감 |
| alpine | jre-alpine | musl libc 기반 경량. ~150MB 수준 |
| non-root user | spring:spring | 보안 베스트 프랙티스 |
| MaxRAMPercentage | 75% | K8s memory limit 안에서 JVM 힙을 자동 산정 |
+UseContainerSupport | 켬 | JVM이 cgroup 기반으로 CPU/메모리 인식 |
.dockerignore 도 같이 작성
.git
.github
.gradle
.idea
build
bin
*.log
docker-compose.yml
→ 빌드 컨텍스트가 작아져 push 빠름 + 불필요 파일 안 들어감.
(2) ECR 리포지토리 생성
aws ecr create-repository \
--repository-name meeny/backend \
--region ap-northeast-2 \
--image-scanning-configuration scanOnPush=true \
--encryption-configuration encryptionType=AES256옵션 선택
| 옵션 | 선택 | 의미 |
|---|---|---|
repository-name | meeny/backend | 슬래시는 단순 네임스페이스 (실제 경로 분리 X) |
scanOnPush | true | push 즉시 CVE 스캔 |
encryptionType | AES256 | S3와 동일. KMS도 가능 |
imageTagMutability | (기본) MUTABLE | 같은 태그 덮어쓰기 가능. 운영은 IMMUTABLE 권장 |
결과 URI
505947591451.dkr.ecr.ap-northeast-2.amazonaws.com/meeny/backend
→ 이 URI가 Docker tag와 Helm values.yaml의 image.repository에 들어간다.
(3, 4) 빌드 + Push
ECR 로그인 (필수, 12시간 유효)
aws ecr get-login-password --region ap-northeast-2 \
| docker login --username AWS \
--password-stdin 505947591451.dkr.ecr.ap-northeast-2.amazonaws.combuildx 크로스빌드 + push (한 번에)
docker buildx build \
--platform linux/amd64 \
-t 505947591451.dkr.ecr.ap-northeast-2.amazonaws.com/meeny/backend:0.0.1 \
-t 505947591451.dkr.ecr.ap-northeast-2.amazonaws.com/meeny/backend:latest \
--push \
.왜 --platform linux/amd64?
- Mac은 arm64 (M-시리즈)
- EKS 노드는 x86_64 (amd64) (
t3.small) - platform 안 지정하면 기본적으로 호스트(arm64) 이미지가 빌드됨 → EKS에서
exec format error - buildx가 QEMU로 자동 에뮬레이션해서 amd64 이미지 생성
태그 전략 (베스트 프랙티스)
| 태그 | 용도 |
|---|---|
0.0.1 (or v1.2.3) | 불변 버전 태그. 정확한 빌드를 가리킴 |
latest | 가장 최신 (편의용, 운영에선 비추) |
git-<sha> | git commit hash 기반 (예: git-abc1234) |
<branch>-<sha> | feature 브랜치별 |
→ 운영 배포는 불변 태그 + IMMUTABLE 리포 조합이 안전.
(5) Helm 차트 작성
위치: helm/backend/
backend/
├── Chart.yaml # 차트 메타정보
├── values.yaml # 기본값
└── templates/
├── deployment.yaml
├── service.yaml
└── ingress.yaml
Chart.yaml
apiVersion: v2
name: meeny-backend
type: application
version: 1.0.0 # 차트 자체 버전
appVersion: "1.0.0" # 앱 버전 (이미지 태그와 별개 가능)values.yaml 핵심
image:
repository: 505947591451.dkr.ecr.ap-northeast-2.amazonaws.com/meeny/backend
tag: "0.0.1"
replicaCount: 2
service:
type: ClusterIP
port: 80
targetPort: 8080
probes:
liveness:
path: /actuator/health/liveness
readiness:
path: /actuator/health/readiness
resources:
requests: { cpu: 250m, memory: 512Mi }
limits: { cpu: 1000m, memory: 1Gi }
ingress:
enabled: true
className: nginx
path: /deployment.yaml 핵심
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"로 ECR 이미지 참조livenessProbe/readinessProbe에 Spring Boot Actuator 경로env에 환경변수
Spring Boot 특화 포인트
| 설정 | 이유 |
|---|---|
port 8080 | Spring Boot 기본 |
liveness /actuator/health/liveness | application.yaml에 probes.enabled: true 켜야 함 |
readiness /actuator/health/readiness | DB 연결 안 되면 Down |
| memory ≥ 512Mi | JVM 부팅 + 힙 |
initialDelaySeconds: 30 | Spring 부팅에 보통 15~25초 |
(6) Helm 배포
첫 배포 (install)
cd helm/backend
helm install backend . -n backend --create-namespace코드/이미지 변경 후 (upgrade)
helm upgrade backend . -n backend잘못 됐을 때 롤백
helm history backend -n backend # 이력
helm rollback backend <REVISION> -n backend차트 검증
helm lint ./backend # 문법
helm template ./backend # 렌더링만 (apply X)
helm install ... --dry-run --debug # 실제 설치 안 하고 시뮬(7) Ingress로 외부 노출
차트의 templates/ingress.yaml이 Ingress 리소스를 만들고, 클러스터의 AWS Load Balancer Controller가 이를 감지해 NLB의 백엔드로 이 서비스를 연결한다.
흐름:
[인터넷]
│
▼
[NLB (internet-facing)] ← AWS Load Balancer Controller가 만듦
│
▼
[ingress-nginx Pod] ← helm으로 설치한 컨트롤러
│
▼ (Host/Path 매칭)
[backend Service]
│
▼
[backend Pod × 2]
→ 새 백엔드 모듈을 추가할 때마다 NLB를 새로 만들 필요 없이 Ingress 리소스만 추가하면 된다. (서브도메인/경로로 분기)
(8) 헬스체크
NLB=$(kubectl get svc -n ingress-nginx ingress-ingress-nginx-controller \
-o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
curl -i "http://$NLB/actuator/health"
# {"status":"UP","groups":["liveness","readiness"]}새 서비스 추가 시 반복할 작업
새 서비스(예: frontend, worker)를 추가할 때:
- 코드에
Dockerfile추가 aws ecr create-repository --repository-name meeny/<서비스>docker buildx build --platform linux/amd64 ... --pushhelm/<서비스>/차트 작성 (backend 차트를 복제해서 시작)helm install <서비스> ./<서비스> -n <서비스> --create-namespace- (외부 노출 필요시) Ingress 추가
→ 패턴이 같아서 자동화하기 좋다. 다음 글에서 CI/CD로 자동화하는 법을 본다.
지금까지의 한계 (다음 글로 이어짐)
- ❌ 수동 빌드/배포: 사람이 docker build, helm upgrade 직접 실행
- ❌ 시크릿 평문:
values.yaml이나 환경변수에 노출 가능 - ❌ 이미지 태그 관리 부재: 누가 어떤 커밋으로 무엇을 배포했는지 추적 어려움
- ❌ 롤백 절차 비공식:
helm rollback손으로 입력 - ❌ 다중 환경(dev/staging/prod) 분리 안 됨
→ 다음 글들에서 차례대로 해결한다.