PHP项目蓝绿发布如何负载切换服务节点

wen PHP项目 27

本文目录导读:

PHP项目蓝绿发布如何负载切换服务节点

  1. 核心原理
  2. 方案一:基于Nginx upstream的动态切换(最常用)
  3. 方案二:基于HAProxy的切换(适合高并发)
  4. 方案三:基于Kubernetes Service的切换(容器化PHP)
  5. 方案四:基于DNS的切换(相对粗粒度)
  6. 切换时的关键注意事项
  7. 推荐实践:最小中断切换流水线

在PHP项目的蓝绿部署中,负载切换服务节点主要涉及流量调度层(如Nginx、HAProxy、Kubernetes Service)的配置更新,以下是几种常见且实用的实现方案:


核心原理

保持两套完全相同的生产环境(蓝组、绿组),但只有一组接收实时流量,另一组待命或用于预发布验证,切换时,只需要修改负载均衡器的目标服务器组即可。


基于Nginx upstream的动态切换(最常用)

配置示例

# /etc/nginx/conf.d/php-app.conf
upstream php-backend {
    # 默认指向绿色组
    server 10.0.1.10:9000 weight=10;  # 绿组节点1
    server 10.0.1.11:9000 weight=10;  # 绿组节点2
    # server 10.0.2.10:9000 weight=10; # 蓝组节点1(备用,切换时启用)
    # server 10.0.2.11:9000 weight=10; # 蓝组节点2(备用)
}
server {
    listen 80;
    server_name app.example.com;
    location / {
        proxy_pass http://php-backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        # PHP-FPM 相关配置(如果直接连接FPM)
        include fastcgi_params;
        fastcgi_pass php-backend;
    }
}

切换脚本(bash)

#!/bin/bash
# blue-green-switch.sh
CURRENT="$(grep '#' /etc/nginx/conf.d/php-app.conf | grep -v '^$' | wc -l)"
if [ "$CURRENT" -eq 0 ]; then
    # 当前是绿组(无注释),切换到蓝组
    sed -i 's/server 10.0.1/sd server 10.0.1/' /etc/nginx/conf.d/php-app.conf
    sed -i 's/sd server 10.0.2/server 10.0.2/' /etc/nginx/conf.d/php-app.conf
    sed -i 's/# server 10.0.1/server 10.0.1/' /etc/nginx/conf.d/php-app.conf
else
    # 当前是蓝组,切换到绿组
    sed -i 's/server 10.0.2/sd server 10.0.2/' /etc/nginx/conf.d/php-app.conf
    sed -i 's/sd server 10.0.1/server 10.0.1/' /etc/nginx/conf.d/php-app.conf
    sed -i 's/# server 10.0.2/server 10.0.2/' /etc/nginx/conf.d/php-app.conf
fi
# 检查配置并重载
nginx -t && nginx -s reload

注意:实际生产中可以维护两组完整的IP列表,通过注释/取消注释来切换。

更优雅的方式:双upstream + map

