Nginx简单处理一次灰度发布问题

Nginx简单处理一次灰度发布问题

1.需求

公司接手的一个半成品项目,客户要求更新版本时不要停掉网站(灰度发布)。

马上想到的方案是:主从热备和负载均衡,nginx+keepalived,然后又想到了自动部署-CI/CD,及时打住避免越想越远。这个项目本身比较粗糙,简单的解决当前的问题即可。

原系统使用session存储会话信息,所以还要解决session如何共享的问题,google了一下,决定nginx配置两台服务器负载均衡,发布新版本时,一次更新一个,然后使用Redis来共享session。

2.实现

时间和精力有限,先在本地和测试服务器跑通一个demo。

两台机器:本机,服务器A

工具 本机 服务器A
JDK 1.8
Nginx ×
Redis ×

2.1 代码

首先新建spring-boot项目,代码如下。

(1) pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

(2) application.properties

1
2
3
4
5
6
server.port=8022

spring.redis.host={服务器A}
spring.redis.port=6379
spring.redis.password=
spring.redis.timeout=5000

(3) Application

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableRedisHttpSession
public class NginxKeepalivedRedisDemoApplication {

public static void main(String[] args) {
SpringApplication.run(NginxKeepalivedRedisDemoApplication.class, args);
}

}

(4) 控制器和实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RestController
public class LoginController {
@GetMapping(value = "/login")
public Map<String, Object> login(HttpSession session) throws UnknownHostException {
Map<String, Object> content = new HashMap<>();
content.put("sessionId",session.getId());
UserInfo userInfo = new UserInfo("01","张三");
session.setAttribute("userInfo", userInfo);
content.put("hostAddress",InetAddress.getLocalHost().getHostAddress());
content.put("hostName",InetAddress.getLocalHost().getHostName());
return content;
}
}

@Data
@AllArgsConstructor
public class UserInfo implements Serializable {
private static final long serialVersionUID = 5392373940126446170L;
private String id;
private String name;
}

2.2 调试

尝试启动,发现报错:Connection refused: no further information。很明显Redis无法连接。

先开了下命令行,尝试 telnet 服务器A 6379 ,无法连通。

检查了一下防火墙,确认端口已经开放了,然后又检查了一下阿里云控制平台上是否在安全组中开放端口,发现也是正确的。

google了一下,尝试修改redis.conf,将bind 127.0.0.1 修改为0.0.0.0。

再次telnet,可以连通,分别在本地和服务器A发布服务(服务器端口8022被占用,启动时指定了端口8023),并分别访问:http://{服务器A}:8023/login

1
2
3
4
5
6
7
8
9
10
11
12
13
# 服务器A返回如下:
{
"hostName":"xxxx",
"sessionId":"6a5e81a6-04f4-4ca8-afd4-1559695c1446",
"hostAddress":"xxx.xxx.xxx.xxx"
}

# 本地返回如下:
{
"hostName":"DESKTOP-xx",
"sessionId":"2a7c4873-1f9b-414a-85fc-6298562de64b",
"hostAddress":"192.168.31.197"
}

访问:http://qxamoy.com:8023/login

修改配置文件nginx.conf(我新建了一个demo.conf并在nginx.conf中引用)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 负载均衡配置
upstream demo_upstream{
server 127.0.0.1:8022 weight=10;
server xxx:8023 weight=20;
}

#虚拟主机的配置
server {
#监听端口
listen 7051;
# 指定ip地址或者域名,多个配置之间用空格分隔
server_name localhost;

#对 "/" 启用反向代理
location / {
proxy_pass http://demo_upstream;
...
}
}

启动nginx。

1
start nginx.exe

访问http://localhost:7051/login

可以观察每次请求后返回结果的变化(ip和session),顺利的话会看到根据权重来切换接收的服务端,所以日常更新版本时可以依次对单个服务器进行更新来完成“灰度发布”。


参考:

🔗 大家是怎么做到服务器热更新的?
🔗 超详细的Nginx负载均衡+高可用配置
🔗 Spring系列.@EnableRedisHttpSession原理简析