地址重写
地址重写在ingress中通过在annotation中添加nginx.ingress.kubernetes.io/rewrite-target: "/$1"
这种类型的配置即可。
通过地址重写,我们可以实现诸如访问a.com/foo 重写到a.com,访问a.com/foo重写到a.com/foo/bar,需要注意的是重写后的地址需要是能真实访问到资源的地址,不然重写也没什么意义。
实例如下:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-rewrite annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/rewrite-target: "/$1" spec: rules: - host: nginx-rewrite.dev.jokerbai.com http: paths: - path: /foo/?(.*) backend: service: name: nginx-svc port: number: 80 pathType: Prefix
然后通过在访问URL中带/foo即可访问到Nginx服务,如下:
$ curl -x 192.168.205.51:80 http://nginx-rewrite.dev.jokerbai.com/foo <!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>
认证访问
有时候,我们暴露出来的域名不想被其他人使用(可能有人会说:那你不暴露出来不就完事了),比如说prometheus的后台,默认是没有登录管理的,而运维是需要到后台去查询监控信息的,为了安全,我们需要给这类域名加上认证,以便在一定程度上降低风险。
ingress提供base auth的认证方式,我们可以通过这种方式为我们的域名提供认证。
(1)创建密码
$ htpasswd -c auth joker New password: Re-type new password: Adding password for user joker
(2)将密码保存到secret中
$ kubectl create secret generic basic-auth --from-file=auth secret/basic-auth created
(3)在Ingress中配置认证 只需要在ingress配置中添加两个annotation即可完成。
- nginx.ingress.kubernetes.io/auth-type: basic 指明认证方式
- nginx.ingress.kubernetes.io/auth-secret: basic-auth 指定认证的账号密码
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-auth annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-secret: basic-auth spec: rules: - host: nginx-auth.dev.jokerbai.com http: paths: - path: / backend: service: name: nginx-svc port: number: 80 pathType: Prefix
然后不带认证信息访问,结果如下:
$ curl -x 192.168.205.51:80 http://nginx-auth.dev.jokerbai.com/foo <html> <head><title>401 Authorization Required</title></head> <body> <center><h1>401 Authorization Required</h1></center> <hr><center>nginx</center> </body> </html>
直接返回401,需要认证才能访问。
下面带上认证信息访问,结果如下:
$ curl -u "joker:123" -x 192.168.205.51:80 http://nginx-auth.dev.jokerbai.com/ <!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>
如果是从浏览器访问,会给我们弹出一个输入框,输入账号密码即可进入应用。
黑白名单
有时候,光有认证访问也并不安全,这时候我们可以通过配置黑白名单的方式,把访问的范围降低,在nginx中,我们可以通过配置allow和deny来配置,在ingress中,也支持类似的配置。
配置白名单
在ingress里配置白名单可以通过两种方式实现:
- 添加annotation,这种是只针对单个域名
- 在ingress-nginx的configmap中配置,全局有效
(1)通过annotation配置,如下:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-whitelist annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/whitelist-source-range: 10.1.10.2 spec: rules: - host: nginx-whitelist.dev.jokerbai.com http: paths: - path: / backend: service: name: nginx-svc port: number: 80 pathType: Prefix
配置了一个不存在的地址10.1.10.2,查看访问效果。
$ curl -I -x 192.168.205.51:80 http://nginx-whitelist.dev.jokerbai.com HTTP/1.1 403 Forbidden Date: Fri, 22 Jul 2022 06:57:45 GMT Content-Type: text/html Content-Length: 146 Connection: keep-alive
给我们返回403拒绝访问。
如果我们把地址配置成可以访问的IP,则可以访问。
(2)配置ConfigMap 部署好ingress-nginx后,会在ingress-nginx的namespace下生成叫“ingress-nginx-controller”的ConfigMap配置文件,我们只需要在这个配置文件进行配置即可。
$ kubectl edit cm -n ingress-nginx ingress-nginx-controller apiVersion: v1 data: allow-snippet-annotations: "true" compute-full-forwarded-for: "true" use-forwarded-headers: "true" whitelist-source-range: 172.16.0.0/24 kind: ConfigMap metadata: 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.3.0 name: ingress-nginx-controller namespace: ingress-nginx
配置完保存即可,ingress-nginx的pod会自动reload。不过这种配置是全局生效,在使用的时候慎重。
配置黑名单
有白名单就有黑名单,ingress的黑名单配置只能通过ConfigMap来,而且是全局生效的。
目前支持以下三种黑名单:
- block-cidrs:限制IP
- block-user-agents:限制User-Agent
- block-referers:限制referer
配置很简单,如下:
$ kubectl edit cm -n ingress-nginx ingress-nginx-controller apiVersion: v1 data: allow-snippet-annotations: "true" compute-full-forwarded-for: "true" use-forwarded-headers: "true" whitelist-source-range: 172.16.0.0/24 block-cidrs: 10.1.10.100 kind: ConfigMap metadata: 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.3.0 name: ingress-nginx-controller namespace: ingress-nginx
访问限速
有时候访问量太大,可以通过在ingress进行限速,配置如下:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-limit annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/limit-rate: "100K" nginx.ingress.kubernetes.io/limit-whitelist: "10.1.10.100" nginx.ingress.kubernetes.io/limit-rps: "1" nginx.ingress.kubernetes.io/limit-rpm: "30" spec: rules: - host: nginx-limit.dev.jokerbai.com http: paths: - path: / backend: service: name: nginx-svc port: number: 80 pathType: Prefix
其中:
- nginx.ingress.kubernetes.io/limit-rate:限制客户端每秒传输的字节数
- nginx.ingress.kubernetes.io/limit-whitelist:白名单中的IP不限速
- nginx.ingress.kubernetes.io/limit-rps:单个IP每秒的连接数
- nginx.ingress.kubernetes.io/limit-rpm:单个IP每分钟的连接数
灰度发布
?Nginx Annotations 支持以下 4 种 Canary 规则:
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 并将请求与其他金丝雀规则进行优先级的比较。
定义两个版本的代码。V1版本代码如下:
package main import ( "github.com/gin-gonic/gin" "net/http" ) func main(){ g:=gin.Default() g.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK,gin.H{ "version": "v1", "data": "hello world", }) }) _ = g.Run("8080") }
V2版本代码如下:
package main import ( "github.com/gin-gonic/gin" "net/http" ) func main(){ g:=gin.Default() g.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK,gin.H{ "version": "v2", "data": "hello world,SB", }) }) _ = g.Run("8080") }
然后制作镜像,Dockerfile如下:
FROM golang AS build-env ADD . /go/src/app WORKDIR /go/src/app RUN go get -u -v github.com/gin-gonic/gin RUN govendor sync RUN GOOS=linux GOARCH=386 go build -v -o /go/src/app/app-server-v1 FROM alpine RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime COPY --from=build-env /go/src/app/app-server-v1 /usr/local/bin/app-server-v1 EXPOSE 8080 CMD [ "/usr/local/bin/app-server-v1" ]
制作镜像并上传:
$ docker build -t registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1 . $ docker push registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1
PS:V2版本操作类似
V1和V2版本的Deployment和Service如下:
apiVersion: apps/v1 kind: Deployment metadata: name: app-server-v1 spec: selector: matchLabels: app: app-server-v1 replicas: 2 template: metadata: labels: app: app-server-v1 spec: containers: - name: app-server-v1 image: registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: app-server-v1-svc spec: selector: app: app-server-v1 ports: - name: http port: 8080 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app-server-v1 annotations: kubernetes.io/ingress.class: "nginx" spec: rules: - host: canary.dev.jokerbai.com http: paths: - path: / backend: service: name: app-server-v1-svc port: number: 8080 pathType: Prefix --- apiVersion: apps/v1 kind: Deployment metadata: name: app-server-v2 spec: selector: matchLabels: app: app-server-v2 replicas: 2 template: metadata: labels: app: app-server-v2 spec: containers: - name: app-server-v2 image: registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v2 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: app-server-v2-svc spec: selector: app: app-server-v2 ports: - name: http port: 8080 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app-server-v2 annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "10" spec: rules: - host: canary.dev.jokerbai.com http: paths: - path: / backend: service: name: app-server-v2-svc port: number: 8080 pathType: Prefix
说明:
- nginx.ingress.kubernetes.io/canary: true 表示开启canary
- nginx.ingress.kubernetes.io/canary-weight: 10 表示权重为10,也就是v1:v2大致为9:1
在实际使用中,也是可以通过部署不同版本的Deployment来实现灰度发布。
总结
目前集群内应用的访问主要是通过Service和Ingress两种方式,其中Service是四层,Ingress是七层,在企业应用中,除了一些必须使用四层的应用,比如MySQL、Redis,其他的基本都采用Ingress进行访问。
Ingress的选型非常多,可以根据企业的实际情况(比如技术栈、熟悉程度、规模大小)进行选择,而Service主要还是在集群内部使用。