kong网关基于header分流灰度发布

kong网关基于header分流灰度发布

在现代微服务架构中,灰度发布(Canary Release)已经成为一种常用且安全的上线策略。它允许我们将新版本的功能仅暴露给一小部分用户,从而在保证系统稳定性的同时收集反馈、验证效果、规避风险。而作为一款轻量高性能的 API 网关,Kong 提供了灵活的路由规则,使得我们可以通过配置请求头、Cookie、路径等条件,实现基于规则的流量分流。

本文将聚焦于一种实用的灰度发布方案:基于请求头(Header)进行流量分发。我们将以 X-App-Version 等请求头为判断依据,将部分请求引导至灰度环境(如 backend-v2),其余流量仍然指向正式环境(backend-v1)。通过 Kong 的声明式配置方式,可以轻松实现无数据库、无插件依赖的灰度发布策略,特别适合前后端联调、测试环境验证以及轻量化场景。

本篇文章将从实际业务需求出发,介绍如何使用 Kong 网关进行权重分流的灰度发布,包括:

  • Kong 中实现header分流的配置;

  • 如何基于 Docker Compose 部署一个 DB-less 模式的灰度发布环境;

  • 配置示例讲解:如何将v2流量导向新版本服务 backend-v2,v1保持在旧版本 backend-v1;

前置条件

节点规划如下:

主机名 节点IP 监听端口 操作系统
frontend 192.168.73.11 8080 Ubuntu 24.04
backend-v1 192.168.73.11 3001 Ubuntu 24.04
backend-v2 192.168.73.11 3002 Ubuntu 24.04
kong-gateway 192.168.73.11 8000 Ubuntu 24.04

已安装 Docker 和 docker-compose工具。

无DB模式部署kong网关

创建kong.yml声明式配置文件:

yaml 复制代码
root@ubuntu:/data/apps/kong# cat kong.yml 
_format_version: "3.0"

services:
  - name: backend-service
    url: http://backend-v1:3000

  - name: backend-service-gray
    url: http://backend-v2:3000

routes:
  - name: backend-route-gray
    service: backend-service-gray
    paths:
      - /admin
    headers:
      X-App-Version:
        - v2
    strip_path: false

  - name: backend-route
    service: backend-service
    paths:
      - /admin
    strip_path: false

plugins:
  - name: cors
    config:
      origins:
        - "*"
      methods:
        - GET
        - POST
        - PUT
        - DELETE
        - OPTIONS
      headers:
        - Accept
        - Authorization
        - Content-Type
        - X-Requested-With
        - X-App-Version
      exposed_headers:
        - X-Custom-Header
      credentials: true
      max_age: 3600

配置参数说明:

好的,以下是你提供的这份 Kong 声明式配置(kong.yml)的逐项参数详解,特别聚焦于灰度发布与 CORS 支持相关部分:


_format_version: "3.0"

  • 说明 :声明配置文件的格式版本,Kong 3.x 需要为 "3.0"
  • 作用:Kong 用来解析声明式配置,必须写。

services

定义了两个后端服务(一个正式,一个灰度):

  1. backend-service
yaml 复制代码
- name: backend-service
  url: http://backend-v1:3000
  • name:服务名称,用于 route 映射。
  • url :实际后端地址,这里是正式环境 backend-v1 的 3000 端口。
  • 备注 :省略了 host/port/protocol 的写法,直接用 url 更简洁。
  1. backend-service-gray
yaml 复制代码
- name: backend-service-gray
  url: http://backend-v2:3000
  • 同上,只不过这是灰度环境的服务地址(backend-v2)。

routes

定义了两条路由规则,基于路径和请求头进行分流:

  1. backend-route-gray
yaml 复制代码
- name: backend-route-gray
  service: backend-service-gray
  paths:
    - /admin
  headers:
    X-App-Version:
      - v2
  strip_path: false
  • name:路由名称。
  • service :绑定到灰度服务 backend-service-gray
  • paths :匹配 /admin 路径的请求。
  • headers :匹配请求头 X-App-Version: v2 的请求。
  • strip_path :为 false 表示保留 /admin 前缀,不会从请求 URL 中剥离。
  1. backend-route
yaml 复制代码
- name: backend-route
  service: backend-service
  paths:
    - /admin
  strip_path: false
  • 匹配 /admin 的默认路由,绑定到正式服务 backend-service
  • 没有 header 限制,所有不匹配前面 route 的请求都会走这个。

