Kubernetes之Ingress

目录

Ingress介绍

Ingress Controller介绍

Ingress和Ingress Controller总结

使用Ingress Controller代理k8s内部应用的流程

Ingress实现业务灰度发布

常见的版本发布方法

蓝绿部署

红黑部署

灰度发布

滚动发布

部署Ingress Controller

部署两个版本的服务

V1版本

V2版本

创建Ingress指向V1版本

基于 Header 的流量切分

基于 Cookie 的流量切分

基于服务权重的流量切分


Ingress介绍

        Ingress官网定义:Ingress可以把进入到集群内部的请求转发到集群中的一些服务上,从而可以把服务映射到集群外部。Ingress 能把集群内Service 配置成外网能够访问的 URL,流量负载均衡,提供基于域名访问的虚拟主机等。

        Ingress简单的理解就是你原来需要改Nginx配置,然后配置各种域名对应哪个 Service,现在把这个动作抽象出来,变成一个 Ingress 对象,你可以用 yaml 创建,每次不要去改Nginx 了,直接改yaml然后创建/更新就行了;那么问题来了:”Nginx 该怎么处理?”

        Ingress Controller 这东西就是解决 “Nginx 的处理方式” 的;Ingress Controller 通过与 Kubernetes API 交互,动态的去感知集群中Ingress规则变化,然后读取他,按照他自己模板生成一段 Nginx 配置,再写到 Nginx Pod 里,最后 reload 一下

Ingress Controller介绍

        Ingress Controller是一个七层负载均衡调度器,客户端的请求先到达这个七层负载均衡调度器,由七层负载均衡器在反向代理到后端pod,常见的七层负载均衡器有nginx、traefik,以我们熟悉的nginx为例,假如请求到达nginx,会通过upstream反向代理到后端pod应用,但是后端pod的ip地址是一直在变化的,因此在后端pod前需要加一个service,这个service只是起到分组的作用,那么我们upstream只需要填写service地址即可

Ingress和Ingress Controller总结

        Ingress Controller 可以理解为控制器,它通过不断的跟 Kubernetes API 交互,实时获取后端Service、Pod的变化,比如新增、删除等,结合Ingress 定义的规则生成配置,然后动态更新上边的 Nginx 或者trafik负载均衡器,并刷新使配置生效,来达到服务自动发现的作用。

        Ingress 则是定义规则,通过它定义某个域名的请求过来之后转发到集群中指定的 Service。它可以通过 Yaml 文件定义,可以给一个或多个 Service 定义一个或多个 Ingress 规则。

使用Ingress Controller代理k8s内部应用的流程

  • 部署Ingress controller,我们ingress controller使用的是nginx
  • 创建Pod应用,可以通过控制器创建pod
  • 创建Service,用来分组pod
  • 创建Ingress http,测试通过http访问应用
  • 创建Ingress https,测试通过https访问应用

客户端通过七层调度器访问后端pod的方式

使用七层负载均衡调度器ingress controller时,当客户端访问kubernetes集群内部的应用时,数据包走向如下图流程所示:

Ingress实现业务灰度发布

常见的版本发布方法

蓝绿部署

        蓝绿部署,是采用两个分开的集群对软件版本进行升级的一种方式。它的部署模型中包括一个蓝色集群 A 和一个绿色集群 B,在没有新版本上线的情况下,两个集群上运行的版本是一致的,同时对外提供服务。

        系统升级时,蓝绿部署的流程是:

首先,从负载均衡器列表中删除集群 A,让集群 B 单独提供服务。

然后,在集群 A 上部署新版本。

接下来,集群 A 升级完毕后,把负载均衡列表全部指向 A,并删除集群 B,由 A 单独提供服务。

在集群 B 上部署完新版本后,再把它添加回负载均衡列表中。

这样,我们就完成了两个集群上所有机器的版本升级。

从上述的部署流程中,也能发现,蓝绿部署它的优点在于发布策略简单、对于用户来说无感知,可以实现升级平滑过渡。但它的缺点也很明显:需要准备正常业务使用资源的两倍以上服务器,需要投入较大的资源成本。当然对于不差钱、追求服务稳定性的公司而言,较为推荐这种部署模式。

