K8s 배포 자동화와 시크릿 관리

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-stagebuilder + runtime최종 이미지에 JDK/Gradle 캐시 안 남김. 크기 절감
alpinejre-alpinemusl libc 기반 경량. ~150MB 수준
non-root userspring:spring보안 베스트 프랙티스
MaxRAMPercentage75%K8s memory limit 안에서 JVM 힙을 자동 산정
+UseContainerSupportJVM이 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-namemeeny/backend슬래시는 단순 네임스페이스 (실제 경로 분리 X)
scanOnPushtruepush 즉시 CVE 스캔
encryptionTypeAES256S3와 동일. KMS도 가능
imageTagMutability(기본) MUTABLE같은 태그 덮어쓰기 가능. 운영은 IMMUTABLE 권장

결과 URI

505947591451.dkr.ecr.ap-northeast-2.amazonaws.com/meeny/backend

→ 이 URI가 Docker tag와 Helm values.yamlimage.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.com

buildx 크로스빌드 + 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 8080Spring Boot 기본
liveness /actuator/health/livenessapplication.yaml에 probes.enabled: true 켜야 함
readiness /actuator/health/readinessDB 연결 안 되면 Down
memory ≥ 512MiJVM 부팅 + 힙
initialDelaySeconds: 30Spring 부팅에 보통 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.yamlIngress 리소스를 만들고, 클러스터의 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)를 추가할 때:

  1. 코드에 Dockerfile 추가
  2. aws ecr create-repository --repository-name meeny/<서비스>
  3. docker buildx build --platform linux/amd64 ... --push
  4. helm/<서비스>/ 차트 작성 (backend 차트를 복제해서 시작)
  5. helm install <서비스> ./<서비스> -n <서비스> --create-namespace
  6. (외부 노출 필요시) Ingress 추가

→ 패턴이 같아서 자동화하기 좋다. 다음 글에서 CI/CD로 자동화하는 법을 본다.


지금까지의 한계 (다음 글로 이어짐)

  • 수동 빌드/배포: 사람이 docker build, helm upgrade 직접 실행
  • 시크릿 평문: values.yaml이나 환경변수에 노출 가능
  • 이미지 태그 관리 부재: 누가 어떤 커밋으로 무엇을 배포했는지 추적 어려움
  • 롤백 절차 비공식: helm rollback 손으로 입력
  • 다중 환경(dev/staging/prod) 분리 안 됨

→ 다음 글들에서 차례대로 해결한다.