bash 脚本中优雅地停止 inotifywait 管道
graceful stop inotifywait pipeline inside a bash script
我正在使用 docker 通过 inotify 和 aws-cli 观察和同步文件夹中的数据,但是当我尝试使用 SIGTERM
终止 docker 时,它退出并显示代码143 但我想获得零退出代码。如果我终止 docker 中的 inotify 进程,它会 return 一个零代码。
那么如何用 TERM
信号和 return 0 代码杀死 entrypoint.sh
?
docker 是 here。我把 bash 脚本放在下面:
#!/usr/bin/env bash
# S3Sync Entry Point
# Bash strict mode
set -euo pipefail
IFS=$'\n\t'
# VARs
S3PATH=${S3PATH:-}
SYNCDIR="${SYNCDIR:-/sync}"
CRON_TIME="${CRON_TIME:-10 * * * *}"
INITIAL_DOWNLOAD="${INITIAL_DOWNLOAD:-true}"
# Log message
log(){
echo "[$(date "+%Y-%m-%dT%H:%M:%S%z") - $(hostname)] ${*}"
}
# Sync files
sync_files(){
local src="${1:-}"
local dst="${2:-}"
mkdir -p "$dst" # Make sure directory exists
log "Sync '${src}' to '${dst}'"
if ! aws s3 sync --no-progress --delete --exact-timestamps "$src" "$dst"; then
log "Could not sync '${src}' to '${dst}'" >&2; exit 1
fi
}
# Download files
download_files(){
sync_files "$S3PATH" "$SYNCDIR"
}
# Upload files
upload_files(){
sync_files "$SYNCDIR" "$S3PATH"
}
# Run initial download
initial_download(){
if [[ "$INITIAL_DOWNLOAD" == 'true' ]]; then
if [[ -d "$SYNCDIR" ]]; then
# directory exists
if [[ $(ls -A "$SYNCDIR" 2>/dev/null) ]]; then
# directory is not empty
log "${SYNCDIR} is not empty; skipping initial download"
else
# directory is empty
download_files
fi
else
# directory does not exist
download_files
fi
elif [[ "$INITIAL_DOWNLOAD" == 'force' ]]; then
download_files
fi
}
# Watch directory using inotify
watch_directory(){
initial_download # Run initial download
log "Watching directory '${SYNCDIR}' for changes"
inotifywait \
--event create \
--event delete \
--event modify \
--event move \
--format "%e %w%f" \
--monitor \
--quiet \
--recursive \
"$SYNCDIR" |
while read -r changed
do
log "$changed"
upload_files
done
}
# Install cron job
run_cron(){
local action="${1:-upload}"
# Run initial download
initial_download
log "Setup the cron job (${CRON_TIME})"
echo "${CRON_TIME} /entrypoint.sh ${action}" > /etc/crontabs/root
exec crond -f -l 6
}
# Main function
main(){
if [[ ! "$S3PATH" =~ s3:// ]]; then
log 'No S3PATH specified' >&2; exit 1
fi
mkdir -p "$SYNCDIR" # Make sure directory exists
# Parse command line arguments
cmd="${1:-download}"
case "$cmd" in
download)
download_files
;;
upload)
upload_files
;;
sync)
watch_directory
;;
periodic_upload)
run_cron upload
;;
periodic_download)
run_cron download
;;
*)
log "Unknown command: ${cmd}"; exit 1
;;
esac
}
main "$@"
像这样尝试陷阱但失败了:
trap "exit" INT TERM
trap "kill 0" EXIT
由 docker 图片的贡献者回答。
https://github.com/vladgh/docker_base_images/issues/62
此图像使用 Tini,它不会对其接收到的信号的含义做出任何假设,只是将其转发给它的 child。
为了让陷阱起作用,您需要在 Dockerfile 中向 Tini 添加 -g 标志 (krallin/tini#process-group-killing):
ENTRYPOINT ["/sbin/tini", "-g", "--", "/entrypoint.sh"]
只有这样你才能在entrypoint.sh
的顶部设置一个陷阱:
trap "exit 0" INT TERM EXIT
我正在使用 docker 通过 inotify 和 aws-cli 观察和同步文件夹中的数据,但是当我尝试使用 SIGTERM
终止 docker 时,它退出并显示代码143 但我想获得零退出代码。如果我终止 docker 中的 inotify 进程,它会 return 一个零代码。
那么如何用 TERM
信号和 return 0 代码杀死 entrypoint.sh
?
docker 是 here。我把 bash 脚本放在下面:
#!/usr/bin/env bash
# S3Sync Entry Point
# Bash strict mode
set -euo pipefail
IFS=$'\n\t'
# VARs
S3PATH=${S3PATH:-}
SYNCDIR="${SYNCDIR:-/sync}"
CRON_TIME="${CRON_TIME:-10 * * * *}"
INITIAL_DOWNLOAD="${INITIAL_DOWNLOAD:-true}"
# Log message
log(){
echo "[$(date "+%Y-%m-%dT%H:%M:%S%z") - $(hostname)] ${*}"
}
# Sync files
sync_files(){
local src="${1:-}"
local dst="${2:-}"
mkdir -p "$dst" # Make sure directory exists
log "Sync '${src}' to '${dst}'"
if ! aws s3 sync --no-progress --delete --exact-timestamps "$src" "$dst"; then
log "Could not sync '${src}' to '${dst}'" >&2; exit 1
fi
}
# Download files
download_files(){
sync_files "$S3PATH" "$SYNCDIR"
}
# Upload files
upload_files(){
sync_files "$SYNCDIR" "$S3PATH"
}
# Run initial download
initial_download(){
if [[ "$INITIAL_DOWNLOAD" == 'true' ]]; then
if [[ -d "$SYNCDIR" ]]; then
# directory exists
if [[ $(ls -A "$SYNCDIR" 2>/dev/null) ]]; then
# directory is not empty
log "${SYNCDIR} is not empty; skipping initial download"
else
# directory is empty
download_files
fi
else
# directory does not exist
download_files
fi
elif [[ "$INITIAL_DOWNLOAD" == 'force' ]]; then
download_files
fi
}
# Watch directory using inotify
watch_directory(){
initial_download # Run initial download
log "Watching directory '${SYNCDIR}' for changes"
inotifywait \
--event create \
--event delete \
--event modify \
--event move \
--format "%e %w%f" \
--monitor \
--quiet \
--recursive \
"$SYNCDIR" |
while read -r changed
do
log "$changed"
upload_files
done
}
# Install cron job
run_cron(){
local action="${1:-upload}"
# Run initial download
initial_download
log "Setup the cron job (${CRON_TIME})"
echo "${CRON_TIME} /entrypoint.sh ${action}" > /etc/crontabs/root
exec crond -f -l 6
}
# Main function
main(){
if [[ ! "$S3PATH" =~ s3:// ]]; then
log 'No S3PATH specified' >&2; exit 1
fi
mkdir -p "$SYNCDIR" # Make sure directory exists
# Parse command line arguments
cmd="${1:-download}"
case "$cmd" in
download)
download_files
;;
upload)
upload_files
;;
sync)
watch_directory
;;
periodic_upload)
run_cron upload
;;
periodic_download)
run_cron download
;;
*)
log "Unknown command: ${cmd}"; exit 1
;;
esac
}
main "$@"
像这样尝试陷阱但失败了:
trap "exit" INT TERM
trap "kill 0" EXIT
由 docker 图片的贡献者回答。
https://github.com/vladgh/docker_base_images/issues/62
此图像使用 Tini,它不会对其接收到的信号的含义做出任何假设,只是将其转发给它的 child。
为了让陷阱起作用,您需要在 Dockerfile 中向 Tini 添加 -g 标志 (krallin/tini#process-group-killing):
ENTRYPOINT ["/sbin/tini", "-g", "--", "/entrypoint.sh"]
只有这样你才能在entrypoint.sh
的顶部设置一个陷阱:
trap "exit 0" INT TERM EXIT