장시간 실행 Docker 컨테이너 정상 종료 방법

DockerBeginner
지금 연습하기

소개

Docker 컨테이너는 다양한 애플리케이션과 서비스를 실행하는 데 널리 사용되지만, 장시간 실행되는 컨테이너의 수명주기를 관리하는 것은 어려울 수 있습니다. 이 튜토리얼에서는 장시간 실행되는 Docker 컨테이너를 정상적으로 종료하는 과정을 안내하여 원활한 전환을 보장하고 잠재적인 데이터 손실이나 애플리케이션 문제를 방지합니다.

Docker 컨테이너 수명주기 이해

Docker 컨테이너는 개발자가 애플리케이션을 효과적으로 관리하기 위해 이해해야 하는 명확한 수명주기를 가지고 있습니다. 이 섹션에서는 Docker 컨테이너 수명주기의 개요를 제공하며, 컨테이너가 이동할 수 있는 다양한 상태와 이 수명주기를 뒷받침하는 주요 개념을 설명합니다.

Docker 컨테이너 상태

Docker 컨테이너는 수명 동안 여러 가지 상태에 있을 수 있습니다.

  1. Created: 컨테이너는 생성되었지만 시작되지 않았습니다.
  2. Running: 컨테이너는 현재 주 프로세스를 실행 중입니다.
  3. Paused: 컨테이너의 주 프로세스가 일시 중지되었지만, 컨테이너는 여전히 실행 중입니다.
  4. Stopped: 컨테이너의 주 프로세스가 중지되었습니다.
  5. Restarting: 컨테이너가 현재 다시 시작 중입니다.
  6. Exited: 컨테이너가 중지되었고 주 프로세스가 종료되었습니다.

이러한 상태는 컨테이너에 대해 수행할 수 있는 작업과 예상되는 동작을 결정하기 때문에 이해하는 것이 중요합니다.

graph LR
    Created --> Running
    Running --> Paused
    Paused --> Running
    Running --> Stopped
    Stopped --> Running
    Stopped --> Exited

Docker 컨테이너 수명주기 이벤트

컨테이너 상태 외에도 Docker 컨테이너 수명주기 동안 발생하는 몇 가지 주요 이벤트가 있습니다.

  1. Create: 새로운 컨테이너가 생성됩니다.
  2. Start: 컨테이너의 주 프로세스가 시작됩니다.
  3. Stop: 컨테이너의 주 프로세스가 중지됩니다.
  4. Restart: 컨테이너가 수동 또는 자동으로 다시 시작됩니다.
  5. Pause/Unpause: 컨테이너의 주 프로세스가 일시 중지되거나 일시 중지 해제됩니다.
  6. Kill: 컨테이너의 주 프로세스가 강제 종료됩니다.
  7. Delete: 컨테이너가 시스템에서 제거됩니다.

이러한 수명주기 이벤트를 이해하는 것은 Docker 컨테이너의 동작을 관리하고 자동화하는 데 필수적입니다.

Docker CLI 를 사용한 컨테이너 수명주기 처리

Docker CLI 는 컨테이너 수명주기와 상호 작용하기 위한 여러 명령어를 제공합니다.

  • docker create: 새로운 컨테이너를 생성합니다.
  • docker start: 중지된 컨테이너를 시작합니다.
  • docker stop: 실행 중인 컨테이너를 중지합니다.
  • docker restart: 컨테이너를 다시 시작합니다.
  • docker pause: 실행 중인 컨테이너를 일시 중지합니다.
  • docker unpause: 일시 중지된 컨테이너를 일시 중지 해제합니다.
  • docker kill: 실행 중인 컨테이너를 강제로 중지합니다.
  • docker rm: 컨테이너를 제거합니다.

이러한 명령어를 통해 Docker 컨테이너의 수명주기를 프로그래밍 방식으로 관리하고 컨테이너 관리와 관련된 다양한 작업을 자동화할 수 있습니다.

장시간 실행 컨테이너의 정상 종료

