상세 컨텐츠

본문 제목

Docker 입문자를 위한 핵심 정리: 이미지, 컨테이너, docker run까지

AI DevOps

by thisnorm 2026. 4. 22. 18:51

본문

Docker를 처음 공부할 때는 용어가 한꺼번에 쏟아져서 흐름이 잘 보이지 않는 경우가 많다. 특히 개념은 이해한 것 같은데 실제 명령어와 연결이 안 되면 금방 막히게 된다.

이 글은 티스토리에서 읽기 편한 흐름으로, 개발에 있어 환경 차이가 문제인 이유, 컨테이너 정의, 컨테이너 vs 가상머신: 무엇이 다른가를 중심으로 Docker의 핵심을 한 번에 정리한 글이다.



개발에 있어 환경 차이가 문제인 이유

 

같은 코드라도 OS, 런타임 버전, 설치된 라이브러리 가 다르면 다르게 동작한다. 개발자 A는 macOS + Python 3.11, 서버는 Ubuntu + Python 3.8이라면? 동작이 보장되지 않는다.

*→ OS 차이 * macOS, Windows, Linux는 파일 경로, 권한, 바이너리 형식이 다르다.

*→ 런타임 버전 차이 * Node.js 18과 20, Python 3.8과 3.11은 문법과 패키지 호환성이 다르다.

→ 라이브러리 버전 차이 같은 패키지도 버전 간 API 변경으로 에러가 난다.

*It works on my machine. *

DevOps 현장에서 가장 많이 듣는 말 중 하나이다.

원인은 대부분 환경 불일치 이다.

컨테이너는 애플리케이션 실행에 필요한 모든 환경을 함께 묶어 배포하기 때문에 이 문제를 근본적으로 줄인다.

컨테이너는 "실행 환경까지 함께 포장해서 배송하는 방식"이다. 택배 상자 안에 제품과 포장재가 함께 들어있는 것처럼, 컨테이너 안에는 앱과 실행 환경이 함께 들어있다.



컨테이너 정의

 

애플리케이션 + 실행 환경을 하나로 묶은 실행 단위

컨테이너는 앱 코드, 런타임, 라이브러리, 설정 파일을 하나의 패키지로 묶는다. 이 패키지는 어느 환경에서든 동일한 방식 으로 실행된다. 개발 노트북, 테스트 서버, 프로덕션 서버 - 모두 같은 결과를 낸다.



재현성 ** 배포 일관성** ** 환경 표준화**
어디서나 같은 방식으로 실행 개발-테스트-운영 환경 통일 팀 전체가 동일한 기반 위에서 작업




컨테이너 vs 가상머신: 무엇이 다른가

 

둘 다 격리된 실행 환경을 제공하지만, VM은 게스트 OS를 통째로 포함 하고, 컨테이너는 호스트 커널을 공유 한다. 컨테이너가 훨씬 가볍고 시작 속도가 빠르다.



컨테이너가 DevOps에 잘 맞는 이유

 

빠른 재현

  • 이미지만 있으면 어디서든 동일 환경을 수 초 안에 재현한다.

빠른 배포

  • 이미지를 레지스토리에 올리면 서버는 pull 하나로 최신 버전으로 교체된다.

자동화 친화적

  • CI/CD 파이프라인과 자연스럽게 연결되어 테스트-빌드-배포를 자동화한다.



Docker의 위치

 

Docker란?

컨테이너를 만들고, 실행하고, 관리하는 가장 널리 쓰이는 컨테이너 도구 이다. 2013년 등장 이후 컨테이너 생태계의 표준이 되었다.

Docker가 하는 일

  • 이미지 빌드 (Dockerfile → 이미지)
  • 이미지 저장 및 배포 (레지스트리)
  • 컨테이너 실행 및 관리
  • 네트워크, 볼륨 관리



Docker 아키텍처: 5가지 핵심 요소

 

Docker Client

  • 사용자가 입력하는 docker 명령

Docker Daemon

  • 실제 작업을 처리하는 백그라운드 서비스

이미지

  • 실행 가능한 패키지 템플릿

컨테이너

  • 이미지를 실행한 인스턴스

레지스트리

  • 이미지를 저장하고 배포하는 저장소



Docker Client와 Daemon의 역할 분리

 

Docker Client

우리가 터미널에 입력하는 docker run, docker ps 등 모든 명령은 클라이언트가 처리한다. 클라이언트는 명령을 받아 Docker Daemon에게 전달한다.

