Docker 컨테이너가 Restarting 상태에서 벗어나지 못하는 이유

작성자

카테고리:

Docker 컨테이너를 실행했는데 정상적으로 서비스가 시작되지 않고 Restarting (1), Restarting (137) 같은 상태가 반복되는 경우가 있습니다. 컨테이너를 재시작해도 잠시 실행되는 듯하다가 종료되고, 다시 시작되는 현상이 계속된다면 단순한 Docker 실행 오류보다 컨테이너 내부 프로세스의 종료 원인을 먼저 확인해야 합니다.

특히 웹서버, 데이터베이스, API 서버를 Docker로 운영하는 환경에서는 컨테이너 재시작이 반복되면서 웹사이트 접속 장애, API 응답 실패, 데이터베이스 연결 오류가 함께 발생할 수 있습니다.

Docker 컨테이너가 Restarting 상태에서 벗어나지 못하는 이유는 애플리케이션 실행 실패, 잘못된 환경변수, 파일 권한 문제, 메모리 부족, 시작 명령어 오류, 외부 서비스 연결 실패 등 다양합니다. 여기에 Docker의 재시작 정책이 적용되면 종료된 컨테이너가 계속 다시 시작될 수 있습니다.

이번 글에서는 컨테이너 상태 확인 → 로그 분석 → 종료 코드 점검 → 메모리 및 설정 확인 → 재시작 정책 분석 → 안전한 복구 순서로 문제의 원인을 찾는 방법을 설명합니다.

Docker Restarting 핵심 명령어 한눈에 보기

다음 명령어에서는 문제가 발생한 컨테이너 이름을 my-container로 가정합니다. 실제 환경에서는 컨테이너 이름 또는 ID로 변경해야 합니다.

점검 목적 명령어
전체 컨테이너 상태 docker ps -a
최근 로그 확인 docker logs --tail 100 my-container
종료 코드 확인 docker inspect -f '{{.State.ExitCode}}' my-container
메모리 강제 종료 여부 docker inspect -f '{{.State.OOMKilled}}' my-container
재시작 횟수 docker inspect -f '{{.RestartCount}}' my-container
재시작 정책 docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' my-container
컨테이너 상세 정보 docker inspect my-container
Docker 이벤트 확인 docker events --since 10m
Compose 설정 검사 docker compose config

가장 중요한 것은 Restarting 상태 자체가 아니라 컨테이너 내부의 주 프로세스가 왜 종료되는지 확인하는 것입니다. 재시작 정책만 변경해도 반복 재시작은 멈출 수 있지만, 애플리케이션 오류까지 해결되는 것은 아닙니다.

Docker 컨테이너 Restarting 상태 반복 원인과 docker logs inspect 종료 코드 확인 방법
Docker Restarting 상태가 반복될 때 컨테이너 로그, 종료 코드, 메모리 및 재시작 정책을 점검하는 방법

1. Docker 컨테이너가 Restarting 상태를 반복하는 이유

Docker 컨테이너는 일반적으로 컨테이너 내부의 주 프로세스가 실행되는 동안 유지됩니다.

하지만 주 프로세스가 오류로 종료되거나 정상 종료되더라도 설정된 재시작 정책의 조건에 해당하면 Docker가 컨테이너를 다시 시작할 수 있습니다.

예를 들어 다음 명령어를 실행했다고 가정하겠습니다.

docker ps -a

출력 예시는 다음과 같습니다.

CONTAINER ID   IMAGE        STATUS
a1b2c3d4e5f6   myapp:latest Restarting (1) 5 seconds ago

여기서 Restarting (1)의 숫자 1은 일반적으로 마지막 종료 코드가 1이었다는 의미입니다.

Docker가 오류를 자동으로 수정했다는 의미가 아니라 종료된 컨테이너를 다시 시작하려고 시도하는 상태입니다.

