[DevOps] Docker 실습: Dockerfile → 멀티스테이지 → Compose

이지연·2026년 3월 9일

DevOps

목록 보기
18/24

개요

Docker 초보자부터 실무자까지 따라할 수 있는 실습 중심 가이드. Java HelloWorld부터 Spring 멀티스테이지 빌드, docker-compose까지 한 번에 마스터. 실습 순서대로 따라가다 보면 Docker의 핵심이 자연스럽게 이해됨.

목표: Dockerfile 작성 → Spring 경량화 → 다중 컨테이너 관리까지 완벽 이해


실습1) 간단한 Java 프로그램 실행

Dockerfile 작성, 실행 실습
java로 간단히 만들어진 프로그램을 도커 컨테이너내에서 실행
컨테이너를 실행하기 위한 절차와 명령어를 이미지안에 docker파일을 통해 사전 설정

도커파일 작성 예시

FROM openjdk:17-jdk-alpine  
WORKDIR /app  
COPY HelloWorld1.java /app  
RUN javac HelloWorld1.java → 빌드타임에 실행되는 명령어(build -t)  
CMD ["java", "HelloWorld1"] → 실행 시점에 실행되는 명령어(run -d)

이미지 빌드
docker build -t 이미지명:버전명 -f <도커스크립트경로> <빌드컨텍스트위치>
예시) docker build -t testjava:version1.0 -f ./DockerfileJava .

빌드컨텍스트
빌드 컨텍스트는 Docker 이미지를 빌드할 때 Docker 데몬에게 전송되는 파일과 디렉토리의 집합
Dockerfile이 실행될 때 참조하는 모든 파일과 리소스를 포함

도커 실행
docker run testjava:version1.0 (보통은 포트 지정을 하나 간단한 실습으로만 진행함)


실습2) Spring 프로그램 실행

멀티 스테이지 도커파일 생성
멀티 스테이지 빌드란 하나의 Dockerfile에서 여러 단계를 거쳐 이미지를 생성하는 방식
불필요한 빌드 도구를 최종 이미지에 포함하지 않고 경량화된 이미지를 얻을 수 있음

도커 컨테이너간 커넥션시 발생하는 문제

  • 컨테이너 내부에서의 localhost는 호스트 PC가 아닌 컨테이너 자기 자신을 의미
    • spring에서 localhost로 db와 redis에 접근불가(루프백 네트워크 인터페이스)
  • 컨테이너에서 호스트 머신의 localhost 를 거쳤다가 db, redis등에 접근 가능
    • 컨테이너 내부에서 호스트 머신 가리킬때 "host.docker.internal" 를 사용
    • 호스트 머신을 통한 다른 컨테이너 접근이 가능하므로, 성능하락 문제 존재
  • 대안: docker-compose를 통한 컨테이너들간의 네트워크 통합
    • 컨테이너 이름으로 다른 컨테이너로 직접 접근가능

싱글 스테이지 예시

FROM eclipse-temurin:17-jdk-alpine
WORKDIR /app
COPY gradle gradle
COPY src src
COPY build.gradle .
COPY gradlew .
COPY settings.gradle .

RUN chmod +x gradlew
RUN ./gradlew bootJar

ENTRYPOINT [ "java", "-jar", "build/libs/ordersystem-0.0.1-SNAPSHOT.jar" ]

로컬 빌드 vs 컨테이너 빌드 고민
내 로컬에서 build해서 컨테이너에 jar 파일만 가져다 놓기 vs build를 하기 위한 파일들을 전부 컨테이너에 갖다놔서 build 하기

일단 전자는 윈도우에서 실행한걸 linux 로 가져간거라서 빌드 에러가 발생 안할 수 있을거라는 보장을 할 수 없다 하지만 후자의 경우 경량화를 도모해야하는 상황

따라서 현재 컨테이너에는 빌드를 위한 파일들과 빌드된 jar 파일이 있는 상태이다

해결책 → 투 스테이지 빌드 전략
컨테이너 1에서 빌드에 필요한 파일들을 세팅하고, 빌드된 jar 파일 결과 컨테이너2에다가 취해주는 것(컨테이너 1은 빌드 이후 버리고 우리가 사용하게 되는건 컨테이너 2가 되는것)

멀티 스테이지 예시

FROM eclipse-temurin:17-jdk-alpine as stage1
WORKDIR /app
COPY gradle gradle
COPY src src
COPY build.gradle .
COPY gradlew .
COPY settings.gradle .

RUN chmod +x gradlew
RUN ./gradlew bootJar