💡 *CLI 외에도 Docker Desktop GUI가 클라이언트 역할을 한다. *

Docker Daemon (dockerd)

실제 이미지 다운로드, 컨테이너 생성/실행/중지를 처리한다. 백그라운드에서 항상 실행 중이어야 한다.

💡 *Docker Desktop이 꺼져 있으면 명령이 동작하지 않는다. "Cannot connect to Docker daemon" 오류가 나면 Docker를 먼저 켜라. *



레지스트리와 Docker Hub

 

레지스트리 는 이미지를 저장하고 공유하는 저장소이다.

Docker Hub 는 가장 기본적인 공개 레지스트리로, nginx, python, postgres 같은 공식 이미지를 무료로 제공한다.

  • Docker pull nginx
  • Docker Hub에서 nginx 이미지를 내려받는다.
  • hub.docker.com
  • 브라우저에서 이미지 태그, 설명, 사용법을 확인할 수 있다.
  • 비공개 레지스트리
  • AWS ECR, GitHub container Registry 등 회사 전용 레지스트리도 있다.



Docker의 실행 흐름

 

이 섹션에서는 Docker의 실행 흐름에서 먼저 이해해야 할 핵심만 짧게 정리해 본다.



Docker의 실행 흐름 관련 이미지



이 7단계 루프가 Docker의 기본 패턴이다.

Dockerfile 작업에서도 같은 루프로 검증할 수 있다.



이미지란 무엇인가

 

이미지의 정의

이미지는 실행 가능한 패키지 템플릿 이다. 앱 코드, 런타임, 라이브러리, 환경 변수, 설정 파일이 레이어 구조로 쌓여 있다.

이미지의 특성

  • 읽기 전용(Read-only) - 실행해도 이미지 자체는 바뀌지 않는다.
  • 레이어 구조 - 변경된 부분만 추가해 저장 효율을 높인다.
  • 태그로 버전 구분- nginx:1.25, nginx:latest
  • 레지스트리에서 공유 가능하다.



컨테이너란 무엇인가

 

컨테이너의 정의

컨테이너는 이미지를 실제로 실행한 인스턴스 이다. 이미지를 기반으로 만들어지며, 실행되는 순간 쓰기 가능한 레이어가 추가된다.

컨테이너의 특성

  • 실행 상태를 가진다: 생성, 실행, 중지, 상태
  • 같은 이미지에서 여러 컨테이너를 만들 수 있다.
  • 컨테이너 내부 변경은 이미지에 반영되지 않는다.
  • 삭제하면 내부 변경사항도 사라진다.



이미지 vs 컨테이너: 비유로 이해하기

 

이미지 = 설계도

설계도는 집을 짓는 방법을 담고 있지만, 설계도 자체는 사람이 살 수 없다. 이미지도 마찬가지로 실행 방법을 담은 템플릿이다.

컨테이너 = 실제 집

설계도로 지은 실제 집에서 사람이 살 수 있다. 하나의 설계도로 여러 채를 지을 수 있듯, 하나의 이미지로 여러 컨테이너를 실행할 수 있다.



이미지 레이어 구조 미리 보기

 

Docker 이미지는 여러 레이어가 쌓인 구조 이다. 예를 들어 Ubuntu 레이어 위에 Python 레이어, 그 위에 앱 코드 레이어가 쌓인다. 변경된 레이어만 다시 다운로드하므로 효율적이다.

💡 *4차시에서 Dockerfile을 작성할 때 명령(RUN, COPY, ADD)이 새 레이어를 만든다는 것을 배운다. *



docker ps vs docker ps -a

 

docker ps

현재 실행 중인 컨테이너만 보여 준다.

컨테이너를 띄운 직후 상태를 확인할 때 가장 먼저 쓰는 명령이다.

docker ps -a

종료된 컨테이너 까지 모두 보여 준다.

실패 분석, 정리 작업, 컨테이너 ID 확인에 필요하다.

💡 자주 하는 실수: docker ps 에서 컨테이너가 안 보인다고 "없다"고 판단하면 안 된다. docker ps -a 로 종료된 컨테이너도 확인한다.



이미지 태그와 버전 관리

 

태그 형식

태그를 지정하지 않으면 자동으로 latest 가 붙는다.

⚠️ latest의 함정

latest 는 가장 최신 버전을 가리키지만, 시점마다 다른 버전 을 가리킬 수 있다. 오늘 받은 latest와 한 달 후 받은 latest가 다를 수 있다.