红黑部署

        红黑部署是Netflix采用的部署手段,Netflix的主要基础设施是在AWS上,它与蓝绿部署类似,红黑部署也是通过两个集群完成软件版本的升级。

        当前提供服务的所有机器都运行在红色集群 A 中,当需要发布新版本的时候,具体流程是这样的:

  • 先在云上申请一个黑色集群 B,在 B 上部署新版本的服务;
  • 等到 B 升级完成后,我们一次性地把负载均衡全部指向 B;
  • 把 A 集群从负载均衡列表中删除,并释放集群 A 中所有机器。

        这样就完成了一个版本的升级。可以看到,与蓝绿部署相比,红黑部署只不过是充分利用了云计算的弹性伸缩优势,从而获得了两个收益:一是,简化了流程;二是,避免了在升级的过程中,由于只有一半的服务器提供服务,而可能导致的系统过载问题。

灰度发布

        灰度发布,也被叫作金丝雀发布。与蓝绿部署、红黑部署不同的是,灰度发布属于增量发布方法。也就是说,服务升级的过程中,新旧版本会同时为用户提供服务。

        灰度发布的具体流程是这样的:在集群的一小部分机器上部署新版本,给一部分用户使用,以测试新版本的功能和性能;确认没有问题之后,再对整个集群进行升级。简单地说,灰度发布就是把部署好的服务分批次、逐步暴露给越来越多的用户,直到最终完全上线。

之所以叫作灰度发布,是因为它介于黑与白之间,并不是版本之间的直接切换,而是一个平滑过渡的过程。

        AB Test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度,而我们平常所说的金丝雀部署也就是灰度发布的一种方式。

        之所以又被叫作金丝雀发布,是因为金丝雀对瓦斯极其敏感,17 世纪时英国矿井工人会携带金丝雀下井,以便及时发现危险。这就与灰色发布过程中,先发布给一部分用户来测试相似,因而得名。

        对于灰度发布来说,它的优点在于如果前期出问题影响范围很小,相对用户体验也少;可以做到及时发现、及时调整问题,影响范围可控。但是采取这种模式对自动化以及运维监控能力的要求非常高。

滚动发布

        滚动发布是指每次只升级一个或多个服务,升级完成后加入生产环境,不断执行这个过程,直到集群中的全部旧版本升级新版本。

  • 红色:正在更新的实例
  • 蓝色:更新完成并加入集群的实例
  • 绿色:正在运行的实例

        这种部署方式相对于蓝绿部署,更加节约资源——它不需要运行两个集群、两倍的实例数。我们可以部分部署,例如每次只取出集群的20%进行升级,比较节约资源,但同时缺点也很明显:采用滚动发布方式部署时,没有一个确定OK的环境。如果使用蓝绿部署,我们能够清晰地知道老版本是OK的,而使用滚动发布,我们无法确定。并且一旦发布过程出现问题,需要回滚,回滚过程非常困难。

        在实际工作当中,升级过程中需要保持服务的连续性、稳定,对外界无感知是几个基本的要求。在生产上选择哪种部署方法最合适?这取决于哪种方法最适合你的业务和技术需求。

部署Ingress Controller

选择合适的版本,本文使用的是Kubernetes 1.23版本,Ingress Controller版本选择为v1.6.4

如果有需要yaml文件的可以评论区@我

# 部署Ingress Controller
[root@master ingress-nginx]# cd
[root@master ~]# kubectl apply -f deploy.yaml 
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created

部署两个版本的服务

V1版本

[root@master ~]# cat nginx_v1.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      version: v1
  template:
    metadata:
      labels:
        app: nginx
        version: v1
    spec:
      containers:
      - name: nginx
        image: "openresty/openresty:centos"
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          protocol: TCP
          containerPort: 80
        volumeMounts:
        - mountPath: /usr/local/openresty/nginx/conf/nginx.conf
          name: config
          subPath: nginx.conf
      volumes:
      - name: config
        configMap:
          name: nginx-v1
---
apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    app: nginx
    version: v1
  name: nginx-v1