주요 발생 원인은 다음과 같습니다.

  • 애플리케이션 오류: 프로그램이 시작 직후 예외를 발생시키고 종료되는 경우
  • 환경변수 누락: 데이터베이스 주소, 비밀번호, API 키 등의 설정이 잘못된 경우
  • 시작 명령어 오류: ENTRYPOINT 또는 CMD에서 지정한 실행 파일이나 스크립트가 잘못된 경우
  • 파일 권한 문제: 애플리케이션이 필요한 파일이나 디렉터리에 접근하지 못하는 경우
  • 메모리 부족: 프로세스가 OOM으로 강제 종료되는 경우
  • 외부 서비스 연결 실패: 데이터베이스 등 필수 서비스에 연결하지 못해 애플리케이션이 종료되는 경우
  • 재시작 정책: 종료된 컨테이너를 계속 다시 시작하도록 설정된 경우

2. docker logs로 컨테이너 종료 원인 확인하기

Docker Restarting 오류가 발생했을 때 가장 먼저 확인해야 할 명령어는 docker logs입니다.

docker logs --tail 100 my-container

최근 로그를 확인하면 애플리케이션이 종료되기 직전에 어떤 오류가 발생했는지 파악할 수 있습니다.

예를 들어 다음과 같은 메시지가 나타날 수 있습니다.

Error: Missing required environment variable: DB_HOST
Application startup failed
Process exited with code 1

위 로그는 데이터베이스 접속에 필요한 환경변수가 없어 애플리케이션이 시작하지 못한 사례입니다.

또 다른 예시는 다음과 같습니다.

Permission denied: /app/start.sh

이 경우에는 시작 스크립트의 실행 권한, 파일 소유권 또는 컨테이너 실행 사용자를 확인해야 합니다.

최근 로그를 실시간으로 확인하려면 다음 명령어를 사용할 수 있습니다.

docker logs -f --tail 50 my-container

다만 컨테이너가 매우 짧은 간격으로 종료된다면 로그가 빠르게 반복될 수 있습니다. 필요한 경우 시간 범위를 지정해 확인하는 것이 좋습니다.

docker logs --since 10m my-container

로그가 비어 있다면 프로그램이 표준 출력이나 표준 오류로 메시지를 남기지 않았거나, 실행 자체가 시작되지 않았을 가능성도 확인해야 합니다.

3. Exit Code로 컨테이너 종료 이유 분석하기

컨테이너가 종료된 이유를 분석할 때 종료 코드(Exit Code)는 중요한 단서가 됩니다.

다음 명령어로 마지막 종료 코드를 확인할 수 있습니다.

docker inspect \
  -f '{{.State.ExitCode}}' \
  my-container

대표적인 종료 코드의 의미는 다음과 같습니다.

종료 코드 일반적인 의미 확인할 항목
0 정상 종료 프로그램이 의도보다 일찍 종료되는지 확인
1 일반적인 실행 오류 애플리케이션 로그 및 설정 확인
126 명령어 실행 불가 실행 권한 및 파일 형식 확인
127 명령어를 찾을 수 없음 실행 파일 경로 및 이미지 구성 확인
137 SIGKILL에 의한 종료일 수 있음 OOM, 강제 종료 및 시스템 로그 확인
143 SIGTERM에 의한 종료일 수 있음 서비스 종료 및 재시작 이력 확인

종료 코드만으로 정확한 원인을 확정할 수는 없습니다. 예를 들어 Exit Code 137은 OOM으로 발생할 수 있지만 사용자가 강제 종료했거나 외부에서 SIGKILL을 보낸 경우에도 나타날 수 있습니다.

따라서 종료 코드와 함께 로그, OOMKilled 상태, Docker 이벤트를 확인해야 합니다.

4. OOMKilled와 메모리 부족 확인하기

Docker 컨테이너가 실행 직후 종료되거나 부하가 증가할 때마다 재시작된다면 메모리 부족 문제를 확인해야 합니다.

다음 명령어로 Docker가 기록한 OOM 종료 여부를 확인합니다.

