Docker 실무 필수 명령어 — 컨테이너 생명주기 완전 정복

Docker 실무 필수 명령어 — 컨테이너 생명주기 완전 정복

docker run만 알면 된다고? 운영 서버에서는 그 다음이 진짜 시작이다.

Docker를 처음 배울 때는 docker run으로 끝난다. 하지만 실제 운영 환경(Production)이나 협업하는 개발 환경에서는 컨테이너를 언제, 어떻게 띄우고 지우느냐가 훨씬 중요하다. 컨테이너가 예기치 않게 죽었을 때 어떻게 다시 살릴지, 백그라운드에서 돌아가는 녀석들에게 어떻게 명령을 내릴지 알아야 진정한 "Docker 실무"를 한다고 할 수 있다.

이 글에서는 단순히 매뉴얼에 나오는 명령어 나열이 아니라, 실무에서 복사해서 바로 쓰는 패턴 위주로 컨테이너 생명주기를 완벽하게 다룬다.

컨테이너를 다루는 기본 흐름

Docker 컨테이너의 라이프사이클은 크게 5단계로 나눌 수 있다.

  1. 생성 및 실행 (run): 이미지를 기반으로 격리된 환경을 만든다.
  2. 모니터링 (ps): 지금 누가 돌아가고 있는지, 포트 충돌은 없는지 확인한다.
  3. 내부 침투 (exec): 띄워둔 환경에 직접 들어가서 설정값을 바꾸거나 디버깅한다.
  4. 중지 및 제어 (stop / restart): 설정을 적용하거나 리소스를 비우기 위해 잠시 멈춘다.
  5. 정리 (rm): 다 쓴 컨테이너를 디스크에서 완전히 날린다.

이제 각 단계별로 가장 많이 쓰는 옵션과 팁을 살펴보자.

docker run — 컨테이너 생성과 실행

컨테이너를 만들고 띄우는 가장 핵심적인 명령어다. docker createdocker start를 합친 것과 같다. 실무에서는 옵션 없이 생으로 쓰는 일이 거의 없다.

자주 쓰는 옵션 조합

가장 흔하게 쓰이는 옵션들을 모은 '국밥' 같은 예제다.

docker run -d \
  --name my-redis \
  --restart unless-stopped \
  -p 6379:6379 \
  -v redis-data:/data \
  -e REDIS_PASSWORD=my_secret_pass \
  redis:7.2 \
  redis-server --requirepass my_secret_pass
  • -d (detach): 백그라운드에서 실행한다. 터미널을 닫아도 컨테이너는 계속 돈다.
  • --name: 컨테이너에 예쁜 이름을 지어준다. 안 주면 elegant_turing 같은 랜덤 이름이 붙어 나중에 찾기 힘들다.
  • --restart unless-stopped: 도커 데몬이 켜질 때, 내가 명시적으로 중지(stop)하지 않은 컨테이너는 무조건 다시 살려낸다. 운영 서버 필수 옵션이다.
  • -p [호스트포트]:[컨테이너포트]: 포트를 연결한다. 외부에서 이 서비스에 접근하려면 필수다.
  • -v [볼륨/경로]:[컨테이너경로]: 데이터를 보존한다. 컨테이너가 지워져도 볼륨에 남는다.
  • -e: 컨테이너 내부에 환경 변수를 주입한다. 비밀번호나 설정값을 넘길 때 쓴다.

쓰고 버리는 일회용 컨테이너 (--rm)

테스트 목적으로 한 번 띄우고 바로 버릴 때는 --rm을 쓴다. 컨테이너가 중지되면 찌꺼기를 남기지 않고 알아서 삭제된다.

docker run --rm -it ubuntu:22.04 /bin/bash
  • -it: -i(interactive)와 -t(tty)의 조합이다. 컨테이너 안에서 내 터미널처럼 키보드 입력을 주고받기 위해 쓴다. 주로 /bin/bash/bin/sh 와 같이 쓴다.

리소스 제한 — 운영 서버에서 필수

운영 서버에 여러 컨테이너를 올리는데, 한 녀석이 메모리 누수로 서버 전체를 다운시키면 안 된다.

docker run -d \
  --name heavy-app \
  --memory 2g \
  --cpus 1.5 \
  my-app:latest
  • --memory 2g: 이 컨테이너는 최대 2GB 메모리만 쓸 수 있다. 넘으면 OOM(Out of Memory)으로 죽는다.
  • --cpus 1.5: 최대 1.5개의 코어만 사용할 수 있도록 제한한다.

