Kubernetes 中的服务是什么?
在 Kubernetes 环境中,您可以拥有数百甚至数千个临时的 pod。 无论是因为节点被缩减,pod 副本被缩减,还是 pod 被重新调度到新节点,pod 的 IP 地址都无法保证。 Pod 的 IP 地址是在它被调度到一个特定的节点之后并且在它被引导之前分配的。
鉴于我们处于云原生环境中,我们希望能够水平扩展 Pod。 因此,要跟踪我们所有应用程序的所有 IP 地址将非常困难。 Kubernetes 有一种资源类型可以解决这个不断变化的 Pod IP 的问题,称为服务。 Kubernetes 服务允许您创建单个常量 IP 地址,其中包含一组包含相同服务的 pod 的 IP 地址。 这非常有用,因为服务 IP 保持静态,而 pod IP 可以不断变化。 您永远不必担心没有正确的 IP 地址。
服务怎样运作?
服务怎样运作? 怎样配置一个?
让我们首先创建一个包含三个 Nginx pod 实例的部署。
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
应用部署清单后,我们可以看到我们有三个使用 IP 运行的 nginx 实例 10.244.242.66, 10.244.242.67, 10.244.230,199
. 另外,请注意我们分配给这些 pod 的标签 app=nginx
. 这对于服务怎样监控这些 pod 至关重要。
$ kubectl get pods -owide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE LABELS
nginx-8f458dc5b-6vcxt 1/1 Running 0 2m27s 10.244.242.66 nodepool-a-95e9c8e86208 app=nginx,pod-template-hash=8f458dc5b
nginx-8f458dc5b-ktvtf 1/1 Running 0 2m27s 10.244.242.67 nodepool-a-95e9c8e86208 app=nginx,pod-template-hash=8f458dc5b
nginx-8f458dc5b-mlkwd 1/1 Running 0 2m27s 10.244.230.199 nodepool-a-11aa1dc199fa app=nginx,pod-template-hash=8f458dc5b
现在,让我们定义我们的服务清单,它在下面定义了定义的细分。
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
selector:
app: nginx
type: ClusterIP
ports:
- name: 80-80
port: 80
protocol: TCP
targetPort: 80
当我们开始分解这个清单时,我们注意到那种是 Service
. 接下来,元数据部分应该很熟悉,因为它与其他 Kubernetes 资源上的元数据字段相同。 规范部分定义了我们的服务将怎样以及与什么进行交互。
这 selector
场地。 您可以在此字段中定义您希望此服务监控哪些 pod。 这是通过标签匹配完成的。 你会注意到这 selector
有一个定义 app: nginx
. 如果您还记得,这些是我们在部署清单中为我们的 pod 定义的标签。 通过这个选择器定义,我们声明任何带有标签的 pod app=nginx
将成为这项服务的一部分。
接下来,我们有 type
场地。 这定义了我们想要的服务类型。 稍后我们将深入研究服务类型,但我们现在刚刚将其定义为 ClusterIP
.
最后,我们有 ports
场地。 在这里,我们定义了流量将怎样以及在何处流经服务。 请注意,此字段接受一个数组,以便您可以定义多个条目。
让我们分解端口中的每个字段:
name:您可以为给定的端口条目分配一个特定的名称。
端口:这是服务将侦听的端口。
targetPort:这是服务将请求转发到的端口。 这应该与 pod 正在侦听的端口匹配。
协议:您希望服务侦听并与之交互的特定协议。
现在我们对服务清单的作用以及它怎样与 pod 交互有了基本的了解,我们部署清单并检查服务。
$ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 90m
nginx ClusterIP 10.108.176.62 <none> 80/TCP 18m
$ kubectl describe svc nginx
Name: nginx
Namespace: default
Labels: app=nginx
Annotations: <none>
Selector: app=nginx
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.108.176.62
IPs: 10.108.176.62
Port: 80-80 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.230.199:80,10.244.242.66:80,10.244.242.67:80
Session Affinity: None
Events: <none>
describe 命令中的所有内容都应与清单定义一致,除了两个字段 IP
和 Endpoints
.
这 IP
字段定义服务的 IP 地址。 这是可用于访问服务背后的 Pod 的静态 IP。
这 Endpoints
字段定义当前分配给此服务的 pod IP。 您注意到这些 IP 来自我们的 Nginx 部署。
在我们继续讨论其他服务类型之前,让我们展示缩小 Nginx 部署并看看该服务将怎样处理这种变化。
$ kubectl scale deployment/nginx --replicas 1
deployment.apps/nginx scaled
$ kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-8f458dc5b-mlkwd 1/1 Running 0 43m 10.244.230.199 nodepool-a-11aa1dc199fa <none> <none>
$ kubectl describe service nginx | grep Endpoints
Endpoints: 10.244.230.199:80
$ kubectl scale deployment/nginx --replicas 5
deployment.apps/nginx scaled
$ kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-8f458dc5b-9pmhb 1/1 Running 0 108s 10.244.242.69 nodepool-a-95e9c8e86208 <none> <none>
nginx-8f458dc5b-mlkwd 1/1 Running 0 47m 10.244.230.199 nodepool-a-11aa1dc199fa <none> <none>
nginx-8f458dc5b-r2pcp 1/1 Running 0 108s 10.244.242.70 nodepool-a-95e9c8e86208 <none> <none>
nginx-8f458dc5b-vgwv6 1/1 Running 0 108s 10.244.242.68 nodepool-a-95e9c8e86208 <none> <none>
nginx-8f458dc5b-x6thl 1/1 Running 0 108s 10.244.230.200 nodepool-a-11aa1dc199fa <none> <none>
$ kubectl describe service nginx | grep Endpoints
Endpoints: 10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...
您可以看到,向上和向下扩展 Pod 会立即反映在服务中,允许单个静态 IP 访问 Pod。
服务类型
在服务资源中,存在三种不同的类型。 它们如下:
集群IP
节点端口
负载均衡器
这些都有自己的行为和特定用例,因此了解何时使用每一个非常重要。 在接下来的部分中,我们将深入探讨其中的每一个。
集群IP
ClusterIP 是“最基本”的服务类型。它将创建一个静态 IP 地址,该地址位于与 Pod IP 范围不同的子网上,并通过标签选择器监视一组 Pod。
ClusterIP 允许 pod 轻松地相互通信,因为 pod 可以向静态 IP 发送请求,也可以使用 Kubernetes DNS 向 {service-name}.{namespace}
. 在之前 example 我们部署,DNS 本来是 nginx.default
. 使用 ClusterIP 有一些限制。 最大的问题是无法将这项服务暴露给外界。 这是服务类型 nodePort
和 loadBalancer
应该使用。
节点端口
这 NodePort
服务建立在 ClusterIP
类型。 它暴露了 ClusterIP
通过在工作节点上打开一个端口来转发流量到外部世界。 这意味着如果您有 50 个工作节点,每个工作节点都会在分配的端口上侦听,即使 pod 不在该工作节点上。
创建一个 NodePort
服务唯一的区别是定义 Type
作为 NodePort
kind: Service
metadata:
labels:
app: nginx
name: nginx
spec:
ports:
- name: 80-80
port: 80
protocol: TCP
targetPort: 80
# nodePort: 30001
selector:
app: nginx
type: NodePort
你可能会注意到 nodePort
被注释掉了。 这是因为如果你不定义 nodePort
内 ports
部分,Kubernetes 将自动从范围内分配一个随机端口 30000-32767
. 如果你想定义一个特定的端口,它必须在这个范围内完成。
部署 NodePort 服务清单并让我们对其进行审查。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 135m
nginx NodePort 10.98.246.253 <none> 80:30828/TCP 5s
$ kubectl describe svc nginx
Name: nginx
Namespace: default
Labels: app=nginx
Annotations: <none>
Selector: app=nginx
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.98.246.253
IPs: 10.98.246.253
Port: 80-80 80/TCP
TargetPort: 80/TCP
NodePort: 80-80 30828/TCP
Endpoints: 10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
在这里,我们看到一个名为 NodePort
与价值 30828/TCP
. 这是分配给此服务的端口和协议。 现在,如果我们使用该端口向我们的任何工作节点 IP 地址发送请求,请求将被发送到服务,然后被发送到我们的 pod。
$ kubectl get nodes -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
nodepool-a-11aa1dc199fa Ready <none> 140m v1.24.4 10.1.96.5 45.77.152.173 Debian GNU/Linux 10 (buster) 4.19.0-22-amd64 containerd://1.6.8
nodepool-a-95e9c8e86208 Ready <none> 140m v1.24.4 10.1.96.4 140.82.44.105 Debian GNU/Linux 10 (buster) 4.19.0-22-amd64 containerd://1.6.8
$ curl 140.82.44.105:30828
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
$ curl 45.77.152.173:30828
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
虽然这是一种将我们的服务暴露给外部世界的有用方式,但它不能很好地扩展。 在高度自动扩展的 Kubernetes 集群中,工作节点可能是短暂的,因为它们可能会不断地上下旋转。 这让我们遇到了与 pod 类似的问题,我们可以在其中找出 IP 是什么,但这并不意味着它们会一直存在。 如果您的服务需要静态外部 IP 地址,则 LoadBalancer
服务类型将满足该需求。
负载均衡器
类似于怎样 NodePort
Â建立在 ClusterIP
, LoadBalancer
服务建立在之上 NodePort
. 随着 LoadBalancer
服务,每个工作节点继续具有分配给特定服务的唯一端口。 但是,现在部署并配置了 L4 以路由到特定的工作节点和端口。 一个高级的请求流程如下:一个请求被发送到负载均衡器,请求被转发到特定节点端口上的工作节点,然后被传递到特定的服务,最后被发送到一个 pod。
配置一个 LoadBalancer
如果需要,服务会涉及更多。 一个非常简单的配置可以如下,只需 type
字段被更改为 LoadBalancer
.
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx
name: nginx
spec:
ports:
- name: 80-80
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
type: LoadBalancer
为了 example,如果您希望更改负载均衡器以强制 SSL 重定向、配置防火墙规则、使用 HTTPS 而不是 HTTP,或者使用不同的平衡算法,那么这将需要定义 Annotations
 为您服务。
