Docker & Kubernetes 완전 정복 2편: Docker 이미지와 컨테이너 관리
Docker & Kubernetes Complete Guide Part 2: Docker Images and Container Management
서론: Docker 핵심 개념 마스터하기
1편에서 Docker의 기본 개념과 설치 방법을 배웠습니다. 이번 2편에서는 Docker를 실무에서 효과적으로 사용하기 위해 반드시 알아야 하는 이미지(Image)와 컨테이너(Container)의 핵심 개념과 관리 방법을 깊이 있게 다루겠습니다.
이미지와 컨테이너의 관계를 정확히 이해하고, 다양한 명령어 옵션을 익히면 Docker를 훨씬 효율적으로 활용할 수 있습니다. 특히 docker run의 다양한 옵션들은 실무에서 매우 자주 사용되므로 꼭 숙지해야 합니다.
1. 이미지와 컨테이너의 차이
1.1 이미지(Image)란?
Docker 이미지는 컨테이너를 생성하기 위한 읽기 전용(Read-Only) 템플릿입니다. 애플리케이션 실행에 필요한 모든 것(코드, 런타임, 라이브러리, 환경 변수, 설정 파일 등)이 포함되어 있습니다.
비유: 이미지는 프로그램의 "설치 파일" 또는 "클래스 정의"와 비슷합니다. 실행되기 전의 상태입니다.
1.2 컨테이너(Container)란?
Docker 컨테이너는 이미지의 실행 가능한 인스턴스입니다. 이미지를 기반으로 생성되며, 자체적인 파일 시스템, 네트워크, 프로세스 공간을 가집니다.
비유: 컨테이너는 프로그램의 "실행 중인 프로세스" 또는 "객체 인스턴스"와 비슷합니다. 실제로 동작하는 상태입니다.
1.3 이미지와 컨테이너의 관계
┌─────────────────────────────────────────────────────────────┐
│ Docker Image │
│ (읽기 전용 템플릿) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Application Code + Dependencies + Configuration │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ docker run
▼
┌─────────────────────────────────────────────────────────────┐
│ Docker Container 1 │
│ (실행 인스턴스) │
│ ┌───────────────────────┐ ┌───────────────────────────┐ │
│ │ Image Layers (RO) │ │ Writable Layer (RW) │ │
│ │ (읽기 전용) │ │ (쓰기 가능) │ │
│ └───────────────────────┘ └───────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ docker run (같은 이미지로)
▼
┌─────────────────────────────────────────────────────────────┐
│ Docker Container 2 │
│ (독립적인 인스턴스) │
└─────────────────────────────────────────────────────────────┘
1.4 핵심 차이점 정리
| 구분 | 이미지 (Image) | 컨테이너 (Container) |
|---|---|---|
| 상태 | 정적 (Static) | 동적 (Dynamic) |
| 수정 가능 여부 | 읽기 전용 (Immutable) | 쓰기 가능 (Mutable) |
| 저장 위치 | 로컬 또는 레지스트리 | 호스트 메모리/디스크 |
| 생성 방법 | Dockerfile 빌드 또는 pull | docker run/create |
| 관계 | 하나의 이미지 = 여러 컨테이너 | 하나의 컨테이너 = 하나의 이미지 |
2. 이미지 레이어 개념
2.1 레이어(Layer)란?
Docker 이미지는 여러 개의 읽기 전용 레이어로 구성됩니다. 각 레이어는 이전 레이어와의 차이점(delta)만 저장합니다. 이 구조는 Union File System (UnionFS)을 사용하여 구현됩니다.
┌─────────────────────────────────────────┐
│ Container Layer (R/W) │ ← 컨테이너 실행 시 추가
├─────────────────────────────────────────┤
│ Layer 4: CMD/ENTRYPOINT │ ← 이미지 레이어들
├─────────────────────────────────────────┤
│ Layer 3: Application Code │
├─────────────────────────────────────────┤
│ Layer 2: Dependencies │
├─────────────────────────────────────────┤
│ Layer 1: Base OS (Ubuntu) │
└─────────────────────────────────────────┘
2.2 레이어의 장점
- 저장 공간 효율화: 동일한 레이어는 여러 이미지에서 공유되어 중복 저장을 방지합니다.
- 빠른 빌드: 변경된 레이어만 다시 빌드하므로 빌드 시간이 단축됩니다.
- 빠른 배포: 이미 존재하는 레이어는 다시 다운로드하지 않습니다.
- 캐싱: 빌드 과정에서 캐시를 효과적으로 활용할 수 있습니다.
2.3 이미지 레이어 확인하기
# 이미지의 레이어 히스토리 확인
docker history nginx
# 결과 예시:
IMAGE CREATED CREATED BY SIZE
a6bd71f48f68 2 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 2 weeks ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
<missing> 2 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
<missing> 2 weeks ago /bin/sh -c #(nop) COPY file:e57eef017a414ca7… 4.62kB
<missing> 2 weeks ago /bin/sh -c set -x && groupadd --system -… 112MB
...
# 이미지 상세 정보 확인 (레이어 포함)
docker inspect nginx
# 특정 필드만 추출 (레이어 목록)
docker inspect --format='{{range .RootFS.Layers}}{{println .}}{{end}}' nginx
3. 이미지 관리 명령어
3.1 docker pull - 이미지 다운로드
Docker Hub 또는 다른 레지스트리에서 이미지를 다운로드합니다.
# 기본 사용법 (latest 태그)
docker pull nginx
# 특정 버전(태그) 지정
docker pull nginx:1.25.3
docker pull nginx:alpine # Alpine Linux 기반 경량 버전
# 특정 레지스트리에서 pull
docker pull gcr.io/google-containers/nginx
# 모든 태그 pull (주의: 많은 용량 사용)
docker pull -a nginx
# pull 진행 상황 확인
docker pull ubuntu:22.04
# 22.04: Pulling from library/ubuntu
# 2ab09b027e7f: Pull complete
# Digest: sha256:...
# Status: Downloaded newer image for ubuntu:22.04
3.2 docker images - 이미지 목록 확인
# 모든 이미지 목록
docker images
docker image ls # 동일한 명령
# 결과 예시:
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest a6bd71f48f68 2 weeks ago 187MB
nginx alpine 8e75cbc5b25c 2 weeks ago 41.1MB
ubuntu 22.04 174c8c134b2a 3 weeks ago 77.8MB
mysql 8.0 3218b38490ce 4 weeks ago 516MB
# 특정 이미지만 필터링
docker images nginx
docker images "nginx:*"
# 이미지 ID만 출력
docker images -q
# Dangling 이미지 표시 (태그가 없는 이미지)
docker images -f "dangling=true"
# 상세 정보 표시
docker images --no-trunc # 전체 이미지 ID
docker images --digests # 다이제스트 포함
# 특정 형식으로 출력
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
3.3 docker push - 이미지 업로드
# Docker Hub에 로그인
docker login
# 이미지 태깅 (Docker Hub 형식: username/repository:tag)
docker tag my-app:latest myusername/my-app:v1.0
# 이미지 push
docker push myusername/my-app:v1.0
# 모든 태그 push
docker push myusername/my-app --all-tags
# 다른 레지스트리에 push
docker tag my-app:latest registry.example.com/my-app:v1.0
docker push registry.example.com/my-app:v1.0
3.4 docker rmi - 이미지 삭제
# 이미지 삭제 (이름:태그 또는 이미지 ID)
docker rmi nginx:alpine
docker rmi a6bd71f48f68
# 강제 삭제 (컨테이너가 사용 중이어도)
docker rmi -f nginx
# 여러 이미지 동시 삭제
docker rmi nginx:alpine mysql:8.0 redis:latest
# Dangling 이미지 모두 삭제
docker image prune
# 사용하지 않는 모든 이미지 삭제
docker image prune -a
# 특정 조건의 이미지 삭제
docker image prune -a --filter "until=24h" # 24시간 이상 된 이미지
3.5 기타 이미지 명령어
# 이미지 상세 정보
docker inspect nginx
# 이미지 저장 (tar 파일로)
docker save -o nginx.tar nginx:latest
docker save nginx:latest > nginx.tar
# 이미지 불러오기 (tar 파일에서)
docker load -i nginx.tar
docker load < nginx.tar
# 이미지 태그 추가
docker tag nginx:latest my-nginx:v1.0
# 컨테이너에서 이미지 생성 (변경사항 커밋)
docker commit my-container my-new-image:v1.0
4. 컨테이너 생명주기
4.1 컨테이너 상태 전이도
docker create
[Image] ─────────────────────► [Created]
│
│ docker start
▼
[Running] ◄────────┐
│ │
docker stop │ │ docker restart
docker kill │ │
▼ │
[Stopped] ─────────┘
│
│ docker rm
▼
[Removed]
4.2 docker create - 컨테이너 생성
docker create는 컨테이너를 생성만 하고 시작하지 않습니다.
# 컨테이너 생성 (시작하지 않음)
docker create --name my-nginx nginx
# 생성된 컨테이너 확인
docker ps -a
# STATUS: Created
4.3 docker start - 컨테이너 시작
# 컨테이너 시작
docker start my-nginx
# 여러 컨테이너 동시 시작
docker start container1 container2 container3
# 대화형 모드로 시작 (표준 입력 연결)
docker start -i my-container
# 출력을 터미널에 연결
docker start -a my-container
4.4 docker stop - 컨테이너 정지
# 컨테이너 정상 종료 (SIGTERM 신호)
docker stop my-nginx
# 타임아웃 지정 (기본 10초)
docker stop -t 30 my-nginx # 30초 대기 후 강제 종료
# 여러 컨테이너 동시 정지
docker stop container1 container2
# 모든 실행 중인 컨테이너 정지
docker stop $(docker ps -q)
4.5 docker restart - 컨테이너 재시작
# 컨테이너 재시작
docker restart my-nginx
# 타임아웃 지정
docker restart -t 5 my-nginx
4.6 docker rm - 컨테이너 삭제
# 정지된 컨테이너 삭제
docker rm my-nginx
# 강제 삭제 (실행 중이어도)
docker rm -f my-nginx
# 볼륨도 함께 삭제
docker rm -v my-nginx
# 정지된 모든 컨테이너 삭제
docker container prune
# 모든 컨테이너 삭제 (강제)
docker rm -f $(docker ps -aq)
4.7 docker kill - 컨테이너 강제 종료
# 강제 종료 (SIGKILL)
docker kill my-nginx
# 특정 시그널 전송
docker kill -s SIGINT my-nginx
docker kill -s SIGHUP my-nginx
5. docker run 옵션 상세
docker run은 docker create + docker start를 한 번에 수행합니다. 가장 자주 사용하는 명령어이므로 옵션을 잘 알아두어야 합니다.
5.1 기본 옵션
# 기본 형식
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# 기본 실행
docker run nginx
# 이름 지정 (--name)
docker run --name my-nginx nginx
# 백그라운드 실행 (-d, --detach)
docker run -d nginx
# 대화형 모드 (-i, -t, -it)
docker run -it ubuntu bash
# -i: 표준 입력(stdin) 활성화
# -t: 가상 터미널(TTY) 할당
# 컨테이너 종료 시 자동 삭제 (--rm)
docker run --rm nginx curl localhost
5.2 포트 매핑 (-p, --publish)
# 호스트포트:컨테이너포트
docker run -d -p 8080:80 nginx
# 호스트의 8080 포트 → 컨테이너의 80 포트
# 여러 포트 매핑
docker run -d -p 8080:80 -p 443:443 nginx
# 특정 IP에만 바인딩
docker run -d -p 127.0.0.1:8080:80 nginx
# 컨테이너 포트만 지정 (호스트 포트 자동 할당)
docker run -d -p 80 nginx
docker port my-nginx # 할당된 포트 확인
# 모든 노출 포트 자동 매핑 (-P)
docker run -d -P nginx
# 프로토콜 지정
docker run -d -p 8080:80/tcp -p 53:53/udp my-app
5.3 볼륨 마운트 (-v, --volume, --mount)
# 호스트 디렉토리 마운트 (Bind Mount)
docker run -d -v /host/path:/container/path nginx
docker run -d -v $(pwd)/html:/usr/share/nginx/html nginx
# 읽기 전용 마운트
docker run -d -v /host/path:/container/path:ro nginx
# Named Volume 사용
docker volume create my-data
docker run -d -v my-data:/app/data mysql
# --mount 옵션 (더 명시적)
docker run -d --mount type=bind,source=/host/path,target=/container/path nginx
docker run -d --mount type=volume,source=my-data,target=/app/data mysql
# 익명 볼륨
docker run -d -v /app/data mysql
# 다른 컨테이너의 볼륨 공유 (--volumes-from)
docker run -d --volumes-from my-container backup-container
5.4 환경 변수 (-e, --env)
# 환경 변수 설정
docker run -d -e MYSQL_ROOT_PASSWORD=secret mysql
docker run -d -e "MY_VAR=hello world" my-app
# 여러 환경 변수
docker run -d \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=mydb \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=pass \
mysql
# 파일에서 환경 변수 읽기 (--env-file)
# .env 파일:
# MYSQL_ROOT_PASSWORD=secret
# MYSQL_DATABASE=mydb
docker run -d --env-file .env mysql
# 호스트 환경 변수 전달
docker run -d -e MY_VAR my-app # 호스트의 MY_VAR 값 사용
5.5 네트워크 옵션 (--network)
# 기본 브릿지 네트워크 (default)
docker run -d nginx
# 호스트 네트워크 사용 (컨테이너가 호스트 네트워크 직접 사용)
docker run -d --network host nginx
# 네트워크 없음
docker run -d --network none nginx
# 사용자 정의 네트워크
docker network create my-network
docker run -d --network my-network --name web nginx
docker run -d --network my-network --name db mysql
# 네트워크 별칭 설정
docker run -d --network my-network --network-alias web-alias nginx
5.6 기타 유용한 옵션
# 작업 디렉토리 설정 (-w, --workdir)
docker run -w /app node npm install
# 사용자 지정 (-u, --user)
docker run -u 1000:1000 nginx
docker run -u root nginx
# 호스트명 설정 (-h, --hostname)
docker run -h my-hostname nginx
# DNS 설정
docker run --dns 8.8.8.8 nginx
# 재시작 정책 (--restart)
docker run -d --restart always nginx # 항상 재시작
docker run -d --restart unless-stopped nginx # 수동 정지 전까지 재시작
docker run -d --restart on-failure:3 nginx # 실패 시 최대 3회 재시작
# 권한 설정 (--privileged, --cap-add, --cap-drop)
docker run --privileged nginx # 모든 권한
docker run --cap-add SYS_ADMIN nginx
docker run --cap-drop NET_RAW nginx
# 장치 접근 (--device)
docker run --device /dev/sda:/dev/xvdc nginx
# 읽기 전용 루트 파일시스템
docker run --read-only nginx
6. docker exec와 attach
6.1 docker exec - 실행 중인 컨테이너에서 명령 실행
docker exec는 이미 실행 중인 컨테이너에 새로운 프로세스를 실행합니다.
# 기본 사용법
docker exec my-nginx ls -la
# 대화형 쉘 접속 (가장 많이 사용)
docker exec -it my-nginx bash
docker exec -it my-nginx sh # bash가 없는 컨테이너
# 특정 사용자로 실행
docker exec -u root my-nginx whoami
# 작업 디렉토리 지정
docker exec -w /etc/nginx my-nginx cat nginx.conf
# 환경 변수 설정
docker exec -e MY_VAR=value my-nginx printenv MY_VAR
# 백그라운드에서 실행
docker exec -d my-nginx touch /tmp/test.txt
6.2 docker attach - 실행 중인 컨테이너에 연결
docker attach는 컨테이너의 메인 프로세스(PID 1)에 표준 입출력을 연결합니다.
# 컨테이너에 연결
docker attach my-container
# 분리 키 설정 (기본: Ctrl+P, Ctrl+Q)
docker attach --detach-keys="ctrl-c" my-container
# 출력만 보기 (입력 비활성화)
docker attach --no-stdin my-container
# 시그널 전달 비활성화
docker attach --sig-proxy=false my-container
6.3 exec vs attach 차이점
| 항목 | docker exec | docker attach |
|---|---|---|
| 연결 대상 | 새로운 프로세스 생성 | 메인 프로세스(PID 1)에 연결 |
| 사용 사례 | 디버깅, 관리 작업 | 로그 확인, 대화형 앱 |
| 종료 시 영향 | 컨테이너에 영향 없음 | Ctrl+C 시 컨테이너 종료 가능 |
| 다중 연결 | 여러 세션 가능 | 같은 출력 공유 |
| 권장 상황 | 대부분의 경우 권장 | 특수한 경우에만 사용 |
# 실무에서 권장하는 패턴
# 1. 디버깅 시
docker exec -it my-container bash
# 2. 로그 확인 시 (attach 대신)
docker logs -f my-container
# 3. attach를 안전하게 사용
docker attach --sig-proxy=false my-container
# 또는 Ctrl+P, Ctrl+Q로 분리
7. 컨테이너 로그 확인 (docker logs)
7.1 기본 로그 명령어
# 전체 로그 출력
docker logs my-nginx
# 실시간 로그 스트리밍 (-f, --follow)
docker logs -f my-nginx
# 마지막 N줄만 출력 (--tail)
docker logs --tail 100 my-nginx
# 타임스탬프 포함 (-t, --timestamps)
docker logs -t my-nginx
# 특정 시간 이후의 로그 (--since)
docker logs --since 2026-01-22T10:00:00 my-nginx
docker logs --since 1h my-nginx # 1시간 이내
docker logs --since 30m my-nginx # 30분 이내
# 특정 시간까지의 로그 (--until)
docker logs --until 2026-01-22T12:00:00 my-nginx
# 조합 예시
docker logs -f --tail 50 -t my-nginx
7.2 로그 드라이버 설정
# 로그 드라이버 확인
docker inspect --format='{{.HostConfig.LogConfig.Type}}' my-nginx
# JSON 파일 로그 드라이버 (기본값)
docker run -d --log-driver json-file nginx
# 로그 옵션 설정
docker run -d \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
nginx
# syslog 드라이버
docker run -d --log-driver syslog nginx
# 로그 비활성화
docker run -d --log-driver none nginx
# 실제 로그 파일 위치 확인
docker inspect --format='{{.LogPath}}' my-nginx
7.3 로그 관리 베스트 프랙티스
# 1. 로그 크기 제한 (운영 환경 필수)
docker run -d \
--log-opt max-size=10m \
--log-opt max-file=5 \
nginx
# 2. 컨테이너별 로그 파일 확인
ls -la /var/lib/docker/containers/[CONTAINER_ID]/
# 3. 로그 삭제 (주의: 실행 중 삭제 시 문제 발생 가능)
truncate -s 0 $(docker inspect --format='{{.LogPath}}' my-nginx)
# 4. grep과 함께 사용
docker logs my-nginx 2>&1 | grep "error"
# 5. 특정 출력 스트림만
docker logs my-nginx 2>/dev/null # stdout만
docker logs my-nginx 2>&1 1>/dev/null # stderr만
8. 리소스 제한 (--memory, --cpus)
8.1 메모리 제한
# 메모리 제한 (--memory, -m)
docker run -d -m 512m nginx
docker run -d --memory 1g nginx
docker run -d --memory 256m nginx
# 메모리 + 스왑 제한
docker run -d -m 512m --memory-swap 1g nginx
# 스왑 비활성화
docker run -d -m 512m --memory-swap 512m nginx
# 메모리 예약 (soft limit)
docker run -d -m 1g --memory-reservation 512m nginx
# OOM Killer 비활성화 (권장하지 않음)
docker run -d -m 512m --oom-kill-disable nginx
8.2 CPU 제한
# CPU 개수 제한 (--cpus)
docker run -d --cpus 1.5 nginx # 1.5개 CPU
docker run -d --cpus 0.5 nginx # 0.5개 CPU (50%)
# CPU 공유 가중치 (--cpu-shares)
docker run -d --cpu-shares 512 nginx # 기본값: 1024
# 상대적 가중치 (경쟁 시에만 영향)
# 특정 CPU 코어에 고정 (--cpuset-cpus)
docker run -d --cpuset-cpus 0,1 nginx # CPU 0, 1만 사용
docker run -d --cpuset-cpus 0-3 nginx # CPU 0~3 사용
# CPU 주기 제한 (고급)
docker run -d --cpu-period 100000 --cpu-quota 50000 nginx
# 100ms 중 50ms 사용 = 50% CPU
8.3 종합 예시
# 운영 환경 리소스 제한 예시
docker run -d \
--name production-app \
--memory 2g \
--memory-reservation 1g \
--cpus 2 \
--restart unless-stopped \
--log-opt max-size=50m \
--log-opt max-file=5 \
-p 8080:80 \
my-app:latest
# 리소스 사용량 실시간 모니터링
docker stats
# 특정 컨테이너만 모니터링
docker stats my-nginx
# 리소스 사용량 일회성 확인
docker stats --no-stream
# 특정 형식으로 출력
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
8.4 실행 중인 컨테이너 리소스 변경
# 메모리 제한 변경
docker update --memory 1g --memory-swap 2g my-container
# CPU 제한 변경
docker update --cpus 2 my-container
# 재시작 정책 변경
docker update --restart unless-stopped my-container
# 여러 옵션 동시 변경
docker update -m 2g --cpus 1.5 my-container
9. 결론 및 다음 단계
이번 2편에서는 Docker의 핵심인 이미지와 컨테이너에 대해 깊이 있게 학습했습니다.
핵심 정리
- 이미지 vs 컨테이너: 이미지는 읽기 전용 템플릿, 컨테이너는 실행 가능한 인스턴스입니다.
- 이미지 레이어: 효율적인 저장과 빌드를 위한 계층 구조입니다.
- 이미지 명령어:
pull,push,images,rmi로 이미지를 관리합니다. - 컨테이너 생명주기:
create,start,stop,restart,rm으로 관리합니다. - docker run 옵션:
-d,-p,-v,-e,--name등이 핵심입니다. - exec vs attach: 대부분의 경우
exec를 사용합니다. - 로그 관리:
docker logs와 로그 드라이버 설정이 중요합니다. - 리소스 제한:
--memory,--cpus로 안정적인 운영이 가능합니다.
다음 편 예고
3편에서는 Dockerfile 작성과 이미지 빌드에 대해 다룹니다:
- Dockerfile 기본 문법
- 주요 명령어 (FROM, RUN, COPY, ADD, CMD, ENTRYPOINT 등)
- 멀티 스테이지 빌드
- 이미지 최적화 기법
- 베스트 프랙티스
Docker를 실무에서 효과적으로 사용하려면 이번 편에서 배운 명령어들을 직접 실습해보는 것이 중요합니다. 다양한 옵션들을 조합해서 사용해보면서 익숙해지시기 바랍니다!