如何调试 K8S 中 NodePort 拒绝连接的原因?

如何调试 K8S 中 NodePort 拒绝连接的原因?

介绍

我最近收到简单的 Web 应用程序正在运行在具有 MicroK8S 的三节点 Ubuntu 服务器上。我决定尝试重建集群并使用 YAML 清单重新安装所有内容,以确保该过程可复制。但是,现在无法从集群外部访问该应用程序。我正在寻找调试技术来深入了解为什么 NodePort 显然没有在所有节点上创建 TCP 侦听器。

这是我的节点:

姓名 知识产权 颜色 角色
阿伦 192.168.50.251 黄色的 领导者
尼卡 192.168.50.74 蓝色的 工人
山崎 192.168.50.135 绿色的 工人

集群再次选择在第三个节点 Yamazaki 上运行工作负载。我预计到达 Arran 或 Nikka 的任何网络流量都会在内部重新路由到 Yamazaki 进行服务,就像之前发生的那样。

我做了什么

从以前工作的集群/应用程序中,下面是我重置所有内容的操作:

  1. microk8s leave在所有跟随节点上执行

  2. 对每个跟随节点的领导者执行此操作microk8s kubectl delete node <nodename>(他们离开时不会被自动删除)

  3. microk8s reset在所有节点上执行

  4. 启用插件(dns、ingress)。我不知道是否有必要

  5. microk8s add-node在领导者上为每个追随者创建加入命令

  6. microk8s join <ip>/<token>在每个关注者上运行一个新的加入命令

  7. 在任何节点上运行microk8s status以确保集群处于 HA 模式

  8. 使用以下方式从领导者处加载应用程序映像 tarballmicrok8s images import workload.tar

  9. 通过以下方式启动应用程序microk8s kubectl apply -f k8s-manifests/production/pod.yaml -f k8s-manifests/production/nodeport.yaml

    这是 Pod:

     apiVersion: v1
     kind: Pod
     metadata:
       name: k8s-workload
       annotations:
         kubectl.kubernetes.io/last-applied-configuration: |
           {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"k8s-workload","namespace":"default"},"spec":{"containers":[{"image":"k8s-workload","imagePullPolicy":"Never","name":"k8s-workload","ports":[{"containerPort":9090,"protocol":"TCP"}]}]}}
     spec:
       containers:
       - image: k8s-workload
         imagePullPolicy: Never
         name: k8s-workload
         ports:
         - containerPort: 9090
           protocol: TCP
    

    这是 NodePort:

     apiVersion: v1
     kind: Service
     metadata:
       name: np-service
     spec:
       type: NodePort
       ports:
         - port: 9090
           targetPort: 9090
           nodePort: 30090
       selector:
         run: k8s-workload
       # This should not be needed, but it didn't help
       # this time anyway
       externalIPs: [192.168.50.251]
    
  10. 检查应用程序是否通过内部容器调用运行microk8s kubectl exec -ti k8s-workload -- curl http://localhost:9090- 这很好

  11. 检查应用程序是否通过任何节点上的端口转发器运行microk8s kubectl port-forward pod/k8s-workload 9090 --address='0.0.0.0'- 这很好

  12. 节点未在外部进行监听(curl http://localhost:30090连接被拒绝,与 LAN 上非集群机器的任何节点 IP 地址相同)

系统状态

以下是正在运行的内容microk8s kubectl get all -o wide

NAME               READY   STATUS    RESTARTS   AGE   IP             NODE       NOMINATED NODE   READINESS GATES
pod/k8s-workload   1/1     Running   0          20h   10.1.134.193   yamazaki   <none>           <none>

NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP      PORT(S)          AGE     SELECTOR
service/kubernetes   ClusterIP   10.152.183.1     <none>           443/TCP          35d     <none>
service/np-service   NodePort    10.152.183.175   192.168.50.251   9090:30090/TCP   3d21h   run=k8s-workload

我不知道它service/kubernetes是什么,我认为它只是标准 K8S 基础设施的一部分。

观察结果

我认为本文表示我的 Web 应用需要成为一项服务,但我只有一个 pod。我认为以前运行此程序时,我只有一个 pod,但集群变得有点混乱,因此有可能应用的服务版本与 pod 版本同时运行。

这篇文章还建议我应该使用入口系统。但是,考虑到 NodePort 是我目前的学习重点,我还不想就此放弃。入口可以稍后再介绍。

我认为我可以确定没有防火墙问题,因为即使在集群中节点上的控制台会话中,任何到端口 30090 的连接都会被拒绝。

我想运行类似的程序microk8s kubectl logs service np-service来查看 NodePort 正在做什么,但是 logs 子命令只对 pod 有效。

下一步我可以尝试什么?

答案1

当使用kubectl run启动 Pod 时,Kubernetes 会自动用部署时使用的名称来标记它们。

例如,看一下由以下生成的 YAML kubectl run nginx --image=nginx -o yaml

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx <- Automatically assigned
  name: nginx
  namespace: default
spec:
  containers:
  - image: nginx
    ...

现在,假设您提供的 Pod 的 YAMLk8s-workload已完成,则此标签目前缺失。这很重要,因为selector您在 中使用了NodePort's specs

apiVersion: v1
kind: Service
metadata:
  name: np-service
spec:
  ...
  selector:
    run: k8s-workload <- This tells Kubernetes who the Service is for

我猜想 Kubernetes 目前只是找不到该服务对应的 Pod。您可以通过运行来测试这个理论kubectl get pods -l run=k8s-workload。您应该会收到类似这样的错误消息No resources found in default namespace

修复此问题非常简单,只需(重新)分配标签即可。可以使用kubectl label以下命令完成此操作kubectl label pod k8s-workload run=k8s-workload

详细指南如何调试服务以及有关标签和选择器的工作原理可以在官方文档中找到。

更新

关于这种情况是否会被记录:没有端点的服务不是错误,而且(据我所知)不会在任何地方记录。想象一下每周只需要几个小时的部署。部署不活跃,因此服务在 90% 的时间内没有任何端点是可以预料的,并不意味着某些东西配置不正确或无法正常工作。

答案2

正如我所料,解决方案很简单。Eleasar 好心地提供了一个label命令来修复这个问题,但我更喜欢在 YAML 中修复它,因为我认为这更容易重复。这是我的新命令pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: k8s-workload
  labels:
    run: k8s-workload
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"k8s-workload","namespace":"default"},"spec":{"containers":[{"image":"k8s-workload","imagePullPolicy":"Never","name":"k8s-workload","ports":[{"containerPort":9090,"protocol":"TCP"}]}]}}
spec:
  containers:
  - image: k8s-workload
    imagePullPolicy: Never
    name: k8s-workload
    ports:
    - containerPort: 9090
      protocol: TCP

只需两行新代码即可为该对象添加唯一标签。

相关内容