大家好,相信大部分公司都已经使用K8S进行容器管理和编排了,但是关于K8S的发布策略,还有很多同学不太清楚,通过这篇文章的介绍,相信大家对目前K8S的发布情况有一个概括的认识。总结下来,共有如下几种:

  1. 重建(recreate) :即停止一个原有的容器,然后进行容器的新建。
  2. 滚动更新(rollingUpdate):停掉一个容器,然后更新一个容器。
  3. 蓝绿布署(blue/green ):准备一套蓝色的容器和一套绿色的容器,进行流量切换。
  4. 金丝雀发布(canary):更新部分容器,没有问题后进行逐步替换,直到切完。
  5. A/B测试发布:即将发布的结果面向部分用户,这块没有现成的组件,需要进行自行处理,比如使用Istio、Linkerd、Traefik等。这种方式采用在Http的Header上进行处理。
  6. 无损发布:现在很多发布都是将容器停掉,当没有请求的时候这个时候发布会实现无损发布。

  接下来,我们将挨个展示来讲。

  一、重新(reCreate)

    就是停掉原来的容器,然后再启动容器,这种方式对于开发环境和测试环境使用还可以,但是对于正式环境就不适用了。相当于本地的服务重启一下,这样会直接影响服务的使用。

  1. spec:
  2. replicas: 2
  3. strategy:
  4. type: Recreate

  这个就是同时启动2个服务,如下图,当我们要新发布服务的时候,需要将这两个都停掉之后才启动新服务。

  这种情况缺点是十分明显的,当服务停止之后再创建新的服务。服务什么时候启动,也是根据服务启动时间决定的。

  二、滚动更新(rollingUpdate)

  滚动更新步骤:

  1. 准备一个新版本的POD,比如新版本为V2,旧版本为V1。

  2. 当V2版本的POD准备好后,在负载均衡中的实例池中将V2的版本加入。

  3. 在实例池中剔除一个V1版本的POD。

  4. 逐个实例替换,直到每个版本都是V2版本。

  如下图所示:

  滚动更新使用的YAML方式如下:

  1. spec:
  2. replicas: 5
  3. strategy:
  4. type: RollingUpdate
  5. rollingUpdate:
  6. maxSurge: 2 # 一次可以添加多少个Pod
  7. maxUnavailable: 1 # 滚动更新期间最大多少个Pod不可用

  我们准备两个版本, my-app-v1.yaml文件。

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: my-app
  5. labels:
  6. app: my-app
  7. spec:
  8. type: NodePort
  9. ports:
  10. - name: http
  11. port: 80
  12. targetPort: http
  13. selector:
  14. app: my-app
  15. ---
  16. apiVersion: apps/v1
  17. kind: Deployment
  18. metadata:
  19. name: my-app
  20. labels:
  21. app: my-app
  22. spec:
  23. replicas: 3
  24. selector:
  25. matchLabels:
  26. app: my-app
  27. strategy:
  28. type: Recreate
  29. selector:
  30. matchLabels:
  31. app: my-app
  32. template:
  33. metadata:
  34. labels:
  35. app: my-app
  36. version: v1.0.0
  37. annotations:
  38. prometheus.io/scrape: "true"
  39. prometheus.io/port: "9101"
  40. spec:
  41. containers:
  42. - name: my-app
  43. image: containersol/k8s-deployment-strategies
  44. ports:
  45. - name: http
  46. containerPort: 8080
  47. - name: probe
  48. containerPort: 8086
  49. env:
  50. - name: VERSION
  51. value: v1.0.0
  52. livenessProbe:
  53. httpGet:
  54. path: /live
  55. port: probe
  56. initialDelaySeconds: 5
  57. periodSeconds: 5
  58. readinessProbe:
  59. httpGet:
  60. path: /ready
  61. port: probe
  62. periodSeconds: 5

  然后我们再准备一下滚动更新的v2版本, my-app-rolling-update.yaml

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: my-app
  5. labels:
  6. app: my-app
  7. spec:
  8. replicas: 10
  9. # maxUnavailable设置为0可以完全确保在滚动更新期间服务不受影响,还可以使用百分比的值来进行设置。
  10. strategy:
  11. type: RollingUpdate
  12. rollingUpdate:
  13. maxSurge: 1
  14. maxUnavailable: 0
  15. selector:
  16. matchLabels:
  17. app: my-app
  18. template:
  19. metadata:
  20. labels:
  21. app: my-app
  22. version: v2.0.0
  23. annotations:
  24. prometheus.io/scrape: "true"
  25. prometheus.io/port: "9101"
  26. spec:
  27. containers:
  28. - name: my-app
  29. image: containersol/k8s-deployment-strategies
  30. ports:
  31. - name: http
  32. containerPort: 8080
  33. - name: probe
  34. containerPort: 8086
  35. env:
  36. - name: VERSION
  37. value: v2.0.0
  38. livenessProbe:
  39. httpGet:
  40. path: /live
  41. port: probe
  42. initialDelaySeconds: 5
  43. periodSeconds: 5
  44. readinessProbe:
  45. httpGet:
  46. path: /ready
  47. port: probe
  48. # 初始延迟设置高点可以更好地观察滚动更新过程
  49. initialDelaySeconds: 15
  50. periodSeconds: 5

  接下来,我们按如下步骤进行操作:

  1. 启动my-app-v1应用

  2. 启动my-app-rollingupdate应用

  3. 观察所有容器版本变为V2版本

  

  启动my-app-v1应用并观察,都已经启动,我们进行接口调用。

  1. apply -f my-app-v1.yaml
  2. kubectl get pods -l app=my-app
  3.  
  4. NAME READY STATUS RESTARTS AGE
  5. my-app-847874cd75-h3d2e 1/1 Running 0 47s
  6. my-app-847874cd75-p4l8f 1/1 Running 0 47s
  7. my-app-847874cd75-qnt7p 1/1 Running 0 47s
  8. $ kubectl get svc my-app
  9. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  10. my-app NodePort 10.109.99.184 <none> 80:30486/TCP 1m
  11. $ curl http://127.0.0.1:30486
  12. Host: my-app-847874cd75-qnt7p, Version: v1.0.0

   在当前窗口监控容器的情况,执行命令

  1. watch kubectl get pods -l app=my-app

  打开一个新的窗口,然后执行滚动更新命令

  1. kubectl apply -f my-app-rolling-update.yaml

  在watch的终端观察,开始的时候并没删除旧的pod,而是创建好新的pod后,然后进行挨个替,我们可以使用如下命令观察接口请求, 渐渐的有了第二个版本的请求。

  1. $ while sleep 0.1; do curl http://127.0.0.1:30486; done
  2.  
  3. Host:my-app-847874cd75-h3d2e, Version: v1.0.0
    ......
    Host: my-app-847874cd75-h3d2e, Version: v1.0.0
    Host: my-app-6b5479d97f-2fk24, Version: v2.0.0

  在这个过程中,我们发现第二个版本有问题,我们需要进行回滚,此时我们需要执行一下如下命令

  1. kubectl rollout undo deploy my-app

  如果想两个版本都观察一下,这个时候需要执行命令。

  1. kubectl rollout pause deploy my-app

  如果发现第二个版本没有问题,那么我们要恢复执行

  1. kubectl rollout resume deploy my-app

  最后我们清理一下所有的部署

  1. kubectl delete -l app=my-app

  总结一下:

  1. 滚动部署没有控制流量的情况。

  2. 各个版本部署的时候需要一定的时间。

  三、蓝绿部署

  我们需要准备两个版本,一个蓝版本,一个绿蓝本,这两个版本同时存在,且两个版本是独立的,这个时候可以瞬间对两个版本的切换。上图:

  接下来,我们采用svc的方式,通过label selector来进行版本之间的选择。

  1. selector:
  2. app: my-app
  3. version: v1.0.0

  我们创建一下 app-v1-svc.yaml文件,我们首先创建一个svc服务,然后通过label selector来指定一下版本。

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: my-app
  5. labels:
  6. app: my-app
  7. spec:
  8. type: NodePort
  9. ports:
  10. - name: http
  11. port: 80
  12. targetPort: http
  13. # 注意这里我们匹配 app 和 version 标签,当要切换流量的时候,我们更新 version 标签的值,比如:v2.0.0
  14. selector:
  15. app: my-app
  16. version: v1.0.0
  17. ---
  18. apiVersion: apps/v1
  19. kind: Deployment
  20. metadata:
  21. name: my-app-v1
  22. labels:
  23. app: my-app
  24. spec:
  25. replicas: 3
  26. selector:
  27. matchLabels:
  28. app: my-app
  29. version: v1.0.0
  30. template:
  31. metadata:
  32. labels:
  33. app: my-app
  34. version: v1.0.0
  35. annotations:
  36. prometheus.io/scrape: "true"
  37. prometheus.io/port: "9101"
  38. spec:
  39. containers:
  40. - name: my-app
  41. image: containersol/k8s-deployment-strategies
  42. ports:
  43. - name: http
  44. containerPort: 8080
  45. - name: probe
  46. containerPort: 8086
  47. env:
  48. - name: VERSION
  49. value: v1.0.0
  50. livenessProbe:
  51. httpGet:
  52. path: /live
  53. port: probe
  54. initialDelaySeconds: 5
  55. periodSeconds: 5
  56. readinessProbe:
  57. httpGet:
  58. path: /ready
  59. port: probe
  60. periodSeconds: 5

  接下来,我们再创建另一个版本 app-v2-svc.yaml文件

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: my-app-v2
  5. labels:
  6. app: my-app
  7. spec:
  8. replicas: 3
  9. selector:
  10. matchLabels:
  11. app: my-app
  12. version: v2.0.0
  13. template:
  14. metadata:
  15. labels:
  16. app: my-app
  17. version: v2.0.0
  18. annotations:
  19. prometheus.io/scrape: "true"
  20. prometheus.io/port: "9101"
  21. spec:
  22. containers:
  23. - name: my-app
  24. image: containersol/k8s-deployment-strategies
  25. ports:
  26. - name: http
  27. containerPort: 8080
  28. - name: probe
  29. containerPort: 8086
  30. env:
  31. - name: VERSION
  32. value: v2.0.0
  33. livenessProbe:
  34. httpGet:
  35. path: /live
  36. port: probe
  37. initialDelaySeconds: 5
  38. periodSeconds: 5
  39. readinessProbe:
  40. httpGet:
  41. path: /ready
  42. port: probe
  43. periodSeconds: 5

  接下来,我们执行一下操作步骤:

  1. 启动版本1服务

  2. 启动版本2服务

  3. 将版本1服务切换到版本2,观察服务情况

  启动版本的服务并观察,没有问题。

  1. kubectl apply -f app-v1-svc.yaml
  2. kubectl get pods -l app=my-app
  3. watch kubectl get pods -l app=my-app
  1. while sleep 0.1; do curl http://127.0.0.1:31539; done
  2. Host: my-app-v1-7b4874cd75-dmq8f, Version: v1.0.0
  3. Host: my-app-v1-7b4874cd75-dmq8f, Version: v1.0.0

  启动版本2的服务, 因为版本2没有挂到SVC,所以没有办法观察,但是我们可以先启动。

  1. kubectl apply -f app-v2-svc.yaml

  这个时候我们进行版本的切换,通过切换标签的方式

  1. kubctl patch service my-ap -p '{"spec":{"selector":{"version":"v2.0.0"}}}'
  1. while sleep 0.1; do curl http://127.0.0.1:31539; done
  2. Host: my-app-v2-f885c8d45-r5m6z, Version: v2.0.0
  3. Host: my-app-v2-f885c8d45-r5m6z, Version: v2.0.0  

  同时,当发现问题的时候,我们再把版本切回到v1.0.0即可。

  总结:

  1. 蓝绿部署需要准备两套资源,相对有的时候需要的资源会多。

  2. 这种情况可以快速还原版本,不会出现滚动更新那样,回滚需要的时间会很久。

 

  四、金丝雀部署

  金丝雀部署就是将部分新版本发在旧的容器池里边,然后进行流量观察,比如10%的流量切到新服务上,90%的流量还在旧服务上。如果你想用更精细粒度的话精使用Istio。上图

  我们这次还是采用svc的方式进行部署的选择,但是这次我们在选择的时候我们只使用label,不使用版本号。新建 app-v1-canary.yaml, 里边有10个pod支撑这个服务。

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: my-app
  5. labels:
  6. app: my-app
  7. spec:
  8. type: NodePort
  9. ports:
  10. - name: http
  11. port: 80
  12. targetPort: http
  13. selector:
  14. app: my-app
  15. ---
  16. apiVersion: apps/v1
  17. kind: Deployment
  18. metadata:
  19. name: my-app-v1
  20. labels:
  21. app: my-app
  22. spec:
  23. replicas: 10
  24. selector:
  25. matchLabels:
  26. app: my-app
  27. version: v1.0.0
  28. template:
  29. metadata:
  30. labels:
  31. app: my-app
  32. version: v1.0.0
  33. annotations:
  34. prometheus.io/scrape: "true"
  35. prometheus.io/port: "9101"
  36. spec:
  37. containers:
  38. - name: my-app
  39. image: containersol/k8s-deployment-strategies
  40. ports:
  41. - name: http
  42. containerPort: 8080
  43. - name: probe
  44. containerPort: 8086
  45. env:
  46. - name: VERSION
  47. value: v1.0.0
  48. livenessProbe:
  49. httpGet:
  50. path: /live
  51. port: probe
  52. initialDelaySeconds: 5
  53. periodSeconds: 5
  54. readinessProbe:
  55. httpGet:
  56. path: /ready
  57. port: probe
  58. periodSeconds: 5

  接下来,我们创建v2版本 app-v2-canary.yaml文件

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: my-app-v2
  5. labels:
  6. app: my-app
  7. spec:
  8. replicas: 1
  9. selector:
  10. matchLabels:
  11. app: my-app
  12. version: v2.0.0
  13. template:
  14. metadata:
  15. labels:
  16. app: my-app
  17. version: v2.0.0
  18. annotations:
  19. prometheus.io/scrape: "true"
  20. prometheus.io/port: "9101"
  21. spec:
  22. containers:
  23. - name: my-app
  24. image: containersol/k8s-deployment-strategies
  25. ports:
  26. - name: http
  27. containerPort: 8080
  28. - name: probe
  29. containerPort: 8086
  30. env:
  31. - name: VERSION
  32. value: v2.0.0
  33. livenessProbe:
  34. httpGet:
  35. path: /live
  36. port: probe
  37. initialDelaySeconds: 5
  38. periodSeconds: 5
  39. readinessProbe:
  40. httpGet:
  41. path: /ready
  42. port: probe
  43. periodSeconds: 5

  我们说一下要执行的步骤:

  1. 启动V1的服务版本:10个复本。

  2. 启动V2的服务版本:1个复本。

  3. 观察V2流量正常的情况的话,那么启动V2的10个复本。

  4. 删除V1的10个复本,流量全部到V2上。

  

  启动V1服务,查看服务是否正确,然后观察一下服务。

  1. kubectl apply -f app-v1-canary.yaml
  2. kubectl get svc -l app=my-app
    curl http://127.0.0.1:30760
  3. watch kubectl get pod

  新打开容器,执行命令,启动V2服务。 上边的watch将观察到新增了1个pod, 此时共有11个pod, 2.0.0的版本已经上来了。

  1. kubectl apply -f app-v2-canary.yaml
  1. hile sleep 0.1; do curl http://127.0.0.1:30760; done
  2. Host: my-app-v1-7b4874cd75-bhxbp, Version: v1.0.0
  3. Host: my-app-v1-7b4874cd75-wmcqc, Version: v1.0.0
  4. Host: my-app-v1-7b4874cd75-tsh2s, Version: v1.0.0
  5. Host: my-app-v1-7b4874cd75-ml58j, Version: v1.0.0
  6. Host: my-app-v1-7b4874cd75-spsdv, Version: v1.0.0
  7. Host: my-app-v2-f885c8d45-mc2fx, Version: v2.0.0

  此时我们观察版本2的服务是否正确,如果正确,那么我们将版本2扩展到10个复本。

  1. kubectl scale --replicas=10 deploy my-app-v2

  这个时候版本1和版本2一样了。我们再将版本1的pod给清除掉

  1. kubectl delete deploy my-app-v1

  如果没有问题可以清理所有服务

  1. kubectl delete all -l app=my-app

  结论:

  1. 因为有部分版本在线上运行,我们能够对其日志进行观察和追踪、定位问题。

  2. 如果有问题也能快速将新版本清理掉

  3. 如果没有问题,相比于蓝绿的话,发布较慢。

  4. 对代码信心不足的情况可以采用此方法,影响范围较小。

  五、A/B测试

  这种方法一般适合于业务场景测试,比如场景A的情况下带来的交易额是否会增大,也就是针对不同的人展示不同的界面,然后来观察效益。

  这个时候,我们需要在需要对流量进行按权重分发,或者是在Header, cookie 中做文章。比如,我们可以采用Istio进行权限的分发。

  1. route:
  2. - tags:
  3. version: v1.0.0
  4. weight: 90
  5. - tags:
  6. version: v2.0.0
  7. weight: 10

  结论:

  1. 这种方法可以将流量进行分发,我们需要做一个全局的链路跟踪

  2. 这种其实不属于部署范围,是流量分发的一种机制。

  

  六、无损发布

  我们经常会遇到的情况是,程序正在运行的时候,pod突然停止了,这个时候,执行的一半的程序很可能会对数据的完整性造成影响。这个时候就需要我们精巧的设计,流量的切换,优雅的停服务。

  有开源的OpenKruise v0.5.0支持无损发布

  基于service mesh网络服务,无损发布的方法是采用K8S+Istio边车模式+envoy实现无损发布。

  阿里云也有企业级的EDAS,支持无损发布,有兴趣的同学也可以去学习一下。、

  

  好了。有问题的同学欢迎留言。