docker inspect \
  -f '{{.State.OOMKilled}}' \
  my-container

결과가 true라면 컨테이너가 메모리 부족으로 종료됐다는 중요한 단서가 됩니다.

현재 설정된 메모리 제한도 확인할 수 있습니다.

docker inspect \
  -f '{{.HostConfig.Memory}}' \
  my-container

출력되는 값은 바이트 단위이며, 0은 Docker의 해당 메모리 제한이 설정되지 않았다는 의미입니다.

다만 컨테이너에 별도 제한이 없더라도 호스트 시스템이나 상위 cgroup의 메모리 제약으로 프로세스가 종료될 수 있습니다.

실행 중인 컨테이너의 메모리 사용량은 다음 명령어로 확인할 수 있습니다.

docker stats --no-stream

호스트 시스템의 메모리 상태도 함께 점검합니다.

free -h

OOM 관련 커널 로그는 다음 명령어로 확인할 수 있습니다.

sudo journalctl -k -b | grep -Ei 'out of memory|oom|killed process'

OOM이 확인됐다면 메모리 제한을 무조건 늘리기보다 애플리케이션의 메모리 사용량과 누수 가능성을 먼저 분석해야 합니다.

5. ENTRYPOINT와 CMD 설정 오류 확인하기

Docker 이미지에서 실행할 명령어가 잘못 지정되어 있으면 컨테이너가 시작 직후 종료될 수 있습니다.

Dockerfile에는 일반적으로 다음과 같이 실행 명령어를 지정합니다.

FROM python:3.12-slim

WORKDIR /app
COPY . /app

CMD ["python", "app.py"]

여기서 app.py 파일이 없거나 실행 과정에서 오류가 발생한다면 주 프로세스가 종료될 수 있습니다.

현재 컨테이너에 설정된 시작 명령어를 확인합니다.

docker inspect \
  -f 'Entrypoint={{json .Config.Entrypoint}} Cmd={{json .Config.Cmd}}' \
  my-container

시작 스크립트를 사용하는 경우에는 파일 실행 권한과 Shebang 설정도 확인해야 합니다.

#!/bin/sh
exec python /app/app.py

위 예시에서 exec는 셸 프로세스를 애플리케이션 프로세스로 교체하는 데 사용됩니다.

컨테이너 시작 명령어를 수정해야 한다면 실행 중인 컨테이너 내부만 임시로 변경하기보다 Dockerfile이나 Compose 설정을 수정하고 이미지를 다시 빌드하는 것이 일반적입니다.

6. 환경변수와 데이터베이스 연결 오류 확인하기

애플리케이션이 시작할 때 데이터베이스 연결을 필수로 요구한다면 DB 서버가 준비되지 않았거나 연결 설정이 잘못됐을 때 컨테이너가 종료될 수 있습니다.

예를 들어 다음과 같은 오류가 발생할 수 있습니다.

Connection refused
Database connection failed
Application startup aborted

이 경우 확인할 사항은 다음과 같습니다.

  • 데이터베이스 컨테이너가 정상 실행 중인지
  • 데이터베이스 호스트 이름과 포트가 정확한지
  • 두 컨테이너가 올바른 Docker 네트워크에 연결되어 있는지
  • 데이터베이스가 연결을 받을 준비가 완료됐는지
  • 애플리케이션에 연결 재시도 기능이 있는지

컨테이너 환경변수는 다음 명령어로 확인할 수 있습니다.

docker inspect \
  -f '{{json .Config.Env}}' \
  my-container

주의할 점은 이 명령어가 비밀번호나 API 키 같은 민감한 값을 출력할 수 있다는 것입니다. 결과를 외부에 공유할 때는 반드시 비밀 정보를 제거해야 합니다.

데이터베이스 연결이 실패한다고 해서 반드시 Docker 네트워크 자체에 문제가 있는 것은 아닙니다. DB 서비스가 아직 초기화 중이거나 애플리케이션의 시작 순서가 맞지 않는 경우도 있습니다.

