Ansible 批量部署 Node.js 服务:ZIP 直解与共享目录分发两种方案对比

Ansible 批量部署 Node.js 服务:ZIP 直解与共享目录分发两种方案对比

在 Windows 服务器集群中批量更新 Node.js 代码,核心诉求是"停服务 → 覆盖代码 → 起服务",同时保留远程 node_modules 不动。本文提供两种落地方案:方案一 用 win_unzip 直接解压覆盖,方案二 借助共享目录中转分发。两者各有适用场景,方案仅供参考,请根据自己的实际情况使用合适的方案和调整合适的playbook。

背景与前提

假设 zip 包中已确认不包含 node_modules ,因此无论哪种方案,远程服务器上已有的依赖目录都不会被触碰。服务通过 Windows 服务(NSSM 或其他方式)托管,服务名假设为 MyNodeApp,目标部署目录为 C:\App\my-node-project。

两种方案共享相同的"停止/启动服务"逻辑,差异在于代码文件如何到达目标机器并覆盖。


方案一:ZIP 直解覆盖(逐机传输)

适用场景

服务器数量较少(10 台以内)、zip 包体积不大(几十 MB 级别)、各机器网络互通性一般、不想额外搭建共享存储。

核心逻辑

Ansible 通过 win_copy 将 zip 包逐个上传到每台目标机,再用 community.windows.win_unzip 解压到部署目录。win_unzip 原生支持 zip 格式,解压时覆盖同名文件,不会删除目标目录中 zip 里不存在的旧文件。

Playbook 示例

yaml 复制代码
---
- name: 方案一 - ZIP直解部署 Node.js 服务
  hosts: windows_servers
  vars:
    service_name: "MyNodeApp"
    app_dir: "C:\\App\\my-node-project"
    zip_source: "files/node-app.zip"
    remote_zip: "C:\\Temp\\node-app.zip"

  tasks:
    - name: 停止 Node.js 服务
      ansible.windows.win_service:
        name: "{{ service_name }}"
        state: stopped

    - name: 上传代码包到目标机
      ansible.windows.win_copy:
        src: "{{ zip_source }}"
        dest: "{{ remote_zip }}"

    - name: 解压代码包到应用目录(覆盖同名文件)
      community.windows.win_unzip:
        src: "{{ remote_zip }}"
        dest: "{{ app_dir }}"
        rm: true

    - name: 启动 Node.js 服务
      ansible.windows.win_service:
        name: "{{ service_name }}"
        state: started

关键注意点

win_unzip 不是幂等模块 。官方文档明确说明它"not really idempotent",每次执行都会解压并报告 changed。这意味着即使代码没变,Playbook 也会显示变更。如果你用 --check 做预检,结果会始终显示"将发生变更",这是预期行为,不影响实际正确性。

覆盖不等于同步 。win_unzip 只覆盖 zip 中存在的文件,不会删除目标目录里 zip 已移除的旧文件。如果你的版本迭代涉及删除文件 (比如废弃了某个 legacy.js),旧文件会残留在部署目录中。如果需要严格对齐目录内容,方案一不适用,或者需要在解压前额外执行清理逻辑(但保留 node_modules)。

大文件传输效率 。win_copy 通过 WinRM 传输,官方文档指出它"not a very efficient transfer mechanism",大文件场景下 base64 编码开销明显。如果 zip 包超过 50MB 且节点较多,方案一的耗时会显著上升。


方案二:共享目录分发(一次上传,多次拉取)

适用场景

服务器数量较多(几十台以上)、zip 包较大、所有目标机都能访问同一个 SMB 共享路径(如 \\fileserver\deploy$\)。核心思路是控制机只上传一次到共享目录,各目标机从共享目录拉取,避免 WinRM 逐机传输的重复开销。

核心逻辑

先用 win_copy 将 zip 包上传到共享目录(如果控制机可以直接写入共享路径,也可以用 ansible.builtin.copy 或 win_copy 的 remote_src 变体)。然后在每台目标机上,用 PowerShell 的 Copy-Item 从共享路径拉取 zip 到本地临时目录,再解压覆盖。

这里需要用到 win_copy 的 remote_src: true 参数,让 Ansible 在远程主机上执行复制,源路径和目标路径都在远程端,不经过控制机中转。

Playbook 示例

yaml 复制代码
---
- name: 方案二 - 共享目录分发部署 Node.js 服务
  hosts: windows_servers
  vars:
    service_name: "MyNodeApp"
    app_dir: "C:\\App\\my-node-project"
    share_zip: "\\\\fileserver\\deploy$\\node-app.zip"
    local_zip: "C:\\Temp\\node-app.zip"

  tasks:
    - name: 停止 Node.js 服务
      ansible.windows.win_service:
        name: "{{ service_name }}"
        state: stopped

    - name: 从共享目录拉取代码包到本地
      ansible.windows.win_copy:
        src: "{{ share_zip }}"
        dest: "{{ local_zip }}"
        remote_src: true

    - name: 解压代码包到应用目录(覆盖同名文件)
      community.windows.win_unzip:
        src: "{{ local_zip }}"
        dest: "{{ app_dir }}"
        rm: true

    - name: 启动 Node.js 服务
      ansible.windows.win_service:
        name: "{{ service_name }}"
        state: started

