feat(docker): 初始化项目并配置定时备份 PostgreSQL 数据库示例

This commit is contained in:
严浩
2025-09-22 23:33:52 +08:00
commit 20808e8a55
9 changed files with 275 additions and 0 deletions

1
.env Normal file
View File

@@ -0,0 +1 @@
TZ=Asia/Shanghai

42
.github/workflows/docker-push.yaml vendored Normal file
View 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
View File

@@ -0,0 +1,2 @@
/backups
.DS_Store

22
Dockerfile Normal file
View 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
View 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
View 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

View 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_

View 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
View 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"