乔克视界 乔克视界
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • Kubernetes
  • Prometheus
  • ELK
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档

乔克

云原生爱好者
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • Kubernetes
  • Prometheus
  • ELK
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档
  • Docker

  • Golang

  • AIOps

  • Python

  • DevOps

  • Kubernetes

    • 什么是云原生?
    • Docker容器技术
    • Kubernetes简介
    • Kubernetes核心对象
    • Kubernetes集群管理
    • Kubernetes权限管理
    • Kubernetes工作负载管理
    • Kubernetes调度管理
    • Kubernetes应用质量管理
    • Kubernetes数据持久化管理
    • Kubernetes应用访问管理
      • Service
        • ClusterIP
        • NodePort
        • LoadBalancer
        • OpenELB
        • ExternalName
        • Headless Service
      • Ingress
        • 安装 Nginx Ingress Controller
        • 暴露第一个服务
        • 进阶使用
        • 使用 SSL 证书
        • 域名重定向
        • 地址重写
        • 认证访问
        • 黑白名单
        • 访问限速
        • 灰度发布
      • 总结
    • Kubernetes应用配置管理
    • Kubernetes有状态应用管理
    • Kubernetes 网络管理
    • Helm 应用包管理
  • Prometheus

  • ELK

  • 专栏
  • Kubernetes
乔克
2025-07-19
目录

Kubernetes应用访问管理

在 Kubernetes 中,如果仅仅是单纯的部署 Pod,部署 Deployment,并没有任何意义,因为我们最终的目的是要为应用和用户提供服务。

在 Kubernetes 中,提供了 Service 和 Ingress 两种对象来实现应用间访问或外部对集群应用访问,这两种对象在实际的工作中会时长使用,非常重要的对象。

# Service

对于 kubernetes 整个集群来说,Pod 的地址也可变的,也就是说如果一个 Pod 因为某些原因退出了,而由于其设置了副本数 replicas 大于 1,那么该 Pod 就会在集群的任意节点重新启动,这个重新启动的 Pod 的 IP 地址与原 IP 地址不同,这对于业务来说,就不能根据 Pod 的 IP 作为业务调度。kubernetes 就引入了 Service 的概念,它为 Pod 提供一个入口,主要通过 Labels 标签来选择后端 Pod,这时候不论后端 Pod 的 IP 地址如何变更,只要 Pod 的 Labels 标签没变,那么 业务通过 service 调度就不会存在问题。

当声明 Service 的时候,会自动生成一个 cluster IP,这个 IP 是虚拟 IP。我们就可以通过这个 IP 来访问后端的 Pod,当然,如果集群配置了 DNS 服务,比如现在的 CoreDNS,那么也可以通过 Service 的名字来访问,它会通过 DNS 自动解析 Service 的 IP 地址。

Service 的类型有 4 种,Cluster IP,LoadBalance,NodePort,ExternalName。其中 Cluster IP 是默认的类型。

(1)、Cluster IP:通过 集群内部 IP 暴露服务,默认是这个类型,选择该值,这个 Service 服务只能通过集群内部访问;

(2)、LoadBalance:使用云提供商的负载均衡器,可以向外部暴露服务,选择该值,外部的负载均衡器可以路由到 NodePort 服务和 Cluster IP 服务;

(3)、NodePort:顾名思义是 Node 基本的 Port,如果选择该值,这个 Service 可以通过 NodeIP:NodePort 访问这个 Service 服务,NodePort 会路由到 Cluster IP 服务,这个 Cluster IP 会通过请求自动创建;

(4)、ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容,没有任何类型代理被创建,可以用于访问集群内其他没有 Labels 的 Pod,也可以访问其他 NameSpace 里的 Service。

kubernetes 主要通过 kube-proxy 创建 iptables 和 ipvs 规则,在每个 Node 节点上都会创建这些规则。

af65f582245b49a68eaf4a2f6924a11b MD5

# ClusterIP

ClusterIP 是 Service 默认的类型,只能在集群的内部访问,也是工作中最常用的一个类型,定义如下:

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
spec:
  type: ClusterIP
  selector:
    app: nginx
  ports:
    - name: http
      port: 80
