概要
起動時たまに、うまく立ち上がらないコンテナを手動で再起動していたのですが、
それを自動化したく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を利用して他のコンテナを監視・再起動させることができました。
毎回手動で再起動する手間が省けました。
参考