首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Ingress灰度路由策略-基于IP灰度路由

Ingress灰度路由策略-基于IP灰度路由

原创
作者头像
dufu
发布2025-03-06 21:21:01
发布2025-03-06 21:21:01
3070
举报
文章被收录于专栏:k8s相关实操k8s相关实操

应用背景:应用发布需要基于指定IP进行灰度测试,这个指定的IP段可能是测试机,也可能是某个网段。这些IP地址访问同域名应用自动转发请求到灰度服务。

组件支持:需要对ingress-nginx-controller做二次开发改造,这里略过改造部分,重点关注测试流程。

测试步骤:准备两套环境,生产环境/测试环境,准备测试应用镜像,根据环境变量输出日志方便观察日志判断流量是被路由到哪个环境的服务,分别部署应用到生产和测试环境,编写测试脚本进行测试。接下来重点展示部署和测试脚本。

一、测试应用app.py

代码语言:txt
复制
import os
import logging
from flask import Flask, jsonify

app = Flask(__name__)

# 日志配置
logging.basicConfig(
    format='%(asctime)s [%(levelname)s] %(message)s',
    level=logging.INFO
)
logger = logging.getLogger(__name__)

@app.route('/')
def main():
    # 从环境变量获取配置
    service_name = os.getenv('SERVICE_NAME', 'default')
    log_message = os.getenv('LOG_MESSAGE', 'Default log message')
    version = os.getenv('VERSION', 'v1')
    
    # 记录日志
    logger.info(f"[{service_name}-{version}] {log_message}")
    
    return jsonify({
        "service": service_name,
        "version": version,
        "message": log_message
    })

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

二、Dockerfile文件

代码语言:txt
复制
FROM python:3.9-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .

CMD ["python", "app.py"]

三、requirements.txt

代码语言:txt
复制
flask==2.0.3
werkzeug==2.0.3  # 固定兼容版本

四、部署脚本k8s.yaml

代码语言:txt
复制
# 生产版本部署
apiVersion: apps/v1
kind: Deployment
metadata:
  name: production
  namespace: python
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demo
      track: stable
  template:
    metadata:
      labels:
        app: demo
        track: stable
    spec:
      imagePullSecrets:
      - name: harbor-admin
      containers:
      - name: web
        image: harbor-lite.tech.21cn.com/python/my-service:v4
        env:
        - name: SERVICE_NAME
          value: "production"
        - name: LOG_MESSAGE
          value: "This is production service"
        - name: VERSION
          value: "v1"
        ports:
        - containerPort: 5000

---
# 灰度版本部署
apiVersion: apps/v1
kind: Deployment
metadata:
  name: canary
  namespace: python-gray
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
      track: canary
  template:
    metadata:
      labels:
        app: demo
        track: canary
    spec:
      imagePullSecrets:
      - name: harbor-admin
      containers:
      - name: web
        image: harbor-lite.tech.21cn.com/python/my-service:v5
        env:
        - name: SERVICE_NAME
          value: "canary"
        - name: LOG_MESSAGE
          value: "This is canary service"
        - name: VERSION
          value: "v2"
        ports:
        - containerPort: 5000

---
# 生产环境 Service
apiVersion: v1
kind: Service
metadata:
  name: prod-svc
  namespace: python
spec:
  selector:
    app: demo
    track: stable  # 精确匹配生产 Pod
  ports:
    - protocol: TCP
      port: 80
      targetPort: 5000

---
# 灰度环境 Service
apiVersion: v1
kind: Service
metadata:
  name: canary-svc
  namespace: python-gray
spec:
  selector:
    app: demo
    track: canary  # 精确匹配灰度 Pod
  ports:
    - protocol: TCP
      port: 80
      targetPort: 5000

---
# 主Ingress(90%流量)
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: main-ingress
  namespace: python
  annotations:
    nginx.ingress.kubernetes.io/canary: "false"
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: canary.demo.com
    http:
      paths:
      - path: /
        backend:
          serviceName: prod-svc
          servicePort: 80

---
# 灰度Ingress(10%流量)
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: canary-ingress
  namespace: python-gray
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  rules:
  - host: canary.demo.com
    http:
      paths:
      - path: /
        backend:
          serviceName: canary-svc
          servicePort: 80

五、测试脚本

代码语言:txt
复制
#!/bin/bash
INGRESS_IP="your_ingress_ip"  # 替换为实际IP
TOTAL=100
SUCCESS=0
FAILED=0
V1_COUNT=0
V2_COUNT=0

# 创建临时文件记录结果
result_file=$(mktemp)
trap 'rm -f "$result_file"' EXIT

for i in $(seq 1 $TOTAL); do
  # 发送请求并记录结果
  {
    response=$(curl -sSf -m 5 -H "Host: canary.demo.com" http://172.19.0.93/ 2>&1)
    exit_code=$?
    
    if [ $exit_code -eq 0 ]; then
      version=$(echo "$response" | jq -r '.version' 2>/dev/null)
      if [[ "$version" == "v1" || "$version" == "v2" ]]; then
        echo "success $version" >> "$result_file"
      else
        echo "invalid" >> "$result_file"
      fi
    else
      echo "failed" >> "$result_file"
    fi
  } &
  
  # 控制并发数
  if (( i % 10 == 0 )); then
    wait
  fi
done

# 等待所有后台任务完成
wait

# 统计结果
while read -r line; do
  case $line in
    "success v1")
      ((SUCCESS++))
      ((V1_COUNT++))
      ;;
    "success v2")
      ((SUCCESS++))
      ((V2_COUNT++))
      ;;
    "invalid")
      ((FAILED++))
      ;;
    "failed")
      ((FAILED++))
      ;;
  esac
done < "$result_file"

# 生成报告
echo "=== 精准测试报告 ==="
echo "总请求数:   $TOTAL"
echo "成功请求:   $SUCCESS (v1: $V1_COUNT, v2: $V2_COUNT)"
echo "失败请求:   $FAILED"
echo "灰度比例:   $(( V2_COUNT * 100 / SUCCESS ))% (预期10%)"

# 验证总数
if [ $(( SUCCESS + FAILED )) -ne $TOTAL ]; then
  echo "[警告] 总数校验失败:$SUCCESS + $FAILED ≠ $TOTAL"
else
  echo "[校验] 总数匹配成功"
fi

测试结果:

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档