这是一个这样的 example LoadBalancer
显现。
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/vultr-loadbalancer-protocol: "http"
service.beta.kubernetes.io/vultr-loadbalancer-https-ports: "443"
# You will need to have created a TLS Secret and pass in the name as the value
service.beta.kubernetes.io/vultr-loadbalancer-ssl: "ssl-secret"
service.beta.kubernetes.io/vultr-loadbalancer-algorithm: "least_connections"
service.beta.kubernetes.io/vultr-loadbalancer-ssl-redirect: "true"
service.beta.kubernetes.io/vultr-loadbalancer-firewall-rules: "0.0.0.0/80;0.0.0.0/443"
labels:
app: nginx
name: nginx
spec:
ports:
- port: 80
name: "http"
- port: 443
name: "https"
selector:
app: nginx
type: LoadBalancer
有关更多详细信息,请参阅我们的 所有可用注释的列表 可与 Vultr 负载均衡器一起使用。
当你第一次部署你的 LoadBalancer
服务,您会看到 EXTERNAL-IP
部分是 pending
$ portfolio [talk-postman] â¡ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 150m
nginx LoadBalancer 10.103.252.120 <pending> 80:30092/TCP 4s
$ portfolio [talk-postman] â¡ kubectl describe service nginx
Name: nginx
Namespace: default
Labels: app=nginx
Annotations: <none>
Selector: app=nginx
Type: LoadBalancer
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.103.252.120
IPs: 10.103.252.120
Port: 80-80 80/TCP
TargetPort: 80/TCP
NodePort: 80-80 30092/TCP
Endpoints: 10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...
Session Affinity: None
External Traffic Policy: Cluster
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal EnsuringLoadBalancer 8s (x4 over 44s) service-controller Ensuring load balancer
Warning SyncLoadBalancerFailed 8s (x4 over 44s) service-controller Error syncing load balancer: failed to ensure load balancer: load-balancer is not yet active - current status: pending
您注意到这里 Kubernetes 已经部署了一个 Vultr 负载均衡器,并且正在不断地通信以检查负载均衡器是否已被配置并分配了一个 IP。
配置 Vultr 负载均衡器并准备好流量后,您将在 EXTERNAL-IP
字段和 LB 已得到保证”。这意味着 Kubernetes 现在可以控制相应地监视和更新负载均衡器。另外,请注意分配了一个唯一的节点端口。
$ portfolio [talk-postman] â¡ k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 154m
nginx LoadBalancer 10.103.252.120 45.63.15.140 80:30092/TCP 4m9s
$ kubectl describe service nginx
Name: nginx
Namespace: default
Labels: app=nginx
Annotations: <none>
Selector: app=nginx
Type: LoadBalancer
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.103.252.120
IPs: 10.103.252.120
LoadBalancer Ingress: 45.63.15.140
Port: 80-80 80/TCP
TargetPort: 80/TCP
NodePort: 80-80 30092/TCP
Endpoints: 10.244.230.199:80,10.244.230.200:80,10.244.242.68:80 + 2 more...
Session Affinity: None
External Traffic Policy: Cluster
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning SyncLoadBalancerFailed 2m21s (x5 over 3m37s) service-controller Error syncing load balancer: failed to ensure load balancer: load-balancer is not yet active - current status: pending
Normal EnsuringLoadBalancer 61s (x6 over 3m37s) service-controller Ensuring load balancer
Normal EnsuredLoadBalancer 60s service-controller Ensured load balancer
为了验证我们的 Vultr 负载均衡器是否正常工作,我们可以向 EXTERNAL-IP
,这是我们的负载均衡器公共 IP 地址。
$ curl 45.63.15.140
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
你可以看看 LoadBalancer
服务类型作为一个了不起的解决方案。 但是,它们有一个缺点。 您不能定义任何类型的 URL 路径,这不允许灵活性或将其路由到多个服务。 这 LoadBalancer
服务绑定到单个服务,因此如果您想从集群中公开另一个应用程序,您将部署另一个 Vultr 负载均衡器,如果您有多个应用程序,它可以快速增加成本。
这个问题的解决方案不是另一种服务类型,而是一种不同的 Kubernetes 资源,称为 ingress
.
入口
一个 ingress
resource 是一种更灵活的方式来定义外部世界可以访问的服务。 这里最大的卖点之一是您可以定义单个入口资源,并将此路由路由到您定义的子域、端口或 URL 路径下的所有服务。 它们还提供大量其他功能,例如速率限制、身份验证、自动 TLS、加权路由等等。 所有这些都可以通过一个负载均衡器来完成 Ingress
资源与 LoadBalancer
服务类型,唯一的区别是部署负载均衡器充当代理将所有流量发送到 Ingress
资源,它充当集群中的 L7 负载均衡器。
这里唯一需要注意的是 Kubernetes 没有内部入口控制器。 那里有各种入口控制器,都具有不同类型的功能。 最终,在决定安装哪个入口控制器之前,这取决于您的需求。 有关配置指南,请参阅入口控制器的文档。
一些比较流行的 Ingress 控制器是:
总结
虽然这篇文章可能会让您对不同类型的 Kubernetes 服务和入口控制器有一个很好的了解,但这只是涉及服务。 这些服务还有更多配置选项和一些免费功能,例如网络策略。 请查看下面列出的一些链接以获取更多信息。
一些有用的链接:
文章标题 名称(可选) 电子邮件(可选) 描述
发送建议
注:本教程在Vultr VPS上测试通过,如需部署请前往Vultr.com