infra
Platform

모듈 맵

[Docker] 컨테이너 실행 옵션과 네임스페이스 작동 원리

0 / 27 완료

펼치기
0 / 27 완료0%

Docker · 02 / 27

[Docker] 컨테이너 실행 옵션과 네임스페이스 작동 원리

docker run의 모든 옵션을 이해하고, 이미지·컨테이너·레지스트리의 관계를 파악합니다

🚨INCIDENT ALERT
HIGH

팀에 합류한 첫날, 선배가 docker run 명령어 하나를 던져줬습니다. 복붙해서 실행했더니 컨테이너는 떠 있는데 브라우저에서는 연결이 안 됩니다.

확인해보니 -p 옵션이 빠져 있었고, 이름을 붙이지 않아 어떤 컨테이너를 봐야 하는지도 헷갈렸습니다. docker run 한 줄 안에는 이미지 다운로드, 컨테이너 생성, 네트워크 설정, 프로세스 실행이 모두 압축되어 있습니다. 옵션 하나가 빠지면 전혀 다른 상황이 됩니다.

docker run 완전 해부 — 이미지, 컨테이너, 레지스트리

팀에 합류한 첫 날, 선배가 docker run 명령어 하나를 던져줬다. 복붙해서 실행했더니 뭔가 돌아가긴 했는데 — 브라우저에선 연결이 안 됐다. -p 옵션이 빠져 있어서였다. 포트 포워딩이 뭔지 몰랐기 때문에 원인을 찾는 데 한 시간이 걸렸다. docker run 한 줄 안에는 이미지 다운로드, 컨테이너 생성, 네트워크 설정, 프로세스 실행이 압축돼 있다. 옵션 하나가 빠지면 전혀 다른 상황이 된다. 명령어를 외우기 전에 각 옵션이 없으면 어떤 일이 벌어지는지를 먼저 알면, 이후엔 절대 잊지 않는다.

docker run 한 줄로 서비스가 뜹니다. 하지만 그 한 줄 안에는 이미지 다운로드, 컨테이너 생성, 네트워크 설정, 프로세스 실행까지 여러 단계가 압축되어 있습니다. 이 모듈에서는 docker run의 각 옵션이 어떤 의미인지, 이미지와 컨테이너와 레지스트리가 어떤 관계인지 완전히 이해합니다.

이번 챕터에서 배울 것
  • 1이미지, 컨테이너, 레지스트리의 관계 — 소스코드와 바이너리와 저장소 비유
  • 2docker run의 필수 옵션 해부 — -d, -p, --name, -e, -v
  • 3docker ps, docker logs, docker exec으로 컨테이너 조회·접속
  • 4이름 없이 실행했을 때 생기는 문제와 --name의 중요성
실습 환경 준비
Docker 실행 여부 확인
docker info
기존 컨테이너 목록 확인
docker ps -a
로컬에 저장된 이미지 목록 확인
docker images
💡개념

이미지, 컨테이너, 레지스트리 — 세 개념의 관계

레지스트리 → 이미지 → 컨테이너 관계 흐름

세 가지 핵심 개념

Docker를 처음 배울 때 가장 헷갈리는 것이 이미지와 컨테이너의 차이입니다. 친숙한 비유로 설명합니다.

소스코드 → 실행 파일 → 실행 중인 프로그램에 빗대면:

Docker 개념비유특성
이미지실행 파일(.exe, 바이너리)읽기 전용, 불변, 저장 가능
컨테이너실행 중인 프로그램 인스턴스읽기/쓰기 가능, 일시적
레지스트리앱스토어 / 아티팩트 저장소이미지를 push/pull

하나의 이미지로 여러 컨테이너를 동시에 실행할 수 있습니다. nginx 이미지 하나로 포트 8080, 8081, 8082에 컨테이너 3개를 띄울 수 있습니다.

레지스트리 (Docker Hub / ECR / GCR)
     │
     │  docker pull
     ▼
  이미지 (로컬 캐시)
  nginx:latest
  ubuntu:22.04
  myapp:v1.2.3
     │
     │  docker run (여러 개 가능)
     ▼
컨테이너 A     컨테이너 B     컨테이너 C
(web-prod)    (web-staging)  (web-test)
포트 8080      포트 8081      포트 8082

docker run 실행 시 일어나는 일

로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/docker/part1/exam_2 && cd /tmp/docker/part1/exam_2

docker run -d -p 8080:80 --name my-nginx nginx:latest

