什么是 Kubernetes 服务?
在 Kubernetes 中,服务是一个实体,代表一组运行应用程序或功能组件的 Pod。该服务保存访问策略,并负责对传入请求强制执行这些策略。
对服务的需求源于 Kubernetes 中 pod 的生命周期很短,可以随时更换。Kubernetes 保证给定 pod 和副本的可用性,但不保证单个 pod 的活跃性。这意味着需要与另一个 Pod 通信的 Pod 不能依赖底层单个 Pod 的 IP 地址。相反,它们连接到服务,该服务将它们中继到相关的当前正在运行的 Pod。
该服务被分配一个虚拟 IP 地址,称为 clusterIP,该地址将一直存在,直到被明确销毁为止。该服务充当组件或应用程序之间通信的可靠端点。
对于 Kubernetes 本机应用程序,使用服务的另一种方法是直接通过 Kubernetes API Server 发出请求。API Server 自动公开并维护运行 Pod 的端点。
Kubernetes 服务由哪些组件组成?
Kubernetes 服务将一组 pod 与抽象服务名称和持久 IP 地址相关联
。这使得 Pod 能够发现彼此并将请求路由到彼此。服务使用标签和选择器将 Pod 与其他应用程序进行匹配。例如,服务可能将应用程序的前端连接到后端,每个后端都在集群内的单独部署中运行。
Kubernetes 服务的基本组件是一个标签选择器,用于标识要将流量路由到的 pod、clusterIP 和端口号、端口定义以及传入端口到 targetPort 的可选映射。我们将在下一节中展示服务配置代码的示例。
另一个不太流行的选择是创建一个没有 pod 选择器的服务。这使您可以将服务指向另一个命名空间、集群中的另一个服务或集群外部的静态 IP。
如何创建 Kubernetes 服务
可以使用 YAML 清单来配置 Kubernetes 服务。以下是服务 YAML 的示例:
yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: nginx
ports:
---protocol: TCP
port: 80
targetPort: 8080
Service中的重要内容:
- 元数据:名称--- 这是服务的逻辑名称,在创建服务时也将成为该服务的 DNS 名称。
- spec:selector --- 选择器标识应包含在服务中的 pod。在此示例中,具有标签 app:nginx 的 pod 将成为服务的一部分。
- spec:ports --- 端口配置列表(可以有一个或多个)。每个端口配置定义一个网络协议和端口号。另外,端口配置可以定义 targetPort,这是 pod 应将流量发送到的端口。
要在集群中创建服务对象,请使用以下命令(替换 YAML 文件的路径):
bash
kubectl apply -f /path/to/service-manifest.yaml
Kubernetes 服务有哪些类型?
集群IP
ClusterIP是Kubernetes中默认的服务类型。它接收集群内部 IP 地址,使其只能从集群内部访问。如果需要,您可以在服务清单中设置特定的 clusterIP,但它必须在集群 IP 范围内。
示例:
yaml
apiVersion: v1
kind: Service
metadata:
name: my-clusterip-service
spec:
type: ClusterIP
clusterIP: 10.10.5.10
ports:
---name: http
protocol: TCP
port: 80
targetPort: 8080
NodePort
NodePort 服务构建在 ClusterIP 服务之上,将其暴露给可从集群外部访问的端口。如果不指定端口号,Kubernetes 会自动选择一个空闲端口。每个节点上的 kube-proxy 组件负责监听节点的外部端口并将客户端流量从 NodePort 转发到 ClusterIP。
默认情况下,集群中的所有节点都会侦听服务的 NodePort,即使它们没有运行与服务选择器匹配的 pod。如果这些节点收到用于该服务的流量,则会通过网络地址转换 (NAT) 进行处理并将其转发到目标 Pod。
NodePort 可用于配置外部负载均衡器,将网络流量从集群外部的客户端转发到一组特定的 Pod。为此,您必须为 NodePort 设置特定的端口号,并配置外部负载均衡器以将流量转发到所有集群节点上的该端口。您还需要在外部负载均衡器中配置运行状况检查,以确定节点是否正在运行健康的 Pod。
服务清单中的 nodePort 字段是可选的,允许您指定 30000-32767 之间的自定义端口。
示例:
yaml
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: nginx
ports:
---name: http
protocol: TCP
port: 80
targetPort: 8080
nodePort: 30000
LoadBalancer 负载均衡器
LoadBalancer 服务基于 NodePort 服务,并添加了在公共云和私有云中配置外部负载均衡器的功能。它通过将网络流量转发到集群节点来公开集群内运行的服务。
LoadBalancer 服务类型允许您动态实现外部负载均衡器。这通常需要在 Kubernetes 集群内运行集成,该集群对 LoadBalancer 服务执行监视。
示例:
yaml
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
type: LoadBalancer
clusterIP: 10.0.160.135
loadBalancerIP: 168.196.90.10
selector:
app: nginx
ports:
---name: http
protocol: TCP
port: 80
targetPort: 8080
ExternalName
ExternalName 服务将服务映射到 DNS 名称而不是选择器。您可以使用spec:externalName 参数定义名称。它返回与 externalName 字段的内容匹配的 CNAME 记录(例如 my.service.domain.com),而不使用代理。
这种类型的服务可用于在 Kubernetes 中创建代表外部组件的服务,例如在 Kubernetes 外部运行的数据库。另一个用例是允许一个命名空间中的 Pod 与另一个命名空间中的服务进行通信 - Pod 可以将ExternalName 作为本地服务进行访问。
清单示例:
yaml
apiVersion: v1
kind: Service
metadata:
name: my-externalname-service
spec:
type: ExternalName
externalName: my.database.domain.com
发现 Kubernetes 服务
Kubernetes 集群中的组件可以通过两种方法发现服务:
- DNS --- 启用 DNS 后,DNS 服务器将添加到 Kubernetes 集群。该服务器监视相关的 Kubernetes API 请求,并为创建的每个新服务创建 DNS 记录。这允许集群中的所有 Pod 执行服务的名称解析。
- 环境变量------kubelet 为每个活动服务的节点上运行的所有 pod 添加环境变量。这允许 Pod 访问与服务匹配的其他 Pod。但是,只有在使用该服务的 Pod 之前创建该服务时,此方法才有效(否则所需的环境变量将不存在)。
Kubernetes Headless服务
在某些情况下,服务不需要 clusterIP。您可以通过在服务清单的 spec:clusterIP 字段中指定 none 来创建"无头服务"。这意味着 Kubernetes 不执行负载均衡和代理,并且 kube-proxy 会忽略这些服务。
有两种方法可以为Headless设置 DNS 配置:
- 使用选择器--- 如果使用选择器,端点控制器会在 API 中创建端点记录,修改 DNS 记录并返回指向所需 Pod 的 A 记录。
- 没有选择器------如果不使用选择器,端点控制器将不会创建任何端点记录。
调试 Kubernetes 服务
服务可能出现哪些问题?
假设您在集群中部署了 Pod,并设置了一项将流量路由到它们的服务。如果客户端尝试访问 Pod 并失败,这可能表明您的服务或底层 Pod 存在许多问题:
- 服务不存在
- 服务没有预期的 DNS 名称
- DNS 通常在集群中不工作
- 服务定义不正确
- 服务未正确映射到您的 Pod
- Pod 不工作或不稳定
- 您的节点上存在 kube-proxy 错误
如何才能看到 Pod 看到的内容?
在调试服务问题时,您将希望从映射到服务的 Pod 的角度查看集群。有两种方法可以做到这一点:
运行 busybox pod
以下是如何在集群中运行 BusyBox。开源 BusyBox 项目可让您在一个小型可执行文件中运行许多常见的 Linux 实用程序:
css
kubectl run -it --rm --restart=Never busybox --image=gcr.io/google-containers/busybox sh
使用Bash 操作pod
如果您有一个正在运行的 pod 应该与您的服务关联,请使用以下命令 bash 进入在 pod 上运行的容器并执行 shell 命令:
xml
kubectl exec <pod-name> -c <container-name> -- <commands to execute>
调试流程
以下调试过程将帮助您确定服务通信链的哪一部分被破坏。
在每个步骤中,如果遇到错误,请停止并修复问题。如果一切正常,请继续下一步。
1、检查服务是否存在:
arduino
kubectl get svc <service-name>
2. 尝试通过同一命名空间中的 DNS 名称访问服务:
xml
nslookup <service-name>
3. 尝试通过另一个命名空间中的 DNS 名称访问服务:
xml
nslookup <service-name>.<namespace>
如果成功,则意味着您需要更改客户端应用程序以访问另一个命名空间中的服务,或者在与服务相同的命名空间中运行它。
4. 检查集群中的DNS是否工作:
arduino
nslookup kubernetes.default
5. 检查是否可以通过 IP 地址访问服务--- 从集群中的 pod 运行以下命令:
bash
for i in $(seq 1 3); do
wget -qO- <ip-of-service>
Done
6. 检查服务定义是否正确------以下是服务清单中的常见错误:
markdown
* 应用程序尝试访问的服务端口未在spec.ports中列出
* 服务中定义的targetPort与pod使用的端口不同
* 端口定义被定义为字符串而不是数字
* 对于命名端口,服务中指定的名称与 pod 使用的端口名称不同
* 服务定义中的协议与 Pod 使用的协议不同
7. 检查服务中定义的标签是否与 pod 匹配--- 将 替换为metadata.labels 中指定的标签:
arduino
kubectl get pods -l <label>
如果您发现 Pod 的活动时间不长或已多次重新启动,这可能表明它们不稳定,并且在应用程序尝试访问它们时可能已关闭。
**注意:**也可以在没有选择器标签的情况下定义服务。通过使用与服务同名的静态 IP 定义端点,可以将服务直接与 pod 匹配。然后该服务将直接连接到该 IP。在这种情况下,要对服务进行故障排除,请检查集群中是否存在与该服务同名的端点。
8. 检查服务是否有端点:
arduino
kubectl get endpoints <service-name>
这将返回一个字符串,其中包含与服务匹配的 pod IP 列表。如果它返回 ,则意味着该服务与任何 pod 都不匹配。确保在服务定义和 pod 清单中指定完全相同的标签。
9. 检查端点IP上访问的Pod是否正常工作:
bash
for ep in 10.0.0.1:9376 10.0.0.2:9376 10.0.0.3:9376; do
wget -qO- $ep
done
**注意:**如果 pod 不存在,这并不一定意味着存在问题。在某些情况下,Pod 当前不可用可能是可以接受的;然后,服务应返回 503 错误,以使期望与客户端保持一致。
10. kube-proxy 的错误------在服务的默认实现中,运行在每个 Kubernetes 节点上的 kube-proxy 机制负责实现服务抽象。如果您已经看到这里,问题可能是 kube-proxy 出现故障。
有关调试 kube-proxy 问题的更多详细信息,请参阅 Kubernetes 文档。
此过程仅涵盖 Kubernetes 服务故障的最简单情况。在某些情况下,服务通信链的多个部分可能存在问题,或者集群中的其他移动部分可能会导致错误。