장시간 실행되는 Docker 컨테이너를 다룰 때는 컨테이너가 종료되기 전에 컨테이너의 주 프로세스가 필요한 정리 또는 종료 작업을 수행할 수 있도록 정상적으로 종료하는 것이 중요합니다. 이 섹션에서는 장시간 실행되는 Docker 컨테이너를 정상적으로 종료하는 전략을 살펴봅니다.

SIGTERM 신호 이해

Docker 컨테이너를 정상적으로 종료하는 주요 메커니즘은 컨테이너의 주 프로세스에 SIGTERM 신호를 보내는 것입니다. 이 신호는 프로세스가 종료 프로세스를 시작하고 필요한 정리 작업을 수행해야 함을 알립니다.

기본적으로 docker stop 명령을 실행하면 Docker 는 컨테이너의 주 프로세스에 SIGTERM 신호를 보내고 프로세스가 종료될 때까지 기본 시간 제한 (일반적으로 10 초) 을 기다립니다. 프로세스가 시간 제한 내에 종료되지 않으면 Docker 는 SIGKILL 신호를 보내 프로세스를 강제 종료합니다.

종료 동작 사용자 지정

장시간 실행되는 Docker 컨테이너의 종료 동작을 사용자 지정하려면 다음 옵션을 사용할 수 있습니다.

  1. --stop-signal: docker stop 명령 중 컨테이너의 주 프로세스에 보낼 대체 신호를 지정합니다. 예를 들어, --stop-signal=SIGINT는 기본 SIGTERM 대신 SIGINT 신호를 보냅니다.

  2. --stop-timeout: SIGKILL 신호를 보내기 전에 컨테이너의 주 프로세스가 종료될 때까지 기다릴 시간 (초) 을 지정합니다. 예를 들어, --stop-timeout=30은 프로세스가 강제 종료되기 전에 30 초 동안 종료될 수 있도록 합니다.

장시간 실행되는 컨테이너를 시작할 때 이러한 옵션을 사용하는 방법의 예는 다음과 같습니다.

docker run -d --name my-app --stop-signal=SIGINT --stop-timeout=60 my-app:latest

이 명령은 이름이 my-app인 장시간 실행 컨테이너를 시작하고, docker stop 명령이 실행되면 컨테이너의 주 프로세스에 SIGINT 신호를 보내고 SIGKILL 신호를 보내기 전에 최대 60 초 동안 종료될 때까지 기다립니다.

애플리케이션에서 정상 종료 구현

장시간 실행되는 Docker 컨테이너가 정상적으로 종료되도록 하려면 애플리케이션이 SIGTERM(또는 대체 신호) 를 처리하고 종료되기 전에 필요한 정리 작업을 수행하도록 설계하는 것이 중요합니다. 이 작업에는 다음이 포함될 수 있습니다.

  • 메모리 데이터를 영구 저장소에 저장
  • 네트워크 연결 또는 데이터베이스 연결 닫기
  • 로그 또는 기타 출력 플러시
  • 기타 애플리케이션별 정리 작업 수행

애플리케이션에 이러한 신호 처리를 구현하면 컨테이너가 제어되고 예측 가능한 방식으로 종료되어 데이터 손실이나 기타 문제를 최소화할 수 있습니다.

정상 종료 전략

장시간 실행되는 Docker 컨테이너를 정상적으로 종료할 때는 원활하고 제어된 종료 프로세스를 보장하기 위해 여러 전략을 활용할 수 있습니다. 이 섹션에서는 정상 종료를 위한 주요 전략과 모범 사례를 살펴봅니다.

애플리케이션의 신호 처리

정상 종료를 위한 가장 중요한 전략 중 하나는 애플리케이션에서 신호 처리를 구현하는 것입니다. 이는 애플리케이션이 종료되기 전에 SIGTERM (또는 대체 신호) 를 감지하고 필요한 정리 작업을 수행하는 코드를 작성하는 것을 포함합니다.

Node.js 애플리케이션에서 신호 처리를 구현하는 예는 다음과 같습니다.

