[도커 30강] 04강. docker run 옵션 실전: -it, -d, --rm, -p, -v

[도커 30강] 04강. docker run 옵션 실전: -it, -d, --rm, -p, -v

도커를 막 배우기 시작하면 docker run은 그냥 “컨테이너 실행 명령”으로만 보입니다. 그런데 실무에서는 docker run 한 줄이 곧 실행 방식, 디버깅 편의성, 네트워크 노출, 데이터 보존 정책까지 결정합니다. 같은 이미지라도 어떤 옵션을 붙이느냐에 따라 완전히 다른 운영 결과가 나옵니다.

이번 강의는 -it, -d, --rm, -p, -v 다섯 가지를 단순 옵션 설명으로 끝내지 않고, “언제 무엇을 선택해야 하는지”를 기준으로 정리합니다. 특히 초보자분들이 많이 겪는 “컨테이너는 떴는데 접속이 안 됨”, “데이터가 사라짐”, “로그를 못 봄” 같은 문제를 예방하는 데 집중하겠습니다.


핵심 개념

docker run은 내부적으로 이미지 확인(pull 필요 시 다운로드) → 컨테이너 생성(create) → 시작(start)을 수행합니다. 여기에 옵션을 붙이면 생성되는 컨테이너의 실행 성격이 달라집니다.

  • -it: 터미널에 붙어서(interactive + tty) 사람이 직접 명령을 입력하며 디버깅하기 좋습니다.
  • -d: 백그라운드(daemon-like) 실행. 서버 프로세스처럼 오래 돌릴 때 기본값에 가깝습니다.
  • --rm: 컨테이너 종료 시 자동 삭제. 일회성 테스트/검증 작업에서 매우 유용합니다.
  • -p: 호스트 포트와 컨테이너 포트 연결. 외부(혹은 로컬 브라우저) 접근의 핵심입니다.
  • -v: 볼륨/바인드 마운트 연결. 컨테이너 밖에 데이터를 두어 영속성/개발 편의성을 확보합니다.

중요한 포인트는 “옵션 각각의 의미”보다 “옵션 조합의 의도”입니다. 예를 들어 개발 중 즉석 디버깅은 -it --rm, 웹 서버 상시 구동은 -d -p, 로컬 소스코드 연동은 -v 조합이 자연스럽습니다.

기본 사용

예제 1) -it로 컨테이너 내부를 직접 다뤄보기

docker run -it --name debug-alpine alpine:3.20 sh

설명:

  • -i는 표준입력을 열어둡니다.
  • -t는 TTY를 할당해 사람이 쓰기 좋은 셸 화면을 만듭니다.
  • 둘을 함께 써서 -it로 자주 표기합니다.
  • 실행 후 컨테이너 내부 셸로 들어가므로 파일 확인, 네트워크 체크, 패키지 설치 테스트 같은 디버깅에 좋습니다.

컨테이너 내부에서 확인 예시:

cat /etc/os-release
pwd
ls -al

종료는 exit를 입력하면 됩니다. 종료 후 컨테이너는 기본적으로 exited 상태로 남습니다(삭제 아님).

예제 2) -d로 백그라운드 서비스 띄우기

docker run -d --name web-nginx nginx:alpine
docker ps --filter "name=web-nginx"
docker logs --tail 20 web-nginx

설명:

  • -d는 터미널을 점유하지 않고 뒤에서 실행됩니다.
  • 웹/DB/캐시처럼 장시간 실행하는 서비스에 기본적으로 사용합니다.
  • 백그라운드 실행이라도 docker logs, docker exec, docker inspect로 상태 점검이 가능합니다.

주의할 점:

  • -d로 띄웠다고 무조건 정상 운영이 아닙니다. 프로세스가 바로 종료되면 컨테이너도 즉시 내려갑니다. docker ps -a와 로그를 반드시 함께 보세요.

예제 3) --rm으로 일회성 작업 깔끔하게 처리하기

docker run --rm alpine:3.20 sh -c "echo 'one-shot task' && sleep 1"
docker ps -a | grep alpine

설명:

  • --rm을 붙이면 컨테이너 종료와 동시에 자동 삭제됩니다.
  • CI의 단발성 검사, 임시 포맷 변환, 간단한 명령 실행에 유용합니다.
  • 다만 종료 후 컨테이너 메타데이터를 다시 보기 어렵기 때문에, 장애 분석 단계에서는 --rm을 잠시 제거하고 관찰하는 편이 낫습니다.

예제 4) -p로 포트 연결해 브라우저 접속 가능하게 만들기

docker run -d --name web-published -p 8080:80 nginx:alpine
curl -I http://localhost:8080

설명:

  • -p 호스트포트:컨테이너포트 형식입니다.
  • 위 예시는 호스트 8080으로 들어온 요청을 컨테이너 80으로 전달합니다.
  • 컨테이너가 정상이어도 -p가 없으면 외부 접근이 안 될 수 있습니다.

추가 확인:

docker port web-published
docker inspect -f '{{json .NetworkSettings.Ports}}' web-published

예제 5) -v로 데이터/코드를 컨테이너 밖에 유지하기

mkdir -p ./demo-html
echo '<h1>Hello from bind mount</h1>' > ./demo-html/index.html
docker run -d --name web-volume -p 8081:80 -v "$(pwd)/demo-html:/usr/share/nginx/html:ro" nginx:alpine
curl http://localhost:8081

설명:

  • 로컬 ./demo-html을 컨테이너의 nginx 문서 루트에 연결했습니다.
  • 파일을 로컬에서 수정하면 컨테이너 재빌드 없이 즉시 반영됩니다(개발 생산성 상승).
  • :ro는 읽기 전용 마운트로, 컨테이너가 호스트 파일을 바꾸지 못하게 막는 안전장치입니다.