1
2
3
4
5
6
7
8
9
10
11
12
13

其中:

  • type 用户指定 Service 类型
  • ports 用户指定 Service 的端口
  • selector 用户选择后端的 Pod

如果 Service 暴露的端口和后端的端口不一致,还可以这样写:

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
spec:
  type: ClusterIP
  selector:
    app: nginx
  ports:
    - name: http
      port: 80
      targetPort: 8080
1
2
3
4
5
6
7
8
9
10
11
12
13
14

targetPort 用于指定后端的 Pod,可以直接指定端口,也可以指定后端声明的端口名字,前提是后端必须声明端口名,如下:

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

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
spec:
  type: ClusterIP
  selector:
    app: nginx
  ports:
    - name: http
      port: 80
      targetPort: http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

如上定义好 Service 并创建过后,会在集群生成对应的 Service 和 Endpoints,如下:

kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
nginx-svc    ClusterIP   10.103.74.70   <none>        80/TCP     12m
kubectl get endpoints
NAME            ENDPOINTS                                            AGE
nginx-svc       172.16.51.208:80,172.16.51.209:80,172.16.51.237:80   12m
1
2
3
4
5
6

可以看到,endpoints 的名字和 service 的名字一样,而且我们并没有创建 endpoints,是由 Kubernetes 自己创建的。其背后的逻辑是:当我们新增 Pod 或者删除 Pod,是从 Endpoints 里添加或者剔除,Service 本身是不改变的,在同一个 namespace 下,Service 和 Endpoints 是通过名字进行关联的。

所以,Endpoints 其实也是一个对象,除了由 Kubernetes 生成还可以自己定义。在实际工作,有些场景是需要自定义 Endpoints 的,比如在集群外部署了一个 Redis 服务,集群内部想通过 Service 的方式进行访问,这时候就可以通过自定义 Endpints 的方式实现,如下:

kind: Service
apiVersion: v1
metadata:
  name: custom-endpoint-svc
spec:
  ports:
    - port: 30018
      protocol: TCP
      name: http
  type: ClusterIP
---
kind: Endpoints
apiVersion: v1
metadata:
  name: custom-endpoint-svc
subsets:
  - addresses:
      - ip: 192.168.32.8
    ports:
      - port: 8080
        name: http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

其中,在 Endpoints 的 subsets 中用于定义目的地址和端口,需要注意的是:

  • Endpoints 和 Service 的 metadata.name 须保持一致
  • Service.spec.ports[x].name 和 Endpoints.subsets[x].ports[x].name 须保持一致

# NodePort

如果 Service 的类型是 NodePort,表示把 Service 的端口映射到了 Node 节点上,这时候就可以通过 NodeIP:NodePort 进行访问,可以实现在外部对集群内应用进行访问。

NodePort 并不是随便选择的,当安装好 Kubernetes 集群后,会给定一个默认的 NodePort 范围,它们是从 30000 到 32767 端口,如果没有指定特定端口,默认会从这个区间范围内随机选择一个。

一个 NodePort 类型的 Service 定义如下:

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - name: http
      port: 80
      targetPort: http
1
2
3
4
5
6
7
8
9
10
11
12
13
14

除了 Type 类型不一样,其他的和定义 ClusterIP 类型的一样。

当创建完成过后,生成的 Service 如下:

kubectl get svc
NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
nginx-svc        NodePort    10.103.74.70     <none>        80:32710/TCP   54m
1
2
3

NodePort 类型的 Service,既可以在集群内部通过 ClusterIP:Port 进行访问,也可以在集群外部通过 NodeIP:NodePort 进行访问。如上,80:32710 中,80 是集群内部端口,32710 是暴露出来的节点端口。

# LoadBalancer

原则上,如果要使用 LoadBalancer 需要云厂商的支持,因为公网 IP 这些基本都是云厂商来分配。定义如下:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: redis
  name: redis-master
spec:
  clusterIP: 10.96.246.58
  externalTrafficPolicy: Local
  healthCheckNodePort: 32006
  ports:
    - name: redis
      nodePort: 30997
      port: 6379
      protocol: TCP
      targetPort: 6379
  selector:
    app: redis
    release: redis
    role: master
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
      - ip: 121.189.210.111
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

