K8S 的服务暴露方式
K8S 的服务暴露方式
简介
在 Kubernetes 中,Pod 是临时性的,它们会被创建、销毁和重新调度,每次重建后 IP 地址都会发生变化。为了解决 Pod IP 不固定的问题,Kubernetes 引入了 Service(服务)的概念。Service 定义了一组 Pod 的逻辑集合和一个访问它们的策略,通过标签选择器(Label Selector)自动关联后端 Pod,为外部或集群内部提供稳定的访问入口。
特点
Service 类型概览
| Service 类型 | 说明 | 访问方式 | 使用场景 |
|---|---|---|---|
| ClusterIP | 集群内部 IP(默认类型) | 仅集群内部可访问 | 微服务间内部通信 |
| NodePort | 在每个节点上开放端口 | 通过节点 IP + NodePort 访问 | 测试环境、简单对外暴露 |
| LoadBalancer | 云厂商负载均衡器 | 通过外部 LoadBalancer IP 访问 | 生产环境对外暴露 |
| ExternalName | 映射到外部 DNS 名称 | 集群内部通过服务名访问 | 引用集群外部服务 |
| Headless | 无 ClusterIP | 直接访问 Pod IP | StatefulSet、直接 Pod 发现 |
ClusterIP
ClusterIP 是 Kubernetes 的默认服务类型,分配一个集群内部的虚拟 IP 地址,集群内的其他应用可以通过这个 IP 访问服务,集群外部无法直接访问。
ClusterIP 示例
apiVersion: v1
kind: Service
metadata:
name: my-internal-service
spec:
type: ClusterIP
selector:
app: mynginx
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP使用 kubectl proxy 访问
虽然从 Internet 无法直接访问 ClusterIP 服务,但可以通过 Kubernetes 的 proxy 模式来访问:
# 启动 proxy 模式
kubectl proxy --port=0访问模式:http://localhost:端口/api/v1/proxy/namespaces/[namespace]/services/[service]:[port]/
适用场景:

NodePort
NodePort 是将外部流量引入服务的最原始方式。它在所有节点上开放一个特定端口(默认范围 30000-32767),任何发送到该端口的流量都会被转发到对应的 Service。
NodePort 示例
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30036 # 节点上开放的端口(可选,不指定则随机分配)
protocol: TCP
适用场景:
注意事项:
LoadBalancer
LoadBalancer 是在云环境中暴露服务到 Internet 的标准方式。它会向云厂商请求一个外部负载均衡器(如 AWS ELB、阿里云 SLB),获得一个独立的公网 IP 地址,将所有流量转发到后端 Service。
LoadBalancer 示例
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
适用场景:
注意事项:
ExternalName
ExternalName 类型将服务映射到外部 DNS 名称,不创建代理或转发规则,仅在集群内部提供 DNS 别名。
apiVersion: v1
kind: Service
metadata:
name: external-database
spec:
type: ExternalName
externalName: database.example.com集群内部通过 external-database.default.svc.cluster.local 即可访问外部的 database.example.com。
Headless Service
Headless Service 不分配 ClusterIP,DNS 查询直接返回后端 Pod 的 IP 地址列表。适用于需要直接与特定 Pod 通信的场景。
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
clusterIP: None # 设置为 None 即为 Headless Service
selector:
app: my-stateful-app
ports:
- port: 3306
targetPort: 3306适用场景:
Ingress
Ingress 不是一种 Service 类型,而是处于多个 Service 前端的"智能路由"或集群入口。它可以根据域名、路径将流量路由到不同的后端 Service,支持 SSL 终止、负载均衡等功能。
Ingress 示例
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: foo.mydomain.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: foo
port:
number: 8080
- host: mydomain.com
http:
paths:
- pathType: Prefix
path: "/bar"
backend:
service:
name: bar
port:
number: 8080
Ingress 控制器常见选择:
适用场景:
Endpoints
Endpoints 是 Service 与后端 Pod 之间的自动关联资源,记录了所有匹配 Selector 的 Pod IP 地址。
# 查看 Endpoints
kubectl get endpoints my-internal-service
# 查看 Endpoints 详情
kubectl describe endpoints my-internal-service手动 Endpoints(无 Selector)
当需要将 Service 代理到集群外部的服务时,可以创建不带 Selector 的 Service,然后手动创建 Endpoints:
# 不带 Selector 的 Service
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
ports:
- port: 3306
targetPort: 3306
---
# 手动创建 Endpoints
apiVersion: v1
kind: Endpoints
metadata:
name: external-service
subsets:
- addresses:
- ip: 192.168.1.100
ports:
- port: 3306服务发现机制
DNS 服务发现
Kubernetes 集群内部署了 CoreDNS 服务,自动为每个 Service 创建 DNS 记录:
# Service DNS 格式
<service-name>.<namespace>.svc.cluster.local
# 示例
my-internal-service.default.svc.cluster.local
# 同一 namespace 下可以简化为
my-internal-service环境变量发现
Kubernetes 也会为每个 Service 在 Pod 中注入环境变量:
# 环境变量命名规则
<SERVICE_NAME>_SERVICE_HOST # Service ClusterIP
<SERVICE_NAME>_SERVICE_PORT # Service Port
# 示例
MY_INTERNAL_SERVICE_SERVICE_HOST=10.96.0.1
MY_INTERNAL_SERVICE_SERVICE_PORT=80Service 高级配置
SessionAffinity 会话保持
apiVersion: v1
kind: Service
metadata:
name: session-affinity-demo
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
sessionAffinity: ClientIP # 基于 Client IP 的会话保持
sessionAffinityConfig:
clientIP:
timeoutSeconds: 3600 # 会话保持时间(默认 10800 秒)# 验证会话保持
for i in {1..10}; do
curl -s http://<service-ip> | grep "pod-name"
done
# 所有请求应路由到同一个 Pod多端口 Service
apiVersion: v1
kind: Service
metadata:
name: multi-port-service
spec:
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
- name: grpc
port: 9090
targetPort: 9090
protocol: TCP
- name: metrics
port: 9100
targetPort: 9100
protocol: TCP外部流量策略
apiVersion: v1
kind: Service
metadata:
name: external-traffic-demo
spec:
type: NodePort
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
nodePort: 30080
externalTrafficPolicy: Local # 只将外部流量路由到本节点的 Pod
# externalTrafficPolicy: Cluster # (默认)将外部流量路由到任意节点的 Pod# Cluster 策略:流量可能经过两次转发(节点 A → 节点 B),客户端源 IP 丢失
# Local 策略:流量直接转发到本节点 Pod,保留客户端源 IP,但没有 Pod 的节点不会转发
# 查看源 IP
kubectl get svc external-traffic-demo -o yaml | grep externalTrafficPolicyIngress 高级配置
# 基于 Nginx Ingress Controller 的高级路由
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: advanced-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com"
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/rate-limit-window: "1m"
nginx.ingress.kubernetes.io/limit-connections: "10"
nginx.ingress.kubernetes.io/limit-rps: "50"
nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8,192.168.0.0/16"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls-secret
rules:
- host: api.example.com
http:
paths:
- pathType: Prefix
path: "/api(/|$)(.*)"
backend:
service:
name: api-service
port:
number: 8080
- pathType: Prefix
path: "/grpc"
backend:
service:
name: grpc-service
port:
number: 9090
- pathType: Exact
path: "/health"
backend:
service:
name: health-service
port:
number: 80Ingress TLS 配置
# 创建 TLS 证书 Secret
kubectl create secret tls api-tls-secret \
--cert=api.example.com.crt \
--key=api.example.com.key
# 使用 cert-manager 自动管理证书
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: api-example-com
spec:
secretName: api-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: api.example.com
dnsNames:
- api.example.com
- "*.api.example.com"网络策略(NetworkPolicy)
# 只允许特定 Service 访问后端 Pod
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow-frontend
namespace: default
spec:
podSelector:
matchLabels:
app: api-backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
---
# 允许所有 Pod 访问 DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53EndpointSlice
# EndpointSlice 是 Endpoints 的升级版本(Kubernetes 1.21+)
# 默认每个 EndpointSlice 最多包含 100 个地址,可自动分片
# 查看 EndpointSlice
kubectl get endpointslices
kubectl describe endpointslice my-internal-service-xxxxx
# EndpointSlice 优势:
# - 支持更多地址(每个 Slice 100 个,旧版 Endpoints 支持有限)
# - 支持按协议分片(TCP、UDP 分开管理)
# - 扩展性更好,大规模集群性能更优Service 常用命令
# 查看 Service 列表
kubectl get services
kubectl get svc
# 查看 Service 详情
kubectl describe svc my-internal-service
# 查看 Endpoints
kubectl get endpoints
# 创建/更新 Service
kubectl apply -f service.yaml
# 删除 Service
kubectl delete svc my-internal-service
# 端口转发(临时调试)
kubectl port-forward svc/my-internal-service 8080:80优点
缺点
总结
K8S Service 为 Pod 提供稳定的访问入口。ClusterIP 集群内访问、NodePort 暴露节点端口、LoadBalancer 接入外部负载均衡器。配合 Ingress 实现 HTTP 路由。
关键知识点
- 部署类主题的核心不是“装成功”,而是“稳定运行、可排障、可回滚”。
- 同一个服务通常至少要关注版本、目录、端口、权限、数据、日志和备份。
- Linux 问题经常跨越系统层、网络层、服务层和应用层。
- Kubernetes 主题必须同时看资源对象、调度行为、网络暴露和配置分发。
项目落地视角
- 把安装步骤补成可重复执行的清单,必要时写成脚本或配置文件。
- 把配置目录、数据目录、日志目录和挂载点明确拆开。
- 上线前检查防火墙、SELinux、时区、磁盘、系统服务和健康检查。
- 上线前检查镜像、命名空间、探针、资源限制、Service/Ingress 和配置来源。
常见误区
- 使用 latest 或未固定版本,导致环境不可复现。
- 只验证启动成功,不验证持久化、开机自启和故障恢复。
- 遇到问题先改配置而不是先看日志和依赖链路。
- 只会 apply YAML,不理解对象之间的依赖关系。
进阶路线
- 继续补齐 systemd、性能监控、安全加固和备份恢复。
- 把单机操作升级成 Docker、Kubernetes 或 IaC 方案。
- 建立标准化运维手册,包括巡检、扩容、回滚和灾备演练。
- 继续补齐调度、网络策略、存储、GitOps 和平台工程能力。
适用场景
- 当你准备把《K8S 的服务暴露方式》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合单机环境初始化、中间件快速搭建、测试环境验证和生产部署前准备。
- 当服务稳定性依赖端口、权限、目录、网络和系统参数时,这类主题会直接影响成败。
落地建议
- 固定版本号与镜像标签,避免“latest”带来的不可预期变化。
- 把配置、数据、日志目录拆开管理,并记录恢复步骤。
- 上线前确认端口、防火墙、SELinux、时区和磁盘空间。
排错清单
- 先查 systemctl、容器日志和应用日志,确认失败发生在哪一层。
- 检查端口占用、目录权限、挂载路径和网络连通性。
- 如果是新环境问题,优先对比与已知正常环境的差异。
复盘问题
- 如果把《K8S 的服务暴露方式》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《K8S 的服务暴露方式》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《K8S 的服务暴露方式》最大的收益和代价分别是什么?
