Kubernetes Ingress策略&常用配置

Kubernetes / 2023-02-06

初始Ingress策略

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: <ingress-name>
spec:
  rules:
  - host: <your.domain>
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: <servicename>
            port:
              number: 80
  ingressClassName: nginx

基于annotations灰度发布策略

示例(根据权重转发):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: <ingress-name>
spec:
  rules:
  - host: <your.domain>
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: <old_servicename>
            port:
              number: 80
  ingressClassName: nginx

---

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: <ingress-name>
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "30"		# 30%权重
spec:
  rules:
  - host: <your.domain>
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: <new_servicename>
            port:
              number: 80
  ingressClassName: nginx

nginx.ingress.kubernetes.io/canary-weight

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

for i in $(seq 1 10); do curl http://<your.domain>; echo '\n'; done
hello world-version1
hello world-version1
hello world-version2
hello world-version2
hello world-version1
hello world-version1
hello world-version1
hello world-version1
hello world-version1
hello world-version1

一、根据请求头转发

annotations:
  kubernetes.io/ingress.class: nginx
  nginx.ingress.kubernetes.io/canary: "true"
  nginx.ingress.kubernetes.io/canary-by-header: "test"
  • nginx.ingress.kubernetes.io/canary-by-header

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

    测试结果如下:

    for i in $(seq 1 5); do curl http://<your.domain>; echo '\n'; done
    hello world-version1
    hello world-version1
    hello world-version1
    hello world-version1
    hello world-version1
    for i in $(seq 1 5); do curl -H 'test:always' http://<your.domain>; echo '\n'; done
    hello world-version2
    hello world-version2
    hello world-version2
    hello world-version2
    hello world-version2
    

二、根据特定的请求头和值转发

annotations:
  kubernetes.io/ingress.class: nginx
  nginx.ingress.kubernetes.io/canary: "true"
  nginx.ingress.kubernetes.io/canary-by-header: "test"
  nginx.ingress.kubernetes.io/canary-by-header-value: "abc"
  • nginx.ingress.kubernetes.io/canary-by-header-value

    要匹配的 Request Header 的值,用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务。当 Request Header 设置为此值时,它将被路由到 Canary 入口。

    测试结果如下:

    for i in $(seq 1 5); do curl -H 'test:always' http://<your.domain>; echo '\n'; done
    hello world-version1
    hello world-version1
    hello world-version1
    hello world-version1
    hello world-version1
    for i in $(seq 1 5); do curl -H 'test:abc' http://<your.domain>; echo '\n'; done
    hello world-version2
    hello world-version2
    hello world-version2
    hello world-version2
    hello world-version2
    
annotations:
  kubernetes.io/ingress.class: nginx
  nginx.ingress.kubernetes.io/canary: "true"
  nginx.ingress.kubernetes.io/canary-by-cookie: "like_music"
  • nginx.ingress.kubernetes.io/canary-by-cookie

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

    测试结果如下:

    for i in $(seq 1 5); do curl -b 'like_music=1' http://<your.domain>; echo '\n'; done
    hello world-version1
    hello world-version1
    hello world-version1
    hello world-version1
    hello world-version1
    for i in $(seq 1 5); do curl -b 'like_music=always' http://<your.domain>; echo '\n'; done
    hello world-version2
    hello world-version2
    hello world-version2
    hello world-version2
    hello world-version2
    

常用ingress配置

access log

  annotations:
     nginx.ingress.kubernetes.io/enable-access-log: "false"

nginx ingress 默认是开启access log的,如果你想关闭,可以通过将 nginx.ingress.kubernetes.io/enable-access-log 设置成false。

默认服务

  annotations:
     nginx.ingress.kubernetes.io/default-backend: 

当客户端请求一个不存在的path的时候,我们不希望返回 404 ,跳转到一个默认的服务上。

允许最大body

  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: 8m

这个主要是针对外部请求,防止将流量打满,proxy-body-size 设置最大请求 body,如果超过则会返回 413 请求错误。

限流

  annotations:
    nginx.ingress.kubernetes.io/limit-rps: "5"
    nginx.ingress.kubernetes.io/limit-rpm: "300"
    nginx.ingress.kubernetes.io/limit-connections: "10"

通过 rps 限制每秒请求数,rpm 限制每分钟请求数,connections限制连接数。

跨域访问

  annotations:
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS"
    nginx.ingress.kubernetes.io/cors-allow-headers: "X-Forwarded-For, X-app123-XPTO"
    nginx.ingress.kubernetes.io/cors-expose-headers: "*, X-CustomResponseHeader"
    nginx.ingress.kubernetes.io/cors-max-age: 600
    nginx.ingress.kubernetes.io/cors-allow-credentials: "false"

我们经常将nginx作为api的网关,支持跨域必不可少。通过 nginx.ingress.kubernetes.io/cors-allow-methods 设置支持跨域请求的方法。

请求超时

  annotations:
    nginx.org/proxy-connect-timeout: "30s"
    nginx.org/proxy-read-timeout: "20s"