이 명령을 실행하면 Docker가 순서대로 다음을 수행합니다:

  1. 로컬 이미지 캐시에서 nginx:latest 검색
  2. 없으면 Docker Hub에서 자동으로 pull
  3. 이미지를 기반으로 새 컨테이너 생성 (쓰기 가능한 레이어 추가)
  4. 네트워크 설정 (가상 NIC 생성, IP 할당)
  5. 포트 바인딩 (호스트 8080 → 컨테이너 80)
  6. 컨테이너 내부에서 nginx 프로세스 실행
💡개념

docker run 옵션 완전 해부

docker run 명령은 옵션 하나가 빠지면 전혀 다른 상황이 펼쳐집니다. -d 없이 실행하면 터미널이 점유되어 Ctrl+C로 서비스를 끄는 실수를 하고, --name 없이 실행하면 컨테이너 ID나 무작위 이름을 외워야 합니다. -p 없이 실행하면 서비스가 외부에서 접근되지 않아 왜 연결이 안 되는지 찾아 헤매게 됩니다. 각 옵션이 없을 때 어떤 상황이 되는지를 먼저 이해하면, 왜 이 옵션들이 거의 항상 함께 쓰이는지 자연스럽게 납득됩니다. 이 ConceptBlock에서는 실무에서 가장 자주 사용하는 docker run 옵션과 그 의미를 다룹니다.

docker run 주요 옵션 — -d, -p, --name, -e, -v

가장 자주 쓰는 옵션들

-d (detached, 백그라운드 실행)

Docker
# 포어그라운드 실행 — 터미널 점유, Ctrl+C로 종료
docker run nginx

# 백그라운드 실행 — 컨테이너 ID만 출력하고 프롬프트 반환
docker run -d nginx
# a3f4b1c2d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5  ← 컨테이너 ID

-p (포트 바인딩)

Docker
# 형식: -p 호스트포트:컨테이너포트
docker run -d -p 8080:80 nginx
# 브라우저에서 http://localhost:8080 접속 → 컨테이너의 80포트

# 여러 포트 바인딩
docker run -d -p 8080:80 -p 8443:443 nginx

# 특정 IP에만 바인딩 (보안)
docker run -d -p 127.0.0.1:8080:80 nginx
# 로컬호스트에서만 접근 가능, 외부 접근 차단

# 호스트 포트를 지정하지 않으면 랜덤 포트
docker run -d -p 80 nginx
docker port <container_id>  # 할당된 포트 확인

--name (컨테이너 이름 지정)

Docker
# 이름 없이 실행 — Docker가 무작위 이름 생성
docker run -d nginx
# 이름: "romantic_turing" 같은 무작위 이름 → 관리하기 어려움

# 이름 지정 — 이름으로 참조 가능
docker run -d --name web-server nginx

# 이름으로 조작 가능
docker stop web-server
docker start web-server
docker logs web-server
docker exec -it web-server bash

-e (환경변수)

Docker
# 환경변수 설정
docker run -d \
  --name db \
  -e POSTGRES_PASSWORD=mysecret \
  -e POSTGRES_USER=appuser \
  -e POSTGRES_DB=mydb \
  postgres:15

# .env 파일에서 읽기
docker run -d --env-file .env myapp:latest

-v (볼륨, 데이터 영구 보존)

Docker
# 컨테이너 내부 데이터는 컨테이너 삭제 시 사라짐
# 볼륨 마운트로 영구 보존

# Named Volume (Docker가 관리)
docker run -d -v db-data:/var/lib/postgresql/data postgres:15

# Bind Mount (호스트 경로 직접 마운트)
docker run -d -v /home/user/myapp:/app myapp:latest
# 호스트 /home/user/myapp ↔ 컨테이너 /app 동기화

--rm (실행 후 자동 삭제)

Docker
# 일회성 명령 실행 후 자동 정리
docker run --rm ubuntu:22.04 echo "Hello, World!"
docker run --rm python:3.11 python -c "import sys; print(sys.version)"

# --rm 없이 실행하면 컨테이너가 Exited 상태로 남아있음
docker run ubuntu:22.04 echo "Hello"
docker ps -a  # Exited 상태 컨테이너 확인

-it (인터랙티브 + 터미널)

Docker
# 컨테이너 안에서 직접 명령 실행 (쉘 접속)
docker run -it ubuntu:22.04 bash
# 컨테이너 쉘 프롬프트가 열림
# root@a3f4b1c2:/# ls /
# root@a3f4b1c2:/# apt-get update
# exit 또는 Ctrl+D로 나옴

