mirror of
https://github.com/yanhao98/docker-cron.git
synced 2026-02-04 10:30:33 +08:00
feat(docker): 初始化项目并配置定时备份 PostgreSQL 数据库示例
This commit is contained in:
42
.github/workflows/docker-push.yaml
vendored
Normal file
42
.github/workflows/docker-push.yaml
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# name: _打包推送镜像
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
env:
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push-ghcr:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
# https://github.com/docker/metadata-action/tree/v5/?tab=readme-ov-file#semver
|
||||||
|
# Event: push, Ref: refs/head/main, Tags: main
|
||||||
|
# Event: push tag, Ref: refs/tags/v1.2.3, Tags: 1.2.3, 1.2, 1, latest
|
||||||
|
# Event: push tag, Ref: refs/tags/v2.0.8-rc1, Tags: 2.0.8-rc1
|
||||||
|
metadata-action-tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=semver,pattern={{major}}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 🔑 登录 GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: 🐳 构建并推送 Docker 镜像
|
||||||
|
uses: yanhao98/composite-actions/docker-build-push@main
|
||||||
|
with:
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
load: false
|
||||||
|
meta_images: ghcr.io/${{ github.repository }}
|
||||||
|
meta_tags: ${{ env.metadata-action-tags }}
|
||||||
|
cache-from: type=gha,scope=${{ github.workflow }}
|
||||||
|
cache-to: type=gha,scope=${{ github.workflow }}
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/backups
|
||||||
|
.DS_Store
|
||||||
22
Dockerfile
Normal file
22
Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
FROM alpine:3.22.1 AS base
|
||||||
|
|
||||||
|
# 解决时区问题
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN apk add alpine-conf && \
|
||||||
|
setup-timezone -z Asia/Shanghai && \
|
||||||
|
apk del alpine-conf
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
|
||||||
|
# 安装 sqlite 工具,cron 默认已包含 (dcron)
|
||||||
|
RUN apk add --no-cache tini
|
||||||
|
|
||||||
|
# 时区
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
|
||||||
|
# 启动脚本与任务脚本
|
||||||
|
COPY entrypoint.sh run-cron-tasks.sh /usr/local/bin/
|
||||||
|
RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/run-cron-tasks.sh && \
|
||||||
|
mkdir -p /docker-entrypoint-init.d /docker-cron.d /var/lib/cron-init
|
||||||
|
|
||||||
|
ENTRYPOINT ["/sbin/tini", "--", "entrypoint.sh"]
|
||||||
43
docker-compose.yaml
Normal file
43
docker-compose.yaml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
volumes:
|
||||||
|
tmp:
|
||||||
|
external: false
|
||||||
|
services:
|
||||||
|
postgres17:
|
||||||
|
container_name: postgres17
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=pguser
|
||||||
|
- POSTGRES_PASSWORD=pgpass
|
||||||
|
- POSTGRES_DB=_
|
||||||
|
volumes:
|
||||||
|
- ./backups:/backups:rw
|
||||||
|
- tmp:/var/lib/postgresql/data
|
||||||
|
image: postgres:17
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 10s
|
||||||
|
|
||||||
|
backup-cron:
|
||||||
|
depends_on:
|
||||||
|
postgres17:
|
||||||
|
condition: service_healthy
|
||||||
|
image: ghcr.io/yanhao98/docker-cron:main
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
environment:
|
||||||
|
- APK_PACKAGES=curl jq
|
||||||
|
- CRON_SCHEDULE=*/1 * * * *
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
- ./backups:/backups:ro
|
||||||
|
# - ./examples/init:/docker-entrypoint-init.d
|
||||||
|
- type: bind
|
||||||
|
source: ./examples/cron/10-postgres-dump-docker-sock.sh
|
||||||
|
target: /docker-cron.d/10-postgres-dump-docker-sock.sh
|
||||||
|
bind:
|
||||||
|
create_host_path: false
|
||||||
|
restart: unless-stopped
|
||||||
85
entrypoint.sh
Normal file
85
entrypoint.sh
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
printf '%s\n' "cron-entrypoint: $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
printf '%s\n' "cron-entrypoint: $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! set -o pipefail 2>/dev/null; then
|
||||||
|
log_info "shell does not support pipefail; continuing without it"
|
||||||
|
fi
|
||||||
|
|
||||||
|
CRON_SCHEDULE="${CRON_SCHEDULE:-*/30 * * * *}"
|
||||||
|
INIT_SCRIPTS_DIR="${INIT_SCRIPTS_DIR:-/docker-entrypoint-init.d}"
|
||||||
|
CRON_TASKS_DIR="${CRON_TASKS_DIR:-/docker-cron.d}"
|
||||||
|
APK_PACKAGES="${APK_PACKAGES:-}"
|
||||||
|
|
||||||
|
STDOUT_FD="/proc/1/fd/1"
|
||||||
|
STDERR_FD="/proc/1/fd/2"
|
||||||
|
CRON_SCRIPT="/usr/local/bin/run-cron-tasks.sh"
|
||||||
|
|
||||||
|
readonly CRON_SCHEDULE INIT_SCRIPTS_DIR CRON_TASKS_DIR APK_PACKAGES STDOUT_FD STDERR_FD CRON_SCRIPT
|
||||||
|
|
||||||
|
if [ -z "$CRON_SCHEDULE" ]; then
|
||||||
|
log_error "CRON_SCHEDULE is empty; refusing to start."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /var/spool/cron/crontabs "$INIT_SCRIPTS_DIR" "$CRON_TASKS_DIR"
|
||||||
|
|
||||||
|
if [ -n "$APK_PACKAGES" ]; then
|
||||||
|
log_info "ensuring apk packages: $APK_PACKAGES"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
if ! wget -qO- https://Git.1-H.CC/Scripts/Linux/raw/branch/main/ensure-apk-packages.sh | \
|
||||||
|
sh -s -- $APK_PACKAGES; then
|
||||||
|
log_error "failed to install requested apk packages"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "no additional apk packages requested"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "starting initialization from $INIT_SCRIPTS_DIR"
|
||||||
|
set -- "$INIT_SCRIPTS_DIR"/*
|
||||||
|
if [ "$1" = "$INIT_SCRIPTS_DIR/*" ]; then
|
||||||
|
log_info "no initialization scripts found"
|
||||||
|
else
|
||||||
|
for script in "$@"; do
|
||||||
|
if [ ! -f "$script" ]; then
|
||||||
|
log_info "skipping non-regular entry $script"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
script_name=$(basename "$script")
|
||||||
|
log_info "running initialization script $script_name"
|
||||||
|
if [ -x "$script" ]; then
|
||||||
|
if ! "$script" >>"$STDOUT_FD" 2>>"$STDERR_FD"; then
|
||||||
|
log_error "initialization script $script_name failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if ! /bin/sh "$script" >>"$STDOUT_FD" 2>>"$STDERR_FD"; then
|
||||||
|
log_error "initialization script $script_name failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "initialization complete"
|
||||||
|
|
||||||
|
cron_env_file=/var/spool/cron/crontabs/root
|
||||||
|
{
|
||||||
|
printf 'SHELL=/bin/sh\n'
|
||||||
|
printf 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n'
|
||||||
|
printf 'CRON_TASKS_DIR=%s\n' "$CRON_TASKS_DIR"
|
||||||
|
printf '%s %s\n' "$CRON_SCHEDULE" "$CRON_SCRIPT >> $STDOUT_FD 2>> $STDERR_FD"
|
||||||
|
} >"$cron_env_file"
|
||||||
|
chmod 0600 "$cron_env_file"
|
||||||
|
|
||||||
|
log_info "starting crond with schedule '$CRON_SCHEDULE'"
|
||||||
|
exec crond -f
|
||||||
7
examples/cron/10-postgres-dump-docker-sock.sh
Executable file
7
examples/cron/10-postgres-dump-docker-sock.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
curl -fsSL https://Git.1-H.CC/Scripts/Linux/raw/branch/main/postgres-dump-zstd-docker-sock.sh |
|
||||||
|
sh -s -- \
|
||||||
|
--socket=/var/run/docker.sock \
|
||||||
|
--api-version=v1.51 \
|
||||||
|
--backup-dir=/backups \
|
||||||
|
--container=postgres17 \
|
||||||
|
--backup-prefix=postgres17_all_databases_zstd_
|
||||||
4
examples/init/00-install-deps.sh
Executable file
4
examples/init/00-install-deps.sh
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Dependency installation moved to entrypoint.sh.
|
||||||
|
# Define APK_PACKAGES environment variable (e.g. "curl jq") to
|
||||||
|
# make the entrypoint ensure the listed Alpine packages are present.
|
||||||
69
run-cron-tasks.sh
Normal file
69
run-cron-tasks.sh
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -u
|
||||||
|
|
||||||
|
CRON_TASKS_DIR="${CRON_TASKS_DIR:-/docker-cron.d}"
|
||||||
|
STDOUT_FD="/proc/1/fd/1"
|
||||||
|
STDERR_FD="/proc/1/fd/2"
|
||||||
|
|
||||||
|
readonly CRON_TASKS_DIR STDOUT_FD STDERR_FD
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
printf '%s\n' "run-cron-tasks: $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
printf '%s\n' "run-cron-tasks: $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ ! -d "$CRON_TASKS_DIR" ]; then
|
||||||
|
log_info "tasks directory $CRON_TASKS_DIR not found; skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_started_at=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
log_info "===== cron run started $run_started_at ====="
|
||||||
|
|
||||||
|
set -- "$CRON_TASKS_DIR"/*
|
||||||
|
if [ "$1" = "$CRON_TASKS_DIR/*" ]; then
|
||||||
|
log_info "no task scripts found in $CRON_TASKS_DIR"
|
||||||
|
run_finished_at=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
log_info "===== cron run finished $run_finished_at (exit 0) ====="
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
max_rc=0
|
||||||
|
for task in "$@"; do
|
||||||
|
if [ ! -f "$task" ]; then
|
||||||
|
log_info "skipping non-regular entry $task"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
task_name=$(basename "$task")
|
||||||
|
log_info "starting task $task_name"
|
||||||
|
|
||||||
|
if [ ! -x "$task" ]; then
|
||||||
|
if ! chmod +x "$task" 2>>"$STDERR_FD"; then
|
||||||
|
log_error "failed to chmod +x $task_name"
|
||||||
|
if [ "$max_rc" -lt 1 ]; then
|
||||||
|
max_rc=1
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if "$task" >>"$STDOUT_FD" 2>>"$STDERR_FD"; then
|
||||||
|
rc=0
|
||||||
|
log_info "task $task_name completed successfully"
|
||||||
|
else
|
||||||
|
rc=$?
|
||||||
|
log_error "task $task_name exited with status $rc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$rc" -gt "$max_rc" ]; then
|
||||||
|
max_rc=$rc
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
run_finished_at=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
log_info "===== cron run finished $run_finished_at (exit $max_rc) ====="
|
||||||
|
exit "$max_rc"
|
||||||
Reference in New Issue
Block a user