K8S发布策略,无损发布的更多相关文章

  1. webpack-高级-发布策略

    webpack的发布策略 在实际开发中,一般会有两套项目方案: 一套是开发期间的项目,包含了测试文件.测试数据.开发工具.测试工具等相关配置,有利于项目的开发和测试,但是这些文件仅用于开发,发布项目时 ...

  2. K8s 1.18.6版本基于 ingress-nginx 实现金丝雀发布(灰度发布)

    K8s 1.18.6版本基于 ingress-nginx 实现金丝雀发布(灰度发布) 环境 软件 版本 kubernetes v1.18.6 nginx-ingress-controller 0.32 ...

  3. k8s:py项目发布完整流程

    k8s:py项目发布流程 1. 编写Dockerfile # cat Dockerfile FROM python:3.6-slim USER root RUN apt-get update & ...

  4. 蓝绿部署、红黑部署、AB测试、灰度发布、金丝雀发布、滚动发布的概念与区别(转)

    出处:https://www.baidu.com/link?url=QjboallwNm_jxcL3fHG57wEakiBfAs_3-TChTGu1eBXstlHEsGBc-NDA7AKTqsiroB ...

  5. 蓝绿部署、A/B测试以及灰度发布(金丝雀发布)

    过去的10多年里,很多大公司都在使用蓝绿部署,安全.可靠是这种部署方式的特点.蓝绿部署虽然算不上”Sliver Bullet“,但确实很实用.在有关于“微服务”.“DevOps”.“Cloud-nat ...

  6. 蓝绿部署、滚动部署、金丝雀(Canary)发布、灰度发布、A/B测试

    最近看到Canary发布,一时没有反应过来是什么,一查才发现就是鼎鼎有名的金丝雀发布,发现经常一起出现的还有灰度发布.蓝绿部署.滚动部署.A/B测试,故一起学习一下这几个概念. 1. 蓝绿部署 目的: ...

  7. 【原创】我所理解的自动更新-APP发布与后台发布

    发布后台 创建渠道:添加新的渠道,设置渠道名称,自动生成渠道id.    查看渠道:查看渠道基本信息,渠道app版本号,资源版本号,是否开启更新.    创建/更新APP:选择打包ios,androi ...

  8. IDEA发布应用时发布到lib下面的包不全

    IDEA发布应用时发布到lib下面的包不全,Tomcate启动时就报:At least one JAR was scanned for TLDs yet contained no TLDs. Enab ...

  9. 各个 C# 版本的主要特性、发布日期和发布方式(C# 1.0 - 7.3)

    原文 各个 C# 版本的主要特性.发布日期和发布方式(C# 1.0 - 7.3) 本文收集各个 C# 版本的主要特性.发布日期和发布方式. C# 8.0 尚在预览版本 C# 7.3 2018 年 5 ...

