实战:ingress-nginx 高级配置1-2022.1.5

实战:ingress-nginx 高级配置1-2022.1.5

image-20220103092629589

目录

实验环境

实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
   k8s version:v1.22.2
   containerd://1.5.5

实验软件

1、Nginx 配置

如果我们还想进行一些自定义配置,则有几种方式可以实现:使用 Configmap 在 Nginx 中设置全局配置、通过 Ingress 的 Annotations 设置特定 Ingress 的规则、自定义模板。接下来我们重点给大家介绍使用注解来对 Ingress 对象进行自定义

1.Basic Auth

📍 演示1:在 Ingress 对象上配置一些基本的 Auth 认证,比如 Basic Auth

🍀 在测试以下实验前,我们先来看下一节课的demo案例(后面几个demo都是在这个案例上改了几处进行测试的)

[root@master1 ingress]#cat first-ingress.yaml 
# my-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    app: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    name: http
  selector:
    app: my-nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-nginx
  namespace: default
spec:
  ingressClassName: nginx  # 使用 nginx 的 IngressClass(关联的 ingress-nginx 控制器)
  rules:
  - host: ngdemo.qikqiak.com  # 将域名映射到 my-nginx 服务
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:  # 将所有请求发送到 my-nginx 服务的 80 端口
            name: my-nginx
            port:
              number: 80
# 不过需要注意大部分Ingress控制器都不是直接转发到Service
# 而是只是通过Service来获取后端的Endpoints列表(因此这里的svc只起到了一个服务发现的作用),直接转发到Pod,这样可以减少网络跳转,提高性能

#pc笔记本hosts文件:域名解析
#C:\WINDOWS\System32\drivers\etc
172.29.9.51 ngdemo.qikqiak.com	

#查看pod和ingress
[root@master1 ingress]#kubectl get ingress my-nginx
NAME       CLASS   HOSTS                ADDRESS       PORTS   AGE
my-nginx   nginx   ngdemo.qikqiak.com   172.29.9.51   80      23h
[root@master1 ingress]#kubectl get po -l app=my-nginx
NAME                        READY   STATUS    RESTARTS   AGE
my-nginx-7c4ff94949-lwvjf   1/1     Running   0          23h

#验证ingress应用
方法1:在自己pc里不配置域名解析,直接用curl命令测试
[root@master1 ingress]#curl -v http://172.29.9.51 -H 'Host: ngdemo.qikqiak.com' #注意下这个命令的用法
* About to connect() to 172.29.9.51 port 80 (#0)
*   Trying 172.29.9.51...
* Connected to 172.29.9.51 (172.29.9.51) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host: ngdemo.qikqiak.com
>
< HTTP/1.1 200 OK
< Date: Mon, 03 Jan 2022 08:51:03 GMT
< Content-Type: text/html
< Content-Length: 615
< Connection: keep-alive
< Last-Modified: Tue, 28 Dec 2021 15:28:38 GMT
< ETag: "61cb2d26-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host 172.29.9.51 left intact

方法2:在自己pc里配置了域名解析,然后在浏览器里进行测试

image-20220103165219193

🍀 我们可以在 Ingress 对象上配置一些基本的 Auth 认证,比如 Basic Auth,可以用 htpasswd 生成一个密码文件来验证身份验证。

root@master1 ~]#mkdir basic-auth
[root@master1 ~]#cd basic-auth/
[root@master1 ~]#yum install -y httpd-tools #记得安装下httpd-tools软件包,htpasswd命令依赖于这个软件包

[root@master1 basic-auth]#htpasswd -c auth foo
New password:
Re-type new password: #当时输入的密码是 foo
Adding password for user foo
[root@master1 basic-auth]#cat auth 
foo:$apr1$GbRmxR7F$PEf8UGh1BEF1vibLSJl5K/ #密码的base64加密

🍀 然后根据上面的 auth 文件创建一个 secret 对象:

[root@master1 basic-auth]#kubectl create secret generic basic-auth --from-file=auth 
secret/basic-auth created
[root@master1 basic-auth]#kubectl get secrets basic-auth -oyaml
apiVersion: v1
data:
  auth: Zm9vOiRhcHIxJEdiUm14UjdGJFBFZjhVR2gxQkVGMXZpYkxTSmw1Sy8K
