From afbff9eb559258899741db94a3eff761cbb8c343 Mon Sep 17 00:00:00 2001 From: nianzhibai Date: Sat, 30 May 2026 11:09:04 +0800 Subject: [PATCH] Add Docker Compose deployment support --- .dockerignore | 21 +++++++++ .github/workflows/docker-build.yml | 68 ++++++++++++++++++++++++++++++ Dockerfile | 66 +++++++++++++++++++++++++++++ README.md | 66 ++++++++++++++++++++++++++++- docker-compose.yml | 9 ++++ docker-entrypoint.sh | 38 +++++++++++++++++ 6 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker-build.yml create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100755 docker-entrypoint.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9e94b57 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +.git +.github +.gitattributes +.gitignore + +node_modules +dist +release +data +backend/data +backend/config.yaml +config.yaml + +*.db +*.sqlite +*.sqlite3 +*.log +*.tmp + +tests +video-site-implementation-plan.md diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..c52665d --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,68 @@ +name: Docker + +on: + push: + branches: + - main + tags: + - "v*" + pull_request: + branches: + - main + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +permissions: + contents: read + packages: write + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=tag + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix=sha- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + VERSION=${{ startsWith(github.ref, 'refs/tags/v') && github.ref_name || '' }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..aa4fab9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,66 @@ +# ---- Stage 1: Build frontend ---- +FROM node:20-slim AS frontend + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY tsconfig.json vite.config.ts index.html ./ +COPY public/ public/ +COPY src/ src/ +RUN npm run build + +# ---- Stage 2: Build backend ---- +FROM golang:1.23-bookworm AS backend + +WORKDIR /app/backend + +COPY backend/go.mod backend/go.sum ./ +COPY backend/vendor/ vendor/ +COPY backend/cmd/ cmd/ +COPY backend/internal/ internal/ +RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/server ./cmd/server + +# ---- Stage 3: Runtime ---- +FROM debian:bookworm-slim AS runtime + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + ffmpeg \ + openssl \ + python3 \ + python3-bs4 \ + python3-lxml \ + python3-requests \ + tar \ + tzdata \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /opt/video-site-91 + +COPY --from=backend /out/server ./server +COPY --from=frontend /app/dist ./dist +COPY backend/config.example.yaml ./config.example.yaml +COPY 91VideoSpider/ ./91VideoSpider/ +COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + +ARG VERSION= + +ENV VIDEO_CONFIG=/opt/video-site-91/data/config.yaml \ + VIDEO_FRONTEND_DIR=/opt/video-site-91/dist \ + VIDEO_GITHUB_REPO=nianzhibai/91 \ + VIDEO_IMAGE_VERSION=${VERSION} \ + VIDEO_LISTEN_PORT=9191 \ + VIDEO_VERSION_FILE=/opt/video-site-91/data/.version + +RUN chmod +x ./server /usr/local/bin/docker-entrypoint.sh + +VOLUME ["/opt/video-site-91/data"] +EXPOSE 9191 + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["./server"] diff --git a/README.md b/README.md index c920940..dee75ce 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ ## 快速开始 -一键安装: +### 一键安装脚本(推荐) ```bash sudo apt update @@ -96,16 +96,78 @@ sudo bash /tmp/install-91.sh update FRONTEND_PORT=8080 sudo -E bash install.sh ``` +### Docker Compose 部署 + +准备目录: + +```bash +mkdir -p video-site-91 +cd video-site-91 +``` + +创建 `docker-compose.yml`: + +```yaml +services: + video-site-91: + image: ghcr.io/nianzhibai/91:latest + container_name: video-site-91 + ports: + - "9191:9191" + volumes: + - ./data:/opt/video-site-91/data + restart: unless-stopped +``` + +启动: + +```bash +docker compose up -d +``` + +部署完成后访问: + +- 前台:`http://服务器IP:9191/` +- 后台:`http://服务器IP:9191/admin` + +所有配置、数据库、封面、预览、上传文件都会保存在当前目录的 `./data` 里。更新时执行: + +```bash +docker compose pull +docker compose up -d +``` + +查看日志: + +```bash +docker compose logs -f +``` + +如果只想下载仓库内置的 Compose 文件: + +```bash +curl -fsSL https://raw.githubusercontent.com/nianzhibai/91/main/docker-compose.yml -o docker-compose.yml +docker compose up -d +``` + --- ## 数据存放位置 -项目会把运行数据保存在本地: +一键安装脚本会把运行数据保存在宿主机: - `/opt/video-site-91/config.yaml`:本地配置、管理员账号、网盘凭证。 - `/opt/video-site-91/data/video-site.db`:SQLite 数据库。 - `/opt/video-site-91/data/previews/`:本地生成的封面和 teaser。 +Docker Compose 部署会把运行数据保存在当前目录的 `./data/`: + +- `./data/config.yaml`:本地配置、管理员账号、网盘凭证。 +- `./data/video-site.db`:SQLite 数据库。 +- `./data/previews/`:本地生成的封面和 teaser。 +- `./data/uploads/`:本地上传的视频文件。 +- `./data/spider91/`:91 爬虫本地保存的视频文件。 + --- ## 了解更多 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3d5b7af --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +services: + video-site-91: + image: ghcr.io/nianzhibai/91:latest + container_name: video-site-91 + ports: + - "9191:9191" + volumes: + - ./data:/opt/video-site-91/data + restart: unless-stopped diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..f7a04ed --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,38 @@ +#!/bin/sh +set -eu + +APP_DIR="/opt/video-site-91" +DATA_DIR="${VIDEO_DATA_DIR:-$APP_DIR/data}" +CONFIG="${VIDEO_CONFIG:-$DATA_DIR/config.yaml}" +EXAMPLE="$APP_DIR/config.example.yaml" +PORT="${VIDEO_LISTEN_PORT:-9191}" + +mkdir -p "$DATA_DIR" "$DATA_DIR/previews" "$DATA_DIR/uploads" "$DATA_DIR/spider91" + +if [ ! -f "$CONFIG" ]; then + if [ ! -f "$EXAMPLE" ]; then + echo "[entrypoint] missing config template: $EXAMPLE" >&2 + exit 1 + fi + + mkdir -p "$(dirname "$CONFIG")" + cp "$EXAMPLE" "$CONFIG" + + SECRET="$(openssl rand -hex 32)" + sed -i -E "s#^([[:space:]]*listen:[[:space:]]*).*\$#\1\"0.0.0.0:${PORT}\"#" "$CONFIG" + sed -i -E "s#^([[:space:]]*session_secret:[[:space:]]*).*\$#\1\"${SECRET}\"#" "$CONFIG" + sed -i -E "s#^([[:space:]]*db_path:[[:space:]]*).*\$#\1\"${DATA_DIR}/video-site.db\"#" "$CONFIG" + sed -i -E "s#^([[:space:]]*local_preview_dir:[[:space:]]*).*\$#\1\"${DATA_DIR}/previews\"#" "$CONFIG" + chmod 600 "$CONFIG" + + echo "[entrypoint] generated $CONFIG" +else + echo "[entrypoint] using existing $CONFIG" +fi + +if [ -n "${VIDEO_VERSION_FILE:-}" ] && [ -n "${VIDEO_IMAGE_VERSION:-}" ] && [ ! -f "$VIDEO_VERSION_FILE" ]; then + mkdir -p "$(dirname "$VIDEO_VERSION_FILE")" + printf '%s\n' "$VIDEO_IMAGE_VERSION" > "$VIDEO_VERSION_FILE" +fi + +exec "$@"