随机推荐

  1. 关于selenium添加使用代理ip

    最近在爬某个网站,发现这个网站的反爬太厉害了,正常时候的访问有时候都会给你弹出来验证,验证你是不是蜘蛛,而且requests发的请求携带了请求头信息,cookie信息,代理ip,也能识别是爬虫,他应该 ...

  2. P3244-[HNOI2015]落忆枫音【dp】

    正题 题目链接:https://www.luogu.com.cn/problem/P3244 题目大意 给出一个\(\text{DAG}\),保证\(1\)可以到达所有点.然后再加入一条边(之后不一定 ...

  3. oracle常见命令

    1.权限 (1)系统权限 系统权限是指对数据库系统的权限和对象结构控制的权限. 如grant create session to 用户名 -赋予用户登录的权限 (2)对象权限 访问其它用户对象的权利 ...

  4. element-ui上传多个文件时会发送多个请求

    1. element-ui的默认 默认是异步多次请求上传单个文件 如果业务就是单纯的上传文件,那么这个样子是没有问题的 前端代码参考 https://element-plus.gitee.io/#/z ...

  5. Serverless 在编程教育中的实践

    说起Serverless这个词,我想大家应该都不陌生,那么Serverless这个词到底是什么意思?Serverless到底能解决什么问题?可能很多朋友还没有深刻的体会和体感,这篇文章我就和大家一起聊 ...

  6. ArcToolbox工具箱

    3D Analyst 工具 Data Interoperability Tools Geostatistical Analyst 工具 Network Analyst 工具 Schematics 工具 ...

  7. InstallScript脚本语言基本知识(一)

    1.自定义函数 1 //函数的声明:prototype 返回值 函数名(形参类型1,...) 2 export prototype STRING GetPreDir(STRING); 3 4 //函数 ...

  8. Java语言程序设计与数据结构(基础篇)第七章答案

    答案为本人求解,如有错误,还望海涵.如有雷同,纯属巧合. 7.1 import java.util.Scanner; public class Main { public static void ma ...

  9. jenkins+allure中测试包括为空,没有数据

  10. 2020.3.21--ICPC训练联盟周赛Benelux Algorithm Programming Contest 2019

    A Appeal to the Audience 要想使得总和最大,就要使最大值被计算的次数最多.要想某个数被计算的多,就要使得它经过尽量多的节点.于是我们的目标就是找到 k 条从长到短的链,这些链互 ...