场景
现在有一个 java 语言编写的服务接口需要做升级优化,为了确保原来的接口有用,也需要保证新的接口也可以使用,故需要做一次灰度发布。
场景设置
准备工作
DDTrace 采集器用于采集链路信息,进入到 DataKit 安装目录下,执行 conf.d/ddtrace/
,复制 ddtrace.conf.sample
并重命名为 ddtrace.conf
,在 ddtrace.conf
配置新增 customer_tags=["test_flag"]
,将对应的 Baggage
转化为 tag 。
应用
调整应用的启动命令,假设端口 8091
为旧的接口应用。
java \
-javaagent:/home/liurui/agent/dd-java-agent-1.30.1-guance.jar \
-Ddd.service.name=server \
-Ddd.trace.header.baggage=test-flag:test_flag \
-Dserver.port=8091 \
-jar springboot-server.jar
复制代码
端口 8092
为优化后的接口应用。
java \
-javaagent:/home/liurui/agent/dd-java-agent-1.30.1-guance.jar \
-Ddd.service.name=server \
-Ddd.trace.header.baggage=test-flag:test_flag \
-Dserver.port=8092 \
-jar springboot-server.jar --client=true
复制代码
启动参数上基本上没啥区别。8092
添加了 --client=true
,会对请求造成异常,假设这个是新的代码调整。
Nginx 配置
采用 nginx 来实现业务分流操作,比如 user_agent
为 curl
的,让请求新的接口,其他的走原来的通道。同时追加 header,方便进行追踪。如 user_agent
为 curl
的相关请求,将自定义 header 值设置为 20240306
,其他默认值为 normal
。
map $http_user_agent $custom_header {
~*curl "20240306";
default "normal";
}
复制代码
将 user_agent
为 curl
的请求分流到 backend2
,默认分流到 backend1
。
set $upstream_name 'backend1';
if ($http_user_agent ~* "curl") {
set $upstream_name 'backend2';
}
proxy_pass http://$upstream_name;
复制代码
根据不同的 upstream_name
设置不同的 header 值。
proxy_set_header Test-Flag $custom_header; # 根据 upstream 地址设置不同的值
复制代码
两个 upstream
。
upstream backend1 {
server localhost:8091;
}
upstream backend2 {
server localhost:8092;
}
复制代码
通过 nginx -s reload
重启 nginx 。至此,nginx 配置基本上完成。
root:/etc/nginx/conf.d# nginx -s reload
info: DATADOG TRACER CONFIGURATION - {"agent_url":"http://localhost:9529","analytics_enabled":false,"analytics_sample_rate":null,"date":"2024-03-06T15:30:24+0800","enabled":true,"env":"prod","lang":"cpp","lang_version":"201402","operation_name_override":"nginx.handle","report_hostname":false,"sampling_rules":"[]","service":"nginx","version":"v1.3.7"}
复制代码
这里 nginx 接入了 ddtrace,非必须,如有需要,可参考文档 Nginx Tracing 。
Nginx 全文配置如下:
map $http_user_agent $custom_header {
~*curl "20240306";
default "normal";
}
upstream backend1 {
server localhost:8091;
}
upstream backend2 {
server localhost:8092;
}
server {
listen 80;
server_name www.springboot.com;
client_max_body_size 100m;
location ^~ / {
set $upstream_name 'backend1';
if ($http_user_agent ~* "curl") {
set $upstream_name 'backend2';
}
add_header 'Access-Control-Allow-Origin' *;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
proxy_pass http://$upstream_name;
proxy_set_header X-datadog-trace-id $opentracing_context_x_datadog_trace_id;
proxy_set_header X-datadog-parent-id $opentracing_context_x_datadog_parent_id;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Test-Flag $custom_header; # 根据 upstream 地址设置不同的值
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
复制代码
测试
分别通过浏览器和 curl
请求接口 http://www.springboot.com/gateway
。其中 curl 返回结果如下:
root:/etc/nginx/conf.d# curl www.springboot.com/gateway
{"msg":"client 调用失败","code":500}
复制代码
浏览器请求则返回如下信息:
{"msg":"支付成功","code":200}
复制代码
从观测云上通过链路追踪,可以发现所有的 span 都有 tag 为 test_flag
,其中值为 20240306
的链路为本次新发布的接口。
红色标记代表异常,说明当前链路处于异常状态。通过查看链路详情可以查看到堆栈信息,最终根据调整的代码进行再次发布,通过同样的方式进行再追踪、再验证。
后记
以上实践只是灰度发布的一部分,但笔者认为这是最核心、最重要的:确保业务的更新和正常使用。如何更有效率的确保发版成功,则需借助可观测性能力,让一切变得肉眼可见。
评论