背景介绍
Node.js是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,以其事件驱动、非阻塞 I/O 模型而闻名,广泛用于构建后端服务和前端应用。
Node.js 的特点使其非常适合容器化部署:
- 轻量级运行时:相比传统后端语言,占用资源更少
- 包管理系统:通过 npm 或 yarn 高效管理依赖
- 异步非阻塞:天然适合高并发应用场景
- 全栈开发:同一语言编写前后端,降低开发门槛
容器化 Node.js 应用需要特别关注以下几个方面:
- 依赖管理:npm 或 yarn 的缓存和依赖安装策略
- 环境隔离:开发、测试和生产环境的配置区分
- 安全考量:避免使用 root 用户运行应用
- 应用类型:区分不同应用类型的构建和部署方式
我们将遵循标准化的多阶段构建思想,为两种常见的 Node.js 应用场景创建优化、安全且高效的 Docker 镜像:
- 后端服务 (SSR 或 API): 通常基于 Express, Koa, NestJS 等框架,需要 Node.js 运行时环境
- 前端应用 (CSR 构建): 使用 React, Vue, Angular 等框架构建出静态文件,最终由 Nginx 托管
构建 Node 工具镜像
Node 工具环境负责提供完整的 Node.js 运行时和开发工具链,用于开发、测试和构建 Node.js 应用。
创建 Node 工具环境目录
首先创建 Node 工具镜像的目录:
mkdir -p common/tools/node
cd common/tools/node
Node 工具环境 Dockerfile 详解
FROM harbor.leops.local/common/os/debian:bullseyeARG NODE_VERSION=22.15.0 \YARN_VERSION=1.22.22LABEL org.opencontainers.image.authors="ops@leops.local" \org.opencontainers.image.source="http://git.leops.local/ops/dockerfiles-base/common/tools/node/Dockerfile" \org.opencontainers.image.description="node ${NODE_VERSION} compiler environment."ENV NODE_VERSION=$NODE_VERSION \YARN_VERSION=$YARN_VERSION \NPM_REGISTRY="http://verdaccio.leops.local/" \YARN_REGISTRY="http://verdaccio.leops.local/"# install dependencies
RUN set -eux \&& apt-get update \&& apt-get install -y --no-install-recommends git python3 python3-pip gcc g++ make \&& apt-get clean \&& rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/* /tmp/* /var/tmp/* \&& truncate -s 0 /var/log/*log# install node
RUN set -eux \&& curl -fsSLO --compressed "http://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" \&& tar -zxf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 --no-same-owner \&& rm "node-v$NODE_VERSION-linux-x64.tar.gz" \&& ln -s /usr/local/bin/node /usr/local/bin/nodejs \# smoke test&& node --version \&& npm --version \# set registry&& npm config set registry $NPM_REGISTRY \&& echo "disturl=${NPM_REGISTRY}/-/binary/node/" >> /root/.npmrc \&& echo "sass_binary_site=${NPM_REGISTRY}/-/binary/node-sass" >> /root/.npmrc \&& echo "canvas_binary_host_mirror=${NPM_REGISTRY}/-/binary/canvas" >> /root/.npmrc \&& echo "python_mirror=${NPM_REGISTRY}/-/binary/python/" >> /root/.npmrc \&& npm config list# install yarn
RUN set -eux \&& curl -fsSLO -k --compressed "https://github.com/yarnpkg/yarn/releases/download/v$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \&& mkdir -p /opt \&& tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \&& ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \&& rm -fv yarn-v$YARN_VERSION.tar.gz \# smoke test&& yarn --version \# set registry&& yarn config set registry $YARN_REGISTRY \&& yarn config list
Dockerfile 关键点解析
该 Node.js 工具环境 Dockerfile 有以下几个重要特点:
- 基于 Debian:使用了标准化的 Debian bullseye 作为基础镜像
- 参数化版本:通过 ARG 参数化 Node.js 和 Yarn 版本,便于维护和更新
- 环境配置:设置了私有 NPM 和 Yarn 镜像源地址
- C/C++ 编译支持:安装了 gcc、g++、make 等编译工具,支持需要本地编译的模块
- 依赖管理:配置了预设的二进制下载镜像,加速 node-sass、canvas 等模块安装
- 验证安装:通过 smoke test 验证 Node.js 和 Yarn 安装成功
- 缓存清理:每个步骤后清理不必要的缓存文件,减小最终镜像体积
镜像构建脚本
使用以下脚本 (build.sh) 来构建和推送 Node 工具镜像:
#!/bin/bashset -e# 配置
REGISTRY="harbor.leops.local"
IMAGE_BASE_NAME="common/tools/node"
VERSION="22.15.0"# 声明镜像地址数组
declare -a IMAGE_PATHS
IMAGE_PATHS+=("${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION}""${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION%.*}""${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION%%.*}""${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION}-debian11""${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION%.*}-debian11""${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION%%.*}-debian11"
)build_image() {echo "Building and pushing image:"for img in "${IMAGE_PATHS[@]}"; do echo -e " $img"; done# 构建镜像docker buildx build \$(for img in "${IMAGE_PATHS[@]}"; do echo -n "-t $img "; done) \--label "org.opencontainers.image.created=$(date --rfc-3339=seconds)" \--build-arg "NODE_VERSION=${VERSION}" \--add-host verdaccio.leops.local=192.168.77.140 \--provenance=false \--pull \--push \.echo "Build complete."
}# 参数处理
case "$1" in"list-tags")# 输出镜像标签列表printf '%s\n'"${IMAGE_PATHS[@]}";;*)build_image;;
esac
构建脚本通过灵活的标签生成逻辑,为镜像创建多个版本标签,包括:完整版本号(22.15.0)、主次版本号(22.15)、主版本号(22)以及带系统标识的组合标签,满足不同场景下的引用需求。
构建 Node.js 后端服务运行镜像 (SSR/API)
Node.js 后端服务运行镜像专注于安全、高效地运行 Node.js 服务端应用,采用 PM2 作为进程管理工具。这适用于 Express、Koa、NestJS、Nuxt 等需要服务端运行的框架。
为什么选择 PM2?
PM2 是一个强大的 Node.js 应用进程管理工具,提供以下关键特性:
- 进程守护:自动重启崩溃的应用
- 负载均衡:自动使用集群模式分发请求
- 日志管理:集中管理应用日志
- 监控功能:实时监控应用状态
- 零停机重载:不中断服务的情况下更新应用
创建 PM2 工具环境目录
首先创建 PM2 工具镜像的目录:
mkdir -p common/runtime/pm2
cd common/runtime/pm2
PM2 工具环境 Dockerfile 详解
#syntax=harbor.leops.local/library/docker/dockerfile:1ARG NODE_VERSION=22FROM harbor.leops.local/common/tools/node:${NODE_VERSION}LABEL org.opencontainers.image.authors="ops@leops.local" \org.opencontainers.image.source="http://git.leops.local/ops/dockerfiles-base/common/runtime/pm2/Dockerfile" \org.opencontainers.image.description="pm2 runtime environment."# install node
RUN set -eux \&& npm install -g pm2 \&& groupadd -r nonroot \&& useradd -r -m -g nonroot nonroot \&& mkdir -p /app/logs /home/nonroot/.pm2 \&& export PM2_HOME=/home/nonroot/.pm2 \&& chown nonroot:nonroot -R /app /home/nonrootUSER nonroot:nonrootCMD [ "pm2-runtime", "start", "ecosystem.config.js" ]
运行镜像重点解析
这个 PM2 运行镜像具有以下关键特点:
- 基于 Node 工具环境:继承我们前面创建的 Node.js 工具环境
- 全局安装 PM2:作为进程管理器确保应用稳定运行
- 非 root 用户:创建专用的 nonroot 用户,提高容器安全性
- 标准目录结构:预先创建 /app/logs 目录和 PM2 主目录
- 权限设置:确保应用目录归非 root 用户所有
- 默认启动命令:配置使用 PM2 运行时模式启动应用
镜像构建脚本
使用以下脚本 (build.sh) 来构建和推送 PM2 工具镜像:
#!/bin/bashset -e# 配置
REGISTRY="harbor.leops.local"
IMAGE_BASE_NAME="common/runtime/pm2"
VERSION="22"# 声明镜像地址数组
declare -a IMAGE_PATHS
IMAGE_PATHS+=("${REGISTRY}/${IMAGE_BASE_NAME}:node-${VERSION}""${REGISTRY}/${IMAGE_BASE_NAME}:node-${VERSION%.*}"
)build_image() {echo "Building and pushing image:"for img in "${IMAGE_PATHS[@]}"; do echo -e " $img"; done# 构建镜像docker buildx build \$(for img in "${IMAGE_PATHS[@]}"; do echo -n "-t $img "; done) \--label "org.opencontainers.image.created=$(date --rfc-3339=seconds)" \--add-host verdaccio.leops.local=192.168.77.140 \--build-arg "NODE_VERSION=${VERSION}" \--provenance=false \--pull \--push \.echo "Build complete."
}# 参数处理
case "$1" in"list-tags")# 输出镜像标签列表printf '%s\n'"${IMAGE_PATHS[@]}";;*)build_image;;
esac
构建前端应用静态文件镜像 (CSR)
对于前端应用(客户端渲染),最终产物是一堆静态的 HTML、CSS 和 JavaScript 文件,最适合使用 Nginx 这样的高性能 Web 服务器来托管。这适用于 React、Vue、Angular 等前端框架构建的静态应用。
为什么选择 Nginx?
Nginx 作为静态文件服务器有以下优势:
- 高性能:能够高效处理并发连接
- 低资源消耗:比动态服务占用更少的系统资源
- 缓存能力:内置优秀的静态文件缓存机制
- 简单配置:容易配置路由规则和重定向
- 安全性:能够限制访问和隐藏敏感信息
创建 Nginx 工具环境目录
首先创建 Nginx 工具镜像的目录:
mkdir -p common/runtime/nginx-csr
cd common/runtime/nginx-csr
Nginx CSR Dockerfile 详解
#syntax=harbor.leops.local/library/docker/dockerfile:1FROM harbor.leops.local/common/os/debian:bullseyeARG NGINX_VERSION=1.26.3LABEL org.opencontainers.image.authors="ops@leops.local" \org.opencontainers.image.source="http://git.leops.local/ops/dockerfiles-base/common/tools/node/Dockerfile" \org.opencontainers.image.description="nginx [engine x] is an HTTP and reverse proxy server"ENV NGINX_VERSION=$NGINX_VERSIONRUN echo 'deb [trusted=yes] https://nginx.org/packages/debian/ bullseye nginx' >> /etc/apt/sources.list.d/nginx.list \&& apt-get update \&& apt-get install -y nginx=${NGINX_VERSION}-1~bullseye \&& apt-get clean \&& chown www-data.www-data -R /var/cache/nginx \&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \&& ln -sf /dev/stdout /var/log/nginx/access.log \&& ln -sf /dev/stderr /var/log/nginx/error.logCOPY ./nginx.conf /etc/nginx/nginx.confEXPOSE80STOPSIGNAL SIGQUITCMD ["/usr/sbin/nginx", "-g", "daemon off;"]
Nginx 配置文件详解
nginx.conf 是一个针对前端单页应用(SPA)优化的配置文件,包含以下关键特性:
user www-data;
worker_processes 1;
worker_rlimit_nofile 65535;error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;events {multi_accept on;worker_connections 65535;
}http {charset utf-8;# MIMEinclude mime.types;default_type application/octet-stream;log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';log_format main_json '{"@timestamp": "$time_iso8601", ''"remote_addr": "$remote_addr", ''"Remoteip": "$http_Remoteip", ''"http_x_forwarded_for": "$http_x_forwarded_for", ''"scheme": "$scheme", ''"request_method": "$request_method", ''"host": "$host", ''"request_uri": "$request_uri", ''"body_bytes_sent": $body_bytes_sent, ''"http_referer": "$http_referer", ''"http_user_agent": "$http_user_agent", ''"request_time": $request_time, ''"request_length": $request_length, ''"status": "$status"}';access_log /var/log/nginx/access.log main_json;sendfile on;tcp_nopush on;tcp_nodelay on;log_not_found off;types_hash_max_size 2048;types_hash_bucket_size 64;client_max_body_size 100M;client_header_buffer_size 32k;large_client_header_buffers 4 32k;underscores_in_headers on;# disable version in error messages and response headerserver_tokens off;proxy_hide_header X-Application-Context;# use etag with expireetag on;server {charset utf-8;listen 80 default_server;server_name _;index index.html;root /app/;# index.html fallbacklocation / {try_files $uri $uri/ $uri/index.html /index.html;}# no cache index.htmllocation ~* index.html {add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';add_header Pragma no-cache;if_modified_since off;etag off;}# favicon.icolocation = /favicon.ico {log_not_found off;access_log off;}# robots.txtlocation = /robots.txt {log_not_found off;access_log off;}# assets, medialocation ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ {expires 7d;access_log off;}# svg, fontslocation ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ {add_header Access-Control-Allow-Origin "*";expires 7d;access_log off;}# deny hidden filelocation ~ /\. {deny all;access_log off;}# gzipgzip on;gzip_vary on;gzip_proxied any;gzip_comp_level 6;gzip_min_length 1k;gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;}
}
配置文件关键点解析
- SPA 路由支持:通过 try_files 指令将未匹配的路由重定向到 index.html,支持前端路由
- 缓存策略优化:
- HTML 文件不缓存,确保内容始终最新
- 静态资源(CSS、JS、图片等)设置 7 天缓存
- 字体文件支持跨域请求和缓存
- 性能优化:
- 开启 gzip 压缩减少传输数据量
- 设置合理的 worker 连接数和文件处理参数
- 使用 sendfile、tcp_nopush 等提升文件传输性能
- 安全设置:
- 隐藏 Nginx 版本信息
- 拒绝访问隐藏文件
- 定制日志格式,便于安全审计和问题排查
- 日志优化:使用 JSON 格式记录访问日志,便于日志收集和分析系统处理
镜像构建脚本
使用以下脚本 (build.sh) 来构建和推送镜像:
#!/bin/bashset -e# 配置
REGISTRY="harbor.leops.local"
IMAGE_BASE_NAME="common/runtime/nginx-csr"
VERSION="1.26.3"# 声明镜像地址数组
declare -a IMAGE_PATHS
IMAGE_PATHS+=("${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION}""${REGISTRY}/${IMAGE_BASE_NAME}:${VERSION%.*}"
)build_image() {echo "Building and pushing image:"for img in "${IMAGE_PATHS[@]}"; do echo -e " $img"; done# 构建镜像docker buildx build \$(for img in "${IMAGE_PATHS[@]}"; do echo -n "-t $img "; done) \--label "org.opencontainers.image.created=$(date --rfc-3339=seconds)" \--build-arg "NGINX_VERSION=${VERSION}" \--provenance=false \--pull \--push \.echo "Build complete."
}# 参数处理
case "$1" in"list-tags")# 输出镜像标签列表printf '%s\n'"${IMAGE_PATHS[@]}";;*)build_image;;
esac
构建应用镜像 - 多阶段构建实战
在准备好基础工具镜像和运行环境镜像后,我们可以使用多阶段构建来创建最终的应用镜像。这种方法使我们能够在一个 Dockerfile 中同时处理构建和运行环境,大幅减小最终镜像体积。
Docker 多阶段构建简介:多阶段构建的核心优势:
- 分离关注点:构建阶段专注于编译和打包,运行阶段专注于高效执行
- 镜像体积优化:最终镜像只包含运行所需的文件,不含构建工具和中间产物
- 构建缓存利用:合理的缓存策略可以大幅提升重复构建速度
- 工作流简化:一个 Dockerfile 完成全部流程,无需额外脚本协调
准备示例应用 Vue
首先,我们获取一个简单的 Vue 应用示例:
git clone https://github.com/lework/ci-demo-vue.git
cd ci-demo-vue
Vue 应用 Dockerfile 详解
下面是一个 Vue 前端应用的多阶段构建 Dockerfile:
#
# ---- 编译环境 ----FROM harbor.leops.local/common/tools/node:22 AS builderARG APP_ENV=test \APP=undefine \GIT_BRANCH= \GIT_COMMIT_ID=ENV APP_ENV=$APP_ENV \APP=$APP \GIT_BRANCH=$GIT_BRANCH \GIT_COMMIT_ID=$GIT_COMMIT_IDWORKDIR /app_buildCOPY package.json package-lock.json ./RUN --mount=type=cache,id=${APP}-npm,target=/root/.npm \--mount=type=cache,id=${APP}-npm-modules,target=./node_modules \npm installCOPY ./ ./RUN --mount=type=cache,id=${APP}-npm,target=/root/.npm \--mount=type=cache,id=${APP}-npm-modules,target=./node_modules \npm run build:${APP_ENV}#
# ---- 运行环境 ----FROM harbor.leops.local/common/runtime/nginx-csr:1.26 AS runningARG APP_ENV=test \APP=undefine \GIT_BRANCH= \GIT_COMMIT_ID=ENV APP_ENV=$APP_ENV \APP=$APP \GIT_BRANCH=$GIT_BRANCH \GIT_COMMIT_ID=$GIT_COMMIT_IDWORKDIR /appCOPY --from=builder /app_build/dist /app/
Vue 多阶段构建关键点解析
这个 Dockerfile 分为两个明确的阶段:
- 编译阶段 (builder):
• 使用我们创建的 Node.js 工具镜像
• 接收构建参数(环境、应用名称、Git 信息等)
• 先复制依赖描述文件安装依赖,最大化利用缓存
• 使用 BuildKit 缓存加速 npm 依赖下载和安装
• 执行对应环境的构建命令,生成静态文件 - 运行阶段 (running):
• 使用我们准备的 Nginx CSR 运行环境镜像
• 仅从编译阶段复制生成的 dist 目录中的静态文件
• 继承环境变量用于版本跟踪和问题排查
• 由 Nginx 提供高性能的静态文件服务
构建应用镜像
执行以下命令构建示例应用:
bash /data/dockerfiles-base/app-build/build-app.sh dev ci-demo-vue
构建完成后,会生成如下格式的镜像标签:
harbor.leops.local/dev/ci-demo-vue:master-256c81b-202504290330
准备示例应用 Nuxt
接下来,我们获取一个 Nuxt.js 应用示例,Nuxt 是基于 Vue.js 的服务端渲染框架:
git clone https://github.com/lework/ci-demo-nuxt.git
cd ci-demo-nuxt
Nuxt 应用 Dockerfile 详解
下面是一个 Nuxt SSR 应用的多阶段构建 Dockerfile:
#syntax=harbor.leops.local/library/docker/dockerfile:1
#
# ---- 编译环境 ----FROM harbor.leops.local/common/tools/node:22 AS builderARG APP_ENV=test \APP=undefine \GIT_BRANCH= \GIT_COMMIT_ID=ENV APP_ENV=$APP_ENV \APP=$APP \GIT_BRANCH=$GIT_BRANCH \GIT_COMMIT_ID=$GIT_COMMIT_IDWORKDIR /app_buildCOPY package.json package-lock.json ./RUN --mount=type=cache,id=${APP}-npm,target=/root/.npm \--mount=type=cache,id=${APP}-node_modules,target=/app_build/node_modules \npm installCOPY ./ ./RUN --mount=type=cache,id=${APP}-npm,target=/root/.npm \--mount=type=cache,id=${APP}-node_modules,target=/app_build/node_modules \npm run build:${APP_ENV}#
# ---- 运行环境 ----FROM harbor.leops.local/common/runtime/pm2:node-22 AS runningARG APP_ENV=test \APP=undefine \GIT_BRANCH= \GIT_COMMIT_ID=ENV APP_ENV=$APP_ENV \APP=$APP \GIT_BRANCH=$GIT_BRANCH \GIT_COMMIT_ID=$GIT_COMMIT_IDWORKDIR /appCOPY --from=builder --link --chown=999:999 /app_build/.output /appCMD ["bash", "-c", "exec pm2-runtime start ecosystem.config.js --json --env ${APP_ENV}"]
Nuxt 多阶段构建关键点解析
这个 Dockerfile 同样分为两个阶段,但与 Vue 应用不同:
- 编译阶段 (builder):
• 同样使用 Node.js 工具环境,流程与 Vue 应用相似
• 构建产物是 .output 目录,而非 dist 目录 - 运行阶段 (running):
• 使用 PM2 运行环境镜像,而非 Nginx
• 使用 --link 优化 BuildKit 缓存策略
• 设置正确的文件所有权 (999:999 对应 nonroot 用户)
• 使用 PM2 运行时启动服务端应用,支持环境变量切换
构建 Nuxt 应用镜像
执行以下命令构建示例应用:
bash /data/dockerfiles-base/app-build/build-app.sh dev ci-demo-nuxt
构建完成后,会生成如下格式的镜像标签:
harbor.leops.local/dev/ci-demo-nuxt:master-e13ed12-202504292036
版本控制
完成构建后,将所有文件提交到 Git 仓库进行版本控制:
git add -A .
git commit -m "feat: add node"
git push
运行应用容器
最终,我们可以运行构建好的应用容器,并验证其功能。
容器运行与验证
# 运行 Vue 前端容器
docker run --rm -d --name ci-demo-vue -p 18084:80 harbor.leops.local/dev/ci-demo-vue:master-256c81b-202504290330# 运行 Nuxt 服务端容器
docker run --rm -d --name ci-demo-nuxt -p 18085:3000 harbor.leops.local/dev/ci-demo-nuxt:master-e13ed12-202504292036# 访问应用
curl http://localhost:18084/
curl http://localhost:18085/# 查看日志
docker logs ci-demo-vue
docker logs ci-demo-nuxt# 停止容器
docker stop ci-demo-vue
docker stop ci-demo-nuxt
生产环境最佳实践
在生产环境中部署 Node.js 应用容器时,建议遵循以下最佳实践:
- 资源限制:使用 --memory 和 --cpus 设置容器资源上限,防止单个应用占用过多资源
- 健康检查:配置健康检查端点和容器健康检查,及时发现问题
- 日志管理:采用集中式日志收集系统,如 ELK 或 Loki,便于问题排查
- 环境变量:通过环境变量注入配置,实现同一镜像在不同环境运行
- 网络设置:仅暴露必要端口,使用内部网络进行服务间通信
- 容器编排:在生产环境中使用 Kubernetes 或 Docker Swarm 进行容器编排和管理
总结
通过本实践篇的学习,我们成功为两种典型的 Node.js 应用场景构建了优化、安全且高效的 Docker 镜像:
-
前端应用 (CSR):使用 Nginx 托管静态文件,具有高性能和优化的缓存策略
-
后端应用 (SSR/API):使用 PM2 管理 Node.js 进程,提供稳定可靠的服务
我们的解决方案具有以下优势: -
分层设计:工具环境和运行环境分离,职责明确
-
多阶段构建:大幅减小最终镜像体积,提高部署效率
-
安全性:使用非 root 用户运行应用,减少安全风险
-
缓存优化:合理利用 BuildKit 缓存加速重复构建
-
标准化:统一的构建流程和镜像结构,便于团队协作和自动化部署
这种方法不仅适用于示例中的 Vue 和 Nuxt 应用,也可以轻松扩展到其他 Node.js 框架和应用类型,为现代 Web 应用的容器化部署提供了可靠的参考方案。