Ingress


1、介绍

在前面文章中已经提到,Service对集群之外暴露服务的主要方式有两种:NodePort和LoadBalancer,但是这两种方式,都有一定的缺点:

NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显。

LB方式的缺点是每个service需要一个LB,浪费、麻烦,并且需要kubernetes之外设备的支持。

基于这种现状,kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。工作机制大致如下图表示:

图片

在Kubernetes中,Ingress是管理Kubernetes集群内部服务的外部访问的API对象。

实际上,Ingress相当于一个7层的负载均衡器,是kubernetes对反向代理的一个抽象,它的工作原理类似于Nginx,可以理解成在Ingress里建立诸多映射规则,Ingress Controller通过监听这些配置规则并转化成Nginx的反向代理配置 , 然后对外部提供服务。在这里有两个核心概念:

  • ingress:kubernetes中的一个对象,作用是定义请求如何转发到service的规则

  • ingress controller:具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发,实现方式有很多,比如Nginx, Contour, Haproxy等等

87.png

88.png

Ingress-Nginx github 地址:https://github.com/kubernetes/ingress-nginx

Ingress-Nginx 官方网站:https://kubernetes.github.io/ingress-nginx

版本与k8s对照表

Supported Ingress-NGINX version k8s supported version Alpine Version Nginx Version Helm Chart Version
? v1.10.1 1.29, 1.28, 1.27, 1.26 3.19.1 1.25.3 4.10.1*
? v1.10.0 1.29, 1.28, 1.27, 1.26 3.19.1 1.25.3 4.10.0*
? v1.9.6 1.29, 1.28, 1.27, 1.26, 1.25 3.19.0 1.21.6 4.9.1*
? v1.9.5 1.28, 1.27, 1.26, 1.25 3.18.4 1.21.6 4.9.0*
? v1.9.4 1.28, 1.27, 1.26, 1.25 3.18.4 1.21.6 4.8.3
? v1.9.3 1.28, 1.27, 1.26, 1.25 3.18.4 1.21.6 4.8.*
? v1.9.1 1.28, 1.27, 1.26, 1.25 3.18.4 1.21.6 4.8.*
? v1.9.0 1.28, 1.27, 1.26, 1.25 3.18.2 1.21.6 4.8.*
v1.8.4 1.27, 1.26, 1.25, 1.24 3.18.2 1.21.6 4.7.*
v1.7.1 1.27, 1.26, 1.25, 1.24 3.17.2 1.21.6 4.6.*
v1.6.4 1.26, 1.25, 1.24, 1.23 3.17.0 1.21.6 4.5.*
v1.5.1 1.25, 1.24, 1.23 3.16.2 1.21.6 4.4.*
v1.4.0 1.25, 1.24, 1.23, 1.22 3.16.2 1.19.10† 4.3.0
v1.3.1 1.24, 1.23, 1.22, 1.21, 1.20 3.16.2 1.19.10† 4.2.5

部署 Ingress-Nginx

# https://kubernetes.github.io/ingress-nginx/deploy/
# 下载部署用yaml文件,大家根据自己的k8s版本更换版本号
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.6.4/deploy/static/provider/cloud/deploy.yaml

# 修改镜像地址为国内镜像地址,否则镜像拉取会失败
vim deploy.yaml

# -----------------------------------------------------------------
# 在第439行
# 将image改为registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.6.3
# 教室局域网可以使用:192.168.57.200:8099/ingress-nginx/controller:v1.6.3
# 第536行和第585行
# 将registry.k8s.io/ingress-nginx/替换为registry.aliyuncs.com/google_containers/
# 教室局域网可以使用:192.168.57.200:8099/ingress-nginx/kube-webhook-certgen:v20220916-gd32f8c343
# -----------------------------------------------------------------

kubectl apply -f deploy.yaml
# 教室局域网专用版本,针对无法使用外网的情况。(已安装,可忽略)
# wget http://192.168.57.200/Software/ingress-nginx-v1.6.3-deploy.yaml
# kubectl apply -f ingress-nginx-v1.6.3-deploy.yaml
[root@k8s-master ~]# kubectl get pod -n ingress-nginx
NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-db6jl        0/1     Completed   0          5h6m
ingress-nginx-admission-patch-m6rvd         0/1     Completed   0          5h6m
ingress-nginx-controller-7998c976fc-ln7z9   1/1     Running     0          5h6m

# 查看ingress-nginx-controller svc 注意: 80:30577/TCP,443:32667/TCP 此处 30577和32667 后续需要使用到  
[root@k8s-master ~]# kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.68.155.84   <pending>     80:30577/TC,443:32667/TCP   5h6m
ingress-nginx-controller-admission   ClusterIP      10.68.89.135   <none>        443/TCP                      5h6m

