Docker 기본기능 사용법

choiJaewon·2026년 2월 18일
post-thumbnail

-개요

이번 포스팅에서는 프로젝트를 진행하면서 사용한 docker의 기본적인 사용방식에 대해 설명하고자 한다.
docker와 kubernates에는 많은 기능이 있어 공부 할것이 많기에 기본적인 요소들 부터 다루고자 한다.


1. Docker란?


Docker는 컨테이너 기반으로하는 오픈소스 가상화 플랫폼이다. 여기서 가상화라는것은? 하드웨어를 책임지는 책임을 사용자에게로 부터 지워준다는 뜻이다.
쉽게 말해, 애플리케이션을 실행하는 데 필요한 모든 것(코드, 런타임, 라이브러리, 설정 등)을 하나의 컨테이너로 패키징하는 기술이다.

2. 왜 사용할까?

왜 Docker를 사용할까?
그 이유는 독립되고 표준화된 애플리케이션 패키지를 원하기 때문이다.

다음과 같이 개발 환경이 달라져버리는 순간 애플리케이션이 환경이 달라져서 실행이 안될수 있다!

소프트웨어 개발 과정에서는 환경 차이로 인해 다양한 문제가 발생하므로 동일한 환경을 제공하기 위해서 docker를 사용하는것이다.

문제 상황Docker로 해결
개발 환경과 운영 환경 차이동일 이미지로 실행
라이브러리 버전 충돌컨테이너 내부에서 관리
실행 환경 재구성 어려움이미지 기반 배포

Docker를 사용하면 실행 환경 자체를 패키징하기 때문에 어디서 실행하든 동일한 결과를 얻을 수 있다.


3. 이미지와 컨테이너란?

그렇다면 docker에서 컨테이너와 이미지라는 개념을 통해서 패키징을 하는데 이것은 무엇일까?

-컨테이너(Container)란?

컨테이너는 애플리케이션과 실행에 필요한 환경을 함께 묶은 단위이다.

예를 들어,

  • Node.js 애플리케이션이라면
  • Node 런타임 + 라이브러리 + 코드

이 모든 것이 하나의 컨테이너 안에 포함된다.


그렇다면 이미지란 무엇일까?
docker에서는 다음과 같은 과정으로 컨테이너가 만들어지게 된다.

-즉 docker 이미지란? 실행 환경이 담긴 템플릿 (빌드 결과물), 컨테이너를 만들기 위한 설계도 라고 생각하면 된다.

-그리고 docker 이미지를 만들기 위한 스크립트를 dockerfile 이라도 한다. dockerfile은 도커 이미지를 자동으로 빌드 하기 위한 지침서 역할을 한다. '


4. Docker file 명령어

그렇다면 docker file 작성 명령어에 대해서 알아보도록 하자

FROM

베이스 이미지를 지정한다. 도커 이미지가 어떤 기본 이미지를 사용할지 명시한다.

FROM ubuntu:20.04

RUN

이미지 빌드 과정에서 명령어를 실행한다. 패키지를 설치하거나 스크립트를 실행할 때 사용한다.

RUN apt-get update

COPY

파일을 호스트에서 컨테이너로 복사한다. 로컬 파일을 도커 이미지로 복사할 때 사용한다.

COPY ./myapp /app

CMD

컨테이너 시작 시 실행할 기본 명령어를 설정한다. 컨테이너가 실행될 때 기본적으로 실행되는 명령어이다.

CMD ["python3", "/app/main.py"]

⚠️ 주의사항: CMD는 컨테이너 실행 시의 기본 명령어로, 여러 개 작성할 수 있지만 마지막으로 작성된 명령어만 실행된다.


WORKDIR

작업 디렉토리를 설정합니다. 이미지 내에서 작업할 기본 디렉토리를 지정합니다.

WORKDIR /app

EXPOSE

컨테이너에서 사용할 포트를 지정한다. 실제로 포트가 열리는 것은 아니다.

📌 문서화용 힌트일 뿐, 실제로 포트를 열려면 docker run-p 옵션을 사용해야 합니다.

EXPOSE 8080

ENV

환경 변수를 설정합니다. 컨테이너 내부에서 사용할 환경 변수를 설정합니다.

ENV APP_ENV=production

멀티 스테이지 빌드 (Multi-Stage Build)

멀티 스테이지 빌드는 빌드용 컨테이너와 실행용 컨테이너를 분리하여 최종 이미지 크기를 줄이는 방식이다.

Stage 1 - Build (빌드 단계)

FROM eclipse-temurin:17-jdk-alpine AS builder

WORKDIR /app

# Gradle Wrapper 스크립트 먼저 복사 (Docker 캐시 활용)
COPY gradlew .
COPY gradle/ gradle/
COPY build.gradle .
COPY settings.gradle .

# 실행 권한 부여
RUN chmod +x gradlew

# 의존성 다운로드 (소스 변경 시 재다운로드 방지)
RUN ./gradlew dependencies --no-daemon -q

# 소스 코드 복사 후 빌드
COPY src/ src/
RUN ./gradlew bootJar --no-daemon -x test

