在微服务与云原生时代,手动部署已成为效率瓶颈。一套成熟的自动化流水线应具备:
层级 | 工具 | 用途 |
|---|---|---|
CI | GitLab CI | 触发构建、执行测试、生成镜像 |
制品仓库 | Harbor | 存储 Docker 镜像 |
容器编排 | Kubernetes (K8s) | 服务调度、弹性伸缩、滚动更新 |
配置管理 | Helm | 模板化 K8s 资源清单 |
监控告警 | Prometheus + Grafana | 自动化运维观测 |
架构流程图(文字描述):
代码 Push → GitLab Webhook → CI Pipeline (test → build → push) → Helm Upgrade → K8s 滚动更新 → 健康检查 → 完成发布
我们使用一个 Vue3 + Flask 的简单投票应用作为示例。
fullstack-automation/
├── frontend/ # Vue3 前端
│ ├── src/
│ ├── Dockerfile
│ ├── nginx.conf
│ └── package.json
├── backend/ # Flask 后端 API
│ ├── app.py
│ ├── requirements.txt
│ ├── Dockerfile
│ └── gunicorn.conf.py
├── k8s/
│ ├── namespace.yaml
│ ├── backend-deploy.yaml
│ ├── backend-svc.yaml
│ ├── frontend-deploy.yaml
│ ├── frontend-svc.yaml
│ └── ingress.yaml
├── helm/ # Helm Chart(可选)
│ └── vote-chart/
├── .gitlab-ci.yml # CI 核心配置
└── README.mdbackend/app.py)from flask import Flask, jsonify, request
from flask_cors import CORS
import os
app = Flask(__name__)
CORS(app)
votes = {"vue": 0, "react": 0, "angular": 0}
@app.route('/api/health', methods=['GET'])
def health():
return jsonify({"status": "ok"}), 200
@app.route('/api/votes', methods=['GET'])
def get_votes():
return jsonify(votes), 200
@app.route('/api/vote', methods=['POST'])
def vote():
data = request.get_json()
framework = data.get('framework')
if framework in votes:
votes[framework] += 1
return jsonify({"success": True, "votes": votes}), 200
return jsonify({"error": "Invalid framework"}), 400
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)frontend/src/App.vue)<template>
<div id="app">
<h1>全栈自动化投票</h1>
<div class="buttons">
<button v-for="fw in frameworks" :key="fw" @click="vote(fw)">
投给 {{ fw }}
</button>
</div>
<div class="results">
<p v-for="(count, fw) in votes" :key="fw">
{{ fw }}: {{ count }} 票
</p>
</div>
</div>
</template>
<script>
import axios from 'axios';
const API_BASE = '/api';
export default {
data() {
return {
frameworks: ['vue', 'react', 'angular'],
votes: {}
};
},
async mounted() {
await this.fetchVotes();
},
methods: {
async fetchVotes() {
const res = await axios.get(`${API_BASE}/votes`);
this.votes = res.data;
},
async vote(framework) {
await axios.post(`${API_BASE}/vote`, { framework });
await this.fetchVotes();
}
}
};
</script># backend/Dockerfile
FROM python:3.10-slim as builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --user -r requirements.txt
FROM python:3.10-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
EXPOSE 5000
CMD ["gunicorn", "-c", "gunicorn.conf.py", "app:app"].gitlab-ci.yml)stages:
- test
- build
- deploy
variables:
DOCKER_REGISTRY: harbor.example.com
BACKEND_IMAGE: $DOCKER_REGISTRY/vote/backend
FRONTEND_IMAGE: $DOCKER_REGISTRY/vote/frontend
K8S_NAMESPACE: vote-prod
cache:
paths:
- frontend/node_modules/
- backend/.venv/
# 1. 测试阶段
test-backend:
stage: test
image: python:3.10-slim
script:
- pip install pytest flask-cors
- pytest backend/tests/ -v
only:
- merge_requests
- main
test-frontend:
stage: test
image: node:18-alpine
script:
- cd frontend
- npm install
- npm run test:unit
only:
- merge_requests
- main
# 2. 构建并推送镜像(使用 Kaniko 无需 Docker-in-Docker)
build-images:
stage: build
image: gcr.io/kaniko-project/executor:v1.9.0-debug
script:
- /kaniko/executor
--context="$CI_PROJECT_DIR/backend"
--dockerfile="$CI_PROJECT_DIR/backend/Dockerfile"
--destination="$BACKEND_IMAGE:$CI_COMMIT_SHORT_SHA"
--destination="$BACKEND_IMAGE:latest"
- /kaniko/executor
--context="$CI_PROJECT_DIR/frontend"
--dockerfile="$CI_PROJECT_DIR/frontend/Dockerfile"
--destination="$FRONTEND_IMAGE:$CI_COMMIT_SHORT_SHA"
--destination="$FRONTEND_IMAGE:latest"
only:
- main
# 3. 部署到 K8s(滚动更新)
deploy-k8s:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/backend-deploy
backend=$BACKEND_IMAGE:$CI_COMMIT_SHORT_SHA
-n $K8S_NAMESPACE
- kubectl set image deployment/frontend-deploy
frontend=$FRONTEND_IMAGE:$CI_COMMIT_SHORT_SHA
-n $K8S_NAMESPACE
- kubectl rollout status deployment/backend-deploy -n $K8S_NAMESPACE
- kubectl rollout status deployment/frontend-deploy -n $K8S_NAMESPACE
only:
- main
environment:
name: production
url: https://vote.example.com后端 Deployment (k8s/backend-deploy.yaml):
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-deploy
namespace: vote-prod
spec:
replicas: 3
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: harbor.example.com/vote/backend:latest
ports:
- containerPort: 5000
livenessProbe:
httpGet:
path: /api/health
port: 5000
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /api/health
port: 5000
initialDelaySeconds: 5
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"Ingress 暴露服务 (k8s/ingress.yaml):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vote-ingress
namespace: vote-prod
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: vote.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: backend-svc
port:
number: 5000
- path: /
pathType: Prefix
backend:
service:
name: frontend-svc
port:
number: 80在 deploy 阶段增加规则,仅当 Tag 符合语义化版本时触发生产发布:
deploy-prod:
stage: deploy
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
script:
- helm upgrade --install vote ./helm/vote-chart
--set backend.image.tag=$CI_COMMIT_TAG
--set frontend.image.tag=$CI_COMMIT_TAG创建 rollback.sh,在发布失败时自动执行:
#!/bin/bash
# 回滚到上一个稳定版本
kubectl rollout undo deployment/backend-deploy -n vote-prod
kubectl rollout undo deployment/frontend-deploy -n vote-prod在 CI 中添加 curl 轮询验证:
verify-deploy:
stage: deploy
image: curlimages/curl:latest
script:
- for i in $(seq 1 10); do
curl -f https://vote.example.com/api/health && break;
sleep 5;
done现象 | 可能原因 | 解决方法 |
|---|---|---|
镜像拉取失败 | 私有仓库未配置 secret | kubectl create secret docker-registry regcred ... |
健康检查失败 | 端口或路径错误 | 检查 livenessProbe 配置 |
滚动更新卡住 | 资源不足或镜像启动慢 | 增加 initialDelaySeconds 或调整 resources |
前端无法访问 API | Ingress 路径重写错误 | 检查 nginx.ingress.kubernetes.io/rewrite-target |
本文实现了从 代码提交 → 自动测试 → 镜像构建 → K8s 滚动更新 的完整闭环。此方案可直接应用于中小型团队,并具备以下扩展能力:
全部代码已上传至 GitHub 示例仓库(替换为你的仓库地址),欢迎 Fork 使用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。