자주 하는 실수

실수 1) -d로 실행했는데 컨테이너가 계속 재빨리 종료됨

  • 원인: 컨테이너의 메인 프로세스가 즉시 끝나는 이미지/명령을 사용했기 때문입니다.
  • 해결: docker logs <name>로 종료 원인을 먼저 확인하고, 필요하면 실행 명령(CMD/ENTRYPOINT)을 점검합니다.

실수 2) -p를 빼먹고 “왜 접속이 안 되지?”라고 헤매기

  • 원인: 컨테이너 내부 포트가 열려 있어도 호스트 포트와 연결하지 않으면 외부 접근이 불가합니다.
  • 해결: -p를 명시하고, 이미 실행 중이면 컨테이너를 삭제 후 올바른 옵션으로 재생성합니다(run 옵션은 생성 시점에 확정).

실수 3) --rm을 습관적으로 붙여서 장애 분석 단서가 사라짐

  • 원인: 깔끔함을 우선하다가 관찰해야 할 컨테이너 자체가 자동 삭제됨.
  • 해결: 디버깅 시에는 --rm 없이 실행해 상태/로그/inspect 정보를 남기고, 문제 해결 후 정리합니다.

실수 4) -v 경로를 잘못 지정해 빈 디렉터리만 마운트됨

  • 원인: 상대경로 기준 위치를 착각하거나 오타가 있음.
  • 해결: 절대경로 확인(pwd), 파일 존재 확인 후 마운트하고, docker inspect로 실제 마운트를 검증합니다.

실수 5) -it-d를 목적 없이 섞어 씀

  • 원인: 옵션 뜻을 모른 채 템플릿처럼 복붙.
  • 해결: 디버깅은 -it, 서비스 구동은 -d를 기준으로 의도를 먼저 정한 뒤 필요한 옵션만 조합합니다.

실무 패턴

실무에서는 아래처럼 “작업 유형별 기본 조합”을 팀 규칙으로 고정하면 실수가 크게 줄어듭니다.

  1. 임시 진단/재현
  • 권장: docker run -it --rm ...
  • 이유: 사람 손으로 빠르게 확인하고 흔적은 자동 정리.
  1. 상시 서비스 실행
  • 권장: docker run -d --name <svc> -p <host>:<container> ...
  • 이유: 백그라운드 구동 + 명시적 포트 관리 + 이름 기반 운영.
  1. 개발 코드 핫리로드/정적 파일 반영
  • 권장: docker run ... -v <local>:<container>[:ro]
  • 이유: 이미지 재빌드 횟수 감소, 개발 반복 속도 개선.
  1. 데이터 보존이 필요한 워크로드(DB, 업로드 파일)
  • 권장: named volume 또는 명시적 bind mount 설계.
  • 이유: 컨테이너 수명과 데이터 수명을 분리해야 백업/복구가 쉬움.

운영 체크리스트도 함께 두면 좋습니다.

docker ps -a
docker logs --tail 100 <container>
docker inspect <container>

위 3종은 “실행 여부, 애플리케이션 메시지, 런타임 설정”을 빠르게 분리 진단하는 최소 세트입니다.

오늘의 결론

docker run은 단순 시작 명령이 아니라, 컨테이너의 성격을 선언하는 설계 문장입니다. -it, -d, --rm, -p, -v를 목적에 맞게 쓰면 디버깅은 빨라지고, 운영은 예측 가능해지며, 데이터 사고를 크게 줄일 수 있습니다.

오늘은 옵션 하나하나를 외우는 것보다, “내가 지금 하려는 작업이 임시 실행인지, 서비스 운영인지, 데이터 영속이 필요한지”를 먼저 판단하는 습관을 가져가시면 됩니다.

연습문제

  1. alpine:3.20으로 -it--rm을 함께 사용해 셸 진입 후 간단한 파일을 만들고 종료하세요. 종료 후 컨테이너가 남는지 확인하고 결과를 설명해보세요.
  2. nginx:alpine-d -p 8082:80으로 실행한 뒤 브라우저 또는 curl로 접속을 확인하세요. 그다음 -p 없이 다시 실행했을 때 차이를 비교해보세요.
  3. 로컬 폴더를 -v로 연결해 HTML 파일을 수정하고, 컨테이너 재시작 없이 반영되는지 검증해보세요.

이전 강의 정답

3강 연습문제 기준 해설:

  • 이미지 1개로 컨테이너 여러 개를 만들 수 있는 이유는, 이미지가 템플릿이고 컨테이너는 실행 인스턴스이기 때문입니다.
  • stop은 상태를 exited로 바꾸고, start는 같은 컨테이너를 다시 실행하며, rm은 컨테이너 객체 자체를 삭제합니다.
  • --rm 실행은 종료 후 컨테이너가 자동 삭제되어 docker ps -a에 흔적이 남지 않거나 즉시 사라집니다.

실습 환경/재현 정보

  • OS: macOS 15+ / Ubuntu 22.04+
  • Docker 버전: Docker Engine 25.x 이상(또는 Docker Desktop 최신 안정)
  • 사용 이미지: alpine:3.20, nginx:alpine
  • 실행 순서:
    1. -it로 대화형 실행
    2. -d로 백그라운드 실행
    3. --rm 동작 차이 확인
    4. -p 포트 공개 확인
    5. -v 마운트 반영 확인
  • 재현 체크 포인트:
    • 백그라운드 실행 후 로그/상태 조회 가능 여부
    • 포트 매핑 유무에 따른 접근 차이
    • 마운트 경로 수정 시 즉시 반영 여부
    • 자동 삭제 옵션 사용 시 흔적 보존 여부