Ingress--k8s七层代理

原理介绍

Ingress 和 Ingress Controller 介绍

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

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

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

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

数据包走向

在这里插入图片描述

高可用Ingress Controller部署

整体思路

将ingress-controller副本数设置为2,利用pod反亲和性,使两个pod调度到不同的节点并共享宿主机网络。使用nginx反向代理两个ingress-controller,keepalived配置虚拟IP并监测nginx工作状态。(这也是常用的高可用实现方案)

数据走向:
域名 ==> 虚拟IP ==> nginx ==> ingress-controller ==> service ==> pod
集群规划:

节点IP角色组件
s1192.168.0.63master
s2192.168.0.64workeringress-controller/nginx/keepalived
s3192.168.0.65workeringress-controller/nginx/keepalived

ip根据自身网段进行设置

安装ingress-controller

Ingress-controller 官网: https://github.com/kubernetes/ingress-nginx/
Ingress-controller 和 k8s 版本对照:
在这里插入图片描述
本文使用 kubernetes-v1.23.1、ingress-controller-v1.1.0 、kube-webhook-certgen-v1.1.1

部署ingress-controller

kubectl label node s2 kubernetes.io/ingress=nginx                     # 给s2、s3节点打上标签
kubectl label node s3 kubernetes.io/ingress=nginx                     # 其实没什么实际意义
kubectl apply -f ingress-deploy.yaml

部署所用yaml文件下载地址:https://download.csdn.net/download/weixin_45826416/85068743

部署后查看pod信息:

kubectl get pods -n ingress-nginx

看到如下pod状态则部署成功
在这里插入图片描述
yaml文件重点内容节选解释:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.10
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.1.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  replicas: 2                                                        # 副本数为2
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx                          
      app.kubernetes.io/instance: ingress-nginx
      app.kubernetes.io/component: controller
  revisionHistoryLimit: 10
  minReadySeconds: 0
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/component: controller
    spec:
      hostNetwork: true                                              # 共享宿主机网络
      affinity:                                                      # pod亲和性定义
        podAntiAffinity:                                             # pod反亲和性
          preferredDuringSchedulingIgnoredDuringExecution:           # 作用在pod调度时的软亲和性
          - weight: 100                                              # 权重(1-100)
            podAffinityTerm:          
              labelSelector:                                         # 通过pod标签选择
                matchLabels:                                          
                  app.kubernetes.io/name: ingress-nginx              # 具有该标签的pod尽量不调度到一起
              topologyKey: kubernetes.io/hostname                    # 不调度主机名相同的节点

部署nginx、keepalived

在s2、s3安装、配置nginx和keepalived

yum install -y nginx keepalived                                     # 安装nginx、keepalived

nginx配置