# 실행 중인 컨테이너에 접속
docker exec -it web-server bash
💡개념

컨테이너 조회와 디버깅 — ps, logs, exec

컨테이너를 실행한 후에는 상태 확인, 로그 조회, 내부 접속 세 가지 동작이 가장 자주 필요합니다. ps, logs, exec는 이 세 가지를 담당하는 기본 진단 명령어입니다.

docker ps / logs / exec — 상태 확인·로그·내부 접속

로컬 터미널
# 실행 중인 컨테이너 목록
docker ps

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

# 컨테이너 로그 확인
docker logs web-server

# 실시간 로그 스트리밍 (tail -f처럼)
docker logs -f web-server

# 최근 50줄만
docker logs --tail 50 web-server

# 실행 중인 컨테이너 내부에서 명령 실행
docker exec web-server ls /etc/nginx
docker exec -it web-server bash  # 인터랙티브 쉘

# 컨테이너 상세 정보 (IP, 포트, 환경변수, 마운트 등)
docker inspect web-server

# 컨테이너 리소스 사용량 실시간 모니터링
docker stats

컨테이너 정지(stop/kill)와 상태 전환(start/rm)은 다음 모듈(컨테이너 생명주기)에서 자세히 다룹니다.

기본 실습

직접 nginx 웹 서버를 컨테이너로 실행하고 관리해봅니다.

Docker
# 1. nginx 컨테이너 실행
docker run -d \
  --name my-nginx \
  -p 8080:80 \
  nginx:alpine

# 2. 실행 확인
docker ps
# CONTAINER ID   IMAGE          COMMAND                  STATUS         PORTS
# a3f4b1c2d0e9   nginx:alpine   "/docker-entrypoint.…"   Up 5 seconds   0.0.0.0:8080->80/tcp

# 3. 접속 테스트
curl http://localhost:8080
# HTML 응답이 오면 성공

# 4. 로그 확인
docker logs my-nginx
# 127.0.0.1 - - [11/Apr/2026:10:00:00 +0000] "GET / HTTP/1.1" 200 ...

# 5. 컨테이너 내부 파일 확인
docker exec my-nginx ls /etc/nginx
docker exec -it my-nginx sh  # Alpine은 bash 대신 sh

# 6. 컨테이너 정지 후 재시작
docker stop my-nginx
docker ps -a  # Exited 상태 확인
docker start my-nginx
curl http://localhost:8080  # 다시 응답

# 7. 정리
docker stop my-nginx
docker rm my-nginx
docker images  # nginx 이미지는 로컬에 캐시됨
🔍실행 후 확인할 것
  • docker ps 출력에서 my-nginx 컨테이너가 Up 상태로 보이는가?
  • PORTS 열에 0.0.0.0:8080->80/tcp 매핑이 표시되는가?
  • curl http://localhost:8080 실행 시 HTML 응답이 출력되는가?
  • docker logs my-nginx 에 HTTP 요청 로그가 남는가?

환경변수로 설정을 주입하는 실습:

Docker
# PostgreSQL 컨테이너 실행 (환경변수로 설정 주입)
docker run -d \
  --name my-postgres \
  -e POSTGRES_PASSWORD=mypassword \
  -e POSTGRES_USER=myuser \
  -e POSTGRES_DB=mydb \
  -p 5432:5432 \
  postgres:15-alpine

# 실행 확인
docker ps
docker logs my-postgres  # 초기화 로그 확인

# 접속 테스트 (psql 클라이언트가 있는 경우)
docker exec -it my-postgres psql -U myuser -d mydb

# 또는 다른 컨테이너에서 접속
docker run --rm \
  --link my-postgres:db \
  postgres:15-alpine \
  psql -h db -U myuser -d mydb -c "SELECT version();"

# 정리
docker stop my-postgres
docker rm my-postgres

docker run --name web nginx를 다시 실행하면 같은 이름의 컨테이너가 이미 있다는 에러가 납니다. 이전에 실행했다가 정지된 컨테이너가 남아있기 때문입니다.

Docker
# 에러 상황
docker run -d --name web nginx
# Error response from daemon: Conflict. The container name "/web" is already in use
# by container "a3f4b1c2...". You have to remove (or rename) that container to be
# able to reuse that name.

# 원인 파악: 정지된 컨테이너 확인
docker ps -a | grep web
# a3f4b1c2  nginx  ...  Exited (0) 5 minutes ago  web

