在第 1 部分中,我们讲解了 Kubernetes 的核心组件,Kubernetes 是一种开源容器编排器,用于在分布式环境中部署和扩展应用程序;我们还讲解了如何在集群中部署一个简单的应用程序,然后更改其副本数量以扩大或缩小其规模。
在本文中,我们将为您深入讲解 Kubernetes 提供的网络和监控功能,为您将自己的应用推广到具有公开网络服务和良好可观察性的生产环境中做准备。
Kubernetes 包含一套全面的网络功能,用于将 Pod 连接在一起,并让它们在集群外可见。这里总结了一些较常使用的基础组件。
Service 是 Kubernetes 对象,用于公开在集群中的 Pod 中运行的网络应用程序。网络流量流经服务,被引到正确的 Pod。
Service 有时会让开发人员感到困惑,因为其术语有时会与传统观点重叠。开发人员通常认为 Service 就是他们在集群中运行的应用程序,但在 Kubernetes 中,Service 具体是指允许访问应用程序的网络组件,是将 Kubernetes 对象联网的基本资源。在部署工作负载时,如果 Pods 需要在它们之间或集群外部进行通信,就需要使用服务。
Serivce 模型在部署副本之间分配流量时需要被使用到。例如,如果您为一个应用程序接口部署了四个副本,那么这四个 Pod 就应该共享网络流量。创建 Service 可以实现这一点,您的应用程序可以连接到服务的 IP 地址,然后该 IP 地址会将网络流量转发到其中一个兼容的 Pod。每个 Service 还分配了一个可预测的集群内 DNS 名称,以方便自动发现服务。
Kubernetes 支持几种不同类型的 Service,以适应常见的网络用例。主要有三种:
ClusterIP
:这种类型的服务分配了一个 IP 地址,只能在群集内访问。这可以防止 Pod 被外部调用。由于这是最安全的服务类型,因此在未指定其他服务类型时,它也是默认服务类型。
NodePort
:这些服务也会分配一个内部 IP 地址,但会额外绑定到节点上的指定端口。如果在端口 80 上创建了 NodePort
服务,且其 Pod 由节点 192.168.0.1
托管,则可以通过本地网络上的 192.168.0.1:80
连接到 Pod。
LoadBalancer
:LoadBalancer
Service 将外部 IP 地址映射到群集中。在使用亚马逊 EKS 或 Google GKE 等托管 Kubernetes 解决方案时,创建LoadBalancer
Service 会自动在云账户中提供一个新的LoadBalancer
资源。LoadBalancer
公共 IP 的流量会路由到 Service 背后的 Pod。当打算将 Pod 公开暴露在群集之外时,请使用此 Service 类型。LoadBalancer
允许您首先在群集的物理节点之间路由流量,然后将流量路由到每个节点上的正确 Pod。
1. 创建 Service
下面的 YAML 清单示例定义了一个 ClusterIP
服务,该服务将其 80 端口的流量导向带有 app.kubernetes.io/name: demo
标签的 Pod 的 8080 端口:
<span class="hljs-attribute">apiVersion</span>: v1
<span class="hljs-attribute">kind</span>: Service
<span class="hljs-attribute">metadata</span>:
<span class="hljs-attribute">name</span>: demo
<span class="hljs-attribute">spec</span>:
<span class="hljs-attribute">selector</span>:
app.kubernetes.io/<span class="hljs-attribute">name</span>: demo
<span class="hljs-attribute">ports</span>:
- <span class="hljs-attribute">protocol</span>: TCP
<span class="hljs-attribute">port</span>: <span class="hljs-number">80</span>
<span class="hljs-attribute">targetPort</span>: <span class="hljs-number">8080</span>
将文件保存为 service.yaml
,然后使用 kubectl 将其应用到集群中:
$ kubectl <span class="hljs-built_in">apply</span> -f service.yaml
service/<span class="hljs-built_in">demo</span> created
运行 get services
命令显示分配给 Service 的群集 IP:
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
demo ClusterIP <span class="hljs-number">10.99</span>
<span class="hljs-number">.219</span>
<span class="hljs-number">.177</span> <none>
<span class="hljs-number">80</span>/TCP
<span class="hljs-number">10</span>s
集群中的 Pod 现在可以与该 IP 地址(10.99.219.177
)通信,以连接标有 app.kubernetes.io/name: demo
的相邻 Pod。由于 Service 会自动发现,您还可以使用分配给它的 DNS 主机名访问服务,其形式为
<service-name>.<namespace-name>.svc.<cluster-domain>
在本例中,主机名为
demo.default.svc.cluster.local
。
2. 针对内部和外部 IP 使用正确的 Service 类型
不同情况不同选择,正确的 Service 类型取决于您需要实现的目标。
如果 Pod 只需要在集群内部访问,例如其他集群内应用程序使用的数据库,则使用 ClusterIP
即可。这样可以防止 Pod 意外暴露在外部,提高集群安全性。对于应从外部访问的应用程序,如 API 和网站部署,则应使用 LoadBalancer
。
NodePort
Service 需要谨慎。它们允许你设置自己的负载平衡解决方案,但经常被误用而造成意想不到的后果。当你手动指定端口范围时,请确保避免冲突。NodePort
服务还会绕过大多数 Kubernetes 网络安全控制,让 Pod 暴露在外。
Service 只能在 IP 和端口级别上运行,因此它们通常与 Ingress 对象配对。Ingress 是用于 HTTP 和 HTTPS 路由的专用资源。Ingress 根据主机名和 URI 等请求特征,将 HTTP 流量映射到集群中的不同服务。它们还提供负载平衡和 SSL 终止功能。
重要的是,Ingresses 本身并不是服务。它们位于服务前面,将服务暴露给外部流量。您可以使用 LoadBalancer 服务直接公开一组 Pod,但这会在没有任何过滤或路由支持的情况下推送流量。而使用 Ingress,您可以在服务之间切换流量,例如将 api.example.com
发送到您的 API 以及将 app.example.com
发送到您的前端。
要使用 Ingress,必须在集群中安装一个 Ingress 控制器。它负责将传入流量与您的 Ingress 对象进行匹配。
Kubernetes 默认情况下不捆绑任何选项;NGINX Ingress 和 Traefik 是易于配置的推荐选项。
Ingress 定义了一个或多个 HTTP 路由及其映射到的 Service。下面是一个将来自 example.com
的流量导向您的 demo
服务的基本示例:
<span class="hljs-attribute">apiVersion</span>: networking.k8s.io/v1
<span class="hljs-attribute">kind</span>: Ingress
<span class="hljs-attribute">metadata</span>:
<span class="hljs-attribute">name</span>: demo
<span class="hljs-attribute">spec</span>:
<span class="hljs-attribute">ingressClassName</span>: nginx
<span class="hljs-attribute">rules</span>:
- <span class="hljs-attribute">host</span>: example.com
<span class="hljs-attribute">http</span>:
<span class="hljs-attribute">paths</span>:
- <span class="hljs-attribute">path</span>: /
<span class="hljs-attribute">pathType</span>: Prefix
<span class="hljs-attribute">backend</span>:
<span class="hljs-attribute">service</span>:
<span class="hljs-attribute">name</span>: demo
<span class="hljs-attribute">port</span>:
<span class="hljs-attribute">number</span>: <span class="hljs-number">80</span>
spec.ingressClassName
的正确值取决于您使用的 Ingress 控制器。
网络策略是一种机制,用于控制哪些 Pod 可以相互联网。在没有网络策略的情况下,无论 Pods 是否被 Service 公开,都可以自由通信。
每个策略使用选择器(selector)针对一个或多个 Pod。策略可以列出单独的 Ingress 和 Egress 规则: Ingress规则定义目标 Pod 可以从哪些 Pod 接收流量;Egress 规则限制目标 Pod 的流量流向。
下面是一个示例:
<span class="hljs-attribute">apiVersion</span>: networking.k8s.io/v1
<span class="hljs-attribute">kind</span>: NetworkPolicy
<span class="hljs-attribute">metadata</span>:
<span class="hljs-attribute">name</span>: demo-policy
<span class="hljs-attribute">spec</span>:
<span class="hljs-attribute">podSelector</span>:
<span class="hljs-attribute">matchLabels</span>:
<span class="hljs-attribute">app-component</span>: database
<span class="hljs-attribute">policyTypes</span>:
- Ingress
- Egress
<span class="hljs-attribute">ingress</span>:
- <span class="hljs-attribute">from</span>:
- <span class="hljs-attribute">ipBlock</span>:
<span class="hljs-attribute">cidr</span>: <span class="hljs-number">172.17</span>.<span class="hljs-number">0.0</span>/<span class="hljs-number">16</span>
- <span class="hljs-attribute">podSelector</span>:
<span class="hljs-attribute">matchLabels</span>:
<span class="hljs-attribute">app-component</span>: api
<span class="hljs-attribute">ports</span>:
- <span class="hljs-attribute">protocol</span>: TCP
<span class="hljs-attribute">port</span>: <span class="hljs-number">3306</span>
<span class="hljs-attribute">egress</span>:
- <span class="hljs-attribute">to</span>:
- <span class="hljs-attribute">ipBlock</span>:
<span class="hljs-attribute">cidr</span>: <span class="hljs-number">172.17</span>.<span class="hljs-number">0.0</span>/<span class="hljs-number">16</span>
- <span class="hljs-attribute">podSelector</span>:
<span class="hljs-attribute">matchLabels</span>:
<span class="hljs-attribute">app-component</span>: api
<span class="hljs-attribute">ports</span>:
- <span class="hljs-attribute">protocol</span>: TCP
<span class="hljs-attribute">port</span>: <span class="hljs-number">3306</span>
该策略规定,只有标有 app-component: api
的 Pod 可以与标有 app-component: database
的 Pod 通信。
策略可以包括多个 Ingress 和 Egress 规则,也可以选择完全忽略一种流量类型。如果流量类型被排除在外,过滤则不适用。如果多个网络策略针对一个 Pod,那么流量需要满足组合列表中的每一条规则。
为所有 Pod 设置网络策略是一种良好做法。这样有助于防止受到攻击的 Pod 向群集中的其他工作负载发送恶意流量。虽然默认情况下流量不进行过滤,但您可以创建名称空间级的 deny all
规则,防止与缺乏更具体策略的 Pod 进行通信:
<span class="hljs-symbol">apiVersion:</span> networking.k8s.io/v1
<span class="hljs-symbol">kind:</span> NetworkPolicy
<span class="hljs-symbol">metadata:</span>
<span class="hljs-symbol"> name:</span> deny-all
<span class="hljs-symbol">spec:</span>
<span class="hljs-symbol"> podSelector:</span> {}
<span class="hljs-symbol"> policyTypes:</span>
- Ingress
- Egress
Kubernetes 集群中的工作负载经常使用到机密信息,如数据库密码和 API 密钥。这些值一旦暴露,就会构成严重的安全威胁。Kubernetes ConfigMap 对象是向 Pod 提供键值数据的标准方式。它们封装了 Pod 所需的任意配置值。
下面是带有几个数据字段的简单 ConfigMap 的 YAML 定义:
<span class="hljs-symbol">apiVersion:</span> v1
<span class="hljs-symbol">kind:</span> ConfigMap
<span class="hljs-symbol">metadata:</span>
<span class="hljs-symbol"> name:</span> app-config
<span class="hljs-symbol">data:</span>
<span class="hljs-symbol"> default_auth_token_lifetime:</span> <span class="hljs-number">900</span>
<span class="hljs-symbol"> default_user_name:</span> <span class="hljs-string">"admin"</span>
<span class="hljs-symbol"> external_auth_enabled:</span> true
你可以在 Pod 中以环境变量或挂载卷的形式使用 ConfigMap。前一种策略允许你通过访问命名的环境变量来检索 ConfigMap 密钥的值,而后一种策略则使用挂载卷将 ConfigMap 的内容存入容器文件系统中的文件。
由于 ConfigMap 数据是以纯文本形式未加密存储的,因此用户不会将其用于密码和 API 密钥等用途。相反,在与敏感数据交互时,请创建一个 Secret 对象。Secrets 对象的工作原理与 ConfigMap 类似,但它们是专为更安全的凭证处理而设计的。Secrets 对象默认以 Base64 编码显示值,并将其与应用程序的常规配置分开,从而降低了无意暴露的风险。这样就能最大限度地降低意外暴露的风险。
你可以在启动集群时配置 Kubernetes API 服务器,选择启用 Secrets 数据加密。
继安全性之后,有效的监控和日志记录功能应成为 Kubernetes 的下一个优先事项。对工作负载性能的良好可见性能让你在新出现的问题造成更大问题之前快速做反应。您可以使用指标、警报和日志来跟踪集群活动。
Kubernetes Metrics Server 是一个可以安装在集群中的插件。它提供一个 API,用于从节点和 Pod 中提取资源利用率数据。
运行以下 kubectl 命令来部署 Metrics Server:
$ kubectl apply -f https:<span class="hljs-regexp">//gi</span>thub.com<span class="hljs-regexp">/kubernetes-sigs/m</span>etrics-server<span class="hljs-regexp">/releases/</span>latest<span class="hljs-regexp">/download/</span>components.yaml
安装完成后,使用 kubectl top
查看集群节点和 Pod 的 CPU 和内存利用率:
$ kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
minikube <span class="hljs-number">265</span>m
<span class="hljs-number">6</span>%
<span class="hljs-number">640</span>Mi
<span class="hljs-number">8</span>%
$ kubectl top pod
NAME CPU(cores) MEMORY(bytes)
nginx <span class="hljs-number">0</span>m
<span class="hljs-number">4</span>Mi
Kube State Metrics 是另一个度量数据源。它提供与集群内对象相关的指标,如运行中的部署、失败的 Pod 和有效任务的数量。
如果你必须手动查询才能获得所需的信息,那么度量指标就不会特别有用。而设置警报和仪表板可确保您了解重要的集群事件,如资源利用率攀升或 Pod 出现故障。
Kubernetes 项目没有为这些功能提供内置解决方案。因此最好部署一个专用的可观察性平台,如 Prometheus。Prometheus 的 Alertmanager 可以在特定事件发生时向通信平台发送通知。将 Prometheus 与 Grafana 等仪表盘工具搭配使用,可将数据可视化并发现指标趋势。
设置操作仪表盘是监控集群的有效方法,可让您对 Kubernetes 和应用程序的健康状况和性能一目了然。
显然,了解指标变化或 Pod 失效的原因至关重要,应用程序生成的日志应该能揭示这些信息。
您可以使用 kubectl logs
命令按 Pod 检索日志,但使用 Fluentd、Logstash 或 Loki 等整合工具可以更轻松地收集、过滤和归档记录。这些解决方案能将集群中的日志流式传输到单独的存储平台,以供长期参考。
日志整合还能让搜索日志和分析重复消息变得更容易。它们会对报文内容进行索引,因此您无需使用 shell 工具手动解析日志,就能对其进行查询。这有助于您发现应用程序中的新错误,追踪问题的根本原因,并在出现异常时及时发现。
本系列由两部分组成,介绍了 Kubernetes 的基础知识,包括为什么它是开发人员亟需的技能,以及它的抽象如何有效地模拟不同的应用组件。通过该文章,您应该已经了解了如何创建部署、设置网络和安全以及监控集群内的工作负载。
无论您是从事开发还是运维工作,Kubernetes 知识都很有价值。它能让你更深入地了解云原生应用如何在生产中运行、操作员为何偏爱某些技术、哪里可能出现问题,以及您的系统如何映射到基础设施资源。当然,使用本地 Kubernetes 集群进行开发也有助于避免环境之间的差异。
参考链接:
https://komodor.com/blog/a-software-developers-guide-to-getting-started-with-kubernetes-part-1/