修改nginx配置文件,主备一致,内容如下:

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}
# 四层负载均衡,为两台 ingress-controller 组件提供负载均衡
stream {
    log_format main '$remote_addr $upstream_addr - [$time_local] $status $upstream_bytes_sent';
    access_log /var/log/nginx/k8s-access.log main;

    upstream k8s-apiserver {                                     # 定义服务器池
        server s2:80;
        server s3:80;
    }
    server {                              
        listen 30080;                                            # 监听30080(必须大于30000)
        proxy_pass k8s-apiserver;                                # 代理到定义的服务器池
    }

}
http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;
    
    include /etc/nginx/conf.d/*.conf;
}

安装stream模块:

yum install nginx-mod-stream -y                               # 安装了才可以使用代理功能

启动nginx:

systemctl enable nginx --now

keepalived配置

主keepalived:

! Configuration File for keepalived

global_defs {
   router_id nginx_master_s2                                 # id,全局唯一
}

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"                  # 定义要使用的检测脚本
}

vrrp_instance VI_1 {                                         # 定义虚拟路由器
    state MASTER                                             # 定义初始状态
    interface ens192                                         # 工作接口
    virtual_router_id 52                                     # 虚拟组id
    priority 100                                             # 优先级
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {      
        192.168.0.112                                        # 虚拟ip
    }
    track_script {
        check_nginx                                          # 检测脚本,返回非0时vip漂移
    }
}

备keepalived:

! Configuration File for keepalived

global_defs {
   router_id nginx_backup__s3
}

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens192
    virtual_router_id 52
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.0.112
    }
    track_script {
        check_nginx
    }
}

在s2、s3创建检测脚本并赋予执行权限:

touch /etc/keepalived/check_nginx.sh
chmod +x /etc/keepalived/check_nginx.sh

两个节点的check_nginx.sh脚本一致,内容如下:

#!/bin/bash
ngx=`ps -C nginx --no-header |wc -l`
if [ $ngx -eq 0 ];then
        systemctl start nginx
        sleep 2
        ngx=`ps -C nginx --no-header |wc -l`
        if [ $ngx -eq 0 ];then
                exit 1
        fi
else
        exit 0
fi

开启s2、s3的keepalived:

systemctl enable keepalived --now

可自行测试keepalived工作情况(vip是否能来回漂移)

测试ingress七层代理

部署后端tomcat服务

kubectl apply -f ingress-demo.yaml

yaml文件内容如下:

apiVersion: v1
kind: Service
metadata:
  name: tomcat
  namespace: default
spec:
  selector:
    app: tomcat
    release: canary
  ports:
  - name: http
    targetPort: 8080
    port: 8080
  - name: ajp
    targetPort: 8009
    port: 8009
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-deploy
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: tomcat
      release: canary
  template:
    metadata:
      labels:
        app: tomcat
        release: canary
    spec:
      containers:
      - name: tomcat
        image: tomcat:8.5.34-jre8-alpine
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 8080
          name: ajp
          containerPort: 8009

查看pod是否部署成功:

kubectl get pods -l app=tomcat

在这里插入图片描述

kubectl get svc                                            # 查看service

在这里插入图片描述

编写ingress规则

cat ingress-myapp.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-myapp
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "nginx"                    # 选择ingress-controller
spec:
  rules:                                                    # 定义后端转发的规则
  - host: tomcat.lucky.com                                  # 通过域名进行转发
    http:
      paths:
      - path: /                                             # #配置访问路径,如果通过 url 进行转发,需要修改;空默认为访问的路径为"/"
        pathType:  Prefix
        backend:                                            
         service:                                           # 配置后端服务
           name: tomcat
           port:
            number: 8080
kubectl apply -f ingress-myapp.yaml                         # 更新yaml文件
kubectl describe ingress ingress-myapp                      # 查看ingress详细信息

在这里插入图片描述

修改host文件,查看效果

修改电脑本地的 hosts文件(C:\Windows\System32\drivers\etc\hosts),增加如下一行,下面的 ip 是 虚拟 ip。

192.168.0.112 tomcat.lucky.com

浏览器访问 tomcat.lucky.com,出现如下页面:
在这里插入图片描述

通过ingress实现灰度发布

使用场景

场景一: 将新版本灰度给部分用户
假设线上运行了一套对外提供 7 层服务的 Service A 服务,后来开发了个新版本 Service A’ 想要上线,但又不想直接替换掉原来的 Service A,希望先灰度一小部分用户,等运行一段时间足够稳定了再逐渐全量上线新版本,最后平滑下线旧版本。这个时候就可以利用 Nginx Ingress 基于 Header 或 Cookie 进行流量切分的策略来发布,业务使用 Header 或 Cookie 来标识不同类型的用户,我们通过配置 Ingress 来实现让带有指定 Header 或 Cookie 的请求被转发到新版本,其它的仍然转发到旧版本,从而实现将新版本灰度给部分用户:
在这里插入图片描述
场景二: 切一定比例的流量给新版本
假设线上运行了一套对外提供 7 层服务的 Service B 服务,后来修复了一些问题,需要灰度上线一个新版本 Service B’,但又不想直接替换掉原来的 Service B,而是让先切 10% 的流量到新版本,等观察一段时间稳定后再逐渐加大新版本的流量比例直至完全替换旧版本,最后再滑下线旧版本,从而实现切一定比例的流量给新版本:
在这里插入图片描述

后端服务部署

部署nginx-v1

部署一个nginx服务,版本为v1,访问时返回值为"nginx-v1",v1.yaml定义了一个deployment、一个configmap、一个service内容如下:

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
kubectl apply -f v1.yaml                                   # 应用yaml文件

部署nginx-v2

部署一个nginx服务,版本为v2,访问时返回值为"nginx-v2",v2.yaml定义了一个deployment、一个configmap、一个service内容如下:

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
kubectl get pods                                           # 查看pod状态

在这里插入图片描述
可以看到nginx-v1 和 nginx-v2都成功运行

基于 Request Header 的流量切分

规则说明

nginx.ingress.kubernetes.io/canary-by-header:基于 Request Header 的流量切分,适用于灰度发布以及 A/B 测试。当 Request Header 设置为 always 时,请求将会被一直发送到 Canary 版本;当 Request Header 设置为 never 时,请求不会被发送到 Canary 入口。

nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务。当 Request Header 设置为此值时,它将被路由到 Canary 入口。

创建一个ingress,指向v1

创建一个ingress,域名为“canary.example.com”,后端服务为“nginx-v1”,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: /
        pathType: Prefix
        backend:
          service:
            name: nginx-v1
            port: 
              number: 80
kubectl apply -f v1-ingress.yaml                           # 应用ingress

访问验证一下:

curl -H "Host: canary.example.com" http://EXTERNAL-IP:PORT      # EXTERNAL-IP 替换为Nginx Ingress自身对外暴露的IP(ingress-controller所在宿主机的ip),端口为ingress-nginx-controller服务暴露的端口。

在这里插入图片描述

创建基于请求头的ingress

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

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: /
        pathType: Prefix
        backend:
          service:
            name: nginx-v2
            port:
              number: 80
kubectl apply -f v2-ingress.yaml

访问测试:

curl -H "Host: canary.example.com" -H "Region: cd" http://192.168.1.64:32147
curl -H "Host: canary.example.com" -H "Region: sz" http://192.168.1.64:32147
curl -H "Host: canary.example.com" -H "Region: bj" http://192.168.1.64:32147

在这里插入图片描述

可以看到当请求头为“cd”或“sz”时,返回值为nginx-v2,其他则返回nginx-v1。

基于 Cookie 的流量切分

规则说明

nginx.ingress.kubernetes.io/canary-by-cookie:基于 Cookie 的流量切分,适用于灰度发布与 A/B 测试。用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务的 cookie。当cookie 值设置为 always 时,它将被路由到 Canary 入口;当 cookie 值设置为 never 时,请求不会被发送到 Canary 入口。

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

kubectl delete -f v2-ingress.yaml                          # 删除基于header的ingress

创建ingress

vim v1-cookie.yaml                                         # 创建新的canary ingress
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: /
        pathType: Prefix
        backend:
          service:
            name: nginx-v2
            port: 
              number: 80
kubectl apply -f v1-cookie.yaml

测试访问:

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

在这里插入图片描述
可以看到,只有cookie user_from_cd 为 always 的请求才由 v2 版本的服务响应。

基于服务权重的流量切分

规则说明

nginx.ingress.kubernetes.io/canary-weight:基于服务权重的流量切分,适用于蓝绿部署,权重范围 0 - 100 按百分比将请求路由到 Canary Ingress 中指定的服务。权重为 0 意味着该金丝雀规则不会向 Canary 入口的服务发送任何请求。权重为 60 意味着 60%流量转到 canary。权重为 100 意味着所有请求都将被发送到 Canary 入口。

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

kubectl delete -f v1-cookie.yaml

创建ingress

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: /
        pathType: Prefix
        backend:
          service:
            name: nginx-v2
            port: 
              number: 80
kubectl apply -f v1-weight.yaml

测试访问:

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

在这里插入图片描述
可以看到,十次访问只有1次访问到nginx-v2。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值