nginx.org/proxy-connect-timeout 和nginx.org/proxy-read-timeout 这两个参数分别设置nginx的建立连接以及等待结果的超时时间。

强制https

  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/preserve-trailing-slash: "true"

nginx.ingress.kubernetes.io/force-ssl-redirect: "true"通过这个annotation可以强制 https,如果是http请求,会通过 301 redirect到 https

https

apiVersion: v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "false"   # 禁用https强制跳转
  generation: 1
  name: test-tls
  namespace: ratel-test1
spec:
  rules:
  - host: test-tls.test.com
    http:
      paths:
      - backend:
          serviceName: ingress-test
          servicePort: 80
        path: /
  tls:
  - hosts:
    - test-tls.test.com
    secretName: ca-cert
设置默认证书:--default-ssl-certificate=default/foo-tls
更改的ingress-controller的启动参数

Redirect

apiVersion: v1
items:
- apiVersion: extensions/v1beta1
  kind: Ingress
  metadata:
    annotations:
      nginx.ingress.kubernetes.io/permanent-redirect: https://www.baidu.com   # 重定向到想去的url
    name: ingress-test
    namespace: ratel-test1
  spec:
    rules:
    - host: ingress.test.com
      http:
        paths:
        - backend:
            serviceName: ingress-test
            servicePort: 80
          path: /
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

Rewrite

apiVersion: v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
  generation: 4
  name: ingress-test
  namespace: ratel-test1
spec:
  rules:
  - host: rewrite.test.com
    http:
      paths:
      - backend:
          serviceName: ingress-test
          servicePort: 80
        path: /something(/|$)(.*)   #($1)($2)

黑白名单

黑名单:拒绝某段IP访问

**白名单:**只允许某段IP访问

Annotations:只对指定的ingress生效

ConfigMap:全局生效

黑名单可以使用ConfigMap去配置,白名单建议使用Annotations去配置

白名单配置(建议使用Annotations)

annotations:
  nginx.ingress.kubernetes.io/whitelist-source-range 10.0.0.0/24,172.10.0.1

黑名单设置(建议使用ConfigMap)(全局生效)

kubectl edit  cm -n ingress-nginx ingress-nginx-controller -oyaml

---
apiVersion: v1
data:                          # 加上data
  block-cidrs: 192.168.1.201   # 黑名单
kind: ConfigMap
metadata:
  annotations:
    meta.helm.sh/release-name: ingress-nginx
    meta.helm.sh/release-namespace: ingress-nginx
...

kubectl delete po -n ingress-nginx --all

针对某个域名设置黑名单–snippet

# 比如ingress代理了www.test.com这个域名,那么想针对这个域名(www.test.com)设置访问黑名单,就编辑这个ingress即可
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  # 在annotations下面加上这几行配置,有多个IP可以deny多个
  annotations:
    nginx.ingress.kubernetes.io/server-snippet: |-    
      deny 192.168.1.101;
      deny 192.168.1.102;
      allow all;
# 然后在deny的主机上访问 www.test.com 就403
[root@k8s-master02 ~]# curl ngdemo.qikqiak.com
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>

匹配请求头设置

annotations:
    nginx.ingress.kubernetes.io/server-snippet: |
        set $agentflag 0;

        if ($http_user_agent ~* "(iPhone)" ){   # 匹配规则设置(设置匹配为iPhone的手机,则重定向到下面的url)
          set $agentflag 1;
        }

        if ( $agentflag = 1 ) {
          return 301 https://www.baidu.com;     # 重定向到指定的url
        }

ingress-nginx基本认证(Basic Auth)

在 Ingress Controller 上面配置基本的 Auth 认证,如 Basic Auth,用 htpasswd 生成一个密码文件来验证身份验证。

[root@k8s-master01 ~]# htpasswd -c auth foo   # 账号foo   密码 123456   
New password:              # 123456
Re-type new password:      # 123456
Adding password for user foo

# 生成一个auth文件

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

kubectl create secret generic basic-auth --from-file=auth

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

  annotations:
    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' # 提示文本

不带用户名和密码进行认证:

➜ curl -v http://k8s.qikqiak.com -H 'Host: foo.bar.com'
...
< HTTP/1.1 401 Unauthorized
< Server: openresty/1.15.8.2
< Date: Sun, 08 Dec 2019 06:44:35 GMT
< Content-Type: text/html
< Content-Length: 185
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Authentication Required"
...

带上用户名和密码进行认证:

➜ curl -v http://k8s.qikqiak.com -H 'Host: foo.bar.com' -u 'foo:foo'
...
< HTTP/1.1 200 OK
< Server: openresty/1.15.8.2
< Date: Sun, 08 Dec 2019 06:46:27 GMT
< Content-Type: text/html
< Content-Length: 612
< Connection: keep-alive
< Vary: Accept-Encoding
< Last-Modified: Tue, 19 Nov 2019 12:50:08 GMT
< ETag: "5dd3e500-264"
< Accept-Ranges: bytes
...