Drollery Medieval drollery of a knight on a horse

🏆 欢迎来到本站: https://xuchangwei.com/希望这里有你感兴趣的内容

flowery border with man falling
flowery border with man falling

Kubernetes: k8s 运维篇-服务发布Ingress进阶

k8s 运维篇-服务发布Ingress进阶

最常用的配置

  • 通过域名发布服务
  • 前后端分离
  • 区分手机端和 pc 端

ingress 服务发布架构

  • 传统架构
    • 浏览器访问 baidu.com
    • dns 解析:解析成1.1.1.1
    • dmz: 入口网关
    • 内网负载均衡:内网负载均衡/nginx/SLB
    • 程序:web/java/go
  • kubernetes
    • 浏览器访问 baidu.com
    • dns 解析:解析成1.1.1.1
    • dmz: 入口网关
    • ingress controller: nginx/haproxy/treafik 相当于内网负载均衡
    • Service
    • Pod:web/java/go

Ingress 组成

  • Kubernetes
    • Kind: ingress 相当于传统架构 nginx.conf
    • Ingress Controller 相当于传统架构 nginx

Ingress 生产级高可用架构

Snipaste_2022-12-29_06-55-56.png

方式1 推荐

  • Pod 使用 HostNetwork 直接使用缩主机网络,不依赖云厂商

方式2:

  • Ingress Service loadbalancer 需要云厂商支持

访问3:

  • Alb 直接到业务 Pod 需要云厂商支持,不依赖 ingress

方式4:

  • Alb 到ingress controller pod 需要云厂商支持,可做内网 nginx

Ingress-nginx和Nginx-ingress的区别

ingress-nginx k8s 官方文档:https://kubernetes.github.io/ingress-nginx/deploy/

nginx-ingress nginx 官方提供 https://github.com/nginxinc/kubernetes-ingress

部署建议:

DaemonSet安装,找几台专门的服务器进行配置ingress (如果没有的足够的资源,就设置QoS,保证ingress最后删除的那个策略)

hostNetwork: true  # 这个设置为true

Ingress 安装

按需修改

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/cloud/deploy.yaml

如果按方式1修改:

  • hostNetwork 为 true
  • dnsPolicy 设置为 ClusterFirstWithHostNet
  • NodeSelector 绑定指定标签节点
  • 类型更改为 kind: DaemonSet
  • 设置 ingressClass

Ingress v1 和 v1beta1 区别

创建一个简单的ingress实例

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx-prod
    nginx.ingress.kubernetes.io/proxy-body-size: 500M
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
    nginx.ingress.kubernetes.io/server-snippet: |
        location ~* /api/actuator {
            deny all;
        }
    nginx.ingress.kubernetes.io/configuration-snippet: |
        add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains;' always;
  name: taskcenter-cms-nginx
  namespace: taskcenter
spec:
  rules:
  - host: xxl-job-prod.xx.com
    http:
      paths:
      - path: /
        #pathType: ImplementationSpecific
        pathType: Prefix
        backend:
          service:
            name: xxl-job
            port:
              number: 8080

1.12版本前

apiVersion: extensions/v1
kind: Ingress
metadata:
  name: ingress-test
  namespace: ratel-test1
spec:
  rules:
  - host: ingress.test.com
    http:
      paths:
      - backend:
          serviceName: ingress-test   # 代理名字为ingress-test
          servicePort: 80             # port为80的svc
        path: /

# 创建ingress
kubectl create -f ingress-demo.yaml

nginx ingress 多实例部署

https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/

修改:名称空间,class controller, clusterrole, validatingwehookconfiggurations 名称

设置 --controller-class=--ingress-class 不同的值

ingress_name='ingress-nginx-prod'
class_name='nginx-prod'
sed  -i.bak '[email protected]/name: [email protected]/name: '"$ingress_name"'@g' deploy.yaml
sed -i 's@namespace: ingress-nginx@namespace: '"$ingress_name"'@g' deploy.yaml
sed -i 's@--controller-class=k8s.io/ingress-nginx@--controller-class=k8s.io/'"$ingress_name"'@g' deploy.yaml
sed -i 's@--ingress-class=nginx@--ingress-class='"$class_name"'@g' deploy.yaml

IngressClass 同样的值