2、准备Service、Pod

为了后面的实验比较方便,创建如下图所示的模型

图片

安装上述图片模型,我们创建3个Nginx Pod和3个Tomcat Pod,并分别为他们创建servce ,yaml文件名叫tomcat-nginx.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tomcat-pod
  template:
    metadata:
      labels:
        app: tomcat-pod
    spec:
      containers:
      - name: tomcat
        image: tomcat:8.0
        ports:
        - containerPort: 8080

---

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80

---

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
  namespace: dev
spec:
  selector:
    app: tomcat-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 8080
    targetPort: 8080

执行文件并查看创建结果

# 1.先创建dev命令空间
[root@k8s-master ~]# kubectl create ns dev
# 2.创建nginx-deployment和tomcat-deployment
[root@k8s-master ~]# kubectl apply -f tomcat-nginx.yaml
deployment.apps/nginx-deployment created
deployment.apps/tomcat-deployment created
service/nginx-service created
service/tomcat-service created
[root@k8s-master ~]#
# 3.查看 deploy
[root@k8s-master ~]# kubectl get deploy -n dev
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment    2/3     3            2           7s
tomcat-deployment   3/3     3            3           7s
[root@k8s-master ~]#
# 4.查看pod
[root@k8s-master ~]# kubectl get pod -n dev
NAME                                 READY   STATUS              RESTARTS   AGE
nginx-deployment-69cbb4f6b6-2nwkd    1/1     Running             0          13s
nginx-deployment-69cbb4f6b6-lsqcp    1/1     Running             0          13s
nginx-deployment-69cbb4f6b6-rmfzc    0/1     ContainerCreating   0          13s
tomcat-deployment-798c966d9d-cg59r   1/1     Running             0          13s
tomcat-deployment-798c966d9d-s5blw   1/1     Running             0          13s
tomcat-deployment-798c966d9d-zrjdf   1/1     Running             0          13s
# 5.查看svc
[root@k8s-master ~]# kubectl get svc -n dev
NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
nginx-service    ClusterIP   None         <none>        80/TCP     17s
tomcat-service   ClusterIP   None         <none>        8080/TCP   17s

3.在本机配置host

更改本机host,模拟两个域名
# 我的master IP 为192.168.8.201 模拟 nginx域名为 nginx.lz.com
[root@k8s-master ~]# echo "192.168.8.201 nginx.lz.com" >> /etc/hosts
# 我的master IP 为192.168.8.201 模拟 tomcat域名为 tomcat.lz.com
[root@k8s-master ~]# echo "192.168.8.201 tomcat.lz.com" >> /etc/hosts
# 查看已添加hosts
[root@k8s-master ~]# cat /etc/hosts
192.168.8.201 nginx.lz.com
192.168.8.201 tomcat.lz.com

4、Http代理

创建ingress-http.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-http
  namespace: dev
spec:
  # 这个很关键,如果写错会导致访问404
  ingressClassName: nginx
  rules:
    - host: nginx.lz.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx-service
                port:
                  number: 80
    - host: tomcat.lz.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: tomcat-service
                port:
                  number: 8080

ingressClassName 如果忘记自己设置的是什么,可以通过以下方式查询

命令:

kubectl describe deploy ingress-nginx-controller -n ingress-nginx

图片

实际操作

# 创建
[root@k8s-master ~]# kubectl apply -f ingress-http.yaml
ingress.networking.k8s.io/ingress-http created

# 查看ingress
[root@k8s-master ~]# kubectl get ing ingress-http -n dev
NAME           CLASS     HOSTS                        ADDRESS   PORTS   AGE
ingress-http   nginx   nginx.lz.com,tomcat.lz.com             80      64s

# 查看ingress详情
[root@k8s-master ~]# kubectl describe ing ingress-http  -n dev
Name:             ingress-http
Labels:           <none>
Namespace:        dev
Address:
Ingress Class:    ingress
Default backend:  <default>
Rules:
  Host           Path  Backends
  ----           ----  --------
  # 可以看出域名  nginx.lz.com 代理了后端 三个nginx pod   访问
  nginx.lz.com
                 /   nginx-service:80 (172.17.169.187:80,172.17.169.190:80,172.17.36.106:80)
  # 可以看出域名  tomcat.lz.com 代理了后端 三个tomcat pod
  tomcat.lz.com
                 /   tomcat-service:8080 (172.17.169.185:8080,172.17.169.188:8080,172.17.36.105:8080)
Annotations:     <none>
Events:          <none>