前置条件:上传 zip 到共享目录

方案二需要一个独立的准备步骤:先把 zip 包放到共享路径。这个步骤只执行一次,不依赖目标机组:

yaml 复制代码
- name: 上传代码包到共享目录
  hosts: localhost
  tasks:
    - name: 复制 zip 到 SMB 共享
      ansible.builtin.copy:
        src: "files/node-app.zip"
        dest: "/mnt/deploy_share/node-app.zip"
      # 假设控制机已挂载 SMB 共享到 /mnt/deploy_share

如果控制机是 Linux 且没有挂载 SMB,也可以用 win_copy 先传到一台"跳板"Windows 机器,再从那台机器复制到共享目录。具体方式取决于你的环境拓扑。

关键注意点

共享目录的访问权限。执行 Ansible 任务的账户必须对共享路径有读取权限。如果使用域账户运行 WinRM 连接,通常可以直接访问域内共享;如果是本地账户,需要在共享服务器上为该账户单独授权。

remote_src: true 的行为 。当 src 和 dest 都在远程主机上时,win_copy 不会经过控制机,直接在目标 Windows 机器内部完成文件复制。但注意 src 如果是 UNC 路径(\\server\share\file),实际上涉及网络 IO,速度取决于目标机到文件服务器的网络质量。

共享目录方案的"一次上传"优势只有在节点数足够多时才明显。如果只有 3 台机器,方案一的逐机上传和方案二的"上传一次 + 三次拉取"差别不大,反而方案二多了一个共享目录的维护成本。


选型建议

维度 方案一:ZIP 直解 方案二:共享目录
节点数量 少(≤10) 多(>10)
ZIP 体积 小(<50MB) 大(>50MB)
网络要求 控制机到各节点 WinRM 可达 各节点到 SMB 共享可达
额外基础设施 无 需要 SMB 共享(或类似文件服务)
传输效率 逐机重复传输 一次上传,N 次拉取
Playbook 复杂度 低 中(多一个共享目录准备步骤)
覆盖语义 同方案二 同方案一

推荐决策路径 :先数节点。≤10 台且 zip 不大,直接用方案一,简单直接。>10 台或 zip 较大,评估共享目录是否现成可用。如果有现成的部署共享盘(很多企业环境都有),方案二能显著减少部署窗口时间。如果没有,搭建共享目录的运维成本可能抵消收益,此时方案一配合 serial 分批执行仍是务实选择。


作者简介

Moshow,一名热爱技术与分享的 Technical Lead & 数据科学家 & SpringBoot 专家。CSDN 博客之星 2025 年度 TOP100,Github 4K stars。

持有多项权威认证,包括:GCA 谷歌云架构师认证、Neo4j 数据科学家认证、阿里云 ACP 认证、金蝶高级 CRM 供应链工程师认证、PMP 项目管理认证、CSPM 项目管理专业人员能力评价认证、广东省人工智能训练师认证。

相关推荐
垚垚学技术_聚焦云原生4 小时前
Ansible Playbook 进阶:when 条件、loop 循环、handlers 触发器落地
linux·ansible·自动化运维·devops
少年的范儿8 小时前
Windows 安装 Node.js 教程:nvm 版本管理 + pnpm 安装配置
windows·npm·node.js
peach9 小时前
同样是装依赖,为什么 npm 长成 node_modules,Maven 长成 .m2
npm·node.js·maven
FungLeo12 小时前
成为全栈·Go 后端篇·重写之前先锁基线:契约、Node 版本与行为清单
golang·node.js·go·openapi·契约测试·成为全栈
Moshow郑锴12 小时前
GCP MIG 重建后 Ansible 初始化实战:PowerShell + NSSM 挂载 Windows 服务
windows·ansible
FungLeo1 天前
成为全栈·Go 后端篇·契约一致性与差分测试:两套实现如何互相校验
golang·node.js·go·合约测试·差分测试·成为全栈·api兼容
月夏1 天前
前端第一次接自己的后端:mock 切真接口踩了 2 个坑,组件只改了一行 import
前端·后端·node.js
SEO_juper2 天前
Node.js 页面主题聚焦度检测:title 和正文各说各的,AI 也读不懂
开发语言·人工智能·node.js·seo·外贸独立站
SEO_juper2 天前
Node.js 重定向链检测工具:301 跳了几跳、有没有成环,一次看清
xml·开发语言·python·node.js·json·bash