# 두번째 스테이지: 이미지 경량화를 위해 스테이지 분리 작업
FROM eclipse-temurin:17-jdk-alpine
WORKDIR /app
COPY --from=stage1 /app/build/libs/*.jar ordersystem.jar
ENTRYPOINT [ "java", "-jar", "ordersystem.jar" ]

결론: 결국 이미지 경량화를 위해 수행하는 작업이다.


Dockerfile 핵심 지시어 정리

FROM

이미지 빌드의 기반이 되는 베이스 이미지를 지정
ex) FROM openjdk:17-jdk-alpine

WORKDIR

명령어를 실행할 디렉토리를 설정
WORKDIR /app

COPY

파일이나 디렉토리를 로컬 파일시스템에서 이미지로 복사
COPY . /app

RUN

이미지 빌드 중에 명령어를 실행
주로 패키지 설치나 빌드 과정에 사용
RUN javac HelloWorld.java

CMD vs ENTRYPOINT

CMD: 컨테이너가 시작될 때 실행할 명령어를 제공 (Dockerfile에서 한 번만 사용 가능)
CMD ["java", "HelloWorld"]

ENTRYPOINT: CMD와 비슷하지만 더 강력함
ENTRYPOINT ["java", "-jar", "myapp.jar"]

ENV

환경 변수를 설정

# docker파일내에서 환경 변수 설정
ENV DB_HOST=localhost
ENV DB_USERNAME=root 
ENV DB_PASSWORD=1234
# docker 컨테이너 실행시 환경 변수 주입
docker run -e DB_HOST=dbserver -e DB_USERNAME=root -e DB_PASSWORD=1234 -p 8080:8080 my-spring-boot-app

EXPOSE

컨테이너가 리스닝할 포트를 문서화하는 목적. 실제 port동작과는 무관
EXPOSE 8080


Docker Compose 입문

활용도

  • 로컬 환경 세팅 → readme 내 docker compose up -d 작성 후 실행 유도하면 끝
  • 과제전형 제출 : zip, github 주소 제출 (실전팁)
  • 배포 목적

설치 및 장점

일반적으로 docker 설치시 docker-compose도 함께 설치
docker-compose 장점:

  • docker간의 통신을 위한 네트워크 구성을 위해 사용
  • 복수 개의 컨테이너를 한꺼번에 실행, 정지시키기 위한 툴
  • compose 네트워크 내부에서만 컨테이너를 접근하게 할수 있어, 보안상 이점

설치된 docker-compose 버전 확인


Docker Compose 실습 가이드

주요 명령어

명령어설명
docker compose build컴포즈 파일 내 정의된 이미지 빌드
docker compose up -d이미지 생성, 컨테이너 생성, 실행
docker-compose up -d 로도 실행가능
즉, docker build, docker run의 과정이 한 번에 수행
기존 이미지가 있는 경우 재빌드시 --build 옵션 추가
docker compose stop컨테이너 stop
docker compose start중지된 컨테이너 시작
docker compose down컨테이너 stop 및 컨테이너 삭제

핵심 키워드

  • version: 사용할 Docker Compose 파일의 버전을 지정
  • services: 애플리케이션을 구성하는 서비스(컨테이너)들을 정의
  • image: 컨테이너의 베이스 이미지를 지정
  • build: Dockerfile의 위치를 지정하여 이미지를 빌드
  • ports: 호스트와 컨테이너 간의 포트 매핑
  • environment: 컨테이너에서 사용할 환경 변수 설정
  • depends_on: 컨테이너의 의존성 정의 (시작 순서 제어)
  • volumes: 호스트 파일시스템에 데이터 지속성 보장
    • log, data, 정적컨텐츠 안정적 저장
    • 컨테이너 삭제되어도 데이터 유지
    • docker compose down -v 시 볼륨까지 삭제

실습

  1. Java 실행 기본 파일 2개 실행 실습

    • 프로그램 2개 동시에 실행 확인
  2. Spring+DB+Redis 동시 실행

    # docker-compose.yml 예시
    version: "3.8"
    services:
      mydb:
        image: mariadb
        environment:
          - MYSQL_ROOT_PASSWORD=****
          - MARIADB_DATABASE=order_system
        ports:
          - "3309:3306"
        volumes:
          - db_data:/var/lib/mysql
      myredis:
        image: redis
        volumes:
          - redis_data:/data
      myordersystem:
        build:
          context: ./ordersystem
          dockerfile: Dockerfile
        ports:
          - "8080:8080"
        depends_on:
          - mydb
          - myredis
        restart: on-failure:10
        environment:
          - SPRING_DATASOURCE_URL=jdbc:mariadb://mydb:3306/order_system
          - SPRING_REDIS_HOST=myredis
    
    volumes:
      db_data:
      redis_data:
      
    • db설정시 docker-compose의 service명을 db의 host명으로 사용

      • SPRING_DATASOURCE_URL=jdbc:mariadb://mydb:3306/order_system
      • SPRING_REDIS_HOST=myredis
    • 선후 관계 또는 적절한 retry 설정을 통해 spring이 db보다 추후에 실행되도록 설정

      • depends_on: [mydb, myredis] → 실행 순서 지정
      • restart: on-failure:10 → DB 준비될 때까지 10회 재시작

실행: docker compose up -d 한 번으로 Spring+MariaDB+Redis 모두 실행됨


전체 내용 요약

구분핵심 내용
Dockerfile 실습Java, Spring 앱을 이미지로 빌드하고 컨테이너에서 실행
멀티 스테이지 빌드빌드용 스테이지와 실행용 스테이지 분리, 이미지 경량화
Docker 파일 구조FROM, WORKDIR, COPY, RUN, CMD, ENV, EXPOSE 등 기본 지시어
docker-compose여러 컨테이너 네트워크 통합, 일괄 실행 및 관리 도구
profile
Eazy하게

0개의 댓글