文章目录
- overview
- Ingress 是什么?
- 为什么使用 Ingress?
- 我们会在这里做些什么?
- HTTP 服务器(Nginx)还能做什么?
- Kubernetes 中的简单示例:
- A) 使用 Service ClusterIP
- B) 手动配置 Nginx 服务作为代理
- C) 使用 Kubernetes Ingress
- Kubernetes Ingress配置示例
- Kubernetes Ingress 示例 / 不同的命名空间
- 如何优化 Ingress Nginx 配置?
- 检查日志 / Nginx
- 使用 Curl 测试配置
- 重定向方法 / Ingress 规则
- SSL / HTTPS
- 总结
overview
在了解ingress之前,首先要先了解service的概念,可以阅读我之前的文章
循序渐进kubenetes Service(Cluster ip、Nodeport、Loadbalancer)
Ingress 是什么?
Kubernetes Ingress 并不是 Kubernetes Service,而是一种配置方式,用于管理外部请求到集群内部服务(如 ClusterIP 类型服务)的路由。简单来说,Ingress 通过配置规则,将外部流量引导至内部的服务。而作为流量入口的 Nginx Pod,通常需要依赖 Kubernetes Service(通常是 LoadBalancer 类型)来实现对外暴露的功能。
理解 Ingress 的工作原理是配置和使用的基础。理清这些概念有助于明确实际需求,从而判断是否需要使用 Ingress。
接下来的示例将展示 Ingress 的配置方法,并以实际应用为基础展开说明。
为什么使用 Ingress?
使用 Ingress 是为了让内部服务能够从集群外部访问。这可以节省宝贵的静态 IP 地址,因为我们不需要为多个服务声明 LoadBalancer 类型的服务。此外,Ingress 还支持更丰富的配置选项,并提供更简便的管理方式,正如我们接下来将看到的那样。
我们会在这里做些什么?
A) 简要介绍 HTTP 服务器的基本概念,特别是 Nginx 的功能及其常见用途。
B) 演示如何手动配置 Ingress,不依赖 Kubernetes 提供的 Ingress 资源实现类似功能。
C) 解析 Kubernetes Ingress 的实质,它本质上是一个预先配置好的 Nginx 服务器,用于简化路由配置和请求转发。
为了更好地理解这些概念,接下来的内容将以分步方式说明。
A) 探索简单的 HTTP 服务器世界
在容器化技术、Kubernetes 和现代云计算普及之前,HTTP 服务器在网络架构中扮演了基础角色。以 Nginx 为例,其主要功能包括:
- 接收通过 HTTP 协议发送的请求。
- 检查系统中的文件路径是否匹配请求内容。
- 如果匹配文件存在,将其作为响应返回给客户端。
这种简单高效的处理方式,构成了后续复杂服务和云技术发展的基础。
在 Nginx 中,我们可以通过如下配置来做到这一点:
location /folder {root /var/www/;index index.html;
}
HTTP 服务器(Nginx)还能做什么?
它可以接受针对某些文件路径的请求,并将这些请求转发到其他服务器(这意味着 Nginx 可以充当代理),然后将该服务器的响应重定向回客户端。对客户端来说,一切都没有变化,接收到的结果仍然是请求的文件(如果存在)。
在 Nginx 中,可以使用如下示例配置重定向代理:
location /folder {proxy_pass http://second-nginx-server:8000;
}
这意味着 Nginx 可以通过充当代理来提供文件系统中的文件或将响应重定向到其他服务器并返回它们的响应。
Kubernetes 中的简单示例:
A) 使用 Service ClusterIP
再次强调,从这一点开始,我们需要理解 Kubernetes 中的 Service。假设我们有两个 worker 节点,这里暂时忽略 master 节点。我们有两个 Service:service-nginx 和 service-python,它们分别指向不同的 Pod。
Service 并不运行在特定的节点上,可以说它在我们集群的“任何地方都可用”。
你需要理解这里发生的事情。在我们的集群内部,我们可以通过它们的 Service 访问 Nginx 和 Python 的 Pod。接下来,我们还希望让它们能够从集群外部访问。因此,我们将它们转换为 Service LoadBalancer。
使用 Service LoadBalancer
你可以看到,我们将 Service ClusterIP 转换为 Service LoadBalancer。假设我们的 Kubernetes 集群托管在云提供商上,因为要使用 Service LoadBalancer,Kubernetes 集群必须托管在支持外部负载均衡功能的云提供商(如 Gcloud、AWS、DigitalOcean 等)上。
转换后,它会创建两个外部负载均衡器,将请求引导到我们的外部 Node IP,然后再进一步将其引导到内部的 Service ClusterIP。
我们看到有两个 LoadBalancer,每个都有自己的公共 IP。如果我们向 22.33.44.55 发送请求,它会被转发到内部的 service-nginx。如果我们向 77.66.55.44 发送请求,它会被转发到内部的 service-python。
但是公共 IP 地址非常稀缺,而负载均衡器的成本取决于云服务提供商。现在想象一下,如果我们不仅有两个而是更多的内部服务需要创建 LoadBalancer,那么费用将会大幅增加。
有没有其他解决方案可以让我们只使用一个 LoadBalancer(带一个 IP 地址),但仍然能够直接访问我们所有的内部服务?
我们先通过一种手动方法(非 Kubernetes)来探讨这个问题。
B) 手动配置 Nginx 服务作为代理
B) 手动配置 Nginx 服务作为代理
正如之前提到的,Nginx 可以作为一个代理。在下图中,我们看到一个名为 service-nginx-proxy 的新 Service,它曾是我们唯一的 Service LoadBalancer。
service-nginx-proxy 仍然会指向一个或多个 Nginx Pod 端点,但为了简化,这里没有在图中显示这些端点。
我们可以看到,我们现在只使用了一个 LoadBalancer(11.22.33.44),但通过不同的 HTTP URL 来区分请求。请求用黄色表示,目标相同,只是内容(请求的 URL)不同。
service-nginx-proxy 会根据请求的 URL 使用 Nginx 的代理功能决定将请求转发到哪个服务。
在这种情况下,我们有两个选项:
- 红色请求重定向到 service-nginx。
- 蓝色请求重定向到 service-python。
location /folder {proxy_pass http://service-nginx:3001;
}
location /other {proxy_pass http://service-python:3002;
}
目前,我们需要手动配置 service-nginx-proxy,例如创建 Nginx 配置文件并指向我们的 Service ClusterIP。
由于这是一个通用的解决方案,Kubernetes 提供了 Ingress,以使配置更加简单且易于管理。
C) 使用 Kubernetes Ingress
对比下图与之前的图。变化并不大,在这里,我们使用了通过 YAML 文件预先配置好的 Kubernetes Ingress。
与手动方式相比,这种方法可以节省大量的工作量。
现在让我们继续看一些配置示例。
安装 Kubernetes Ingress 控制器
Kubernetes Ingress 是一个可以通过以下方式安装的 Kubernetes 附加资源:
部署 Ingress NGINX 控制器的基本组件
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml
使用以下命令,我们可以看到安装在 ingress-nginx 命名空间中的 k8s 资源:
kubeuser@k8smaster:~/yaml$ kubectl get svc,pod --namespace=ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.106.67.131 172.19.6.5 80:31463/TCP,443:31658/TCP 53m
service/ingress-nginx-controller-admission ClusterIP 10.107.97.6 <none> 443/TCP 53mNAME READY STATUS RESTARTS AGE
pod/ingress-nginx-admission-create-8nr4v 1/1 Running 0 53m
pod/ingress-nginx-admission-patch-hkqc7 1/1 Running 2 53m
pod/ingress-nginx-controller-7d56585cd5-28bhl 1/1 Running 0 53m
注意,如果是在内网环境,service的externalIP会返回为空,那是因为没有云端环境提供loadbalancer IP,这种情况下,可以为Service指定externalIP
kubectl patch svc ingress-nginx-controller -n ingress-nginx -p '{"spec":{"externalIPs":["172.19.6.5"]}}'
同时,我们为了ingress controller能够跨节点路由,也需要修改路由策略
kubectl patch svc ingress-nginx-controller -n ingress-nginx -p '{"spec":{"externalTrafficPolicy":"Cluster"}}'
我们可以看到一个正常工作的 Service LoadBalancer,它具有外部 IP,并且相关的 Pod 正在运行。我们甚至可以通过执行以下命令运行 kubectl exec 进入该 Pod,查看其中的配置内容,发现它已经预先配置了一个 Nginx 服务器:
kubectl exec -it -n ingress-nginx ingress-nginx-controller-7d56585cd5-28bhl -- /bin/bash
Mem: 2833712K used, 393868K free, 8076K shrd, 135072K buff, 1887712K cached
CPU: 1% usr 0% sys 0% nic 97% idle 0% io 0% irq 0% sirq
Load average: 0.05 0.06 0.09 1/454 67PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND25 19 www-data S 126m 4% 0 0% nginx: worker process7 1 www-data S 1246m 39% 0 0% /nginx-ingress-controller --publish-service=ingress-nginx/ingress-nginx-controller --election-id=ingress-nginx-leader --controller-class=k8s.io60 0 www-data S 1026m 32% 0 0% /bin/bash67 60 www-data R 1025m 32% 0 0% top19 7 www-data S 114m 4% 0 0% nginx: master process /usr/bin/nginx -c /etc/nginx/nginx.conf26 19 www-data S 112m 4% 0 0% nginx: cache manager process1 0 www-data S 220 0% 0 0% /usr/bin/dumb-init -- /nginx-ingress-controller --publish-service=ingress-nginx/ingress-nginx-controller --election-id=ingress-nginx-leader --c
在 nginx.conf 中,我们将看到各种重定向代理设置和其他相关配置。
Kubernetes Ingress配置示例
准备测试环境:
创建deployment
service-python deployment yaml示例内容:
cat <<EOF | kubectl apply -n default --dry-run=server -f -
apiVersion: apps/v1
kind: Deployment
metadata:name: service-python
spec:replicas: 1selector:matchLabels:app: service-pythontemplate:metadata:labels:app: service-pythonspec:containers:- name: nginximage: nginx:latestports:- containerPort: 80volumeMounts:- name: web-contentmountPath: /usr/share/nginx/htmlvolumes:- name: web-contentconfigMap:name: service-python-config
---
apiVersion: v1
kind: ConfigMap
metadata:name: service-python-config
data:index.html: |<html><head><title>Hello service-python</title></head><body><h1>Hello, world!</h1></body></html>
EOF
service-nginx deployment yaml示例内容:
cat <<EOF | kubectl apply -n default -f -
apiVersion: apps/v1
kind: Deployment
metadata:name: service-nginx
spec:replicas: 1selector:matchLabels:app: service-nginxtemplate:metadata:labels:app: service-nginxspec:containers:- name: nginximage: nginx:latestports:- containerPort: 80volumeMounts:- name: web-contentmountPath: /usr/share/nginx/htmlvolumes:- name: web-contentconfigMap:name: service-nginx-config
---
apiVersion: v1
kind: ConfigMap
metadata:name: service-nginx-config
data:index.html: |<html><head><title>Hello service-nginx</title></head><body><h1>Hello, service-nginx!</h1></body></html>
EOF
创建service
python-service service yaml示例内容
cat <<EOF |kubectl apply -n default -f -
apiVersion: v1
kind: Service
metadata:name: python-service
spec:selector:app: service-pythonports:- protocol: TCPport: 81targetPort: 80
EOF
nginx-service service yaml示例内容
cat <<EOF |kubectl apply -n default -f -
apiVersion: v1
kind: Service
metadata:name: nginx-service
spec:selector:app: service-nginxports:- protocol: TCPport: 81targetPort: 80
EOF
创建ingress
我们正在使用的示例的 Ingress yaml 示例如下所示:
cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:kubernetes.io/ingress.class: nginxnginx.ingress.kubernetes.io/rewrite-target: "/"namespace: defaultname: test-ingress
spec:ingressClassName: nginxrules:- http:paths:- path: /folderpathType: Prefixbackend:service:name: nginx-service port:number: 81- http:paths:- path: /otherpathType: Prefixbackend:service:name: python-service port:number: 81
EOF
注意nginx.ingress.kubernetes.io/rewrite-target: "/"选项,那是因为上面的示例deployment中部署的nginx pod,均只保留了默认设定,故这里使用此选项做转向设定以便示范过程简单化,当然,你也可以不使用nginx.ingress.kubernetes.io/rewrite-target: “/”,同时kubelet exec进入到pod内部,自定义nginx的路径
location / {root /usr/share/nginx/html;index index.html index.htm;}
测试1:
kubeuser@k8sworker1:~$ curl http://172.19.6.5/folder
<html>
<head><title>Hello service-nginx</title></head>
<body>
<h1>Hello, service-nginx!</h1>
</body>
</html>
测试2:
kubeuser@k8sworker1:~$ curl http://172.19.6.5/other
<html>
<head><title>Hello service-python</title></head>
<body>
<h1>Hello,service-python!</h1>
</body>
</html>
上述两个测试将分别返回"Hello, service-nginx!"与”Hello,service-python!“
上面的例子中两个服务 nginx-service 和 python-service,它们都在 default 命名空间中。所以我们可以创建一个 Ingress 配置文件来路由请求:
这里,http://172.19.6.5/folder会路由到 nginx-service,而 http://172.19.6.5 会路由到 python-service。所有流量会使用同一个loadbalancer的 IP 地址。
假如感兴趣,也可以使用kubectl exec 进入到ingress controller的pod内部,查看nginx的设定内容
kubectl exec -it -n ingress-nginx ingress-nginx-controller-7d56585cd5-mvwsq -- /bin/bash
将会看到,之前ingress内设定的路径已经注册到ingress controller的pod内
ingress-nginx-controller-7d56585cd5-mvwsq:/etc/nginx$ cat /etc/nginx/nginx.conf|grep "location"location ~* "^/folder" {set $location_path "/folder";location ~* "^/other" {set $location_path "/other";location ~* "^/" {set $location_path "";
Kubernetes Ingress 示例 / 不同的命名空间
在 Kubernetes 中,Ingress 是用来管理外部访问服务的入口。前面的示例,ingress都是引用同namespace中的service,但是,Ingress 配置中默认只能引用与它在同一个命名空间内的服务。如果有一个服务位于不同的命名空间,而你希望通过 Ingress 转发流量到这个服务,就会遇到限制。
因此,需要找到一种方法来解决这个问题,使得 Ingress 可以访问其他命名空间中的服务。
基于这个原因,我们需要创建两个 Ingress 配置文件。例如:
service-nginx 仍然位于default命名空间:
cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:kubernetes.io/ingress.class: nginxnginx.ingress.kubernetes.io/rewrite-target: "/"namespace: defaultname: ingress1
spec:ingressClassName: nginxrules:- http:paths:- path: /folderpathType: Prefixbackend:service:name: nginx-service port:number: 81
EOF
然后service-python位于命名空间web中:
cat <<EOF |kubectl apply -f -
apiVersion: v1
kind: Service
metadata:name: python-servicenamespace: web
spec:selector:app: service-pythonports:- protocol: TCPport: 81targetPort: 80
EOF
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:name: service-pythonnamespace: web
spec:replicas: 1selector:matchLabels:app: service-pythontemplate:metadata:labels:app: service-pythonspec:containers:- name: nginximage: nginx:latestports:- containerPort: 80volumeMounts:- name: web-contentmountPath: /usr/share/nginx/htmlvolumes:- name: web-contentconfigMap:name: service-python-config
---
apiVersion: v1
kind: ConfigMap
metadata:name: service-python-confignamespace: web
data:index.html: |<html><head><title>Hello service-python</title></head><body><h1>Hello, service-python</h1></body></html>
EOF
cat <<EOF |kubectl apply -f -
apiVersion: v1
kind: Service
metadata:name: python-servicenamespace: web
spec:selector:app: service-pythonports:- protocol: TCPport: 81targetPort: 80
EOF
如何优化 Ingress Nginx 配置?
我们可以利用 Kubernetes Ingress 资源中的annotations添加注解来实现。例如,我们可以配置一些通常在 Nginx 中可以直接配置的选项:
kind: Ingress
metadata:name: ingressannotations:kubernetes.io/ingress.class: nginxnginx.ingress.kubernetes.io/proxy-connect-timeout: '30'nginx.ingress.kubernetes.io/proxy-send-timeout: '500'nginx.ingress.kubernetes.io/proxy-read-timeout: '500'nginx.ingress.kubernetes.io/send-timeout: "500"nginx.ingress.kubernetes.io/enable-cors: "true"nginx.ingress.kubernetes.io/cors-allow-methods: "*"nginx.ingress.kubernetes.io/cors-allow-origin: "*"
甚至可以制定非常具体的规则,例如:
nginx.ingress.kubernetes.io/configuration-snippet: |if ($host = 'www.test.com' ) {rewrite ^ https://test.com$request_uri permanent;}
这些注解(Annotations)随后会被转换为 Nginx 的配置。我们可以通过使用 kubectl exec 命令手动进入 Nginx 的 Pod 中查看这些配置。
检查日志 / Nginx
要排查问题或错误,也可以查看 Ingress 的日志:
kubectl logs ingress-nginx-controller-7d56585cd5-mvwsq -n ingress-nginx
10.0.0.169 - - [02/Jan/2025:10:13:27 +0000] "GET /other HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" 557 0.001 [web-python-service-81] [] 10.0.1.135:80 0 0.001 304 2317f913f2a6a56c9bce94749fde97ec
使用 Curl 测试配置
如果我们想测试 Ingress / Nginx 的重定向规则,建议使用 curl -v yourhost.com 而不是使用浏览器,以避免缓存等问题。
重定向方法 / Ingress 规则
在之前的示例中,我们使用了类似 /folder 或 /other/directory 的路径,将其重定向到不同的服务。这被称为“路径列表(A list of paths)”。
我们也可以使用域名或子域名来区分各个请求的重定向。例如,将 api.myurl.com 和 web.myurl.com 分别指向不同的内部 ClusterIP 服务。看起来可以像这样:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:kubernetes.io/ingress.class: nginx # 使用 NGINX Ingress 控制器nginx.ingress.kubernetes.io/rewrite-target: /name: ingress-host-samplenamespace: web
spec:ingressClassName: nginxrules:- host: api.test.com #測試用域名,或使用 IP 地址(需要設置 hosts 文件)- http:paths:- backend:service:name: python-serviceport:number: 81path: /otherpathType: Prefix
status:loadBalancer:ingress:- ip: 172.19.6.5
此示例展示了如何针对特定域名,将不同的 HTTP 路径路由到不同的内部服务。
SSL / HTTPS
SSL 是一种常见的加密协议,可用于实现安全的 HTTPS 访问。Kubernetes Ingress 提供了 “TLS Termination" 功能,负责处理所有 SSL 通信,解密或终止 SSL 请求,并将解密后的请求转发至内部服务。
当多个内部服务共享相同的 SSL 证书(包括通配符证书)时,该功能尤为高效。只需在 Ingress 上配置一次 SSL 证书,无需在每个内部服务上单独配置。Ingress 支持使用预先配置的 Kubernetes TLS Secret 来存储和管理 SSL 证书。
本例使用自签名证书与密钥
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:annotations:nginx.ingress.kubernetes.io/rewrite-target: /name: ingress-host-samplenamespace: web
spec:ingressClassName: nginxtls:- hosts:- api.test.comsecretName: test-tls-secretrules:- host: api.test.comhttp:paths:- path: /otherpathType: Prefixbackend:service:name: python-serviceport:number: 81
我们只需要确保,如果在不同的命名空间中有多个Ingress 资源,那么TLS Secret(金钥)也必须在所有定义了使用该金钥的Ingress 资源的命名空间中可用。
总结
Kubernetes Ingress 的核心功能是提供一种高效的方式来配置 Nginx 等反向代理服务器,从而将外部请求路由到集群内部的服务。这种机制能够减少对静态 IP 和负载均衡器资源的需求,有助于节省成本和资源。
Ingress 本身并不是 Kubernetes 服务的一种,而是一种管理外部访问的规则集合。它通常与负载均衡器类型的服务协同工作,借助服务将请求引导至目标应用。此外,除了基于 Nginx 的实现,还有一些其他类型的 Ingress 控制器,它们可能使用不同的代理技术来实现类似功能。