upstream green {
    server 10.0.1.10:9000;
    server 10.0.1.11:9000;
}
upstream blue {
    server 10.0.2.10:9000;
    server 10.0.2.11:9000;
}
# 通过map变量控制
map $cookie_blu_gren $backend {
    default green;          # 默认绿色
    blue     blue;          # 通过Cookie切换到蓝色
}
server {
    # ...
    location ~ \.php$ {
        fastcgi_pass $backend;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

切换时只需设置Cookie Set-Cookie: blu_gren=blue,或者使用Lua/OpenResty动态从Redis/Etcd读取状态。


基于HAProxy的切换(适合高并发)

haproxy.cfg 配置

frontend php_front
    bind *:80
    default_backend php_green
backend php_green
    balance roundrobin
    server node1 10.0.1.10:9000 check weight 10
    server node2 10.0.1.11:9000 check weight 10
backend php_blue
    balance roundrobin
    server node1 10.0.2.10:9000 check weight 10
    server node2 10.0.2.11:9000 check weight 10

切换命令(无需重启)

# 切换到蓝组
echo "set weight php_blue/node1 10" | socat /var/run/haproxy.sock stdio
echo "set weight php_blue/node2 10" | socat /var/run/haproxy.sock stdio
echo "set weight php_green/node1 0" | socat /var/run/haproxy.sock stdio
echo "set weight php_green/node2 0" | socat /var/run/haproxy.sock stdio
# 或者更彻底:修改default_backend(需reload但几乎无中断)
echo "set weight php_blue/node1 10" | socat /var/run/haproxy.sock stdio
echo "set weight php_blue/node2 10" | socat /var/run/haproxy.sock stdio
echo "disable backend php_green" | socat /var/run/haproxy.sock stdio

更推荐:使用Runtime API实现平滑切换

# 用Python脚本通过API控制
import subprocess
def switch_to_blue():
    commands = [
        "disable backend php_green",
        "enable backend php_blue"
    ]
    for cmd in commands:
        subprocess.run(['socat', '/var/run/haproxy.sock', '-'], input=cmd + '\n', text=True)

基于Kubernetes Service的切换(容器化PHP)

创建两个Deployment + 两个Service

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-app-green
  labels:
    app: php-app
    color: green
spec:
  replicas: 2
  selector:
    matchLabels:
      app: php-app
      color: green
  template:
    metadata:
      labels:
        app: php-app
        color: green
    spec:
      containers:
      - name: php-fpm
        image: myapp:green-v1
---
apiVersion: v1
kind: Service
metadata:
  name: php-app-green-svc
spec:
  selector:
    app: php-app
    color: green
  ports:
  - port: 9000

类似地创建 php-app-bluephp-app-blue-svc

入口Ingress切换

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: php-app-ingress
spec:
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: php-app-green-svc   # 切换时改为 blue-svc
            port:
              number: 9000

一键切换命令

# 切换到蓝色
kubectl patch ingress php-app-ingress -p '{"spec":{"rules":[{"host":"app.example.com","http":{"paths":[{"path":"/","pathType":"Prefix","backend":{"service":{"name":"php-app-blue-svc","port":{"number":9000}}}}]}}]}}'
# 或者使用kubectl edit手动修改
kubectl edit ingress php-app-ingress

基于DNS的切换(相对粗粒度)

  1. 为两组节点配置不同A记录:
    • green.app.example.com → 绿组节点IP列表
    • blue.app.example.com → 蓝组节点IP列表
  2. 生产域名 app.example.com 的CNAME指向 greenblue
  3. 降低DNS TTL到60-120秒

缺点:DNS缓存导致切换延迟不可控,不建议用于关键业务。


切换时的关键注意事项

要点 说明
Session共享 PHP Session必须存储在外部(Redis/Memcached),不能依赖本地文件
数据库兼容 新版本代码必须兼容当前数据库结构(或使用读写分离+渐进式迁移)
缓存清理 切换后若代码变更,需要清除OPcache(opcache_reset() 或重启PHP-FPM)
健康检查 负载均衡器必须配置对新组的健康检查(HTTP 200或PHP探针)
灰度验证 推荐先切换部分流量(5%-10%)观察5-10分钟,再全量切换
回滚预案 保留回滚脚本或一键恢复命令,确保切换失败时60秒内恢复

推荐实践:最小中断切换流水线

graph LR
    A[部署蓝组] --> B[运行自动化测试]
    B --> C[健康检查通过?]
    C -->|Yes| D[负载均衡切换]
    C -->|No| E[回滚]
    D --> F[观察5分钟]
    F --> G[正常?]
    G -->|Yes| H[下线绿组]
    G -->|No| I[切回绿组]
    E --> J[修复]
    I --> J
    J --> A

  • 中小项目:Nginx upstream配置热加载(方案一)最简单直接
  • 高并发/复杂路由:HAProxy + Runtime API(方案二)更灵活
  • K8s环境:Ingress + Service切换(方案三)最优雅
  • 最重要:无论哪种方案,Session和缓存的外部化 是蓝绿发布成功的前提

抱歉,评论功能暂时关闭!