⚠️ 匹配顺序重要backend-route-gray 必须在前面,Kong 按顺序匹配 route,先匹配成功的就执行,后面的不再判断。


plugins

配置了全局的 CORS(跨域资源共享)插件:

yaml 复制代码
- name: cors
  config:
    origins:
      - "*"
    methods:
      - GET
      - POST
      - PUT
      - DELETE
      - OPTIONS
    headers:
      - Accept
      - Authorization
      - Content-Type
      - X-Requested-With
      - X-App-Version
    exposed_headers:
      - X-Custom-Header
    credentials: true
    max_age: 3600

参数说明:

参数 说明
origins 允许哪些源(Origin)跨域访问。* 表示全部允许
methods 允许的 HTTP 方法
headers 允许客户端请求时携带的请求头(包括自定义的如 X-App-Version
exposed_headers 响应中暴露给前端的响应头
credentials 是否允许携带 Cookie、认证信息等
max_age 预检请求(OPTIONS)结果缓存时间(秒)

最终请求 http://kong网关/admin

  • 如果带 X-App-Version: v2 请求头 → 进入 灰度服务
  • 如果不带或值不为 v2 → 进入 正式服务
  • 启用了全局的 CORS 支持,适合 Web 前端跨域请求调用 API

创建docker外部网络,打通kong网关与前后端服务网络

bash 复制代码
docker network create app_network

创建部署kong网关的docker-compose.yaml

yaml 复制代码
root@ubuntu:/data/apps/kong# cat docker-compose.yaml
name: 'kong-gateway'

services:
  kong:
    image: kong:3.9.1
    container_name: kong-gateway
    restart: always
    environment:
      KONG_DATABASE: "off"
      KONG_DECLARATIVE_CONFIG: /usr/local/kong/declarative/kong.yml
      # kong proxy
      KONG_PROXY_LISTEN: 0.0.0.0:8000
      KONG_PROXY_LISTEN_SSL: 0.0.0.0:8443
      # kong admin
      KONG_ADMIN_LISTEN: 0.0.0.0:8001, 0.0.0.0:8444 ssl
      # kong manager
      KONG_ADMIN_GUI_LISTEN: 0.0.0.0:8002, 0.0.0.0:8445 ssl
      # kong logs
      KONG_PROXY_ACCESS_LOG: /dev/stdout
      KONG_PROXY_ERROR_LOG: /dev/stderr
      KONG_ADMIN_ACCESS_LOG: /dev/stdout
      KONG_ADMIN_ERROR_LOG: /dev/stderr
    ports:
      - "8000:8000" # Proxy
      - "8443:8443" # Proxy SSL
      - "8001:8001" # Admin API
      - "8444:8444" # Admin API SSL
      - "8002:8002" # Kong Manager
      - "8445:8445" # Kong Manager SSL
    volumes:
      - .kong.yml:/usr/local/kong/declarative/kong.yml
    networks:
      - app-net
    healthcheck:
      test: ["CMD-SHELL", "kong health"]
      interval: 15s
      timeout: 10s
      retries: 3

networks:
  app-net:
    external: true
    name: app_network

启动kong网关

bash 复制代码
docker compose up -d

查看服务运行状态

bash 复制代码
root@ubuntu:/data/apps/kong# docker compose ps
NAME           IMAGE        COMMAND                  SERVICE   CREATED          STATUS                    PORTS
kong-gateway   kong:3.9.1   "/docker-entrypoint...."   kong      31 seconds ago   Up 31 seconds (healthy)   0.0.0.0:8000-8002->8000-8002/tcp, [::]:8000-8002->8000-8002/tcp, 0.0.0.0:8443-8445->8443-8445/tcp, [::]:8443-8445->8443-8445/tcp
root@ubuntu:/data/apps/kong# 

访问kong manager查看创建的转发规则

Gateway Services

Routes

Plugins

部署前后端服务

示例 Canaryheader 是一个基于Kong网关和docker-compose的Web端灰度发布演示项目,支持基于百分比的流量灰度策略。

架构如下:

主要特性

  • 前端:html/js,展示后端返回的版本信息
  • 后端:Node.js (Express),分别为v1和v2版本
  • Kong网关:流量分流、服务注册、路由配置,支持基于header灰度
  • 容器化:所有服务均为Docker容器
  • 一键部署:docker-compose编排

目录结构

bash 复制代码
canaryheader/
├── docker-compose.yml
├── backend-v1/
│   ├── Dockerfile
│   └── index.js           # v1后端代码
├── backend-v2/
│   ├── Dockerfile
│   └── index.js           # v2后端代码
├── frontend/
│   ├── Dockerfile
│   └── index.html

backend-v1/index.js

js 复制代码
root@ubuntu:/data/git/canary-header-app# cat backend-v1/index.js 
const express = require('express');
const app = express();
app.get('/admin', (req, res) => {
  res.json({ version: 'v1', message: 'Hello from backend v1!' });
});
app.listen(3000, () => console.log('Backend v1 running on 3000'));

backend-v1/Dockerfile

bash 复制代码
root@ubuntu:/data/git/canary-header-app# cat backend-v1/Dockerfile 
FROM node:24-alpine
WORKDIR /app
COPY index.js ./
RUN npm config set registry https://registry.npmmirror.com && \
    npm init -y && \
    npm install express
EXPOSE 3000
CMD ["node", "index.js"] 

backend-v2/index.js

js 复制代码
root@ubuntu:/data/git/canary-header-app# cat backend-v2/index.js 
const express = require('express');
const app = express();
app.get('/admin', (req, res) => {
  res.json({ version: 'v2', message: 'Hello from backend v2!' });
});
app.listen(3000, () => console.log('Backend v2 running on 3000'));

backend-v2/Dockerfile

bash 复制代码
root@ubuntu:/data/git/canary-header-app# cat backend-v2/Dockerfile 
FROM node:24-alpine
WORKDIR /app
COPY index.js ./
RUN npm config set registry https://registry.npmmirror.com && \
    npm init -y && \
    npm install express
EXPOSE 3000
CMD ["node", "index.js"] 

frontend/index.html

html 复制代码
root@ubuntu:/data/git/canary-header-app# cat frontend/index.html 
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Canary Flow</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      max-width: 600px;
      margin: 0 auto;
      padding: 20px;
    }
    .test-section {
      margin: 20px 0;
      padding: 15px;
      border: 1px solid #ddd;
      border-radius: 5px;
    }
    button {
      margin: 5px;
      padding: 10px 15px;
      background-color: #007bff;
      color: white;
      border: none;
      border-radius: 3px;
      cursor: pointer;
    }
    button:hover {
      background-color: #0056b3;
    }
    pre {
      background-color: #f8f9fa;
      padding: 10px;
      border-radius: 3px;
      white-space: pre-wrap;
    }
    .version-v1 { color: #28a745; font-weight: bold; }
    .version-v2 { color: #dc3545; font-weight: bold; }
  </style>
</head>
<body>
  <h1>Canary Flow 灰度发布演示</h1>
  
  <div class="test-section">
    <h3>灰度规则测试</h3>
    <p>基于请求头 <code>X-App-Version</code> 的简单灰度规则:</p>
    <ul>
      <li><strong>X-App-Version: v2</strong> → 路由到 Backend v2</li>
      <li><strong>无请求头或其他值</strong> → 路由到 Backend v1</li>
    </ul>
  </div>

  <div class="test-section">
    <h3>测试按钮</h3>
    <button onclick="fetchData()">默认请求 (v1)</button>
    <button onclick="fetchDataWithVersion('v2')">使用 v2 版本</button>
    <button onclick="fetchDataWithVersion('v1')">强制使用 v1</button>
  </div>

  <pre id="output">点击按钮开始测试...</pre>

  <script>
    async function fetchData() {
      try {
        const res = await fetch("http://192.168.73.11:8000/admin");
        const data = await res.json();
        displayResult(data, "默认请求(无请求头)");
      } catch (error) {
        document.getElementById("output").innerText = "错误: " + error.message;
      }
    }

    async function fetchDataWithVersion(version) {
      try {
        const res = await fetch("http://192.168.73.11:8000/admin", {
          headers: {
            'X-App-Version': version
          }
        });
        const data = await res.json();
        displayResult(data, `X-App-Version: ${version}`);
      } catch (error) {
        document.getElementById("output").innerText = "错误: " + error.message;
      }
    }

    function displayResult(data, requestInfo = "") {
      const output = document.getElementById("output");
      const versionClass = data.version === 'v1' ? 'version-v1' : 'version-v2';
      const timestamp = new Date().toLocaleTimeString();
      
      output.innerHTML = `
请求信息: ${requestInfo}
时间: ${timestamp}
版本: <span class="${versionClass}">${data.version}</span>
消息: ${data.message}
      `;
    }
  </script>
</body>
</html>

说明:前端直接通过kong网关接口http://192.168.73.11:8000/admin调用后端服务。

frontend/Dockerfile

bash 复制代码
root@ubuntu:/data/git/canary-header-app# cat frontend/Dockerfile 
FROM nginx:alpine
RUN rm -rf /usr/share/nginx/html/*
COPY index.html /usr/share/nginx/html/

docker-compose.yaml

yaml 复制代码
root@ubuntu:/data/git/canary-header-app# cat docker-compose.yml 
name: 'canary-flow-app'

services:
  frontend:
    build: ./frontend
    image: registry.cn-shenzhen.aliyuncs.com/cnmirror/canary-header-frontend:v1.0
    container_name: canary-frontend
    ports:
      - "8080:80"
    networks:
      - app-net

  backend-v1:
    build: ./backend-v1
    image: registry.cn-shenzhen.aliyuncs.com/cnmirror/canary-header-backend-v1:v1.0
    container_name: canary-backend-v1
    ports:
      - "3001:3000"
    networks:
      - app-net

  backend-v2:
    build: ./backend-v2
    image: registry.cn-shenzhen.aliyuncs.com/cnmirror/canary-header-backend-v2:v1.0
    container_name: canary-backend-v2
    ports:
      - "3002:3000"
    networks:
      - app-net

networks:
  app-net:
    external: true
    name: app_network

启动前后端服务

构建镜像

bash 复制代码
docker compose build

启动服务

bash 复制代码
docker compose up -d

查看服务运行状态

bash 复制代码
root@ubuntu:/data/git/canary-header-app# docker compose ps
NAME                IMAGE                                                                    COMMAND                  SERVICE      CREATED          STATUS          PORTS
canary-backend-v1   registry.cn-shenzhen.aliyuncs.com/cnmirror/canary-flow-backend-v1:v1.0   "docker-entrypoint.s..."   backend-v1   10 minutes ago   Up 10 minutes   0.0.0.0:3001->3000/tcp, [::]:3001->3000/tcp
canary-backend-v2   registry.cn-shenzhen.aliyuncs.com/cnmirror/canary-header-backend-v2:v1.0   "docker-entrypoint.s..."   backend-v2   10 minutes ago   Up 10 minutes   0.0.0.0:3002->3000/tcp, [::]:3002->3000/tcp
canary-frontend     registry.cn-shenzhen.aliyuncs.com/cnmirror/canary-header-frontend:v1.0     "/docker-entrypoint...."   frontend     10 minutes ago   Up 10 minutes   0.0.0.0:8080->80/tcp, [::]:8080->80/tcp
root@ubuntu:/data/git/canary-flow-app# 

灰度流量分流

Kong通过routes--headers将流量按header分配到v1和v2后端,实现灰度发布。通过kong网关多次请求接口进行验证:

json 复制代码
root@ubuntu:/data/apps/kong# curl -s -H "X-App-Version: v1" http://192.168.73.11:8000/admin | jq
{
  "version": "v1",
  "message": "Hello from backend v1!"
}
root@ubuntu:/data/apps/kong# 
root@ubuntu:/data/apps/kong# curl -s -H "X-App-Version: v2" http://192.168.73.11:8000/admin | jq
{
  "version": "v2",
  "message": "Hello from backend v2!"
}
root@ubuntu:/data/apps/kong# curl -s http://192.168.73.11:8000/admin | jq
{
  "version": "v1",
  "message": "Hello from backend v1!"
}
root@ubuntu:/data/apps/kong# 

浏览器访问frontend验证:


相关推荐
tnan25223 天前
记录docker使用kong consul postgresql配置dns异常解决
docker·kong·consul
William一直在路上15 天前
KONG API Gateway中的核心概念
网络·gateway·kong
freesharer15 天前
kong网关集成Safeline WAF 插件
kong
悟能不能悟1 个月前
kong是什么
kong
JohnGox2 个月前
KONG根据请求参数限流
kong·限流
星释2 个月前
使用API网关Kong配置反向代理和负载均衡
运维·负载均衡·kong
莱茵不哈哈3 个月前
OpenResty 深度解析:构建高性能 Web 服务的终极方案
nginx·lua·kong·openresty·conf
泽济天下3 个月前
【工作记录】Kong Gateway入门篇之简介
gateway·kong
泽济天下3 个月前
【工作记录】Kong Gateway 入门篇之部署及简单测试
gateway·kong