kind: Secret
metadata:
  creationTimestamp: "2022-01-03T11:13:38Z"
  name: basic-auth
  namespace: default
  resourceVersion: "1248417"
  uid: 2a4cd879-fc92-4ad2-a546-e6357530bc5c
type: Opaque

🍀 然后对上面的 my-nginx 应用创建一个具有 Basic Auth 的 Ingress 对象:

[root@master1 basic-auth]#vim ingress-with-auth.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-with-auth #1.修改下ingress名称
  namespace: default
  annotations: #2.添加下如下信息
    nginx.ingress.kubernetes.io/auth-type: basic  # 认证类型
    nginx.ingress.kubernetes.io/auth-secret: basic-auth  # 包含 user/password 定义的 secret 对象名
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'  # 要显示的带有适当上下文的消息,说明需要身份验证的原因
spec: 
  ingressClassName: nginx  # 使用 nginx 的 IngressClass(关联的 ingress-nginx 控制器)
  rules:
  - host: bauth.qikqiak.com  #3.修改下域名,将域名映射到 my-nginx 服务
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:  # 将所有请求发送到 my-nginx 服务的 80 端口
            name: my-nginx
            port:
              number: 80

🍀 直接创建上面的资源对象,然后通过下面的命令或者在浏览器中直接打开配置的域名进行测试

你如果放到外网环境上,起码一个简答的认证要加上的;

