[도커 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를 기준으로 의도를 먼저 정한 뒤 필요한 옵션만 조합합니다.
실무 패턴
실무에서는 아래처럼 “작업 유형별 기본 조합”을 팀 규칙으로 고정하면 실수가 크게 줄어듭니다.
- 임시 진단/재현
- 권장:
docker run -it --rm ... - 이유: 사람 손으로 빠르게 확인하고 흔적은 자동 정리.
- 상시 서비스 실행
- 권장:
docker run -d --name <svc> -p <host>:<container> ... - 이유: 백그라운드 구동 + 명시적 포트 관리 + 이름 기반 운영.
- 개발 코드 핫리로드/정적 파일 반영
- 권장:
docker run ... -v <local>:<container>[:ro] - 이유: 이미지 재빌드 횟수 감소, 개발 반복 속도 개선.
- 데이터 보존이 필요한 워크로드(DB, 업로드 파일)
- 권장: named volume 또는 명시적 bind mount 설계.
- 이유: 컨테이너 수명과 데이터 수명을 분리해야 백업/복구가 쉬움.
운영 체크리스트도 함께 두면 좋습니다.
docker ps -a
docker logs --tail 100 <container>
docker inspect <container>
위 3종은 “실행 여부, 애플리케이션 메시지, 런타임 설정”을 빠르게 분리 진단하는 최소 세트입니다.
오늘의 결론
docker run은 단순 시작 명령이 아니라, 컨테이너의 성격을 선언하는 설계 문장입니다. -it, -d, --rm, -p, -v를 목적에 맞게 쓰면 디버깅은 빨라지고, 운영은 예측 가능해지며, 데이터 사고를 크게 줄일 수 있습니다.
오늘은 옵션 하나하나를 외우는 것보다, “내가 지금 하려는 작업이 임시 실행인지, 서비스 운영인지, 데이터 영속이 필요한지”를 먼저 판단하는 습관을 가져가시면 됩니다.
연습문제
alpine:3.20으로-it와--rm을 함께 사용해 셸 진입 후 간단한 파일을 만들고 종료하세요. 종료 후 컨테이너가 남는지 확인하고 결과를 설명해보세요.nginx:alpine을-d -p 8082:80으로 실행한 뒤 브라우저 또는curl로 접속을 확인하세요. 그다음-p없이 다시 실행했을 때 차이를 비교해보세요.- 로컬 폴더를
-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 - 실행 순서:
-it로 대화형 실행-d로 백그라운드 실행--rm동작 차이 확인-p포트 공개 확인-v마운트 반영 확인
- 재현 체크 포인트:
- 백그라운드 실행 후 로그/상태 조회 가능 여부
- 포트 매핑 유무에 따른 접근 차이
- 마운트 경로 수정 시 즉시 반영 여부
- 자동 삭제 옵션 사용 시 흔적 보존 여부