# 还记得上篇文章中我们 安装完ingree-nginx 后,查看 ingress-nginx-controller service的结果吗
# 在上面我们已经安装完ingress-http 如果想在外部访问则需此处PORTS,这个端口意思是,如果ing代理的是http即80端口,则外部访问需要使用30577(随机生成的,可以自定义)端口访问,如果ing代理的是https即443端口,则外部访问需要使用32667(也是随机生成,当然可以自定义)端口访问
[root@k8s-master ~]# kubectl get svc -n ingress-nginx
NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort    10.15.245.169   <none>        80:30577/TCP,443:32667/TCP   41h
ingress-nginx-controller-admission   ClusterIP   10.0.156.229    <none>        443/TCP

# 本次实例我们的tomcat和nginx都是80 端口,所以访问的时候都需要在域名 后面增加 :30577 才可正常访问

本机访问效果

图片

图片

5、Https代理

5.1、创建证书

实际生产我们需要申请https nginx证书,这里我们就模拟创建一个证书

# 创建证书
[root@k8s-master ~]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=lz.com"
Generating a 2048 bit RSA private key
...........+++
..............................+++
writing new private key to 'tls.key'
-----

# 证书已创建
[root@k8s-master ~]# ls
tls.crt  tls.key

# 创建密钥 这个秘钥创建,如果不会先记着,后续有讲解
[root@k8s-master ~]# kubectl create secret tls tls-secret --key tls.key --cert tls.crt
secret/tls-secret created

# 查看已创建秘钥
[root@k8s-master ~]# kubectl get secret
NAME         TYPE                DATA   AGE
tls-secret   kubernetes.io/tls   2      6s

# 5.2、创建ingress-https.yaml
vim ingress-https.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-https
  namespace: dev
spec:
  tls:
    - hosts:
        - nginx.lz.com
        - tomcat.lz.com
      secretName: tls-secret # 指定秘钥
  # 这个很关键,如果写错会导致访问404
  ingressClassName: nginx
  rules:
    - host: nginx.lz.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx-service
                port:
                  number: 80
    - host: tomcat.lz.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: tomcat-service
                port:
                  number: 8080

5.3、实例操作效果

# 创建
[root@k8s-master ~]# kubectl apply -f ingress-https.yaml
ingress.networking.k8s.io/ingress-https created
[root@k8s-master ~]#
# 查看ing
[root@k8s-master ~]# kubectl get ing ingress-https -n dev
NAME            CLASS           HOSTS                        ADDRESS   PORTS     AGE
ingress-https   ingress-nginx   nginx.lz.com,tomcat.lz.com             80, 443   7s
# 查看ing 详情
[root@k8s-master ~]# kubectl describe ing ingress-https -n dev
Name:             ingress-https
Labels:           <none>
Namespace:        dev
Address:
Ingress Class:    ingress-nginx
Default backend:  <default>
# TLS已关联
TLS:
  tls-secret terminates nginx.lz.com,tomcat.lz.com
Rules:
  # 规则也已经创建
  Host           Path  Backends
  ----           ----  --------
  nginx.lz.com
                 /   nginx-service:80 (172.17.169.187:80,172.17.169.190:80,172.17.36.106:80)
  tomcat.lz.com
                 /   tomcat-service:8080 (172.17.169.185:8080,172.17.169.188:8080,172.17.36.105:8080)
Annotations:     <none>
Events:          <none>

# 查看访问443 需要什么用什么端口  此处是32667
[root@k8s-master ~]# kubectl get svc -n ingress-nginx
NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort    10.15.245.169   <none>        80:30577/TCP,443:32667/TCP   41h
ingress-nginx-controller-admission   ClusterIP   10.0.156.229    <none>        443/TCP

# 在本地访问 https://tomcat.lz.com:32667  和 https://nginx.lz.com:32667  查看效果

图片

图片

Ingress-Nginx 实战

一、安装Ingress-Nginx控制器

1. 部署Ingress-Nginx(兼容K8s 1.23+)

# 部署稳定版控制器(v1.2.1)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.2.1/deploy/static/provider/cloud/deploy.yaml

# 等待控制器就绪(约2-3分钟)
kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=120s

2. 确认部署状态

# 查看命名空间下的Pod(应在2个node上调度)
kubectl get pods -n ingress-nginx -o wide

# 查看Service(记录80端口对应的NodePort,如31234)
kubectl get svc -n ingress-nginx ingress-nginx-controller

二、部署Nginx和Tomcat服务

1. 部署Nginx应用