apiVersion: networking.k8s.io/v1
kind: IngressClass
.......
  name: nginx-prod
spec:
  controller: k8s.io/ingress-nginx-pord

同时修改以下值:

Nmaespace: name: ingress-nginx-prod

ValidatingWebhookConfiguration: name: ingress-nginx-admission-prod

sed -i 's@--webhook-name=ingress-nginx-admission@--webhook-name=ingress-nginx-admission-prod@g' deploy.yaml

ClusterRole: name: ingress-nginx-prod

ClusterRole: name: ingress-nginx-admission-prod

ClusterRoleBinding:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
......
  name: ingress-nginx-prod
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx-prod
......
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
.......
  name: ingress-nginx-admission-prod
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx-admission-prod
......

自定义:

默认aws nlb, 改成clusterip. 自定义configmap

---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx-prod
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.11.2
  name: ingress-nginx-controller
  namespace: ingress-nginx-prod
spec:
  ports:
  - appProtocol: http
    name: http
    port: 80
    protocol: TCP
    targetPort: http
  - appProtocol: https
    name: https
    port: 443
    protocol: TCP
    targetPort: https
  - name: prometheus
    port: 10254
    protocol: TCP
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx-prod
  type: ClusterIP
---
apiVersion: v1
data:
  enable-underscores-in-headers: "true"
  allow-snippet-annotations: "true"
  annotations-risk-level: Critical
  compute-full-forwarded-for: "true"
  forwarded-for-header: "X-Forwarded-For"
  use-forwarded-headers: "true"
  enable-brotli: "true"
  brotli-level: "6"
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx-prod
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.11.2
  name: ingress-nginx-controller
  namespace: ingress-nginx-prod

生成alb

cat <<\EOF> ingress-alb.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-prod
  namespace: ingress-nginx-prod
  annotations:
    #kubernetes.io/ingress.class: "alb"
    alb.ingress.kubernetes.io/group.name: "ingress-prod"
    alb.ingress.kubernetes.io/group.order: "900"
    alb.ingress.kubernetes.io/load-balancer-name: ingress-prod-alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/security-groups: sg-0ab7cbb82b63d437d, sg-0473503fd2278b0f5
    alb.ingress.kubernetes.io/subnets: subnet-06ff69c0f8b9c5c90, subnet-0dd914d53a539646e, subnet-0905c8c21ce63230b
    alb.ingress.kubernetes.io/success-codes: '200-499'
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80},{"HTTPS": 443}]'
    alb.ingress.kubernetes.io/ssl-redirect: '443'
    alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-2021-06
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:af-south-1:339712911907:certificate/f69c55bf-8602-443c-af2d-0b9a700d89b5
    alb.ingress.kubernetes.io/tags: Techteam=RC, Application=Platform, Name=ingress-prod-alb, Environment=Production, Author=jasper.xu
