金丝雀发布

什么是金丝雀发布?

相传上个世纪煤矿工人在作业时,为了避免瓦斯中毒会随身带一只金丝雀下到矿洞,由于金丝雀对二氧化碳非常敏感,所以看到金丝雀昏厥的时候矿工们就知道该逃生了。

金丝雀发布带来的好处是显而易见的,从上线的角度讲,每一次上线发布都是有风险的,如何最小化风险,当然不是不上线,逐步验证,直到确认无误再逐步全量更新,这就是金丝雀发布的意义,我们来看一个简单的demo

源码以message内容作为区分,pong-v1对应w1版本, pong-v2对应w2版本

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong-v1”,//pong-v2
        })
    })
    r.Run(":8000")
}

Dockerfile如下所示

FROM golang as builder

ENV GO111MODULE=on

WORKDIR /app

COPY go.mod .
COPY go.sum .

RUN export GOPROXY=https://mirrors.aliyun.com/goproxy/ && go mod download

COPY . .

# 因为是在树莓派上运行,所以需要将GOARCH设置成arm
RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -o httpserver

FROM scratch
COPY --from=builder /app/httpserver /app/
EXPOSE 8000
ENTRYPOINT ["/app/httpserver"]

启动两个版本的容器

docker -D run -p 127.0.0.3:8002:8000 w2
docker -D run -p 127.0.0.2:8001:8000 w1

在nginx配置文件中增加以下内容

upstream backend {
    server 127.0.0.2:8001 weight=10;
    server 127.0.0.3:8002 weight=5;
}


server {
    listen       8080;
    location /ping {
    proxy_pass http://backend;
    }
}

接口调用

➜  ~ curl http://10.200.182.14:8080/ping
{"message":"pong-v1"}%                                                                               
➜  ~ curl http://10.200.182.14:8080/ping
{"message":"pong-v1"}%                                                                               
➜  ~ curl http://10.200.182.14:8080/ping
{"message":"pong-v2"}%                                                                               
➜  ~ curl http://10.200.182.14:8080/ping
{"message":"pong-v1"}%                                                                               
➜  ~ curl http://10.200.182.14:8080/ping
{"message":"pong-v1"}%                                                                               
➜  ~ curl http://10.200.182.14:8080/ping
{"message":"pong-v2"}%

可以明显看到 pong-v1pong-v2的数量对比为2:1, 正好与nginx中的配置的权重相等,这里如果把w2比作待上线版本,w1看作当前生产版本,如若w2有问题,只会影响1/3的接口流量,这就做到了降低风险的目的。这里使用的是加权的轮询算法(ptr++ mod nodeNum

弊端:按序集中,整体流量不够分散 需要一个相对分散,看起来更平滑的轮询算法

当然,这只是一个简单的Demo,具体到生产环境还要考虑其它问题,比如以用户ID作为流量切分的依据、新版本服务无感知加入生产服务列表、快速回滚预案、数据修复预案等等。

参考:

comments powered by Disqus