Stage 1 포인트

  • Java 17 JDK 이미지를 빌드용으로 사용 (AS builder로 이름 지정)
  • alpine: 경량 Linux 배포판 사용 (이미지 크기 최적화)
  • chmod +x gradlew: Gradle Wrapper 실행 권한 부여
  • 의존성 먼저 다운로드 후 소스 복사 → 소스 변경 시 의존성 레이어 캐시 재사용
  • chmod +x 후 git에서 clone 시 실행 권한이 없을 수 있으므로 필요
  • -x test: 테스트 생략 (빌드 시간 단축, Windows에서 Gradle 실행 시 권한 이슈 방지)

Stage 2 - Run (실행 단계)

FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

# 빌드된 JAR 파일만 복사
COPY --from=builder /app/build/libs/*.jar app.jar

# 8080 포트 노출
EXPOSE 8080

# 애플리케이너 실행 (실행 가능한 환경 변수와 함께 최종 주 명령어 지정)
ENTRYPOINT ["java", "-jar", "app.jar"]

Stage 2 포인트

  • JDK → JRE: 실행에만 필요한 JRE로 교체 (약 200MB 차이)
  • --from=builder: Stage 1(builder)에서 빌드된 파일만 가져옴
  • 소스 코드, Gradle 파일 등 빌드에 필요했던 파일들은 최종 이미지에 포함되지 않아 보안성도 향상
  • EXPOSE 8080: 포트 번호 문서화 (docker run-p 옵션으로 실제 포트 연결 필요)
  • ENTRYPOINT: 컨테이너가 항상 실행할 명령어 지정

전체 Dockerfile 예시

# ---- Stage 1: Build ----
FROM eclipse-temurin:17-jdk-alpine AS builder

WORKDIR /app

COPY gradlew .
COPY gradle/ gradle/
COPY build.gradle .
COPY settings.gradle .

RUN chmod +x gradlew
RUN ./gradlew dependencies --no-daemon -q

COPY src/ src/
RUN ./gradlew bootJar --no-daemon -x test

# ---- Stage 2: Run ----
FROM eclipse-temurin:17-jre-alpine

WORKDIR /app

COPY --from=builder /app/build/libs/*.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

5. Docker의 구조


앞에서 언급한 FROM은 베이스 이미지를 선정하여 주는 명령어이다. 베이스 이미지란 무엇일까? 이것은 Docker의 구조와 밀접하게 관련이 되어있다.

베이스 이미지란?

베이스 이미지는 도커 이미지를 만들기 위한 기반이 되는 이미지다.

-도커 이미지는 여러 개의 레이어로 구성되며, 베이스 이미지는 가장 하단의 레이어를 담당한다
도커 이미지를 만들기 위한 기본 환경을 제공한다
Docker Hub에서 다운로드 받을 수 있다

-도커 이미지 레이어 (Docker Image Layer)
레이어 구조
도커 이미지(Docker Image)는 여러 개의 읽기 전용 레이어로 구성된다.

-FROM, RUN, COPY 등 각 명령어가 실행될 때마다 새로운 레이어가 생성됨
레이어는 기본적으로 캐시되어 재사용 가능 → 이미 존재하는 레이어는 다시 다운로드할 필요가 없음

Container Layer ← Read & Write (컨테이너 실행 시 추가되는 레이어)
─────────────────────────────────
Layer 1: RUN yum install -y wget ← Read Only
─────────────────────────────────
Base Layer: FROM centos:7 ← Read Only


이미지 레이어의 특징 각 레이어는 이전 레이어에 의존하며, 마지막 레이어는 완전한 도커 이미지 변경 사항이 생기면 그 레이어부터 다시 빌드됨 → 효율적인 빌드 레이어 기반 구조 덕분에 저장 공간을 절약하고, 배포 속도가 빨라짐

레이어와 컨테이너의 관계
이미지 레이어 vs 컨테이너 레이어
┌─────────────────────┐
│ Container Layer │ ← Read & Write (컨테이너 실행 시 생성)
├─────────────────────┤
│ Image Layer │ ← Non-Writeable (읽기 전용)
│ Image Layer │
│ Image Layer │
├─────────────────────┤
│ Base Layer │ ← Non-Writeable (읽기 전용)
└─────────────────────┘

이미지 레이어 위에 Container 층이 쌓이는 구조
이미지 층은 수정 불가 (Non-Writeable)
컨테이너 층만 읽기/쓰기 가능

즉, 이미지는 수정이 안 되고 컨테이너들만 차례대로 쌓이는 구조다.
비효율적인 부분은 모두 copy가 된다 (Copy-on-Write 방식)


6. docker 이미지와 컨테이너 명령어

그렇다면 마지막으로 이미지와 컨테이너를 다루는 명령어에 대해서 알아보자

이미지 관련 명령어

# 이미지 목록 보기
docker images

# 이미지 빌드 (Dockerfile 있는 디렉토리에서)
docker build -t 이미지명:태그 .

# 이미지 삭제 -> 삭제는 docker desktop에서 관리해서 지우는게 더 편하기는 하다.
docker rmi 이미지명

# Docker Hub에서 이미지 받기
docker pull mysql:8.0

컨테이너 관련 명령어

# 컨테이너 실행
docker run -d -p 8080:8080 --name 컨테이너이름 이미지명

# 실행 중인 컨테이너 목록
docker ps

# 모든 컨테이너 목록 (중지된 것 포함)
docker ps -a

# 컨테이너 중지
docker stop 컨테이너이름

# 컨테이너 시작
docker start 컨테이너이름

# 컨테이너 재시작
docker restart 컨테이너이름

# 컨테이너 삭제 -> 삭제는 docker desktop에서 관리해서 지우는게 더 편하기는 하다.
docker rm 컨테이너이름

# 컨테이너 로그 보기
docker logs 컨테이너이름
docker logs -f 컨테이너이름   # 실시간 (-f = follow)

# 컨테이너 내부 접속 (bash 셸)
docker exec -it 컨테이너이름 bash

run 옵션 정리

옵션설명
-d백그라운드 실행
-p 8080:8080호스트포트:컨테이너포트 연결
--name컨테이너 이름 지정
-e환경변수 설정
-v볼륨(폴더) 마운트
--rm종료 시 자동 삭제
--network네트워크 지정

컨테이너 포트와 호스트 포트의 관계

컨테이너 내부 애플리케이션은 특정 포트에서 실행되고(예: 3000), docker run -p 옵션으로 호스트 포트와 매핑한다.

docker run -p 8080:3000 myapp
# → 브라우저에서 localhost:8080 접속 시, 컨테이너 내부의 3000 포트로 전달

형식: docker run -p <호스트포트>:<컨테이너포트>


Docker 컨테이너 실행 모드

Docker에서는 컨테이너를 실행할 때 Detached, Attached, Interactive 세 가지 모드로 실행할 수 있다.

Detached 모드 (-d)

  • 컨테이너를 백그라운드에서 실행
  • 터미널에 연결되지 않고, 백그라운드에서 계속 실행됨
  • 서버처럼 백그라운드에서 돌리면서 다른 작업을 수행할 수 있음
docker run -d 이미지명

Attached 모드 (기본 모드)

  • 컨테이너를 실행하면서 터미널에 연결
  • 실행된 컨테이너의 출력이 바로 터미널에 표시됨
  • 기본적으로 docker run 명령어는 Attached 모드로 실행됨
docker run 이미지명

Interactive 모드 (-it)

  • 컨테이너 실행 시 표준 입력을 열어두어 사용자가 명령어를 직접 입력 가능
  • 주로 디버깅, 테스트, 수동 작업을 수행할 때 사용
  • -i는 입력, -t는 터미널을 의미
docker run -it 이미지명

컨테이너 삭제

# 컨테이너 목록 확인
docker ps -a

# (Optional) 컨테이너 중지
docker stop container_id

# 컨테이너 삭제
docker rm container_id

# 컨테이너 모두 삭제
docker rm $(docker ps -a -q)

Docker Desktop에서도 삭제 가능 (복잡하지 않으니 Desktop 사용 추천)


이미지 삭제

# 이미지 목록 확인
docker images

# 이미지 삭제
docker rmi image_id

# 이미지 모두 삭제
docker rmi $(docker images -q)

⚠️ 주의: 이미지를 삭제할 때는 해당 이미지를 사용하는 모든 컨테이너를 먼저 삭제해야 한다.
컨테이너가 이미지 위에 쌓인 레이어층이기 때문이다.


이미지에 이름(Tag) 붙이기

Docker 이미지는 이름:태그 형식으로 관리된다.

# 빌드 시 이름(tag) 지정
docker build -t mynode:1 .

# 실행 시 이름으로 실행 가능
docker run -p 3000:3000 mynode:1

태그 규칙

  • mynode:1 → 이름 = mynode, 태그 = 1 (버전 1)
  • mynode:2 → 이름 = mynode, 태그 = 2 (버전 2)
  • mynode:latest → 태그 생략 시 기본값은 :latest

💡 Tip: docker run mynode = docker run mynode:latest
운영 환경에서는 latest 대신 특정 버전 태그를 쓰는 것이 안전하다.

태그를 사용하면 여러 버전을 동시에 관리할 수 있고, ID 대신 이름을 사용하면 명령어가 간단하고 직관적이다.


예제: Python Hello World 컨테이너

1. app.py 작성

print("Hello World from Docker!")

2. Dockerfile 작성

FROM python:3.12-slim
WORKDIR /app
COPY app.py .
CMD ["python", "app.py"]

3. 이미지 빌드

docker build -t py-hello .

4. 컨테이너 실행

docker run --rm py-hello

결과

Hello World from Docker!


다음 포스팅에서는 docker compose와 볼륨들의 docker 고급 기능을 다루어보고자 한다.![](https://velog.velcdn.com/images/jaewon0009/post/7b9aa9cc-3d29-4913-a91e-202bc4a11747/image.png)

0개의 댓글