创建nginx-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app
spec:
  replicas: 2  # 2个副本,分布在2个node上
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
        - containerPort: 80
        # 自定义首页内容,便于区分
        command: ["/bin/sh", "-c"]
        args:
          - echo "<h1>Welcome to Nginx Service</h1>" > /usr/share/nginx/html/index.html;
            nginx -g "daemon off;";
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80
  type: ClusterIP  # 内部服务,无需外部暴露

2. 部署Tomcat应用

创建tomcat-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: tomcat
  template:
    metadata:
      labels:
        app: tomcat
    spec:
      containers:
      - name: tomcat
        image: tomcat:9.0
        ports:
        - containerPort: 8080
        # 自定义欢迎页
        command: ["/bin/sh", "-c"]
        args:
          - echo "<h1>Welcome to Tomcat Service</h1>" > /usr/local/tomcat/webapps/ROOT/index.html;
            catalina.sh run;
---
apiVersion: v1
kind: Service
metadata:
  name: tomcat-svc
spec:
  selector:
    app: tomcat
  ports:
  - port: 80
    targetPort: 8080  # Tomcat默认端口是8080
  type: ClusterIP

3. 执行部署

kubectl apply -f nginx-deploy.yaml
kubectl apply -f tomcat-deploy.yaml

# 验证部署(确认Pod分布在2个node上)
kubectl get pods -o wide

三、配置Ingress路由规则

创建ingress-config.yaml(通过路径区分Nginx和Tomcat):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-tomcat-ingress
  annotations:
    # 路径重写:去掉前缀后转发到服务
    nginx.ingress.kubernetes.io/rewrite-target: /
    # 启用负载均衡(多副本时生效)
    nginx.ingress.kubernetes.io/load-balance: round_robin
spec:
  ingressClassName: nginx  # 指定Ingress控制器类型
  rules:
  - http:  # 无域名,使用IP+路径访问
      paths:
      - path: /nginx  # 访问路径:IP:端口/nginx → 路由到Nginx
        pathType: Prefix
        backend:
          service:
            name: nginx-svc
            port:
              number: 80
      - path: /tomcat  # 访问路径:IP:端口/tomcat → 路由到Tomcat
        pathType: Prefix
        backend:
          service:
            name: tomcat-svc
            port:
              number: 80

应用配置:

kubectl apply -f ingress-config.yaml

# 查看Ingress规则
kubectl get ingress nginx-tomcat-ingress

四、访问测试

1. 获取访问地址

# 获取任意node节点的IP(选一个能从外部访问的node)
NODE_IP=$(kubectl get nodes node-1 -o jsonpath='{.status.addresses[0].address}')  # 替换node-1为实际节点名
# 获取Ingress的NodePort端口
INGRESS_PORT=$(kubectl get svc -n ingress-nginx ingress-nginx-controller -o jsonpath='{.spec.ports[0].nodePort}')

echo "Nginx访问地址:http://$NODE_IP:$INGRESS_PORT/nginx"
echo "Tomcat访问地址:http://$NODE_IP:$INGRESS_PORT/tomcat"

2. 测试访问

# 测试Nginx服务
curl http://$NODE_IP:$INGRESS_PORT/nginx
# 预期输出:<h1>Welcome to Nginx Service</h1>

# 测试Tomcat服务
curl http://$NODE_IP:$INGRESS_PORT/tomcat
# 预期输出:<h1>Welcome to Tomcat Service</h1>

也可在浏览器中访问上述地址,验证页面内容。

五、负载均衡验证(可选)

由于两个服务都部署了2个副本(分布在不同node上),可验证负载均衡效果:

  1. 查看Nginx Pod的IP:

bash kubectl get pods -l app=nginx -o jsonpath='{range .items[*]}{.status.podIP}{"\n"}{end}'

  1. 多次访问并查看日志(确认请求分发到不同Pod):

```bash # 连续访问5次 for i in {1..5}; do curl http://$NODE_IP:$INGRESS_PORT/nginx; done

# 查看每个Nginx Pod的访问日志 for pod in $(kubectl get pods -l app=nginx -o jsonpath='{.items[*].metadata.name}'); do echo "Logs from $pod:" kubectl logs $pod | grep "GET /" done ```

六、配置说明

  1. 多节点调度:通过replicas: 2确保服务分布在2个node上,提高可用性。
  2. 路径路由:通过/nginx/tomcat路径区分两个服务,无需域名。
  3. 负载均衡:Ingress-Nginx默认会对多副本服务进行轮询负载均衡。
  4. 路径重写rewrite-target: /确保服务收到的是根路径请求(而非带前缀的路径)。

如果需要通过域名访问(如nginx.example.comtomcat.example.com),只需在Ingress规则中添加host字段并配置本地hosts解析即可。