一、灰度发布

  灰度发布是一种发布方式,也叫金丝雀发布,起源是矿工在下井之前会先放一只金丝雀到井里,如果金丝雀不叫了,就代表瓦斯浓度高。原因是金丝雀对瓦斯气体很敏感。灰度发布的做法是:会在现存旧应用的基础上,启动一个新版应用,但是新版应用并不会直接让用户访问。而是先让测试同学去进行测试。如果没有问题,则可以将真正的用户流量慢慢导入到新版,在这中间,持续对新版本运行状态做观察,直到慢慢切换过去,这就是所谓的A/B测试。当然,你也可以招募一些灰度用户,给他们设置独有的灰度标示(Cookie,Header),来让他们可以访问到新版应用,当然,如果中间切换出现问题,也应该将流量迅速地切换到老应用上。

1)准备新版本的service

  拷贝一份deployment文件:

cp deployment-user-v1.yaml deployment-user-v2.yaml

  修改之前写过的内容:

apiVersion: apps/v1  #API 配置版本
kind: Deployment #资源类型
metadata:
+ name: user-v2 #资源名称
spec:
selector:
matchLabels:
+ app: user-v2 #告诉deployment根据规则匹配相应的Pod进行控制和管理,matchLabels字段匹配Pod的label值
replicas: 3 #声明一个 Pod,副本的数量
template:
metadata:
labels:
+ app: user-v2 #Pod的名称
spec: #组内创建的 Pod 信息
containers:
- name: nginx #容器的名称
+ image: registry.cn-beijing.aliyuncs.com/zhangrenyang/nginx:user-v2
ports:
- containerPort: 80 #容器内映射的端口

  然后service的文件内容是这样的:

apiVersion: v1
kind: Service
metadata:
+ name: service-user-v2
spec:
selector:
+ app: user-v2
ports:
- protocol: TCP
port: 80
targetPort: 80
type: NodePort

  启动:

kubectl apply -f deployment-user-v2.yaml service-user-v2.yaml

2)根据cookie切分流量

  基于 Cookie 切分流量。这种实现原理主要根据用户请求中的 Cookie 是否存在灰度标示 Cookie去判断是否为灰度用户,再决定是否返回灰度版本服务

  • nginx.ingress.kubernetes.io/canary:可选值为 true / false 。代表是否开启灰度功能
  • nginx.ingress.kubernetes.io/canary-by-cookie:灰度发布 cookie 的 key。当 key 值等于 always 时,灰度触发生效。等于其他值时,则不会走灰度环境 ingress-gray.yaml

  我们创建一个ingress-gray.yaml文件:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: user-canary
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-cookie: "vip_user"
spec:
rules:
- http:
paths:
- backend:
serviceName: service-user-v2
servicePort: 80
backend:
serviceName: service-user-v2
servicePort: 80

  使文件生效:

kubectl apply -f ./ingress-gray.yaml 

  获取外部接口:

kubectl -n ingress-nginx get svc

  测试:

curl http://172.31.178.169:31234/user
curl http://118.190.156.138:31234/user
curl --cookie "vip_user=always" http://172.31.178.169:31234/user

3)基于header切分流量

  基于 Header 切分流量,这种实现原理主要根据用户请求中的 header 是否存在灰度标示 header去判断是否为灰度用户,再决定是否返回灰度版本服务。

  修改下上面的ingress-gray.yml文件即可:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: user-canary
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/canary: "true"
+ nginx.ingress.kubernetes.io/canary-by-header: "name"
+ nginx.ingress.kubernetes.io/canary-by-header-value: "vip"
spec:
rules:
- http:
paths:
- backend:
serviceName: service-user-v2
servicePort: 80
backend:
serviceName: service-user-v2
servicePort: 80

  同样的:

kubectl apply -f ingress-gray.yaml
curl --header "name:vip" http://172.31.178.169:31234/user

4)基于权重切分流量

  这种实现原理主要是根据用户请求,通过根据灰度百分比决定是否转发到灰度服务环境中

  • nginx.ingress.kubernetes.io/canary-weight:值是字符串,为 0-100 的数字,代表灰度环境命中概率。如果值为 0,则表示不会走灰度。值越大命中概率越大。当值 = 100 时,代表全走灰度。

  一样一样的,修改下配置参数罢了:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: user-canary
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/canary: "true"
+ nginx.ingress.kubernetes.io/canary-weight: "50"
spec:
rules:
- http:
paths:
- backend:
serviceName: service-user-v2
servicePort: 80
backend:
serviceName: service-user-v2
servicePort: 80

  测试下:

kubectl apply -f ingress-gray.yaml
for ((i=1; i<=10; i++)); do curl http://172.31.178.169:31234/user; done

  k8s 会优先去匹配 header ,如果未匹配则去匹配 cookie ,最后是 weight。

二、滚动发布

  滚动发布,则是我们一般所说的无宕机发布。其发布方式如同名称一样,一次取出一台/多台服务器(看策略配置)进行新版本更新。当取出的服务器新版确保无问题后,接着采用同等方式更新后面的服务器。k8s创建副本应用程序的最佳方法就是部署(Deployment),部署自动创建副本集(ReplicaSet),副本集可以精确地控制每次替换的Pod数量,从而可以很好的实现滚动更新。k8s每次使用一个新的副本控制器(replication controller)来替换已存在的副本控制器,从而始终使用一个新的Pod模板来替换旧的pod模板

  • 创建一个新的replication controller
  • 增加或减少pod副本数量,直到满足当前批次期望的数量
  • 删除旧的replication controller

  滚动发布的优缺点如下:

  • 优点

    • 不需要停机更新,无感知平滑更新。
    • 版本更新成本小,不需要新旧版本共存
  • 缺点
    • 更新时间长:每次只更新一个/多个镜像,需要频繁连续等待服务启动缓冲
    • 旧版本环境无法得到备份:始终只有一个环境存在
    • 回滚版本异常痛苦:如果滚动发布到一半出了问题,回滚时需要使用同样的滚动策略回滚旧版本

  我们下面来尝试下,先扩容为10个副本:

kubectl get deploy
kubectl scale deployment user-v1 --replicas=10

  修改deployment-user-v1.yaml文件:

apiVersion: apps/v1  #API 配置版本
kind: Deployment #资源类型
metadata:
name: user-v1 #资源名称
spec:
minReadySeconds: 1
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ selector:
+ matchLabels:
+ app: user-v1 #告诉deployment根据规则匹配相应的Pod进行控制和管理,matchLabels字段匹配Pod的label值
replicas: 10 #声明一个 Pod,副本的数量
template:
metadata:
labels:
app: user-v1 #Pod的名称
spec: #组内创建的 Pod 信息
containers:
- name: nginx #容器的名称
+ image: registry.cn-beijing.aliyuncs.com/zhangrenyang/nginx:user-v3 #使用哪个镜像
ports:
- containerPort: 80 #容器内映射的端口
参数 含义
minReadySeconds 容器接受流量延缓时间:单位为秒,默认为0。如果没有设置的话,k8s会认为容器启动成功后就可以用了。设置该值可以延缓容器流量切分
strategy.type = RollingUpdate ReplicaSet 发布类型,声明为滚动发布,默认也为滚动发布
strategy.rollingUpdate.maxSurge 最多Pod数量:为数字类型/百分比。如果 maxSurge 设置为1,replicas 设置为10,则在发布过程中pod数量最多为10 + 1个(多出来的为旧版本pod,平滑期不可用状态)。maxUnavailable 为 0 时,该值也不能设置为0
strategy.rollingUpdate.maxUnavailable 升级中最多不可用pod的数量:为数字类型/百分比。当 maxSurge 为 0 时,该值也不能设置为0

  启动:

kubectl apply -f ./deployment-user-v1.yaml
deployment.apps/user-v1 configured

  然后查看状态:

kubectl rollout status deployment/user-v1

三、服务可用性探针

  当 Pod 的状态为 Running 时,该 Pod 就可以被分配流量(可以访问到)了。一个后端容器启动成功,不一定不代表服务启动成功。

3.2.1 存活探针 LivenessProbe

  第一种是存活探针。存活探针是对运行中的容器检测的。如果想检测你的服务在运行中有没有发生崩溃,服务有没有中途退出或无响应,可以使用这个探针。如果探针探测到错误, Kubernetes 就会杀掉这个 Pod;否则就不会进行处理。如果默认没有配置这个探针, Pod 不会被杀死。

3.2.2 可用探针 ReadinessProbe

  第二种是可用探针。作用是用来检测 Pod 是否允许被访问到(是否准备好接受流量)。如果你的服务加载很多数据,或者有其他需求要求在特定情况下不被分配到流量,那么可以用这个探针。如果探针检测失败,流量就不会分配给该 Pod。在没有配置该探针的情况下,会一直将流量分配给 Pod。当然,探针检测失败,Pod 不会被杀死。

3.2.3 启动探针 StartupProbe

  第三种是启动探针。作用是用来检测 Pod 是否已经启动成功。如果你的服务启动需要一些加载时长(例如初始化日志,等待其他调用的服务启动成功)才代表服务启动成功,则可以用这个探针。如果探针检测失败,该 Pod 就会被杀死重启。在没有配置该探针的情况下,默认不会杀死 Pod 。在启动探针运行时,其他所有的探针检测都会失效。

