728x90
반응형
🐳 들어가며
"내 컴퓨터에서는 잘 되는데요?"
개발자라면 누구나 한 번쯤 들어본 말입니다. Docker는 이런 문제를 완벽하게 해결해줍니다. 애플리케이션과 모든 의존성을 하나의 컨테이너로 패키징하여 어디서든 동일하게 실행할 수 있죠.
이 글에서는 Spring Boot 애플리케이션을 Docker로 컨테이너화하는 방법부터 운영 환경에서의 최적화까지, 실무에서 바로 활용할 수 있는 모든 내용을 다룹니다.
📑 목차
- Docker 기초 이해하기
- Spring Boot Dockerfile 작성
- 멀티 스테이지 빌드
- Docker Compose로 환경 구성
- 개발 환경 최적화
- 운영 환경 배포
- 보안 및 최적화
- 트러블슈팅
Docker 기초 이해하기
🎯 Docker란?
Docker는 애플리케이션을 컨테이너라는 단위로 패키징하고 실행하는 플랫폼입니다.
mermaid
graph LR
A[소스 코드] --> B[Docker Image]
B --> C[Container 1]
B --> D[Container 2]
B --> E[Container 3]
핵심 개념
개념설명비유
Image | 읽기 전용 템플릿 | 설계도 |
Container | 이미지의 실행 인스턴스 | 실제 건물 |
Dockerfile | 이미지 빌드 명령서 | 레시피 |
Registry | 이미지 저장소 | 창고 |
Volume | 영구 데이터 저장소 | 외장 하드 |
Docker 설치
Windows/Mac
Docker Desktop 다운로드 및 설치
Linux (Ubuntu)
bash
# Docker 설치 스크립트
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# 사용자 권한 설정
sudo usermod -aG docker $USER
newgrp docker
# 설치 확인
docker --version
docker run hello-world
Spring Boot Dockerfile 작성
1. 기본 Dockerfile
dockerfile
# 기본 Dockerfile (단일 스테이지)
FROM eclipse-temurin:17-jre
# 작업 디렉토리 설정
WORKDIR /app
# JAR 파일 복사
COPY target/*.jar app.jar
# 포트 노출
EXPOSE 8080
# 실행 명령
ENTRYPOINT ["java", "-jar", "app.jar"]
2. 개선된 Dockerfile
dockerfile
# 개선된 Dockerfile
FROM eclipse-temurin:17-jre-alpine
# 필수 패키지 설치
RUN apk add --no-cache tzdata curl
# 시간대 설정
ENV TZ=Asia/Seoul
RUN cp /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 애플리케이션 사용자 생성
RUN addgroup -g 1000 spring && \
adduser -D -u 1000 -G spring spring
# 작업 디렉토리 생성
WORKDIR /app
# 파일 복사 및 권한 설정
COPY --chown=spring:spring target/*.jar app.jar
# 사용자 전환
USER spring:spring
# 헬스체크 추가
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
# 포트 노출
EXPOSE 8080
# JVM 옵션과 함께 실행
ENTRYPOINT ["java", \
"-XX:+UseContainerSupport", \
"-XX:MaxRAMPercentage=75.0", \
"-Djava.security.egd=file:/dev/./urandom", \
"-jar", \
"app.jar"]
멀티 스테이지 빌드
1. Maven 멀티 스테이지 빌드
dockerfile
# 1단계: 빌드 스테이지
FROM maven:3.9-eclipse-temurin-17 AS builder
# 작업 디렉토리
WORKDIR /build
# 의존성 캐싱을 위한 pom.xml 먼저 복사
COPY pom.xml .
RUN mvn dependency:go-offline -B
# 소스 코드 복사 및 빌드
COPY src ./src
RUN mvn clean package -DskipTests
# 2단계: 런타임 스테이지
FROM eclipse-temurin:17-jre-alpine
# 필수 패키지 설치
RUN apk add --no-cache tzdata
# 시간대 설정
ENV TZ=Asia/Seoul
RUN cp /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 보안을 위한 non-root 사용자
RUN addgroup -g 1000 spring && \
adduser -D -u 1000 -G spring spring
WORKDIR /app
# 빌드 스테이지에서 JAR 파일 복사
COPY --from=builder --chown=spring:spring /build/target/*.jar app.jar
# 사용자 전환
USER spring:spring
# 메타데이터
LABEL maintainer="your-email@example.com" \
version="1.0" \
description="Spring Boot Application"
# 헬스체크
HEALTHCHECK --interval=30s --timeout=3s --retries=3 --start-period=40s \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1
EXPOSE 8080
# JVM 메모리 최적화
ENV JAVA_OPTS="-XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \
-XX:InitialRAMPercentage=50.0 \
-XX:+UseG1GC \
-XX:+UseStringDeduplication"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
2. Gradle 멀티 스테이지 빌드
dockerfile
# Gradle 버전
FROM gradle:8.5-jdk17 AS builder
WORKDIR /build
# Gradle 캐시 활용
COPY build.gradle settings.gradle ./
COPY gradle ./gradle
RUN gradle dependencies --no-daemon
# 소스 코드 복사 및 빌드
COPY src ./src
RUN gradle clean bootJar --no-daemon
# 런타임 스테이지 (위와 동일)
FROM eclipse-temurin:17-jre-alpine
# ... (이하 동일)
3. Spring Boot 레이어드 JAR 활용
dockerfile
# 레이어드 JAR를 활용한 최적화
FROM eclipse-temurin:17-jre-alpine AS builder
WORKDIR /app
# JAR 파일 복사
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
# 레이어 추출
RUN java -Djarmode=layertools -jar app.jar extract
# 런타임 스테이지
FROM eclipse-temurin:17-jre-alpine
RUN apk add --no-cache tzdata && \
addgroup -g 1000 spring && \
adduser -D -u 1000 -G spring spring
WORKDIR /app
# 레이어별로 복사 (캐시 최적화)
COPY --from=builder --chown=spring:spring /app/dependencies/ ./
COPY --from=builder --chown=spring:spring /app/spring-boot-loader/ ./
COPY --from=builder --chown=spring:spring /app/snapshot-dependencies/ ./
COPY --from=builder --chown=spring:spring /app/application/ ./
USER spring:spring
EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
Docker Compose로 환경 구성
1. 개발 환경 구성
yaml
# docker-compose.yml
version: '3.8'
services:
# Spring Boot 애플리케이션
app:
build:
context: .
dockerfile: Dockerfile
container_name: spring-app
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=docker
- TZ=Asia/Seoul
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- app-network
volumes:
- ./logs:/app/logs
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# MariaDB 데이터베이스
db:
image: mariadb:11.2
container_name: spring-db
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: rootpass123!
MYSQL_DATABASE: springdb
MYSQL_USER: springuser
MYSQL_PASSWORD: springpass123!
TZ: Asia/Seoul
volumes:
- db_data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --max-connections=200
# Redis 캐시
redis:
image: redis:7-alpine
container_name: spring-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- app-network
restart: unless-stopped
command: redis-server --appendonly yes --requirepass redispass123!
# Nginx 리버스 프록시
nginx:
image: nginx:alpine
container_name: spring-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./static:/usr/share/nginx/html/static:ro
depends_on:
- app
networks:
- app-network
restart: unless-stopped
# Prometheus 모니터링
prometheus:
image: prom/prometheus:latest
container_name: spring-prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
networks:
- app-network
restart: unless-stopped
# Grafana 대시보드
grafana:
image: grafana/grafana:latest
container_name: spring-grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123!
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
depends_on:
- prometheus
networks:
- app-network
restart: unless-stopped
# 볼륨 정의
volumes:
db_data:
driver: local
redis_data:
driver: local
prometheus_data:
driver: local
grafana_data:
driver: local
# 네트워크 정의
networks:
app-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
2. 환경별 설정
yaml
# docker-compose.override.yml (개발 환경)
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
environment:
- SPRING_PROFILES_ACTIVE=dev
- DEBUG=true
volumes:
- ./src:/app/src:ro
- ./target:/app/target
ports:
- "5005:5005" # 디버그 포트
command: ["java", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", "-jar", "app.jar"]
yaml
# docker-compose.prod.yml (운영 환경)
version: '3.8'
services:
app:
image: your-registry/spring-app:${VERSION:-latest}
environment:
- SPRING_PROFILES_ACTIVE=prod
- JAVA_OPTS=-Xms512m -Xmx2048m
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
3. Nginx 설정
nginx
# nginx/conf.d/default.conf
upstream spring-app {
least_conn;
server app:8080 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
server_name example.com;
# HTTP를 HTTPS로 리다이렉트
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
# SSL 설정
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 보안 헤더
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# 정적 파일 처리
location /static/ {
alias /usr/share/nginx/html/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# API 프록시
location /api/ {
proxy_pass http://spring-app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 타임아웃 설정
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 버퍼 설정
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
# 웹소켓 지원
location /ws/ {
proxy_pass http://spring-app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
개발 환경 최적화
1. 핫 리로드 설정
dockerfile
# Dockerfile.dev
FROM eclipse-temurin:17-jdk
RUN apt-get update && apt-get install -y \
inotify-tools \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Maven Wrapper 복사
COPY mvnw .
COPY .mvn .mvn
# 의존성 캐싱
COPY pom.xml .
RUN ./mvnw dependency:go-offline
# 소스 코드는 볼륨으로 마운트
VOLUME ["/app/src", "/app/target"]
# 개발 모드 실행
CMD ["./mvnw", "spring-boot:run", "-Dspring-boot.run.profiles=docker"]
2. 효율적인 개발 워크플로우
makefile
# Makefile
.PHONY: help build run stop clean logs
help:
@echo "사용 가능한 명령어:"
@echo " make build - Docker 이미지 빌드"
@echo " make run - 컨테이너 실행"
@echo " make stop - 컨테이너 중지"
@echo " make clean - 컨테이너 및 이미지 삭제"
@echo " make logs - 로그 확인"
build:
docker-compose build --no-cache
run:
docker-compose up -d
@echo "애플리케이션이 http://localhost:8080 에서 실행 중입니다"
stop:
docker-compose down
clean:
docker-compose down -v
docker rmi $$(docker images -q spring-app) 2>/dev/null || true
logs:
docker-compose logs -f app
# 개발 환경 실행
dev:
docker-compose -f docker-compose.yml -f docker-compose.override.yml up
# 운영 환경 실행
prod:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 데이터베이스 백업
backup:
docker exec spring-db mysqldump -u root -prootpass123! springdb > backup_$$(date +%Y%m%d_%H%M%S).sql
# 헬스체크
health:
@docker-compose ps
@echo "\n=== 헬스체크 ==="
@curl -s http://localhost:8080/actuator/health | jq .
3. 개발용 스크립트
bash
#!/bin/bash
# dev.sh - 개발 환경 관리 스크립트
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
function print_usage() {
echo "사용법: ./dev.sh [명령어]"
echo "명령어:"
echo " start - 개발 환경 시작"
echo " stop - 개발 환경 중지"
echo " restart - 개발 환경 재시작"
echo " logs - 로그 확인"
echo " shell - 컨테이너 쉘 접속"
echo " db - 데이터베이스 접속"
echo " clean - 전체 정리"
}
function start_dev() {
echo -e "${GREEN}개발 환경을 시작합니다...${NC}"
docker-compose up -d
echo -e "${GREEN}완료! http://localhost:8080${NC}"
}
function stop_dev() {
echo -e "${YELLOW}개발 환경을 중지합니다...${NC}"
docker-compose down
}
function show_logs() {
docker-compose logs -f app
}
function enter_shell() {
docker-compose exec app /bin/sh
}
function enter_db() {
docker-compose exec db mysql -u springuser -pspringpass123! springdb
}
function clean_all() {
echo -e "${RED}모든 컨테이너와 볼륨을 삭제합니다. 계속하시겠습니까? (y/N)${NC}"
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
docker-compose down -v --rmi all
echo -e "${GREEN}정리 완료!${NC}"
fi
}
case "$1" in
start)
start_dev
;;
stop)
stop_dev
;;
restart)
stop_dev
start_dev
;;
logs)
show_logs
;;
shell)
enter_shell
;;
db)
enter_db
;;
clean)
clean_all
;;
*)
print_usage
exit 1
;;
esac
운영 환경 배포
1. CI/CD 파이프라인
yaml
# .github/workflows/docker-build.yml
name: Docker Build and Push
on:
push:
branches: [ main, develop ]
tags: [ 'v*' ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Cache Maven dependencies
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Build with Maven
run: mvn clean package -DskipTests
- name: Run tests
run: mvn test
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_DATE=${{ github.event.repository.updated_at }}
VCS_REF=${{ github.sha }}
2. Kubernetes 배포
yaml
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-app
labels:
app: spring-app
spec:
replicas: 3
selector:
matchLabels:
app: spring-app
template:
metadata:
labels:
app: spring-app
spec:
containers:
- name: app
image: ghcr.io/yourusername/spring-app:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http
env:
- name: SPRING_PROFILES_ACTIVE
value: "k8s"
- name: DB_HOST
valueFrom:
secretKeyRef:
name: db-secret
key: host
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
volumeMounts:
- name: app-config
mountPath: /app/config
readOnly: true
volumes:
- name: app-config
configMap:
name: app-config
---
apiVersion: v1
kind: Service
metadata:
name: spring-app-service
spec:
selector:
app: spring-app
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
3. Docker Swarm 배포
yaml
# docker-stack.yml
version: '3.8'
services:
app:
image: ghcr.io/yourusername/spring-app:latest
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
placement:
constraints:
- node.role == worker
networks:
- app-net
secrets:
- db_password
- app_secret
configs:
- source: app_config
target: /app/application.yml
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
networks:
app-net:
driver: overlay
secrets:
db_password:
external: true
app_secret:
external: true
configs:
app_config:
external: true
배포 명령:
bash
# Docker Swarm 초기화
docker swarm init
# 시크릿 생성
echo "mydbpass" | docker secret create db_password -
echo "myappsecret" | docker secret create app_secret -
# 설정 생성
docker config create app_config ./application-prod.yml
# 스택 배포
docker stack deploy -c docker-stack.yml myapp
# 서비스 확인
docker service ls
docker service ps myapp_app
보안 및 최적화
1. 이미지 보안 스캔
bash
# Trivy를 사용한 취약점 스캔
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image your-image:tag
# Dockerfile 린트
docker run --rm -i hadolint/hadolint < Dockerfile
2. 이미지 크기 최적화
dockerfile
# 최적화된 Dockerfile
FROM eclipse-temurin:17-jre-alpine AS runtime
# 불필요한 파일 제거
RUN apk add --no-cache tzdata && \
cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
echo "Asia/Seoul" > /etc/timezone && \
apk del tzdata
# distroless 이미지 사용 (더 작은 크기)
FROM gcr.io/distroless/java17-debian11
COPY --from=runtime /etc/localtime /etc/localtime
COPY --from=runtime /etc/timezone /etc/timezone
WORKDIR /app
COPY --chown=nonroot:nonroot target/*.jar app.jar
USER nonroot
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
3. 보안 설정
yaml
# docker-compose.security.yml
version: '3.8'
services:
app:
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
read_only: true
tmpfs:
- /tmp
user: "1000:1000"
4. 리소스 제한
yaml
# docker-compose.resources.yml
version: '3.8'
services:
app:
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
ulimits:
nofile:
soft: 65536
hard: 65536
트러블슈팅
1. 빌드 관련 문제
문제: 빌드 캐시로 인한 문제
bash
# 캐시 없이 빌드
docker build --no-cache -t myapp .
# 모든 빌드 캐시 삭제
docker builder prune -a
문제: 의존성 다운로드 실패
dockerfile
# 타임아웃 증가
RUN mvn dependency:go-offline -B \
-Dmaven.wagon.http.timeout=120000 \
-Dmaven.wagon.http.retryHandler.count=3
2. 실행 관련 문제
문제: 메모리 부족
bash
# 컨테이너 메모리 사용량 확인
docker stats
# JVM 힙 덤프 생성
docker exec <container-id> jcmd 1 GC.heap_dump /tmp/heapdump.hprof
docker cp <container-id>:/tmp/heapdump.hprof .
문제: 포트 충돌
bash
# 사용 중인 포트 확인
sudo lsof -i :8080
netstat -tulpn | grep :8080
# 다른 포트로 실행
docker run -p 8081:8080 myapp
3. 네트워크 문제
문제: 컨테이너 간 통신 불가
bash
# 네트워크 확인
docker network ls
docker network inspect bridge
# 컨테이너 네트워크 확인
docker inspect <container> | grep -i network
# ping 테스트
docker exec app ping db
4. 디버깅 팁
bash
# 실행 중인 컨테이너에 접속
docker exec -it <container> /bin/sh
# 로그 실시간 확인
docker logs -f <container>
# 특정 시간 이후 로그만 보기
docker logs --since 10m <container>
# 컨테이너 상세 정보
docker inspect <container>
# 프로세스 확인
docker top <container>
# 파일 시스템 변경 사항 확인
docker diff <container>
마무리
Docker는 현대 소프트웨어 개발의 필수 도구입니다. 이 가이드를 통해 Spring Boot 애플리케이션을 효과적으로 컨테이너화하고 배포할 수 있기를 바랍니다.
핵심 체크리스트
- ✅ 멀티 스테이지 빌드로 이미지 크기 최적화
- ✅ 보안을 위한 non-root 사용자 실행
- ✅ 헬스체크 구성
- ✅ 환경별 설정 분리
- ✅ CI/CD 파이프라인 구축
- ✅ 모니터링 및 로깅 설정
다음 단계
- Kubernetes 오케스트레이션
- 서비스 메시 (Istio/Linkerd)
- GitOps (ArgoCD/Flux)
- 카나리 배포 전략
태그: #Docker #SpringBoot #DevOps #Containerization #Kubernetes
728x90
반응형
'유용한 컴공 테크닉' 카테고리의 다른 글
Thymeleaf 오류 완벽 해결 가이드: 빨간 화면과 작별하기 (4) | 2025.07.21 |
---|---|
Spring Boot 오류 완벽 가이드: 에러 메시지로 배우는 트러블슈팅 (1) | 2025.07.21 |
Spring Boot와 MariaDB/MySQL 연결하기: H2에서 실전 DB로 레벨업 (0) | 2025.07.21 |
Spring Boot + Thymeleaf로 웹 페이지 만들기: Bootstrap을 곁들인 CRUD 완성하기 (1) | 2025.07.20 |
Spring Boot H2 Console 사용 완벽 가이드 (0) | 2025.07.11 |