spec:
  ingressClassName: alb
  rules:
    - host: "*.xxx.com"
      http:
        paths:
          - pathType: ImplementationSpecific
            path: /*
            backend:
              service:
                name: ingress-nginx-controller
                port:
                  number: 80
EOF

ingress 中引用ingressClass

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  ingressClassName: nginx-prod
  ...

Ingress Redirect 新旧域名替换

ingress 配置说明:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/

  • configmap 全局生效
  • ingress 中的 annotations 对当前的 ingress 生产,可覆盖 configmap 配置。
apiVersion: v1
items:
- apiVersion: extensions/v1
  kind: Ingress
  metadata:
    annotations:
      nginx.ingress.kubernetes.io/permanent-redirect: https://www.baidu.com   # 301重定向到想去的url
    name: ingress-test
    namespace: ratel-test1
  spec:
    rules:
    - host: ingress.test.com
      http:
        paths:
        - backend:
            serviceName: ingress-test
            servicePort: 80
          path: /
# 创建ingress
kubectl edit ingress-demo.yaml

# entry 容器查看
kubectl get po -n ingress-nginx
kubectl exec -it nginx-ingress-controller-b2442 -n ingress-nginx -- bash
cat /etc/nginx/nginx.conf

修改状态码使用 nginx.ingress.kubernetes.io/permanent-redirect-code: '308'

Ingress Rewrite 实现前后端分离

使用场景:

www.baidu.com

  • / 能访问到 web 访问路径: /
  • /api-a 报404 因为 backend-a 访问路径:/api
  • /api-b 报404 因为 backend-b 访问路径:/api

将/something 重写为/:

$ echo '
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
  name: rewrite
  namespace: default
spec:
  ingressClassName: nginx
  rules:
  - host: rewrite.bar.com
    http:
      paths:
      - path: /something(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: http-svc
            port: 
              number: 80
' | kubectl create -f -

浏览器访问

http://rewrite.bar.com/something 跳转到/

注意:多个域名都需要 rewrite 可以写在同一个 ingress 文件中,反之有不需要 rewrite 的域名单独一个 ingress

Inggress 错误代码友好页面

nginx.ingress.kubernetes.io/custom-http-errors: "404,415"

Ingress 实现 https 访问

生产环境中,证书一般放在访问环节中的最外层 DMZ 网关,如 F5/slb/lvs/haproxy。

官网:https://kubernetes.github.io/ingress-nginx/user-guide/tls/

生成本地证书,生产环境,用购买的。

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ${KEY_FILE} -out ${CERT_FILE} -subj "/CN=${HOST}/O=${HOST}"

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls-key -out tls.cert -subj "/CN=test-tls.test.com/O=test-tls.test.com"

# create the secret in the cluster via
kubectl create secret tls ca-cert --key tls.key --cert tls.cert -n ratel-test1

禁用https强制跳转

nginx.ingress.kubernetes.io/ssl-redirect: "false"

echo '
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    #kubernetes.io/ingress.class: nginx-example
    nginx.ingress.kubernetes.io/ssl-redirect: "false"   # 禁用https强制跳转
  name: tls-example-ingress
spec:
  ingressClassName: nginx-example
  tls:
  - hosts:
      - https-example.foo.com
    secretName: testsecret-tls
  rules:
  - host: https-example.foo.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80
' | kubectl create -f -

1.22 版本以上有 secretName 需要加 ingressClassName ,没有则使用默认的

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
  name: nginx-example
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx
apiVersion: extensions/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的启动参数

Dashboard自定义证书

containers:
- args:
  - --auto-generate-certificates=false
  - --tls-key-file=server.key
  - --tls-cert-file=server.pem
  - --token-ttl=21600
  - --authentication-mode=basic,token
  - --namespace=kubernetes-dashboard
  image: kubernetesui/dashboard:v2.0.0-rc5
  imagePullPolicy: Always

  volumeMounts:
  - mountPath: /certs
    name: kubernetes-dashboard-new
  - mountPath: /tmp
    name: tmp-volume

volumes:
- name: kubernetes-dashboard-new
  secret:
    defaultMode: 420
    secretName: kubernetes-dashboard-new
- emptyDir: {}
  name: tmp-volume

Ingress 区分手机端和 PC 端

nginx.ingress.kubernetes.io/server-snippet: |
  set $agentflag 0;
  if ($http_user_agent ~* "(Android|iPhone|Windows|Phone|UC|Kindel)"){
    set $agentflag 1;
  }
  if ($agentflab = 1){
    return 301 http://m.test.com;
  }

Ingress 基本认证

文档:https://kubernetes.github.io/ingress-nginx/examples/auth/basic/

同样我们还可以在 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文件
[root@k8s-master01 ~]# ls auth
auth

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

[root@k8s-master01 ~]# kubectl create secret generic basic-auth --from-file=auth
secret/basic-auth created
[root@k8s-master01 ~]# kubectl get secret basic-auth -o yaml
apiVersion: v1
data:
  auth: Zm9vOiRhcHIxJGZzREw0b0xmJHNuUUNDTFkxbTE2N1BkNUdEMHIwcC8K
kind: Secret
metadata:
  name: basic-auth
  namespace: default
type: Opaque

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

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-with-auth
  annotations:
    # 认证类型
    nginx.ingress.kubernetes.io/auth-type: basic
    # 包含 user/password 定义的 secret 对象名
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    # 要显示的带有适当上下文的消息,说明需要身份验证的原因
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /
        backend:
          serviceName: my-nginx
          servicePort: 80

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

➜ curl -v http://k8s.qikqiak.com -H 'Host: foo.bar.com'
'* Rebuilt URL to: http://k8s.qikqiak.com/
'*   Trying 123.59.188.12...
'* TCP_NODELAY set
'* Connected to k8s.qikqiak.com (123.59.188.12) port 80 (#0)
> GET / HTTP/1.1
> Host: foo.bar.com
> User-Agent: curl/7.54.0
> Accept: */*
>
< 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 - foo"
<
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>openresty/1.15.8.2</center>
</body>
</html>

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

