Kubernetes 101,第八部分,网络基础知识
到目前为止,我们已经了解了 Kubernetes 中的主要工作负载对象。
这些对象使我们能够在集群中有效地运行和管理多个应用程序。此外,我们还可以从各种部署策略中获益,创建有状态应用程序,以及执行和调度作业。
为了协同运行,集群内的应用程序(Pod)依赖于彼此之间的通信。
让我们创建一个名为server的 Pod ,在其中运行一个 NGINX 服务器:
$ kubectl run server --image=nginx
pod/server created
🔵 Pod 的内部 IP
与互联网上的一切一样,Kubernetes 集群中的 Pod 被分配了内部 IP 地址,从而可以使用这些 IP 在它们之间进行通信。
$ kubectl describe pod/server | grep IP
172.17.0.5
现在,让我们启动一个名为client的新 Pod ,它执行curl
与服务器通信的命令,然后终止。
$ kubectl run client \
--image=alpine/curl \
-it \
--rm \
--restart=Never \
--command \
-- curl http://172.17.0.5/
- 我们正在使用图像
alpine/curl
,因此无需安装curl
命令 - 该命令
curl
执行并向服务器 Pod 的 80 端口(HTTP)发出 HTTP 请求 - 完成
curl
命令后,要求 Pod不要重新启动,否则 Kubernetes 将执行不断重启
输出如下:
<!DOCTYPE html>
<html>
...
<body>
<h1>Welcome to nginx!</h1>
...
...进而:
pod "client" deleted
那真是太好了,嗯?
🤔
Pod 与容器类似,被设计为短暂的。每当 Deployment 或其他控制器重新启动 Pod 时,都无法保证新的 Pod 会收到与之前相同的 IP 地址。
Pod 应该通过提供更可靠的内部 IP 地址的Kubernetes对象相互通信,而不是依赖于单个 Pod IP 地址的短暂性。
是的,我们正在谈论服务。
🔵 服务
通过将 Pod 放置在服务“后面”,其他 Pod 可以使用服务的 IP 地址而不是单个 Pod IP 地址与它们通信。
$ kubectl expose pod server \
--name=server \
--type=ClusterIP \
--port=80 \
--target-port=80
service/server exposed
该kubectl expose
命令需要以下参数:
name
服务的type
服务的ip 地址,默认类型为ClusterIP。其他类型将在本文后面讨论。port
服务将监听的target-port
服务将向其转发消息的Pod
让我们检查一下服务IP地址:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
server ClusterIP 10.43.205.0 <none> 80/TCP 7s
在 Kubernetes 中公开服务时获取的CLUSTER -IP应用于通信。与单个 Pod IP(由于其短暂性,可能会频繁更改)相比,CLUSTER-IP 提供了更可靠、更稳定的 IP 地址。
⚠️
除非您使用手动删除服务,否则创建新服务将导致分配的CLUSTER-IPkubectl delete
发生变化。
现在,从客户端 Pod,让我们运行curl命令10.42.205.0
,这是服务的 IP 地址:
$ kubectl run client \
--image=alpine/curl \
-it \
--rm \
--restart=Never \
--command \
-- curl http://10.43.205.0/
输出:
<!DOCTYPE html>
<html>
...
<body>
<h1>Welcome to nginx!</h1>
...
👉 服务 DNS
Kubernetes 为每个 Service 提供了一个内部 DNS 表,使我们能够使用固定名称而不是 IP 地址。通过利用此 DNS 解析,Service 可以有效地将消息转发到底层 Pod。
服务地址的模式由以下方式指定:
[service-name].[namespace].svc.cluster.local
让我们更新示例如下:
$ curl http://server.default.svc.cluster.local/
<!DOCTYPE html>
<html>
...
<body>
<h1>Welcome to nginx!</h1>
...
超级棒耶!
🤔
在某些情况下,我们可能需要从 Kubernetes 节点本身的任意端口访问服务。
此访问允许通过指定端口与节点进行通信,然后将其转发给服务。
反过来,服务将消息转发给与其关联的底层 Pod。
这使得可以通过节点的端口转发机制从集群外部访问服务。
👉 服务 NodePort
这种类型的 Kubernetes 服务被分配一个内部集群 IP,此外,在集群中的每个节点上打开一个范围内的端口。30000-32767
例如,让我们考虑一个场景,我们的服务配置为公开节点端口32420
,并且我们有一个由 4 个节点组成的集群:
- 10.40.50.1
- 10.40.50.2
- 10.40.50.3
- 10.40.50.4
该服务确保在所有节点上打开相同的端口(本例中为 32420),并且指向该端口的任何流量都将转发到该服务,然后再转发到底层 Pod。
你说得对,Kubernetes 中的服务就像负载均衡器一样!
$ kubectl expose pod server \
--name=server \
--type=NodePort \
--port=80 \
--target-port=80
然后,让我们获取已打开的端口,在本例中为端口30942
:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
server NodePort 10.43.23.47 <none> 80:30942/TCP 103s
免责声明:
以下命令可能不适用于你的环境,因为这取决于你运行 Kubernetes 集群的位置。我使用的是 OSX 上的 colima。
让我们获取节点名称:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
colima Ready control-plane,master 111m v1.25.0+k3s1
然后,我们来找出 Node 的 IP 地址。由于我是在本地使用的,所以这是我的“虚拟机” IP:
$ kubectl describe node colima | grep InternalIP
192.168.106.2
curl
现在,我们可以直接对节点 IP 和端口执行:
$ kubectl run ...
...
curl http://192.168.106.2:30942/
<!DOCTYPE html>
<html>
...
<body>
<h1>Welcome to nginx!</h1>
...
👉 访问多个节点 IP
手动访问每个节点 IP 可能非常繁琐且容易出错。为了简化此过程,我们可以在集群前引入一个“代理”。
有多种解决方案可用,NGINX就是其中之一。在深入研究 Kubernetes 特定的解决方案之前,让我们先探索一下集群外使用 NGINX 的潜在方案。
一旦我们理解了这一点,我们就可以继续利用 Kubernetes 提供的功能。
👉 困难的方法,NGINX 反向代理
一点免责声明:
我不会详细介绍 NGINX 功能,您可以随时查看 NGINX 文档。
也就是说,让我们配置我们的站点配置,名为“acme.conf”:
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_pass http://192.168.106.2:30942;
}
}
请注意,我们的NGINX 负载均衡器将把消息代理到节点 IP 和端口,在 NGINX 术语中我们将其称为“后端”。
如果我们想要添加更多的 Kubernetes 节点,我们可以根据需要灵活地添加任意数量的“后端”。
是时候启动 NGINX 容器了,它将使用host
网络并为我们的站点配置获取一个挂载卷:
$ docker run -d --rm \
--name nginx-lb \
--net=host \
-v $(pwd)/acme.conf:/etc/nginx/conf.d/acme.conf \
nginx
现在,我们可以使用curl来访问localhost
NGINX,因为 NGINX 将代理到我们集群中的每个节点(站点):
$ curl localhost
<!DOCTYPE html>
<html>
...
<body>
<h1>Welcome to nginx!</h1>
...
然而,在 Kubernetes 这样的云原生环境中管理单独的代理并不理想。
幸运的是,Kubernetes 提供了一个更原生的解决方案:Service 类型的LoadBalancer。
👉 服务负载均衡器
在提供负载均衡器支持的云提供商上, Kubernetes 中的LoadBalancer服务类型将自动向底层提供商请求负载均衡器。
此设置为我们的服务提供了单一入口点,负载均衡器将负责在每个节点上打开必要的端口,确保高效的流量路由。
在我的开发环境中,
colima
带来了一个虚拟集成负载均衡器,它将把我的“localhost”暴露给底层服务。
创建服务:
$ kubectl expose pod server \
--name=server \
--type=LoadBalancer \
--port=80 \
--target-port=80
因此,我们可以确认,在 Kubernetes 中使用 LoadBalancer 服务类型时,该服务将被分配一个用于内部通信的集群 IP 和一个用于外部访问的节点端口:
$ kubectl get svc
server LoadBalancer 10.43.168.46 192.168.106.2 80:31995/TCP 79s
我们可以 curl 到集群 IP、节点以及本地主机:
# Cluster IP, from internal resources such as Pods
$ curl http://10.43.168.46/
# NodePort, from external host
$ curl http://192.168.106.2:31995/
# LoadBalancer, from external host
$ curl localhost
这三个curl
命令将得到相同的输出:
<!DOCTYPE html>
<html>
...
<body>
<h1>Welcome to nginx!</h1>
...
👉 使用 Service LoadBalancer 的局限性
但是,了解在 Kubernetes 中使用服务作为负载均衡器的一些限制非常重要。
让我们创建另一个名为的 Pod other-server
:
$ kubectl run other-server --image=nginx
现在,将其公开other-server
为服务负载均衡器:
$ kubectl expose pod other-server \
--name=other-server \
--type=LoadBalancer \
--port=80 \
--target-port=80
然后,检查服务:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
server LoadBalancer 10.43.168.46 192.168.106.2 80:31995/TCP 15m
other-server LoadBalancer 10.43.43.51 <pending> 80:32344/TCP 52s
请注意,other-server
服务负载均衡器不会获取外部 IP。这是因为该localhost
地址已可供先前调用的服务使用server
。
因此,我们的other-server
服务:
- 可以curl到集群IP
- 可以 curl 到节点中的端口
- 无法 curl 到本地主机
简而言之,Service LoadBalancers 的局限性可以描述如下:
-
云提供商依赖性:由于它依赖于底层云提供商的负载平衡功能,因此可能并非在所有云环境中都可用或受支持。
-
协议支持有限:LoadBalancer 服务通常支持 TCP 和 UDP 协议。其他协议(例如 SCTP 或 WebSocket)的支持情况可能因云提供商和 Kubernetes 版本而异。
-
昂贵的成本:利用云提供商的外部负载均衡器可能会涉及额外的成本,因为一些提供商会对负载均衡器的使用收费。
...等等。
👉 让我们重新启用 NGINX
让我们回顾一下我们提出的使用 NGINX 的解决方案,这是一个开源解决方案,不依赖于任何特定的云提供商。然而,需要注意的是,我们的解决方案将 NGINX 置于集群之外,作为一个独立的组件运行。
那么,将 NGINX 纳入集群怎么样?这将涉及将 NGINX 部署为 Kubernetes 集群的一部分,并提供类似如下的架构:
- NGINX 将充当集群内的“控制器”,负责在每个节点上打开端口并接收消息。
- 某种类似于NGINX 站点配置的“规则表”将用于将消息从 NGINX 转发到适当的内部服务(ClusterIP)。
输入入口控制器。
🔵 Ingress 控制器
Kubernetes 允许使用自定义资源定义 (CRD) 创建新的控制器,从而将功能扩展到基本工作负载控制器之外。
必须在集群中安装Ingress Controller才能提供像 NGINX 这样的代理功能。
幸运的是,有一个名为NGINX Ingress Controller的控制器,它是开源的并且被广泛使用。
它可以通过多种方式安装,包括通过kubectl
:
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.0/deploy/static/provider/cloud/deploy.yaml
让我们检查一下在命名空间中创建的 Pod ingress-nginx
:
$ kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-cvw5d 0/1 Completed 0 44h
ingress-nginx-admission-patch-nhhtr 0/1 Completed 1 44h
ingress-nginx-controller-8d8d76dd7-949dc 1/1 Running 1 (6h45m ago) 44h
如果我们深入研究 Pod 的ingress-nginx-controller-8d8d76dd7-949dc
详细信息,我们会发现它正在运行一个 NGINX 服务器!
另外,通过检查命名空间中的服务ingress-nginx
:
$ kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller-admission ClusterIP 10.43.26.101 <none> 443/TCP 44h
ingress-nginx-controller LoadBalancer 10.43.119.0 192.168.106.2 80:30834/TCP,443:31247/TCP 44h
我们可以看到我们有一个名为的 Service LoadBalancer,ingress-nginx-controller
它将仅路由到入口控制器 Pod。
非常棒,嗯?
是时候应用 NGINX Ingress Controller 将用来将消息转发到我们的底层服务的规则了。
是的,我们来了解一下 Ingress。
👉 Ingress 对象
总之,Ingress 是一个与 Ingress Controller(例如 NGINX 或任何其他控制器)相关联的 Kubernetes 对象。
它支持将各种特定于控制器的功能应用于后端服务,提供一种管理外部访问和在 Kubernetes 集群内应用路由、负载平衡和其他功能的方法。
让我们创建一个IngressClass
代表我们的 IngressController:
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: my-ingress-class
spec:
controller: k8s.io/ingress-nginx
然后,我们创建一个Ingress
与适当的IngressClass
后端服务关联的对象并将一组规则应用于后端服务。
这些规则定义了路由和访问服务的所需行为和配置,在本例中使用路径。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: server-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: my-ingress-class
rules:
- host: localhost
http:
paths:
- path: /server
pathType: Prefix
backend:
service:
name: server
port:
number: 80
- path: /other-server
pathType: Prefix
backend:
service:
name: other-server
port:
number: 80
现在,我们可以使用curl作为localhost
端点,因为入口控制器(在本例中为 NGINX)配置为处理不同的路径。
然后,NGINX 会将请求转发到各自的内部服务,然后再将请求转发到为请求的应用程序或服务提供服务的底层 Pod。
这使我们能够使用指定的路径轻松测试和访问集群内的不同服务。
$ curl localhost/server
$ curl localhost/other-server
耶!
👉 关于云中的入口控制器的说明
请注意,在云环境中使用 Ingress Controller 时,将配置负载均衡器。
但是,与LoadBalancer 类型的服务(其中每个服务都有自己专用的负载均衡器)不同,当使用 Ingress Controller 时,通常只会创建一个云负载均衡器,因为会自动创建一个服务 LoadBalancer。
此共享负载均衡器将处理Ingress配置中定义的多个后端服务的路由和负载均衡。这种方法有助于优化资源利用率,并减少为每个服务管理单独的负载均衡器的开销。
总结
在本指南中,我们探讨了Kubernetes 中的网络基础知识。
我们介绍了 Pod 及其内部 IP 地址、不同类型的服务的基本概念,并讨论了某些限制。我们还探讨了Ingress 控制器如何解决这些限制。
在接下来的文章中,我们将继续深入探讨 Kubernetes 的基础方面。
请继续关注有关 Kubernetes 基础知识的更多深刻内容!
参考
Kubernetes 服务
Kubernetes Ingress
入口控制器
NGINX 文档
NGINX 入口控制器 Docker 镜像
这篇文章是在 ChatGPT 的帮助下撰写的,它在语法上提供了一些“视觉享受”。
文章来源:https://dev.to/leandronsp/kubernetes-101-part-viii-networking-fundamentals-mo7