K8S Ingress、IngressController 快速开始
假设有如下三个节点的 K8S 集群:
k8s31master 是控制节点
k8s31node1、k8s31node2 是工作节点
容器运行时是 containerd
一、理论介绍
1)什么是 Ingress
- 定义:Ingress 是 Kubernetes 中的一种资源对象,它定义了外部访问集群内服务的规则。可以将其理解为一个智能的 “流量路由器”,根据接收到的 HTTP/HTTPS 请求的不同规则,将流量转发到集群内不同的服务上。
- 作用
- 统一入口:为集群内的多个服务提供一个统一的外部入口点,使得外部用户可以通过一个固定的 IP 地址或域名来访问不同的服务,而不需要为每个服务都暴露独立的 IP 和端口。
- 规则定义:支持基于域名、URL 路径等条件来定义路由规则。例如,可以配置让域名
www.example.com
的请求被路由到服务 A,而api.example.com
的请求被路由到服务 B。还能根据 URL 路径进行更精细的路由,如/app1/*
路径的请求转发到服务 C,/app2/*
路径的请求转发到服务 D。
2)什么是 IngressController
- 定义:Ingress Controller 是实际负责执行 Ingress 资源中定义的路由规则的组件。它是一个运行在 Kubernetes 集群中的服务,通常以 Pod 的形式存在,不断监听 Ingress 资源的变化,并根据最新的规则来配置和更新负载均衡器或代理服务器。
- 作用
- 流量转发:根据 Ingress 规则,将接收到的外部流量准确地转发到对应的后端服务。它会与底层的网络基础设施进行交互,实现数据包的正确路由。
- 负载均衡:在将流量转发到后端服务时,Ingress Controller 可以实现负载均衡功能,将请求均匀地分配到多个后端 Pod 上,以提高服务的可用性和性能。常见的负载均衡算法有轮询、加权轮询、最少连接数等。
- 安全防护:许多 Ingress Controller 还支持 TLS/SSL 加密功能,用于对传输中的数据进行加密,确保通信安全。同时,它也可以进行一些基本的安全防护,如防止常见的网络攻击。
- 常见类型
- Nginx Ingress Controller:基于 Nginx 服务器的 Ingress Controller,具有高性能、丰富的功能和良好的稳定性,广泛应用于 Kubernetes 集群中。
- Traefik Ingress Controller:专注于提供自动化的服务发现和动态配置功能,能够根据 Kubernetes 集群中的服务和端点的变化自动更新路由规则。
- HAProxy Ingress Controller:基于 HAProxy 负载均衡器,具有出色的性能和可靠性,适用于大规模流量的场景。
3)局限性
4)总结
简单点说,Ingress 类似于 nginx 的配置文件,它里面定义了路由规则。
IngressController 类似于 nginx,它不断监听 Ingress 资源的变化,并根据最新的规则来配置和更新它所包装的负载均衡器或代理服务器。
二、安装 Ingress Nginx Controller
Ingress-Nginx GitHub:https://github.com/kubernetes/ingress-nginx
裸金属部署yml:https://github.com/kubernetes/ingress-nginx/blob/release-1.12/deploy/static/provider/baremetal/deploy.yaml
-
版本兼容性
因为我的 K8S 是 1.31,所以 Ingress-Nginx 可以选择 1.12.2、1.12.1、1.12.0。
-
裸金属
baremetal:
适用于 裸金属(Bare Metal)或本地环境(如物理服务器、本地 Kubernetes 集群、无云厂商集成的环境)。通常需要手动管理外部访问(如 NodePort、HostNetwork 或外部负载均衡器)。依赖 Kubernetes 的 NodePort 或 LoadBalancer 类型 Service,但需要自行配置外部负载均衡(如 MetalLB、HAProxy 等)。
-
镜像准备
整个部署只依赖两个镜像:
registry.k8s.io/ingress-nginx/controller:v1.12.2
registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.5.3
找个国内的镜像站,提前下载好镜像。
[root@k8s31node1 ~]# ctr -n=k8s.io images pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/controller:v1.12.2
[root@k8s31node1 ~]# ctr -n=k8s.io images tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/controller:v1.12.2 registry.k8s.io/ingress-nginx/controller:v1.12.2
# 查看导入情况
[root@k8s31node1 ~]# crictl images ls|grep ingress
[root@k8s31node1 ~]# ctr -n=k8s.io images pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.5.3
[root@k8s31node1 ~]# ctr -n=k8s.io images tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.5.3 registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.5.3
# 查看导入情况
[root@k8s31node1 ~]# crictl images ls|grep webhook[root@k8s31node2 ~]# ctr -n=k8s.io images pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/controller:v1.12.2
[root@k8s31node2 ~]# ctr -n=k8s.io images tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/controller:v1.12.2 registry.k8s.io/ingress-nginx/controller:v1.12.2
# 查看导入情况
[root@k8s31node2 ~]# crictl images ls|grep ingress
[root@k8s31node2 ~]# ctr -n=k8s.io images pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.5.3
[root@k8s31node2 ~]# ctr -n=k8s.io images tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.5.3 registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.5.3
# 查看导入情况
[root@k8s31node2 ~]# crictl images ls|grep webhook
-
修改 deploy.yml
- 修改镜像名称与拉取策略
image: registry.k8s.io/ingress-nginx/controller:v1.12.2 # 优先拉取本地镜像 imagePullPolicy: IfNotPresent image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.5.3 # 优先拉取本地镜像 imagePullPolicy: IfNotPresent
- 修改 Deployment 副本数
根据工作节点个数,修改副本数。
- 增加 Pod 反亲和性
affinity:podAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100podAffinityTerm:labelSelector:matchLabels:app.kubernetes.io/name: ingress-nginxtopologyKey: kubernetes.io/hostname
使用主机名作为 逻辑拓扑键,通过 Pod 反亲和性,让多个 Pod 部署在不同的节点上。
- 使用主机网络
hostNetwork: true dnsPolicy: ClusterFirstWithHostNet
hostNetwork 作用:hostNetwork 是 Pod 规范中的一个配置选项,它控制 Pod 如何使用网络命名空间。当设置为 true 时,Pod 将直接使用宿主机(Node)的网络栈,而不是创建独立的网络命名空间。
当你设置 hostNetwork: true 时,Pod 将与宿主机共享这些网络资源,这意味着:
Pod 内的应用程序将使用宿主机的 IP 地址
Pod 内的服务监听的端口将直接暴露在宿主机上
Pod 可以直接访问宿主机上的网络设备和网络配置
ClusterFirstWithHostNet 作用:当 Pod 使用主机网络(即 hostNetwork: true)时,此策略强制 Pod 使用集群 DNS 服务(CoreDNS)进行解析,而不是继承 Node 节点的 DNS 配置。 适用场景:适用于使用主机网络但仍需要访问集群内部服务的 Pod。
- 最后配置如下
-
查看 ingress-nginx-controller Pod
kubectl get pod -n=ingress-nginx -owide
两个 ingress-nginx-controller pod 分别部署在 node1、node2,且它们的 IP 共享节点 IP。
-
查看 ingressClass
kubectl get ingressClass -owide
在 Kubernetes 中,IngressClass 是一个用于管理和配置 Ingress 资源的重要概念。它允许集群管理员定义和区分不同的 Ingress 实现,使得多个 Ingress Controller 可以在同一个集群中共存,并为不同的 Ingress 资源指定特定的控制器。
IngressClass 是一个集群级别的资源,用于定义和配置 Ingress Controller。
它主要包含两个核心字段:
- controller:指定负责处理该类 Ingress 的控制器名称(例如 k8s.io/ingress-nginx)。
- parameters(可选):指向一个参数资源,用于配置特定控制器的行为。
三、使用 HTTP 访问
1)部署后端服务
apiVersion: apps/v1
kind: Deployment
metadata:name: tomcat-deploy
spec:replicas: 2selector:matchLabels:app: tomcattemplate:metadata:labels:app: tomcatspec:containers:- name: tomcatimage: tomcat:8.5-jre8-alpineimagePullPolicy: IfNotPresentports:- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:name: tomcat-svc
spec:type: ClusterIPselector:app: tomcatports:- port: 8080protocol: TCPtargetPort: 8080
使用 Deployment 控制器,部署了两个 tomcat Pod 并新建了一个 ClusterIP 类型的 Service 代理了这两个 Pod。
2)编写 Ingress
# ing ingress 的缩写
kubectl explain ing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: nginx-ingress
spec:ingressClassName: nginx # 指定使用上面看到的 "nginx" IngressClassrules: # 路由规则- host: tomcat-app.comhttp:paths:- path: /pathType: Prefixbackend:service:name: tomcat-svcport:number: 8080
- ingressClassName: 指定负责处理此 Ingress 的控制器类别(这里是nginx)。
- 路由规则(Rules):
主机规则(Host) host: tomcat-app.com 当外部请求的 HTTP Host 头为 tomcat-app.com 时,
此规则生效。host 是网络主机的完全限定域名。 host 不可以 1、不允许使用 IP 地址。 2、不识别 : 分隔符,因为不允许使用端口。
目前 Ingress 的端口默认为 http 使用 :80,https 使用 :443。
这些在未来可能会改变。host 可以是 "精确" 主机名(例如 "foo.bar.com") "通配符" 主机名:带单个通配符标签的域名(例如 ".foo.com")。
通配符字符 * 必须单独作为第一个 DNS 标签出现,并且只匹配单个标签。不能单独使用通配符标签(例如 Host == "")。路径规则(Paths) path: / 匹配所有以/开头的 URL 路径(即所有请求)。 pathType: Prefix: 指定路径匹配类型为前缀匹配,
即任何以/开头的路径都会被匹配(例如/api、/static/js/main.js等)。后端服务(Backend) service.name: tomcat-svc 将匹配的请求转发到名为 tomcat-svc 的
Kubernetes Service。service.port.number: 8080 转发到 Service 的 8080 端口
(与 tomcat-svc.spec.ports.port 相对应)。
- 整体功能
这个 Ingress 配置的作用是:
- 接收外部请求:当用户访问 http://tomcat-app.com 时,请求会首先到达 Nginx Ingress Controller。
- 路由到内部服务:控制器根据规则将请求转发到名为 tomcat-svc 的 Service 的 8080 端口。
- 负载均衡:Service 会将请求进一步负载均衡到后端的 Tomcat Pod 实例上。
3)修改 Windows hosts 文件
C:\Windows\System32\drivers\etc\hosts
# 192.168.40.20 是节点 k8s31node1 IP
# 这里也可以是 192.168.40.30
192.168.40.20 tomcat-app.com
4)浏览器访问
http://tomcat-app.com/
整个请求的过程如图:
四、使用 HTTPS 访问
1)准备证书
- 生成私钥
openssl genrsa -out tls.key 2048
- openssl:它属于一个功能丰富的加密工具包,能够支持像密钥生成、证书签署等众多密码学操作。
- genrsa:此为 openssl 的子命令,专门用于生成 RSA 私钥。
- -out tls.key:该参数的作用是将生成的私钥保存到名为 tls.key 的文件中。你可以根据自身需求,把 tls.key 替换成其他文件名。
- 2048:这里的数字代表私钥的位数。2048 位的私钥在当前属于比较安全的配置,能够有效平衡安全性和性能。
- 生成自签名 SSL/TLS 证书
openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Fujian/L=Xiamen/O=Study/CN=https-tomcat-app.com
- openssl req openssl 的子命令,用于创建和处理证书签名请求(CSR)。
- -new 生成一个新的证书请求(通常用于向 CA 机构申请证书)。
- -x509 直接生成自签名证书,而非仅生成 CSR。自签名证书无需 CA 机构签名,但浏览器会显示 “不安全” 警告(适用于测试环境)。
- -key tls.key 指定私钥文件(需提前用 genrsa 命令生成,如之前的 tls.key)。私钥用于对证书签名,需妥善保管。
- -out tls.crt 指定输出的证书文件名(.crt 或 .pem 格式)。
- -subj /C=CN/ST=Fujian/L=Xiamen/O=Study/CN=https-tomcat-app.com
- 证书的主题信息(Distinguished Name),
- 格式为:
- /C=CN:国家(Country)代码,如 CN(中国)。
- /ST=Fujian:州 / 省份(State/Province)。
- /L=Xiamen:城市 / 地区(Locality)。
- /O=Study:组织(Organization)。
- /CN=https-tomcat-app.com:域名(Common Name),必须与网站访问的域名完全一致(否则浏览器会报错)。
2)创建 Secret
- 命令帮助
# 需要创建一个 tls 类型的 Secret
# tls Transport Layer Security
kubectl create secret tls --help
- 创建 Secret
# 创建 TLS 类型 Secret
kubectl create secret tls nginx-ingress-secret --cert=tls.crt --key=tls.key
# 查看是否创建成功
kubectl get secret
# 查看详情
kubectl describe secret nginx-ingress-secret
3)编写 Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: tls-nginx-ingress
spec:ingressClassName: nginxtls:- hosts: ["https-tomcat-app.com"]secretName: nginx-ingress-secretrules:- host: https-tomcat-app.comhttp:paths:- path: /pathType: Prefixbackend:service:name: tomcat-svcport:number: 8080
# 需删除上一小节的 ingress 不然两个 ingress 都是节点 80 端口,会有问题
kubectl apply -f tls-ingress.yaml
kubectl get ing
4)修改 Windows hosts 文件
# 192.168.40.30 是节点 k8s31node2 IP
192.168.40.30 https-tomcat-app.com
5)浏览器访问
https://https-tomcat-app.com/
五、使用 keepalived + nginx 实现 Ingress 高可用
使用主机网络 hostNetwork 存在一个问题,那就是客户端持有的是节点的 IP,倘若节点挂掉之后,客户端就找不到整个集群的入口了,服务的调用更是无从谈起。所以有必要对 Ingress 进行高可用设计。具体方案如下:
- 部署方式
ingress-nginx-controller 根据 Deployment+nodeSelector+Pod 反亲和性方式 部署在 k8s 工作节点(前面已实现),ingress-nginx-controller 这些 Pod 共享宿主机IP(hostNetwork ),然后通过 keepalived+nginx 实现 ingress-nginx-controller 高可用。
- 入口
keepalived 帮我们虚拟出 VIP:192.168.40.80 作为入口,域名或主机名解析到这个 VIP
1)部署 keepalived + nginx
可以看这篇博文。
2)修改 nginx.conf
配置的路径基于 yum 安装的 nginx 目录:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;events {worker_connections 1024;
}http {include /etc/nginx/mime.types;default_type application/octet-stream;log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log /var/log/nginx/access.log main;sendfile on;keepalive_timeout 65;# 定义 ingress-nginx-controller 的 upstream(负载均衡)upstream ingress_backend {# 替换为你的 Kubernetes Node IP + NodePortserver 192.168.40.20:30927; # Node1server 192.168.40.30:30927; # Node2}# HTTP 服务器(80端口 → 强制跳转 HTTPS)server {listen 80;server_name https-tomcat-app.com;return 301 https://$host$request_uri; }# HTTPS 服务器(443端口 → 代理到 ingress_backend)server {listen 443 ssl;server_name https-tomcat-app.com;# SSL 证书配置(替换为你的证书路径)ssl_certificate /etc/nginx/ssl/tls.crt;ssl_certificate_key /etc/nginx/ssl/tls.key;# 推荐的安全配置ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';ssl_prefer_server_ciphers on;ssl_session_cache shared:SSL:10m;ssl_session_timeout 10m;# 代理到 ingress_backend(负载均衡)location / {proxy_pass https://ingress_backend; # 使用 upstreamproxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";# 可调整缓冲区、超时等proxy_buffer_size 16k;proxy_buffers 4 32k;proxy_connect_timeout 90s;proxy_send_timeout 90s;proxy_read_timeout 90s;}# 错误页面error_page 500 502 503 504 /50x.html;location = /50x.html {root /usr/share/nginx/html;}}
}
- ingress-nginx-controller 443 的 NodePort 是 30927
- 将 4.1 生成的 私钥跟证书上传到 nginx1、nginx2 的 /etc/nginx/ssl
3)修改 Windows hosts 文件
# 主机名指向 VIP
192.168.40.80 https-tomcat-app.com
4)浏览器访问
https://https-tomcat-app.com/
5)云之展望
上面是本地部署的高可用方案,云上的部署其实也是类似。
把本地DNS换成云解析,keepalived + nginx 换成云服务商 LB。