[root@master1 basic-auth]#kubectl apply -f ingress-with-auth.yaml 
ingress.networking.k8s.io/ingress-with-auth created
[root@master1 basic-auth]#kubectl get ingress
NAME                CLASS   HOSTS                ADDRESS       PORTS   AGE
ingress-with-auth   nginx   bauth.qikqiak.com    172.29.9.51   80      69s
my-nginx            nginx   ngdemo.qikqiak.com   172.29.9.51   80      26h
[root@master1 basic-auth]#curl -v http://172.29.9.51 -H 'Host: bauth.qikqiak.com'
* About to connect() to 172.29.9.51 port 80 (#0)
*   Trying 172.29.9.51...
* Connected to 172.29.9.51 (172.29.9.51) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host: bauth.qikqiak.com
>
< HTTP/1.1 401 Unauthorized
< Date: Mon, 03 Jan 2022 11:27:00 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Authentication Required - foo"
<
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host 172.29.9.51 left intact

我们可以看到出现了 401 认证失败错误,然后带上我们配置的用户名和密码进行认证:

[root@master1 basic-auth]#curl -v http://172.29.9.51 -H 'Host: bauth.qikqiak.com' -u 'foo:foo'
* About to connect() to 172.29.9.51 port 80 (#0)
*   Trying 172.29.9.51...
* Connected to 172.29.9.51 (172.29.9.51) port 80 (#0)
* Server auth using Basic with user 'foo'
> GET / HTTP/1.1
> Authorization: Basic Zm9vOmZvbw==
> User-Agent: curl/7.29.0
> Accept: */*
> Host: bauth.qikqiak.com
>
< HTTP/1.1 200 OK
< Date: Mon, 03 Jan 2022 11:28:18 GMT
< Content-Type: text/html
< Content-Length: 615
< Connection: keep-alive
< Last-Modified: Tue, 28 Dec 2021 15:28:38 GMT
< ETag: "61cb2d26-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host 172.29.9.51 left intact
[root@master1 basic-auth]#

可以看到已经认证成功了。

或者我们可以在自己pc配置下域名解析,然后在自己浏览器上进行验证:

#pc笔记本hosts文件:域名解析
#C:\WINDOWS\System32\drivers\etc
172.29.9.51 ngdemo.qikqiak.com	bauth.qikqiak.com

image-20220103193004714

image-20220103193015667

测试结束。

📍 演示2:使用外部的 Basic Auth 认证信息

🍀 除了可以使用我们自己在本地集群创建的 Auth 信息之外,还可以使用外部的 Basic Auth 认证信息,比如我们使用 https://httpbin.org (国外网站,打开贼慢😓)的外部 Basic Auth 认证,创建如下所示的 Ingress 资源对象:

image-20220103193210529

[root@master1 basic-auth]#vim external-auth.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations: #1.修改点1
    # 配置外部认证服务地址 
    nginx.ingress.kubernetes.io/auth-url: https://httpbin.org/basic-auth/user/passwd
  name: external-auth #修改点2:修改ingress name
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: external-bauth.qikqiak.com #修改点3:配置应用域名
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-nginx
            port:
              number: 80

🍀 上面的资源对象创建完成后,再进行简单的测试:

[root@master1 basic-auth]#kubectl apply -f external-auth.yaml 
ingress.networking.k8s.io/external-auth created
[root@master1 basic-auth]#kubectl get ingress
NAME                CLASS   HOSTS                        ADDRESS       PORTS   AGE
external-auth       nginx   external-bauth.qikqiak.com   172.29.9.51   80      52s
ingress-with-auth   nginx   bauth.qikqiak.com            172.29.9.51   80      10m
my-nginx            nginx   ngdemo.qikqiak.com           172.29.9.51   80      26h

[root@master1 basic-auth]#curl -k http://172.29.9.51 -v -H 'Host: external-bauth.qikqiak.com'
* About to connect() to 172.29.9.51 port 80 (#0)
*   Trying 172.29.9.51...
* Connected to 172.29.9.51 (172.29.9.51) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host: external-bauth.qikqiak.com
>
< HTTP/1.1 401 Unauthorized
< Date: Mon, 03 Jan 2022 11:36:07 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Fake Realm"
<
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host 172.29.9.51 left intact

然后使用正确的用户名和密码测试:

[root@master1 basic-auth]#curl -k http://172.29.9.51 -v -H 'Host: external-bauth.qikqiak.com' -u 'user:passwd'
* About to connect() to 172.29.9.51 port 80 (#0)
*   Trying 172.29.9.51...
* Connected to 172.29.9.51 (172.29.9.51) port 80 (#0)
* Server auth using Basic with user 'user'
> GET / HTTP/1.1
> Authorization: Basic dXNlcjpwYXNzd2Q=
> User-Agent: curl/7.29.0
> Accept: */*
> Host: external-bauth.qikqiak.com
>
< HTTP/1.1 200 OK
< Date: Mon, 03 Jan 2022 11:36:44 GMT
< Content-Type: text/html
< Content-Length: 615
< Connection: keep-alive
< Last-Modified: Tue, 28 Dec 2021 15:28:38 GMT
< ETag: "61cb2d26-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host 172.29.9.51 left intact
[root@master1 basic-auth]#

如果用户名或者密码错误则同样会出现401的状态码:

curl -k http://192.168.31.31 -v -H 'Host: external-bauth.qikqiak.com' -u 'user:passwd123'
*   Trying 192.168.31.31...
* TCP_NODELAY set
* Connected to 192.168.31.31 (192.168.31.31) port 80 (#0)
* Server auth using Basic with user 'user'
> GET / HTTP/1.1
> Host: external-bauth.qikqiak.com
> Authorization: Basic dXNlcjpwYXNzd2QxMjM=
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Thu, 16 Dec 2021 10:59:18 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
* Authentication problem. Ignoring this.
< WWW-Authenticate: Basic realm="Fake Realm"
<
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host 192.168.31.31 left intact
* Closing connection 0
#pc笔记本hosts文件:域名解析
#C:\WINDOWS\System32\drivers\etc
172.29.9.51 ngdemo.qikqiak.com	bauth.qikqiak.com	external-bauth.qikqiak.com 

image-20220103193752940

image-20220103193805668

测试结束。

当然除了 Basic Auth 这一种简单的认证方式之外,ingress-nginx 还支持一些其他高级的认证,比如我们可以使用 GitHub OAuth😘 来认证 Kubernetes 的 Dashboard。

2.URL Rewrite(可能平常用的最多的是这个功能)

ingress-nginx 很多高级的用法可以通过 Ingress 对象的 annotation 进行配置,比如常用的 URL Rewrite 功能。很多时候我们会将 ingress-nginx 当成网关使用,比如对访问的服务加上 /app 这样的前缀,在 nginx 的配置里面我们知道有一个 proxy_pass 指令可以实现:

location /app/ {
  proxy_pass http://127.0.0.1/remote/;
}

可能要加上/app,或者/gateway,/api,特别是在我们微服务里,我们很多时候要把我们的微服务提供的一些接口给它聚合在某一个子路径下面去,比如果/api.或者/api/v1下面,当然这些功能我们可以直接在网关层ingress这里实现这样的功能。

proxy_pass 后面加了 /remote 这个路径,此时会将匹配到该规则路径中的 /app/remote 替换掉,相当于截掉路径中的 /app。同样的在 Kubernetes 中使用 ingress-nginx 又该如何来实现呢?我们可以使用 rewrite-target 的注解来实现这个需求。比如现在我们想要通过 rewrite.qikqiak.com/gateway/ 来访问到 Nginx 服务,则我们需要对访问的 URL 路径做一个 Rewrite,在 PATH 中添加一个 gateway 的前缀,关于 Rewrite 的操作在 ingress-nginx 官方文档中也给出对应的说明:

ingress nginx rewrite

当前http://ngdemo.qikqiak.com/现象:

image-20220103204230505

当前http://ngdemo.qikqiak.com/gateway现象:

image-20220103204249365

📍 演示1:注解 rewrite-target

🍀 按照要求我们需要在 path 中匹配前缀 gateway,然后通过 rewrite-target 指定目标,Ingress 对象如下所示:

[root@master1 basic-auth]#vim rewrite.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: rewrite.qikqiak.com
    http:
      paths:
      - path: /gateway(/|$)(.*) #(/|$)代表可能/gateway 或者/gateway/
        pathType: Prefix
        backend:
          service:
            name: my-nginx
            port:
              number: 80

🍀 在本地pc配置下域名解析并部署:

#pc笔记本hosts文件:域名解析
#C:\WINDOWS\System32\drivers\etc
172.29.9.51 rewrite.qikqiak.com ngdemo.qikqiak.com	bauth.qikqiak.com	external-bauth.qikqiak.com 	

[root@master1 basic-auth]#kubectl apply -f rewrite.yaml 
ingress.networking.k8s.io/rewrite created
[root@master1 basic-auth]#kubectl get ingress
NAME                CLASS   HOSTS                        ADDRESS       PORTS   AGE
external-auth       nginx   external-bauth.qikqiak.com   172.29.9.51   80      108m
ingress-with-auth   nginx   bauth.qikqiak.com            172.29.9.51   80      118m
my-nginx            nginx   ngdemo.qikqiak.com           172.29.9.51   80      28h
rewrite             nginx   rewrite.qikqiak.com                        80      7s

🍀 更新后,我们可以预见到直接访问域名肯定是不行了,因为我们没有匹配 / 的 path 路径:

image-20220103212936098

但是我们带上 gateway 的前缀再去访问:

image-20220103213208794

image-20220103213226345

我们可以看到已经可以访问到了,这是因为我们在 path 中通过正则表达式 /gateway(/|$)(.*) 将匹配的路径设置成了 rewrite-target 的目标路径了,所以我们访问 rewite.qikqiak.com/gateway/ 的时候实际上相当于访问的就是后端/了。😘

测试完成。

📍 演示2:注解 app-root(解决我们访问主域名出现 404 的问题)

要解决我们访问主域名出现 404 的问题,我们可以给应用设置一个 app-root 的注解,这样当我们访问主域名的时候会自动跳转到我们指定的 app-root 目录下面,如下所示:

这里在rewrite.yaml配置文件上进行更改:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite
  annotations:
    nginx.ingress.kubernetes.io/app-root: /gateway/ #修改点1:添加app-root注解
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: rewrite.qikqiak.com
    http:
      paths:
      - path: /gateway(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: my-nginx
            port:
              number: 80

这个时候我们更新应用后访问主域名 rewrite.qikqiak.com 就会自动跳转到 rewrite.qikqiak.com/gateway/ 路径下面去了。

[root@master1 basic-auth]#kubectl apply -f rewrite.yaml 
ingress.networking.k8s.io/rewrite configured
[root@master1 basic-auth]#kubectl get ingress
NAME                CLASS   HOSTS                        ADDRESS       PORTS   AGE
external-auth       nginx   external-bauth.qikqiak.com   172.29.9.51   80      11h
ingress-with-auth   nginx   bauth.qikqiak.com            172.29.9.51   80      12h
my-nginx            nginx   ngdemo.qikqiak.com           172.29.9.51   80      38h
rewrite             nginx   rewrite.qikqiak.com          172.29.9.51   80      10h

image-20220104073654243

测试结束。

📍 演示3:注解 configuration-snippet(希望我们的应用在最后添加一个 / 这样的 slash)

但是还有一个问题是我们的 path 路径其实也匹配了 /app 这样的路径,可能我们更加希望我们的应用在最后添加一个 / 这样的 slash,同样我们可以通过 configuration-snippet 配置来完成,如下 Ingress 对象:

#这里继续在rewrite.yaml文件上进行修改:
#[root@master1 basic-auth]#vim rewrite.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rewrite
  annotations:
    nginx.ingress.kubernetes.io/app-root: /gateway/
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/configuration-snippet: | #修改点1:
      rewrite ^(/gateway)$ $1/ redirect;
spec:
  ingressClassName: nginx
  rules:
  - host: rewrite.qikqiak.com
    http:
      paths:
      - path: /gateway(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: my-nginx
            port:
              number: 80

更新后我们的应用就都会以 / 这样的 slash 结尾了。这样就完成了我们的需求,如果你原本对 nginx 的配置就非常熟悉的话应该可以很快就能理解这种配置方式了。(可以我不懂nginx😥😥)

[root@master1 basic-auth]#kubectl apply -f rewrite.yaml 
ingress.networking.k8s.io/rewrite configured
[root@master1 basic-auth]#kubectl get ingress
NAME                CLASS   HOSTS                        ADDRESS       PORTS   AGE
external-auth       nginx   external-bauth.qikqiak.com   172.29.9.51   80      12h
ingress-with-auth   nginx   bauth.qikqiak.com            172.29.9.51   80      12h
my-nginx            nginx   ngdemo.qikqiak.com           172.29.9.51   80      38h
rewrite             nginx   rewrite.qikqiak.com          172.29.9.51   80      10h
[root@master1 basic-auth]#

在本地pc浏览器输入:rewrite.qikqiak.com/gateway进行测试,发现都会被后缀加上一个/来着的:

image-20220104074405423

🍀 这边我们再来看下ingress-controllerpod里面的nginx.conf配置文件

[root@master1 basic-auth]#kubectl exec ingress-nginx-controller-hq8cq -ningress-nginx -- cat /etc/nginx/nginx.conf

image-20220104191849808

至此,实验结束。😘

3.灰度发布

个人第一次接触灰度发布,感觉有些难度啊;😥😥

在日常工作中我们经常需要对服务进行版本更新升级,所以我们经常会使用到滚动升级、蓝绿发布、灰度发布等不同的发布操作。而 ingress-nginx 支持通过 Annotations 配置来实现不同场景下的灰度发布和测试,可以满足金丝雀发布、蓝绿部署与 A/B 测试等业务场景。

ingress-nginx 的 Annotations 支持以下 4 种 Canary(金丝雀) 规则:

阳总say:我不知道我们线上会不会真正用这个ingress-nginx的annotations去做一个灰度,毕竟anotations它的这个配置方式其实不是特别云原生。一般来说,如果可以把他翻译成我们的CRD之类的,可能我们写起来就会方便一点的,目前来说,它是通过我们这个annotations方式来配置的。

  • nginx.ingress.kubernetes.io/canary-by-header:基于 Request Header 的流量切分,适用于灰度发布以及 A/B 测试。当 Request Header 设置为 always 时,请求将会被一直发送到 Canary 版本;当 Request Header 设置为 never 时,请求不会被发送到 Canary 入口;对于任何其他 Header 值,将忽略 Header,并通过优先级将请求与其他金丝雀规则进行优先级的比较。
  • nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务。当 Request Header 设置为此值时,它将被路由到 Canary 入口。该规则允许用户自定义 Request Header 的值,必须与上一个 annotation (canary-by-header) 一起使用。
  • nginx.ingress.kubernetes.io/canary-weight:基于服务权重的流量切分,适用于蓝绿部署,权重范围 0 - 100 按百分比将请求路由到 Canary Ingress 中指定的服务。权重为 0 意味着该金丝雀规则不会向 Canary 入口的服务发送任何请求,权重为 100 意味着所有请求都将被发送到 Canary 入口。
  • nginx.ingress.kubernetes.io/canary-by-cookie:基于 cookie 的流量切分,适用于灰度发布与 A/B 测试。用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务的cookie。当 cookie 值设置为 always 时,它将被路由到 Canary 入口;当 cookie 值设置为 never 时,请求不会被发送到 Canary 入口;对于任何其他值,将忽略 cookie 并将请求与其他金丝雀规则进行优先级的比较。

⚠️ 需要注意的是金丝雀规则按优先顺序进行排序:canary-by-header > canary-by-cookie > canary-weight

总的来说可以把以上的四个 annotation 规则划分为以下两类:

  • 基于权重的 Canary 规则

    基于权重的 Canary 规则

  • 基于用户请求的 Canary 规则基于用户请求的 Canary 规则

下面我们通过一个示例应用来对灰度发布功能进行说明。

📍 演示1:灰度发布测试
第1步. 部署 Production 应用

首先创建一个 production 环境的应用资源清单:(注意自己的测试环境要保持干净!)

[root@master1 ~]#mkdir gray-test
[root@master1 ~]#cd gray-test/
[root@master1 gray-test]#vim production.yaml
# production.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: production
  labels:
    app: production
spec:
  selector:
    matchLabels:
      app: production
  template:
    metadata:
      labels:
        app: production
    spec:
      containers:
      - name: production
        image: cnych/echoserver #老师侧测试镜像
        ports:
        - containerPort: 8080
        env:
          - name: NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
  name: production
  labels:
    app: production
spec:
  ports:
  - port: 80
    targetPort: 8080
    name: http
  selector:
    app: production

🍀 然后创建一个用于 production 环境访问的 Ingress 资源对象:

[root@master1 gray-test]#vim production-ingress.yaml
# production-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: production
spec:
  ingressClassName: nginx
  rules:
  - host: echo.qikqiak.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: production
            port:
              number: 80

🍀 直接创建上面的几个资源对象:

[root@master1 gray-test]#kubectl apply -f production.yaml
deployment.apps/production created
service/production created
[root@master1 gray-test]#kubectl apply -f production-ingress.yaml
ingress.networking.k8s.io/production created
[root@master1 gray-test]#kubectl get po -l app=production
NAME                         READY   STATUS    RESTARTS   AGE
production-856d5fb99-jcbc5   1/1     Running   0          33s
[root@master1 gray-test]#kubectl get ingress
NAME                CLASS   HOSTS                        ADDRESS       PORTS   AGE
production          nginx   echo.qikqiak.com             172.29.9.51   80      38s

🍀 应用部署成功后,将域名 echo.qikqiak.com 映射到 master1 节点(ingress-nginx 所在的节点)的 IP即可正常访问应用:

这里记得在自己本地pc的hosts里面做下域名解析:
C:\WINDOWS\System32\drivers\etc
172.29.9.51 echo.qikqiak.com 

或者在master1节点上配置:
[root@master1 gray-test]#vim /etc/hosts
172.29.9.51 echo.qikqiak.com
[root@master1 gray-test]#curl echo.qikqiak.com


Hostname: production-856d5fb99-jcbc5

Pod Information:
        node name:      node2
        pod name:       production-856d5fb99-jcbc5
        pod namespace:  default
        pod IP: 10.244.2.234

Server values:
        server_version=nginx: 1.13.3 - lua: 10008

Request Information:
        client_address=10.244.0.0
        method=GET
        real path=/
        query=
        request_version=1.1
        request_scheme=http
        request_uri=http://echo.qikqiak.com:8080/

Request Headers:
        accept=*/*
        host=echo.qikqiak.com
        user-agent=curl/7.29.0
        x-forwarded-for=172.29.9.51
        x-forwarded-host=echo.qikqiak.com
        x-forwarded-port=80
        x-forwarded-proto=http
        x-forwarded-scheme=http
        x-real-ip=172.29.9.51
        x-request-id=f8bf93426535b202f25c7ff15608a1ab
        x-scheme=http

Request Body:
        -no body in request-

[root@master1 gray-test]#
第2步. 创建 Canary 版本

参考将上述 Production 版本的 production.yaml 文件,再创建一个 Canary 版本的应用。

[root@master1 gray-test]#vim canary.yaml
# canary.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: canary
  labels:
    app: canary
spec:
  selector:
    matchLabels:
      app: canary
  template:
    metadata:
      labels:
        app: canary
    spec:
      containers:
      - name: canary
        image: cnych/echoserver
        ports:
        - containerPort: 8080
        env:
          - name: NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
          - name: POD_IP
            valueFrom:
              fieldRef:
                fieldPath: status.podIP
---
apiVersion: v1
kind: Service
metadata:
  name: canary
  labels:
    app: canary
spec:
  ports:
  - port: 80
    targetPort: 8080
    name: http
  selector:
    app: canary

接下来就可以通过配置 Annotation 规则进行流量切分了。

第3步. Annotation 规则配置
1.基于权重

基于权重的流量切分的典型应用场景就是蓝绿部署,可通过将权重设置为 0 或 100 来实现。例如,可将 Green 版本设置为主要部分,并将 Blue 版本的入口配置为 Canary。最初,将权重设置为 0,因此不会将流量代理到 Blue 版本。一旦新版本测试和验证都成功后,即可将 Blue 版本的权重设置为 100,即所有流量从 Green 版本转向 Blue。

🍀 创建一个基于权重的 Canary 版本的应用路由 Ingress 对象。

[root@master1 gray-test]#vim canary-ingress.yaml
# canary-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"   # 要开启灰度发布机制,首先需要启用 Canary
    nginx.ingress.kubernetes.io/canary-weight: "30"  # 分配30%流量到当前Canary版本
spec:
  ingressClassName: nginx
  rules:
  - host: echo.qikqiak.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: canary
            port:
              number: 80

🍀 直接创建上面的资源对象即可:

[root@master1 gray-test]#kubectl apply -f canary.yaml 
deployment.apps/canary created
service/canary created
[root@master1 gray-test]#kubectl apply -f canary-ingress.yaml 
ingress.networking.k8s.io/canary created
[root@master1 gray-test]#kubectl get po
NAME                         READY   STATUS    RESTARTS   AGE
canary-66cb497b7f-7gl9x      1/1     Running   0          16s
production-856d5fb99-jcbc5   1/1     Running   0          14m
[root@master1 gray-test]#kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
canary       ClusterIP   10.107.120.213   <none>        80/TCP    20s
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP   66d
production   ClusterIP   10.105.176.74    <none>        80/TCP    14m
[root@master1 gray-test]#kubectl get ingress
NAME         CLASS   HOSTS              ADDRESS       PORTS   AGE
canary       nginx   echo.qikqiak.com   172.29.9.51   80      27s
production   nginx   echo.qikqiak.com   172.29.9.51   80      14m

🍀 Canary 版本应用创建成功后,接下来我们在命令行终端中来不断访问这个应用,观察 Hostname 变化:

$ for i in $(seq 1 10); do curl -s echo.qikqiak.com | grep "Hostname"; done
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: production-856d5fb99-jcbc5
Hostname: canary-66cb497b7f-7gl9x

由于我们给 Canary 版本应用分配了 30% 左右权重的流量,所以上面我们访问10次有3次访问到了 Canary 版本的应用,符合我们的预期。

2.基于 Request Header

基于 Request Header 进行流量切分的典型应用场景即灰度发布或 A/B 测试场景

在上面的 Canary 版本的 Ingress 对象中新增一条 annotation 配置 nginx.ingress.kubernetes.io/canary-by-header: canary(这里的 value 可以是任意值),使当前的 Ingress 实现基于 Request Header 进行流量切分,由于 canary-by-header 的优先级大于 canary-weight,所以会忽略原有的 canary-weight 的规则。

[root@master1 gray-test]#vim canary-ingress.yaml
……
annotations:
  nginx.ingress.kubernetes.io/canary: "true"   # 要开启灰度发布机制,首先需要启用 Canary
  nginx.ingress.kubernetes.io/canary-by-header: canary  # 基于header的流量切分
  nginx.ingress.kubernetes.io/canary-weight: "30"  # 会被忽略,因为配置了 canary-by-headerCanary版本
  ……

更新上面的 Ingress 资源对象后,我们在请求中加入不同的 Header 值,再次访问应用的域名。

注意:当 Request Header 设置为 never 或 always 时,请求将不会或一直被发送到 Canary 版本,对于任何其他 Header 值,将忽略 Header,并通过优先级将请求与其他 Canary 规则进行优先级的比较。

[root@master1 gray-test]#kubectl apply -f canary-ingress.yaml 
ingress.networking.k8s.io/canary configured
➜ for i in $(seq 1 10); do curl -s -H "canary: never" echo.qikqiak.com | grep "Hostname"; done
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5

这里我们在请求的时候设置了 canary: never 这个 Header 值,所以请求没有发送到 Canary 应用中去。如果设置为其他值呢:

for i in $(seq 1 10); do curl -s -H "canary: other-value" echo.qikqiak.com | grep "Hostname"; done
Hostname: production-856d5fb99-jcbc5
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: production-856d5fb99-jcbc5
Hostname: canary-66cb497b7f-7gl9x

由于我们请求设置的 Header 值为 canary: other-value,所以 ingress-nginx 会通过优先级将请求与其他 Canary 规则进行优先级的比较,我们这里也就会进入 canary-weight: "30" 这个规则去。

这个时候我们可以在上一个 annotation (即 canary-by-header)的基础上添加一条 nginx.ingress.kubernetes.io/canary-by-header-value: user-value 这样的规则,就可以将请求路由到 Canary Ingress 中指定的服务了。

[root@master1 gray-test]#vim canary-ingress.yaml
……
annotations:
  nginx.ingress.kubernetes.io/canary: "true"   # 要开启灰度发布机制,首先需要启用 Canary
  nginx.ingress.kubernetes.io/canary-by-header-value: user-value
  nginx.ingress.kubernetes.io/canary-by-header: canary  # 基于header的流量切分
  nginx.ingress.kubernetes.io/canary-weight: "30"  # 分配30%流量到当前Canary版本
……

同样更新 Ingress 对象后,重新访问应用,当 Request Header 满足 canary: user-value时,所有请求就会被路由到 Canary 版本:

[root@master1 gray-test]#kubectl apply -f canary-ingress.yaml 
ingress.networking.k8s.io/canary configured
➜ for i in $(seq 1 10); do curl -s -H "canary: user-value" echo.qikqiak.com | grep "Hostname"; done
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
3.基于 Cookie

与基于 Request Header 的 annotation 用法规则类似。例如在 A/B 测试场景下,需要让地域为北京的用户访问 Canary 版本。那么当 cookie 的 annotation 设置为 nginx.ingress.kubernetes.io/canary-by-cookie: "users_from_Beijing",此时后台可对登录的用户请求进行检查,如果该用户访问源来自北京则设置 cookie users_from_Beijing 的值为 always,这样就可以确保北京的用户仅访问 Canary 版本。

同样我们更新 Canary 版本的 Ingress 资源对象,采用基于 Cookie 来进行流量切分,

[root@master1 gray-test]#vim canary-ingress.yaml
……
annotations:
  nginx.ingress.kubernetes.io/canary: "true"   # 要开启灰度发布机制,首先需要启用 Canary
  nginx.ingress.kubernetes.io/canary-by-cookie: "users_from_Beijing"  # 基于 cookie
  nginx.ingress.kubernetes.io/canary-weight: "30"  # 会被忽略,因为配置了 canary-by-cookie
……  

更新上面的 Ingress 资源对象后,我们在请求中设置一个 users_from_Beijing=always 的 Cookie 值,再次访问应用的域名。

[root@master1 gray-test]#kubectl apply -f canary-ingress.yaml
ingress.networking.k8s.io/canary configured
➜ for i in $(seq 1 10); do curl -s -b "users_from_Beijing=always" echo.qikqiak.com | grep "Hostname"; done
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x
Hostname: canary-66cb497b7f-7gl9x

我们可以看到应用都被路由到了 Canary 版本的应用中去了,如果我们将这个 Cookie 值设置为 never,则不会路由到 Canary 应用中。

测试结束。😘

关于我

我的博客主旨:我希望每一个人拿着我的博客都可以做出实验现象,先把实验做出来,然后再结合理论知识更深层次去理解技术点,这样学习起来才有乐趣和动力。并且,我的博客内容步骤是很完整的,也分享源码和实验用到的软件,希望能和大家一起共同进步!

各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人免费帮您解决问题:

  1. 个人微信二维码:x2675263825 (舍得), qq:2675263825。

    image-20211002091450217

  2. 个人博客地址:www.onlyonexl.cn

    image-20211002092057988

  3. 个人微信公众号:云原生架构师实战

    image-20211002141739664

  4. 个人github

    https://github.com/OnlyOnexl

    image-20220105203338290

  5. 个人csdn

    https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

    image-20211002092344616

最后

​ 好了,关于ingress-nginx 高级配置1实验就到这里了,感谢大家阅读,最后贴上我女神的photo,祝大家生活快乐,每天都过的有意义哦,我们下期见!

image-20220105203522079

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值