访问Kubernetes Pods的多种方法介绍

67 阅读10分钟

回到基础知识。访问Kubernetes Pods

Kubernetes是一个巨大的野兽。在它开始发挥作用之前,你需要了解许多概念。在这里,学习几种访问集群外的吊舱的方法。

Kubernetes是一个巨大的野兽。在它开始发挥作用之前,你需要了解许多不同的概念。当一切都设置好后,你可能会想把一些pod暴露在集群的外部。Kubernetes提供了不同的方法来做到这一点。我将在这篇文章中描述它们。

设置

为了进行演示,我将使用kind

kind是一个使用Docker容器 "节点 "运行本地Kubernetes集群的工具。 kind主要是为测试Kubernetes本身设计的,但也可用于本地开发或CI。

我将使用一个双节点集群。

YAML

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30800             # 1
    hostPort: 30800                  # 1
- role: worker                       # 2
- role: worker                       # 2
  • #1:端口转发以应对Mac上的Docker VM层(见下文)
  • #2:两个节点

Shell

kind create cluster -- config kind.yml

接下来,我们需要一个容器。它不应该只是运行和停止。让我们使用在写这篇文章时可用的最新的Nginx镜像。

有了实物,我们必须预装镜像,以便它们可以使用。

Shell

docker pull nginx:1.23
kind load docker-image nginx:1.23

最后,我把kubetcl 别名为k

Shell

alias k=kubectl

默认情况下没有外部访问

默认的情况是不提供对集群外部的访问。

Shell

k create deployment nginx --image=nginx:1.23 # 1
  • #1:创建一个单一pod的部署。

让我们检查一下是否一切正常。

Shell

k get pods

纯文本

NAME                     READY   STATUS    RESTARTS   AGE
nginx-6c7985744b-c7cpl   1/1     Running   0          67s

这个pod有一个IP,但是我们无法在集群外到达它。

Shell

k get pod nginx-6c7985744b-c7cpl --template '{{.status.podIP}}'

纯文本

10.244.1.2

让我们通过在pod内部运行shell来确认这个IP。

Shell

k exec -it nginx-6c7985744b-c7cpl -- /bin/bash
hostname -I

纯文本

10.244.1.2

我们无法成功地在集群外ping到这个IP:这是一个内部IP。

内部IP是不稳定的

我们创建了一个部署;因此,如果我们删除单个pod,Kubernetes会检测到它并创建一个新的,这要感谢它的自我修复能力。

Shell

k delete pod nginx-6c7985744b-c7cpl
k get pods

纯文本

NAME                     READY   STATUS    RESTARTS   AGE
nginx-6c7985744b-c6f92   1/1     Running   0          71s

让我们检查一下它的新IP。

Shell

k exec -it nginx-6c7985744b-c6f92 -- /bin/bash
hostname -I

纯文本

10.244.2.2

Kubernetes创建了一个新的pod,但它的IP与被删除的pod的不同。我们不能依靠这个IP进行pod间的通信。事实上,我们不应该直接使用一个pod的IP。

为了解决这个问题,Kubernetes提供了Service 对象。服务在pod面前代表一个稳定的接口。Kubernetes管理一个服务和它的pod(s)之间的映射关系。它绑定新的pod,并解除对已删除pod的绑定。

ClusterIP:在集群内部的IP上公开Service 。选择这个值使得Service 只能从集群内部到达。这是默认的ServiceType

-发布服务(ClusterIP)

让我们用一个服务来公开现有的部署。

Shell

k expose deployment nginx --type=ClusterIP --port=8080
k get svc

纯文本

NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP    9m47s
nginx        ClusterIP   10.96.93.97   <none>        8080/TCP   4s

从这一点开始,就可以通过服务的ClusterIP 来访问pod。

所有的设置都是为了在集群内部访问。从外部来看,这还不可能。那么我们为什么要使用ClusteIP ?对于那些你不想暴露在外部世界的服务来说,它是非常有用的:数据库、ElasticSearch节点、Redis节点等等。

访问一个Pod

从集群外部访问一个pod是事情变得有趣的时候。

我们首先需要删除现有的部署和服务。

Shell

k delete deployment nginx
k delete svc nginx

允许外部访问的最简单的方法是将服务的类型改为NodePort
NodePort 添加一个访问端口到ClusterIP

NodePort:在每个节点的IP上以一个静态端口(NodePort )暴露服务。一个ClusterIP 服务,NodePort 服务将被自动创建为其路由。你可以通过请求: ,从集群外部联系NodePort 服务。