data:
  nginx.conf: |-
    worker_processes  1;
    events {
        accept_mutex on;
        multi_accept on;
        use epoll;
        worker_connections  1024;
    }
    http {
        ignore_invalid_headers off;
        server {
            listen 80;
            location / {
                access_by_lua '
                    local header_str = ngx.say("nginx-v1")
                ';
            }
        }
    }
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-v1
spec:
  type: ClusterIP
  ports:
  - port: 80
    protocol: TCP
    name: http
  selector:
    app: nginx
    version: v1
[root@master ~]# kubectl apply -f nginx_v1.yaml 
deployment.apps/nginx-v1 created
configmap/nginx-v1 created
service/nginx-v1 created

V2版本

[root@master ~]# cat nginx_v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      version: v2
  template:
    metadata:
      labels:
        app: nginx
        version: v2
    spec:
      containers:
      - name: nginx
        image: "openresty/openresty:centos"
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          protocol: TCP
          containerPort: 80
        volumeMounts:
        - mountPath: /usr/local/openresty/nginx/conf/nginx.conf
          name: config
          subPath: nginx.conf
      volumes:
      - name: config
        configMap:
          name: nginx-v2
---
apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    app: nginx
    version: v2
  name: nginx-v2
data:
  nginx.conf: |-
    worker_processes  1;
    events {
        accept_mutex on;
        multi_accept on;
        use epoll;
        worker_connections  1024;
    }
    http {
        ignore_invalid_headers off;
        server {
            listen 80;
            location / {
                access_by_lua '
                    local header_str = ngx.say("nginx-v2")
                ';
            }
        }
    }
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-v2
spec:
  type: ClusterIP
  ports:
  - port: 80
    protocol: TCP
    name: http
  selector:
    app: nginx
    version: v2
[root@master ~]# kubectl apply -f nginx_v2.yaml
deployment.apps/nginx-v2 created
configmap/nginx-v2 created
service/nginx-v2 created

创建Ingress指向V1版本

[root@master ~]# cat nginx_ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - path: /  #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
        pathType:  Prefix
        backend:  #配置后端服务
         service:
           name: nginx-v1
           port:
            number: 80
[root@master ~]# kubectl apply -f nginx_ingress.yaml 
ingress.networking.k8s.io/nginx created

访问测试

curl -H "Host: canary.example.com" http://EXTERNAL-IP # EXTERNAL-IP 替换为 Nginx Ingress 自身对外暴露的 IP

[root@master ~]# kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.1.139.131   <pending>     80:31357/TCP,443:30170/TCP   82s
ingress-nginx-controller-admission   ClusterIP      10.1.212.45    <none>        443/TCP                      82s

[root@master ~]# curl -H "Host: canary.example.com" http://10.1.139.131
nginx-v1

或者使用以下方法进行测试

[root@master ~]# kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.1.139.131   <pending>     80:31357/TCP,443:30170/TCP   4m27s
ingress-nginx-controller-admission   ClusterIP      10.1.212.45    <none>        443/TCP                      4m27s
[root@master ~]# kubectl describe svc -n ingress-nginx ingress-nginx-controller 
Name:                     ingress-nginx-controller
Namespace:                ingress-nginx
Labels:                   app.kubernetes.io/component=controller
                          app.kubernetes.io/instance=ingress-nginx
                          app.kubernetes.io/name=ingress-nginx
                          app.kubernetes.io/part-of=ingress-nginx
                          app.kubernetes.io/version=1.6.4
Annotations:              <none>
Selector:                 app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.1.139.131
IPs:                      10.1.139.131
Port:                     http  80/TCP
TargetPort:               http/TCP
NodePort:                 http  31357/TCP
Endpoints:                192.168.207.166:80
Port:                     https  443/TCP
TargetPort:               https/TCP
NodePort:                 https  30170/TCP
Endpoints:                192.168.207.166:443
Session Affinity:         None
External Traffic Policy:  Local
HealthCheck NodePort:     31952
Events:                   <none>

# 根据查到的Endpoints,找一台机器写入hosts解析
192.168.207.166 canary.example.com

# 在浏览器访问canary.example.com域名

基于 Header 的流量切分

        创建 Canary Ingress,指定 v2 版本的后端服务,且加上一些 annotation,实现仅将带有名为 Region 且值为 cd 或 sz 的请求头的请求转发给当前 Canary Ingress,模拟灰度新版本给成都和深圳地域的用户。