➜ curl -v http://k8s.qikqiak.com -H 'Host: foo.bar.com' -u 'foo:foo'
'* Rebuilt URL to: http://k8s.qikqiak.com/
'*   Trying 123.59.188.12...
'* TCP_NODELAY set
'* Connected to k8s.qikqiak.com (123.59.188.12) port 80 (#0)
'* Server auth using Basic with user 'foo'
> GET / HTTP/1.1
> Host: foo.bar.com
> Authorization: Basic Zm9vOmZvbw==
> User-Agent: curl/7.54.0
> Accept: */*
>
< 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
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    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>

可以看到已经认证成功了。当然出来 Basic Auth 这一种简单的认证方式之外,NGINX Ingress Controller 还支持一些其他高级的认证,比如 OAUTH 认证之类的。

Ingress 实现黑白名单

  • 黑名单:拒绝某段IP访问
  • 白名单:只允许某段IP访问
  • Annotations:只对指定的ingress生效
  • ConfigMap:全局生效

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

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

官网:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#whitelist-source-range

annotations:
  nginx.ingress.kubernetes.io/whitelist-source-range 10.0.0.0/24,172.10.0.1  # 后面可以跟一个或者多个IP

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

# 因为黑名单可能会不定时加上去,防止恶意攻击的。所以用ConfigMap(热更新)

# 1、更改ingress-nginx的cm
[root@k8s-master01 ~]#  kubectl edit  cm -n ingress-nginx ingress-nginx-controller -oyaml
apiVersion: v1
data:                          # 加上data
  block-cidrs: 192.168.1.201   # 加上block-cidrs,后面也可以跟多个IP,隔开
kind: ConfigMap
metadata:
  annotations:
    meta.helm.sh/release-name: ingress-nginx
    meta.helm.sh/release-namespace: ingress-nginx
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/version: 0.43.0
    helm.sh/chart: ingress-nginx-3.20.1
  name: ingress-nginx-controller
  namespace: ingress-nginx

# 2、删除ingress-nginx的pod,重新加载配置
[root@k8s-master01 ~]# kubectl get po -n ingress-nginx
[root@k8s-master01 ~]# kubectl delete po -n ingress-nginx --all  # 生产一个一个删,防止配置错误都挂了

# 3、在192.168.1.201节点上,访问ingress代理的域名,然后403表示配置成功

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

官网参考:https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md#canary

# 比如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>

Ingress 访问速率限制

官网参考: https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md#canary

https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#rate-limiting

nginx.ingress.kubernetes.io/limit-connections 1 # 连接数只有一个
nginx.ingress.kubernetes.io/limit-rps 1 # 每秒请求数

Ingress 实现灰度发布/金丝雀发布

准备2个svc,用于演示
kubectl expose deploy my-nginx1 --port 80
kubectl expose deploy my-nginx2 --port 8
[root@k8s-master01 app]# kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
my-nginx     ClusterIP   10.104.87.14    <none>        80/TCP    91m
my-nginx1    ClusterIP   10.103.175.67   <none>        80/TCP    2m12s
[root@k8s-master01 app]# curl 10.104.87.14
Canary v1
[root@k8s-master01 app]# curl 10.103.175.67 
Canary v2
开启基于ingress的灰度发布
官网参考:https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md#canary
# 开启了灰度发布,才能在同一个ns下创建2个同样域名
# 先创建一个普通的ingress,通过canary.test.com就可以访问到svc my-nginx,版本是v1
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: canary.test.com  # 将域名映射到 my-nginx 服务
    http:
      paths:
      - path: /
        backend:
          serviceName: my-nginx  # 将所有请求发送到 my-nginx 服务的 80 端口
          servicePort: 80     
  • 基于权重的流量调度

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

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: my-nginx1
      annotations:
        kubernetes.io/ingress.class: "nginx"
    spec:
      rules:
      - host: canary.test.com
        http:
          paths:
          - path: /
            backend:
              serviceName: my-nginx1
              servicePort: 80     
    # 这个是代理V2的ingress,如果使用相同域名,创建会报错
    [root@k8s-master01 app]# kubectl apply -f  bbb.yaml 
    Error from server (BadRequest): error when creating "bbb.yaml": admission webhook "validate.nginx.ingress.kubernetes.io" denied the request: host "canary.test.com" and path "/" is already defined in ingress default/my-nginx
    
    # 基于权重
    annotations:
      nginx.ingress.kubernetes.io/canary: "true"        # 要开启灰度发布机制,首先需要启用 Canary
      nginx.ingress.kubernetes.io/canary-weight: "30"   # 切30%的流量到v2去,设置100就全部切过去了
    
    # 创建后查看ingress,可以看到创建了2个代理相同域名的ingress
    [root@k8s-master01 app]# kubectl get ingress
    NAME        CLASS    HOSTS             ADDRESS         PORTS   AGE
    my-nginx    <none>   canary.test.com   10.101.29.125   80      20m
    my-nginx1   <none>   canary.test.com   10.101.29.125   80      3m6s
    

    验证是否配置成功:

    [root@k8s-master02 ~]# for i in $(seq 1 3); do curl -s -H canary.test.com; done
    Canary v1
    Canary v2
    Canary v1
    
    • 安装 ruby 测试

      脚本

      vim test.rb
      counts = Hash.new(0)
      
      100.times do
        output = `curl -s canary.test.com | grep 'Canary'| awk '{print $2}'| awk -F"<" '{print $1}'`
        counts[output.strip.split.last += 1
      end
      
      puts counts
      

      测试

      yum install ruby -y
      ruby test.rb
      
  • 基于Request Header(还有个never、always不进行演示)

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

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

    # 基于 Request Header
    annotations:
      kubernetes.io/ingress.class: nginx 
      nginx.ingress.kubernetes.io/canary: "true"                  # 要开启灰度发布机制,首先需要启用 Canary
      nginx.ingress.kubernetes.io/canary-by-header-value: canary  # 基于header的流量切分 value
      nginx.ingress.kubernetes.io/canary-by-header: user          # 基于header的流量切分  key
      nginx.ingress.kubernetes.io/canary-weight: "30"             # 会被忽略,因为配置了 canary-by-headerCanary版本
    

    验证:

    ➜ for i in $(seq 1 10); do curl -s -H "canary: never" echo.qikqiak.com | grep "Hostname"; done
    Hostname: production-856d5fb99-d6bds
    Hostname: production-856d5fb99-d6bds
    Hostname: production-856d5fb99-d6bds
    Hostname: production-856d5fb99-d6bds
    Hostname: production-856d5fb99-d6bds
    Hostname: production-856d5fb99-d6bds
    Hostname: production-856d5fb99-d6bds
    Hostname: production-856d5fb99-d6bds
    Hostname: production-856d5fb99-d6bds
    Hostname: production-856d5fb99-d6bds
    
    # 流量全部切到V2了,从而实现灰度发布
    [root@k8s-master02 ~]# for i in $(seq 1 5); do curl -s -H "user: canary" canary.test.com; done
    v2
    v2
    v2
    v2
    v2
    
  • 基于 Cookie

    **基于 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 版本。

    annotations:
      kubernetes.io/ingress.class: nginx 
      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
    ➜ for i in $(seq 1 10); do curl -s -b "users_from_Beijing=always" echo.qikqiak.com | grep "Hostname"; done
    Hostname: canary-66cb497b7f-48zx4
    Hostname: canary-66cb497b7f-48zx4
    Hostname: canary-66cb497b7f-48zx4
    Hostname: canary-66cb497b7f-48zx4
    Hostname: canary-66cb497b7f-48zx4
    Hostname: canary-66cb497b7f-48zx4
    Hostname: canary-66cb497b7f-48zx4
    Hostname: canary-66cb497b7f-48zx4
    Hostname: canary-66cb497b7f-48zx4
    Hostname: canary-66cb497b7f-48zx4
    

Ingress Nginx 自定义错误页

nginx.ingress.kubernetes.io/server-snippet:
  error_page 404 https://www.baidu.com # 如果访问不存在,跳转到baidu
  # 和nginx的配置没有区别

官方错误页面

https://github.com/kubernetes/ingress-nginx/blob/master/docs/examples/customization/custom-errors/custom-default-backend.yaml

Ingress Nginx 监控

步骤:https://kubernetes.github.io/ingress-nginx/user-guide/monitoring/

源码地址:https://github.com/kubernetes/ingress-nginx/tree/master/deploy

配置grafana: https://github.com/kubernetes/ingress-nginx/tree/master/deploy/grafana/dashboards

    spec:
      containers:
      - args:
       - --enable-metrics=true

        ports:
        - containerPort: 10254
          name: prometheus
          protocol: TCP

kind: Service
  namespace: ingress-nginx-prod
spec:
  ports:
  - name: prometheus
    port: 10254
    protocol: TCP

演示

1、背景和环境概述

本文中涉及到的环境中、 prometheus监控和grafana基本环境已部署好。 在 nginx ingress controller 的官方文档中对监控有相应描述 https://kubernetes.github.io/ingress-nginx/user-guide/monitoring/

2、修改prometheus配置

修改prometheus的配置,增加对ingress nginx的监控配置,可按照官方yaml 进行修改:

vim prometheus-configmap.yaml
- job_name: 'ingress-nginx-endpoints'
  kubernetes_sd_configs:
  - role: pod
    namespaces:
      names:
      - ingress-nginx
  relabel_configs:
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
    action: keep
    regex: true
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme]
    action: replace
    target_label: __scheme__
    regex: (https?)
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
    action: replace
    target_label: __metrics_path__
    regex: (.+)
  - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
    action: replace
    target_label: __address__
    regex: ([^:]+)(?::\d+)?;(\d+)
    replacement: $1:$2
  - source_labels: [__meta_kubernetes_service_name]
    regex: prometheus-server
    action: drop

重新apply一下configmap

kubectl apply -f prometheus-configmap.yaml

或者使用servicemonitor

cat <<\EOF> servicemonitor.yaml
---
#ingress-nginx
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: ingress-nginx-dev
  namespace: monitoring
  labels:
    app.kubernetes.io/name: ingress-nginx-dev
    app.kubernetes.io/instance: ingress-nginx
    release: prometheus
spec:
  endpoints:
    - path: /metrics
      port: prometheus
      #interval: 15s
  namespaceSelector:
    matchNames:
      - ingress-nginx-dev
  selector:
    matchLabels:
      app.kubernetes.io/component: controller
      app.kubernetes.io/instance: ingress-nginx
      app.kubernetes.io/name: ingress-nginx-dev
      app.kubernetes.io/part-of: ingress-nginx
      app.kubernetes.io/version: 1.10.1
EOF

3、检查是否生效

打开`prometheus`界面,查看`target`中是否有`ingress nginx`的相关记录 检查查询取值

4、配置grafana图形

在grafana图形中导入模板,模板可以按照官方给出的json文件操作,下载此json文件,在grafana中导入即可

查看图形

多域名指向同一后端

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cici
  namespace: dev
spec:
  ingressClassName: nginx-dev
  rules: #多域名指向同一后端
  - host: cicitest.xxx.online
    http: &cici_rules
      paths:
      - backend:
          service:
            name: cici-svc
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  - host: ci-kjr6wutest.xxx.online
    http: *cici_rules
  - host: ci-vnioustest.xxx.online
    http: *cici_rules

ingress-nginx 引用缓存

---
apiVersion: v1
data:
  enable-underscores-in-headers: "true"  #识别有_下划线的http头部
  allow-snippet-annotations: "true"      #开启所有注释配置
  #设置全局缓存
  http-snippet: |-
    proxy_cache_path /tmp/section_group_test levels=1:2 keys_zone=section_group_test:20m inactive=7d max_size=1g;
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx-dev
    app.kubernetes.io/name: ingress-nginx-dev
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.10.1
  name: ingress-nginx-controller
  namespace: ingress-nginx-dev

引用缓存

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: 50M
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
    #nginx.ingress.kubernetes.io/http-snippet: |
    #    proxy_cache_path /tmp/api_cache/section_group_test levels=1:2 keys_zone=section_group_test:20m inactive=7d max_size=1g;
    #modify ingress-controler configmap
    nginx.ingress.kubernetes.io/server-snippet: |
        location ~* /api/actuator {
            deny all;
        }
        location ^~ /app/complain {
                return 200;
        }
        location /xc/episodic_drama/dramas/app/compilations_40.txt {
                proxy_pass http://xxxxokio41x-us-west-2c.s3.us-west-2.vpce.amazonaws.com;
        }
        location = /xc/section_group {
                add_header X-Cache $upstream_cache_status;
                proxy_cache section_group_test;
                proxy_cache_methods GET POST;
                proxy_cache_valid  200 206 304 301 302 1m;
                proxy_cache_key $scheme$http_origin$request_method$uri$http_client_id$arg_sectionGroupTypeValue;
                proxy_pass http://xc-server-svc.dev1:80/;
        }
    nginx.ingress.kubernetes.io/configuration-snippet: |
        add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains;' always;
  name: xc
  namespace: dev
spec:
  ingressClassName: nginx-dev
  rules:
  - host: xc.xxx.online
    http:
      paths:
      - backend:
          service:
            name: xc-server-svc
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific

指定文件缓存

#ingress 的 中新增如下内容,可以通过request_uri按需配置指定文件的cache control:
  annotations:
    nginx.ingress.kubernetes.io/configuration-snippet: >
      more_set_headers "cache-Control: no-cache, no-store"; if ($request_uri ~*
      \.(?:ico|css|js|gif|jpe?g|png|svg|woff2|woff|ttf|eo|mp3|glb)$) {
        more_set_headers "cache-control: public, max-age=2592000";
      }

静态文件压缩

ingress-nginx支持gzip, brotli压缩,默认关闭。 brotli压缩率性能更好些

开启gzip压缩

修改configmap

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-configuration
data:
  use-gzip: "true"
  gzip-level: "6"

开启brotli压缩

https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#enable-brotli

修改configmap

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-configuration
data:
  enable-brotli: "true"
  brotli-level: "6"

记录真实ip

业务应用经常有需要用到用户真实ip的场景,比如,用户ip记录与审计,ip限制 等功能,通常,用户ip的传递依靠的是X-Forwarded-*参数。Ingress-Nginx默认 会通过X-Forward-For和X-Real-IP来透传客户端IP,但是当客户端主动在请求头 里指定了X-Forward-For和X-Real-IP,会导致服务端获取不到真实的客户端IP。

修改configmap配置:

---
apiVersion: v1
data:
  compute-full-forwarded-for: "true"
  forwarded-for-header: "X-Forwarded-For"
  use-forwarded-headers: "true"
kind: ConfigMap

参考: https://199604.com/2577

监控

用处不大

ingress-nginx启动参数开启指标

#service
spec:
  ports:
  - name: prometheus
    port: 10254
    protocol: TCP
#deployment
    spec:
      containers:
      - args:
        - --enable-metrics=true
        ports:
        - containerPort: 10254
          name: prometheus
          protocol: TCP

创建ServiceMonitor

---
#ingress-nginx
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: ingress-nginx-dev
  namespace: monitoring
  labels:
     app.kubernetes.io/name: ingress-nginx-dev
    app.kubernetes.io/instance: ingress-nginx-dev
    release: prometheus
spec:
  endpoints:
    - path: /metrics
      port: prometheus
      #interval: 15s
  namespaceSelector:
    matchNames:
      - ingress-nginx-dev
  selector:
    matchLabels:
      app.kubernetes.io/component: controller
      app.kubernetes.io/instance: ingress-nginx-dev
      app.kubernetes.io/name: ingress-nginx-dev
      app.kubernetes.io/part-of: ingress-nginx
      app.kubernetes.io/version: 1.10.1

告警规则可以从官方获得

从helm包找到告警规则

helm show values ingress-nginx --repo https://kubernetes.github.io/ingress-nginx
helm pull ingress-nginx --repo https://kubernetes.github.io/ingress-nginx

helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
-n default-info \
--create-namespace \
--set controller.metrics.enabled=true \
--set-string controller.podAnnotations."prometheus\.io/scrape"="true" \
--set-string controller.podAnnotations."prometheus\.io/port"="10254"  --dry-run -oyaml