# 해결 1: 기존 컨테이너 삭제 후 재실행
docker rm web
docker run -d --name web nginx

# 해결 2: 기존 컨테이너 강제 삭제 후 재실행 (실행 중이어도)
docker run -d --name web nginx

# 해결 3: 스크립트에서 항상 깔끔하게 재시작하는 패턴
docker run -d --name web -p 80:80 nginx

# 예방: --rm 옵션 사용 (종료 시 자동 삭제)
# 단, --rm은 서버 데몬(-d)과 함께 쓰기엔 부적합
# 일회성 실행에만 사용
docker run --rm --name temp-job myapp:latest python run_job.py
위험 명령어컨테이너가 즉시 종료되고 컨테이너 내부 임시 데이터가 사라질 수 있습니다.

실행 중인 컨테이너 강제 삭제

안전한 실행 조건: 삭제 대상이 실습용 컨테이너이며 필요한 로그나 데이터가 남아있지 않은 경우에만 실행합니다.

실행 전 반드시 확인

  • docker ps -a로 컨테이너 이름이 web인지 확인했다
  • 운영 또는 공유 개발 컨테이너가 아님을 확인했다
  • 필요한 로그와 데이터 백업이 끝났다
docker rm -f web

위 항목을 모두 확인한 후 복사할 수 있습니다

핵심: docker ps -adocker ps와 다른 결과를 보여줄 때, 정지된 컨테이너가 남아있는 것입니다. 개발 중에는 docker container prune으로 정지된 컨테이너를 한 번에 정리하는 것이 편합니다.

증상

docker run으로 컨테이너를 실행했는데, docker ps에 아무것도 나타나지 않습니다.

Docker
docker run ubuntu
# (아무 출력 없음, 또는 환경 변수 출력 후 종료)

docker ps
# CONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS   NAMES
# (비어있음)

docker ps -a
# CONTAINER ID   IMAGE    COMMAND       STATUS
# abc123         ubuntu   "/bin/bash"   Exited (0) 3 seconds ago

원인

컨테이너는 CMD/ENTRYPOINT에 지정된 프로세스가 살아있는 동안만 실행됩니다. ubuntu 이미지의 기본 CMD는 /bin/bash인데, -it(대화형 터미널) 없이 실행하면 bash가 입력을 받을 곳이 없어서 즉시 종료됩니다.

docker run ubuntu   → bash 실행 → 입력 없음 → bash 종료 → 컨테이너 종료

해결

Docker
# 방법 1: 대화형 터미널로 실행
docker run -it ubuntu bash
# root@abc123:/#  ← 대화형 쉘 접속됨

# 방법 2: 백그라운드에서 지속적으로 실행될 명령 지정
docker run -d ubuntu sleep infinity
docker ps  # 이제 Running 상태로 보임

# 방법 3: 웹서버 등 포어그라운드로 실행되는 프로세스
docker run -d nginx  # nginx는 포어그라운드 실행 → 컨테이너 유지됨

# 방법 4: 일회성 명령 실행 후 종료 (의도한 동작)
docker run --rm ubuntu echo "Hello"
# Hello
# → --rm 옵션으로 종료 후 컨테이너 자동 삭제

증상

포트를 지정해서 컨테이너를 실행했는데, 포트 바인딩 오류가 납니다.

Docker
docker run -d -p 5432:5432 postgres:15
# docker: Error response from daemon: driver failed programming external connectivity
# on endpoint postgres: Bind for 0.0.0.0:5432 failed: port is already allocated.

원인 진단

로컬 터미널
# 해당 포트를 사용하는 프로세스 확인
ss -tlnp | grep :5432
# LISTEN  0  128  0.0.0.0:5432  0.0.0.0:*  users:(("postgres",pid=1234,fd=5))

# 또는
sudo lsof -i :5432
# COMMAND  PID     USER   FD   TYPE  DEVICE  SIZE/OFF  NODE  NAME
# postgres 1234  postgres  5u  IPv4  123456       0t0  TCP  *:postgresql (LISTEN)

해결

Docker
# 방법 1: 다른 포트로 바인딩 (호스트 포트만 변경)
docker run -d -p 5433:5432 postgres:15
# → 호스트 5433 포트로 접근

# 방법 2: 충돌하는 로컬 서비스 중지 후 실행
sudo systemctl stop postgresql  # 로컬 PostgreSQL 중지
docker run -d -p 5432:5432 postgres:15