process.on("SIGTERM", () => {
  console.log("SIGTERM 신호를 수신했습니다. 정상 종료 시작...");
  // 정리 작업 수행, 예:
  // - 메모리 데이터를 영구 저장소에 저장
  // - 네트워크 연결 또는 데이터베이스 연결 닫기
  // - 로그 또는 기타 출력 플러시
  // - 기타 애플리케이션별 정리 작업 수행
  console.log("정상 종료 완료, 프로세스 종료.");
  process.exit(0);
});

이러한 신호 처리를 구현하면 애플리케이션이 제어된 종료를 보장하여 데이터 손실이나 기타 문제 발생 위험을 최소화할 수 있습니다.

Healthcheck 및 Liveness Probe 사용

정상 종료를 위한 또 다른 전략은 Docker 의 내장 Health check 및 Liveness probe 기능을 사용하는 것입니다. 이러한 기능을 통해 컨테이너의 상태와 준비 상태를 확인하는 검사를 정의할 수 있습니다.

종료 프로세스 중에 이러한 probe 를 사용하여 컨테이너가 종료 중임을 Docker 에 알릴 수 있으며, Docker 는 컨테이너 제거 전에 종료가 완료될 때까지 기다릴 수 있습니다.

Docker 컨테이너에서 Health check 및 Liveness probe 를 구성하는 예는 다음과 같습니다.

## Dockerfile
FROM node:14-alpine
COPY . /app
WORKDIR /app
CMD ["node", "server.js"]
HEALTHCHECK --interval=5s --timeout=3s \
  CMD curl -f http://localhost:3000/healthz || exit 1
LABEL com.labex.shutdown.signal=SIGINT
LABEL com.labex.shutdown.timeout=60

이 예제에서 HEALTHCHECK 지시문은 컨테이너 웹 서버의 /healthz 엔드포인트를 확인하는 Health check 를 정의합니다. LABEL 지시문은 정상 종료에 사용할 신호 (SIGINT) 와 시간 제한 (60 초) 을 정의합니다.

종료 프로세스 중에 애플리케이션은 Health check 엔드포인트를 업데이트하여 종료가 진행 중임을 알릴 수 있으며, Docker 는 컨테이너 제거 전에 종료가 완료될 때까지 기다릴 수 있습니다.

오케스트레이션 프레임워크 활용

Kubernetes 또는 Docker Swarm 과 같은 오케스트레이션 프레임워크에서 Docker 컨테이너를 실행하는 경우 이러한 프레임워크의 내장 기능을 활용하여 정상 종료를 지원할 수 있습니다.

예를 들어, Kubernetes 에서 preStop 훅을 사용하여 컨테이너가 종료되기 전에 정리 작업을 수행하는 명령 또는 스크립트를 실행할 수 있습니다. 또한 terminationGracePeriodSeconds 필드를 사용하여 컨테이너가 정상적으로 종료될 수 있도록 주어지는 시간을 지정할 수 있습니다.

preStop 훅과 정상 종료 기간을 사용하여 Kubernetes 배포를 구성하는 예는 다음과 같습니다.

## kubernetes-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: my-app:latest
          ports:
            - containerPort: 3000
          lifecycle:
            preStop:
              exec:
                command: ["/app/shutdown.sh"]
          terminationGracePeriodSeconds: 60

이 예제에서 preStop 훅은 컨테이너가 종료되기 전에 필요한 모든 정리 작업을 수행하는 스크립트 (/app/shutdown.sh) 를 실행합니다. terminationGracePeriodSeconds 필드는 컨테이너가 강제 종료되기 전에 정상적으로 종료될 수 있도록 60 초를 제공합니다.

이러한 오케스트레이션 프레임워크 기능을 활용하면 컨테이너 종료 프로세스의 안정성과 예측성을 더욱 향상시킬 수 있습니다.

요약

이 튜토리얼에서는 Docker 컨테이너 수명 주기를 이해하고 장시간 실행되는 컨테이너를 정상적으로 종료하는 전략의 중요성을 배웠습니다. 제시된 모범 사례를 따르면 원활하고 안정적인 종료 프로세스를 보장하고 데이터 손실이나 애플리케이션 중단 위험을 최소화할 수 있습니다. 정상적인 컨테이너 종료 기술은 모든 Docker 개발자 또는 관리자에게 필수적인 기술입니다.