这样就可以直接通过公网 IP 和端口(121.189.210.111:6379)进行访问。

但是,为了在内网环境也使用 LoadBalancer 功能,有很多开源方案,其中 OpenELB 是一个非常不错的项目,我们下面可以来简单使用一下。

# OpenELB

OpenELB 之前叫 PorterLB,是为物理机(Bare-metal)、边缘(Edge)和私有化环境设计的负载均衡器插件,可作为 Kubernetes (opens new window)、K3s、KubeSphere 的 LB 插件对集群外暴露 LoadBalancer 类型的服务,现阶段是 CNCF 沙箱项目,核心功能包括:

  • 基于 BGP 与 Layer 2 模式的负载均衡
  • 基于路由器 ECMP 的负载均衡
  • IP 地址池管理
  • 使用 CRD 进行 BGP 配置

(1)安装

要安装 OpenELB 非常简单,直接使用以下命令即可。

kubectl apply -f https://raw.githubusercontent.com/openelb/openelb/master/deploy/openelb.yaml
1

值得注意的是,如果本地不能获取 k8s.gcr.io 镜像(需要翻墙),可以把上面的 YAML 文件下载下来,根据里面的注释做相应的镜像替换即可。

安装完成过后,会生成如下 Pod:

kubectl get pod -n openelb-system
NAME                              READY   STATUS      RESTARTS   AGE
openelb-admission-create-ltfcv    0/1     Completed   0          4m19s
openelb-admission-patch-h485q     0/1     Completed   0          4m19s
openelb-keepalive-vip-7mnl7       1/1     Running     0          3m8s
openelb-manager-98764b5ff-4c58s   1/1     Running     0          4m19s
1
2
3
4
5
6

(2)配置

首先,需要为 kube-proxy 启动 strictARP,以便 Kubernetes 集群中的所有网卡停止响应其他网卡的 ARP 请求,而由 OpenELB 来处理 ARP 请求。

kubectl edit configmap kube-proxy -n kube-system
......
ipvs:
  strictARP: true
......
1
2
3
4
5

然后重启 kube-proxy 组件,命令如下:

kubectl rollout restart daemonset kube-proxy -n kube-system
1

如果安装 OpenELB 的节点有多个网卡,则需要指定 OpenELB 在二层模式下使用的网卡,如果节点只有一个网卡,则可以跳过此步骤,假设安装了 OpenELB 的 master1 节点有两个网卡(eth0 192.168.0.2 和 ens33 192.168.0.111),并且 eth0 192.168.0.2 将用于 OpenELB,那么需要为 master1 节点添加一个 annotation 来指定网卡:

kubectl annotate nodes master1 layer2.openelb.kubesphere.io/v1alpha1="192.168.0.2"
1

接下来就可以创建一个 EIP 对象来充当 OpenELB 的 IP 地址池,YAML 清单如下:

apiVersion: network.kubesphere.io/v1alpha2
kind: Eip
metadata:
  name: eip-pool
spec:
  address: 192.168.205.50-192.168.205.60
  protocol: layer2
  disable: false
  interface: ens33
1
2
3
4
5
6
7
8
9

值得注意的是:

  • address 用于配置地址池,如果是在 layer2 层,则需要和集群的地址保持在同一网段
  • protocol 用户指定模式,默认是 bgp,我们这里指定的是 layer2。
  • interface 用于指定网卡,由于这里是 layer2,所以需要指定 interface。
  • disable 表示禁用 Eip 对象

创建完成过后,可以看看 Eip 地址池状态:

kubectl get eip
NAME       CIDR                            USAGE   TOTAL
eip-pool   192.168.205.50-192.168.205.60   1       11
1
2
3

最后,我们创建一个 LoadBalancer 类型的 Service,如下:

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
  annotations:
    lb.kubesphere.io/v1alpha1: openelb
    protocol.openelb.kubesphere.io/v1alpha1: layer2
    eip.openelb.kubesphere.io/v1alpha2: eip-pool
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
    - name: http
      port: 80
      targetPort: http
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

