--- url: /sre/devops/foundation/production-env.md description: >- 理解生产环境的两个最低必要条件——7×24 与公网 IP;完成第一次 SSH + scp 手动部署,并用数学语言立下运维工作定义 $\forall x \in X, f(x)=c$。 --- 在上一篇文章中,已经在本地用 Nginx 跑通了静态站点。然而,`http://localhost` 只有你自己的电脑能访问。如果想让网站发布到互联网,需要将它部署到 {{term:生产环境}}。 ## 什么是生产环境 从本地开发到线上生产,核心变化在于服务对象的改变。我们用一张表直观对比: | 维度 | 开发环境 (Development) | 生产环境 (Production) | | :--- | :--- | :--- | | **服务用户** | 你自己 | 互联网真实用户 | | **运行时间** | 随用随开 | 7 × 24 小时无间断 | | **网络边界** | 本地局域网 | 互联网公网可达 | | **数据故障** | 随时重来 | 线上事故 | 要让网站上线,生产环境必须跨越两个最低门槛:**7 × 24 小时运行** 与 **具备公网 IP**。对于个人而言,最快捷、低成本的达成方案就是租用一台 {{term:云服务器}}。 ## 为什么选择云服务器 满足 7 × 24 小时运行和公网 IP 这两个条件,最短路径就是云服务器。云厂商对新用户扶持力度大,包年购买一台入门的 **2 核 2G(2C2G)** 轻量服务器通常只需数十元。 在选购云服务器时,规格选择 **2 核 2G**,系统镜像首选 **Ubuntu 22.04 或 24.04 LTS**,避开预装各种面板的系统,保持环境纯净。 您可以通过我的专属优惠链接直接选购: * [阿里云上云优选通道(专享优惠)](https://www.aliyun.com/daily-act/ecs/activity_selection?userCode=d1pmxxar) * [腾讯云轻量服务器通道(专享优惠)](https://curl.qcloud.com/LL4937aa) > **💡 运维小经验:2C2G 的配置为什么够用?** > Linux 生产服务器无须运行图形界面,系统本身开销通常不到 200MB。Nginx 作为静态资源服务器时也很轻量,2GB 内存足以支撑个人站点和学习环境。 ## 准备工作 ### SSH 连接服务器 购买云服务器后,你通常会拿到三类信息: | 信息 | 作用 | | --- | --- | | 公网 IP | 从互联网访问这台服务器 | | 登录用户 | 不同厂商系统可能不同,推荐使用默认用户 `ubuntu` | | 登录密码或 SSH 密钥 | 用来证明“你有权限登录” | 连接服务器有两种常见方式。 **方式一:在本地终端使用 SSH** 这是最推荐掌握的方式,因为后续部署、上传文件、排查问题都离不开命令行: ```bash ssh ubuntu@your-public-IP ``` 如果云厂商默认用户不是 `ubuntu`,就把命令里的 `ubuntu` 换成对应用户名,高权限操作通过 `sudo` 临时提权,能降低误删系统文件、误改全局配置的风险。 ```bash whoami ``` 可以查看当前登录用户。 首次 SSH 连接会提示确认主机指纹,输入 `yes`,然后输入服务器登录密码即可登录。 连接成功后,你会看到类似这样的终端提示: ```text Welcome to Ubuntu 24.04 LTS (GNU/Linux 5.15.0-xx-generic x86_64) ubuntu@your-server:~$ ``` 这说明你已经登录到一台运行在云端的 Linux 服务器上了。 **方式二:使用云厂商提供的 WebUI** 阿里云、腾讯云等控制台通常都提供“远程连接”之类的浏览器入口(腾讯云侧常见为 OrcaTerm 等),不需要你本地先配好 SSH。 WebUI 适合第一次确认服务器能否登录。但它更像备用入口:后续 `scp` 上传、脚本化部署、CI 自动发布,都会更依赖本地终端里的 SSH。 ### 密码登录和 SSH 密钥有什么区别 SSH 是连接服务器的协议,密码和 SSH 密钥是两种不同的认证方式。 | 认证方式 | 怎么理解 | 优点 | 缺点 | | --- | --- | --- | --- | | 密码登录 | 输入服务器账号密码 | 简单直观,适合初学者第一次登录 | 容易被弱密码、撞库、暴力破解影响 | | SSH 密钥 | 本地保存私钥,服务器保存公钥 | 更安全,适合长期使用和自动化部署 | 初学者第一次配置稍微复杂 | 我的建议是:**初学者第一次学习可以先用 `ubuntu` 用户 + 密码登录,跑通完整流程;一旦网站真正长期在线,就切换到 SSH 密钥登录,并关闭 root 密码登录。** ### 把终端这把剑磨快 开发写代码、运维连服务器、排障看日志——软件从业者几乎每天都要和终端、Shell 打交道。GUI 能完成一部分工作,但一到 SSH、脚本、批量操作、远程排查,终端才是真正的主战场。 既然这把剑天天出鞘,就值得认真武装:选一个顺手的终端 App,配好 Shell 与必备插件,让补全、高亮、分屏成为肌肉记忆。**App 决定长相,插件决定手感**,两者叠加,不冲突。Windows 用户用 PowerShell 即可上手;想和云服务器对齐,装 **WSL Ubuntu**(Linux 子系统)并用 **Windows Terminal** 打开——本地与云上同一套 Linux 习惯,不用来回翻译。Mac 自带 Terminal 已够用;想换更现代的 AI 终端,[Warp](https://www.warp.dev/) 值得一试。选型见延伸阅读。 按环境把“剑”磨利: | 使用环境 | 推荐阅读 | | --- | --- | | Mac | [Mac 终端起手式:iTerm2、zsh 与插件](../../tools/mac-terminal-starter.md) | | Windows | [Windows 上使用 WSL Ubuntu 开发](../../tools/wsl-ubuntu-dev.md) | | 云服务器 | [云服务器 Linux 起手式优化](../../tools/linux-guide-optimization.md) | ### 安装 Nginx 在服务器上安装 Nginx(和本地一样的步骤): ```bash sudo apt update sudo apt install nginx ``` 验证安装: ```bash nginx -v ``` 启动 Nginx: ```bash sudo systemctl start nginx ``` > 如果浏览器访问公网 IP 加载超时,优先检查云控制台的 {{term:安全组}} 或“防火墙”是否已放通 TCP 80 端口。 此时在浏览器访问 `http://your-public-IP`,应该能看到 Nginx 默认欢迎页面。恭喜,你的服务器已经在公网可访问了。 ## 手动部署 现在把本地的网站文件传到服务器上。 ### 本地构建 在本地项目目录中构建生产版本: ```bash pnpm run docs:build ``` 构建产物在 `docs/.vitepress/dist/` 目录下。 ### scp 上传到服务器 使用 `scp` 命令将构建产物上传到服务器: ```bash scp -r docs/.vitepress/dist ubuntu@your-public-IP:~/ ``` 这条命令做了什么: * `scp`:基于 SSH 的文件复制命令 * `-r`:递归复制目录 * `docs/.vitepress/dist`:本地构建产物目录 * `ubuntu@your-public-IP:~/`:复制到服务器 `ubuntu` 用户的家目录 上传完成后,登录服务器,将文件复制到 Nginx 静态资源目录: ```bash sudo cp -r ~/dist/* /var/www/html/ ``` 静态资源已经复制到 Nginx 目录,不需要重启 Nginx。刷新浏览器访问 `http://your-public-IP`,看到你的网站了——这就是 {{term:上线}}。 ## 上线只是开始 到这里,你已经完成了一次完整的“本地 → 服务器”部署。流程是通的,但用不了多久你就会遇到这些问题。 ### 本地和服务器环境不一致 部署不是一步到位的。第一次把网站放到服务器上,大概率不会一次成功——Nginx 配置路径不对、权限不够、文件位置不对,都需要登录服务器反复调试。这意味着你需要同时维护**本地开发环境**和**服务器生产环境**两套配置。 回顾一下:本地 Mac 上安装 Nginx 用 `brew install nginx`,服务器 Ubuntu 上用 `apt install nginx`,连安装方式都不一样。配置文件路径也不同——Mac 在 `/opt/homebrew/etc/nginx/`,Ubuntu 在 `/etc/nginx/`。 好消息是,这些差异通常只需要首次部署时排查清楚,后续就稳定了。坏消息是,**可迁移性很差**——如果你换了一台服务器(比如从阿里云换到腾讯云),或者换了操作系统版本,之前踩过的坑可能要重新踩一遍。环境配置的细节散落在操作记忆里,没有被固化下来。 当前部署的是纯静态资源,跨平台传输没有问题。但涉及编译产物时,环境差异的影响会更加突出,我们在[第 04 篇](./server-side-deploy.md)展开。 ### 每次发布都要手动 scp 软件交付其实分两段,**节奏完全不同**: | 阶段 | 角色 | 环境 | 节奏 | 在干什么 | | --- | --- | --- | --- | --- | | **开发** | 开发者 | 本地 | **循环多次** | 改 → 预览 → 再改,直到满意 | | **部署** | 开发者操作 | 本地 → 云服务器 | **验收后通常一次** | 构建 → 上传 → 落盘 → 公网验证 | 一句话:**多次 dev,一次 prod**——本地转很多圈;生产发布是“做完再发”,不是边改边发。 ![多次开发闭环、一次生产发布](https://media.xiaolin.fun/docs/img-production-env/diagram-dev-prod-loop.png) 本地热更新很快;真正费事的是验收后那一次跨机器发布——构建在本地,上传与落盘在云服务器,验证还要看公网。步骤一多就容易漏,表面都是“页面没变化”。 不过发布链本身可以**模板化**:构建 → 上传 → 落盘 → 验证。这就是 {{term:流水线}}(Pipeline)的雏形——也是本系列给“运维工作”下定义的入口。 ### 用数学语言定义运维工作 业务工作回答“**改什么**”:文案、功能、逻辑。\ 运维工作回答“**怎么稳定地让改动变成线上可用状态**”:同一套动作可重复、可验证,不因业务细节每次重写。 本系列把(自动化)运维先钉成一句定义: > **在约定的规范集合内,用固定结构的运维动作,把任意一次合法业务变更,稳定变成约定的发布结果。** 写成数学形态: $$ \forall x \in X, \quad f(x) = c $$ | 符号 | 含义 | 在本篇的例子 | | --- | --- | --- | | $x$ | 一次具体的业务变更(源码改动) | 改了一段 Markdown | | $X$ | **规范集合**:这套流程承诺能接管的变更范围 | “VitePress 静态站点” | | $f$ | **运维动作**:固定结构的动作链 | 构建 → 上传 → 落盘 → 验证 | | $c$ | **约定结果**:产物路径、落盘位置、验证方式一致 | `dist/` → `/var/www/html`,公网可访问 | 读法:**只要 $x$ 仍落在 $X$ 内,$f$ 就不改。** 业务可以改一万次;运维侧重复同一条动作链。 **关键在于 $X$ 与 $f$ 的关系,而不是公式本身:** * 业务仍在 $X$ 内(文案、参数、局部逻辑)——$f$ 不动。 * 变化**突破** $X$(换构建工具、引入新中间件、交付形态变了)——$f$ 才演进。 * $f$ 演进后,对更大的 $X'$ 重新闭合。 $$ X \xrightarrow{\text{突破}} X' \quad\Rightarrow\quad f \to f', \quad \forall x' \in X': f'(x') = c' $$ 因此,**运维工作的稳态是关于 $X$ 的闭包**:集合内业务怎么热闹都与 $f$ 无关;只有边界被突破时,$f$ 才升级一次,然后再次回到“对所有 $x$ 输出恒定”。\ “集合 → 闭包 → 扩容 → 再闭包”——这正是后续 DevOps、CI/CD 能持续运转的基石:**让运维动作稳定,业务变化自由。** 套回今天这次部署: * $f$ = 构建 → 上传 → 落盘 → 验证(仍是手动四步) * $X$ = “VitePress 静态站点” * $c$ = `dist/` 经 scp 落到 `/var/www/html`,Nginx 直接服务 下次只改文案,**这四步还是这四步**——就是“$x \in X$,$f$ 不变”。 当前缺口不是定义不成立,而是 **$f$ 的每一步仍靠人敲**:定义有了,自动化还没有。 ### 没有版本记录 运维领域的常规做法是:部署前先备份当前版本,再覆盖新版本。比如把旧的 `dist` 重命名为 `dist-20260713`,然后再上传新的 `dist`。这样出了问题至少还能手动回退。 但手动备份有三个绕不开的断点: **第一,备份这一步本身会被跳过。** “这次改动很小,应该没问题”、“备份目录越来越多,占空间”、“上次备份了也没用过”——这些理由每次都成立,结果就是备份时有时无,**一旦跳过就回不去**。 **第二,即使做了备份,也只是“一份旧的目录”。** `dist-20260713` 对应的是哪次代码变更?改了什么?只有日期,没有上下文。事后想排查“当时为什么挂”,找不到入口。 **第三,回退要重做一遍部署动作。** 出问题后,要手动把备份目录重命名回去,再手动重启服务。回退本身就是另一轮容易出错的手动流程——一次部署出错的概率不高,但**部署 + 回退都要靠人不出错,概率就高了**。 这三个断点叠在一起,手动备份有点像**错题本**:抄了但没看,看了又找不到,找到也用不上——形式上在做版本管理,实际上只是一套越来越繁琐的运维琐事。 更隐蔽的是**版本不一致**。本地构建的 `dist` 是基于当前代码生成的,但服务器上跑的 Nginx 配置、环境变量、依赖版本可能是上次部署时遗留的。你更新了 `dist`,却忘了同步 Nginx 配置;或者反过来,改了 Nginx 配置,但 `dist` 还是旧的。新旧版本混在一起,踩坑是常态——页面白屏、接口 404、样式错乱,排查半天才发现是前后端版本对不上。 **手动备份解决不了 {{term:版本管理}}。** 它只是把“覆盖”变成了“覆盖前先复制一份”,但“历史可追溯”、“变更可对比”、“回退可一键”这些能力,手动方式都给不了。这些不是备份的问题,是版本管理的问题——下一步我们会看到,Git 天然就有这套能力。 ## 流程旁白 * **谁发起**:开发者(本人)决定“可以上线了”。 * **谁执行**:同一人完成构建、`scp`、落盘、公网验证。 * **谁审批**:本步无审批——个人学习场景下,发起即执行。 * **职责边界**:业务改动可以任意试;**服务器上的发布动作与回退**仍未成文,默认全压在执行者记忆里。后续篇会逐步把边界写清楚。 ## 小结 生产环境的两个最低必要条件是 7×24 和公网 IP,云服务器是最短路径。通过 SSH 连接服务器 + scp 上传文件,就能完成一次最朴素的手动部署。 本篇同时埋下运维定义:$\forall x \in X,\ f(x)=c$——规范集合内动作恒定。手动部署能跑通,但 $f$ 的每一步仍靠人。下一步引入 Git 和 GitHub,先解决版本与可追溯,再谈如何把 $f$ 跑稳。 ## 思考 1. 如果你的服务器宕机了,用户访问会发生什么?生产环境的“7×24”是由谁保证的? 2. `scp` 和直接在服务器上改文件有什么区别?为什么我们选择在本地构建再上传? 3. 你在 `scp` 时输错了目标路径,覆盖了服务器上的其他文件,怎么办? ## 延伸阅读:终端怎么选 终端 App 和 Shell 插件是两层叠加:**App 负责长相,插件负责手感**。选型优先 **稳定、延迟低、习惯顺手**——花哨功能其次。 ### 终端 App | 平台 | 默认 / 推荐 | 一句话特点 | | --- | --- | --- | | Windows | **PowerShell**(系统自带) | 原生上手;命令习惯偏 Windows | | Windows | **Windows Terminal + WSL Ubuntu** | 学 Linux 运维的默认组合 | | macOS | **Terminal.app**(系统自带) | 够用,配 zsh 已现代化 | | macOS | [iTerm2](https://iterm2.com/) | 分屏、搜索、主题成熟 | | macOS | [Warp](https://www.warp.dev/) | AI 终端:补全、解释、工作流内置 | > **为什么还要装 Windows Terminal?** WSL Ubuntu 自带的是系统 Console 里的基础窗口——能用但丑、无分屏、字体受限。Windows Terminal 是独立的“终端前端”,能挂 WSL、PowerShell、SSH 等多个 shell 到同一窗口,标签页 / 分屏 / 字体 / 配色统一管。这是两个不同的东西,WSL 自带的那个不是 Windows Terminal。 > > Mac 默认 Terminal 与云服务器命令习惯完全兼容(都是 Unix 一脉),换 Warp / iTerm2 是体验升级,不是必需。 ### Shell 插件(zsh,Mac / WSL Ubuntu / Linux 都适用) | 插件 | 作用 | | --- | --- | | [oh-my-zsh](https://ohmyz.sh/) | zsh 配置框架,主题 + 插件管理 | | [git](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/git) | 大量 `gst` / `gco` / `gp` 别名 + 提示 | | [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting) | 合法命令变绿、非法变红——输错当下就知道 | | [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions) | 按历史灰字提示,→ 一键采纳 | | [zsh-completions](https://github.com/zsh-users/zsh-completions) | 补全 `kubectl` / `docker` / `aws` 等额外命令 | **最小可用组合**:oh-my-zsh + git + zsh-syntax-highlighting + zsh-autosuggestions。Windows 原生 PowerShell 走自己体系(`PSReadLine`、`posh-git`),不通用。 ## 参考 1. [SSH 官方文档](https://www.openssh.com/manual.html) 2. [scp 命令使用指南](https://linux.die.net/man/1/scp) 3. [腾讯云轻量应用服务器](https://cloud.tencent.com/product/lighthouse) 4. [阿里云轻量应用服务器](https://www.aliyun.com/product/swas)