探针名称 在哪个环节触发 作用 检测失败对Pod的反应
启动探针 Pod 运行时 检测服务是否启动成功 杀死 Pod 并重启
存活探针 Pod 运行时 检测服务是否崩溃,是否需要重启服务 杀死 Pod 并重启
可用探针 Pod 运行时 检测服务是不是允许被访问到 停止Pod的访问调度,不会被杀死重启

检测方式

1、ExecAction

  通过在 Pod 的容器内执行预定的 Shell 脚本命令。如果执行的命令没有报错退出(返回值为0),代表容器状态健康。否则就是有问题的

  我们来新建一个文件,vi shell-probe.yaml,内容如下:

apiVersion: v1
kind: Pod
metadata:
labels:
test: shell-probe
name: shell-probe
spec:
containers:
- name: shell-probe
image: registry.aliyuncs.com/google_containers/busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5

  然后执行下面的命令,查看情况:

kubectl apply -f liveness.yaml
kubectl get pods | grep liveness-exec
kubectl describe pods liveness-exec

2、TCPSocketAction

  这种方式是使用 TCP 套接字检测。 Kubernetes 会尝试在 Pod 内与指定的端口进行连接。如果能建立连接(Pod的端口打开了),这个容器就代表是健康的,如果不能,则代表这个 Pod 就是有问题的。

  创建文件如下,tcp-probe.yaml:

apiVersion: v1
kind: Pod
metadata:
name: tcp-probe
labels:
app: tcp-probe
spec:
containers:
- name: tcp-probe
image: nginx
ports:
- containerPort: 80
readinessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
periodSeconds: 10

  类似的:

kubectl apply -f tcp-probe.yaml
kubectl get pods | grep tcp-probe
kubectl describe pods tcp-probe

  进入到容器内部:

kubectl exec -it tcp-probe  -- /bin/sh

  更新apt-get并安装vim:

apt-get update
apt-get install vim -y
vi /etc/nginx/conf.d/default.conf

  修改nginx文件配置,把80端口修改为8080,然后重载一下nginx:

nginx -s reload

  看一下状态:

kubectl describe pod tcp-probe

3、HTTPGetAction

  这种方式是使用 HTTP GET 请求。Kubernetes 会尝试访问 Pod 内指定的API路径。如果返回200,代表容器就是健康的。如果不能,代表这个 Pod 是有问题的。

  添加http-probe.yaml文件:

apiVersion: v1
kind: Pod
metadata:
labels:
test: http-probe
name: http-probe
spec:
containers:
- name: http-probe
image: registry.cn-beijing.aliyuncs.com/zhangrenyang/http-probe:1.0.0
livenessProbe:
httpGet:
path: /liveness
port: 80
httpHeaders:
- name: source
value: probe
initialDelaySeconds: 3
periodSeconds: 3

  然后,运行并查看状态:

kubectl apply -f ./http-probe.yaml
kubectl describe pods http-probe
kubectl replace --force -f http-probe.yaml

  Dockerfile内容如下:

FROM node
COPY ./app /app
WORKDIR /app
EXPOSE 3000
CMD node index.js

  node服务文件如下:

let http = require('http');
let start = Date.now();
http.createServer(function(req,res){
if(req.url === '/liveness'){
let value = req.headers['source'];
if(value === 'probe'){
let duration = Date.now()-start;
if(duration>10*1000){
res.statusCode=500;
res.end('error');
}else{
res.statusCode=200;
res.end('success');
}
}else{
res.statusCode=200;
res.end('liveness');
}
}else{
res.statusCode=200;
res.end('liveness');
}
}).listen(3000,function(){console.log("http server started on 3000")});

  好了今天的内容就到这里了。