docker exec — 실행 중인 컨테이너에 명령 보내기

컨테이너가 잘 돌아가는 것 같은데, 데이터베이스에 제대로 연결이 되는지, 설정 파일이 잘 들어갔는지 확인하고 싶을 때 쓴다. ssh로 접속하는 느낌과 비슷하다.

디버깅 실전 패턴: 내부 접속하기

가장 많이 쓰는 패턴이다. 쉘을 열어서 안으로 쏙 들어간다.

# bash가 있는 컨테이너 (Ubuntu, Debian 기반)
docker exec -it my-nginx /bin/bash

# bash가 없는 가벼운 컨테이너 (Alpine Linux 기반)
docker exec -it my-nginx /bin/sh

디버깅 실전 패턴: 밖에서 단일 명령만 실행하기

굳이 안으로 들어가지 않고, 밖에서 결과만 쏙 빼오고 싶을 때 쓴다. 스크립트 짤 때 유용하다.

# Nginx 설정 파일 문법 검사만 해보고 싶을 때
docker exec my-nginx nginx -t

# 컨테이너 안의 환경 변수 값만 보고 싶을 때
docker exec my-nginx env | grep DATABASE_URL

docker stop / restart / kill — 컨테이너 제어

설정 파일을 바꿨거나, 리소스 점유율이 너무 높아져서 내렸다가 올려야 할 때가 있다.

정상적인 중지와 강제 종료의 차이

docker stop my-app

stop은 컨테이너 안의 1번 프로세스에게 "이제 그만 종료해줄래?" 하고 정중하게 SIGTERM 시그널을 보낸다. 앱은 열어둔 파일을 닫고, 진행 중인 요청을 처리할 시간을 갖는다. (기본 10초)

docker kill my-app

kill은 얄짤없다. SIGKILL 시그널을 보내 프로세스의 모가지를 당장 비틀어버린다. 앱이 정상적으로 종료 처리를 못 하므로 데이터 유실의 위험이 있다. stop이 너무 오래 걸리거나 응답이 없을 때만 최후의 수단으로 쓴다.

타임아웃 조정하기

앱이 정상 종료(Graceful Shutdown)하는 데 10초보다 오래 걸린다면, 강제로 죽기 전에 충분한 시간을 줘야 한다.

docker stop --time 30 my-app

docker rm / docker container prune — 정리

다 쓴 컨테이너는 디스크만 차지한다. 지워주자.

# 특정 컨테이너 삭제
docker rm my-old-app

# 실행 중인 컨테이너 강제로 삭제하기 (stop + rm)
docker rm -f my-running-app

운영하다 보면 중지된 채 방치된 컨테이너가 수십 개씩 쌓인다. 하나씩 지우기 귀찮을 때 쓰는 '마법의 명령어'가 있다.

docker container prune

중지된(Exited) 모든 컨테이너를 일괄 삭제한다. (실행 중인 녀석은 건드리지 않는다)

docker ps — 상태 확인의 시작점

지금 우리 집에 누가 살고 있는지 확인하는 명령어다.

docker ps      # 실행 중인 컨테이너만 보기
docker ps -a   # 중지된 컨테이너까지 전부 보기 (all)

실무에서 유용한 팁: 필터링과 포맷팅

컨테이너가 수십 개 떠 있으면 docker ps 결과가 너무 길고 터미널에서 줄바꿈이 깨져 보기 힘들다.

# 이름에 'web'이 들어간 컨테이너만 찾기
docker ps --filter "name=web"

# 터미널 창에 딱 맞게, 필요한 정보만 예쁘게 뽑아보기
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

📝 정리

운영 서버에 접속했을 때 가장 먼저 치게 되는 명령어들을 알아보았다.

  • [x] docker run: 데몬처럼 띄울 땐 -d, 재시작은 --restart unless-stopped
  • [x] docker exec: 내부 점검이 필요할 땐 -it [컨테이너명] /bin/bash
  • [x] docker stop: 강제 종료하는 kill 대신 안전하게 멈출 때
  • [x] docker rm / prune: 디스크가 꽉 차기 전에 중지된 녀석들 청소하기
  • [x] docker ps: 누가 떠 있는지, 포트는 어디로 열려 있는지 확인하기

컨테이너를 올리고 내리는 법을 익혔다면, 다음은 "왜 내 컨테이너가 죽었는지" 범인을 찾아내는 방법을 배울 차례다. 2편(로그·모니터링·트러블슈팅)에서 docker logsdocker stats를 활용한 장애 대응법을 다뤄보자.