From 724336bce318b25306165ffc494fb861f02f0d00 Mon Sep 17 00:00:00 2001 From: isno Date: Wed, 17 Jan 2024 11:21:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Observability/metrics.md | 6 +- architecture/container.md | 8 +- container/k8s-deploy-containerd.md | 6 ++ container/resource-limit.md | 29 +++++- kubernetes/cpu-resource.md | 15 --- kubernetes/ingress.md | 84 --------------- kubernetes/service.md | 161 ----------------------------- 7 files changed, 40 insertions(+), 269 deletions(-) delete mode 100644 kubernetes/cpu-resource.md delete mode 100644 kubernetes/ingress.md delete mode 100644 kubernetes/service.md diff --git a/Observability/metrics.md b/Observability/metrics.md index 8a64af57..33f0d818 100644 --- a/Observability/metrics.md +++ b/Observability/metrics.md @@ -13,4 +13,8 @@ 云原生监控指标可观测产品大都是从传统的监控产品发展而来的,传统监控中Zabbix以其高可用和图形化展示而广受欢迎。 -而在云原生时代,CNCF孵化的监控工具Prometheus取代了以Zabbix为代表的众多传统监控工具,已基本成为云原生监控体系通用的解决方案,并可以通过配合Grafana工具实现监控数据图形化进行可视化分析。 \ No newline at end of file +而在云原生时代,CNCF孵化的监控工具Prometheus取代了以Zabbix为代表的众多传统监控工具,已基本成为云原生监控体系通用的解决方案,并可以通过配合Grafana工具实现监控数据图形化进行可视化分析。 + +从存储上来讲所有的监控指标metric都是相同的,但是在不同的场景下这些metric又有一些细微的差异。例如,在Node Exporter返回的样本中指标node_load1反应的是当前系统的负载状态,随着时间的变化这个指标返回的样本数据是在不断变化的。而指标node_cpu所获取到的样本数据却不同,它是一个持续增大的值,因为其反应的是CPU的累积使用时间,从理论上讲只要系统不关机,这个值是会无限变大的。 + +为了能够帮助用户理解和区分这些不同监控指标之间的差异,Prometheus定义了4种不同的指标类型(metric type):Counter(计数器)、Gauge(仪表盘)、Histogram(直方图)、Summary(摘要)。 \ No newline at end of file diff --git a/architecture/container.md b/architecture/container.md index e0e8208a..b19ea14e 100644 --- a/architecture/container.md +++ b/architecture/container.md @@ -1,6 +1,8 @@ # 1.5.1 容器技术 -虽然容器概念是在 Docker 出现以后才开始在全球范围内火起来的,但容器却不是从 Docker 诞生的。事实上,容器连新技术都算不上,在 Docker 之前,就已经有无数先驱在探索这一极具前瞻性的虚拟化技术。 +时间回到 2013 年,当一条简单的 docker run postgre 命令就能运行起 postgre 这样复杂的传统服务时,开发者在震惊之余犹如受到天启。 + +以 docker 为代表的实用容器技术的横空出世,也预示着一扇通往敏捷基础设施的大门即将打开。越来越多的开发者开始采用容器作为一种标准构建和运行方式,同时业界也意识到,很容易将这种封装方式引入计算集群,通过 Kubernetes 或 Mesos 这样的编排器来调度计算任务 —— 自此,容器便成为这些调度器最重要的 workload 类型。 本节内容我们概览容器技术演进历程以及各个阶段所试图解决的问题,以便更全面地了解容器技术。 @@ -14,9 +16,9 @@ chroot 被认为是最早的容器化技术之一,chroot 可以重定向进程 ## 2.Linux 容器阶段:封装系统 -2006 年,Google 推出 Process Container(进程容器),Process Container 的目的非常直白,它希望能够像虚拟化技术那样给进程提供操作系统级别的资源限制、优先级控制、资源审计能力和进程控制能力。带着这样的设计思路,Process Container 推出不久就进入了 Linux Kernel 主干,不过由于 container 这一命名在内核中具有许多不同的含义,为了避免代码命名的混乱,后来就将 Process Container 更名为了 Control Groups,简称 cgroups。 +2006 年,Google 推出 Process Container(进程容器),Process Container 的目的非常直白,它希望能够像虚拟化技术那样给进程提供操作系统级别的资源限制、优先级控制、资源审计能力和进程控制能力。带着这样的设计思路,Process Container 推出不久就进入了 Linux 内核主干,不过由于 container 这一命名在内核中具有许多不同的含义,为了避免代码命名的混乱,后来就将 Process Container 更名为了 Control Groups,简称 cgroups。 -2008 年 Linux Kernel 2.6.24 刚刚开始提供 cgroups 的同一时间,社区开发者将 cgroups 的资源管理能力和 Linux namespace(命名空间)的资源隔离能力组合在一起,形成了完整的容器技术 LXC(Linux Container,Linux 容器),这就是如今被广泛应用的容器技术的实现基础。 +2008 年 Linux 内核版本 2.6.24 刚刚开始提供 cgroups 的同一时间,社区开发者将 cgroups 的资源管理能力和 Linux namespace(命名空间)的资源隔离能力组合在一起,形成了完整的容器技术 LXC(Linux Container,Linux 容器),这就是如今被广泛应用的容器技术的实现基础。 至 2013 年,Linux 虚拟化技术已基本成型,通过 cgroups、namespace 以及安全防护机制,大体上解决了容器核心技术“运行环境隔离”。彼时,虽然容器运行环境隔离基础已经基本就位,但仍需等待另一项关键技术的出现,才能迎来容器技术的全面繁荣。 diff --git a/container/k8s-deploy-containerd.md b/container/k8s-deploy-containerd.md index 782de73b..cddd2693 100644 --- a/container/k8s-deploy-containerd.md +++ b/container/k8s-deploy-containerd.md @@ -80,6 +80,11 @@ kubelet 和底层容器运行时都需要对接 cgroup 实现容器的资源的 部分系统譬如 debian、centos7 都是使用 systemd 初始化系统,相当于已经有一套 cgroup 资源分配视图了。如果 kubelet 和容器运行时使用 cgroupfs ,也就意味着一个系统里面存在两套资源分配视图。 +kubernetes 1.25.0 版本已经全面支持 cgroup v2[^3],将 cgroupDriver 配置为 systemd,这样将 kubelet 可以通过 systemd 在 cgroup 的 v1 和 v2 版本之间进行自适应: +- 操作系统发行版启用 cgroup v2 +- Linux 内核为 5.8 或更高版本 +- 容器运行时支持 cgroup v2(containerd v1.4+、cri-o v1.20+) + 4. 创建 containerd 的 systemd service 文件 也可从 gtihub 中下载 containerd service 配置文件[^2],确认二进制执行文件配置正确。 @@ -164,4 +169,5 @@ $ ctr run docker.io/library/nginx:alpine nginx [^2]: 参见 https://raw.githubusercontent.com/containerd/containerd/main/containerd.service [^1]: 参见 https://github.com/kubernetes/cri-api/blob/master/pkg/apis/runtime/v1/api.proto +[^3]: 参见 https://kubernetes.io/zh-cn/docs/concepts/architecture/cgroups/ diff --git a/container/resource-limit.md b/container/resource-limit.md index 8c8c67ec..0e7ece68 100644 --- a/container/resource-limit.md +++ b/container/resource-limit.md @@ -1,8 +1,18 @@ # 7.7 资源与调度 -对于 Kubernetes 编排系统而言,Node 是资源的提供者,Pod 是资源的使用者,调度的意思是实现两者之间最恰当的撮合,撮合的关键在于容器编排系统如何管理以及分配集群节点的资源。 - -管理集群的资源至少要考虑这几个问题:资源模型的抽象(有何种资源、如何表示这些资源);资源的调度(如何描述一个资源申请、如何描述一台 node 资源分配的状态以及调度的算法);资源不足时,如何驱逐,如何实现 Pod 的优先级。 +调度是编排系统最重要的功能之一。对于 Kubernetes 这个编排系统而言,Node 是资源的提供者,Pod 是资源的使用者,Kuberntes 调度的职责便是实现两者之间最恰当的撮合,撮合的关键在于容器编排系统。 + +想要实现资源恰当的撮合,管理以及分配集群节点的资源,至少要清楚这么几个问题:资源模型的抽象(有哪些种类的资源,如何表示这些资源)、 +- 资源模型的抽象 + - 有哪些种类的资源,例如,CPU、内存等; + - 如何用数据结构表示这些资源 +- 资源的调度 + - 如何描述一个 workload 的资源申请(spec),例如,“该容器需要 4 核和 12GB~16GB 内存”; + - 如何描述一台 node 当前的资源分配状态,例如已分配/未分配资源量,是否支持超分等 + - 调度算法:如何根据 workload spec 为它挑选最合适的 node +- 资源的限额 + - 如何确保 workload 使用的资源量不超出预设范围 + - 如何确保 workload 和系统/基础服务的限额,使二者互不影响 ## 资源模型的抽象 @@ -10,7 +20,7 @@ 可压缩的资源典型的是 CPU,此类资源超限时,容器进程使用 CPU 会被限制,应用表现变得卡顿,业务延迟明显增加,但**容器进程不会被杀掉**。在 Kubernetes 中,一个 Node 节点 CPU 核心数量乘以 1000,得到的就是该 Node 节点总的 CPU 总数量。CPU 的计量单位为毫核(m),计量方式有多种写法:50m、0.5(`1000m*0.5`)。CPU 的计量是绝对值,这意味着无论容器运行在单核、双核机器上,500m CPU 表示的是相同的计算能力。 -不可压缩的资源为内存、显存(GPU),此类资源不足时,容器进程产生 OOM 问题(Out of Memory,内存溢出)并**被杀掉**。内存的计算单位为字节,计量方式有多种写法,譬如使用 M(Megabyte)、Mi(Mebibyte)以及不带单位的数字,以下表达式所代表的是相同的值。 +不可压缩的资源为内存、显存(GPU),容器之间无法共享且完全独占,这也就意味着资源一旦耗尽或者不足,容器进程一定产生 OOM 问题(Out of Memory,内存溢出)并**被杀掉**。内存的计算单位为字节,计量方式有多种写法,譬如使用 M(Megabyte)、Mi(Mebibyte)以及不带单位的数字,以下表达式所代表的是相同的值。 ```plain 128974848, 129e6, 129M, 123Mi @@ -24,7 +34,16 @@ Kubernetes 抽象了两个概念 requests 和 limits 用以描述容器资源的 - **requests** 容器需要的最小资源量。举例来讲,对于一个 Spring Boot 业务容器,这里的 requests 必须是容器镜像中 JVM 虚拟机需要占用的最少资源。 - **limits** 容器最大可以消耗的资源上限,防止过量消耗资源导致资源短缺甚至宕机。 -Pod 是由一到多个容器组成,所以资源的需求作用在容器上的。如下图所示,每一个容器都可以独立地通过 resources 属性设定相应的 requests 和 limits,容器的资源调度以 requests 为准。 +Pod 是由一到多个容器组成,所以资源的需求作用在容器上的。如下图所示,每一个容器都可以独立地通过 resources 属性设定相应的 requests 和 limits, + + + +CPU 属于可压缩资源,其中 CPU 资源的分配和管理是 Linux 内核借助于完全公平调度算法( CFS )和 Cgroup 机制共同完成的。简单地讲,如果 pod 中服务使用 CPU 超过设置的 CPU limits, pod 的 CPU 资源会被限流( throttled )。对于没有设置 limit 的 pod ,一旦节点的空闲 CPU 资源耗尽,之前分配的 CPU 资源会逐渐减少。不管是上面的哪种情况,最终的结果都是 Pod 已经越来越无法承载外部更多的请求,表现为应用延时增加,响应变慢。 + + +容器的资源调度以 requests 为准。 + +有的 Pod 内部进程在初始化启动时会提前开辟出一段内存空间。比如 JVM 虚拟机在启动的时候会申请一段内存空间,如果内存 requests 指定的数值小于 JVM 虚拟机向系统申请的内存,导致内存申请失败( oom-kill ),从而 Pod 出现不断地失败重启。
diff --git a/kubernetes/cpu-resource.md b/kubernetes/cpu-resource.md deleted file mode 100644 index 4695b897..00000000 --- a/kubernetes/cpu-resource.md +++ /dev/null @@ -1,15 +0,0 @@ -# - -在业务高峰期间, - -- 在业务比较繁忙的时候,节点的 CPU 全负荷运行。业务延迟明显增加,有时甚至机器会莫名其妙地进入 CPU 软死锁等“假死”状态 -- 类似地,部署负载的时候,不设置内存 requests 或者内存 requests 设置得过低,这时会发现有些 Pod 会不断地失败重启。而不断重启的这些 Pod 通常跑的是 Java 业务应用。但是这些 Java 应用本地调试运行地时候明明都是正常的。 - -如果在业务高峰时间遇到上述问题,并且机器已经 hang 住甚至无法远程 ssh 登陆,那么通常留给集群管理员的只剩下重启集群这一个选项 - - -CPU 属于可压缩资源,其中 CPU 资源的分配和管理是 Linux 内核借助于完全公平调度算法( CFS )和 Cgroup 机制共同完成的。简单地讲,如果 pod 中服务使用 CPU 超过设置的 CPU limits, pod 的 CPU 资源会被限流( throttled )。对于没有设置 limit 的 pod ,一旦节点的空闲 CPU 资源耗尽,之前分配的 CPU 资源会逐渐减少。不管是上面的哪种情况,最终的结果都是 Pod 已经越来越无法承载外部更多的请求,表现为应用延时增加,响应变慢。 - -内存属于不可压缩资源, Pod 之间是无法共享且完全独占,这也就意味着资源一旦耗尽或者不足,分配新的资源一定会失败。 - -有的 Pod 内部进程在初始化启动时会提前开辟出一段内存空间。比如 JVM 虚拟机在启动的时候会申请一段内存空间,如果内存 requests 指定的数值小于 JVM 虚拟机向系统申请的内存,导致内存申请失败( oom-kill ),从而 Pod 出现不断地失败重启。 \ No newline at end of file diff --git a/kubernetes/ingress.md b/kubernetes/ingress.md deleted file mode 100644 index 6ce790bc..00000000 --- a/kubernetes/ingress.md +++ /dev/null @@ -1,84 +0,0 @@ -# 8.4.5 Ingress - -在 Kubernetes 中,为了使外部的应用能够访问集群内的 service,最为常用的是使用 NodePort 和 LoadBalancer 两种类型的 service,但它们在使用上还是有一些限制,譬如对外提供访问时,NodePort 类型需要在外部搭建额外的负载均衡,其次 NodePort 会占用很多集群机器的端口。而 LoadBalancer 要求 Kubernetes 必须跑在支持的 Cloud Provider 上,由云厂商提供公网 IP 地址,当同时当存在多个 LoadBalancer 的类型 Service 时,会占用大量公网 ip 地址。 以上的问题有没有解决方案呢? 答案是使用 Ingress。 - -Ingress 是英文入口的意思,在 Kubernetes 中,Ingress 用于定义如何从集群外部访问 Kubernetes 内部服务,是对集群中服务的外部访问进行管理的 API 对象。典型的访问方式,譬如 HTTP 和 HTTPS,另外也可提供负载均衡、SSL 卸载和基于名称的虚拟托管服务等功能。 - -
- -
- -## Ingress 的工作机制 - -要想使用 Ingress 功能,必须在 Kubernetes 集群上安装 Ingress Controller。Ingress Controller 有很多种实现,最常见的就是 Kubernetes 官方维护的 NGINX Ingress Controller。 - -外部请求首先到达 Ingress Controller,Ingress Controller 根据 Ingress 的路由规则,查找到对应的 Service,进而通过 Endpoint 查询到 Pod 的 IP 地址,然后将请求转发给 Pod。 - -
- -
- -## 创建 Ingress - -下面例子中,使用 http 协议,关联的后端 Service 为 `nginx:8080`,使用 ELB 作为 Ingress 控制器(metadata.annotations 字段都是指定使用哪个 ELB 实例),当访问`http://192.168.10.155:8080/test”时,流量转发“nginx:8080`对应的 Service,从而将流量转发到对应 Pod。 - -```plain -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: test-ingress - annotations: - kubernetes.io/ingress.class: cce - kubernetes.io/elb.port: '8080' - kubernetes.io/elb.ip: 192.168.10.155 - kubernetes.io/elb.id: aa7cf5ec-7218-4c43-98d4-c36c0744667a -spec: - rules: - - host: '' - http: - paths: - - backend: - serviceName: nginx - servicePort: 8080 - path: "/test" - property: - ingress.beta.kubernetes.io/url-match-mode: STARTS_WITH -``` - -Ingress 中还可以设置外部域名,这样您就可以通过域名来访问到 ELB,进而访问到后端服务。 - -```plain -spec: - rules: - - host: www.thebyte.com.cn # 域名 - http: - paths: - - path: / - backend: - serviceName: nginx - servicePort: 80 - -``` - -## 路由到多个服务 - -Ingress 可以同时路由到多个服务,配置如下所示。 - -- 当访问“http://www.thebyte.com.cn/test”时,访问的是“s1:80”后端。 -- 当访问“http://www.thebyte.com.cn/demo”时,访问的是“s2:80”后端。 - -```plain -spec: - rules: - - host: www.thebyte.com.cn # host地址 - http: - paths: - - path: "/test" - backend: - serviceName: s1 - servicePort: 80 - - path: "/demo" - backend: - serviceName: s2 - servicePort: 80 -``` \ No newline at end of file diff --git a/kubernetes/service.md b/kubernetes/service.md deleted file mode 100644 index e394c17a..00000000 --- a/kubernetes/service.md +++ /dev/null @@ -1,161 +0,0 @@ -# 8.4.3 Service - -## Pod 访问问题 - -Pod 被创建之后,该如何访问呢?直接访问 Pod 会有以下几个问题: - -- Pod 的 IP 地址是在 Pod 启动后才被分配,在启动前并不知道 Pod 的 IP 地址。 -- Pod 的 IP 存在变化的可能性,被 Deployment 重建 IP 就会发生变化。 -- 应用服务往往是一组 Pod 组成,逐个访问 Pod 也不现实。 - -为了以一种固定的方式访问 Pod,Kubernetes 提供了一种负载均衡和服务发现的机制: Service。Service 创建后会提供一个固定的虚拟 IP(以 ClusterIP 类型的 Service 为例), 这样,用户无需关心 Pod 在哪个节点,通过固定 Service IP 即可实现对 Pod 地访问,并且 Service 可以对访问进行负载均衡。 - - -示例:创建一个 Service - -```plain -apiVersion: v1 -kind: Service -metadata: - name: nginx # Service的名称 -spec: - selector: # Label Selector,选择包含app=nginx标签的Pod - app: nginx - ports: - - name: service0 - targetPort: 80 # Pod的端口 - port: 8080 # Service对外暴露的端口 - protocol: TCP # 转发协议类型,支持TCP和UDP - type: ClusterIP # Service的类型 -``` - -
- -
- -### 使用 ServiceName 访问 Service - -使用 IP 直连有一个问题,如果 Service 被删除,相应的 Cluster IP 也会被回收。域名发现 - -为此 Kubernetes 结合 DNS 的。CoreDNS 安装成功后会成为 DNS 服务器,当创建 Service 后,CoreDNS 会将 Service 的名称与 IP 记录起来,这样 Pod 就可以通过向 CoreDNS 查询 Service 的名称获得 Service 的 IP 地址。 - - -coreDNS 提供格式如 `..svs.cluster.local` 的服务,访问该地址,集群内的域名解析解析服务器会返回该服务所对应的 A 记录。实际使用中,同一个命名空间下可以省略`.svc.cluster.local`,直接使用 ServiceName 即可。 - -例如上面创建的名为 nginx 的 Service,直接通过 `nginx:8080` 就可以访问到 Service,进而访问 Pod, 这样无需感知具体 Service 的 IP 地址。 - -## Service 类型 - -Service 的类型除了 ClusterIP 还有 NodePort、LoadBalancer 和 Headless Service,这几种类型的 Service 有着不同的用途。 - -- ClusterIP:用于在集群内部互相访问的场景,通过 ClusterIP 访问 Service。 -- NodePort:用于从集群外部访问的场景,通过节点上的端口访问 Service,详细介绍请参见 NodePort 类型的 Service。 -- LoadBalancer:用于从集群外部访问的场景,其实是 NodePort 的扩展,通过一个特定的 LoadBalancer 访问 Service,这个 LoadBalancer 将请求转发到节点的 NodePort,而外部只需要访问 LoadBalancer。 -- Headless Service:用于 Pod 间互相发现,该类型的 Service 并不会分配单独的 ClusterIP, 而且集群也不会为它们进行负载均衡和路由。您可通过指定 spec.clusterIP 字段的值为 “None” 来创建 Headless Service。 - -### NodePort 类型的 Service - -NodePort 类型的 Service 可以让 Kubernetes 集群每个节点上保留一个相同的端口, 外部访问连接首先访问节点 IP:Port,然后将这些连接转发给服务对应的 Pod。如下图所示。 - -下面是一个创建 NodePort 类型的 Service。创建完成后,可以通过节点的 IP:Port访问到后台 Pod - -```plain -apiVersion: v1 -kind: Service -metadata: - name: nodeport-service -spec: - type: NodePort - ports: - - port: 8080 - targetPort: 80 - nodePort: 3030 - selector: - app: nginx -``` - -### LoadBalancer 类型的 Service - -LoadBalancer 类型的 Service 其实是 NodePort 类型 Service 的扩展,通过一个特定的 LoadBalancer 访问 Service,这个 LoadBalancer 将请求转发到节点的 NodePort。 - -LoadBalancer 本身不是属于 Kubernetes 的组件,这部分通常是由具体厂商(云服务提供商)提供,不同厂商的 Kubernetes 集群与 LoadBalancer 的 对接实现各不相同,例如 CCE 对接了 ELB。这就导致了创建 LoadBalancer 类型的 Service 有不同的实现。 - -
- -
- - -下面是一个创建 LoadBalancer 类型的 Service。创建完成后,可以通过 ELB 的 IP:Port访问到后台 Pod - -```plain -apiVersion: v1 -kind: Service -metadata: - annotations: - kubernetes.io/elb.id: 3c7caa5a-a641-4bff-801a-feace27424b6 - labels: - app: nginx - name: nginx -spec: - loadBalancerIP: 10.28.12.100 # ELB实例的IP地址 - ports: - - name: service0 - port: 80 - protocol: TCP - targetPort: 80 - nodePort: 3030 - selector: - app: nginx - type: LoadBalancer # 类型为LoadBalancer -``` - -### Headless Service - -前面讲的 Service 解决了 Pod 的内外部访问问题,允许客户端连接到 Service 关联的某个 Pod。但还有下面这些问题没解决。 - -- 同时访问所有 Pod -- 一个 Service 内部的 Pod 互相访问 - -为了解决以上问题,Kubernetes 提供了另一种较为特殊的 Service 类型,称为 Headless Service。 - -对于其他 Service 来说,客户端在访问服务时,DNS 查询时只会返回 Service 的 ClusterIP 地址,具体访问到哪个 Pod 是由集群转发规则(IPVS 或 iptables)决定的。而 Headless Service 并不会分配单独的 ClusterIP,在进行 DNS 查询时会返回所有 Pod 的 DNS 记录,这样就可查询到每个 Pod 的 IP 地址。StatefulSet 中 StatefulSet 正是使用 Headless Service 解决 Pod 间互相访问的问题 - -```plain -apiVersion: v1 -kind: Service # 对象类型为Service -metadata: - name: nginx-headless - labels: - app: nginx -spec: - ports: - - name: nginx # Pod间通信的端口名称 - port: 80 # Pod间通信的端口号 - selector: - app: nginx # 选择标签为app:nginx的Pod - clusterIP: None # 必须设置为None,表示Headless Service -``` -创建完成后可以查询 Service。 - -```plain -# kubectl get svc -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -nginx-headless ClusterIP None 80/TCP 5s -``` - -创建一个 Pod 来查询 DNS,可以看到能返回所有 Pod 的记录,这就解决了访问所有 Pod 的问题了。 - -```plain -$ kubectl run -i --tty --image tutum/dnsutils dnsutils --restart=Never --rm /bin/sh -If you don't see a command prompt, try pressing enter. -/ # nslookup nginx-headless -Server: 10.247.3.10 -Address: 10.247.3.10#53 - -Name: nginx-headless.default.svc.cluster.local -Address: 172.16.0.31 -Name: nginx-headless.default.svc.cluster.local -Address: 172.16.0.18 -Name: nginx-headless.default.svc.cluster.local -Address: 172.16.0.19 -```