kubernetes Ingress
Ingress介绍
ingress主要解决k8s集群中东西流量的访问也就是外部客户端访问k8s内部服务的流量,具体架构图如下
k8s如果使用Ingress功能,需要安装Ingress控制器,直接创建Ingress规则是没有任何效果的,官方维护的Ingress控制器有:
- AWS
- GCE
- nginx Ingress 控制器
Ingress控制器安装
安装Ingress nginx
可参考之前博客k8s部署ingress-nginxk8s部署ingress-nginx,本文通过Helm部署,需要首先安装helm管理工具:https://github.com/helm/helm
Ingress nginx控制器官方:https://kubernetes.github.io/ingress-nginx/deploy/#using-helm
安装Helm
[root@k8s01 ~]# ls
helm-v3.7.2-linux-amd64.tar.gz
[root@k8s01 ~]# tar xf helm-v3.7.2-linux-amd64.tar.gz
[root@k8s01 ~]# cp linux-amd64/helm /usr/bin/
#可以执行以下命令表示安装完成
[root@k8s01 ~]# helm version
version.BuildInfo{Version:"v3.7.2", GitCommit:"663a896f4a815053445eec4153677ddc24a0a361", GitTreeState:"clean", GoVersion:"go1.16.10"}
安装ingress控制器
由于Ingress资源的api版本进行了很多次调整,所以如果k8s集群版本低于1.19,请安装3.X版本或较低版本的ingress控制器,创建ingress时api版本也要正确填写
官方说明:https://kubernetes.github.io/ingress-nginx/deploy/#running-on-kubernetes-versions-older-than-119
#添加官方仓库
[root@k8s01 ~]# helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
"ingress-nginx" has been added to your repositories
#由于我的k8s是1.18的版本所以需要调整控制器的版本
,下载4版本以下的包
[root@k8s01 ~]# helm pull ingress-nginx/ingress-nginx --version='<4'
#解压下载的压缩包
[root@k8s01 ~]# tar xf ingress-nginx-3.40.0.tgz -C /tmp/
#修改配置
[root@k8s01 ~]# cd /tmp/ingress-nginx/
dnsPolicy: ClusterFirstWithHostNet #修改dns策略
hostNetwork: true #使用hostnetwork,使用宿主机网络
kind: DaemonSet #部署方式使用ds控制器
nodeSelector:
kubernetes.io/os: linux
ingress-nginx: true #添加一个nodeselector的标签
type: ClusterIP #svc类型改为ClusterIP
#创建一个命名空间
[root@k8s01 ~]# kubectl create ns ingress-nginx
#创建ingress控制
[root@k8s01 ~]# helm install ingress-nginx -n ingress-nginx .
#验证
[root@k8s01 ~]# kubectl get po -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-8742l 1/1 Running 1 46h
ingress使用
ingress的api版本历经过多次变化他们的配置项也不太一样分别是
- extensions/v1beta1:1.16版本之前使用
- networking.k8s.io/v1beta1:1.19版本之前使用
- networking.k8s.io/v1:1.19版本之后使用
ingress版本区别
如果Kubernetes版本低于1.19 ,可以使用networking.k8s.io/v1beta1替代,配置可以参考下述的networking.k8s.io/v1beta1,只有backend配置不一样
v1beta1基本使用
示例文件:
apiVersion: networking.k8s.io/v1beta1 # api版本号
kind: Ingress # 资源类型
metadata:
annotations: #一般一些nginx特性在此定义
kubernetes.io/ingress.class: "nginx" #使用那个ingress控制器,集群有多个可以选择
nginx.ingress.kubernetes.io/rewrite-target: "/$1" #nginx的rewrite功能
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" #后端如果是https协议需要说明,默认http
name: nginx #名称
spec:
rules: #一个Ingress可以配置多个rules
- host: www.test.com #域名配置,可以不写,匹配*, *.bar.com
http:
paths: #相当于nginx的location配合,同一个host可以配置多个path / /abc
- backend: #后端svc定义
serviceName: nginx-a #svc名称
servicePort: 80 #svc端口
path: "/a/(.*)" #匹配的路径
- backend:
serviceName: nginx-b
servicePort: 80
path: "/b/(.*)"
tls: #需要https加密
- hosts: #主机域名列表,可以配置多个
- www.test.com
- www.dev.com
secretName: tls #secret证书文件,可以写多个
secretName: tls1
v1基本使用
示例文件
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
annotations:
kubernetes.io/ingress.class: "nginx" #使用那个ingress控制器,集群有多个可以选择
nginx.ingress.kubernetes.io/rewrite-target: "/$1" #nginx的rewrite功能
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" #后端如果是https协议
spec:
rules: #一个Ingress可以配置多个rules
- host: www.test.com #域名配置,可以不写,匹配*, *.bar.com
http:
paths: #相当于nginx的location配合,同一个host可以配置多个path / /abc
- pathType: Prefix
path: "/"
backend: #后端svc定义
service: #service的定义
name: nginx #svc名称
port:
number: 80 #svc端口
tls: #需要https加密
- hosts: #主机域名列表,可以配置多个
- www.test.com
- www.dev.com
secretName: tls #secret证书文件,可以写多个
secretName: tls1
ingress-nginx使用
ingress-nginx是k8s官方维护的ingress项目,当然nginx官方也维护了一个自己的ingress名称为nginx-ingress
ingress-nginx官方文档:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/
ingress-nginx有三种配置方式:
- ConfigMap:使用Configmap在NGINX中设置全局配置,设置后对所有ingress规则生效。https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
- Annotations:如果您想要特定 Ingress 规则的特定配置,请使用此选项。https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
- 自定义模板:当需要更具体的设置时,例如open_file_cache ,在无法通过 ConfigMap 更改配置时调整监听选项。https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/custom-template/
域名重定向Redirect
官方说明:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#permanent-redirect
在 Nginx 作为代理服务器时,Redirect 可用于域名的重定向,比如访问 old.com 被重定向到 new.com。Ingress 可以更简单的实现 Redirect 功能,接下来用 www.test.com 作为旧域名, www.baidu.com 作为新域名进行演示:
相关配置项:
- permanent-redirect:重定向到的域名
- permanent-redirect-code:重定向代码,不配置默认301(永久重定向,如有其他原因可自行配置)
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: www.test.com
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx" #这里是ingress规则选用的那个ingress控制器,如果设置ingress-nginx为默认控制器可不写
nginx.ingress.kubernetes.io/permanent-redirect: https://www.baidu.com #重定向后的新域名
nginx.ingress.kubernetes.io/permanent-redirect-code: '301' #重定向http状态码
spec:
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
serviceName: service-v1
servicePort: 80
验证
[root@k8s01 ~]# curl www.test.com -I
HTTP/1.1 301 Moved Permanently #301重定向
Date: Wed, 12 Oct 2022 08:55:39 GMT
Content-Type: text/html
Content-Length: 277
Connection: keep-alive
Location: https://www.baidu.com #定向到的新域名
前后端分离Rewrite
官方示例:https://kubernetes.github.io/ingress-nginx/examples/rewrite/
现在有2个服务分别是service-v1与service-v2服务假设一个是后端服务一个是前端服务,访问service-v1需要访问/api-a,访问service-v2需要访问/api-b,俩个服务的请求地址都是/index.html,如果不使用Rewrite则所有请求默认会返回404。
相关配置项:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: www.test.com
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: "/$2"
spec:
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/api-a(/|$)(.*)"
backend:
serviceName: service-v1
servicePort: 80
- pathType: Prefix
path: "/api-b(/|$)(.*)"
backend:
serviceName: service-v2
servicePort: 80
测试
[root@k8s01 ~]# curl http://www.test.com/api-a
111111111111111
[root@k8s01 ~]# curl http://www.test.com/api-b
222222222222222
错误代码重定向
这里的错误代码重定向分为全局设置与某个ingress设置
开启全局ingress示例,错误代码重定向功能需要使用helm重新部署ingress-nginx控制器部署步骤如下:
#修改valume.yml文件
defaultBackend:
##
enabled: true #开启这个功能
image:
repository: woshitiancai/defaultbackend-amd64 #配置镜像
tag: "1.5"
#更新ingress
[root@k8s01 ~]# helm upgrade -n ingress-nginx ingress-nginx .
#验证是否生成一个新Pod
[root@k8s01 ~]# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-nbxgf 1/1 Running 0 17s
ingress-nginx-defaultbackend-58d87944cb-jd5tj 1/1 Running 0 30s
更新完成后访问一个不存在的页面会跳转到Error Server中的页面
[root@k8s01 ~]# curl www.test.com/login.html
default backend - 404
设置某个ingress的错误重定向会覆盖全局设置,示例如下
- nginx.ingress.kubernetes.io/default-backend: 默认后端service名称
- nginx.ingress.kubernetes.io/custom-http-errors: 那些错误代理重定向到默认后端sv
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-v1
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/default-backend: 'service-v2'
nginx.ingress.kubernetes.io/custom-http-errors: "404,415"
spec:
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/a"
backend:
serviceName: service-v1
servicePort: 80
使用ssl加密
生产环境对外的服务,一般需要配置https协议,使用Ingress也可以非常方便的添加https的证书。 可使用OpenSSL生成一个测试证书。如果是生产环境,证书为在第三方公司购买的证书,无需自行生成
#生成证书
[root@k8s01 ~]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout com.key -out com.crt -subj "/CN=www.test.com"
[root@k8s01 ~]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout org.key -out org.crt -subj "/CN=www.test.com"
#创建证书secret
[root@k8s01 ~]# kubectl create secret tls www.test.com --cert=com.crt --key=com.key
添加ingress的TLS配置
如果在ingress中设置tls,即默认的http会被强制重定向到https,http即不可访问,如果需要http与https同时可以使用可以设置
- nginx.ingress.kubernetes.io/ssl-redirect: “false” :禁用强制重定向,这样可以同时使用http与https
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: www.test.com
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
serviceName: service-v1
servicePort: 80
tls:
- hosts:
- www.test.com #证书所授权的域名列表
secretName: www.zhangzhuo.com #证书的Secret名字,自己匹配
可以看到Ingress添加TLS配置也非常简单,只需要在spec下添加一个tls字段即可:
- hosts:证书所授权的域名列表
- secretName:证书的 Secret 名字
- ingressClassName: ingress class 的名字,1.22+需要配置
匹配请求头
注意Ingress annotations的nginx.ingress.kubernetes.io/server-snippet配置。Snippet 配置是专门用于一些复杂的Nginx配置,和Nginx配置通用,在这里模拟下移动端与电脑端访问同一个域名转发到不同服务
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: www.test.com
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/server-snippet: |
set $agentflag 0;
if ($http_user_agent ~* "(Android|IPhone)" ){ #如果是安卓与IPhone访问会重定向到www.baidu.com的新网站
set $agentflag 1;
}
if ( $agentflag = 1 ) {
return 302 http://www.baidu.com;
}
spec:
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
serviceName: service-v1
servicePort: 80
验证
[root@k8s01 ~]# curl www.test.com -I
HTTP/1.1 200 OK
11111111111
#模拟安卓访问
[root@k8s01 ~]# curl 'www.test.com' -H 'User-Agent: Android' -I
HTTP/1.1 302 Moved Temporarily
Location: http://www.baidu.com
基本认证使用
有些网站可能需要通过密码来访问,对于这类网站可以使用Nginx的basic-auth设置密码访问,具体方法如下,由于需要使用 htpasswd工具,所以需要安装httpd
[root@k8s01 ~]# yum install httpd
#生成用户名密码
[root@k8s01 ~]# htpasswd -c auth test
[root@k8s01 ~]# cat auth
test:$apr1$MWrO9I1q$.FXzcU6l.riRUGkKXyvVz1
#创建secret
[root@k8s01 ~]# kubectl create secret generic http-auth --from-file=auth
创建包含密码认证的Ingress:
- nginx.ingress.kubernetes.io/auth-type:认证类型,可以是 basic 和 digest
- nginx.ingress.kubernetes.io/auth-secret:密码文件的 Secret 名称
- nginx.ingress.kubernetes.io/auth-realm:需要密码认证的消息提醒
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: www.test.com
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/auth-type: basic #认证类型
nginx.ingress.kubernetes.io/auth-secret: http-auth #认证密码secret资源名称
nginx.ingress.kubernetes.io/auth-realm: "请输入用户名密码:" #需要密码认证的消息提醒
spec:
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
serviceName: service-v1
servicePort: 80
配置黑名单/白名单
黑名单配置详解
配置黑名单禁止某一个或某一段 IP,需要在 Nginx Ingress的ConfigMap中配置,比如将 192.168.200.161(多个配置逗号分隔)添加至黑名单
configmap官方配置文档说明:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
[root@k8s01 ingress-nginx]# vim values.yaml
...
config:
block-cidrs: 192.168.200.161
...
#更新验证
[root@k8s01 ingress-nginx]# helm upgrade -n ingress-nginx ingress-nginx .
[root@k8s01 ingress-nginx]# curl www.test.com -I
HTTP/1.1 403 Forbidden #这里拒绝访问
白名单配置详解
白名单表示只允许某个IP可以访问,直接在yaml文件中配置即可(也可以通过ConfigMap配置),比如只允许192.168.200.161访问,只需要添加一个nginx.ingress.kubernetes.io/whitelist-source-range 注释即可:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: www.test.com
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/whitelist-source-range: 192.168.200.161 #配置白名单
spec:
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
serviceName: service-v1
servicePort: 80
速率限制
有时候可能需要限制速率以降低后端压力,或者限制单个IP每秒的访问速率防止攻击。此时可以使用 Nginx的rate limit进行配置,相关的设置如下:
#限制每秒的连接,单个 IP:
nginx.ingress.kubernetes.io/limit-rps
#限制每分钟的连接,单个 IP:
nginx.ingress.kubernetes.io/limit-rpm
#限制客户端每秒传输的字节数,单位为 K,需要开启proxy-buffering:
nginx.ingress.kubernetes.io/limit-rate
#速率限制白名单
nginx.ingress.kubernetes.io/limit-whitelist
首先没有加速率限制,使用ab进行访问,Failed为0:
[root@k8s01 ingress-nginx]# ab -c 10 -n 100 http://www.test.com/ | grep requests
Complete requests: 100
Failed requests: 0
添加速率限制,限制只能有一个连接,只需要添加 nginx.ingress.kubernetes.io/limit-connections 为 1 即可
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: www.test.com
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/limit-connections: "1"
spec:
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
serviceName: service-v1
servicePort: 80
验证
[root@k8s01 ingress-nginx]# ab -c 10 -n 100 http://www.test.com/ | grep requests
Complete requests: 100
Failed requests: 91
实现灰度或者金丝雀发布
首先创建一个v1版本的ingress
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-v1
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
serviceName: service-v1
servicePort: 80
创建 v2 版本的 Ingress 时,需要添加两个注释
- nginx.ingress.kubernetes.io/canary:表明是灰度环境
- nginx.ingress.kubernetes.io/canary-weight:表明切多少流量到该环境,本示例为 10%
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-v2
namespace: default
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
rules:
- host: www.test.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
serviceName: service-v2
servicePort: 80
验证
[root@k8s01 ingress-nginx]# for i in {1..100};do curl www.test.com >> 1.txt;done
[18:50:26 root@node-1 ingress]#cat 1.txt | sort | uniq -c
89 111111111111111
11 222222222222222
#比例大概为9:1
代理后端https协议服务
如果后端服务是https协议,需要使用nginx.ingress.kubernetes.io/backend-protocols声明后端服务是https。这里代理K8s的web控制台作为示例。代理后访问默认会强制跳转到https协议,返回的证书也是服务本身的证书
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: kubernetes-dashboard
namespace: kubernetes-dashboard
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: "/$1"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" #这里声明后端服务是https协议,不写默认http
spec:
rules:
- host: dashboard.k8s.com
http:
paths:
- pathType: Prefix
path: "/dashboard/(.*)"
backend:
serviceName: kubernetes-dashboard
servicePort: 443
ingress-nginx四层代理
在新版本的nginx也引入了四层代理的概念,可直接代理TCP与UDP协议,在ingress-nginx也可以通过四层代理实现service的代理,实现方式如下:
官方文档:https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/
首先在使用helm部署ingress-nginx之后,需修改nginx启动资源的启动参数添加–tcp-services-configmap、–udp-services-configmap的配置启用四层代理,通过修改配置configmap资源进行四层代理的配置
TCP代理
在ingress-nginx-controller中添加–tcp-services-configmap启动参数
args:
- /nginx-ingress-controller
- '--publish-service=$(POD_NAMESPACE)/ingress-nginx-controller'
- '--tcp-services-configmap=$(POD_NAMESPACE)/tcp-configmap-example' #主要是这里
- '--election-id=ingress-controller-leader'
- '--ingress-class=nginx'
- '--configmap=ingress-nginx/ingress-nginx-controller'
- '--validating-webhook=:8443'
- '--validating-webhook-certificate=/usr/local/certificates/cert'
- '--validating-webhook-key=/usr/local/certificates/key'
#参数说明如下
--tcp-services-configmap=$(POD_NAMESPACE)/ 这里为固定,表示tcp配置选择ingress-nginx安装的命名空间的configmap资源
tcp-configmap-example 为configmap资源的名称
创建configmap配置文件
[root@k8s01 ingress-nginx]# vim tcp-configmap-example.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: tcp-configmap-example
data:
9000: "default/kubernetes:443"
9001: "kubernetes-dashboard/kubernetes-dashboard:443"
[root@k8s01 ingress-nginx]# kubectl apply -f tcp-configmap-example.yaml -n ingress-nginx
#主要配置参数介绍
代理k8s内部svc资源
9000 对外暴露的端口
"命名空间/svc名称:svc端口"
所有的TCP代理配置都可以写入到这个configmap资源当中,修改后ingress-nginx会自动更新配置,根据配置暴露相应的端口,如果配置错误则不会进行变更
UDP代理
在ingress-nginx-controller中添加–udp-services-configmap启动参数
args:
- /nginx-ingress-controller
- '--publish-service=$(POD_NAMESPACE)/ingress-nginx-controller'
- '--tcp-services-configmap=$(POD_NAMESPACE)/tcp-configmap-example'
- '--udp-services-configmap=$(POD_NAMESPACE)/udp-configmap-example' #主要是这里
- '--election-id=ingress-controller-leader'
- '--ingress-class=nginx'
- '--configmap=ingress-nginx/ingress-nginx-controller'
- '--validating-webhook=:8443'
- '--validating-webhook-certificate=/usr/local/certificates/cert'
- '--validating-webhook-key=/usr/local/certificates/key'
#参数说明如下
--udp-services-configmap=$(POD_NAMESPACE)/ 这里为固定,表示tcp配置选择ingress-nginx安装的命名空间的configmap资源
udp-configmap-example 为configmap资源的名称
创建configmap配置文件
[root@k8s01 ingress-nginx]# vim udp-configmap-example.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: udp-configmap-example
data:
53: "kube-system/kube-dns:53"
[root@k8s01 ingress-nginx]# kubectl apply -f udp-configmap-example.yaml -n ingress-nginx
#主要配置参数介绍
代理k8s内部svc资源
9000 对外暴露的端口
"命名空间/svc名称:svc端口"