- 发布服务(NodePort)

我想让pod返回其IP和主机名来演示它。我们必须从命令行转移到专门的Kubernetes清单文件,因为我们必须配置Nginx。其结果与命令行的状态相同,但增加了Nginx配置。

YAML

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.23
        volumeMounts:                                                     # 1
          - name: conf
            mountPath: /etc/nginx/nginx.conf
            subPath: nginx.conf
            readOnly: true
      volumes:                                                            # 1
        - name: conf
          configMap:
            name: nginx-conf
            items:
              - key: nginx.conf
                path: nginx.conf
---
apiVersion: v1                                                            # 1
kind: ConfigMap
metadata:
  name: nginx-conf
data:
  nginx.conf: |
    events {
        worker_connections  1024;
    }
    http {
        server {
            location / {
                default_type text/plain;
                return 200 "host: $hostname\nIP:   $server_addr\n";
            }
        }
    }
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nginx
  type: NodePort                                                          # 2
  ports:
    - port: 80
      nodePort: 30800
  • #1:覆盖默认配置,返回主机名和IP地址。
  • #2:NodePort ,将pod的端口映射到一个外部可访问的端口。

让我们应用该配置。

Shell

k apply -f deployment.yml

注意,我在Mac上运行;因此,在Docker周围有一个VM容器,就像在Windows中一样。由于这个原因,kind需要将虚拟机的端口转发到主机上。请查看kind文档,了解如何实现它。

一旦Kubernetes调度了pod,我们就可以在配置的端口上访问它。

Shell

curl localhost:30800

纯文本

host: nginx-b69d8877c-p2s79
IP:   10.244.2.2

请求的路径如下(尽管Mac/Windows上有虚拟机层):

  • curl ,请求会进入任何节点。

    注意,在云提供商的设置上,你可以针对任何承载部署中的pod部分的Kubernetes节点。在本地设置中,我们的目标是localhost ,让虚拟机层针对一个节点。

  • 节点看到端口30800 ,并将请求转发给相关端口的NodePort 服务。

  • 该服务将请求转发给pod,将端口从30800 转移到80

现在,让我们把部署中的pod数量增加到两个。

Shell

k scale deployment nginx --replicas=2
k get pods -o wide

Kubernetes平衡集群,使每个pod驻留在一个不同的节点上。

纯文本

NAME                    READY   STATUS    RESTARTS   AGE    IP           NODE           NOMINATED NODE   READINESS GATES
nginx-b69d8877c-w7db4   1/1     Running   0          129m   10.244.2.2   kind-worker    <none>           <none>
nginx-b69d8877c-z5kqs   1/1     Running   0          38m    10.244.1.2   kind-worker2   <none>           <none>

请求将被发送到哪个节点/pod?

Shell

while true; do curl localhost:30800; done

纯文本

host: nginx-b69d8877c-w7db4
IP:   10.244.2.2
host: nginx-b69d8877c-w7db4
IP:   10.244.2.2
host: nginx-b69d8877c-z5kqs
IP:   10.244.1.2
host: nginx-b69d8877c-z5kqs
IP:   10.244.1.2
host: nginx-b69d8877c-w7db4
IP:   10.244.2.2
host: nginx-b69d8877c-w7db4
IP:   10.244.2.2

该服务在所有可用的pod之间平衡请求。

负载平衡抽象允许查询任何集群节点

NodePort 允许查询任何集群节点。LoadBalancer 是集群上的一个门面,用于...负载平衡。这是一个由Kubernetes提供的抽象对象:每个云提供商根据其特殊性对其进行不同的实现,尽管其行为是相同的。

LoadBalancer。使用云提供商的负载平衡器向外部开放服务。NodePort 和 ClusterIP 服务将被自动创建,外部负载平衡器将对其进行路由。

- 发布服务(LoadBalander)

首先,我们需要一个LoadBalancer 实现。kind有开箱即用的与MetalLB的集成。

MetalLB是一个用于裸机Kubernetes集群的负载均衡器实现,使用标准路由协议。

-金属LB

转述kind关于如何安装MetalLB的优秀文档是没有用的。我们可以相应地更新清单。

YAML

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nginx
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 30800

让我们来看看这些服务。

Shell

k get svc

纯文本

NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP          4h37m
nginx        LoadBalancer   10.96.216.126   127.0.0.240   8080:31513/TCP   82m     # 1
  • #1:它有一个外部IP!

不幸的是,正如我上面提到的,在Mac(和Windows)上,Docker运行在一个虚拟机中。因此,我们无法从主机上访问 "外部 "IP。拥有适当的Linux系统的读者应该可以访问它。

