Init
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
---
|
||||
tags: [cicd, github-actions, docker, kubernetes, pipeline]
|
||||
create time: 2026-05-17 10:00
|
||||
---
|
||||
|
||||
# CI/CD 基础与实践
|
||||
|
||||
## 概述
|
||||
|
||||
本文档带你从零搭建微服务的自动化发布流水线。内容覆盖:从代码提交到镜像构建、安全扫描、Staging 验证、灰度上线的完整流程,以 GitHub Actions 为例进行实战讲解。更多决策分析(如工具选型)参见 [[../03-CICD与GitOps]]。
|
||||
|
||||
## 为什么需要 CI/CD?
|
||||
|
||||
> [!question] 想象一下
|
||||
>
|
||||
> 你有 50 个微服务,每次发布都需要手动 SSH 到服务器、停旧启新、检查日志——如果出错了还得回滚。**人工操作**在这个规模下就是最大的风险源。
|
||||
>
|
||||
> CI/CD 的核心目标:**消除发布日的手工操作**,让任何一次 commit 都可以被安全地部署。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
CODE["代码提交"] --> TEST["测试 & 静态分析"]
|
||||
TEST --> SCAN["安全扫描"]
|
||||
SCAN --> BUILD["构建镜像"]
|
||||
BUILD --> PUSH["推送仓库"]
|
||||
PUSH --> STAGING["Staging 验证"]
|
||||
STAGING -->|"人工审批"| PROD["Production 部署"]
|
||||
|
||||
style CODE fill:#e3f2fd
|
||||
style TEST fill:#fff3e0
|
||||
style SCAN fill:#fce4ec
|
||||
style BUILD fill:#e8f5e9
|
||||
style PUSH fill:#f3e5f5
|
||||
style STAGING fill:#e0f7fa
|
||||
style PROD fill:#c8e6c9
|
||||
```
|
||||
|
||||
## 核心设计原则
|
||||
|
||||
### 一次构建,多处部署
|
||||
|
||||
镜像不随环境重新编译,只改 K8s ConfigMap 或环境变量。Staging 和 Production 用的是**同一个镜像**。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
DEV["开发机"] --> BUILD["构建一次"]
|
||||
BUILD --> REGISTRY["Docker Registry"]
|
||||
REGISTRY --> STAGING["Staging 环境"]
|
||||
REGISTRY --> PROD["Production 环境"]
|
||||
|
||||
STAGING -. "同一镜像" .- PROD
|
||||
|
||||
style DEV fill:#e3f2fd
|
||||
style BUILD fill:#e8f5e9
|
||||
style REGISTRY fill:#f3e5f5
|
||||
style STAGING fill:#fff3e0
|
||||
style PROD fill:#c8e6c9
|
||||
```
|
||||
|
||||
### 语义化版本 + Commit SHA 双标签
|
||||
|
||||
| Tag 类型 | 示例 | 用途 |
|
||||
|----------|------|------|
|
||||
| **Commit SHA** | `sha256:a1b2c3d` | 机器精确引用,保证可回滚 |
|
||||
| **运行序号** | `v123` | 人类快速参考,方便手动回滚 |
|
||||
|
||||
> [!tip] 永远不要用 `latest` 做生产环境的镜像标签。`latest` 可被覆盖,回滚时你不知道回滚到哪一刻的镜像。
|
||||
|
||||
### 路径过滤
|
||||
|
||||
只对相关服务的代码变更触发构建。`order-service` 改了代码,不应该触发 `payment-service` 的 pipeline。
|
||||
|
||||
### 流水线即代码
|
||||
|
||||
Pipeline 配置写在 Git 中(`.github/workflows/`),和源码一起 Review。没人知道它长什么样,就是最好的理由。
|
||||
|
||||
## Pipeline 实战:单服务完整流水线
|
||||
|
||||
以下是一个微服务(以 `order-service` 为例)的 CI/CD 流水线。核心思路:**代码推送到 main 分支 → 测试 → 构建镜像 → 推到 Staging → 人工审批 → 灰度上线**。
|
||||
|
||||
```yaml
|
||||
# .github/workflows/deploy.yml
|
||||
name: Deploy order-service
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "services/order/**" # 只在 order 服务有变更时触发
|
||||
|
||||
env:
|
||||
REGISTRY: registry.example.com
|
||||
IMAGE: order-service
|
||||
|
||||
jobs:
|
||||
# ========== Stage 1: Build & Test ==========
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run tests
|
||||
run: make test
|
||||
|
||||
- name: Security scan (filesystem)
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
scan-type: 'fs'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
docker build \
|
||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ github.sha }} \
|
||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE }}:v${{ github.run_number }} \
|
||||
-f services/order/Dockerfile \
|
||||
services/order
|
||||
|
||||
- name: Push to registry
|
||||
run: |
|
||||
echo "${{ secrets.REGISTRY_PASSWORD }}" | \
|
||||
docker login -u ${{ secrets.REGISTRY_USER }} --password-stdin
|
||||
docker push ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ github.sha }}
|
||||
docker push ${{ env.REGISTRY }}/${{ env.IMAGE }}:v${{ github.run_number }}
|
||||
|
||||
# ========== Stage 2: Deploy to Staging ==========
|
||||
deploy-staging:
|
||||
needs: build-and-test
|
||||
runs-on: ubuntu-latest
|
||||
environment: staging
|
||||
steps:
|
||||
- name: Deploy to staging
|
||||
run: |
|
||||
kubectl set image deployment/order-service \
|
||||
order=${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ github.sha }} \
|
||||
--namespace=staging
|
||||
kubectl rollout status deployment/order-service \
|
||||
--namespace=staging --timeout=120s
|
||||
|
||||
- name: Smoke test
|
||||
run: |
|
||||
# 等待 Pod Ready 后发送探测请求
|
||||
sleep 10
|
||||
ENDPOINT=$(kubectl get ingress order-service \
|
||||
-o jsonpath='{.spec.rules[0].host}' -n staging)
|
||||
curl -f http://$ENDPOINT/health || exit 1
|
||||
|
||||
# ========== Stage 3: Deploy to Production ==========
|
||||
deploy-production:
|
||||
needs: deploy-staging
|
||||
runs-on: ubuntu-latest
|
||||
environment: production # 需人工 Approval Gate
|
||||
steps:
|
||||
- name: Canary release (10% → 50% → 100%)
|
||||
run: |
|
||||
kubectl patch canary order-service --type merge \
|
||||
-p '{"spec":{"weight":10}}'
|
||||
# 等待 10 分钟,检查 error rate / latency
|
||||
echo "Monitor metrics before proceeding..."
|
||||
|
||||
- name: Promote to 50%
|
||||
if: success()
|
||||
run: |
|
||||
kubectl patch canary order-service --type merge \
|
||||
-p '{"spec":{"weight":50}}'
|
||||
|
||||
- name: Full promotion
|
||||
if: success()
|
||||
run: |
|
||||
kubectl patch canary order-service --type merge \
|
||||
-p '{"spec":{"weight":100}}'
|
||||
```
|
||||
|
||||
> [!info] Secret 管理原则
|
||||
>
|
||||
> 所有敏感信息通过 **GitHub Actions Secrets** 存储,按环境分离:
|
||||
>
|
||||
> | Secret 名称 | 用途 | 存放位置 |
|
||||
> |------------|------|---------|
|
||||
> | `REGISTRY_USER` / `REGISTRY_PASSWORD` | 镜像仓库认证 | Repository Settings → Secrets |
|
||||
> | `KUBECONFIG_STAGING` | K8s 集群配置 | Environment: staging |
|
||||
> | `KUBECONFIG_PRODUCTION` | K8s 集群配置 | Environment: production |
|
||||
>
|
||||
> > [!tip] 不要在 YAML 中明文写密码,哪怕加了 `${{ secrets.XXX }}` 的 workflow 在 fork 的 PR 中仍然有泄露风险。对 fork PR 使用 `environment` 保护可以阻止 secrets 暴露。
|
||||
|
||||
## Pipeline 逐阶段解读
|
||||
|
||||
| 阶段 | 做什么 | 为什么重要 |
|
||||
|------|--------|-----------|
|
||||
| **路径过滤** | `paths` 指定只对特定目录变更触发 | 避免每次 PR 都重建所有服务的镜像 |
|
||||
| **Secrets 管理** | GitHub Secrets 按环境分离存储 | 防止密钥泄露到 fork PR 或日志中 |
|
||||
| **安全扫描** | Trivy 在构建前后扫描漏洞 | 把安全问题挡在镜像层 |
|
||||
| **双 Tag 策略** | 同时打 Commit SHA + run number | SHA 用于精确操作,run number 用于快速参考 |
|
||||
| **Environment 保护** | `environment: production` 配置审批人 | GitHub 级别的 gates |
|
||||
| **冒烟测试** | 部署后发送健康探测请求 | 确认 Pod 真正 Ready,而非只等 rollout 完成 |
|
||||
| **Canary 发布** | 10% → 50% → 100% 逐步放量 | 有 Bug 只影响 10% 用户 |
|
||||
|
||||
## 回滚与 Rollout
|
||||
|
||||
> [!question] 上线后发现 Bug,怎么最快恢复?
|
||||
>
|
||||
> 答案不是「快速修好再发一次」——而是**先回滚,后修复**。MTTR(平均恢复时间)比根因分析优先级更高。
|
||||
|
||||
### K8s Rollout 模式对比
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
RU["RollingUpdate\n滚动更新"] --> P1["逐步替换旧 Pod"]
|
||||
RU --> P2["期间服务不中断"]
|
||||
RU --> P3["支持 ProgressDeadline"]
|
||||
|
||||
RC["Recreate\n全部重建"] --> R1["先杀所有旧 Pod"]
|
||||
RC --> R2["再启新 Pod"]
|
||||
RC --> R3["有短暂不可用窗口"]
|
||||
|
||||
style RU fill:#e8f5e9
|
||||
style RC fill:#fff3e0
|
||||
```
|
||||
|
||||
| 特性 | RollingUpdate | Recreate |
|
||||
|------|--------------|----------|
|
||||
| **可用性** | 零停机 | 短暂中断 |
|
||||
| **资源峰值** | Old + New 并存 | 只有一个版本运行 |
|
||||
| **适用场景** | 绝大多数微服务 | 独占存储卷、单写数据库 |
|
||||
| **回滚速度** | `kubectl rollout undo` 秒级恢复 | 重新 deploy 上一个版本 |
|
||||
|
||||
### 回滚命令速查
|
||||
|
||||
```bash
|
||||
# 查看当前 rollout 状态
|
||||
kubectl rollout status deployment/order-service -n production
|
||||
|
||||
# 回滚到上一个版本
|
||||
kubectl rollout undo deployment/order-service -n production
|
||||
|
||||
# 查看历史版本列表
|
||||
kubectl rollout history deployment/order-service -n production
|
||||
|
||||
# 回滚到指定 revision
|
||||
kubectl rollout undo deployment/order-service -n production --to-revision=3
|
||||
|
||||
# 暂停/恢复 rollout(适合批量变更)
|
||||
kubectl rollout pause deployment/order-service -n production
|
||||
# ... 应用多个变更 ...
|
||||
kubectl rollout resume deployment/order-service -n production
|
||||
```
|
||||
|
||||
> [!tip] 如果使用了 Canary / Istio 等流量治理工具,回滚步骤变为:
|
||||
> 1. **先把流量切回稳定版本**(改 weight = 0)— 秒级止血
|
||||
> 2. **再删除问题部署** — 释放资源
|
||||
> 3. **最后排查和修复**
|
||||
>
|
||||
> 流量切换比 Pod 重建更快,这是灰度发布最大的安全优势。
|
||||
|
||||
## 生产级增强
|
||||
|
||||
上面的示例做了精简,真实环境中通常还会加入:
|
||||
|
||||
| 增强项 | 说明 | 工具 / 做法 |
|
||||
|--------|------|------------|
|
||||
| **Dependency Lock** | `go mod tidy` 后提交 `go.sum`,确保每次依赖一致 | `go.sum` / `package-lock.json` |
|
||||
| **Trivy Image Scan** | 镜像推送后再扫描一次镜像内漏洞 | `trivy image <image>` |
|
||||
| **OpenTelemetry Trace ID** | 注入 trace ID,追踪本次构建产生的请求 | 环境变量注入 `OTEL_SERVICE_VERSION` |
|
||||
| **Slack 通知** | 每阶段完成后通过 Webhook 通知团队 | GitHub Actions `slack/notification` |
|
||||
| **代码覆盖率门控** | 低于阈值直接失败(coverage < 80% = fail) | `gocover` / `codecov` |
|
||||
| **SBOM 生成** | 用 `syft` 生成软件物料清单,满足合规要求 | `syft dir:. -o cyclonedx-json > sbom.json` |
|
||||
| **Docker BuildKit** | 利用缓存层加速重建 | `DOCKER_BUILDKIT=1 docker build` |
|
||||
| **并发测试** | 单元测试和 lint 并行执行缩短流水线时间 | GitHub Actions `strategy.matrix` |
|
||||
|
||||
```bash
|
||||
# Docker BuildKit 加速构建
|
||||
DOCKER_BUILDKIT=1 docker build \
|
||||
--cache-from ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ github.sha }} \
|
||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ github.sha }} \
|
||||
services/order
|
||||
|
||||
# SBOM 生成
|
||||
syft dir:. -o cyclonedx-json > sbom.json
|
||||
```
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
> [!warning] 这些坑踩过一次就记住了
|
||||
|
||||
1. **不要在 pipeline 里用 `latest` tag** — 不同环境编译行为不一致
|
||||
2. **不要让 Staging 和 Production 各自 `docker build`** — 这是最常见的一致性 bug 来源
|
||||
3. **不要跳过 Staging 直连 Production** — 哪怕你的测试写得再好,也需要一个集成验证环节
|
||||
4. **不要硬编码 Secret 到 YAML 或代码里** — 参考 [[04-安全与发布策略]]
|
||||
5. **不要忽略 `kubectl rollout status`** — 不检查 rollout 状态等于盲发
|
||||
6. **不要把日志级别开到 DEBUG 上生产** — 不仅浪费存储,还会暴露敏感数据
|
||||
7. **不要用 cron 做主触发器替代 push** — 定时构建无法响应具体 commit,丢失审计链
|
||||
8. **不要忘记配置 `ProgressDeadlineSeconds`** — 一个卡在 Pending 状态的 rollout 不会被自动中止
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[02-GitOps与ArgoCD]] — GitOps 模式下的部署工作流
|
||||
- [[03-Helm模板管理]] — Helm Chart 编写与管理
|
||||
- [[04-安全与发布策略]] — 安全管理和发布策略决策
|
||||
- [[../03-CICD与GitOps]] — 参考手册与决策指南
|
||||
Reference in New Issue
Block a user