7. Docker Restart Policy가 무한 재시작에 미치는 영향

Docker는 컨테이너가 종료됐을 때 자동으로 다시 시작하도록 설정할 수 있습니다.

현재 재시작 정책을 확인합니다.

docker inspect \
  -f '{{.HostConfig.RestartPolicy.Name}}' \
  my-container

대표적인 재시작 정책은 다음과 같습니다.

정책 동작
no 자동 재시작하지 않음
always 종료된 컨테이너를 자동 재시작하도록 관리
unless-stopped 명시적으로 중지된 경우를 제외하고 자동 재시작
on-failure 0이 아닌 종료 코드로 종료되면 재시작

예를 들어 restart: always가 설정되어 있고 애플리케이션이 시작할 때마다 오류로 종료된다면 컨테이너가 반복적으로 Restarting 상태에 들어갈 수 있습니다.

재시작 횟수는 다음 명령어로 확인할 수 있습니다.

docker inspect \
  -f '{{.RestartCount}}' \
  my-container

재시작 횟수가 계속 증가한다면 주 프로세스가 반복 종료되고 있는지 확인해야 합니다.

장애 분석을 위해 자동 재시작을 일시적으로 중단해야 한다면 서비스 영향과 복구 절차를 확인한 후 다음 명령어를 사용할 수 있습니다.

docker update --restart=no my-container

이 명령어는 컨테이너의 재시작 정책을 변경합니다. 그러나 애플리케이션의 오류 자체를 해결하지는 않습니다.

또한 Docker Compose로 관리하는 서비스라면 Compose 파일의 restart 설정도 함께 확인해야 합니다.

8. Docker Compose에서 Restarting 오류 확인하기

Docker Compose 환경에서는 여러 컨테이너가 서로 의존하는 경우가 많습니다.

서비스 상태를 확인합니다.

docker compose ps -a

특정 서비스의 로그를 확인합니다.

docker compose logs --tail 100 app

Compose 설정 파일의 문법과 최종 적용 구성을 확인합니다.

docker compose config

단, Compose 설정 출력에는 환경변수 값 등 민감한 정보가 포함될 수 있으므로 공유 시 주의해야 합니다.

다음은 데이터베이스와 애플리케이션을 함께 사용하는 Compose 설정의 예시입니다.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  app:
    image: myapp:latest
    depends_on:
      db:
        condition: service_healthy
    restart: on-failure

위 설정은 DB 서비스의 헬스체크가 통과한 뒤 애플리케이션 시작을 진행하도록 구성한 예시입니다. 실제 환경에서는 데이터베이스 사용자와 헬스체크 명령어를 맞게 조정해야 합니다.

다만 depends_on과 헬스체크를 설정하더라도 서비스가 실행된 이후의 장애까지 자동으로 해결하는 것은 아닙니다. 애플리케이션에도 적절한 연결 재시도 처리가 필요합니다.

9. Healthcheck 실패와 Restarting 상태 구분하기

Docker에서 unhealthy 상태가 나타났다고 해서 반드시 컨테이너가 자동으로 재시작되는 것은 아닙니다.

일반적인 Docker Engine 환경에서는 헬스체크 실패만으로 재시작 정책이 실행되지는 않습니다.

컨테이너가 Restarting 상태라면 주 프로세스 종료와 재시작 정책을 우선 확인해야 합니다.

헬스체크 상태는 다음 명령어로 확인할 수 있습니다.

docker inspect \
  -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}no-healthcheck{{end}}' \
  my-container

헬스체크 로그가 필요한 경우에는 다음 명령어를 사용할 수 있습니다.

docker inspect \
  -f '{{json .State.Health}}' \
  my-container

단, 별도의 오케스트레이션 도구나 감시 시스템이 헬스체크 결과를 기반으로 컨테이너를 재생성하도록 구성되어 있다면 해당 시스템의 정책도 확인해야 합니다.