注意这里我们为 Service 添加了几个 annotations 注解:

  • lb.kubesphere.io/v1alpha1: openelb 用来指定该 Service 使用 OpenELB
  • protocol.openelb.kubesphere.io/v1alpha1: layer2 表示指定 OpenELB 用于 Layer2 模式
  • eip.openelb.kubesphere.io/v1alpha2: eip-pool 用来指定了 OpenELB 使用的 Eip 对象,如果未配置此注解,OpenELB 会自动使用与协议匹配的第一个可用 Eip 对象,此外也可以删除此注解并添加 spec:loadBalancerIP 字段(例如 spec:loadBalancerIP: 192.168.0.108)以将特定 IP 地址分配给 Service。

Service 创建过后,可以查看其状态是否正确:

kubectl get svc
NAME             TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)        AGE
nginx-svc        LoadBalancer   10.103.74.70     192.168.205.50   80:32710/TCP   105m
1
2
3

可以看到在EXTERNAL-IP处为我们分配了一个 Eip,现在就可以直接使用 192.168.205.50:80 从外部直接访问集群内的服务了。

# ExternalName

ExternalName  是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。 对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。例如:

kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com
1
2
3
4
5
6
7
8

当查询主机 my-service.prod.svc.cluster.local (后面服务发现的时候我们会再深入讲解)时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。 如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 Service 的 type,完全不需要修改调用的代码,这样就完全解耦了。

# Headless Service

上面介绍的几种 Service 的类型,都会生成一个 ClusterIP 供集群使用,如果在 Kubernetes 集群中配置了 DNS,解析 Service Name 会得到 ClusterIP。但是在一些特殊的场景下,我们希望直接和应用 Pod 进行通信,比如数据库等有状态应用,为此,社区提供了 Headless Service,也就是无头服务。

无头服务,也就是在配置 Service 的时候将 spec.clusterIP 的值设置为“None”,如下:

apiVersion: v1
kind: Service
metadata:
  name: nginx-headless-service
  labels:
    name: nginx-headless-service
spec:
  clusterIP: None
  selector:
    name: nginx
  ports:
    - port: 8000
      targetPort: 80
1
2
3
4
5
6
7
8
9
10
11
12
13

创建过后,就不会生成 ClusterIP,如下:

kubectl get svc
NAME                     TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)        AGE     17d
nginx-headless-service   ClusterIP      None             <none>           8000/TCP       3s
1
2
3

这种类型的 Service 主要用在有状态服务,后续在有状态应用管理的时候会感受到具体是如何使用的。

# Ingress

上面介绍的 Service 主要用在集群内部,当然 NodePort 和 LoadBalancer 类型也可以用于外部访问,但是它们也有一定的弊端:

(1)如果使用 NodePort 类型,需要维护好每个应用的端口地址,如果服务太多就不好管理

(2)如果使用 LoadBalancer 类型,基本是在云上使用,需要的 IP 比较多,价格也比较昂贵

(3)不论是 NodePort 还是 LoadBalancer,都是工作在四层,对于 HTTPS 类型的请求无法直接进行 SSL 校验

因此,社区提供了 Ingress 对象,为集群提供统一的入口,逻辑如下:

fb1034be18e9d8d0047bf15f7c41b359 MD5

其中 Ingress 代理的并不是 Pod 的 Service,而是 Pod,之所以在配置的时候是配置的 Service,是为了获取 Pod 的信息。

Ingress 提供七层访问入口,但是 Kubernetes 的 Ingress 对象本身没有任何功能,需要借助一些 Controller 来实现,常用的有:

  • Nginx Ingress Controller
  • Traefik Ingress Controller
  • Kong Ingress Controller
  • APISix Ingress Controller
  • .....

最常用的是 nginx ingress controller,这里也会安装 nginx ingress controller 来进行介绍。

# 安装 Nginx Ingress Controller

安装方式非常简单,可以使用 Helm 和普通的 YAML 进行安装,由于我们还没有学习 Helm,所以采用 YAML 清单的方式。

如果网络条件好(可以翻墙),可直接使用以下命令安装:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.0/deploy/static/provider/cloud/deploy.yaml
1

如果网络条件不好,可以使用:

kubectl apply -f https://raw.githubusercontent.com/joker-bai/kubernetes-software-yaml/main/ingress/nginx/ingress-nginx.yaml
1

安装完成过后,资源信息如下:

kubectl get all -n ingress-nginx
NAME                                           READY   STATUS      RESTARTS   AGE
pod/ingress-nginx-admission-create-kj7ch       0/1     Completed   0          8m16s
pod/ingress-nginx-admission-patch-gtncg        0/1     Completed   0          8m16s
pod/ingress-nginx-controller-d5c4c7ffb-6q5gn   1/1     Running     0          8m16s

NAME                                         TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)                      AGE
service/ingress-nginx-controller             LoadBalancer   10.109.208.249   192.168.205.51   80:31306/TCP,443:31079/TCP   8m16s
service/ingress-nginx-controller-admission   ClusterIP      10.103.169.99    <none>           443/TCP                      8m16s

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ingress-nginx-controller   1/1     1            1           8m16s

NAME                                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/ingress-nginx-controller-d5c4c7ffb   1         1         1       8m16s

NAME                                       COMPLETIONS   DURATION   AGE
job.batch/ingress-nginx-admission-create   1/1           5s         8m16s
job.batch/ingress-nginx-admission-patch    1/1           6s         8m16s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

在这里,ingress-nginx 对外暴露采用的是 LoadBalancer,也就是说把域名解析到 192.168.205.51 地址上,可以直接通过 80 端口访问。

# 暴露第一个服务

上面已经把 ingress-nginx controller 部署好了,接下来就可以创建一个应用进行暴露。

我们这里通过暴露一个 nginx 服务,如下:

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

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
spec:
  type: ClusterIP
  selector:
    app: nginx
  ports:
    - name: http
      port: 80
      targetPort: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
    - host: nginx.dev.jokerbai.com
      http:
        paths:
          - path: /
            backend:
              service:
                name: nginx-svc
                port:
                  number: 80
            pathType: Prefix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

注意:对于不同版本的 Ingress 配置可能不一样,在实际中可以通过kubectl explain ingrss来查看具体的配置。

部署完成过后,查看 ingress 信息如下:

kubectl get ingress
NAME    CLASS    HOSTS                    ADDRESS          PORTS   AGE
nginx   <none>   nginx.dev.jokerbai.com   192.168.205.51   80      88s
1
2
3

将域名进行解析,则可以访问,如下:

curl -x 192.168.205.51:80 http://nginx.dev.jokerbai.com
<!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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 进阶使用

上面使用 ingress 暴露了第一个域名,但是在实际的生成中,可能还有更多复杂的应用,比如:

  • 域名使用 SSL 证书访问
  • 域名重定向
  • 地址重写
  • 认证访问
  • 黑白名单
  • 域名限速
  • 灰度发布
  • ......

# 使用 SSL 证书

在实际工作中,为了安全考虑,更多的是采用 HTTPS 进行访问,Ingress 也支持配置 HTTPS。

首先需要域名证书,这里手动创建私有证书进行配置。

openssl genrsa -out tls.key 2048
openssl req -new -x509  -key tls.key -out tls.crt -subj "/C=CN/ST=Chongqing/L=Chongqing/OU=DevOps/CN=nginx.dev.jokerbai.com"
1
2

然后将证书保存到 secret 对象中(在 Kubernetes 应用配置管理会介绍)。

kubectl create secret tls tls-secret --key tls.key --cert tls.crt
1

证书创建好了之后,就可以配置 ingress 了,如下:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
    - hosts:
        - nginx.dev.jokerbai.com
      secretName: tls-secret
  rules:
    - host: nginx.dev.jokerbai.com
      http:
        paths:
          - path: /
            backend:
              service:
                name: nginx-svc
                port:
                  number: 80
            pathType: Prefix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

其中 Ingress.spec.tls 字段用来配置域名证书。

接下来就可以通过 HTTPS 进行访问。

bb761403c827ed82f792d8c36fe24263 MD5

PS:由于是自定义的证书,是无效的,所以访问的时候会报错,在实际工作中是会购买专用证书。

# 域名重定向