根据云供应商的情况,LoadBalancer 可能会提供额外的专有功能。

入站,当你需要路由时

Ingress 着重于路由请求到集群中的服务。

它与LoadBalancer 共享某些方面:

  • 它拦截入站流量。
  • 它依赖于实现,实现提供不同的功能:例如 NginxTraefikHAProxy等。

然而,它不是一个Service

Ingress将HTTP和HTTPS路由从集群外暴露给集群内的服务。流量路由是由Ingress资源上定义的规则控制的。

-什么是Ingress?

安装Ingress ,在很大程度上取决于实现。唯一的共同因素是它涉及到CRD。

为了演示,我将使用Apache APISIX Ingress控制器。我不会转述Minikube上Ingress APISIX的安装说明。唯一的区别是将NodePort ,设置为一个设定值。

Shell

helm install apisix apisix/apisix \
  --set gateway.type=NodePort \
  --set gateway.http.nodePort=30800 \
  --set ingress-controller.enabled=true \
  --namespace ingress-apisix \
  --set ingress-controller.config.apisix.serviceNamespace=ingress-apisix

注意,虽然文档中提到了Minikube,但它适用于任何本地集群,包括同类。

以下服务应该在ingress-apisix 名称空间中可用。

普通文本

NAME                        TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
apisix-admin                ClusterIP   10.96.98.159   <none>        9180/TCP            22h
apisix-etcd                 ClusterIP   10.96.80.154   <none>        2379/TCP,2380/TCP   22h
apisix-etcd-headless        ClusterIP   None           <none>        2379/TCP,2380/TCP   22h
apisix-gateway              NodePort    10.96.233.74   <none>        80:30800/TCP        22h
apisix-ingress-controller   ClusterIP   10.96.125.41   <none>        80/TCP              22h

为了演示,我们将有两个服务:每个服务将有一个pod的底层部署。请求/left ,将击中一个服务,并返回left;/right,right

让我们相应地更新拓扑结构。

YAML

apiVersin: apps/v1
kind: Deployment
metadata:
  name: left
  labels:
    app: left
spec:
  replicas: 1
  selector:
    matchLabels:
      app: left
  template:
    metadata:
      labels:
        app: left
    spec:
      containers:
      - name: nginx
        image: nginx:1.23
        volumeMounts:
          - name: conf
            mountPath: /etc/nginx/nginx.conf
            subPath: nginx.conf
            readOnly: true
      volumes:
        - name: conf
          configMap:
            name: left-conf
            items:
              - key: nginx.conf
                path: nginx.conf
---
apiVersion: v1
kind: Service
metadata:
  name: left
spec:
  selector:
    app: left
  ports:
    - port: 80
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: left-conf
data:
  nginx.conf: |
    events {
        worker_connections  1024;
    }
    http {
        server {
            location / {
                default_type text/plain;
                return 200 "left\n";
            }
        }
    }

上面的片段只描述了left 的路径。它应该包含对right 路径的类似配置。

在这一点上,我们可以创建配置,将路径路由到服务。

YAML

apiVersion: apisix.apache.org/v2beta3            # 1
kind: ApisixRoute                                # 1
metadata:
  name: apisix-route
spec:
  http:
  - name: left
    match:
      paths:
      - "/left"
    backends:
    - serviceName: left                          # 2
      servicePort: 80                            # 2
  - name: right
    match:
      paths:
        - "/right"
    backends:
    - serviceName: right                         # 3
      servicePort: 80                            # 3
  1. 使用安装时创建的ApisixRoute CRD。
  2. 将请求转发给left 服务。
  3. 将请求转发到right 服务。

下面是它应该有的样子。请注意,我选择只表示left 路径和一个节点,以避免图表过载。

为了检查它是否有效,让我们再来一次curl。

Shell

curl localhost:30800

纯文本

{"error_msg":"404 Route Not Found"}

这是个好迹象。APISIX有反应了。

我们现在可以尝试卷曲right 路径,以确保它将转发到相关的pod。

Shell

curl localhost:30800/right

纯文本

right

/left,它也能工作。

总结

在这篇文章中,我描述了几种访问集群外的pod的方法:NodePortLoadBalancer 服务以及Ingress 。对于Ingress ,你可能已经注意到,ApisixRoute 对象是一个专有的CRD。为了避免它,Kubernetes旨在提供一个抽象:CNCF正在开发一个Gateway API项目。

我将在未来的文章中描述它。