[ 15주차 - 1125 ]
금일 커리큘럼
├ 09:00 ~ 12:00 Devops (Docker 이미지와 레이어, Dockerfile)
└ 13:00 ~ 18:00 Devops (Docker 이미지 빌드/관리, Docker 레지스트리, Docker Compose)
1. Docker 이미지와 레이어
1.1 Docker 이미지 레이어
- Docker 이미지는 여러 레이어(layer)로 구성된 파일 시스템
- 각 레이어는 변경 사항을 포함하며, 불변(immutable) 특성을 가짐
- 레이어는 캐시되어, 동일한 레이어를 사용하는 이미지 간에 저장 공간을 절약
- 이미지 빌드 시, 변경된 레이어만 새로 생성되어 빌드 속도가 향상
- 레이어 = 읽기 전용 파일 시스템 스냅샷
1.2 Docker 레이어 구조
예시 Dockerfile
# dockerfile 예시
FROM ubuntu:20.04 # 베이스 이미지 (layer 1)
RUN apt-get update # apt 패키지 목록 업데이트 (layer 2)
RUN apt-get install -y python3 # python3 설치 (layer 3)
COPY . /app # 애플리케이션 코드 복사 (layer 4)
CMD ["python3", "/app/app.py"] # 실행 명령어 (layer 5)
레이어 구조 설명
- Layer 1 :
ubuntu:20.04베이스 이미지 - Layer 2 :
apt-get update명령어 실행 결과 - Layer 3 :
apt-get install -y python3명령어 실행 결과 - Layer 4 : 애플리케이션 코드 복사 결과
- Layer 5 : 컨테이너 실행 시 사용할 명령어 설정
1.3 Docker 이미지 관리 명령어
이미지 빌드
# docker build -t <이미지_이름>:<태그> <Dockerfile_경로>
# 1. 해당 경로에서
docker build -t my_python_app:latest .
# 1. 특정 경로에서
docker build -t my_python_app:latest -f /path/to/Dockerfile .
이미지 목록 조회
docker images
# 예시 출력
REPOSITORY TAG IMAGE ID CREATED SIZE
my_python_app latest abcdef123456 10 minutes ago 500MB
ubuntu 20.04 123456abcdef 2 weeks ago 72MB
nginx latest fedcba654321 3 weeks ago 133MB
# ... (생략)
특정 이미지 레이어 확인
# docker history <이미지_이름>:<태그>
docker history my_python_app:latest
# 예시 출력
IMAGE CREATED CREATED BY SIZE
abcdef123456 10 minutes ago /bin/sh -c CMD ["python3", "/app/app.py"] 0B
123456abcdef 15 minutes ago /bin/sh -c COPY . /app 5MB
fedcba654321 20 minutes ago /bin/sh -c apt-get install -y python3 100MB
654321fedcba 25 minutes ago /bin/sh -c apt-get update 50MB
이미지 삭제
# docker rmi <이미지_이름>:<태그>
docker rmi my_python_app:latest
1.4 이미지 변경 추적과 캐싱
Docker 빌드 프로세스
- Docker는 Dockerfile의 각 명령어를 실행할 때마다 새로운 레이어를 생성
- 이전에 빌드된 레이어가 캐시에 존재하면, 해당 레이어를 재사용하여 빌드 속도를 향상시킴
- 예를 들어,
apt-get update명령어가 이미 캐시에 있다면, Docker는 이를 다시 실행하지 않고 캐시된 레이어를 사용 - 이는 개발 및 테스트 과정에서 매우 유용하며, 반복적인 빌드 시간을 크게 단축시킴
캐시 무효화
- Dockerfile의 내용이 변경되면, 해당 변경 이후의 모든 레이어는 다시 빌드됨
- 예를 들어,
COPY . /app명령어 이후에 소스 코드가 변경되면, 이 명령어와 그 이후의 모든 명령어는 다시 실행됨 - 이를 통해 개발자는 효율적으로 이미지를 관리하고, 빌드 시간을 최적화할 수 있음
Docker 캐시 무효화 예제
- ENV 변경으로 캐시 무효화 케이스 (dockerfile)
# 첫번째 빌드용 Dockerfile
FROM node:18 # 레이어 1
ENV APP_MODE=dev # 레이어 2
RUN echo "Install steps..." # 레이어 3
COPY . /app # 레이어 4
# 두번째 빌드용 Dockerfile (ENV 변경)
FROM node:18 # 레이어 1
ENV APP_MODE=prod # 레이어 2 (캐시 무효화 발생 부분)
RUN echo "Install steps..." # 레이어 3
COPY . /app # 레이어 4
- 캐시 사용/무효화 빌드 예제 (명령어)
# 첫번째 빌드 (모든 레이어 새로 빌드)
docker build -t my_node_app:dev -f Dockerfile.dev .
# [1/4] FROM node:18 # new
# [2/4] ENV APP_MODE=dev # new
# [3/4] RUN echo "Install steps..." # new
# [4/4] COPY . /app # new
# 두번째 빌드 (ENV 변경으로 인해 레이어 2부터 다시 빌드)
docker build -t my_node_app:prod -f Dockerfile.prod .
# [1/4] FROM node:18 # used cache
# [2/4] ENV APP_MODE=prod # 캐시 무효화, 다시 빌드
# [3/4] RUN echo "Install steps..." # new
# [4/4] COPY . /app # new
2. Dockerfile 작성
- Dockerfile은 Docker 이미지를 자동으로 빌드하기 위한 설정 파일
docker build명령어를 통해 Dockerfile을 읽고 이미지를 생성- 이미지 빌드 과정을 명시적으로 정의하여, 일관된 환경을 제공
2.1 Dockerfile 기본 구조
- FROM : 베이스 이미지 지정
# FROM <이미지_이름>:<태그>
FROM ubuntu:20.04
# 베이스 이미지로 우분투 20.04 사용
- RUN : 이미지 빌드 시 실행할 명령어
# RUN <명령어>
RUN apt-get update && apt-get install -y python3
# apt 패키지 목록 업데이트 및 python3 설치
- COPY, ADD : 파일/디렉토리 복사
# COPY <호스트_경로> <컨테이너_경로>
COPY . /app
# 호스트의 현재 디렉토리 내용을 컨테이너의 /app 디렉토리에 복사
# ADD <호스트_경로> <컨테이너_경로>
ADD config.tar.gz /config
# 호스트의 config.tar.gz 파일을 컨테이너의 /config 디렉토리에 압축 해제하며 복사
- WORKDIR : 작업 디렉토리 설정
# WORKDIR <디렉토리_경로>
WORKDIR /app
# 이후 명령어들은 /app 디렉토리에서 실행됨
- CMD : 컨테이너 실행 시 기본 명령어 지정
# CMD ["명령어", "인자1", "인자2", ...]
CMD ["python3", "app.py"]
# 컨테이너가 시작될 때 python3 app.py 명령어 실행
- ENV : 환경 변수 설정
# ENV <변수_이름> <값>
ENV APP_ENV=prod
# APP_ENV 환경 변수를 prod로 설정
ENV APP_HOME=/app
ENV LANG=Kor.UTF-8 TZ=Asia/Seoul
WORKDIR $APP_HOME
- EXPOSE : 컨테이너가 수신할 포트 지정
# EXPOSE <포트_번호>
EXPOSE 8080
# 컨테이너가 8080 포트를 수신하도록 지정
EXPOSE 8080 8888
EXPOSE 80/tcp
EXPOSE 443/tcp
2.2 Dockerfile 예시
Python Flask 애플리케이션
Python Flask - dockerfile
FROM python:3.9-slim
# 작업 디렉토리 설정
WORKDIR /app
# 의존성 파일 먼저 복사 (캐싱 활용)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 소스 코드 복사
COPY . /app
# 환경 변수 설정
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
# 포트 명시
EXPOSE 5000
# 실행 명령
CMD ["flask", "run"]
Spring Boot 애플리케이션
Spring Boot - dockerfile (멀티스테이지)
# 빌드 스테이지
FROM gradle:7.6-jdk17 AS build
WORKDIR /app
COPY build.gradle settings.gradle ./
COPY src ./src
RUN gradle build --no-daemon
# 런타임 스테이지
FROM openjdk:17-jdk-alpine
WORKDIR /app
# 빌드 결과만 복사
COPY --from=build /app/build/libs/*.jar app.jar
# 환경 변수
ENV SPRING_PROFILES_ACTIVE=prod
ENV SERVER_PORT=8080
# 포트
EXPOSE 8080
# 실행
ENTRYPOINT ["java", "-jar", "app.jar"]
React.js 애플리케이션
React.js - dockerfile (멀티스테이지)
# 빌드 스테이지
FROM node:18 AS build
WORKDIR /app
# package.json 먼저 복사 (캐싱)
COPY package*.json ./
RUN npm ci --only=production
# 소스 코드 복사 및 빌드
COPY . .
RUN npm run build
# 프로덕션 스테이지
FROM nginx:alpine
# 빌드 결과를 nginx로 복사
COPY --from=build /app/build /usr/share/nginx/html
# nginx 설정 복사 (선택사항)
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
베스트 프랙티스 - 레이어 최소화
dockerfile - 레이어 최소화
# ❌ 나쁜 예 - 레이어 3개
RUN apt-get update
RUN apt-get install -y python3
RUN apt-get clean
# ✅ 좋은 예 - 레이어 1개
RUN apt-get update && \
apt-get install -y python3 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
베스트 프랙티스 - 빌드 순서 최적화
dockerfile - 빌드 순서 최적화
# ✅ 좋은 예 - 변경 빈도가 낮은 것부터
FROM python:3.9-slim
# 1. 의존성 파일 (변경 빈도 낮음)
COPY requirements.txt .
RUN pip install -r requirements.txt
# 1. 소스 코드 (변경 빈도 높음)
COPY . /app
# ❌ 나쁜 예 - 소스 코드를 먼저 복사
# COPY . /app ← 소스 변경 시 아래 pip install도 재실행
# RUN pip install -r requirements.txt
베스트 프랙티스 - 멀티 스테이지 빌드
dockerfile - 멀티 스테이지 빌드
# 빌드 도구와 런타임 분리 → 이미지 크기 최소화
FROM maven:3.8-openjdk-17 AS build
WORKDIR /app
COPY . .
RUN mvn clean package
# 런타임 이미지 (빌드 도구 불포함)
FROM openjdk:17-jdk-alpine
COPY --from=build /app/target/*.jar app.jar
CMD ["java", "-jar", "app.jar"]
.dockerignore 활용
.dockerignore
# .dockerignore 파일
.git
.gitignore
node_modules
npm-debug.log
Dockerfile
.dockerignore
.env
.vscode
__pycache__
*.pyc
target/
build/
3. 이미지 빌드/관리
3.1 이미지 빌드
# docker build -t <이미지명>:<태그> <빌드컨텍스트경로>
# 기본 빌드 (현재 디렉토리)
docker build -t myapp:1.0 .
# 다른 Dockerfile 지정
docker build -f Dockerfile.prod -t myapp:prod .
# 빌드 인자 전달
docker build --build-arg ENV=production -t myapp:prod .
# 캐시 사용 안 함 (완전 재빌드)
docker build --no-cache -t myapp:latest .
# 특정 플랫폼용 빌드
docker build --platform linux/amd64 -t myapp:amd64 .
# 빌드 과정 상세 출력
docker build --progress=plain -t myapp:latest .
3.2 이미지 태그
# 버전 태그 추가
docker tag myapp:latest myapp:v1.0
docker tag myapp:latest myapp:stable
# Docker Hub 업로드용 태그
docker tag myapp:latest username/myapp:latest
# 사설 레지스트리용 태그
docker tag myapp:latest registry.company.com/myapp:v1.0
3.3 이미지 목록/삭제
# 이미지 목록 조회
docker images
# 이미지 삭제
docker rmi myapp:latest
# 여러 이미지 삭제
docker rmi ubuntu:20.04 python:3.9 nginx:alpine
# 이미지 ID로 삭제
docker rmi 1234abcd
# 강제 삭제 (-f)
docker rmi -f myapp:latest
4. docker 레지스트리
- Docker 레지스트리는 Docker 이미지를 저장하고 배포하는 중앙 저장소
- 퍼블릭 레지스트리(예: Docker Hub)와 프라이빗 레지스트리(사내 전용)로 구분
- 이미지를 푸시(push)하고 풀(pull)하여 컨테이너 배포에 활용
4.1 레지스트리 종류
- Docker Hub : 가장 널리 사용되는 퍼블릭 레지스트리
- Amazon ECR : AWS에서 제공하는 프라이빗 레지스트리
- Google Container Registry : GCP에서 제공하는 레지스트리
- Azure Container Registry : Azure에서 제공하는 레지스트리
4.2 docker hub 사용법
로그인
# Docker Hub에 로그인
docker login # 사용자명과 비밀번호 입력
# 특정 레지스트리에 로그인
docker login myregistry.com
# 로그아웃
docker logout
이미지 푸시/풀
# 이미지 푸시
# docker push <레지스트리_이름>/<이미지_이름>:<태그>
docker push <도커허브계정>/myapp:latest
# 이미지 풀
# docker pull <레지스트리_이름>/<이미지_이름>:<태그>
docker pull <도커허브계정>/myapp:latest
4.3 AWS ECR 사용법
ECR 로그인
# AWS CLI로 ECR 로그인
aws ecr get-login-password --region <region> | \
docker login --username AWS \
--password-stdin <aws_account_id>.dkr.ecr.<region>.amazonaws.com
# --username AWS : 고정값
# --password-stdin : 보안상 권장되는 로그인 방식
# <aws_account_id> : AWS 계정 ID
# <region> : ECR 리전 (예: us-west-2)
# 로그아웃
docker logout <aws_account_id>.dkr.ecr.<region>.amazonaws.com
이미지 푸시/풀
# 이미지 태그 (ECR용)
docker tag myapp:latest <aws_account_id>.dkr.ecr.<region>.amazonaws.com/myapp:latest
# /<이미지>:<태그> 형식으로 태그 지정
# 이미지 푸시
docker push <aws_account_id>.dkr.ecr.<region>.amazonaws.com/myapp:latest
# 이미지 풀
docker pull <aws_account_id>.dkr.ecr.<region>.amazonaws.com/myapp:latest
5. Docker Compose 기본
- 여러 개의 Docker 컨테이너를 하나의 설정 파일로 관리하는 도구
docker-compose가 필요한 이유는 복잡한 멀티 컨테이너 애플리케이션을 쉽게 정의하고 실행하기 위함docker-compose.yml파일에 서비스, 네트워크, 볼륨 등을 정의하여 일괄적으로 관리 가능
5.1 docker-compose 구조
services: 실행할 컨테이너들 정의networks: 네트워크 설정 (선택 사항)volumes: 데이터 볼륨 설정 (선택 사항)
docker-compose.yml 기본 구조
services: # 실행할 컨테이너들
service1:
# 서비스 설정
service2:
# 서비스 설정
networks: # 네트워크 설정 (선택)
network1:
volumes: # 볼륨 설정 (선택)
volume1:
compose - nginx 웹서버 예시
services:
web: # nginx 웹서버 서비스
image: nginx:alpine # 사용할 이미지
ports:
- "8080:80"
5.2 compose 핵심 설정
서비스 정의 (services)
services:
db:
image: mysql:8.0 # Docker Hub의 이미지
포트 매핑 (ports)
services:
web:
image: nginx:alpine
ports:
- "8080:80" # 호스트 8080 -> 컨테이너 80
- "8443:443" # 호스트 8443 -> 컨테이너 443
환경 변수 (environment)
# 방법 1 : 키-값 쌍
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password # 루트 비밀번호
MYSQL_DATABASE: testdb # 데이터베이스 이름
MYSQL_USER: testuser # 사용자 이름
MYSQL_PASSWORD: password # 사용자 비밀번호
# 방법 2 : 리스트 형식
services:
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=password
- MYSQL_DATABASE=testdb
- MYSQL_USER=testuser
- MYSQL_PASSWORD=password
빌드설정 (build)
- 이미지 대신 Dockerfile로 빌드
# 방법 1 : 간단한 빌드 설정
services:
app:
build: . # 현재 디렉토리의 Dockerfile 사용
ports:
- "8080:8080"
# 방법 2 : 상세 빌드 설정
services:
app:
build:
context: ./backend # Dockerfile이 있는 디렉토리
dockerfile: Dockerfile # Dockerfile 이름
ports:
- "8080:8080"
볼륨 마운트 (volumes)
# 방법 1 : 호스트 디렉토리 마운트
services:
db:
image: mysql:8.0
volumes:
# 호스트의 db_data 볼륨을 컨테이너의 /var/lib/mysql에 마운트
- db_data:/var/lib/mysql
# 방법 2 : 호스트 경로 마운트
services:
web:
image: nginx:alpine
volumes:
# 호스트의 ./html 디렉토리를 컨테이너의 웹 루트에 마운트
- ./html:/usr/share/nginx/html
# 호스트의 nginx.conf 파일을 컨테이너에 마운트
- ./nginx.conf:/etc/nginx/nginx.conf
네트워크 설정 (networks)
# 같은 네트워크
# backend 컨테이너에서 db라는 이름으로 데이터베이스에 접근
services:
backend:
image: myapp:1.0
db:
image: postgres:15
# 커스텀 네트워크 설정
services:
web: # nginx 웹서버, frontend 네트워크에 연결
image: nginx:alpine
networks:
- frontend
app: # app 서버, frontend와 backend 네트워크에 연결
image: myapp:1.0
networks:
- frontend
- backend
db: # db 서버, backend 네트워크에 연결
image: postgres:15
networks:
- backend
networks: # 네트워크 정의
frontend:
backend:
5.3 Docker Compose 명령어
기본 실행/확인 명령어
# compose 서비스 시작 (-d : 백그라운드)
docker-compose up -d
docker-compose up
# 서비스 중지 및 삭제 (-v : 볼륨도 함께 삭제)
docker-compose down -v
docker-compose down
# 실행 중인 서비스 확인
docker-compose ps
# 로그 확인 (-f : 실시간 출력)
docker-compose logs -f
# 특정 서비스 로그 확인
docker-compose logs -f web
서비스 제어
# 특정 서비스 시작
docker-compose up -d web
# 특정 서비스 중지
docker-compose stop web
# 실행 중인 컨테이너에 실행
docker-compose exec web sh
docker-compose exec db mysql -u root -p
빌드 관련
# 이미지 빌드
docker-compose build
# 빌드 후 시작
docker-compose up -d --build
# 특정 서비스만 빌드
docker-compose build app
5.4 Docker Compose 예시
spring boot + mysql
- application.yml 예시
spring:
datasource:
url: ${SPRING_DATASOURCE_URL}
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}
- docker-compose.yml 예시
services:
mysql2: # mysql 서비스
image: mysql:8.0
container_name: myapp-mysql
environment:
MYSQL_ROOT_PASSWORD: mydb
MYSQL_DATABASE: mydb
MYSQL_USER: myuser
MYSQL_PASSWORD: myuser
volumes:
- mysql-data:/var/lib/mysql
ports:
- "3307:3306"
app: # 스프링부트 서비스
build: .
container_name: myapp-backend
environment:
# 환경변수로 DB 연결정보 전달 (서비스이름으로 호스트 지정)
SPRING_DATASOURCE_URL: jdbc:mysql://mysql2:3306/mydb
SPRING_DATASOURCE_USERNAME: myuser
SPRING_DATASOURCE_PASSWORD: myuser
ports:
- "8080:8080"
depends_on: # 의존성 설정
- mysql2
volumes:
mysql-data: # mysql 데이터 영속화 볼륨
etc. 스프링부트에서 docker 실습
1. 네트워크 및 MySQL 컨테이너 생성
네트워크 생성
docker network create --driver bridge spring-net
MySQL 컨테이너 실행
docker run -d \
--name mymysql \
--network spring-net \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=testdb \
-e MYSQL_USER=testuser \
-e MYSQL_PASSWORD=testpass \
-p 3307:3306 \
mysql:8
# 로그확인
docker logs mymysql
# 접속확인
docker exec -it mymysql mysql -u testuser -p
1. 스프링부트 애플리케이션 설정
spring:
profiles:
active: prod
datasource:
url: jdbc:mysql://mymysql:3306/testdb # localhost -> mymysql (컨테이너 이름)
username: testuser
password: testpass
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
2. Dockerfile 작성
# Base Image: JDK 21 사용 (가볍고 안정적인 eclipse-temurin 사용 권장)
FROM eclipse-temurin:21-jdk-alpine
# 작업 디렉토리 생성
WORKDIR /app
# 필수 패키지 설치 (ping 용도)
RUN apk update && apk add --no-cache iputils
# 빌드 산출물(프로젝트 JAR)을 복사
COPY build/libs/*-SNAPSHOT.jar todoapp.jar
# 컨테이너가 사용하는 포트
EXPOSE 8080
# 실행 명령
ENTRYPOINT ["java", "-jar", "todoapp.jar", "--spring.profiles.active=prod"]
3. 이미지 빌드 및 실행
# 스프링부트 빌드
./gradlew build -x test
# 도커 이미지 빌드
docker build -t todoapp:1.0 .
# 도커 컨테이너 실행
docker run -d \
--name springboot \
--network spring-net \
-p 8080:8080 \
todoapp:1.0
4. 연결 접속 테스트
# 로그 확인
docker logs -f springboot
# 컨테이너 상태 확인
docker ps
# springboot 컨테이너에서 MySQL 연결 테스트
docker exec -it springboot ping mymysql
# 웹브라우저 접속
# http://localhost:8080/
5. docker hub 배포
# 도커 허브 로그인
docker login
# 이미지 태그 (도커 허브용)
docker tag todoapp:1.0 <도커허브계정>/todoapp:1.0
# 이미지 푸시
docker push <도커허브계정>/todoapp:1.0'멋사 - 부트캠프 19기 : Java > DevOps' 카테고리의 다른 글
| (12.01) DevOps - 모니터링 이해, Spring Boot Actuator, Prometheus 메트릭 (0) | 2025.12.02 |
|---|---|
| (11.26) DevOps - Nginx, 로드밸런싱, nginx 환경 구성 적용 (0) | 2025.11.26 |
| (11.24) DevOps - Docker 심화, Ubuntu, Nginx, Docker 네트워크 (0) | 2025.11.26 |
| (11.20-1) DevOps - Linux 프로세스 명령어, 백그라운드 작업, 로깅, Cron (0) | 2025.11.20 |
| (11.19) DevOps - Linux 시스템 및 네트워크 명령어, bash 스크립트, bash 변수/제어문/에러처리/디버깅 (0) | 2025.11.19 |