💡 *실무에서는 nginx:1.25.3처럼 버전을 고정해야 재현성이 보장된다. *



docker pull: 이미지 가져오기

 

명령 형식

태그를 생략하면 latest 를 받는다.

실행 후 확인



docker images



이미지가 로컬에 저장되었는지 확인한다. REPOSITORY, TAG, IMAGE ID, SIZE 컬럼을 확인한다.

💡 *docker run 을 실행하면 이미지가 없을 때 자동으로 pull한다. 하지만 명시적으로 pull하는 것이 의도를 분명히 한다. *



docker run: 컨테이너 실행의 핵심

 

docker run 은 이미지를 컨테이너로 실행하는 가장 중요한 명령이다. 옵션이 많지만, 기본 구조를 이해하면 어렵지 않다.

💡 *순서 중요: 옵션은 이미지 이름 앞에, 컨테이너 내부에서 실행할 명령은 이미지 이름 뒤에 온다. *



docker run 주요 옵션 해설



옵션 ** 의미** ** 사용 예**
-d 백그라운드(detached) 실행 서버형 컨테이너에 사용
-p 호스트:컨테이너 포트 매핑 -p 8080:80
--name 컨테이너 이름 지정 --name my-nginx
-it 대화형 터미널 연결 디버깅, 실습에 유용
--rm 종료 시 자동 삭제 일회성 실습 컨테이너
-e 환경 변수 전달 -e MYSQL_ROOT_PASSWORD=pass
-v 볼륨 마운트 -v $(pwd):/app




docker ps: 실행 중인 컨테이너 보기

 

명령 실행

docker ps

출력 컬럼 해석:

  • CONTAINER ID: 컨테이너 고유 식별자
  • IMAGE: 어떤 이미지에서 실행했는지
  • STATUS: Up(실행 중), Exited(종료됨)
  • PORTS: 포트 매핑 정보
  • NAMES: 컨테이너 이름

출력 예시

💡 PORTS 컬럼의 0.0.0.0:8080->80/tcp 는 *로컬 8080 → 컨테이너 80"으로 연결됨을 의미한다.



docker logs: 로그 보기

 

컨테이너가 실행 중 출력한 로그를 확인하는 명령이다. 실행 실패 원인 파악서비스 상태 확인 의 출발점이다.

💡 *좋은 습관: 컨테이너가 바로 종료될 때는 docker ps -a 로 상태 확인 → docker logs 로 원인 파악 순서로 진행한다. *



docker exec -it: 컨테이너 내부 진입

 

명령 형식

진입 후 exit 를 입력하면 컨테이너는 계속 실행 상태로 유지되고 터미널만 나온다.

왜 내부에 들어가는가

  • 설정 파일 위치 확인
  • 환경 변수 확인
  • 프로세스 실행 여부 확인
  • 네트워크 연결 테스트

컨테이너를 블랙박스로 보지 않고 내부를 이해하는 감각을 기른다.



docker rmi: 이미지 삭제

 

💡 *삭제가 안 될 때: "image is being used by stopped container"가 나오면 해당 이미지로 만든 컨테이너(종료된 것 포함)를 먼저 docker rm 으로 삭제한다. 이미지는 컨테이너보다 나중에 삭제한다. *



유용한 추가 옵션들

 

--name: 이름 붙이기

docker run -d --name web nginx

랜덤 이름(예: loving_fermat) 대신 의미 있는 이름을 붙이면 관리가 쉽다. docker stop web 처럼 이름으로 바로 조작한다.

docker inspect: 상세 정보

docker inspect my-nginx

IP 주소, 환경 변수, 마운트 정보 등 JSON 형태로 상세 정보를 확인한다.

-d-it 비교



옵션 용도
-d 서버형 컨테이너 (nginx, DB 등)
-it 대화형 실습, 디버깅



--rm 주의

종료 시 자동 삭제되어 편리하지만, 로그를 나중에 볼 수 없다. 실패 분석이 필요하면 --rm 을 사용하지 말아야 한다.



컨테이너가 바로 종료될 때 대처법

 

컨테이너가 실행하자마자 종료되는 것은 초보자가 가장 많이 만나는 상황이다. 상태 → 로그 → 원인 순서로 체계적으로 확인한다.

  • 포트 충돌
  • 이미 같은 포트를 사용 중인 경우
  • 잘못된 명령
  • CMD나 ENTRYPOINT 오류
  • 파일 누락
  • 필요한 설정 파일이 없는 경우
  • 권한 문제
  • 파일 접근 권한 부족



