Ver3.0 ๐ณ Spring Boot์ Docker๋ก ์์ํ๋ ์ปจํ ์ด๋ ์ฌํ

๐ณ Spring Boot์ Docker๋ก ์์ํ๋ ์ปจํ ์ด๋ ์ฌํ
๊ฐ๋ฐ๋ถํฐ ๋ฐฐํฌ๊น์ง, ์ปจํ ์ด๋๋ก ํ ๋ฐฉ์ ํด๊ฒฐํ๊ธฐ
๐ ์ ์ง๊ธ Docker์ธ๊ฐ?
์น๊ตฌ์ผ, ํน์ ์ด๋ฐ ๊ฒฝํ ์์ด? ๋ก์ปฌ์์๋ ์๋ฒฝํ๊ฒ ๋์๊ฐ๋ ์ ํ๋ฆฌ์ผ์ด์
์ด ์๋ฒ์ ์ฌ๋ฆฌ๋๊น ๊ฐ์๊ธฐ ์ ๋์๊ฐ๋ ์ํฉ ๋ง์ด์ผ. ๐ฑ
"์ด? ๋ด ์ปดํจํฐ์์๋ ์ ๋๋๋ฐ..."๋ผ๋ ๋ง์ ์ด์ ๊ฐ๋ฐ์๋ค ์ฌ์ด์์ ๋ฐ์ด ๋์ด๋ฒ๋ ธ์ง. ๋ฐ๋ก ์ด๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ฑ์ฅํ ๊ฒ Docker์ผ.
Docker๋ ์ ํ๋ฆฌ์ผ์ด์
์ ์ปจํ
์ด๋๋ผ๋ ํ์คํ๋ ๋จ์๋ก ํจํค์งํด์, ์ด๋์๋ ๋์ผํ๊ฒ ์คํํ ์ ์๊ฒ ํด์ฃผ๋ ํ๋ซํผ์ด์ผ. ๋ง์น ๋ฐฐ์ก์ฉ ์ปจํ
์ด๋๊ฐ ์ด๋ค ๋ฐฐ์ ์ค์ด๋ ๋๊ฐ์ด ์ด์ก๋๋ ๊ฒ์ฒ๋ผ ๋ง์ด์ผ. ๐ข
โ ๋น ๋ฅธ ๋ฐฐํฌ: ๋ช ์ด ๋ง์ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๊ฐ๋ฅ
โ ๋ฆฌ์์ค ํจ์จ: ๊ฐ์๋จธ์ ๋ณด๋ค ํจ์ฌ ๊ฐ๋ณ๊ณ ๋น ๋ฆ
โ ํ์ฅ์ฑ: ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์ ์ต์ ํ
โ ๊ฒฉ๋ฆฌ์ฑ: ๊ฐ ์ปจํ ์ด๋๊ฐ ๋ ๋ฆฝ์ ์ผ๋ก ์คํ
โ Spring Boot์ Docker์ ํ์ ๊ถํฉ
Spring Boot๋ Java ๊ธฐ๋ฐ์ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ๋น ๋ฅด๊ฒ ๊ฐ๋ฐํ ์ ์๊ฒ ํด์ฃผ๋ ํ๋ ์์ํฌ์ผ. ๊ทธ๋ฆฌ๊ณ Docker๋ ์ด๋ ๊ฒ ๋ง๋ ์ ํ๋ฆฌ์ผ์ด์
์ ์ด๋์๋ ์คํํ ์ ์๊ฒ ํด์ฃผ์ง.
์ด ๋์ ์กฐํฉ์ด ์ ์ข๋๊ณ ? Spring Boot๋ ์ด๋ฏธ ๋ด์ฅ ํฐ์บฃ์ ๊ฐ์ง๊ณ ์์ด์ JAR ํ์ผ ํ๋๋ก ์คํ ๊ฐ๋ฅํ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง๋ค์ด. ์ด๊ฑธ Docker ์ด๋ฏธ์ง๋ก ๋ง๋ค๋ฉด ์ ๋ง ์๋ฒฝํ ๋ฐฐํฌ ๋จ์๊ฐ ๋๋ ๊ฑฐ์ผ! ๐ฏ
1๋จ๊ณ: Spring Boot๋ก ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ ๐จโ๐ป
2๋จ๊ณ: Dockerfile ์์ฑ์ผ๋ก ์ด๋ฏธ์ง ์ ์ ๐
3๋จ๊ณ: Docker ์ด๋ฏธ์ง ๋น๋ ๐จ
4๋จ๊ณ: ์ปจํ ์ด๋๋ก ์คํ ๋ฐ ๋ฐฐํฌ ๐
5๋จ๊ณ: ํ์์ Docker Hub์ ๊ณต์ ๐
๐ ๏ธ ์ค์ ! Spring Boot ์ ํ๋ฆฌ์ผ์ด์ Dockerํํ๊ธฐ
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์ค์ต์ ํด๋ณด์! ๊ฐ๋จํ Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ์ Docker ์ปจํ ์ด๋๋ก ๋ง๋๋ ์ ๊ณผ์ ์ ํจ๊ป ๋ฐ๋ผ๊ฐ๋ณด์. ๐
๐ฆ 1๋จ๊ณ: Spring Boot ํ๋ก์ ํธ ์ค๋น
๋จผ์ ๊ฐ๋จํ Spring Boot ์ ํ๋ฆฌ์ผ์ด์
์ด ํ์ํด. Spring Initializr(start.spring.io)์์ ํ๋ก์ ํธ๋ฅผ ์์ฑํ๊ฑฐ๋, ๊ธฐ์กด ํ๋ก์ ํธ๋ฅผ ์ฌ์ฉํด๋ ๋ผ.
์ฌ๊ธฐ์๋ ๊ฐ๋จํ REST API๋ฅผ ๋ง๋ค์ด๋ณผ๊ฒ:
// Application.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/")
public String hello() {
return "Hello from Docker Container! ๐ณ";
}
@GetMapping("/health")
public String health() {
return "Application is running smoothly!";
}
}
๐๏ธ 2๋จ๊ณ: Dockerfile ์์ฑํ๊ธฐ
Dockerfile์ Docker ์ด๋ฏธ์ง๋ฅผ ๋ง๋ค๊ธฐ ์ํ ๋ ์ํผ์ผ. ํ๋ก์ ํธ ๋ฃจํธ ๋๋ ํ ๋ฆฌ์ Dockerfile์ด๋ผ๋ ์ด๋ฆ์ผ๋ก ํ์ผ์ ๋ง๋ค์ด๋ณด์:
# ๊ธฐ๋ณธ ๋ฐฉ์ - ๊ฐ๋จํ์ง๋ง ์ด๋ฏธ์ง ํฌ๊ธฐ๊ฐ ํผ
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
์ Dockerfile์ ํ ์ค์ฉ ์ค๋ช
ํด๋ณผ๊ฒ:
FROM openjdk:17-jdk-slim - OpenJDK 17 ๊ธฐ๋ฐ ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํด. slim ๋ฒ์ ์ ๋ถํ์ํ ํจํค์ง๋ฅผ ์ ๊ฑฐํด์ ์ฉ๋์ด ์์.
WORKDIR /app - ์ปจํ
์ด๋ ๋ด๋ถ์์ ์์
ํ ๋๋ ํ ๋ฆฌ๋ฅผ /app์ผ๋ก ์ค์ ํด.
COPY target/*.jar app.jar - ๋น๋๋ JAR ํ์ผ์ ์ปจํ
์ด๋๋ก ๋ณต์ฌํด.
EXPOSE 8080 - ์ปจํ
์ด๋๊ฐ 8080 ํฌํธ๋ฅผ ์ฌ์ฉํ๋ค๊ณ ๋ฌธ์ํํด. (์ค์ ๋ก ํฌํธ๋ฅผ ์ฌ๋ ๊ฑด ์๋๊ณ ํํธ๋ฅผ ์ฃผ๋ ๊ฑฐ์ผ)
ENTRYPOINT - ์ปจํ
์ด๋๊ฐ ์์๋ ๋ ์คํํ ๋ช
๋ น์ด๋ฅผ ์ง์ ํด.
๐ฏ 3๋จ๊ณ: ๋ฉํฐ ์คํ ์ด์ง ๋น๋๋ก ์ต์ ํํ๊ธฐ
๋ฉํฐ ์คํ ์ด์ง ๋น๋๋ Docker์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋์ผ. ๋น๋ ํ๊ฒฝ๊ณผ ์คํ ํ๊ฒฝ์ ๋ถ๋ฆฌํด์ ์ต์ข ์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ๋ํญ ์ค์ผ ์ ์์ด. ๐
# ๋ฉํฐ ์คํ
์ด์ง ๋น๋ - ์ต์ ํ๋ ๋ฐฉ์
# Stage 1: Build
FROM maven:3.8.5-openjdk-17 AS build
WORKDIR /app
# pom.xml ๋จผ์ ๋ณต์ฌํด์ ์์กด์ฑ ์บ์ฑ
COPY pom.xml .
RUN mvn dependency:go-offline
# ์์ค ์ฝ๋ ๋ณต์ฌ ๋ฐ ๋น๋
COPY src ./src
RUN mvn clean package -DskipTests
# Stage 2: Runtime
FROM openjdk:17-jdk-slim
WORKDIR /app
# ๋น๋ ์คํ
์ด์ง์์ ์์ฑ๋ JAR๋ง ๋ณต์ฌ
COPY --from=build /app/target/*.jar app.jar
# ๋ณด์์ ์ํ non-root ์ฌ์ฉ์ ์์ฑ
RUN addgroup --system spring && adduser --system spring --ingroup spring
USER spring:spring
EXPOSE 8080
# JVM ์ต์
์ต์ ํ
ENTRYPOINT ["java", \
"-XX:+UseContainerSupport", \
"-XX:MaxRAMPercentage=75.0", \
"-Djava.security.egd=file:/dev/./urandom", \
"-jar", "app.jar"]
์ด ๋ฐฉ์์ ์ฅ์ ์ ์ ๋ฆฌํด๋ณผ๊ฒ:
โจ ์ด๋ฏธ์ง ํฌ๊ธฐ ๊ฐ์: Maven๊ณผ ์์ค ์ฝ๋๊ฐ ์ต์ข
์ด๋ฏธ์ง์ ํฌํจ๋์ง ์์
โจ ๋น๋ ์บ์ฑ: pom.xml์ ๋จผ์ ๋ณต์ฌํด์ ์์กด์ฑ ๋ค์ด๋ก๋๋ฅผ ์บ์ฑํ ์ ์์ด
โจ ๋ณด์ ๊ฐํ: non-root ์ฌ์ฉ์๋ก ์คํํด์ ๋ณด์ ๋ฆฌ์คํฌ๋ฅผ ์ค์ฌ
โจ ์ฑ๋ฅ ์ต์ ํ: JVM ์ต์
์ผ๋ก ์ปจํ
์ด๋ ํ๊ฒฝ์ ์ต์ ํ
๐จ 4๋จ๊ณ: Docker ์ด๋ฏธ์ง ๋น๋ํ๊ธฐ
Dockerfile์ด ์ค๋น๋์ผ๋ฉด ์ด์ ์ด๋ฏธ์ง๋ฅผ ๋น๋ํ ์ฐจ๋ก์ผ. ํฐ๋ฏธ๋์์ ํ๋ก์ ํธ ๋ฃจํธ ๋๋ ํ ๋ฆฌ๋ก ์ด๋ํ ๋ค์ ์๋ ๋ช ๋ น์ด๋ฅผ ์คํํด:
# ๊ธฐ๋ณธ ๋น๋
docker build -t my-spring-app:1.0 .
# ๋น๋ ์บ์ ์์ด ์๋ก ๋น๋
docker build --no-cache -t my-spring-app:1.0 .
# ํน์ Dockerfile ์ฌ์ฉ
docker build -f Dockerfile.prod -t my-spring-app:1.0 .
๋ช
๋ น์ด๋ฅผ ๋ถ์ํด๋ณด๋ฉด:
-t my-spring-app:1.0 - ์ด๋ฏธ์ง์ ํ๊ทธ๋ฅผ ๋ถ์ฌ. ์ฌ๊ธฐ์๋ ์ด๋ฆ์ด my-spring-app์ด๊ณ ๋ฒ์ ์ด 1.0์ด์ผ.
. - ํ์ฌ ๋๋ ํ ๋ฆฌ๋ฅผ ๋น๋ ์ปจํ
์คํธ๋ก ์ฌ์ฉํ๋ค๋ ์๋ฏธ์ผ.
๋น๋๊ฐ ์งํ๋๋ฉด ๊ฐ ๋จ๊ณ๋ณ๋ก ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋ผ. ์ฒ์์๋ ์๊ฐ์ด ์ข ๊ฑธ๋ฆฌ์ง๋ง, ๋ ๋ฒ์งธ๋ถํฐ๋ ์บ์ ๋๋ถ์ ํจ์ฌ ๋น ๋ฅด๊ฒ ๋น๋๋ผ! โก
target/
.git/
.idea/
*.md
.env
node_modules/
๐ 5๋จ๊ณ: ์ปจํ ์ด๋ ์คํํ๊ธฐ
์ด๋ฏธ์ง๊ฐ ๋น๋๋์ผ๋ฉด ์ด์ ์ปจํ ์ด๋๋ก ์คํํด๋ณด์:
# ๊ธฐ๋ณธ ์คํ
docker run -p 8080:8080 my-spring-app:1.0
# ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์คํ (detached mode)
docker run -d -p 8080:8080 --name my-app my-spring-app:1.0
# ํ๊ฒฝ ๋ณ์์ ํจ๊ป ์คํ
docker run -d \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e DATABASE_URL=jdbc:mysql://db:3306/mydb \
--name my-app \
my-spring-app:1.0
# ๋ณผ๋ฅจ ๋ง์ดํธ์ ํจ๊ป ์คํ
docker run -d \
-p 8080:8080 \
-v /path/to/logs:/app/logs \
--name my-app \
my-spring-app:1.0
์ฃผ์ ์ต์
๋ค์ ์ดํด๋ณด์:
-p 8080:8080 - ํธ์คํธ์ 8080 ํฌํธ๋ฅผ ์ปจํ
์ด๋์ 8080 ํฌํธ๋ก ๋งคํํด.
-d - ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์คํํด. (detached mode)
--name my-app - ์ปจํ
์ด๋์ ์ด๋ฆ์ ๋ถ์ฌ. ์ด๋ฆ์ด ์์ผ๋ฉด ๋๋ค์ผ๋ก ์์ฑ๋ผ.
-e - ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํด. Spring Boot์ ํ๋กํ์ผ์ด๋ ์ค์ ์ ์ค๋ฒ๋ผ์ด๋ํ ์ ์์ด.
-v - ๋ณผ๋ฅจ์ ๋ง์ดํธํด. ๋ก๊ทธ ํ์ผ์ด๋ ๋ฐ์ดํฐ๋ฅผ ํธ์คํธ์ ์ ์ฅํ ์ ์์ด.
๐ 6๋จ๊ณ: ์ปจํ ์ด๋ ๊ด๋ฆฌํ๊ธฐ
์ปจํ ์ด๋๊ฐ ์คํ๋๋ฉด ๋ค์ํ ๋ช ๋ น์ด๋ก ๊ด๋ฆฌํ ์ ์์ด:
# ์คํ ์ค์ธ ์ปจํ
์ด๋ ํ์ธ
docker ps
# ๋ชจ๋ ์ปจํ
์ด๋ ํ์ธ (์ค์ง๋ ๊ฒ ํฌํจ)
docker ps -a
# ์ปจํ
์ด๋ ๋ก๊ทธ ํ์ธ
docker logs my-app
# ์ค์๊ฐ ๋ก๊ทธ ํ์ธ
docker logs -f my-app
# ์ปจํ
์ด๋ ๋ด๋ถ ์ ์
docker exec -it my-app /bin/bash
# ์ปจํ
์ด๋ ์ค์ง
docker stop my-app
# ์ปจํ
์ด๋ ์์
docker start my-app
# ์ปจํ
์ด๋ ์ฌ์์
docker restart my-app
# ์ปจํ
์ด๋ ์ญ์
docker rm my-app
# ๊ฐ์ ์ญ์ (์คํ ์ค์ด์ด๋)
docker rm -f my-app
Dockerfile์ ํฌ์ค ์ฒดํฌ๋ฅผ ์ถ๊ฐํ๋ฉด ์ปจํ ์ด๋์ ์ํ๋ฅผ ์๋์ผ๋ก ๋ชจ๋ํฐ๋งํ ์ ์์ด:
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
์ด๋ ๊ฒ ํ๋ฉด Docker๊ฐ ์ฃผ๊ธฐ์ ์ผ๋ก /health ์๋ํฌ์ธํธ๋ฅผ ํธ์ถํด์ ์ ํ๋ฆฌ์ผ์ด์
์ด ์ ์์ธ์ง ํ์ธํด. ๐ฅ
๐ผ Docker Compose๋ก ๋ฉํฐ ์ปจํ ์ด๋ ๊ด๋ฆฌํ๊ธฐ
์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณดํต ์ฌ๋ฌ ์๋น์ค๋ก ๊ตฌ์ฑ๋ผ ์์ด. ์๋ฅผ ๋ค์ด Spring Boot ์ฑ, MySQL ๋ฐ์ดํฐ๋ฒ ์ด์ค, Redis ์บ์ ๋ฑ์ด ํจ๊ป ๋์ํ์ง. ์ด๋ด ๋ Docker Compose๋ฅผ ์ฌ์ฉํ๋ฉด ์ฌ๋ฌ ์ปจํ ์ด๋๋ฅผ ํ ๋ฒ์ ๊ด๋ฆฌํ ์ ์์ด! ๐ต
๐ docker-compose.yml ์์ฑํ๊ธฐ
ํ๋ก์ ํธ ๋ฃจํธ์ docker-compose.yml ํ์ผ์ ๋ง๋ค์ด๋ณด์:
version: '3.8'
services:
# Spring Boot ์ ํ๋ฆฌ์ผ์ด์
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/mydb
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_PASSWORD=secret
- SPRING_REDIS_HOST=redis
depends_on:
- db
- redis
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# MySQL ๋ฐ์ดํฐ๋ฒ ์ด์ค
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=mydb
ports:
- "3306:3306"
volumes:
- db-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
# Redis ์บ์
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
app-network:
driver: bridge
volumes:
db-data:
redis-data:
์ด ์ค์ ํ์ผ์ ์ฃผ์ ํฌ์ธํธ๋ฅผ ์ดํด๋ณด์:
services: ์คํํ ์ปจํ
์ด๋๋ค์ ์ ์ํด. ์ฌ๊ธฐ์๋ app, db, redis ์ธ ๊ฐ์ ์๋น์ค๊ฐ ์์ด.
depends_on: ์๋น์ค ๊ฐ ์์กด์ฑ์ ์ ์ํด. app์ db์ redis๊ฐ ๋จผ์ ์์๋์ด์ผ ํด.
networks: ์ปจํ
์ด๋๋ค์ด ์๋ก ํต์ ํ ์ ์๋ ๋คํธ์ํฌ๋ฅผ ๋ง๋ค์ด.
volumes: ๋ฐ์ดํฐ๋ฅผ ์๊ตฌ์ ์ผ๋ก ์ ์ฅํ ๋ณผ๋ฅจ์ ์ ์ํด. ์ปจํ
์ด๋๊ฐ ์ญ์ ๋์ด๋ ๋ฐ์ดํฐ๋ ๋จ์์์ด.
healthcheck: ๊ฐ ์๋น์ค์ ์ํ๋ฅผ ๋ชจ๋ํฐ๋งํด.
๐ฎ Docker Compose ๋ช ๋ น์ด
Docker Compose๋ฅผ ์ฌ์ฉํ๋ ์ฃผ์ ๋ช ๋ น์ด๋ค์ด์ผ:
# ๋ชจ๋ ์๋น์ค ์์ (๋ฐฑ๊ทธ๋ผ์ด๋)
docker-compose up -d
# ํน์ ์๋น์ค๋ง ์์
docker-compose up -d app
# ๋น๋์ ํจ๊ป ์์
docker-compose up -d --build
# ๋ก๊ทธ ํ์ธ
docker-compose logs
# ํน์ ์๋น์ค ๋ก๊ทธ ํ์ธ
docker-compose logs -f app
# ์คํ ์ค์ธ ์๋น์ค ํ์ธ
docker-compose ps
# ์๋น์ค ์ค์ง
docker-compose stop
# ์๋น์ค ์ค์ง ๋ฐ ์ปจํ
์ด๋ ์ญ์
docker-compose down
# ๋ณผ๋ฅจ๊น์ง ์ญ์
docker-compose down -v
# ํน์ ์๋น์ค ์ฌ์์
docker-compose restart app
# ์๋น์ค ์ค์ผ์ผ๋ง
docker-compose up -d --scale app=3
1๏ธโฃ ๊ฐํธํ ๊ด๋ฆฌ: ์ฌ๋ฌ ์ปจํ ์ด๋๋ฅผ ํ๋์ ๋ช ๋ น์ด๋ก ์ ์ด
2๏ธโฃ ํ๊ฒฝ ์ผ๊ด์ฑ: ๊ฐ๋ฐ, ํ ์คํธ, ์ด์ ํ๊ฒฝ์ ๋์ผํ๊ฒ ๊ตฌ์ฑ
3๏ธโฃ ๋คํธ์ํน ์๋ํ: ์ปจํ ์ด๋ ๊ฐ ํต์ ์ด ์๋์ผ๋ก ์ค์ ๋จ
4๏ธโฃ ๋ฒ์ ๊ด๋ฆฌ: YAML ํ์ผ๋ก ์ธํ๋ผ๋ฅผ ์ฝ๋๋ก ๊ด๋ฆฌ
5๏ธโฃ ๋น ๋ฅธ ๊ฐ๋ฐ: ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์ ๋ช ์ด ๋ง์ ๊ตฌ์ถ
๐ ๋ณด์๊ณผ ์ต์ ํ ๋ฒ ์คํธ ํ๋ํฐ์ค
Docker ์ปจํ ์ด๋๋ฅผ ํ๋ก๋์ ํ๊ฒฝ์์ ์ฌ์ฉํ๋ ค๋ฉด ๋ณด์๊ณผ ์ฑ๋ฅ ์ต์ ํ๊ฐ ์ ๋ง ์ค์ํด. ์ค์ ์์ ๊ผญ ์์์ผ ํ ํ๋ค์ ์ ๋ฆฌํด๋ดค์ด! ๐ก๏ธ
๐ ๋ณด์ ๊ฐํ ์ ๋ต
| ๋ณด์ ํญ๋ชฉ | ์ค๋ช | ๊ตฌํ ๋ฐฉ๋ฒ |
|---|---|---|
| Non-root ์ฌ์ฉ์ | root ๊ถํ์ผ๋ก ์คํํ์ง ์๊ธฐ | USER spring:spring |
| ์ต์ ๊ถํ ์์น | ํ์ํ ๊ถํ๋ง ๋ถ์ฌ | --cap-drop=ALL |
| ์ฝ๊ธฐ ์ ์ฉ ํ์ผ์์คํ | ์ปจํ ์ด๋ ํ์ผ์์คํ ์ ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก | --read-only |
| ์ํฌ๋ฆฟ ๊ด๋ฆฌ | ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ์์ ํ๊ฒ ๊ด๋ฆฌ | Docker Secrets ๋๋ ํ๊ฒฝ ๋ณ์ |
| ์ด๋ฏธ์ง ์ค์บ | ์ทจ์ฝ์ ์๋ ๊ฒ์ฌ | docker scan |
| ๋คํธ์ํฌ ๊ฒฉ๋ฆฌ | ๋ถํ์ํ ๋คํธ์ํฌ ๋ ธ์ถ ์ฐจ๋จ | ์ปค์คํ ๋คํธ์ํฌ ์ฌ์ฉ |
โก ์ฑ๋ฅ ์ต์ ํ ๊ธฐ๋ฒ
1. ๋ ์ด์ด ์บ์ฑ ์ต์ ํ
Dockerfile์ ๋ช
๋ น์ด ์์๊ฐ ์ค์ํด. ์์ฃผ ๋ณ๊ฒฝ๋์ง ์๋ ๊ฒ์ ๋จผ์ ๋ฐฐ์นํ๋ฉด ๋น๋ ์บ์๋ฅผ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ด:
# โ ๋นํจ์จ์ ์ธ ๋ฐฉ์
COPY . /app
RUN mvn clean package
# โ
ํจ์จ์ ์ธ ๋ฐฉ์
COPY pom.xml /app/
RUN mvn dependency:go-offline
COPY src /app/src
RUN mvn clean package
2. ๋ฉํฐ ์คํ
์ด์ง ๋น๋ ํ์ฉ
์์์ ๋ดค๋ฏ์ด ๋ฉํฐ ์คํ
์ด์ง ๋น๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ต์ข
์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ๋ํญ ์ค์ผ ์ ์์ด. ์ด๊ฑด ์ ๋ง ํ์์ผ!
3. JVM ๋ฉ๋ชจ๋ฆฌ ์ค์
์ปจํ
์ด๋ ํ๊ฒฝ์์๋ JVM ๋ฉ๋ชจ๋ฆฌ ์ค์ ์ด ์ค์ํด:
# Dockerfile์์ JVM ์ต์
์ค์
ENTRYPOINT ["java", \
"-XX:+UseContainerSupport", \
"-XX:MaxRAMPercentage=75.0", \
"-XX:InitialRAMPercentage=50.0", \
"-Xms512m", \
"-Xmx2g", \
"-XX:+UseG1GC", \
"-XX:MaxGCPauseMillis=200", \
"-jar", "app.jar"]
4. ๋ถํ์ํ ํ์ผ ์ ๊ฑฐ
.dockerignore ํ์ผ์ ์ ๊ทน ํ์ฉํด์ ๋น๋ ์ปจํ
์คํธ๋ฅผ ์ต์ํํ์:
# .dockerignore
target/
.git/
.gitignore
.idea/
*.iml
*.md
.env
.DS_Store
node_modules/
logs/
*.log
๐ ํ๊ฒฝ ๋ณ์๋ก ์ ๋ฌ
๐ Docker Secrets ์ฌ์ฉ
๐ ์ธ๋ถ ์ค์ ์๋ฒ (Spring Cloud Config)
๐ Kubernetes Secrets (K8s ํ๊ฒฝ)
๐ ๋ชจ๋ํฐ๋ง๊ณผ ๋ก๊น
์ปจํ ์ด๋ ํ๊ฒฝ์์๋ ๋ก๊น ๊ณผ ๋ชจ๋ํฐ๋ง์ด ๋์ฑ ์ค์ํด. Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ก๊ทธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์:
# application.yml
logging:
level:
root: INFO
com.example: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
file:
name: /app/logs/application.log
max-size: 10MB
max-history: 30
# Docker Compose์์ ๋ก๊ทธ ๋๋ผ์ด๋ฒ ์ค์
services:
app:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
๐ Docker Hub์ ์ด๋ฏธ์ง ๋ฐฐํฌ
์ด๋ฏธ์ง๋ฅผ ๋ง๋ค์์ผ๋ฉด ์ด์ ๋ค๋ฅธ ์ฌ๋๋ค๊ณผ ๊ณต์ ํ๊ฑฐ๋ ๋ค๋ฅธ ์๋ฒ์ ๋ฐฐํฌํ ์ฐจ๋ก์ผ. Docker Hub๋ GitHub์ฒ๋ผ Docker ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ๊ณ ๊ณต์ ํ ์ ์๋ ๋ ์ง์คํธ๋ฆฌ์ผ. ๐
๐ค Docker Hub์ ์ด๋ฏธ์ง ํธ์ํ๊ธฐ
# 1. Docker Hub ๋ก๊ทธ์ธ
docker login
# 2. ์ด๋ฏธ์ง์ ํ๊ทธ ๋ถ์ด๊ธฐ
docker tag my-spring-app:1.0 username/my-spring-app:1.0
docker tag my-spring-app:1.0 username/my-spring-app:latest
# 3. ์ด๋ฏธ์ง ํธ์
docker push username/my-spring-app:1.0
docker push username/my-spring-app:latest
# 4. ๋ค๋ฅธ ์๋ฒ์์ ์ด๋ฏธ์ง ๊ฐ์ ธ์ค๊ธฐ
docker pull username/my-spring-app:latest
docker run -d -p 8080:8080 username/my-spring-app:latest
๐ CI/CD ํ์ดํ๋ผ์ธ ํตํฉ
Docker๋ฅผ CI/CD ํ์ดํ๋ผ์ธ์ ํตํฉํ๋ฉด ์๋ํ๋ ๋น๋์ ๋ฐฐํฌ๊ฐ ๊ฐ๋ฅํด. GitHub Actions๋ฅผ ์๋ก ๋ค์ด๋ณผ๊ฒ:
# .github/workflows/docker-build.yml
name: Docker Build and Push
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Build with Maven
run: mvn clean package -DskipTests
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
username/my-spring-app:latest
username/my-spring-app:${{ github.sha }}
- name: Deploy to production
run: |
ssh user@server "docker pull username/my-spring-app:latest"
ssh user@server "docker-compose up -d"
์ด๋ ๊ฒ ์ค์ ํ๋ฉด main ๋ธ๋์น์ ์ฝ๋๋ฅผ ํธ์ํ ๋๋ง๋ค ์๋์ผ๋ก Docker ์ด๋ฏธ์ง๊ฐ ๋น๋๋๊ณ ๋ฐฐํฌ๋ผ! ๐
๐ ํธ๋ฌ๋ธ์ํ ๊ณผ ๋๋ฒ๊น
Docker๋ฅผ ์ฌ์ฉํ๋ค ๋ณด๋ฉด ์ฌ๋ฌ ๊ฐ์ง ๋ฌธ์ ์ ๋ถ๋ชํ ์ ์์ด. ์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ ๋ฆฌํด๋ดค์ด! ๐ง
โ ์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค
์ฆ์: docker ps๋ก ํ์ธํ๋ฉด ์ปจํ ์ด๋๊ฐ ๋ณด์ด์ง ์์
ํด๊ฒฐ:
# ์ข
๋ฃ๋ ์ปจํ
์ด๋ ํ์ธ
docker ps -a
# ๋ก๊ทธ ํ์ธ
docker logs container-name
# ์ผ๋ฐ์ ์ธ ์์ธ
# - ์ ํ๋ฆฌ์ผ์ด์
์์ ์คํจ
# - ํฌํธ ์ถฉ๋
# - ํ๊ฒฝ ๋ณ์ ๋๋ฝ
# - ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์คํจ
์ฆ์: "port is already allocated" ์๋ฌ
ํด๊ฒฐ:
# ํฌํธ ์ฌ์ฉ ์ค์ธ ํ๋ก์ธ์ค ํ์ธ (Linux/Mac)
lsof -i :8080
# ํฌํธ ์ฌ์ฉ ์ค์ธ ํ๋ก์ธ์ค ํ์ธ (Windows)
netstat -ano | findstr :8080
# ๋ค๋ฅธ ํฌํธ๋ก ๋งคํ
docker run -p 8081:8080 my-spring-app:1.0
์ฆ์: "Connection refused" ๋๋ "Unknown host"
ํด๊ฒฐ:
# Docker Compose ์ฌ์ฉ ์ ์๋น์ค ์ด๋ฆ์ผ๋ก ์ฐ๊ฒฐ
spring.datasource.url=jdbc:mysql://db:3306/mydb
# ๋คํธ์ํฌ ํ์ธ
docker network ls
docker network inspect app-network
# ์ปจํ
์ด๋ ๊ฐ ํต์ ํ
์คํธ
docker exec -it app ping db
ํด๊ฒฐ:
# BuildKit ํ์ฑํ (๋ ๋น ๋ฅธ ๋น๋)
export DOCKER_BUILDKIT=1
docker build -t my-spring-app:1.0 .
# ๋น๋ ์บ์ ํ์ฉ
# - .dockerignore ํ์ผ ์์ฑ
# - ๋ ์ด์ด ์์ ์ต์ ํ
# - ๋ฉํฐ ์คํ
์ด์ง ๋น๋ ์ฌ์ฉ
๐ ๋๋ฒ๊น ํ
์ปจํ ์ด๋ ๋ด๋ถ์์ ๋ฌธ์ ๋ฅผ ๋๋ฒ๊น ํ๋ ์ ์ฉํ ๋ช ๋ น์ด๋ค์ด์ผ:
# ์ปจํ
์ด๋ ๋ด๋ถ ์ ์
docker exec -it container-name /bin/bash
# ์ปจํ
์ด๋ ๋ฆฌ์์ค ์ฌ์ฉ๋ ํ์ธ
docker stats
# ์ปจํ
์ด๋ ์์ธ ์ ๋ณด ํ์ธ
docker inspect container-name
# ์ปจํ
์ด๋ ํ๋ก์ธ์ค ํ์ธ
docker top container-name
# ์ปจํ
์ด๋ ํ์ผ ์์คํ
๋ณ๊ฒฝ ์ฌํญ ํ์ธ
docker diff container-name
# ์ค์๊ฐ ๋ก๊ทธ ์คํธ๋ฆฌ๋ฐ
docker logs -f --tail 100 container-name
# ํน์ ์๊ฐ๋ ๋ก๊ทธ ํ์ธ
docker logs --since 30m container-name
๊ฐ๋ฐ ์ค์๋ ๋๋ฒ๊น ํฌํธ๋ฅผ ์ด์ด์ IDE์์ ์๊ฒฉ ๋๋ฒ๊น ์ ํ ์ ์์ด:
# Dockerfile์ ๋๋ฒ๊ทธ ์ต์
์ถ๊ฐ
ENTRYPOINT ["java", \
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", \
"-jar", "app.jar"]
# ๋๋ฒ๊ทธ ํฌํธ์ ํจ๊ป ์คํ
docker run -p 8080:8080 -p 5005:5005 my-spring-app:1.0
IntelliJ๋ Eclipse์์ Remote Debug ์ค์ ์ผ๋ก localhost:5005์ ์ฐ๊ฒฐํ๋ฉด ๋ผ!
๐ ์ค์ ํ๋ก์ ํธ ์์
์ด์ ๋ฐฐ์ด ๋ด์ฉ์ ์ข ํฉํด์ ์ค์ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์! REST API, ๋ฐ์ดํฐ๋ฒ ์ด์ค, ์บ์๋ฅผ ๋ชจ๋ ํฌํจํ ์์ ํ ์์ ์ผ. ๐ช
๐ ํ๋ก์ ํธ ๊ตฌ์กฐ
my-spring-app/
โโโ src/
โ โโโ main/
โ โ โโโ java/
โ โ โ โโโ com/example/demo/
โ โ โ โโโ Application.java
โ โ โ โโโ controller/
โ โ โ โ โโโ UserController.java
โ โ โ โโโ service/
โ โ โ โ โโโ UserService.java
โ โ โ โโโ repository/
โ โ โ โ โโโ UserRepository.java
โ โ โ โโโ model/
โ โ โ โโโ User.java
โ โ โโโ resources/
โ โ โโโ application.yml
โ โ โโโ application-prod.yml
โโโ Dockerfile
โโโ docker-compose.yml
โโโ .dockerignore
โโโ pom.xml
โโโ README.md
๐ป ํต์ฌ ์ฝ๋
User ์ํฐํฐ:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
@CreatedDate
private LocalDateTime createdAt;
// getters, setters, constructors
}
UserController:
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
@GetMapping("/{id}")
@Cacheable(value = "users", key = "#id")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
@PostMapping
public User createUser(@RequestBody User user) {
return userService.save(user);
}
@DeleteMapping("/{id}")
@CacheEvict(value = "users", key = "#id")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}
}
application.yml:
spring:
application:
name: my-spring-app
datasource:
url: jdbc:mysql://${DB_HOST:localhost}:3306/${DB_NAME:mydb}
username: ${DB_USER:root}
password: ${DB_PASSWORD:secret}
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
redis:
host: ${REDIS_HOST:localhost}
port: 6379
cache:
type: redis
redis:
time-to-live: 600000
server:
port: 8080
logging:
level:
root: INFO
com.example: DEBUG
๐ณ ์์ ํ docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: spring-app
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_HOST=db
- DB_NAME=mydb
- DB_USER=root
- DB_PASSWORD=secret123
- REDIS_HOST=redis
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: mysql:8.0
container_name: mysql-db
environment:
- MYSQL_ROOT_PASSWORD=secret123
- MYSQL_DATABASE=mydb
ports:
- "3306:3306"
volumes:
- db-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-psecret123"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
redis:
image: redis:7-alpine
container_name: redis-cache
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- app-network
restart: unless-stopped
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
nginx:
image: nginx:alpine
container_name: nginx-proxy
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- app
networks:
- app-network
restart: unless-stopped
networks:
app-network:
driver: bridge
volumes:
db-data:
redis-data:
์ด ์ค์ ์ผ๋ก docker-compose up -d ๋ช ๋ น์ด ํ๋๋ฉด ์ ์ฒด ์คํ์ด ์คํ๋ผ! ์ ๋ง ํธ๋ฆฌํ์ง? ๐
โ๏ธ ํด๋ผ์ฐ๋ ๋ฐฐํฌ ์ ๋ต
๋ก์ปฌ์์ ์๋ฒฝํ๊ฒ ๋์ํ๋ ์ปจํ ์ด๋๋ฅผ ์ด์ ํด๋ผ์ฐ๋์ ๋ฐฐํฌํด๋ณด์! AWS, Azure, GCP ๋ชจ๋ Docker๋ฅผ ์ง์ํด. ๐ฅ๏ธ
๐ฏ AWS ECS (Elastic Container Service)
AWS ECS๋ Docker ์ปจํ ์ด๋๋ฅผ ์ฝ๊ฒ ์คํํ๊ณ ๊ด๋ฆฌํ ์ ์๋ ์๋น์ค์ผ:
# 1. ECR์ ์ด๋ฏธ์ง ํธ์
aws ecr get-login-password --region ap-northeast-2 | \
docker login --username AWS --password-stdin \
123456789.dkr.ecr.ap-northeast-2.amazonaws.com
docker tag my-spring-app:1.0 \
123456789.dkr.ecr.ap-northeast-2.amazonaws.com/my-spring-app:1.0
docker push 123456789.dkr.ecr.ap-northeast-2.amazonaws.com/my-spring-app:1.0
# 2. ECS Task Definition ์์ฑ (JSON)
{
"family": "my-spring-app",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"containerDefinitions": [
{
"name": "app",
"image": "123456789.dkr.ecr.ap-northeast-2.amazonaws.com/my-spring-app:1.0",
"portMappings": [
{
"containerPort": 8080,
"protocol": "tcp"
}
],
"environment": [
{
"name": "SPRING_PROFILES_ACTIVE",
"value": "prod"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/my-spring-app",
"awslogs-region": "ap-northeast-2",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
โธ๏ธ Kubernetes ๋ฐฐํฌ
Kubernetes๋ ์ปจํ ์ด๋ ์ค์ผ์คํธ๋ ์ด์ ์ ํ์ค์ด์ผ. ๋ณต์กํ์ง๋ง ๊ฐ๋ ฅํด:
# 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: username/my-spring-app:1.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: db.host
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: spring-app-service
spec:
type: LoadBalancer
selector:
app: spring-app
ports:
- protocol: TCP
port: 80
targetPort: 8080
๐ ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง๊ณผ ๊ด์ฐฐ์ฑ
ํ๋ก๋์ ํ๊ฒฝ์์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ๋ฅผ ์ค์๊ฐ์ผ๋ก ๋ชจ๋ํฐ๋งํ๋ ๊ฒ ์ ๋ง ์ค์ํด. Spring Boot Actuator์ Prometheus, Grafana๋ฅผ ์กฐํฉํ๋ฉด ๊ฐ๋ ฅํ ๋ชจ๋ํฐ๋ง ์์คํ ์ ๊ตฌ์ถํ ์ ์์ด! ๐
๐ Spring Boot Actuator ์ค์
// pom.xml์ ์์กด์ฑ ์ถ๊ฐ
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
๐ Prometheus + Grafana ์ค์
# docker-compose.yml์ ์ถ๊ฐ
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
networks:
- app-network
restart: unless-stopped
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana-data:/var/lib/grafana
depends_on:
- prometheus
networks:
- app-network
restart: unless-stopped
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'spring-boot-app'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['app:8080']
์ด์ Grafana(http://localhost:3000)์ ์ ์ํด์ Prometheus๋ฅผ ๋ฐ์ดํฐ ์์ค๋ก ์ถ๊ฐํ๊ณ , ๋์๋ณด๋๋ฅผ ๋ง๋ค๋ฉด ์ค์๊ฐ์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์ ๋ฉํธ๋ฆญ์ ๋ณผ ์ ์์ด! ๐
๐ ํ์ต ๋ก๋๋งต๊ณผ ๋ค์ ๋จ๊ณ
์ฌ๊ธฐ๊น์ง ๋ฐ๋ผ์๋ค๋ฉด ์ ๋ง ๋๋จํด! ๐ ์ด์ Spring Boot์ Docker์ ๊ธฐ๋ณธ์ ๋ง์คํฐํ์ด. ๋ค์ ๋จ๊ณ๋ก ๋์๊ฐ๊ธฐ ์ํ ๋ก๋๋งต์ ์ ๋ฆฌํด๋ดค์ด:
โ Docker ๊ธฐ๋ณธ ๊ฐ๋ ์ดํด
โ Dockerfile ์์ฑ
โ Docker Compose ํ์ฉ
โ Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์ด๋ํ
โ ๊ธฐ๋ณธ์ ์ธ ๋คํธ์ํน๊ณผ ๋ณผ๋ฅจ ๊ด๋ฆฌ
๐ฏ ๋ฉํฐ ์คํ ์ด์ง ๋น๋ ์ต์ ํ
๐ฏ Docker ๋คํธ์ํฌ ์ฌํ
๐ฏ ๋ณด์ ๊ฐํ (Secrets, ์ค์บ)
๐ฏ CI/CD ํ์ดํ๋ผ์ธ ๊ตฌ์ถ
๐ฏ ๋ชจ๋ํฐ๋ง๊ณผ ๋ก๊น ์์คํ ๊ตฌ์ถ
๐ฏ Docker Swarm ํ์ต
๐ Kubernetes ๋ง์คํฐ
๐ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ ์ค๊ณ
๐ ์๋น์ค ๋ฉ์ (Istio, Linkerd)
๐ GitOps (ArgoCD, Flux)
๐ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ํจํด
๐ ์ฑ๋ฅ ํ๋๊ณผ ์ต์ ํ
๐ ์ถ์ฒ ํ์ต ์๋ฃ
๊ณต์ ๋ฌธ์:
โข Docker ๊ณต์ ๋ฌธ์: https://docs.docker.com
โข Spring Boot ๊ณต์ ๋ฌธ์: https://spring.io/projects/spring-boot
โข Kubernetes ๊ณต์ ๋ฌธ์: https://kubernetes.io/docs
์ค์ต ํ๋ซํผ:
โข Play with Docker: https://labs.play-with-docker.com
โข Katacoda: ๋ฌด๋ฃ ์ธํฐ๋ํฐ๋ธ ํ์ต
โข Docker Hub: ๋ค์ํ ์ด๋ฏธ์ง ํ์
์ปค๋ฎค๋ํฐ:
โข Docker Community Forums
โข Stack Overflow
โข Reddit r/docker
โข ํ๊ตญ Docker ์ฌ์ฉ์ ๋ชจ์
๐ฌ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์, ์ฌ๊ธฐ๊น์ง Spring Boot์ Docker๋ฅผ ํ์ฉํ ์ปจํ
์ด๋ํ์ ๋ํด ์์๋ดค์ด! ์ฒ์์๋ ๋ณต์กํด ๋ณด์ผ ์ ์์ง๋ง, ํ๋์ฉ ๋ฐ๋ผํ๋ค ๋ณด๋ฉด ์ด๋์ Docker ์ ๋ฌธ๊ฐ๊ฐ ๋์ด ์์ ๊ฑฐ์ผ. ๐
Docker๋ ๋จ์ํ ๋ฐฐํฌ ๋๊ตฌ๊ฐ ์๋๋ผ, ํ๋์ ์ธ ์ํํธ์จ์ด ๊ฐ๋ฐ์ ํ์ ์์์ผ. ๊ฐ๋ฐ ํ๊ฒฝ์ ์ผ๊ด์ฑ, ๋น ๋ฅธ ๋ฐฐํฌ, ํจ์จ์ ์ธ ๋ฆฌ์์ค ๊ด๋ฆฌ ๋ฑ Docker๊ฐ ์ ๊ณตํ๋ ์ด์ ์ ์ ๋ง ๋ง์.
ํนํ Spring Boot์์ ์กฐํฉ์ Java ๊ฐ๋ฐ์์๊ฒ ์ต๊ณ ์ ์ ํ์ด์ผ. Spring Boot์ ๊ฐํธํจ๊ณผ Docker์ ์ด์์ฑ์ด ๋ง๋๋ฉด ์ ๋ง ๊ฐ๋ ฅํ ์๋์ง๋ฅผ ๋ฐํํ๊ฑฐ๋ ! ๐
๊ธฐ์ตํด์ผ ํ ํต์ฌ ํฌ์ธํธ๋ค:
๐ฏ ๋ฉํฐ ์คํ
์ด์ง ๋น๋๋ก ์ด๋ฏธ์ง ํฌ๊ธฐ ์ต์ ํ
๐ฏ Docker Compose๋ก ๋ฉํฐ ์ปจํ
์ด๋ ๊ด๋ฆฌ
๐ฏ ๋ณด์์ ํญ์ ์ต์ฐ์ ์ผ๋ก ๊ณ ๋ ค
๐ฏ ๋ชจ๋ํฐ๋ง๊ณผ ๋ก๊น
์์คํ
๊ตฌ์ถ
๐ฏ CI/CD ํ์ดํ๋ผ์ธ ์๋ํ
์ด์ ๋น์ ๋ Docker ์ปจํ
์ด๋์ ์ธ๊ณ๋ก ๋ ๋ ์ค๋น๊ฐ ๋์ด! ์ค์ตํ๋ฉด์ ๋งํ๋ ๋ถ๋ถ์ด ์๋ค๋ฉด ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ๊ฑฐ๋, ์ปค๋ฎค๋ํฐ์ ์ง๋ฌธํ๊ฑฐ๋, ์ ๋ฌธ๊ฐ์ ๋์์ ๋ฐ์๋ณด๋ ๊ฒ๋ ์ข์.
๋ง์ง๋ง์ผ๋ก, ํ์ต์ ๋์ด ์์ด. ๊ณ์ํด์ ์๋ก์ด ๊ธฐ์ ์ ๋ฐฐ์ฐ๊ณ , ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด๋ฉด์ ์ฑ์ฅํด๋๊ฐ์! ๐ช
Happy Dockerizing! ๐ณโจ
๐ ์ถํํฉ๋๋ค!
Spring Boot์ Docker ์ปจํ
์ด๋ํ ์ฌ์ ์ ์๋ฃํ์ต๋๋ค.
์ด์ ๋น์ ์ ํ๋์ ์ธ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ฐ๋ฐํ ์ค๋น๊ฐ ๋์์ต๋๋ค!
๊ณ์ํด์ ์ค์ตํ๊ณ , ์คํํ๊ณ , ์ฑ์ฅํ์ธ์! ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.