有时候需要把域名请求重定向到另外的域名,在 nginx 中,我们可以配置 redirect,在 ingress 中,也可以使用 redirect,不过是配置在 annotation 中,如下:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-redirect
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/permanent-redirect: "https://www.baidu.com"
spec:
  rules:
    - host: nginx-redirect.dev.jokerbai.com
      http:
        paths:
          - path: /
            backend:
              service:
                name: nginx-svc
                port:
                  number: 80
            pathType: Prefix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

访问的时候可以看到 301 重定向,如下:

curl -I -x 192.168.205.51:80 http://nginx-redirect.dev.jokerbai.com
HTTP/1.1 301 Moved Permanently
Date: Fri, 22 Jul 2022 05:50:59 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
Location: https://www.baidu.com
1
2
3
4
5
6
7

其实现方式是在 ingress 配置的 annotation 中添加声明配置:nginx.ingress.kubernetes.io/permanent-redirect: "https://www.baidu.com" (opens new window) 即可。

# 地址重写

地址重写在 ingress 中通过在 annotation 中添加nginx.ingress.kubernetes.io/rewrite-target: "/$1"这种类型的配置即可。

通过地址重写,我们可以实现诸如访问 a.com/foo 重写到 a.com,访问 a.com/foo 重写到 a.com/foo/bar,需要注意的是重写后的地址需要是能真实访问到资源的地址,不然重写也没什么意义。

实例如下:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-rewrite
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: "/$1"
spec:
  rules:
    - host: nginx-rewrite.dev.jokerbai.com
      http:
        paths:
          - path: /foo/?(.*)
            backend:
              service:
                name: nginx-svc
                port:
                  number: 80
            pathType: Prefix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

然后通过在访问 URL 中带/foo 即可访问到 Nginx 服务,如下:

curl  -x 192.168.205.51:80 http://nginx-rewrite.dev.jokerbai.com/foo
<!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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 认证访问

有时候,我们暴露出来的域名不想被其他人使用(可能有人会说:那你不暴露出来不就完事了),比如说 prometheus 的后台,默认是没有登录管理的,而运维是需要到后台去查询监控信息的,为了安全,我们需要给这类域名加上认证,以便在一定程度上降低风险。

ingress 提供 base auth 的认证方式,我们可以通过这种方式为我们的域名提供认证。

(1)创建密码

htpasswd -c auth joker
New password:
Re-type new password:
Adding password for user joker
1
2
3
4

(2)将密码保存到 secret 中

kubectl create secret generic basic-auth --from-file=auth
secret/basic-auth created
1
2

(3)在 Ingress 中配置认证

只需要在 ingress 配置中添加两个 annotation 即可完成。

  • nginx.ingress.kubernetes.io/auth-type: basic 指明认证方式
  • nginx.ingress.kubernetes.io/auth-secret: basic-auth 指定认证的账号密码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-auth
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
spec:
  rules:
    - host: nginx-auth.dev.jokerbai.com
      http:
        paths:
          - path: /
            backend:
              service:
                name: nginx-svc
                port:
                  number: 80
            pathType: Prefix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

然后不带认证信息访问,结果如下:

curl  -x 192.168.205.51:80 http://nginx-auth.dev.jokerbai.com/foo
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx</center>
</body>
</html>
1
2
3
4
5
6
7
8

直接返回 401,需要认证才能访问。

下面带上认证信息访问,结果如下:

curl -u "joker:123"  -x 192.168.205.51:80 http://nginx-auth.dev.jokerbai.com/
<!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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

如果是从浏览器访问,会给我们弹出一个输入框,输入账号密码即可进入应用。

# 黑白名单

有时候,光有认证访问也并不安全,这时候我们可以通过配置黑白名单的方式,把访问的范围降低,在 nginx 中,我们可以通过配置 allow 和 deny 来配置,在 ingress 中,也支持类似的配置。

######## 配置白名单 在 ingress 里配置白名单可以通过两种方式实现:

  • 添加 annotation,这种是只针对单个域名
  • 在 ingress-nginx 的 configmap 中配置,全局有效

