K8S-Ingress进阶
- Kubernetes
- 2024-12-05
- 8218热度
- 2评论
K8S-Ingress进阶
在前面Service章节其实已经介绍过Ingress,但是针对于生产环境下的Ingress配置会更加多样化。针对不同的应用场景,在本章节会详细介绍Ingress的不同使用方法。如果读者之前有接触过Nginx,其实会发现跟Nginx的功能点非常类似。
在开始前,为读者介绍两个概念,分别是hostNetwork和Annotations。
HostNetwork
hostNetwork 是 Kubernetes 中 Pod 配置的一个字段,用于指定 Pod 是否使用主机的网络命名空间。在 Kubernetes 中,Pod 通常会被分配一个虚拟网络接口,所有容器都在这个虚拟网络接口中进行通信。而设置 hostNetwork: true 时,Pod 会直接使用宿主机的网络接口,意味着该 Pod 共享宿主机的 IP 地址和网络堆栈。
hostNetwork 的工作原理:默认情况下,Pod 使用自己的网络命名空间,也就是每个 Pod 和宿主机有不同的网络接口和 IP 地址。启用 hostNetwork后,Pod 使用宿主机的网络接口。Pod 将不再有独立的 IP 地址,而是直接使用宿主机的 IP 地址。Pod 内的容器共享宿主机的网络栈,包括所有网络端口和防火墙规则。
hostNetwork 的使用场景:
1、高性能网络应用:某些网络密集型应用(如网络代理、DNS 服务、负载均衡器等)可能需要与宿主机的网络栈紧密集成,使用 hostNetwork 可以减少网络延迟和带宽消耗。
2、需要访问宿主机的特定网络接口:当需要访问宿主机的某些特定网络设备时(如物理网卡或内网 IP),可以设置 hostNetwork。
3、网络插件和操作系统级别的配置:例如,CNI 插件(如 Calico、Flannel)或系统级网络配置可能需要在主机网络空间中运行 Pod。
hostNetwork 的优点:
1、直接访问宿主机网络:Pod 可以直接访问宿主机的网络接口,这对于某些需要低延迟或高带宽的应用非常重要。
2、支持特殊网络配置:对于需要特殊网络配置(例如某些硬件接口、特定子网等)的应用,使用 hostNetwork 可以简化网络设置。
hostNetwork 的缺点:
1、端口冲突:如果多个 Pod 使用相同的端口并设置了 hostNetwork: true,它们将共享宿主机的端口,这可能导致端口冲突。因此,需要非常小心地分配端口。
2、限制灵活性:使用宿主机的网络命名空间限制了 Pod 在网络配置上的灵活性。Pod 可能无法在多个网络中独立工作。
3、安全性问题:Pod 直接使用宿主机网络可能增加安全风险,因为容器可以访问宿主机的网络接口,绕过一些网络隔离。
Ingress中的Annotations
在 Kubernetes 中,Annotations(注解) 确实可以翻译为“注释”,但是在 Kubernetes 的资源对象中,Annotations 并不仅仅用于存储注释信息。它们是一个可以存储 非标识性、非结构化数据 的键值对,允许附加额外的信息和配置信息。虽然它的名字是“注释”,但它在 Kubernetes 中的作用远远超出了普通的注释,实际上是用来扩展资源功能的。
Annotations 与 Labels 的区别:
1、Labels:用于标识和选择 Kubernetes 资源,通常用于资源的筛选、查询、分组等操作,且具有结构化的键值对。
2、Annotations:用于存储任意的非结构化元数据(如配置、文档、说明、状态等),它的内容可以是长文本,并且通常不会直接影响对象的选择或调度。
对于 Ingress 对象,Annotations 字段用来扩展 Ingress Controller 的功能,允许用户为 Ingress 资源配置额外的行为、优化、功能开关等。这些注解不会影响 Kubernetes 对象本身的选择或调度,而是通过 Ingress Controller(如 NGINX Ingress Controller)来解读和执行。
Ingress Controller 在启动时会解析 Ingress 资源的 annotations 字段,基于这些键值对来动态地调整其行为和配置。例如:配置负载均衡策略(如轮询、最小连接数等)、启用/禁用某些功能(如 SSL 重定向、路径重写、请求限流等)、配置超时、请求体大小限制等(如 proxy body size)。这些功能的生效并不依赖 Kubernetes 内部的调度或选择机制,而是通过解析注解并修改 Ingress Controller 的行为来实现的。
Annotations的优点:
1、灵活性和扩展性:Annotations 提供了一种非侵入式的方式来扩展 Kubernetes 对象的功能,而不需要改变资源本身的结构。
2、避免改变标准 API:Kubernetes 本身的 API 设计是通用的,Annotations 允许在不更改核心 API 的情况下,实现不同的功能扩展和个性化需求。
3、向后兼容:Ingress Controller 可以检查是否存在特定的注解来决定是否启用某些功能,因此可以方便地进行版本控制和逐步过渡。
本次实验与之前的章节进行区分,使用Helm进行安装。(Helm的安装此处省略,详情请参看之前的Helm&Operate章节)
添加Ingress Nginx Controller仓库,并拉取对应的安装包
[root@master-01 ~]# helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
"ingress-nginx" has been added to your repositories
[root@master-01 ~]# helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "ingress-nginx" chart repository
Update Complete. ⎈Happy Helming!⎈
[root@master-01 ~]# helm pull ingress-nginx/ingress-nginx --version 4.11.3
解压软件包,切换到ingress-nginx目录下,修改对应配置
[root@master-01 ingress-nginx]# vim values.yaml
1、修改拉取的镜像地址,此处不再赘述了。可以选择代理拉取、本地克隆或者直接寻找国内镜像(如果拉取国内镜像的,需要将镜像的digest值注释)
2、hostNetwork字段的值设为true
hostNetwork: true
3、修改dnsPolicy字段的值为ClusterFirstWithHostNet
dnsPolicy: ClusterFirstWithHostNet
4、添加节点选择的标签 ingress: true(添加双引号,变成字符串)
nodeSelector:
kubernetes.io/os: linux
ingress: "true"
5、容器部署类型修改为DaemonSet
# -- Use a `DaemonSet` or `Deployment`
kind: DaemonSet
6、修改Service类型为NodePort
service:
...省略部分输出...
labels: {}
# -- Type of the external controller service.
# Ref: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types
type: NodePort
7、 将ingress nginx 设置为默认的 ingressClass
ingressClassResource:
# -- Name of the IngressClass
name: nginx
# -- Create the IngressClass or not
enabled: true
# -- If true, Ingresses without `ingressClassName` get assigned to this IngressClass on creation.
# Ingress creation gets rejected if there are multiple default IngressClasses.
# Ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#default-ingress-class
default: true #原本为false
# -- Annotations to be added to the IngressClass resource.
annotations: {}
# -- Controller of the IngressClass. An Ingress Controller looks for IngressClasses it should reconcile by this value.
# This value is also being set as the `--controller-class` argument of this Ingress Controller.
# Ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class
controllerValue: k8s.io/ingress-nginx
修改完成后,对部署的节点添加标签
[root@master-01 ingress-nginx]# kubectl label nodes node-02 ingress=true
node/node-02 labeled
创建对应的命名空间后,创建ingress-nginx
[root@master-01 ingress-nginx]# kubectl create ns ingress-nginx
[root@master-01 ingress-nginx]# helm install ingress-nginx . -n ingress-nginx
NAME: ingress-nginx
LAST DEPLOYED: Wed Dec 4 14:35:10 2024
NAMESPACE: ingress-nginx
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The ingress-nginx controller has been installed.
Get the application URL by running these commands:
export HTTP_NODE_PORT=$(kubectl get service --namespace ingress-nginx ingress-nginx-controller --output jsonpath="{.spec.ports[0].nodePort}")
export HTTPS_NODE_PORT=$(kubectl get service --namespace ingress-nginx ingress-nginx-controller --output jsonpath="{.spec.ports[1].nodePort}")
export NODE_IP="$(kubectl get nodes --output jsonpath="{.items[0].status.addresses[1].address}")"
echo "Visit http://${NODE_IP}:${HTTP_NODE_PORT} to access your application via HTTP."
echo "Visit https://${NODE_IP}:${HTTPS_NODE_PORT} to access your application via HTTPS."
An example Ingress that makes use of the controller:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example
namespace: foo
spec:
ingressClassName: nginx
rules:
- host: www.example.com
http:
paths:
- pathType: Prefix
backend:
service:
name: exampleService
port:
number: 80
path: /
# This section is only required if TLS is to be enabled for the Ingress
tls:
- hosts:
- www.example.com
secretName: example-tls
If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided:
apiVersion: v1
kind: Secret
metadata:
name: example-tls
namespace: foo
data:
tls.crt: <base64 encoded cert>
tls.key: <base64 encoded key>
type: kubernetes.io/tls
注意:作者在部署时,遇到了报错,此处与读者分享
在使用境外机器下载镜像到国内时,下载没有出现问题,但是推送到阿里云镜像仓库时,出现报错
ctr: content digest sha256:e1fc6d49e0e3225374e9fe43a4ebbdd791e9b0d821b2f4d4da2b9f1af3b57736: not found
解决方法:在拉取镜像时,添加--all-platforms参数。所以建议读者使用Github的方法进行克隆
[root@master-03 ~]# ctr images pull registry.k8s.io/ingress-nginx/controller:v1.11.3 --all-platforms
使用Helm install安装时报错如下
[root@master-01 ingress-nginx]# helm install ingress-nginx -n ingress-study .
Error: INSTALLATION FAILED: Unable to continue with install: ClusterRole "ingress-nginx" in namespace "" exists and cannot be imported into the current release: invalid ownership metadata; label validation error: missing key "app.kubernetes.io/managed-by": must be set to "Helm"; annotation validation error: missing key "meta.helm.sh/release-name": must be set to "ingress-nginx"; annotation validation error: missing key "meta.helm.sh/release-namespace": must be set to "ingress-study"
根据报错,可以观察到有ClusterRole资源已经存在了,原因是在之前实验时,没有清理环境,将对应的ClusterRole资源删除,后面报错提示有些KV不对应,原因也是旧的资源没有清理
[root@master-01 ~]# kubectl delete -f ingress.yaml
namespace "ingress-nginx" deleted
serviceaccount "ingress-nginx" deleted
serviceaccount "ingress-nginx-admission" deleted
role.rbac.authorization.k8s.io "ingress-nginx" deleted
role.rbac.authorization.k8s.io "ingress-nginx-admission" deleted
clusterrole.rbac.authorization.k8s.io "ingress-nginx" deleted
clusterrole.rbac.authorization.k8s.io "ingress-nginx-admission" deleted
rolebinding.rbac.authorization.k8s.io "ingress-nginx" deleted
...省略部分输出...
继续安装,仍然提示等待前置资源超时
[root@master-01 ingress-nginx]# helm install ingress-nginx -n ingress-nginx .
Error: INSTALLATION FAILED: failed pre-install: 1 error occurred:
* timed out waiting for the condition
解决思路和方法:查看在ingress-nginx命名空间下创建的镜像,可以观察到Pod拉取镜像失败
[root@master-01 ingress-nginx]# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-zxj49 0/1 ImagePullBackOff 0 2m
通过describe命令查看具体的镜像,手动拉取镜像,或者是修改values.yaml文件内镜像的下载地址
Warning Failed 26s (x3 over 111s) kubelet Error: ErrImagePull
Normal BackOff 0s (x4 over 111s) kubelet Back-off pulling image "registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.4"
Warning Failed 0s (x4 over 111s) kubelet Error: ImagePullBackOff
创建实验使用的命名空间
[root@master-01 ~]# kubectl create namespace study-ingress
namespace/study-ingress created
创建Nginx容器模拟Web服务,并且暴露该容器的80端口
root@master-01 ~]# kubectl create deploy nginx --image=registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:1.15.12 -n study-ingress
deployment.apps/nginx created
[root@master-01 ~]# kubectl expose deployment nginx --port 80 -n study-ingress
service/nginx exposed
创建Ingress服务指向Service
[root@master-01 ~]# vim web-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
namespace: study-ingress
spec:
rules:
- host: nginx.test.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: nginx
port:
number: 80
[root@master-01 ~]# kubectl create -f web-ingress.yaml
ingress.networking.k8s.io/nginx-ingress created
[root@master-01 ~]# kubectl get -f web-ingress.yaml
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx-ingress nginx nginx.test.com 80 5s
切换到node-02,读者需要注意,由于使用了hostnetwork,所以Ingress将直接绑定到宿主机的网络接口,监听来自外部的流量。如果 Service 是类型为 ClusterIP 或 NodePort,它的端口不会直接暴露到物理机上,而是仍然只在集群内部可访问。只有当你使用 LoadBalancer 类型的 Service,或者通过 Ingress 控制器的配置将流量路由到特定的服务时,流量才会通过宿主机端口暴露(作者后面将Ingress的Service修改为了LoadBalancer)
[root@master-01 ~]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.96.32.35 <pending> 80:30826/TCP,443:32622/TCP 11h
[root@node-02 ~]# ss -lntp | grep 80
LISTEN 0 16384 *:80 *:* users:(("nginx",pid=36012,fd=15),("nginx",pid=35183,fd=15))
LISTEN 0 16384 *:80 *:* users:(("nginx",pid=36012,fd=7),("nginx",pid=35182,fd=7))
LISTEN 0 16384 [::]:80 [::]:* users:(("nginx",pid=36012,fd=8),("nginx",pid=35182,fd=8))
LISTEN 0 16384 [::]:80 [::]:* users:(("nginx",pid=36012,fd=16),("nginx",pid=35183,fd=16))
[root@node-02 ~]# ss -lntp | grep 443
LISTEN 0 16384 *:443 *:* users:(("nginx",pid=36012,fd=17),("nginx",pid=35183,fd=17))
LISTEN 0 16384 *:443 *:* users:(("nginx",pid=36012,fd=9),("nginx",pid=35182,fd=9))
LISTEN 0 16384 [::]:8443 [::]:* users:(("nginx-ingress-c",pid=35846,fd=12))
LISTEN 0 16384 [::]:443 [::]:* users:(("nginx",pid=36012,fd=10),("nginx",pid=35182,fd=10))
LISTEN 0 16384 [::]:443 [::]:* users:(("nginx",pid=36012,fd=18),("nginx",pid=35183,fd=18))
修改物理机上的hosts文件,添加映射,通过浏览器直接访问域名(注意:LoadBalancer类型访问不需要加端口)
192.168.132.172 nginx.test.com
Ingress Nginx 域名重定向 Redirect
在 Nginx 作为代理服务器时,Redirect 可用于域名的重定向,比如访问 old.com 被重定向到new.com。Ingress 可以更简单的实现 Redirect 功能,接下来用 nginx.redirect.com 作为旧域名,blog.caijxlinux.work 作为新域名进行演示
[root@master-01 ~]# vim redirect.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-redirect
namespace: study-ingress
annotations:
nginx.ingress.kubernetes.io/permanent-redirect: https://blog.caijxlinux.work
spec:
rules:
- host: nginx.redirect.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: nginx
port:
number: 80
[root@master-01 ~]# kubectl create -f redirect.yaml
ingress.networking.k8s.io/nginx-redirect created
通过curl命令访问域名nginx.redirect.com,可以看到 301(请求被重定向的返回值)
[root@master-01 ~]# curl -I -H "Host:nginx.redirect.com" 192.168.132.173
HTTP/1.1 301 Moved Permanently
Date: Wed, 04 Dec 2024 18:40:02 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
Location: https://blog.caijxlinux.work
Ingress Nginx 前后端分离 Rewrite
前后端分离常常涉及到将请求从前端(通常是静态资源)和后端(通常是 API 服务)进行分离和路由。Rewrite 规则可以用于将请求路径改写为后端服务需要的路径,以便让前端和后端在不同的路径上运行,且能够无缝协作
案例:假设有一个后端服务,托管在根路径下,而前端 API 服务托管在 /api-a 路径下。你希望用户通过访问 nginx.test.com/api-a 来访问后端。
在这种情况下,Ingress Nginx 的配置可以使用 Rewrite-target 注解来实现路径的重写
创建一个Nginx容器模拟后端服务并暴露80端口,该容器只有根路径,并没有/api-a路径的对应页面
[root@master-01 ~]# kubectl create deploy backend-api --image=registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:backend-api -n study-ingress
deployment.apps/backend-api created
[root@master-01 ~]# kubectl expose deploy backend-api --port 80 -n study-ingress
service/backend-api exposed
验证路径
[root@master-01 ~]# kubectl get svc -n study-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
backend-api ClusterIP 10.96.17.221 <none> 80/TCP 9s
nginx ClusterIP 10.96.135.131 <none> 80/TCP 108m
[root@master-01 ~]# curl 10.96.17.221/api-a
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.15.12</center>
</body>
</html>
[root@master-01 ~]# curl 10.96.17.221
<h1> backend for ingress rewrite </h1>
<h2> Path: /api-a </h2>
<a href="http://gaoxin.kubeasy.com"> Kubeasy </a>
配置Ingress rewrite功能,将/api-a 重写为"/"。
1、nginx.ingress.kubernetes.io/rewrite-target: /$2:这个注解将请求路径中的第二个捕获组(即 /api-a/ 后面的部分)作为新路径传递给后端服务。比如,访问 /api-a/foo 会被重写为 /foo。
2、path: /api-a(/|$)(.*):使用正则表达式匹配 /api-a 后面的路径。它会捕获 /api-a/ 后面的所有内容,并通过 $2 传递给后端服务。
[root@master-01 ~]# vim rewrite.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: backend-api
namespace: study-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: nginx.test.com
http:
paths:
- path: /api-a(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: backend-api
port:
number: 80
[root@master-01 ~]# kubectl create -f rewrite.yaml
ingress.networking.k8s.io/backend-api created
修改任意主机的/etc/hosts文件,增加映射
[root@node-02 ~]# vim /etc/hosts
192.168.132.173 nginx.test.com
通过curl命令访问,可以观察到/api-a路径被重定向到根目录下
[root@node-02 ~]# curl nginx.test.com/api-a
<h1> backend for ingress rewrite </h1>
<h2> Path: /api-a </h2>
Ingress Nginx 错误代码重定向
当访问某个服务时遇到 404 或 500 错误,可以将用户重定向到一个自定义的错误页面或其他 URL
创建具有错误代码重定向功能的Ingress
[root@master-01 ~]# cat error.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: error-redirect-ingress
namespace: default
annotations:
nginx.ingress.kubernetes.io/error-pages: "404,500"
nginx.ingress.kubernetes.io/custom-http-errors: "404,500"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/permanent-redirect: "https://blog.caijxlinux.work"
spec:
rules:
- host: nginx.test.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80
参数 | 解析 |
---|---|
nginx.ingress.kubernetes.io/error-pages: "404,500" | 定义了在发生错误时要捕获的 HTTP 错误代码,这里是 404 和 500 错误 |
nginx.ingress.kubernetes.io/custom-http-errors: "404,500" | Nginx Ingress Controller 如果返回 404 或 500 错误,应该进行自定义处理 |
nginx.ingress.kubernetes.io/permanent-redirect: "https://blog.caijxlinux.work" | 当错误发生时,用户会被重定向到指定的 URL(这里是"https://blog.caijxlinux.work")。这个 URL 可以是一个外部错误页面,或者集群内部的服务页面。 |
nginx.ingress.kubernetes.io/rewrite-target: / | 确保请求的路径会被重写成 /,让错误页面可以处理该路径 |
[root@master-01 ~]# kubectl create -f error.yaml
ingress.networking.k8s.io/error-redirect-ingress created
修改/etc/hosts文件,增加映射,尝试访问一个不存在的界面,可以观察到错误界面被重定向
[root@master-01 ~]# curl -I http://nginx.test.com/test
HTTP/1.1 301 Moved Permanently
Date: Wed, 04 Dec 2024 20:00:32 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
Location: https://blog.caijxlinux.work
第二种方法是修改values.yaml文件,启用defaultbackend功能(把上面方法的ingress删除掉才能使用第二种方法)
[root@master-01 ingress-nginx]# vim values.yaml
defaultBackend:
##
enabled: true
name: defaultbackend
image:
registry: registry.cn-guangzhou.aliyuncs.com
image: caijxlinux/defaultbackend-amd64
## for backwards compatibility consider setting the full image url via the repository value below
## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail
## repository:
tag: "1.5"
pullPolicy: IfNotPresent
修改ingress-nginx-controller的ConfigMap
[root@master-01 ingress-nginx]# kubectl edit cm -n ingress-nginx ingress-nginx-controller
data:
allow-snippet-annotations: "false"
apiVersion: v1
client_max_body_size: 20m
custom-http-errors: 404,503
更新Release
[root@master-01 ingress-nginx]# helm upgrade ingress-nginx -n ingress-nginx .
Release "ingress-nginx" has been upgraded. Happy Helming!
...省略部分输出...
访问不存在的界面,已经重定向到了自定义的错误界面
[root@master-01 ingress-nginx]# curl nginx.test.com/caijx
default backend - 404
Ingress Nginx SSL
使用 Nginx Ingress Controller 配置 SSL(HTTPS)是一个常见的需求,尤其是在生产环境中需要为应用程序提供加密的安全通信。可以通过配置 Ingress 资源来启用 SSL,并使用 Kubernetes 的 Secret 存储 SSL 证书和私钥
使用 OpenSSL 生成一个测试证书
[root@master-01 ~]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginx.test.com"
Generating a 2048 bit RSA private key
................................+++
.........................................................................................................................................................+++
writing new private key to 'tls.key'
-----
生成secret
[root@master-01 ~]# kubectl create secret tls ca-secret --cert=tls.crt --key=tls.key -n study-ingress
secret/ca-secret created
配置 Ingress 添加 TLS 配置
[root@master-01 ~]# vim ingress-ssl.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
namespace: study-ingress
spec:
ingressClassName: nginx # 指定使用 nginx Ingress Controller
rules:
- host: nginx.test.com # 定义主机名
http:
paths:
- path: / # 路径匹配,匹配根路径
pathType: ImplementationSpecific
backend:
service:
name: nginx # 后端服务名
port:
number: 80 # 后端服务端口
tls:
- hosts:
- nginx.test.com # 定义使用 SSL 的主机名
secretName: ca-secret # SSL 证书存储的 Secret 名称
[root@master-01 ~]# kubectl create -f ingress-ssl.yaml
ingress.networking.k8s.io/nginx-ingress created
访问http://nginx.test.com,可以观察到已经重定向到https://nginx.test.com
[root@master-01 ~]# curl -I http://nginx.test.com
HTTP/1.1 308 Permanent Redirect
Date: Wed, 04 Dec 2024 20:43:02 GMT
Content-Type: text/html
Content-Length: 164
Connection: keep-alive
Location: https://nginx.test.com
Ingress Nginx 匹配请求头
假设有一个 Web 应用,其中需要根据不同的请求头来将流量路由到不同的后端服务。具体来说,类似某些网页,针对手机端和PC端会有不同的显示和优化,Ingress可以对User-Agent字段进行区分,将流量分发到不同的后端
部署面向移动端设备的后端,并暴露80端口
[root@master-01 ~]# kubectl create deployment phone --image=registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:phone -n study-ingress
deployment.apps/phone created
[root@master-01 ~]# kubectl expose deploy phone --port=80 -n study-ingress
service/phone exposed
创建一个名为 phone 的 Ingress 资源。配置 Ingress 规则,将来自 m.test.com 域名下所有路径 (/*) 的请求转发到名为 phone 的服务的 80 端口
[root@master-01 ~]# kubectl create ingress phone --rule=m.test.com/*=phone:80 -n study-ingress
ingress.networking.k8s.io/phone created
部署面向PC端设备的后端,并暴露80端口
[root@master-01 ~]# kubectl create deployment laptop --image=registry.cn-guangzhou.aliyuncs.com/caijxlinux/nginx:laptop -n study-ingress
deployment.apps/laptop created
[root@master-01 ~]# kubectl expose deploy laptop --port=80 -n study-ingress
service/laptop exposed
创建Ingress,基于请求头中的 User-Agent 来判断请求的客户端设备类型,如果是移动设备(如 Android 或 iPhone 等),则会将请求重定向到 m.test.com,否则正常路由到名为 laptop 的服务
[root@master-01 ~]# vim laptop-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
set $agentflag 0;
if ($http_user_agent ~* "(Android|iPhone|Windows Phone|UC|Kindle)") {
set $agentflag 1;
}
if ($agentflag = 1) {
return 301 http://m.test.com;
}
name: laptop
namespace: study-ingress
spec:
ingressClassName: nginx
rules:
- host: test.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: laptop
port:
number: 80
创建资源,会提示报错
[root@master-01 ~]# kubectl create -f laptop-ingress.yaml
Error from server (BadRequest): error when creating "laptop-ingress.yaml": admission webhook "validate.nginx.ingress.kubernetes.io" denied the request: nginx.ingress.kubernetes.io/server-snippet annotation cannot be used. Snippet directives are disabled by the Ingress administrator
需要修改ingress-controller的ConfigMap,启用该注解。修改完成后重启Ingress
[root@master-01 ~]# kubectl edit configmap -n ingress-nginx ingress-nginx-controller
data:
allow-snippet-annotations: "true"
configmap/ingress-nginx-controller edited
[root@master-01 ~]# kubectl rollout restart daemonset -n ingress-nginx ingress-nginx-controller
daemonset.apps/ingress-nginx-controller restarted
成功创建Ingress资源
[root@master-01 ~]# kubectl create -f laptop-ingress.yaml
ingress.networking.k8s.io/laptop created
在/etc/hosts文件添加对应映射,通过不同的User-Agent进行访问,可以观察到如果对应的用户头为移动端设置,会自动跳转到m.test.com
[root@master-01 ~]# curl -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)" http://test.com/
<h1> Access From Laptop </h1>
[root@master-01 ~]# curl -H "User-Agent: iPhone" http://test.com/ -I
HTTP/1.1 301 Moved Permanently
Date: Wed, 04 Dec 2024 21:17:03 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
Location: http://m.test.com
Ingress Nginx 基本认证
有些网站可能需要通过密码来访问,对于这类网站可以使用 Nginx 的 basic-auth 设置密码访 问,具体方法如下,由于需要使用 htpasswd 工具,所以需要安装 httpd
使用 htpasswd 创建 foo 用户的密码
[root@master-01 ~]# htpasswd -c auth foo
New password:
Re-type new password:
Adding password for user foo
基于生成的密码文件生成Secret
[root@master-01 ~]# kubectl create secret generic basic-auth --from-file auth -n study-ingress
secret/basic-auth created
创建具有认证功能的Ingress
[root@master-01 ~]# vim ingrees-auth.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-with-auth
namespace: study-ingress
annotations:
# kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/auth-realm: "Please Input Your Username and Password"
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-type: basic
spec:
ingressClassName: nginx # for k8s >= 1.22+
rules:
- host: auth.test.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: nginx
port:
number: 80
在任意节点修改/etc/hosts文件,添加映射,此处以master-03为例
[root@master-03 ~]# vim /etc/hosts
192.168.132.173 node-02 auth.test.com
访问时不添加账号和密码,访问报错
[root@master-03 ~]# curl -v -H "Host: auth.test.com" http://192.168.132.173
* About to connect() to 192.168.132.173 port 80 (#0)
* Trying 192.168.132.173...
* Connected to 192.168.132.173 (192.168.132.173) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host: auth.test.com
>
< HTTP/1.1 401 Unauthorized
< Date: Thu, 05 Dec 2024 16:40:05 GMT
< Content-Type: text/html
< Content-Length: 172
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Please Input Your Username and Password"
<
<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.132.173 left intact
添加正确的账号和密码连接Ingress,正常访问界面
[root@master-03 ~]# curl -v -H "Host: auth.test.com" http://192.168.132.173 -u 'foo:123'
* About to connect() to 192.168.132.173 port 80 (#0)
* Trying 192.168.132.173...
* Connected to 192.168.132.173 (192.168.132.173) port 80 (#0)
* Server auth using Basic with user 'foo'
> GET / HTTP/1.1
> Authorization: Basic Zm9vOjEyMw==
> User-Agent: curl/7.29.0
> Accept: */*
> Host: auth.test.com
>
< HTTP/1.1 200 OK
< Date: Thu, 05 Dec 2024 16:42:34 GMT
< Content-Type: text/html
< Content-Length: 612
< Connection: keep-alive
< Last-Modified: Tue, 16 Apr 2019 13:08:19 GMT
< ETag: "5cb5d3c3-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>
* Connection #0 to host 192.168.132.173 left intact
Ingress Nginx 黑/白名单
在 Kubernetes 中使用 Ingress 配置 Nginx 进行 IP 黑/白名单控制,可以通过 nginx.ingress.kubernetes.io/whitelist-source-range 和 nginx.ingress.kubernetes.io/deny 注解来实现
配置黑名单禁止某一个或某一段 IP,需要在 Nginx Ingress 的 ConfigMap 中配置,比如将192.168.132.169(多个配置逗号分隔)添加至黑名单
[root@master-01 ~]# kubectl edit configmaps -n ingress-nginx ingress-nginx-controller
data:
block-cidrs: 192.168.132.169
configmap/ingress-nginx-controller edited
重启Ingress
[root@master-01 ~]# kubectl rollout restart daemonset -n ingress-nginx ingress-nginx-controller
daemonset.apps/ingress-nginx-controller restarted
在master-01节点的/etc/hosts文件添加映射,尝试访问,提示403 Forbidden
[root@master-01 ~]# curl -I auth.test.com
HTTP/1.1 403 Forbidden
Date: Thu, 05 Dec 2024 18:09:20 GMT
Content-Type: text/html
Content-Length: 146
Connection: keep-alive
[root@master-01 ~]# curl m.test.com
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>
在master-03节点可以正常访问界面(401是因为没有输入账号和密码)
[root@master-03 ~]# curl -I auth.test.com
HTTP/1.1 401 Unauthorized
Date: Thu, 05 Dec 2024 18:09:36 GMT
Content-Type: text/html
Content-Length: 172
Connection: keep-alive
WWW-Authenticate: Basic realm="Please Input Your Username and Password"
白名单表示只允许某个 IP 可以访问,直接在 yaml 文件中配置即可(也可以通过 ConfigMap 配置),比如只允许 192.168.132.170 访问,只需要添加一个 nginx.ingress.kubernetes.io/whitelist- source-range 注释即可
重写ingress-auth.yaml配置文件
[root@master-01 ~]# vim ingrees-auth.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-with-auth
namespace: study-ingress
annotations:
kubernetes.io/ingress.class: nginx # K8s >= 1.22+ 使用 ingressClassName 替代
nginx.ingress.kubernetes.io/auth-realm: "Please Input Your Username and Password"
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/whitelist-source-range: "192.168.132.170"
spec:
rules:
- host: auth.test.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: nginx
port:
number: 80
更新Ingress配置文件
[root@master-01 ~]# kubectl replace -f ingrees-auth.yaml
ingress.networking.k8s.io/ingress-with-auth replaced
在master-02的/etc/hosts文件添加映射,测试访问,可以正常访问
[root@master-02 ~]# curl -I auth.test.com
HTTP/1.1 401 Unauthorized
Date: Thu, 05 Dec 2024 18:17:43 GMT
Content-Type: text/html
Content-Length: 172
Connection: keep-alive
WWW-Authenticate: Basic realm="Please Input Your Username and Password"
在master-01和master-03节点访问,访问失败
[root@master-01 ~]# curl -I auth.test.com
HTTP/1.1 403 Forbidden
Date: Thu, 05 Dec 2024 18:18:44 GMT
Content-Type: text/html
Content-Length: 146
Connection: keep-alive
[root@master-03 ~]# curl -I auth.test.com
HTTP/1.1 403 Forbidden
Date: Thu, 05 Dec 2024 18:18:36 GMT
Content-Type: text/html
Content-Length: 146
Connection: keep-alive
Ingress Nginx 速率限制
速率限制常用于防止恶意用户对应用程序发起暴力破解攻击、保护暴露的 API,避免滥用和过度请求、防止 DDoS(分布式拒绝服务)攻击的一个有效手段、对于一些资源消耗较大的接口(如数据处理、报告生成等),可以使用速率限制来确保系统负载不会过大,避免某个请求或某类请求耗尽资源,影响整体服务稳定性。速率限制是一项重要的安全功能,帮助管理系统负载、保护应用免受恶意攻击、以及保障公平性和系统稳定性。通过 Nginx Ingress Controller 提供的注解,可以轻松地对不同的请求设置合适的速率限制,确保系统在高流量环境下的可用性和响应能力
首先没有加速率限制,使用 ab 进行访问,Failed 为 0(由于上面的实验限制,所以在master-02上测试)
[root@master-02 ~]# ab -c 10 -n 100 http://auth.test.com/ | grep requests
Complete requests: 100
Failed requests: 0
Time per request: 0.730 [ms] (mean, across all concurrent requests)
删除旧的有白名单限制的Ingress配置,添加新的具有速率限制功能的Ingress
[root@master-01 ~]# vim rate-limit.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: customer-api-ingress
namespace: study-ingress
annotations:
#nginx.ingress.kubernetes.io/limit-rps: "1" # 每秒最多 1 个请求
#nginx.ingress.kubernetes.io/limit-burst-multiplier: "1" # 突发流量最大 1 个请求
nginx.ingress.kubernetes.io/limit-connections: "1" # 每个客户最大连接数为 1
spec:
rules:
- host: auth.test.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: nginx
port:
number: 80
构建Ingress
[root@master-01 ~]# kubectl create -f rate-limit.yaml
ingress.networking.k8s.io/customer-api-ingress created
master-02进行验证,可以观察到访问已经被限制(注意:此处有一个问题,只要replace文件后,限制就会失败)
[root@master-02 ~]# ab -c 10 -n 100 http://auth.test.com/ | grep requests
Complete requests: 100
Failed requests: 77
Time per request: 1.145 [ms] (mean, across all concurrent requests)
Ingress Nginx 实现灰度/金丝雀发布
灰度发布(Grey Release)和金丝雀发布(Canary Release)都是软件部署中常见的策略,旨在通过逐步推出新版本,减少系统风险,确保发布的新版本不会对整个系统造成负面影响。这两种策略在 Kubernetes 环境下,尤其是在结合 Ingress 和 Kubernetes Deployments 时,常被用于控制流量的分配和逐步升级。
灰度发布(Grey Release):灰度发布是指将新版本的功能逐渐推送到一部分用户或流量中,而不是一次性更新所有用户。在灰度发布中,可以根据不同的用户群体、地区或者其他标识进行分阶段发布。常见的做法是,先将新版本发布给一部分用户,然后根据反馈决定是否扩大范围。
金丝雀发布(Canary Release):金丝雀发布是一种逐步部署新版本的策略,通常将一小部分流量(例如 5% 或 10%)引导到新版本,观察新版本的稳定性和性能表现。如果没有问题,再将更多流量逐步引导到新版本,最终完成全面发布。
创建模拟 Production(生产)环境的 Namespace 和服务
[root@master-01 ~]# kubectl create ns production
namespace/production created
[root@master-01 ~]# kubectl create deployment canary-v1 --image=registry.cn-guangzhou.aliyuncs.com/caijxlinux/canary:v1 -n production
deployment.apps/canary-v1 created
[root@master-01 ~]# kubectl expose deployment -n production canary-v1 --port 8080
service/canary-v1 exposed
[root@master-01 ~]# kubectl create ingress canary-v1 --rule=canary.com/*=canary-v1:8080 -n production
ingress.networking.k8s.io/canary-v1 created
检测网页状态(记得把黑名单删掉,否则在master-01节点无法测试)
[root@master-01 ~]# curl canary.com
<h1>Canary v1</h1>
创建 v2 版本,充当灰度环境
[root@master-01 ~]# kubectl create ns canary
namespace/canary created
[root@master-01 ~]# kubectl create deployment canary-v2 --image=registry.cn-
guangzhou.aliyuncs.com/caijxlinux/canary:v2 -n canary
[root@master-01 ~]# kubectl expose deploy canary-v2 --port 8080 -n canary
容器启动完成后,通过 Service 访问该服务,会返回 Canary v2
[root@master-01 ~]# kubectl get svc -n canary
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
backend-api ClusterIP 10.96.17.223 <none> 8080/TCP 9s
[root@master-01 ~]# curl 10.96.17.223:8080
<h1>Canary v2</h1>
创建具有金丝雀功能的Ingress,创建 v2 版本的 Ingress 时,需要添加两个注释,一个是 nginx.ingress.kubernetes.io/canary,表明是灰度环境,nginx.ingress.kubernetes.io/canary-weight 表明切多少流量到该环境,本示例为10%
[root@master-01 ~]# vim canary-v2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: canary-v2
namespace: canary
annotations:
nginx.ingress.kubernetes.io/canary: "true" # 启用金丝雀发布
nginx.ingress.kubernetes.io/canary-weight: "10" # 10% 的流量到新版本
spec:
ingressClassName: nginx # 对应 Nginx Ingress Controller,K8s >= 1.22+
rules:
- host: canary.com
http:
paths:
- path: / # 默认路由路径
pathType: ImplementationSpecific
backend:
service:
name: canary-v2 # 指向 canary-v2 服务
port:
number: 8080 # 服务端口
[root@master-01 ~]# Kubectl create -f canary-v2.yaml
ingress.networking.k8s.io/canary-v2 created
使用Shell脚本进行测试,此脚本会输出 v1 和 v2 的访问次数比值
[root@master-01 ~]# vim test-canary.sh
#!/bin/bash
# 初始化计数器
v1_count=0
v2_count=0
# 循环访问网站 100 次
for i in {1..100}
do
# 获取网站内容
output=$(curl -s canary.com | awk -F '[ <]' '{prinf $3}')
# 检查是否包含 v1 或 v2
if [[ "$output" == *"v1"* ]]; then
((v1_count++))
elif [[ "$output" == *"v2"* ]]; then
((v2_count++))
fi
done
# 打印结果
echo "v1 count: $v1_count"
echo "v2 count: $v2_count"
[root@master-01 ~]# chmod +x test-canary.sh
[root@master-01 ~]# sh test-canary.sh
v1 count: 91
v2 count: 9
Ingress的Service可以不修改为NodePort,依然保持LoadBalancer也是可以的
ruby脚本也可以测试金丝雀
counts = Hash.new(0)
# 执行 100 次
100.times do
# 使用 curl 访问页面,抓取输出并直接解析
output = `curl -s canary.com | grep ‘Canary’ | sed ‘s/.*<\(.*\)>/\1/’`
# 提取输出的最后一部分(假设是空格分隔的最后一个字段)
last_word = output.strip.split.last
# 统计出现次数
counts[last_word] += 1
end
# 打印统计结果
puts counts