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