(1)通过 annotation 配置,如下:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-whitelist
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/whitelist-source-range: 10.1.10.2
spec:
  rules:
    - host: nginx-whitelist.dev.jokerbai.com
      http:
        paths:
          - path: /
            backend:
              service:
                name: nginx-svc
                port:
                  number: 80
            pathType: Prefix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

配置了一个不存在的地址 10.1.10.2,查看访问效果。

curl -I -x 192.168.205.51:80 http://nginx-whitelist.dev.jokerbai.com
HTTP/1.1 403 Forbidden
Date: Fri, 22 Jul 2022 06:57:45 GMT
Content-Type: text/html
Content-Length: 146
Connection: keep-alive
1
2
3
4
5
6

给我们返回 403 拒绝访问。

如果我们把地址配置成可以访问的 IP,则可以访问。

(2)配置 ConfigMap

部署好 ingress-nginx 后,会在 ingress-nginx 的 namespace 下生成叫“ingress-nginx-controller”的 ConfigMap 配置文件,我们只需要在这个配置文件进行配置即可。

kubectl edit cm -n ingress-nginx ingress-nginx-controller
apiVersion: v1
data:
  allow-snippet-annotations: "true"
  compute-full-forwarded-for: "true"
  use-forwarded-headers: "true"
  whitelist-source-range: 172.16.0.0/24
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.3.0
  name: ingress-nginx-controller
  namespace: ingress-nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

配置完保存即可,ingress-nginx 的 pod 会自动 reload。不过这种配置是全局生效,在使用的时候慎重。

######## 配置黑名单 有白名单就有黑名单,ingress 的黑名单配置只能通过 ConfigMap 来,而且是全局生效的。

目前支持以下三种黑名单:

  • block-cidrs:限制 IP
  • block-user-agents:限制 User-Agent
  • block-referers:限制 referer

配置很简单,如下:

kubectl edit cm -n ingress-nginx ingress-nginx-controller
apiVersion: v1
data:
  allow-snippet-annotations: "true"
  compute-full-forwarded-for: "true"
  use-forwarded-headers: "true"
  whitelist-source-range: 172.16.0.0/24
  block-cidrs: 10.1.10.100
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
    app.kubernetes.io/version: 1.3.0
  name: ingress-nginx-controller
  namespace: ingress-nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 访问限速

有时候访问量太大,可以通过在 ingress 进行限速,配置如下:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-limit
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/limit-rate: "100K"
    nginx.ingress.kubernetes.io/limit-whitelist: "10.1.10.100"
    nginx.ingress.kubernetes.io/limit-rps: "1"
    nginx.ingress.kubernetes.io/limit-rpm: "30"
spec:
  rules:
    - host: nginx-limit.dev.jokerbai.com
      http:
        paths:
          - path: /
            backend:
              service:
                name: nginx-svc
                port:
                  number: 80
            pathType: Prefix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

其中:

  • nginx.ingress.kubernetes.io/limit-rate:限制客户端每秒传输的字节数
  • nginx.ingress.kubernetes.io/limit-whitelist:白名单中的 IP 不限速
  • nginx.ingress.kubernetes.io/limit-rps:单个 IP 每秒的连接数
  • nginx.ingress.kubernetes.io/limit-rpm:单个 IP 每分钟的连接数

# 灰度发布

Nginx Annotations (opens new window) 支持以下 4 种 Canary 规则:

  • nginx.ingress.kubernetes.io/canary-by-header:基于 Request Header 的流量切分,适用于灰度发布以及 A/B 测试。当 Request Header 设置为 always时,请求将会被一直发送到 Canary 版本;当 Request Header 设置为 never时,请求不会被发送到 Canary 入口;对于任何其他 Header 值,将忽略 Header,并通过优先级将请求与其他金丝雀规则进行优先级的比较。
  • nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务。当 Request Header 设置为此值时,它将被路由到 Canary 入口。该规则允许用户自定义 Request Header 的值,必须与上一个 annotation (即:canary-by-header)一起使用。
  • nginx.ingress.kubernetes.io/canary-weight:基于服务权重的流量切分,适用于蓝绿部署,权重范围 0 - 100 按百分比将请求路由到 Canary Ingress 中指定的服务。权重为 0 意味着该金丝雀规则不会向 Canary 入口的服务发送任何请求。权重为 100 意味着所有请求都将被发送到 Canary 入口。
  • nginx.ingress.kubernetes.io/canary-by-cookie:基于 Cookie 的流量切分,适用于灰度发布与 A/B 测试。用于通知 Ingress 将请求路由到 Canary Ingress 中指定的服务的 cookie。当 cookie 值设置为 always时,它将被路由到 Canary 入口;当 cookie 值设置为 never时,请求不会被发送到 Canary 入口;对于任何其他值,将忽略 cookie 并将请求与其他金丝雀规则进行优先级的比较。

