kubernetes 上的 RollingUpdate 不会阻止网关超时

RollingUpdate on kubernetes does not prevent Gateway Timeout

我关注了 http://rahmonov.me/posts/zero-downtime-deployment-with-kubernetes/ 博客,创建了两个 docker 图像 index.html 返回 'Version 1 of an app' 和 'Version 2 of an app'。我想要实现的是零停机发布。我正在使用

kubectl apply -f mydeployment.yaml

里面有image: mynamespace/nodowntime-test:v1

部署v1版本到k8s然后运行:

while True
    do
            printf "\n---------------------------------------------\n"
            curl "http://myhosthere"
            sleep 1s
    done

到目前为止一切正常。短时间卷曲 returns 'Version 1 of an app' 后。 然后我使用 image: mynamespace/nodowntime-test:v2 应用相同的 k8s 部署文件。好吧,它可以工作,但是在 v1 和 v2 之间有一个(总是一个)网关超时 响应。所以它并不是真的没有停机版本; ) 比没有 RollingUpdate 要好很多,但并不完美。

我正在使用 RollingUpdate 策略和 readinessProbe:

---                              
apiVersion: apps/v1              
kind: Deployment                 
metadata:                        
  name: nodowntime-deployment    
spec:                            
  replicas: 1                    
  strategy:                      
    type: RollingUpdate          
    rollingUpdate:               
      maxUnavailable: 0          
      maxSurge: 1                
  selector:                      
    matchLabels:                 
      app: nodowntime-test       
  template:                      
    metadata:                    
      labels:                    
        app: nodowntime-test     
    spec:                        
      containers:                
      ...                        
        readinessProbe:          
          httpGet:               
            path: /              
            port: 80             
          initialDelaySeconds: 5 
          periodSeconds: 5       
          successThreshold: 5 

我可以做得更好吗?将所有这些与入口控制器同步是否存在问题?我知道我可以通过使用 minReadySeconds 来调整它,所以新旧 pod 会重叠一段时间,但这是唯一的解决方案吗?

我重新创建了上述实验,并通过启动以下三个同时进程将请求数更改为接近每秒 30 次:

While True
    do
        curl -s https://<NodeIP>:<NodePort>/ -m 0.1 --connect-timeout 0.1 | grep Version || echo "fail"
done

经过多次编辑部署和更改镜像版本,在转换过程中完全没有丢包。 我什至发现了两张图片同时发出服务请求的片刻。

  Version 1 of my awesome app! Money is pouring in!
  Version 1 of my awesome app! Money is pouring in!
  Version 1 of my awesome app! Money is pouring in!
  Version 2 of my awesome app! More Money is pouring in!
  Version 1 of my awesome app! Money is pouring in!
  Version 1 of my awesome app! Money is pouring in!
  Version 2 of my awesome app! More Money is pouring in!
  Version 2 of my awesome app! More Money is pouring in!
  Version 2 of my awesome app! More Money is pouring in!

因此,如果您直接向服务发送请求,它将按预期工作。

“Gateway Timeout” 错误是来自 Traefik 代理的回复。它通过一组 iptables 规则打开到后端的 TCP 连接。
当你执行 RollingUpdates 时,iptables 规则已经改变但 Traefic 不知道,所以从 Traefik 的角度来看连接仍然被认为是打开的。在第一次尝试通过不存在的 iptables 规则失败后,Traefik 报告 "Gateway Timeout" 并关闭 tcp 连接。在下一次尝试中,它通过新的 iptables 规则打开了一个到后端的新连接,一切都再次顺利。

Traefik 中 enabling retries 可以修复它。

# Enable retry sending request if network error
[retry]

# Number of attempts
#
# Optional
# Default: (number servers in backend) -1
#
# attempts = 3

更新:

最后我们在不使用 traefik 的 'retry' 功能的情况下解决了这个问题,这可能需要对所有服务进行幂等处理(无论如何都有它很好,但我们负担不起强迫所有项目都这样做)。您需要的是 kubernetes RollingUpdate 策略 + ReadinessProbe 配置并在您的应用中实现优雅关闭。