[root@master ~]# cat nginx_canary.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-header: "Region"
    nginx.ingress.kubernetes.io/canary-by-header-pattern: "cd|sz"
  name: nginx-canary
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - path: /  #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
        pathType:  Prefix
        backend:  #配置后端服务
         service:
           name: nginx-v2
           port:
            number: 80
[root@master ~]# kubectl apply -f nginx_canary.yaml 
ingress.networking.k8s.io/nginx-canary created

访问测试

curl -H "Host: canary.example.com" -H "Region: cd" http://EXTERNAL-IP # EXTERNAL-IP 替换为 Nginx Ingress 自身对外暴露的 IP

[root@master ~]# kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.1.139.131   <pending>     80:31357/TCP,443:30170/TCP   12m
ingress-nginx-controller-admission   ClusterIP      10.1.212.45    <none>        443/TCP                      12m

# 可以看到,只有 header Region 为 cd 或 sz 的请求才由 v2 版本服务响应
[root@master ~]# curl -H "Host: canary.example.com" -H "Region: cd" 10.1.139.131
nginx-v2
[root@master ~]# curl -H "Host: canary.example.com" -H "Region: sz" 10.1.139.131
nginx-v2
[root@master ~]# curl -H "Host: canary.example.com" -H "Region: zz" 10.1.139.131
nginx-v1
[root@master ~]# curl -H "Host: canary.example.com" -H "Region: bj" 10.1.139.131
nginx-v1

基于 Cookie 的流量切分

        与前面 Header 类似,不过使用 Cookie 就无法自定义 value 了,这里以模拟灰度成都地域用户为例,仅将带有名为 user_from_cd 的 cookie 的请求转发给当前 Canary Ingress 。先删除前面基于 Header 的流量切分的 Canary Ingress,然后创建下面新的 Canary Ingress。

[root@master ~]# cat nginx_canary.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-by-cookie: "user_from_cd"
  name: nginx-canary
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - path: /  #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
        pathType:  Prefix
        backend:  #配置后端服务
         service:
           name: nginx-v2
           port:
            number: 80
[root@master ~]# kubectl apply -f nginx_canary.yaml 
ingress.networking.k8s.io/nginx-canary configured

访问测试

curl -s -H "Host: canary.example.com" --cookie "user_from_cd=always" http://EXTERNAL-IP # EXTERNAL-IP 替换为 Nginx Ingress 自身对外暴露的 IP

[root@master ~]# kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.1.139.131   <pending>     80:31357/TCP,443:30170/TCP   15m
ingress-nginx-controller-admission   ClusterIP      10.1.212.45    <none>        443/TCP                      15m

# 可以看到,只有 cookie user_from_cd 为 always 的请求才由 v2 版本的服务响应
[root@master ~]# curl -s -H "Host: canary.example.com" --cookie "user_from_cd=always" 10.1.139.131
nginx-v2
[root@master ~]# curl -s -H "Host: canary.example.com" --cookie "user_from_cd=never" 10.1.139.131
nginx-v1

基于服务权重的流量切分

        基于服务权重的 Canary Ingress 就简单了,直接定义需要导入的流量比例,这里以导入 10% 流量到 v2 版本为例 (如果有,先删除之前的 Canary Ingress)

[root@master ~]# cat nginx_canary.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
  name: nginx-canary
spec:
  rules:
  - host: canary.example.com
    http:
      paths:
      - path: /  #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
        pathType:  Prefix
        backend:  #配置后端服务
         service:
           name: nginx-v2
           port:
            number: 80
[root@master ~]# kubectl apply -f nginx_canary.yaml 
ingress.networking.k8s.io/nginx-canary configured

访问测试

for i in {1..10}; do curl -H "Host: canary.example.com" http://EXTERNAL-IP; done;

[root@master ~]# kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.1.139.131   <pending>     80:31357/TCP,443:30170/TCP   22m
ingress-nginx-controller-admission   ClusterIP      10.1.212.45    <none>        443/TCP                      22m

# 可以看到,大概只有十分之一的几率由 v2 版本的服务响应,符合 10% 服务权重的设置,可以多访问几次试试
[root@master ~]# for i in {1..10}; do curl -H "Host: canary.example.com" http://10.1.139.131; done;
nginx-v2
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1

  • 36
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值