定义两个版本的代码。

V1 版本代码如下:

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main(){
	g:=gin.Default()
	g.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"version": "v1",
			"data": "hello world",
		})

		})
	_ = g.Run("8080")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

V2 版本代码如下:

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main(){
	g:=gin.Default()
	g.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"version": "v2",
			"data": "hello world,SB",
		})

		})
	_ = g.Run("8080")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

然后制作镜像,Dockerfile 如下:

FROM golang AS build-env
ADD . /go/src/app
WORKDIR /go/src/app
RUN go get -u -v github.com/gin-gonic/gin
RUN govendor sync
RUN GOOS=linux GOARCH=386 go build -v -o /go/src/app/app-server-v1

FROM alpine
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime
COPY --from=build-env /go/src/app/app-server-v1 /usr/local/bin/app-server-v1
EXPOSE 8080
CMD [ "/usr/local/bin/app-server-v1" ]
1
2
3
4
5
6
7
8
9
10
11
12

制作镜像并上传:

docker build -t registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1 .
docker push registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1
1
2

PS:V2 版本操作类似

V1 和 V2 版本的 Deployment 和 Service 如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-server-v1
spec:
  selector:
    matchLabels:
      app: app-server-v1
  replicas: 2
  template:
    metadata:
      labels:
        app: app-server-v1
    spec:
      containers:
        - name: app-server-v1
          image: registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v1
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: app-server-v1-svc
spec:
  selector:
    app: app-server-v1
  ports:
    - name: http
      port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-server-v1
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
    - host: canary.dev.jokerbai.com
      http:
        paths:
          - path: /
            backend:
              service:
                name: app-server-v1-svc
                port:
                  number: 8080
            pathType: Prefix
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-server-v2
spec:
  selector:
    matchLabels:
      app: app-server-v2
  replicas: 2
  template:
    metadata:
      labels:
        app: app-server-v2
    spec:
      containers:
        - name: app-server-v2
          image: registry.cn-hangzhou.aliyuncs.com/rookieops/go-test:v2
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: app-server-v2-svc
spec:
  selector:
    app: app-server-v2
  ports:
    - name: http
      port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-server-v2
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  rules:
    - host: canary.dev.jokerbai.com
      http:
        paths:
          - path: /
            backend:
              service:
                name: app-server-v2-svc
                port:
                  number: 8080
            pathType: Prefix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

说明:

  • nginx.ingress.kubernetes.io/canary: true 表示开启 canary
  • nginx.ingress.kubernetes.io/canary-weight: 10 表示权重为 10,也就是 v1:v2 大致为 9:1

在实际使用中,也是可以通过部署不同版本的 Deployment 来实现灰度发布。

# 总结

目前集群内应用的访问主要是通过 Service 和 Ingress 两种方式,其中 Service 是四层,Ingress 是七层,在企业应用中,除了一些必须使用四层的应用,比如 MySQL、Redis,其他的基本都采用 Ingress 进行访问。

Ingress 的选型非常多,可以根据企业的实际情况(比如技术栈、熟悉程度、规模大小)进行选择,而 Service 主要还是在集群内部使用。

上次更新: 2025/07/19, 21:23:02
Kubernetes数据持久化管理
Kubernetes应用配置管理

← Kubernetes数据持久化管理 Kubernetes应用配置管理→

最近更新
01
elastic 账户认证 401 问题
07-20
02
使用 helm 安装 es 和 kibana
07-20
03
elastic stack 搭建
07-20
更多文章>
Theme by Vdoing | Copyright © 2019-2025 乔克 | MIT License | 渝ICP备20002153号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式