Kubernetes学习笔记02:Deployment、Service和Ingress

发表于 2024-01-23 22:08 1367 字 7 min read

Spring AI学习笔记(四)工具调用和MCPSpring AI学习笔记(三)RAG从文档入库到回答Spring AI学习笔记(二)ChatClient从怎么调到怎么封装Spring AI学习笔记(一)它到底解决什么问题java新版本-java25学习笔记(四)用JFR和GC日志做一次体检java新版本-java25学习笔记(三)虚拟线程要和资源边界一起看java新版本-java25学习笔记(二)运行时基线先统一java新版本-java25学习笔记(一)LTS版本对比和学习路线主流AI Agent能力对比与工程选型我用Kiro做了个自己的工具站盘一盘虚拟线程我用Trae做了个AstrBot的AI角色扮演插件Python初学笔记(六)常用标准库先学这几个Python初学笔记(五)读写文件和处理异常Python初学笔记(四)函数让代码开始有结构Python初学笔记(三)条件、循环和推导式Python初学笔记(二)变量和基础类型比想象中重要Python初学笔记(一)先把环境和运行方式弄明白主流AI大模型能力对比Java 21和Spring Boot 3升级笔记(五)日志指标与可观测性Java 21和Spring Boot 3升级笔记(四)数据访问层适配Java 21和Spring Boot 3升级笔记(三)虚拟线程使用边界Java 21和Spring Boot 3升级笔记(二)Jakarta迁移要点Java 21和Spring Boot 3升级笔记(一)工程基线整理魔法值怎么能忍!JPA的Specification大改造!处理生僻字乱码:JPA框架对于Oracle的NVarchar2,NChar,NClob类型支持Redis Stream能不能当轻量消息队列用RocketMQ 5学习笔记:普通消息之外要看什么事件流不是换个消息队列这么简单Kubernetes学习笔记04:发布、排障和观测Kubernetes学习笔记03:配置、密钥和存储Kubernetes学习笔记02:Deployment、Service和IngressKubernetes学习笔记01:Pod和控制器mysql索引原理02--存储引擎索引的实现mysql索引原理01--索引的数据结构
  上一篇把 Pod、ReplicaSet、Deployment 的关系讲了一遍。但一个应用只是在集群里跑起来还不够,其他服务要能访问它,外部用户也要能访问它。   这就会遇到 Service 和 Ingress。   很多 k8s 初学问题都卡在这里:Deployment 明明启动成功了,为什么浏览器访问不到?Pod IP...

前言

  上一篇把 Pod、ReplicaSet、Deployment 的关系讲了一遍。但一个应用只是在集群里跑起来还不够,其他服务要能访问它,外部用户也要能访问它。

  这就会遇到 Service 和 Ingress。

  很多 k8s 初学问题都卡在这里:Deployment 明明启动成功了,为什么浏览器访问不到?Pod IP 明明能通,为什么重启后又不行?Service 和 Ingress 到底谁负责什么?

这篇就按一次普通 Web 服务发布的路径,把 Deployment、Service、Ingress 串起来看。

Pod IP 不适合直接依赖

  Pod 启动后会有自己的 IP,但这个 IP 不稳定。

Pod 被重建、调度到其他节点、滚动发布时,IP 都可能变化。如果其他服务直接记 Pod IP,就会很脆弱。

这和传统机器部署不一样。传统部署里我们可能会固定几台机器,然后在 Nginx 或注册中心里配置它们。k8s 里 Pod 是可替换的,应用实例随时可能被销毁再创建。

所以访问后端时,不应该直接依赖 Pod IP,而是通过 Service。

Service 提供稳定入口

  Service 的作用是给一组 Pod 提供稳定访问入口。

它通过 selector 找到后端 Pod,然后给调用方暴露一个稳定的服务名和虚拟 IP。即使后端 Pod 发生变化,调用方仍然访问同一个 Service。

一个最常见的 ClusterIP Service:

apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  type: ClusterIP
  selector:
    app: order-service
  ports:
    - name: http
      port: 80
      targetPort: 8080

这里的 port 是 Service 暴露的端口,targetPort 是 Pod 容器实际监听的端口。

如果另一个服务也在集群内,就可以访问:

http://order-service

在同一个 namespace 下,DNS 会把 order-service 解析到 Service。跨 namespace 时,可以用更完整的域名,比如 order-service.mall.svc.cluster.local

selector 一定要对齐

  Service 能不能转发到 Pod,核心看 selector 是否能匹配到 Pod label。

Deployment 里通常会写:

template:
  metadata:
    labels:
      app: order-service

Service 里要对应:

selector:
  app: order-service

如果这里写错,Service 本身可能是正常创建的,但没有任何后端端点。排查时可以看 endpoints:

kubectl get endpoints order-service

如果结果为空,优先查 label 和 selector,而不是一上来就怀疑网络插件。

Service 类型不要乱用

  Service 有几种类型,刚开始容易混。

ClusterIP 是默认类型,只在集群内部暴露服务。微服务之间互相调用,大多数用它就够了。

NodePort 会在每个节点上开一个端口,把流量转到 Service。它可以用于简单测试,但生产里直接暴露 NodePort 并不优雅,端口管理也容易混乱。

LoadBalancer 通常依赖云厂商或负载均衡实现,会给 Service 分配一个外部负载均衡入口。云上场景比较常见。

ExternalName 则更像 DNS 别名,用来把集群内服务名指向外部域名。

我的习惯是:内部服务先用 ClusterIP;外部 HTTP 流量优先考虑 Ingress;确实需要四层负载均衡时再看 LoadBalancer。

Ingress 负责 HTTP 入口

  如果一个 Web 应用要从集群外访问,通常会引入 Ingress。

Ingress 本身是一种路由规则,真正转发流量的是 Ingress Controller,比如 Nginx Ingress Controller、Traefik、HAProxy 或云厂商提供的实现。

一个简单 Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: mall-ingress
spec:
  rules:
    - host: mall.example.com
      http:
        paths:
          - path: /api/order
            pathType: Prefix
            backend:
              service:
                name: order-service
                port:
                  number: 80

这条规则表达的是:访问 mall.example.com/api/order 的请求,转发给 order-service 这个 Service 的 80 端口。

所以链路是这样的:

Client -> Ingress Controller -> Service -> Pod

Ingress 不直接找 Pod,它找 Service。Service 再通过 selector 找 Pod。

先把一条链路跑通

  排查访问问题时,我比较喜欢从内到外确认。

第一步,确认 Pod 正常:

kubectl get pods -l app=order-service
kubectl logs deploy/order-service

第二步,确认 Service 有后端:

kubectl get service order-service
kubectl get endpoints order-service

第三步,从集群内访问 Service:

kubectl run curl-test --rm -it --image=curlimages/curl -- sh
curl http://order-service

第四步,再看 Ingress 规则和 Controller 日志。

这样排查比直接在浏览器里刷新要有效。外部访问失败可能是域名、证书、Ingress、Service、Pod、应用端口任何一层的问题。逐层收窄,才不会乱猜。

Java 服务要注意端口和健康检查

  很多 Spring Boot 服务默认监听 8080。Service 里 targetPort 要对应容器真实端口,Ingress 后端端口要对应 Service 端口。

如果应用启动慢,还要配合 readinessProbe。否则 Pod 刚创建,Service 就把流量打进去,用户可能遇到启动阶段的错误。

一个简单 readinessProbe:

readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 20
  periodSeconds: 10

这不是形式主义。滚动发布时,readiness 决定一个新 Pod 什么时候可以接流量。如果这个探针不准,发布稳定性会直接受影响。

小结

  Deployment 负责让应用副本跑起来,Service 负责给一组 Pod 提供稳定入口,Ingress 负责把外部 HTTP 请求路由到 Service。

  理解这条链路以后,k8s 里的“能不能访问”问题就会清晰很多。先看 Pod,再看 Service endpoints,再看 Ingress Controller,不要一开始就把所有问题混在一起。

  对于普通后端服务,最常见的组合就是 Deployment + ClusterIP Service + Ingress。把 label、端口、健康检查写稳,后面的发布和排障会轻松很多。

喜欢的话,留下你的评论吧~

© 2019 - 2026 VincentHo @VincentHo
Powered by theme astro-koharu · Inspired by Shoka