《前端运维》五、k8s--3灰度发布、滚动更新与探针的更多相关文章

  1. 【运维工具】Git代码发布系统

    引言 代码发布系统是互联网公司必备的运维系统,作用主要用户发布业务代码 到 业务服务器 为什么需要代码发布系统 有的同学可能说,我们公司服务器就那么一台,做个发布系统太麻烦了? 不认同这说法 发布系统 ...

  2. k8s实现灰度发布

    灰度发布在实际生产部署中是经常被使用的方式,常规的方法是手动从前端LB(负载均衡)上将后端服务器摘掉,然后,停服务,最后上传代码,完成软连接更新.在使用CI/CD工具时,这个过程变得自动化了,我们只需 ...

  3. Windows运维之Windows8.1-KB2999226-x64安装提示 此更新不适用你的计算机

    摘要:本文主要向大家介绍了Windows运维之Windows8.1-KB2999226-x64安装提示 此更新不适用你的计算机,通过具体的内容向大家展现,希望对大家学习Windows运维有所帮助. 本 ...

  4. Linux运维五:定时任务crond服务

    一.crond简介 crond是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动cro ...

  5. 真正云原生的智能运维体系,阿里云发布ECS自动化运维套件

    云计算的发展,推动了自动化运维.DevOps.AIOps 等趋势的兴起,在业务快速变化的今天,企业希望通过一套自动化运维的专家系统提高运维效率,为业务提供支撑. 传统的方式下,打造一套成熟的 DevO ...

  6. docker swarm实现java项目的发布/滚动更新/回滚/镜像管理

    使用docker swarm滚动更新java项目,部署集群,这一切的前提是使用Jenkins+maven进行项目打包,分发等功能 具体可以参考我的另外三篇文章 https://www.cnblogs. ...

  7. 《前端运维》五、k8s--1安装与基本配置

    一.k8s基础概念与安装 k8s,即kubernetes是用于自动部署,扩展和管理容器化应用程序的开源系统.详细的描述就不多说了,官网有更详细的内容.简单来说,k8s,是一个可以操作多台机器调度部署镜 ...

  8. 《前端运维》三、Docker--2其他

    一.制作DockerFile docker的镜像类似于用一层一层的文件组成.inspect命令可以查看镜像或容器的的信息,其中Layers就是镜像的层文件,只读不能修改,基于镜像创建的容器会共享这些层 ...

  9. 高级运维(五):构建memcached服务、LNMP+memcached、使用Tomcat设置Session、Tomcat实现session共享

    一.构建memcached服务 目标: 本案例要求先快速搭建好一台memcached服务器,并对memcached进行简单的添.删.改.查操作: 1> 安装memcached软件,并启动服务d ...

随机推荐

  1. aspnetcore 使用serilog日志

    而在实际项目开发中,使用第三方日志框架来记录日志也是非常多的,首先一般基础的内置日志记录器在第三方日志框架中都有实现,然后很多第三方日志框架在功能上更强大和丰富,能满足我们更多的项目分析和诊断的需求. ...

  2. macbook安装scala、hadoop、saprk环境

    一.scala安装 1. 安装jdk 有mac专用的jdk安装包,这里下载安装jdk1.8 2. 安装scala 2.1下载scala 2.2解压到指定目录 tar -zxvf /Users/lode ...

  3. CPU使用率过高怎么办

    实际上前文中关于CPU使用率过高如何通过各种工具获得相关的热点进程.那么进程有了,那得疑惑到底哪个哪段代码导致了这个进程成为热点呢? 如果在调试阶段,可以使用gdb中断运行,但是在生产环境肯定不行.L ...

  4. JAVA8学习——新的时间日期API&Java8总结

    JAVA8-时间日期API java8之前用过的时间日期类. Date Calendar SimpleDateFormat 有很多致命的问题. 1.没有时区概念 2.计算麻烦,实现困难 3.类是可变的 ...

  5. jenkins发布代码选择不同分支

    jenkins上传代码分支以前都是用变量的方式,手动实现.过程就像这样 构建时候的界面就像下面这样,需要手动输入分支版本. 或者有固定的上线分支,用参数化构建 选项参数 来选择.总之这些方法比较传统, ...

  6. 使用jvisualvm监控k8s内java项目

    资源文件yaml配置 -javaagent:/usr/skywalking/agent/skywalking-agent.jar -Dcom.sun.management.jmxremote -Dja ...

  7. Python 中线程和进程

    目录 线程和进程 一. 什么是进程 / 线程 1. 引论 2. 线程 3. 进程 4. 区别 5. 使用 二. 多线程使用 1. 常用方法 2. 常用参数 3. 多线程的应用 3.1 重写线程法 3. ...

  8. 在不受支持的 Mac 上安装 macOS Monterey 12(OpenCore Patcher)

    一.介绍 本文通用于 macOS Big Sur 和 macOS Monterey,也可以视作笔者 早期文章 的升级版. 这一章节将介绍 macOS Monterey 的系统要求和不受支持的 Mac ...

  9. 计算机网络-IP篇

    目录 IP 基本认识 IP地址 IP 地址的分类 公有 IP 地址与私有 IP 地址 IP 地址与路由控制 IP 分⽚与重组 IPv6 基本认识 IPv4 ⾸部与 IPv6 ⾸部 IP协议 DNS A ...

  10. C# 重载运算符--不合理设计,只支持静态

    什么叫做重载运算符 比如:int x=1; int y=2; int total=x+y; 我们比较喜欢看上面这种写法,而不是这种, int x=1; int y=1; int total=int.a ...