From f45df870d7debbc492f9c1b0df1bb45fc41692c8 Mon Sep 17 00:00:00 2001 From: rbetree Date: Fri, 20 Feb 2026 00:52:23 +0800 Subject: [PATCH] refactor(docker): simplify to single compose + dynamic build default --- Dockerfile | 29 ++++---------- Dockerfile.static | 38 +++++++++++++++++++ README.md | 57 ++++++++++------------------ docker-compose.yml | 15 ++++---- docker/entrypoint-build-and-serve.sh | 25 ++++++++++++ 5 files changed, 98 insertions(+), 66 deletions(-) create mode 100644 Dockerfile.static create mode 100644 docker/entrypoint-build-and-serve.sh diff --git a/Dockerfile b/Dockerfile index eaae827..c24626f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,37 +1,22 @@ -# syntax=docker/dockerfile:1.7 +# 动态构建版本(默认):容器启动时执行 `npm run build` 生成 dist,再由 nginx 提供静态文件。 -FROM node:22-alpine AS builder +FROM node:22-alpine WORKDIR /app ENV HUSKY=0 COPY package.json package-lock.json ./ -RUN npm ci +RUN npm ci && apk add --no-cache nginx COPY . . -ARG MENAV_ENABLE_SYNC=false -ARG MENAV_IMPORT_BOOKMARKS=false - -RUN if [ "${MENAV_IMPORT_BOOKMARKS}" = "true" ]; then \ - MENAV_BOOKMARKS_DETERMINISTIC=1 npm run import-bookmarks; \ - fi \ - && if [ "${MENAV_ENABLE_SYNC}" = "true" ]; then \ - npm run build; \ - else \ - PROJECTS_ENABLED=false HEATMAP_ENABLED=false RSS_ENABLED=false npm run build; \ - fi - -FROM nginx:1.27-alpine AS runtime - -WORKDIR /usr/share/nginx/html - -COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf -COPY --from=builder /app/dist ./ +COPY docker/nginx/default.conf /etc/nginx/http.d/default.conf +COPY docker/entrypoint-build-and-serve.sh /usr/local/bin/entrypoint-build-and-serve.sh +RUN chmod +x /usr/local/bin/entrypoint-build-and-serve.sh EXPOSE 80 STOPSIGNAL SIGQUIT -CMD ["nginx", "-g", "daemon off;"] +ENTRYPOINT ["/usr/local/bin/entrypoint-build-and-serve.sh"] diff --git a/Dockerfile.static b/Dockerfile.static new file mode 100644 index 0000000..5fa7c10 --- /dev/null +++ b/Dockerfile.static @@ -0,0 +1,38 @@ +# 静态构建版本(可选):构建阶段生成 dist,运行阶段仅 nginx 提供静态文件。 +# 默认 Docker 方案请使用仓库根目录的 Dockerfile(动态构建,可挂载配置并通过重启生效)。 + +FROM node:22-alpine AS builder + +WORKDIR /app + +ENV HUSKY=0 + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . . + +ARG MENAV_ENABLE_SYNC=false +ARG MENAV_IMPORT_BOOKMARKS=false + +RUN if [ "${MENAV_IMPORT_BOOKMARKS}" = "true" ]; then \ + MENAV_BOOKMARKS_DETERMINISTIC=1 npm run import-bookmarks; \ + fi \ + && if [ "${MENAV_ENABLE_SYNC}" = "true" ]; then \ + npm run build; \ + else \ + PROJECTS_ENABLED=false HEATMAP_ENABLED=false RSS_ENABLED=false npm run build; \ + fi + +FROM nginx:1.27-alpine AS runtime + +WORKDIR /usr/share/nginx/html + +COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf +COPY --from=builder /app/dist ./ + +EXPOSE 80 + +STOPSIGNAL SIGQUIT + +CMD ["nginx", "-g", "daemon off;"] diff --git a/README.md b/README.md index fd7b4a5..be254b3 100644 --- a/README.md +++ b/README.md @@ -206,15 +206,20 @@ npm run format
点击展开 -MeNav 构建后是纯静态站点(`dist/`),仓库已内置 Docker 部署文件,可直接构建并运行。 +仓库已内置 `docker-compose.yml`,并提供 GHCR 预构建镜像;两种方式都建议统一使用 Docker Compose。 -#### 方式一:Docker Compose(常用) +> 说明:容器每次启动都会在容器内执行 `npm run build` 生成 `dist/`,然后用 nginx 提供静态文件。 +> +> 请在仓库根目录执行(需要 `config/_default` 等文件)。 -1. 准备配置(首次使用): - - 按 [设置配置文件](#设置配置文件) 完成 `config/user/` 配置 - - 如果要导入书签,可先把书签 HTML 放到 `bookmarks/` 目录 +#### 方式 A:使用预构建镜像(推荐,免本地构建) -2. 构建并启动: +```bash +docker compose pull +docker compose up -d --no-build +``` + +#### 方式 B:本地构建镜像(适合二次开发/改源码) ```bash docker compose up -d --build @@ -222,46 +227,24 @@ docker compose up -d --build 默认访问地址:`http://localhost:8080` -3. 常用可选参数(通过环境变量传入): +#### 可选参数(环境变量) ```bash -MENAV_PORT=80 MENAV_ENABLE_SYNC=true MENAV_IMPORT_BOOKMARKS=true docker compose up -d --build +MENAV_PORT=80 MENAV_ENABLE_SYNC=true MENAV_IMPORT_BOOKMARKS=true docker compose up -d --no-build ``` - `MENAV_PORT`:宿主机端口(默认 `8080`) -- `MENAV_ENABLE_SYNC`:是否在镜像构建时联网执行 `sync-*`(默认 `false`,更稳定) -- `MENAV_IMPORT_BOOKMARKS`:是否在镜像构建时执行 `npm run import-bookmarks`(默认 `false`) +- `MENAV_ENABLE_SYNC`:启动构建时是否联网执行 `sync-*`(默认 `false`,更稳定) +- `MENAV_IMPORT_BOOKMARKS`:启动构建前是否执行 `npm run import-bookmarks`(默认 `false`) -4. 更新站点: - - 修改配置或内容后,重新执行 `docker compose up -d --build` +#### 配置与更新 -#### 方式二:直接使用 Docker 命令 +- 配置目录挂载在 `./config`,个人配置按“完全替换策略”建议:将 `config/_default/` 完整复制到 `config/user/` 再修改(见 [设置配置文件](#设置配置文件) 与 `config/README.md`)。 +- 如需导入书签:将浏览器导出的书签 HTML 放到 `./bookmarks/`,并设置 `MENAV_IMPORT_BOOKMARKS=true` 后重启容器。 +- 修改配置/书签后生效方式(触发重新构建): ```bash -docker build -t menav \ - --build-arg MENAV_ENABLE_SYNC=false \ - --build-arg MENAV_IMPORT_BOOKMARKS=false . - -docker run -d --name menav -p 8080:80 --restart unless-stopped menav -``` - -#### 使用 GHCR 预构建镜像(免本地构建) - -仓库已提供 `Docker Publish (GHCR)` 工作流(`.github/workflows/docker-ghcr.yml`): - -- 推送到 `main` 时自动发布 `latest` -- 推送 `v*` 标签时自动发布版本标签(如 `v1.3.0`) -- 对外标签策略:仅 `latest` 与版本标签 - -首次启用前请在仓库设置确认: - -1. `Settings -> Actions -> General -> Workflow permissions` 设为 `Read and write permissions` -2. 在 `Packages` 中将镜像可见性设为 `Public`(否则匿名用户无法拉取) - -发布后,用户可直接拉取并运行(将 `/` 替换为你的仓库路径): - -```bash -docker run -d --name menav -p 8080:80 --restart unless-stopped ghcr.io//:latest +docker compose restart menav ```
diff --git a/docker-compose.yml b/docker-compose.yml index 5e151c2..fd83db5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,14 @@ services: menav: container_name: menav - image: menav:latest - build: - context: . - dockerfile: Dockerfile - args: - MENAV_ENABLE_SYNC: ${MENAV_ENABLE_SYNC:-false} - MENAV_IMPORT_BOOKMARKS: ${MENAV_IMPORT_BOOKMARKS:-false} + image: ${MENAV_IMAGE:-ghcr.io/rbetree/menav:latest} + build: . + environment: + MENAV_ENABLE_SYNC: ${MENAV_ENABLE_SYNC:-false} + MENAV_IMPORT_BOOKMARKS: ${MENAV_IMPORT_BOOKMARKS:-false} ports: - '${MENAV_PORT:-8080}:80' + volumes: + - ./config:/app/config + - ./bookmarks:/app/bookmarks restart: unless-stopped diff --git a/docker/entrypoint-build-and-serve.sh b/docker/entrypoint-build-and-serve.sh new file mode 100644 index 0000000..425f67c --- /dev/null +++ b/docker/entrypoint-build-and-serve.sh @@ -0,0 +1,25 @@ +#!/bin/sh +set -eu + +echo "[menav] starting dynamic build mode" + +if [ "${MENAV_IMPORT_BOOKMARKS:-false}" = "true" ]; then + echo "[menav] importing bookmarks before build" + MENAV_BOOKMARKS_DETERMINISTIC=1 npm run import-bookmarks +fi + +if [ "${MENAV_ENABLE_SYNC:-false}" = "true" ]; then + echo "[menav] building with sync enabled" + npm run build +else + echo "[menav] building with sync disabled" + PROJECTS_ENABLED=false HEATMAP_ENABLED=false RSS_ENABLED=false npm run build +fi + +echo "[menav] syncing dist to nginx web root" +mkdir -p /usr/share/nginx/html +rm -rf /usr/share/nginx/html/* +cp -a /app/dist/. /usr/share/nginx/html/ + +echo "[menav] serving dist with nginx" +exec nginx -g 'daemon off;'