10. Docker 컨테이너 Restarting 상태 해결 순서

반복 재시작 문제는 무작정 컨테이너를 삭제하거나 이미지를 다시 설치하기보다 원인을 순서대로 확인하는 것이 중요합니다.

먼저 컨테이너 상태와 로그를 확인합니다.

docker ps -a
docker logs --tail 100 my-container

다음으로 종료 코드와 메모리 종료 여부를 확인합니다.

docker inspect \
  -f 'ExitCode={{.State.ExitCode}} OOMKilled={{.State.OOMKilled}}' \
  my-container

재시작 횟수와 정책을 확인합니다.

docker inspect \
  -f 'RestartCount={{.RestartCount}} RestartPolicy={{.HostConfig.RestartPolicy.Name}}' \
  my-container

이후 로그에서 확인된 오류에 따라 환경변수, 시작 명령어, 파일 권한, 메모리 제한, 외부 서비스 연결을 점검합니다.

원인을 수정했다면 컨테이너를 다시 실행하고 상태와 로그가 안정적으로 유지되는지 확인합니다.

docker ps -a
docker logs --tail 50 my-container

Docker Compose 환경이라면 설정 변경 후 서비스 재생성이 필요한지 확인해야 합니다. 이미지나 환경변수 설정을 변경했다면 기존 컨테이너를 단순히 재시작하는 것만으로는 변경 사항이 반영되지 않을 수 있습니다.

실전 점검 명령어 모음

다음 명령어를 순서대로 실행하면 Docker Restarting 오류의 주요 원인을 확인할 수 있습니다.

# 1. 컨테이너 상태 확인
docker ps -a

# 2. 최근 오류 로그 확인
docker logs --tail 100 my-container

# 3. 종료 코드 확인
docker inspect -f '{{.State.ExitCode}}' my-container

# 4. OOM 종료 여부 확인
docker inspect -f '{{.State.OOMKilled}}' my-container

# 5. 재시작 횟수 확인
docker inspect -f '{{.RestartCount}}' my-container

# 6. 재시작 정책 확인
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' my-container

# 7. 시작 명령어 확인
docker inspect -f 'Entrypoint={{json .Config.Entrypoint}} Cmd={{json .Config.Cmd}}' my-container

# 8. 최근 Docker 이벤트 확인
docker events --since 10m --until 0s

# 9. 호스트 메모리 확인
free -h

# 10. OOM 관련 로그 확인
sudo journalctl -k -b | grep -Ei 'out of memory|oom|killed process'

위 명령어에서 my-container는 실제 컨테이너 이름이나 ID로 변경해야 합니다.

Docker 이벤트와 로그에는 서비스 구성에 따라 민감한 정보가 포함될 수 있으므로 외부에 공유하기 전 확인하는 것이 좋습니다.

정리

Docker 컨테이너가 Restarting 상태에서 벗어나지 못하는 이유는 대부분 주 프로세스가 종료되고 재시작 정책에 따라 다시 실행되는 과정이 반복되기 때문입니다.

애플리케이션 실행 오류, 환경변수 누락, 시작 명령어 문제, 파일 권한 오류, 메모리 부족, 데이터베이스 연결 실패 등이 원인이 될 수 있습니다.

먼저 docker logs로 오류 메시지를 확인하고, docker inspect로 종료 코드와 OOMKilled 상태, 재시작 정책을 분석해야 합니다.

특히 Exit Code 137이 나타난다면 OOM 여부를 추가로 확인해야 하며, Exit Code 1이 반복된다면 애플리케이션 로그와 설정을 우선 살펴보는 것이 좋습니다.

핵심 점검 순서는 컨테이너 상태 확인 → 로그 분석 → 종료 코드 점검 → 메모리 및 실행 설정 확인 → 재시작 정책 분석 → 원인 수정 후 정상 실행 확인입니다.

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다