포트 매핑이 필요한 이유

 

컨테이너는 기본적으로 격리된 네트워크 안에 있다. 브라우저에서 localhost:8080으로 접속하려면 로컬(호스트) 포트와 컨테이너 내부 포트를 명시적으로 연결해야 한다.

💡 *포트 매핑 없이 실행한 nginx는 컨테이너 내부 80번 포트에서 동작하지만, 로컬 브라우저에서는 접근할 방법이 없다. *



포트 매핑 읽는 법: -p 8080:80

 

형식: -p 호스트포트: 컨테이너포트

-p 8080:80 은 '로컬 8080번 포트로 들어오는 요청을 컨테이너 80번 포트로 전달"이라는 의미이다.



docker run -d -p 8080:80 nginx



브라우저: http://localhost:8080

핵심 규칙

  • 왼쪽 = 호스트(내 컴퓨터) 포트
  • 오른쪽 = 컨테이너 내부 포트
  • 오른쪽은 이미지 문서에서 확인 (nginx는 80번)
  • 왼쪽은 내 컴퓨터에서 사용 안 하는 번호로 자유롭게 지정

💡 헷갈리면 "왼쪽이 내 컴퓨터"라고 기억하기



포트 충돌 오류와 해결

 

이 섹션에서는 포트 충돌 오류와 해결에서 먼저 이해해야 할 핵심만 짧게 정리해 본다.

오류 메시지 예시



Error: Bind for 0.0.0.0:8080 failed:
port is already allocated



같은 호스트 포트는 동시에 하나의 컨테이너만 사용할 수 있다. 80880이 이미 사용 중이라면 다른 포트를 사용한다.

  • 원인 확인
  • docker ps #8080 사용 중인 컨테이너 찾기
  • 해결 방법 1
  • docker stop 기존컨테이너 #기존 컨테이너 중지 후 재실행
  • 해결 방법 2
  • docker run -p 8081:80 nginx #다른 호스트 포트 사용



볼륨이 필요한 이유

 

컨테이너 내부 데이터의 문제

컨테이너가 삭제되면 *내부에서 생성된 데이터도 함께 사라진다. * 데이터베이스의 저장 데이터, 업로드된 파일, 로그 등이 모두 없어진다.

볼륨이 해결하는 문제

볼륨은 컨테이너 외부에 데이터를 저장한다. 컨테이너를 삭제해도 데이터는 유지된다. 여러 컨테이너가 같은 데이터를 공유할 수도 있다.



포트와 볼륨에서 흔한 실수

 

포트 방향 혼동

-p 80:8080 으로 거꾸로 쓰면 브라우저에서 80번으로 접속해도 컨테이너 8080이 연결된다. nginx 같은 80번 포트 앱이면 동작하지 않는다.

기억법: "왼쪽 = 내 컴퓨터 포트"

볼륨 경로 오타

로컬 경로나 컨테이너 내부 경로를 틀리면 마운트가 안 된다. docker inspect 로 실제 마운트 경로를 확인한다.

절대 /상대 경로 혼동

바인드 마운트에는 절대 경로가 필요하다.

./html 대신 $(pwd)/html 또는 전체 경로를 사용한다.



AI가 생성한 Docker 명령, 검토하는 법!

 

1. 포트 충돌 가능성

docker ps 로 같은 호스트 포트를 이미 사용 중인 컨테이너가 있는지 확인한다.

2. 불필요한 root 권한

--privileged, --user root 가 포함되어 있으면 왜 필요한지 이해하고 제거를 검토한다.

3. 삭제 옵션 확인

--rm이 있으면 종료 후 로그를 볼 수 없다. 실습 중에는 제거하는 것이 좋다.

4. 성공 기준 명확화

이 명령이 성공했는지 어떻게 확인할 것인가? 브라우저 접속, docker ps 확인, 로그 출력을 미리 생각한다.



마무리

 

Docker는 처음 보면 명령어가 많아 보여도, 실제로는 이미지와 컨테이너의 차이를 이해하고 실행 흐름을 몇 번 반복해 보면 빠르게 익숙해진다.

처음에는 모든 옵션을 외우기보다, Docker를 직접 띄워 보고 상태를 확인하고 로그를 읽는 흐름까지 연결해서 익히는 것이 훨씬 중요하다.

반응형

관련글 더보기