# 방법 3: 이미 실행 중인 컨테이너 확인 (같은 이름으로 재실행한 경우)
docker ps -a | grep postgres
docker stop <컨테이너 ID> && docker rm <컨테이너 ID>
docker run -d -p 5432:5432 postgres:15
💼
실무 맥락
현업 패턴

시나리오: 신규 서비스 로컬 개발 환경 세팅 — "README 없이도 팀원 누구나 5분 안에 실행"

신규 팀원이 합류했을 때 "환경 세팅하는 데 이틀이 걸렸어요"라는 말을 없애고 싶습니다. Docker를 활용해 로컬 개발 환경을 표준화하는 패턴입니다.

Docker
# 기존 방식: README에 적힌 20단계 설치 과정
# 1. Python 3.9 설치
# 2. venv 생성
# 3. pip install -r requirements.txt
# 4. PostgreSQL 설치
# 5. DB 초기화
# ...
# 에러가 나면 "저 환경에서는 됐는데요"

# Docker 방식: 한 줄로 모든 의존성 실행
docker run -d \
  --name dev-postgres \
  -e POSTGRES_PASSWORD=dev \
  -e POSTGRES_USER=dev \
  -e POSTGRES_DB=myapp_dev \
  -p 5432:5432 \
  postgres:15-alpine

docker run -d \
  --name dev-redis \
  -p 6379:6379 \
  redis:7-alpine

# 앱만 로컬에서 실행 (핫리로드 등 개발 편의 유지)
# 의존 서비스(DB, 캐시, 메시지큐)는 컨테이너로
DATABASE_URL=postgresql://dev:dev@localhost:5432/myapp_dev \
REDIS_URL=redis://localhost:6379 \
python main.py

# 팀원 온보딩 체크리스트 (Docker로 간소화)
echo "Docker 설치 확인..."
docker --version

echo "개발용 DB 시작..."
docker run -d --name dev-postgres \
  -e POSTGRES_PASSWORD=dev \
  -p 5432:5432 \
  postgres:15-alpine

echo "개발용 Redis 시작..."
docker run -d --name dev-redis -p 6379:6379 redis:7-alpine

echo "준비 완료! 앱을 시작하세요: python main.py"

# 퇴근 시 정리 (선택)
docker stop dev-postgres dev-redis

# 출근 시 재시작 (이미지 재다운로드 없음)
docker start dev-postgres dev-redis

실무 포인트: 개발 의존 서비스(DB, 캐시, 큐)를 컨테이너화하면 팀원마다 "다른 버전의 PostgreSQL"로 인한 버그를 없앨 수 있습니다. 단, 앱 자체는 로컬에서 직접 실행하면서 핫리로드와 디버거를 활용하는 패턴이 개발 편의성과 환경 표준화를 동시에 달성합니다.

핵심 요약

명령용도
docker run -d --name X -p H:C image컨테이너 백그라운드 실행
docker ps / docker ps -a실행중 / 전체 컨테이너 목록
docker stop / docker start정지 / 재시작
docker rm / docker rm -f삭제 / 강제 삭제
docker logs -f NAME실시간 로그 확인
docker exec -it NAME bash컨테이너 쉘 접속
docker inspect NAME컨테이너 상세 정보
docker stats리소스 사용량 모니터링

다음 모듈에서는 컨테이너의 실행 재료인 이미지를 직접 다룹니다. pull, build, tag, push 흐름을 따라가며 이미지가 어떻게 만들어지고 공유되는지 확인합니다.

지식 확인

퀴즈 — 5문제

Q1

docker run -d -p 8080:80 --name web nginx 명령에서 '-d' 옵션의 의미는?

Q2

docker run -p 8080:80 에서 포트 바인딩의 의미로 올바른 것은?

Q3

실행 중인 컨테이너를 삭제하려면 어떻게 해야 하나?

Q4

docker run --rm 옵션의 효과는?

Q5

docker ps -a 와 docker ps 의 차이는?

0 / 5 답변

🧪 실습으로 확인하기

Docker Compose 멀티 서비스 구성

초급

docker-compose.yml로 nginx + 앱 컨테이너를 함께 정의하고, 서비스 간 통신과 볼륨 마운트를 구성한다.

35📋 4단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

docker입문 · 45
[Docker] 가상화의 혁신과 Docker 엔진의 핵심 구조
Docker 트랙 계속
networking입문 · 45
[Network] OSI 7계층과 TCP/IP 4계층 모델 실무적 관점 분석
Networking 트랙 시작점