这一篇用一步步、可复制的方式,教你把一个 HelloWorld 程序从本地写好到实现自动化部署。我们会从准备工作开始,讲清楚每一步为什么要这么做,再给出 Docker 化、CI(以 GitHub Actions 为例)、镜像推送、到云端或 VPS 的自动化发布与回滚策略,顺便覆盖测试、日志与常见故障排查,让你能在不同场景下迅速搭建可靠流水线。


为什么要做自动化部署(先讲为什么)
想象一下,你在本地改了几行代码,然后要把它放到线上:传统方式是人工打包、传文件、重启服务,过程容易出错、费时,而且难以回溯。自动化部署就是把这套重复工作交给程序做,保证每次上线都按同样步骤走,能快速回滚、可观测并能融入代码审查与测试流程。
先了解全貌(整体架构)
这次教程的目标是把 HelloWorld 应用做成“可发布”的单元,关键环节如下:
- 本地开发:实现并测试 HelloWorld 应用(示例用 Node.js/Express)
- 容器化:用 Docker 将应用打包为镜像
- 版本与仓库:把代码放到 Git(例如 GitHub)
- CI/CD:用 GitHub Actions 做构建、测试、打镜像并推到镜像仓库(Docker Hub 或私有仓库)
- 部署目标:通过 docker-compose 或 Kubernetes 将镜像部署到目标环境(VPS、云主机或云 k8s)
- 配套:日志、健康检查、回滚策略与监控
准备工作(所需工具与账号)
- 本地开发环境:Node.js(或你选择的语言运行时)、Git、Docker
- 代码仓库:GitHub(或 GitLab/Bitbucket)账号与仓库
- 镜像仓库:Docker Hub 或私有镜像仓库(需账户和仓库名)
- 部署主机:VPS(如一台 Ubuntu)、或云服务(带 Kubernetes 的集群)
- 可选:域名、HTTPS 证书(letsencrypt)
第一部分:构建一个简单的 HelloWorld 应用
示例:Node.js + Express
把这个当作最小可运行单元,后续所有自动化都围绕它进行。
文件结构示例
- hello-world/
- package.json
- index.js
- README.md
- Dockerfile
index.js(示例代码)
(把下面代码保存为 index.js)
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.send('Hello World');
});
app.listen(port, () => {
console.log(App listening on port ${port});
});
package.json 最小示例:
{
"name": "hello-world",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.18.0"
}
}
本地验证:在项目目录运行 npm install 然后 npm start,访问 http://localhost:3000/ 应能看到 Hello World。
第二部分:容器化(Dockerfile)
把应用打包进镜像,优点是环境一致、部署方便。
示例 Dockerfile(基于 Node 官方镜像)
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV PORT=3000
EXPOSE 3000
CMD ["node", "index.js"]
构建并运行测试镜像:
- 构建:docker build -t yourname/hello-world:local .
- 运行:docker run -p 3000:3000 yourname/hello-world:local
若一切正常,说明容器化成功。
第三部分:将代码推到 Git(示例流程)
- 初始化仓库:git init;添加文件并提交
- 在 GitHub 上创建仓库,然后 git remote add origin <repo-url>;git push -u origin main
保证主分支(main 或 master)有清晰的提交记录,CI 会在推送或 PR 时触发。
第四部分:CI/CD —— 用 GitHub Actions 自动化构建与推镜像
CI 的任务:在每次合并/提交时进行构建、测试(如有)、打镜像并发布到镜像仓库,最后触发部署(如果需要自动部署到生产)。
创建 GitHub Actions 工作流文件
在仓库中创建 .github/workflows/ci.yml,示例内容如下:
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build_and_push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
with:
push: true
tags: yourname/hello-world:${{ github.sha }} , yourname/hello-world:latest</code></pre>
说明:
- secrets.DOCKERHUB_USERNAME、secrets.DOCKERHUB_TOKEN 在仓库设置中配置
- 使用 github.sha 做镜像标签可保证唯一性
- 可在 push 步骤后触发部署步骤或由另一个 workflow 监听镜像仓库事件触发
第五部分:镜像仓库与标签策略
建议使用两类标签:短期可回滚的(如 git sha 或 CI 构建号)和长期稳定的(如 latest、stable、v1.2.3)。
标签
用途
sha(唯一)
精确回滚、追溯镜像来源
latest
方便临时部署或测试(不建议生产永远用 latest)
语义化版本(vX.Y.Z)
用于正式发布
第六部分:部署到服务器(两种常见方式)
方案 A:用 docker-compose(适合单机或少量服务)
在目标主机上准备好 Docker 与 docker-compose,创建 docker-compose.yml:
version: '3.8'
services:
web:
image: yourname/hello-world:latest
ports:
- "80:3000"
restart: always
environment:
- PORT=3000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/"]
interval: 30s
timeout: 5s
retries: 3
部署步骤(手动或自动化脚本):
- ssh 到目标主机
- docker pull yourname/hello-world:latest
- docker-compose up -d
想自动化这步,可以把上面步骤写成一个脚本,并在 CI 完成打镜像后通过 SSH(用 GitHub Actions 的 deploy key 或 actions/ssh)远程触发。
方案 B:用 Kubernetes(适合生产与弹性伸缩)
最小的 Deployment + Service 示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world
spec:
replicas: 2
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: yourname/hello-world:sha-xxxxx
ports:
- containerPort: 3000
livenessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 10
periodSeconds: 20
---
apiVersion: v1
kind: Service
metadata:
name: hello-svc
spec:
type: LoadBalancer
selector:
app: hello
ports:
- port: 80
targetPort: 3000
在 CI 完成镜像推送后,可用 kubectl set image 或 Helm 来更新 Deployment。使用 Rolling Update 策略可以实现无缝切换。
第七部分:发布策略与回滚
- 滚动发布(Rolling Update):逐个替换 Pod,保证可用性
- 蓝绿部署(Blue-Green):新版本在独立环境验收后切换流量
- 金丝雀发布(Canary):先对小部分流量放行,再逐步扩大
回滚策略:
- 保留最近若干个镜像标签(如最近 10 个 sha)
- 在 k8s 中直接 kubectl rollout undo deployment/hello-world
- 在 docker-compose 场景中,通过记录上一个镜像 tag 并 docker-compose pull + up 恢复
第八部分:测试、健康检查与日志
自动化部署不是把镜像推上去就完事了,必须确保可观测性:
- 在容器中加入 healthcheck(Dockerfile 或 docker-compose 的 healthcheck)
- 在 k8s 中配 liveness/readiness probe
- 把日志集中到一个地方(例如 ELK/EFK、Grafana Loki)或至少使用 cloud provider 的日志服务
- 在 CI 中加入基本的集成测试(例如启动镜像后的 HTTP smoke test)
第九部分:安全与凭证管理
几点必须注意的地方:
- 不要在代码中硬编码凭证;在 CI 中使用 secrets 管理(GitHub Secrets、GitLab CI Variables)
- 对容器使用非 root 用户运行(在 Dockerfile 中添加用户)
- 仅暴露必要端口,配置防火墙规则
- 及时扫描镜像依赖漏洞(如使用 Trivy)
第十部分:常见故障与排查思路(便于快速定位问题)
- 服务无法启动:先看容器日志(docker logs / kubectl logs),确认依赖是否缺失或端口占用
- 镜像拉取失败:检查镜像名称、tag 是否存在,CI 推送是否成功;检查仓库权限
- 健康检查失败:看看 probe 配置是否合理,是否需要延长 initialDelaySeconds
- 部署后功能异常:回滚到上一个已知良好镜像并比对差异
第十一步:示例完整流水线(把所有环节串起来)
下面给出一个简化的流水线思路,按步骤执行即可:
- 本地开发并通过单元测试,提交到 feature 分支并发起 PR
- CI(PR 检查)跑 lint、unit test;通过后允许合并到 main
- 合并触发构建 job:构建镜像、打标签为 sha、推向镜像仓库
- 构建成功后触发部署 job(或由另外的 CD 系统触发):在目标环境执行拉镜像并更新服务
- 部署后执行 smoke test;若失败自动回滚同时通知负责人
- 部署成功则标记发布版本(git tag)并关闭相关 issue
第十二步:小团队与初学者的简化建议
- 先用 docker-compose + 手动脚本把流程跑通,再迁移到 k8s
- 把 CI 的 credentials 用 secrets 管理,避免在仓库泄露
- 先把自动化范围控制在“构建-推镜像-部署到测试机”,生产环境再加审批与金丝雀
工具清单与命令速览(便于参考)
任务
常用命令/工具
本地运行
npm install;npm start;curl http://localhost:3000/
构建镜像
docker build -t yourname/hello-world:tag .
推镜像
docker login;docker push yourname/hello-world:tag
docker-compose 部署
docker-compose pull;docker-compose up -d
k8s 部署
kubectl apply -f deployment.yaml;kubectl set image
CI 配置
GitHub Actions (.github/workflows/*.yml)
调优与扩展方向(写进未来计划)
- 把构建缓存与多阶段构建引入 Dockerfile 以减小镜像体积
- 引入镜像安全扫描(Trivy)与依赖漏洞报警
- 将部署流程从简单脚本迁到 Helm,以便管理版本与配置
- 引入监控与告警(Prometheus + Grafana)并与通知系统集成
说到底,自动化部署是一件把重复工作规范化的事情:先把最小可行的过程做通,然后逐步把可靠性、回滚能力、安全性补齐。实践中会遇到网络、凭证和配置差异带来的小坑,遇到就把对策写成脚本并放进 CI,这样下次就少踩一次坑。好了,以上就是把 HelloWorld 做成自动化流水线的全流程,按着步骤走,你应该能把它从开发机「搬」到线上并具备可回滚、可观测的能力。