0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Docker】コンテナからサービスを監視して適宜再起動させたい

Posted at

概要

起動時たまに、うまく立ち上がらないコンテナを手動で再起動していたのですが、
それを自動化したくDooD(Docker outside of Docker)を試してみました。

DooD (Docker outside of Docker)とは?

ホストマシンのDockerプロセスを利用して、コンテナ内からDockerコマンドを実行する方法です。

⚠️ 注意
ホストの Docker ソケット(/var/run/docker.sock)を共有するため、
コンテナにホストの root 権限相当のアクセス権を与えるリスクがあります。
セキュリティ上の懸念がある環境では十分注意してください。

どうすればできる?

下記二つをコマンドを実行させたいコンテナに実施すれば、DooDが可能になります。

  • ホストマシンの/var/run/docker.sockをコンテナの/var/run/docker.sockへバインド
volumes:
  - /var/run/docker.sock:/var/run/docker.sock
  • コンテナへdocker-ce-cli docker-composeをインストール

お試し

構成

以下の二つのコンテナを作成し、DooDをお試しします。

  • container-monitor
    他のコンテナサービスを監視し、失敗しているコンテナを再起動します。
  • monitor-stub
    /healthへのGETリクエストに対してstatus 500を返すだけのAPI。

二つのコンテナは下記のdocker-compose.yamlで立ち上げます。

name: docker-monitor

services:
  container-monitor:
    build: ./container_monitor
    restart: unless-stopped
    depends_on:
      - monitor-stub
    environment:
      TARGET_SERVICE: monitor-stub
      HEALTHCHECK_URL: http://monitor-stub:8000/health
      SLEEP_SECONDS: "5"
      FAILURE_THRESHOLD: "3"
      INITIAL_WAIT_SECONDS: "60"
      SUCCESS_WINDOW_SECONDS: "60"
      CURL_MAX_TIME: "4"
      COMPOSE_FILE_PATH: /workspace/docker-compose.yml
      COMPOSE_PROJECT_NAME: docker-monitor
    volumes:
      - type: bind
        source: .
        target: /workspace
      - type: bind
        source: /var/run/docker.sock
        target: /var/run/docker.sock

  monitor-stub:
    build: ./monitor_stub
    restart: unless-stopped
    environment:
      return_status: "500"
      timeout_seconds: "3600"
    ports:
      - "8000:8000"

container_monitorのDockerfileは下記です。
※イメージにdockerを利用しているため、docker-ce-cliとdocker-composeのインストールなしにDooDが可能です。

FROM docker:27-cli

RUN apk add --no-cache curl bash

WORKDIR /workspace

COPY monitor.sh /usr/local/bin/monitor.sh

RUN chmod +x /usr/local/bin/monitor.sh

ENTRYPOINT ["/usr/local/bin/monitor.sh"]

container_monitorで実行するshスクリプトは下記です。
対象のコンテナサービスに対してGETリクエストを実行し3回失敗したらそのサービスをリスタートさせます。

#!/usr/bin/env sh
set -eu

TARGET_SERVICE="${TARGET_SERVICE:-monitor-stub}"
HEALTHCHECK_URL="${HEALTHCHECK_URL:-http://monitor-stub:8000/health}"
SLEEP_SECONDS="${SLEEP_SECONDS:-5}"
FAILURE_THRESHOLD="${FAILURE_THRESHOLD:-3}"
INITIAL_WAIT_SECONDS="${INITIAL_WAIT_SECONDS:-60}"
SUCCESS_WINDOW_SECONDS="${SUCCESS_WINDOW_SECONDS:-60}"
COMPOSE_FILE_PATH="${COMPOSE_FILE_PATH:-/workspace/docker-compose.yml}"
COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-}"


check_once() {
  curl --silent --show-error --fail --max-time "${CURL_MAX_TIME:-4}" "${HEALTHCHECK_URL}"
}

restart_service() {
  if [ -n "${COMPOSE_PROJECT_NAME}" ]; then
    docker compose --project-name "${COMPOSE_PROJECT_NAME}" -f "${COMPOSE_FILE_PATH}" restart "${TARGET_SERVICE}"
  else
    docker compose -f "${COMPOSE_FILE_PATH}" restart "${TARGET_SERVICE}"
  fi
}

echo "Waiting ${INITIAL_WAIT_SECONDS} seconds for dependent services..."
sleep "${INITIAL_WAIT_SECONDS}"
echo "Starting health checks for ${TARGET_SERVICE}"

fail_count=0
success_window_start=0


trap 'echo "Shutting down monitor"; exit 0' INT TERM

while true; do
  if check_once; then
    fail_count=0
    if [ "${success_window_start}" -eq 0 ]; then
      success_window_start="$(date +%s)"
    fi
  else
    fail_count=$((fail_count + 1))
    success_window_start=0
    echo "Health check failed (${fail_count}/${FAILURE_THRESHOLD}) for ${TARGET_SERVICE}"
    if [ "${fail_count}" -ge "${FAILURE_THRESHOLD}" ]; then
      echo "Restarting ${TARGET_SERVICE}"
      restart_service || echo "Failed to restart ${TARGET_SERVICE}"
      fail_count=0
      sleep "${SLEEP_SECONDS}"
      continue
    fi
  fi

  if [ "${success_window_start}" -ne 0 ]; then
    now="$(date +%s)"
    if [ $(( now - success_window_start )) -ge "${SUCCESS_WINDOW_SECONDS}" ]; then
      echo "No errors detected for ${SUCCESS_WINDOW_SECONDS} seconds. Stopping monitor."
      exit 0
    fi
  fi

  sleep "${SLEEP_SECONDS}"
done

実行

実際にコンテナを立ち上げてみます。
コンテナを立ち上げてそのログを追います

docker compose up -d
docker compose logs container-monitor -f

3回curlが失敗したのちmonitor-stubを再起動させていそうです。

container-monitor-1  | curl: (22) The requested URL returned error: 500
container-monitor-1  | Health check failed (1/3) for monitor-stub
container-monitor-1  | curl: (22) The requested URL returned error: 500
container-monitor-1  | Health check failed (2/3) for monitor-stub
container-monitor-1  | curl: (22) The requested URL returned error: 500
container-monitor-1  | Health check failed (3/3) for monitor-stub
container-monitor-1  | Restarting monitor-stub
container-monitor-1  |  Container docker-monitor-monitor-stub-1  Restarting
container-monitor-1  |  Container docker-monitor-monitor-stub-1  Started

コンテナのstatusを見ても再起動されていることが確認できます。

sudo docker compose ps
NAME                                 IMAGE                              COMMAND                  SERVICE             CREATED         STATUS              PORTS
docker-monitor-container-monitor-1   docker-monitor-container-monitor   "/usr/local/bin/moni…"   container-monitor   2 minutes ago   Up About a minute   
docker-monitor-monitor-stub-1        docker-monitor-monitor-stub        "gunicorn --bind 0.0…"   monitor-stub        2 minutes ago   Up 10 seconds       0.0.0.0:8000->8000/tcp, [::]:8000->8000/tcp

まとめ

DooDを利用して他のコンテナを監視・再起動させることができました。
毎回手動で再起動する手間が省けました。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?