CKA-备考指南-全-

CKA 备考指南(全)

原文:annas-archive.org/md5/0240fbdeb5414fd034b97d8d79373123

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

Kubernetes 迄今为止是最流行的容器编排工具,但管理该工具的复杂性促使了完全托管的 Kubernetes 服务在过去几年中的兴起。认证 Kubernetes 管理员CKA)认证旨在确保认证候选人具备帮助他们在就业市场上建立信誉和价值所需的技能和知识,从而支持业务增长。

本书将从介绍 Kubernetes 架构和 Kubernetes 的核心概念开始,然后我们将深入探讨主要的 Kubernetes 原语,结合安装和配置、集群管理和工作负载调度、网络和安全等实际场景进行学习。此外,我们还将讨论如何在日常实践中排查 Kubernetes 的故障。

到本书结束时,你将熟练掌握 Kubernetes 的安装与配置,能够轻松应对集群管理、存储、网络、安全配置和故障排查等技能,特别是在原生 Kubernetes 环境下。

如果你想了解更多关于 Kubernetes 的信息,可以查看这个播放列表 Kubernetes in 30 days - www.youtube.com/watch?v=csPu6y6A7oY&list=PLyDI9q8xNovlhCqRhouXmSKQ-PP6_SsIQ

本书适合的人群

本书面向希望通过 CKA 考试认证 Kubernetes 管理员技能的应用开发人员、DevOps 工程师、数据工程师和云架构师。建议具备一定的 Kubernetes 基础知识。

本书的内容

第一章Kubernetes 概述,介绍了 Kubernetes 的架构及其核心概念,深入探讨了常见的 Kubernetes 工具,并通过实际操作展示了 Kubernetes 的不同发行版和生态系统的全貌。

第二章安装和配置 Kubernetes 集群,介绍了 Kubernetes 的不同配置,并通过设置一个单节点和多节点 Kubernetes 集群,使用合适的工具进行实践。

第三章维护 Kubernetes 集群,介绍了在维护 Kubernetes 集群时的不同方法,并实际操作进行 Kubernetes 集群的升级,备份和恢复 ETCD。本章涵盖了 CKA 考试内容的 25%。

第四章应用调度与生命周期管理,描述了如何使用 Kubernetes 部署来部署 pods,扩展 pods,执行滚动更新和回滚,进行资源管理,以及使用 ConfigMaps 配置 pods。本章涵盖了 CKA 考试内容的 15%。

第五章解密 Kubernetes 存储,讨论了 Kubernetes 存储的核心概念,针对有状态的工作负载,并展示了如何配置带有挂载存储和动态持久存储的应用程序。本章涵盖了 CKA 考试内容的 10%。

第六章保障 Kubernetes 安全性,介绍了 Kubernetes 身份验证和授权模式的工作原理,然后深入讲解 Kubernetes 的基于角色的访问控制RBAC)。从那里,我们将管理在 Kubernetes 上部署的应用程序的安全性放入视角。此部分内容不到 CKA 考试内容的 5%。

第七章解密 Kubernetes 网络,描述了如何使用 Kubernetes 网络模型和核心概念,以及如何在集群节点上配置 Kubernetes 网络和网络策略,配置 Ingress 控制器和 Ingress 资源,配置和利用 CoreDNS,以及如何选择合适的容器网络接口插件。本章涵盖了 CKA 考试内容的 20%。

第八章监控和记录 Kubernetes 集群与应用程序,描述了如何监控 Kubernetes 集群组件和应用程序,以及如何获取基础设施级、系统级和应用程序级的日志,以作为日志分析的来源或进一步故障排除的依据。结合接下来的两章关于故障排除集群组件和应用程序以及故障排除 Kubernetes 安全性和网络设置的内容,它覆盖了 CKA 考试内容的 30%。

第九章故障排除集群组件和应用程序,描述了通用的故障排除方法,以及如何排除由于集群组件故障或应用程序部署过程中发生的问题所引起的错误。

第十章故障排除安全性和网络设置,紧接着 第九章,提供了排除由于 RBAC 限制或网络设置引起的错误的通用故障排除方法。在 第六章 中,我们提到过如何启用 Kubernetes RBAC 并配置 Kubernetes DNS。在深入本章之前,一定要回顾这些重要的概念。

为了充分利用本书

本书是一本全面的实践学习指南,重点提供与场景相关的实际操作技能,同时提供核心知识帮助读者进行预热。本书涉及的软件和硬件如下:

本书涉及的软件/硬件 操作系统要求
Minikube Windows、macOS 或 Linux
kubectl, kubeadm Windows 或 Linux
Docker Desktop Windows 10 或 11
WSL 2 Windows 10 或 11

下载彩色图像

我们还提供了一份包含本书所用截图和图表彩色图片的 PDF 文件。您可以在此下载:packt.link/AKr3r

使用的约定

本书中使用了若干文本约定。

文本中的代码:表示文本中的代码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 账号。例如:“您可以通过使用alias k=kubectl命令为 kubectl 设置别名,然后使用k get命令。”

代码块如下所示:

apiVersion: v1
kind: Pod
metadata:
   name: melon-serviceaccount-pod
spec:
   serviceAccountName: melon-serviceaccount
   containers:
   - name: melonapp-svcaccount-container
     image: busybox
     command: ['sh', '-c','echo stay tuned!&& sleep 3600']

当我们希望引起你对代码块中特定部分的注意时,相关的行或项目会使用粗体显示:

spec:
   serviceAccountName: melon-serviceaccount
   containers:

任何命令行输入或输出都按如下方式编写:

kubectl delete samelon-serviceaccount

粗体:表示一个新术语、一个重要单词,或您在屏幕上看到的单词。例如,菜单或对话框中的词语通常以粗体显示。以下是一个示例:“此命令将返回当前显示为未分配的节点。”

提示或重要说明

以这种方式显示。

联系我们

我们始终欢迎读者的反馈。

一般反馈:如果您对本书的任何方面有疑问,请通过电子邮件联系我们:customercare@packtpub.com,并在邮件主题中注明书名。

勘误:尽管我们已经尽力确保内容的准确性,但错误还是难免发生。如果您在本书中发现错误,我们将非常感激您向我们报告。请访问 www.packtpub.com/support/errata 填写表单。

盗版:如果您在互联网上发现任何非法复制的我们的作品,我们将非常感激您提供位置地址或网站名称。请通过 copyright@packt.com 联系我们,并提供该材料的链接。

如果您有兴趣成为作者:如果您在某个领域有专业知识,并且有意撰写或贡献书籍,请访问 authors.packtpub.com

分享您的想法

阅读完《认证 Kubernetes 管理员(CKA)考试指南》后,我们很想听听您的想法!请点击这里直接前往亚马逊评论页面,分享您的反馈。

您的反馈对我们和技术社区都非常重要,帮助我们确保提供优质的内容。

第一部分:集群架构、安装与配置

本部分将介绍 Kubernetes 的概述,并探讨其概念和工具。此外,您将学习如何安装和设置 Kubernetes 集群。本部分涵盖 CKA 考试内容的 25%。

本书的这一部分包括以下章节:

  • 第一章**,Kubernetes 概述

  • 第二章**,安装与配置 Kubernetes 集群

  • 第三章**,维护 Kubernetes 集群

第一章:Kubernetes 概览

本章介绍了 Kubernetes 架构和核心概念,深入探讨了常见的 Kubernetes 工具并进行实践,展示了 Kubernetes 中不同发行版和生态系统的整体情况。本章将覆盖以下主要主题:

  • CKA 考试概述

  • 集群架构与组件

  • Kubernetes 核心概念

  • Kubernetes 市场中的发行版和生态系统

CKA 考试概述

认证 Kubernetes 管理员 (CKA) 认证是一个实践考试,涵盖了一些常见的 Kubernetes 工作场景。你需要在限定的时间内完成考试。我们强烈建议你在自己的环境中通读本书,并确保你理解并实践每一个步骤,直到你能培养直觉,快速完成所有任务而无需再三思考。时间管理是成功通过此考试的关键。

在撰写本书时,CKA 考试是基于 Kubernetes 1.22 版本的。请访问官方示例页面,确保你了解考试大纲的任何更新:www.cncf.io/certification/cka/。要了解 Kubernetes 的更新内容,请查看社区发布说明:github.com/kubernetes/kubernetes/releases

本书内容与 CKA 考试大纲高度一致:

  • 第一部分第一章第三章 涉及 Kubernetes 集群架构、安装与配置,约占考试的 25%。

  • 第二部分第四章 涉及 工作负载与调度,约占考试的 15%,第五章 涉及 存储服务与网络,约占考试的 10%,第六章第七章 涉及 服务与网络,约占考试的 20%。

  • 第三部分第八章第十章 涉及 故障排除,约占考试的 30%。

考试大纲的目标是帮助你为 CKA 考试做准备,并帮助你全面了解每个领域,这将有助于你在职业生涯中成为熟练的 Kubernetes 管理员。在阅读本书时,如果你已经熟悉某些话题,可以跳过不太需要了解的部分,直接阅读你最需要掌握的领域。

请注意,2020 年 11 月之前的一些 Kubernetes 安全内容已经逐渐移至 认证 Kubernetes 安全专家CKS)考试。作为一个全面的 Kubernetes 管理员,深入了解 Kubernetes 安全是至关重要的。事实上,单独将 Kubernetes 安全作为一个独立的主题是比较困难的;然而,了解安全上下文和 基于角色的访问控制RBAC)等主题仍然是您成功完成考试的必要条件。因此,本书仍将涵盖一些关键的安全概念,为您日后如果打算攻读 CKS 认证奠定基础。如需了解更多 Kubernetes 认证信息,请访问 Linux Foundation 网站的常见问题解答:docs.linuxfoundation.org/tc-docs/certification/faq-cka-ckad-cks

在 CKA 考试中需要预期的内容

在考试之前,您必须确保您在考试期间使用的计算机符合考试提供方定义的系统要求。摄像头和麦克风在考试期间必须开启。您只能使用单一实例的基于 Chromium 的浏览器进行考试。您可以在这里查看基于 Chromium 的浏览器列表:en.wikipedia.org/wiki/Chromium_(web_browser)

请通过运行兼容性检查工具确保您的硬件符合最低要求,您可以在这里找到该工具:www.examslocal.com/ScheduleExam/Home/CompatibilityCheck。详细的系统要求可以在这里查看:docs.linuxfoundation.org/tc-docs/certification/faq-cka-ckad-cks#what-are-the-system-requirements-to-take-the-exam

重要提示

由于本次考试为在线远程监控考试,您还可以查看考试的具体情况,点击这里:psi.wistia.com/medias/5kidxdd0ry

在考试期间,你可以查看官方 Kubernetes 文档,包括kubernetes.iogithub.com/kubernetes上的文章和文档,且可以在同一浏览器实例中与考试界面一起查看。CKA 考试由大约 20 个基于场景的任务组成,任务需在 Linux 基础的 shell 和一组预定义的 Kubernetes 集群中完成。这些基于场景的任务以需要解决的问题形式提供,并附有额外的信息。考生必须根据提供的信息提出解决方案并及时执行。CKA 考试时长大约为 2 小时,考试结束后将标记为已提交。如果你愿意,可以使用多个显示器参加考试,尽管在此之前请查看考试政策,确保你已满足组织方的所有要求:docs.linuxfoundation.org/tc-docs/certification/faq-cka-ckad-cks#how-is-the-exam-proctored

我们强烈推荐你通过killer.sh提供的模拟考试,走一遍样本场景,并收藏对你有帮助的官方文档。访问killer.sh培训网站killer.sh/course/来测试模拟考试环境并试用场景。

欲了解更多 CKA 考试说明和技巧,请访问docs.linuxfoundation.org/tc-docs/certification/tips-cka-and-ckad

你需要至少 66%的分数才能通过考试,考试结果将在考试结束后的 24 到 36 小时内通过邮件发送给你。你将收到有效期为 3 年的 PDF 格式证书,稍后还会收到徽章。如果有任何问题,你可以通过邮件联系certificationsupport@cncf.io寻求进一步帮助。

CKA 考试技巧和窍门

成功通过 CKA 考试或任何其他 Kubernetes 认证的两个关键因素如下:

  • 优秀的时间管理能力

  • 练习,因为我们知道“熟能生巧”

在进入考试部分之前,你必须熟悉 Kubernetes;在准备考试时,不要只关注认证。深入了解 Kubernetes 集群架构和生态系统将为学习任何与考试相关的内容奠定坚实的基础。

获得一些基本的 Linux shell 理解

看到考试本身,基本的 Linux shell 知识将帮助你更快达成目标。在你完成本书中的练习时,以下命令会对你有所帮助:

  • 尽量使用sudo以避免权限问题,使用sudo su来获取 root 权限

  • curl

  • 在命令过滤结果中使用| grep

  • vi/vim/nano 或其他 Linux 文本编辑器

  • cat

  • cp/mv/mkdir/touch

  • cp/scp

  • json 路径的良好理解是加分项,使用 jq 进行 JSON 解析是获取命令输出信息的一个好方法。

在本书中,我们将逐一介绍所有的考试主题,练习中会涉及大部分命令。确保你理解并能独立自信地完成所有练习,不要着急。

设置 kubectl 别名以节省时间

在考试的各种场景中,你会反复使用很多命令,因此一个便捷的 kubectl 快捷方式是必不可少的,因为几乎所有的命令中都会使用它:

alias k=kubectl 
alias kg='kubectl get'
alias kgpo='kubectl get pod'

GitHub 上有一个 kubectl-aliases 仓库,你可以参考(github.com/ahmetb/kubectl-aliases)。这个仓库是由一位贡献者创建的,展示了很多很好的 kubectl 别名示例。

如果你不想记住太多内容,你可以尝试理解 Kubernetes 中快捷方式的命名约定。例如,svc 是 services 的缩写,kubectl get services 可以变成 kubectl get svc,或者 kubectl get nodes 可以变成 k get no,等等。我创建了一个 melonkube playbook 仓库,涵盖了 Kubernetes 对象的所有快捷方式(github.com/cloudmelon/melonkube/blob/master/00%20-%20Shortcuts.md)。

你可以参考这个仓库,找到最适合你的方式。然而,请保持简单,因为在实际考试中,出于某些原因,你的大脑可能会变得很紧张。多加练习会让你更快上手。

设置 kubectl 自动补全

你可以在你的 shell 中设置自动补全;这通常在考试时的 Linux shell 中有效。你可以通过以下方式实现:

source <(kubectl completion bash) # setup autocomplete in bash into the current shell, bash-completion package should be installed first.
echo "source <(kubectl completion bash)" >> ~/.bashrc # add autocomplete permanently to your bash shell.

与快捷方式配合使用时,你可以执行以下操作:

alias k=kubectl
complete -F __start_kubectl k

尽管有时从 bash 自动补全 中寻找正确的命令可能会花费更多时间,但我认为通过实践建立对技术的深入理解将有助于你更快提高技能。

在浏览器中为陌生但重要的文档添加书签

熟悉 Kubernetes 官方文档,了解如何找到你需要的信息。CKA 的目标是不是记忆,而是实际操作技能;知道如何找到正确的路径并解决挑战才是关键。你可以在以下领域为文档添加书签:

我通常推荐人们收藏的第一页是 kubectl 快捷命令表:kubernetes.io/docs/reference/kubectl/cheatsheet/。另一个好的收藏是官方文档搜索:kubernetes.io/search/?q=kubecon

小心安全上下文

上下文是最重要的指示器,可以帮助你了解当前操作的是哪个 Kubernetes 集群。我们将在本书后面更详细地讨论安全上下文。我建议你在处理任何新问题之前,先检查一下上下文,因为有时候你可能会感到困惑。请注意,如果你没有在该问题的目标 Kubernetes 集群上操作,你将 不会 得分。

你可以使用以下命令查看上下文:

kubectl config current-context

如果你想切换到一个特定的 Kubernetes 集群,可以使用以下命令:

kubectl config use-context my-current-cluster-name

你还可以通过以下命令查看你曾经操作过的 Kubernetes 集群列表,特别是在实际考试中:

kubectl config get-contexts  

明智地管理你的时间

时间管理是 CKA 考试成功的关键,合理管理时间,调整任务顺序非常重要。通常情况下,所有考试任务的难度从易到难排列。当你接近最后几个问题时,你可能会发现某些任务虽然非常耗时,但并不是最难的。你可以跳过这些问题,先处理那些你有信心的问题,之后再回到这些耗时的问题上。这就是为什么了解当前操作的 Kubernetes 集群非常重要。

最后的思考

如果你已经完成了本书中的所有练习,并希望更深入地理解 Kubernetes,我推荐你阅读一本我在 2020 年共同编写的书,名为 The Kubernetes Workshop,同样由 Packt 出版,书中提供了大量的 Kubernetes 练习,帮助你提高技术水平。

集群架构和组件

Kubernetes 是一个便携式、高度可扩展的开源编排系统,旨在管理容器化工作负载和服务,并协调容器以在不同的工作节点上实现所需的状态。值得一提的是,官方文档中提到,Kubernetes 在希腊语中的意思是 pilot,它的名字来源于此,这对于它的功能来说是非常合适的。

它支持各种工作负载,例如无状态、状态化以及数据处理工作负载。从理论上讲,任何可以容器化的应用程序都可以在 Kubernetes 上运行。

一个 Kubernetes 集群由一组工作节点组成;这些工作节点运行实际的工作负载,即容器化的应用程序。一个 Kubernetes 集群可以有从 1 到 5000 个节点(截至写本章节时,我们使用的是 Kubernetes 1.23 版本)。

我们通常启动一个节点进行快速测试,而在生产环境中,集群有多个工作节点以确保高可用性和故障恢复。

Kubernetes 采用主/工作节点架构,这是一种机制,其中一个进程充当主控组件,控制一个或多个其他组件(称为从节点,或者在我们的案例中是工作节点)。一个典型的 Kubernetes 集群架构如下所示:

图 1.1 – Kubernetes 集群架构

图 1.1 – Kubernetes 集群架构

Kubernetes 主节点或控制平面负责响应集群事件,它包含以下组件:

  • kube-apiserver 负责公开 Kubernetes REST API。你可以将它看作是 Kubernetes 集群中各个组件之间的通信管理器。

  • etcd:这是一个分布式键值存储,存储关于集群信息以及在 Kubernetes 集群中运行的所有对象状态的信息,如 Kubernetes 集群节点、Pods、配置映射、密钥、服务账户、角色和绑定。

  • kube-scheduler 是 Kubernetes 的默认调度器。你可以把它想象成一个邮递员,它将 Pod 的信息发送到每个节点,当信息到达目标节点时,该节点上的 kubelet 代理将根据收到的规范为容器化工作负载提供支持。

  • Kubernetes 中的 kube-controller-manager。这些控制器的示例包括副本控制器、端点控制器和命名空间控制器。

除了控制平面外,Kubernetes 集群中每个运行实际工作负载的工作节点还包含以下组件:

  • kubelet:kubelet 是一个在每个工作节点上运行的代理。它接受来自 API 服务器或本地(用于静态 Pod)发送的 Pod 规格,并在相应的节点上配置容器化工作负载,如 Pod、StatefulSet 和 ReplicaSet。

  • 容器运行时:这是帮助在每个节点的 Pods 中运行容器的软件虚拟化层。Docker、CRI-O 和 containerd 是常见的与 Kubernetes 一起工作的容器运行时的例子。

  • kube-proxy:此组件在每个工作节点上运行,负责在 Kubernetes 集群中部署服务对象时实现网络规则和流量转发。

了解这些组件及其工作原理将帮助你理解 Kubernetes 的核心概念。

Kubernetes 核心概念

在深入探讨 Kubernetes 的核心内容之前,我们将首先解释一些关键概念,帮助你开始 Kubernetes 的学习旅程。

容器化工作负载

容器化工作负载是指在 Kubernetes 上运行的应用程序。回到容器化的基本定义,容器为你的应用程序提供了一个隔离的环境,相比于部署在物理服务器或 虚拟机VMs)上的应用程序,容器具有更高的密度和更好的基础设施利用率:

图 1.2 – 虚拟机与容器

图 1.2 – 虚拟机与容器

上图展示了虚拟机(VM)和容器之间的区别。与虚拟机相比,容器更加高效,且更易于管理。

容器镜像

容器将应用程序及其所有依赖项、库、二进制文件和配置文件隔离开来。应用程序的包,连同其依赖项、库、二进制文件和配置,称为容器镜像。一旦容器镜像构建完成,镜像的内容就变得不可变。所有代码更改和依赖项更新都需要构建新的镜像。

容器注册表

为了存储容器镜像,我们需要一个容器注册表。容器注册表可以位于本地机器、内部网络,或者有时在云端。你需要认证才能访问容器注册表的内容,以确保安全性。大多数公共注册表,如 DockerHub 和 quay.io,允许广泛的非限制性容器镜像分发:

图 1.3 – 容器镜像

图 1.3 – 容器镜像

整个机制的优点在于,它允许开发人员专注于编码和配置,这正是他们工作中的核心价值,而无需担心底层基础设施或管理安装在主机节点上的依赖项和库,正如上图所示。

容器运行时

容器运行时负责运行容器,也称为容器引擎。这是一个运行在主机操作系统上的软件虚拟化层,用于运行容器。像 Docker 这样的容器运行时可以从容器注册表拉取容器镜像,并使用 CLI 命令管理容器生命周期,在这种情况下,使用 Docker CLI 命令,正如下图所示:

图 1.4 – 管理 Docker 容器

图 1.4 – 管理 Docker 容器

除了 Docker,Kubernetes 还支持多种容器运行时,如 containerd 和 CRI-O。在 Kubernetes 的上下文中,容器运行时帮助在每个工作节点上的 Pod 内启动并运行容器。我们将在下一章中介绍如何设置容器运行时,作为在配置 Kubernetes 集群之前的准备工作。

重要提示

Kubernetes 通过配置运行在工作节点上的 Pods 来运行容器化的工作负载。一个节点可以是物理机或虚拟机,无论是在本地还是云端。

Kubernetes 基本工作流

我们在集群架构和组件章节中看到了一种典型的工作流程,展示了 Kubernetes 如何与 Kubernetes 组件协同工作,以及它们如何相互配合。当你使用 kubectl 命令、YAML 规范或其他方式调用 API 时,API 服务器会创建一个 Pod 定义,调度器则会识别可用的节点来部署新的 Pod。调度器有两个步骤:过滤评分。过滤步骤会找到一组可用的候选节点来部署 Pod,评分步骤会对最合适的 Pod 部署进行排名。

API 服务器将这些信息传递给目标工作节点上的 kubelet 代理。然后,kubelet 在节点上创建 Pod,并指示容器运行时引擎部署应用镜像。一旦完成,kubelet 会将状态信息反馈给 API 服务器,后者会更新 etcd 存储中的数据,并通知用户 Pod 已被创建。

这个机制在每次执行任务并与 Kubernetes 集群进行交互时都会重复,不论是使用 kubectl 命令、部署 YAML 定义文件,还是通过其他方式触发 API 服务器的 REST API 调用。

以下图示展示了我们刚才描述的过程:

图 1.5 – Kubernetes 集群基本工作流程

图 1.5 – Kubernetes 集群基本工作流程

了解基本的 Kubernetes 工作流程将帮助你理解 Kubernetes 集群组件如何相互协作,为学习 Kubernetes 插件模型和 API 对象打下基础。

Kubernetes 插件模型

Kubernetes 主导市场并成为云原生生态系统新常态的最重要原因之一是它灵活、高度可配置且具有高度可扩展的架构。Kubernetes 在以下几个层面上具有高度的可配置性和可扩展性:

  • 容器运行时:容器运行时是运行容器的最低级别软件虚拟化层。由于容器运行时接口CRI)插件的支持,这一层支持市场上多种不同的运行时。CRI 包含一组协议缓冲区、规范、gRPC API、库和工具。在 第二章安装和配置 Kubernetes 集群 中,我们将介绍如何在部署 Kubernetes 集群时与不同的运行时进行合作。

  • 网络:Kubernetes 的网络层由 kubenet 或 容器网络接口CNI)定义,负责为 Linux 容器配置网络接口,在我们的案例中,主要是 Kubernetes Pods。CNI 实际上是一个 云原生计算基金会CNCF)项目,包含 CNI 规范、插件和库。在 第七章解密 Kubernetes 网络 中,我们将详细讨论 Kubernetes 网络的更多内容。

  • 存储:Kubernetes 的存储层曾是一个极具挑战性的部分,直到容器存储接口CSI)被引入作为暴露块存储和文件存储系统的标准接口。存储卷由存储供应商量身定制的存储驱动程序管理,这部分曾是 Kubernetes 源代码的一部分。与 CSI 兼容的卷驱动程序为用户提供服务,使其能够将 CSI 卷附加或挂载到 Kubernetes 集群中运行的 Pods 上。我们将在第五章《解密 Kubernetes 存储》中详细介绍 Kubernetes 中的存储管理。

它们可以像下图所示轻松地布局:

图 1.6 – Kubernetes 插件模型

图 1.6 – Kubernetes 插件模型

深入理解 Kubernetes 插件模型,不仅有助于你作为 Kubernetes 管理员的日常工作,还能为你奠定基础,帮助你快速学习 Kubernetes 生态系统和云原生社区标准。

Kubernetes API 原语

所有组件之间的操作和通信以及外部用户命令,都是 API 服务器处理的 REST API 调用。在 Kubernetes 中,所有内容都被视为一个 API 对象。

在 Kubernetes 中,当你运行kubectl命令时,kubectl工具实际上是连接到 kube-apiserver 的。kube-apiserver首先对请求进行身份验证和验证,然后更新etcd中的信息并检索请求的信息。

在每个工作节点中,kubelet 代理在每个节点上获取由 API 服务器提供的 Podspecs,配置容器化工作负载,并确保 Pods(如 Podspec 中所描述的)正在运行并保持健康。Podspec 是 YAML 定义文件的主体,它被转化为一个 JSON 对象,描述工作负载的规格。Kubernetes 通过 API 服务器发起 API 调用,然后由控制平面进行处理。

Kubernetes API 原语,也被称为 Kubernetes 对象,是任何在 Kubernetes 集群中运行的容器化工作负载的基础构件。

以下是我们在日常使用 Kubernetes 集群时将要使用的主要 Kubernetes 对象:

  • Pods:Kubernetes 中最小的可部署单元是 Pod。工作节点托管着 Pods,Pods 中包含了实际的应用工作负载。应用被打包并部署在容器中。一个 Pod 包含一个或多个容器。

  • 副本集:副本集帮助 Pods 在用户定义的副本数量下实现更高的可用性。副本集的作用是确保集群始终有精确数量的副本在 Kubernetes 集群中运行。如果其中任何一个副本失败,新的副本将会被部署。

  • DaemonSet:DaemonSet 类似于 ReplicaSet,但它确保至少在 Kubernetes 集群中的每个节点上都有一个副本的 Pod。如果集群中添加了新节点,该 Pod 的副本将自动分配到该节点。同样,当节点被移除时,Pod 也会自动被移除。

  • StatefulSet:StatefulSet 用于管理有状态的应用。当工作负载需要存储卷以提供持久性时,用户可以使用 StatefulSet。

  • Completed 状态。使用作业的一个示例是当我们希望运行一个特定目标的工作负载,并确保它只运行一次且成功时。

  • 使用 cron 表达式根据需求定义特定的调度时间。

  • Deployment:Deployment 是一种方便的方式,允许你定义所需的状态,例如部署一个具有特定副本数的 ReplicaSet,并且可以轻松地进行版本回滚或发布。

我们将在第四章《应用调度与生命周期管理》中详细介绍如何使用这些 Kubernetes 对象。敬请期待!

使用命名空间共享集群

理解 Kubernetes 基本对象将让你初步了解 Kubernetes 如何在工作负载级别上运作。随着我们的深入讲解,还会涵盖更多细节和其他相关对象。这些在 Kubernetes 集群上运行的对象在进行开发、测试或快速入门时会正常工作,尽管在企业级组织的生产环境中,我们需要考虑工作负载的隔离,这时命名空间的作用就体现出来了。

命名空间是对单一 Kubernetes 集群中所有命名空间对象的逻辑隔离。命名空间对象的示例包括 Deployments、Services、Secrets 等。还有一些 Kubernetes 对象是全局范围的,例如 StorageClasses、Nodes 和 PersistentVolumes。资源的名称在一个命名空间内必须唯一,但它在所有命名空间中都是通过命名空间名称和对象名称来标记的。

命名空间旨在将集群资源在多个用户之间进行隔离,从而为组织内部的多个项目共享集群提供了可能性。我们称这种模式为Kubernetes 多租户模型。多租户模型是一种有效的方式,帮助不同的项目和团队共享集群并最大化地利用同一个集群。多租户模型有助于最小化资源浪费。尤其在使用云端 Kubernetes 时,云服务商总是会保留一定的资源,这时多租户模型特别有用。尽管如此,多租户模型也给资源管理和安全方面带来了额外的挑战。我们将在第四章《应用调度与生命周期管理》中讨论资源管理。

为了更好的物理隔离,我们建议组织使用多个 Kubernetes 集群。这将为不同的项目和团队带来物理边界,尽管 Kubernetes 系统所保留的资源也会在集群之间进行复制。除此之外,跨多个 Kubernetes 集群工作也是具有挑战性的,因为它涉及到通过切换安全上下文来建立有效的机制,以及处理网络方面的复杂性。我们将在第六章保护 Kubernetes,以及 Kubernetes 网络部分的第七章揭秘 Kubernetes 网络中详细讨论 Kubernetes 安全性和网络。以下是一个显示 Kubernetes 多租户与多集群比较的图示:

图 1.7 – Kubernetes 多租户与多集群

图 1.7 – Kubernetes 多租户与多集群

Kubernetes 市场发行版和生态系统

Kubernetes 得到了一个快速发展的、充满活力的开源社区的支持。目前市场上有超过 60 种已知的 Kubernetes 平台和发行版。从宏观上看,市场上有来自上游社区的托管 Kubernetes 和标准 Kubernetes 发行版。在本节中,我们将提供关于 Kubernetes 及其生态系统的高层次总结。

上游原生 Kubernetes

上游原生 Kubernetes 通常在组织希望管理自己的 Kubernetes 集群以及本地基础设施或基于云的虚拟机时使用。Kubernetes 发行版的源代码来自上游 Kubernetes 社区项目。它是开放的,欢迎贡献,因此请随时加入任何兴趣小组SIG)小组;这里是社区小组的完整列表:github.com/kubernetes/community/blob/master/sig-list.md

如果你有任何想法或希望向社区学习:kubernetes.io/docs/contribute/generate-ref-docs/contribute-upstream/

托管 Kubernetes

云服务商托管的 Kubernetes 发行版通常属于这一类别。托管的 Kubernetes 发行版通常基于原生 Kubernetes 集群,不同的厂商在此基础上构建其功能,使其更适应其基础设施。托管的 Kubernetes 发行版通常由厂商管理控制平面,用户只需管理工作节点,并将精力集中在基于核心专业知识提供价值上。

Microsoft Azure 提供 Azure Kubernetes ServiceAKS),Amazon Web ServiceAWS)提供 Elastic Kubernetes ServiceEKS),而 Google Cloud PlatformGCP)则以其 Google Kubernetes EngineGKE)为荣。

其他流行的 Kubernetes 发行版包括 VMware 的 Tanzu、RedHat 的 OpenShift、Canonical 的 Charmed Kubernetes 和 Ranger Lab 的 Kubernetes。

Kubernetes 生态系统

Kubernetes 生态系统不仅限于供应和管理工具;它还有多种工具用于安全、网络、可观察性等方面,涵盖了使用 Kubernetes 时所有重要的方面。Kubernetes 生态系统是云原生领域的重要组成部分。由于 Kubernetes 具有高度的可移植性和平台无关性,我们可以将 Kubernetes 带到任何地方。它容易与安全敏感的断开场景集成,或者随着组织向云迁移,与混合场景集成。生态系统中的这些工具相互补充,推动了 Kubernetes 作为云原生技术的巨大增长,并对社区以及各类规模的企业产生了积极影响。查看云原生领域的更多内容,请访问landscape.cncf.io

学习 Kubernetes 及其生态系统将帮助你更好地理解如何为你的组织使用 Kubernetes,以及如何帮助你的组织从 Kubernetes 中获得最大的收益。

总结

本章介绍了 Kubernetes 的一些核心概念,并简要概览了市场上所有流行的 Kubernetes 发行版。激动人心的旅程即将开始!

在下一章,我们将深入探讨 Kubernetes 集群的安装和配置细节,敬请期待!

第二章:安装和配置 Kubernetes 集群

本章介绍了 Kubernetes 的不同配置,这是使用 Kubernetes 的第一步。我们将通过设置一个包含单个工作节点的 Kubernetes 集群,然后再扩展为多个工作节点,来进行实践。本章将帮助你熟悉 Kubernetes 安装,这是作为 Kubernetes 管理员日常工作中的一项关键技能。

在本章中,我们将涵盖以下主题:

  • Kubernetes 工具实践

  • 安装和配置 Kubernetes 集群

  • 使用 minikube 设置单节点 Kubernetes 集群

  • 使用 kubeadm 安装基本的 Kubernetes 集群

  • 使用 kubeadm 设置高可用集群

技术要求

为了开始工作,我们需要确保你的本地机器满足以下技术要求:

  • 兼容的 Linux 主机 – 我们推荐使用基于 Debian 的 Linux 发行版,如 Ubuntu 18.04 或更高版本。

  • 确保你的主机至少有 2 GB 的内存、2 个 CPU 核心,以及大约 20 GB 的空闲磁盘空间。

Kubernetes 工具实践

市场上有许多 Kubernetes 工具 – 我们将首先介绍一些广泛使用的 Kubernetes 工具,用于与 Kubernetes 集群进行交互。我们将在本章后面通过实际操作深入讲解一些关键工具。

核心工具

在本节中,我们将介绍用于与 Kubernetes 和容器一起工作的工具。

kubectl

kubectl 是一个 Kubernetes 命令行工具,用于与 Kubernetes 集群进行交互。它无疑是最常用和最重要的工具,使你能够在 Kubernetes 集群中运行命令。kubectl 提供了许多命令,允许用户执行与 Kubernetes 集群相关的操作,比如部署容器化应用程序、管理集群资源、监控和可视化事件与日志。我们将在整个过程中介绍大部分常用的 kubectl 命令,并通过示例进行讲解。

要设置 kubectl 工具,如果你使用的是基于 Red Hat 的发行版,如 CentOS 或 Fedora,请参考官方文章获取更多信息:kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-using-native-package-management。你可以使用以下命令:

cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
sudo yum install -y kubectl 

如果你使用的是基于 Debian 的发行版,如 Ubuntu 18.04,可以按照以下说明进行操作:

  1. 首先,你需要更新 apt 包索引,然后,你需要按顺序运行以下命令来安装使用 Kubernetes apt 仓库所需的软件包:

    sudo apt-get update
    sudo apt-get install -y apt-transport-https ca-certificates curl
    
  2. 下载 Google Cloud 公共签名密钥,并使用以下命令添加 Kubernetes apt 仓库:

    sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
    echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
    
  3. 现在,你可以开始了。确保你再次更新 apt 包索引并使用 apt-get install 命令安装 kubectl 工具:

    sudo apt-get update
    sudo apt-get install -y kubectl
    
  4. 在完成之前的步骤后,你可以通过运行以下命令来验证kubectl是否已经成功安装:

    kubectl version --client
    

如果你成功安装了kubectl,你将看到类似下面的输出:

图 2.1 – kubectl 安装成功

图 2.1 – kubectl 安装成功

有关在不同环境中安装kubectl的说明,请参考kubernetes.io/docs/tasks/tools/

容器运行时

现在,我们将按照这些指示设置containerd作为我们的容器运行时:

  1. 更新apt索引,添加 Docker 的官方GPG密钥,并通过运行以下指令设置apt仓库:

    sudo apt-get update
    sudo apt-get install \
        ca-certificates \
        curl \
        gnupg \
        lsb-release
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
      $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
  2. 安装 Docker 引擎和containerd.io

    sudo apt-get update
    sudo apt-get install docker-ce docker-ce-cli containerd.io
    
  3. 使用以下命令验证 Docker 是否已经成功安装:

    sudo docker ps
    #optional - running your first docker container
    sudo docker run hello-world
    

如果你成功安装了kubectl,你将看到类似下面的输出:

图 2.2 – Docker 已启动并运行

图 2.2 – Docker 已启动并运行

  1. 如果你准备将containerd配置为容器运行时,可以使用以下命令并将配置设置为default

    sudo mkdir -p /etc/containerd
    containerd config default | sudo tee /etc/containerd/config.toml
    
  2. 重启containerd以确保更改生效:

    sudo systemctl restart containerd
    

如果你想了解如何设置 CRI-O 作为运行时,请查看以下链接:kubernetes.io/docs/setup/production-environment/container-runtimes/#cri-o。该链接将展示containerd如何在 Kubernetes 上下文中作为容器运行时工作。

部署工具

要引导一个 Kubernetes 集群,我们依赖于部署工具。市面上有很多有用的工具可以帮助启动一个 Kubernetes 集群,其中许多是与供应商相关的。在这里,我们将介绍 CKA 考试中要求的工具。这也是我们专注于上游 Kubernetes 的主要原因,这些工具将帮助我们在本地启动集群。以下工具将帮助你设置 Kubernetes 集群,我们将在下一章中详细介绍每个工具的使用方法:

  • kubeadm是帮助你通过考试练习的最重要工具。它帮助安装并设置 Kubernetes 集群,采用最佳实践。使用kubeadm,你可以配置单节点集群,更重要的是,多节点集群。这是大多数希望管理自己 Kubernetes 集群并使用自有本地服务器的公司首选的工具。

  • minikube是一个流行的本地 Kubernetes,可以在你的本地笔记本电脑或虚拟机VM)上进行配置。它非常轻量,专注于使学习 Kubernetes 变得简单,并能够快速进行测试。

  • kind 类似于 minikube,它专注于本地 Kubernetes 集群的配置以及一些简单的 CI 场景和开发。它使用 Docker 运行时运行本地 Kubernetes 集群——可以运行单节点 Kubernetes 集群或 Kubernetes 多节点集群。你可以使用 kind 测试许多有用的简单场景。

其他工具

一些其他工具不在 CKA 考试中覆盖——但是,它们在你作为 Kubernetes 管理员的日常工作中仍然会非常有用。

Helm

Helm 是一个管理工具,用于管理以图表形式预配置的 Kubernetes 对象包——我们称这些为 Helm 图表。

要安装 helm,你可以按照以下针对基于 Debian 的发行版(例如 Ubuntu 18.04)的说明进行操作:

  1. 更新 apt 包索引:

    curl https://baltocdn.com/helm/signing.asc | sudo apt-key add -
    sudo apt-get install apt-transport-https --yes
    
  2. 使用以下命令安装 Helm apt 仓库的相关软件包:

    echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
    
  3. 确保再次使用新的仓库更新 apt 包索引,然后使用 apt-get install 命令安装 Helm:

    sudo apt-get update
    sudo apt-get install helm
    
  4. 使用以下 Helm 命令验证其安装是否成功:

    helm version
    

你将看到类似于以下内容的输出:

图 2.3 – Helm 安装成功

图 2.3 – Helm 安装成功

要了解更多安装 Helm 的方法,请查看以下链接:helm.sh/docs/intro/install/

Kompose

大多数使用 Docker 的人都知道 Docker Compose。Docker Compose 是一个用于定义和运行由 Docker 容器化的多容器应用的工具。它也使用 YAML 文件来定义应用程序的规格。随着越来越多的人不再单纯使用 Docker Swarm 或 Docker Desktop,而是利用企业级的容器编排系统,Kompose 成为一个有用的转换工具,将 Docker Compose 转换为 Kubernetes 等容器编排系统——同样的结构也适用于 Redhat OpenShift。

你可以通过在 Ubuntu 18.04 上运行以下说明来安装 Kompose:

  1. 获取 kompose 二进制文件:

    curl -L https://github.com/kubernetes/kompose/releases/download/v1.26.0/kompose-linux-amd64 -o kompose
    chmod +x kompose
    sudo mv ./kompose /usr/local/bin/kompose
    
  2. 然后,你可以从官方网站获取一个 docker compose 示例文件,并按照如下方式测试 kompose convert 命令:

    wget https://raw.githubusercontent.com/kubernetes/kompose/master/examples/docker-compose-v3.yaml -O docker-compose.yaml
    kompose convert
    

你的输出将类似于以下内容:

图 2.4 – 一个  命令将 Docker compose 转换为 Kubernetes 本地的 YAML 定义文件

图 2.4 – 一个 kompose convert 命令将 Docker compose 转换为 Kubernetes 本地的 YAML 定义文件

  1. 然后,使用以下命令将这些 YAML 文件部署到本地 Kubernetes 集群:

    kubectl apply -f .
    

你的输出将类似于以下内容:

图 2.5 – Kubernetes Pods 启动成功

图 2.5 – Kubernetes Pods 启动成功

上面的截图显示了在 Kubernetes 集群中运行的 Redis Pods。

仪表盘

您可以为 Kubernetes 集群安装一个基于 Web 的用户界面UI)。它不仅显示集群状态并展示 Kubernetes 集群的运行情况,还允许您部署容器化应用程序、进行故障排除,并管理集群及其相关资源。

以下是一个示例控制面板:

图 2.6 – Kubernetes 控制面板

图 2.6 – Kubernetes 控制面板

控制面板有时对从 UI 快速监控集群状态很有帮助,且对不熟悉kubectl命令的人来说,界面友好,便于协作。

安装和配置 Kubernetes 集群

本节重点介绍 Kubernetes 集群的安装及其相关配置。通过在第一章中获得的良好理解,您已经了解了 Kubernetes 集群架构和 Kubernetes 工具,接下来您将通过minikubekubeadm来进行 Kubernetes 集群安装,并且更新集群版本。

请注意,使用minikube启动单节点集群并不包含在 CKA 考试范围内,但它在你想在本地机器上测试 Kubernetes 时非常有用。同样,使用kubeadm安装 Kubernetes 多节点集群,以及设置高可用HA)Kubernetes 集群也不在考试范围内。

我们希望您通过两种方式学习,但更侧重于通过kubeadm进行的动手实验。从下一个章节开始,我们将带您逐步完成安装新的 Kubernetes 集群及其配置的过程。

安装 Kubernetes 集群的先决条件

为了开始,我们需要确保您的本地机器满足以下技术要求,以便同时支持minikubekubeadm

  • 一个兼容的 Linux 主机 – 我们推荐使用基于 Debian 的 Linux 发行版,如 Ubuntu 18.04 或更高版本。

  • 确保您的主机至少有 2 GB 内存、2 个 CPU 核心,以及大约 20 GB 的空闲磁盘空间。

  • 网络连接:因为在整个过程中,您需要下载依赖项。

  • 在创建 Kubernetes 集群之前,需要安装容器运行时。在集群创建过程中,Kubernetes 集群会通过扫描本地机器中的 Unix 域套接字(如果有的话)来自动检测已安装的容器运行时。Unix 域套接字使用传输控制协议TCP)作为底层传输协议。它用于同一操作系统上双向数据通信。我们在第一章中讨论了如何安装和配置容器运行时,请按照其中的说明操作。

在开始之前,让我们完成以下清单:

检查是否禁用了 swap

对于kubeadm,我们必须禁用swap才能使kubelet正常工作,您可以通过以下步骤禁用swap

sudo swapoff -a

检查容器运行时

你可以按照指示检查 Unix 域套接字的路径,以验证你的容器运行时 —— 这个路径可以被 Kubernetes 检测到。按照本章前面讲解的安装 Docker 的指示,安装 kubelet 代理后,你会在 /var/run/dockershim.sock 路径下找到 Unix 域套接字路径。要验证 Docker 是否已成功安装,可以运行 docker ps 命令:

sudo docker ps

以下命令的输出结果如下:

图 2.7 – 检查 Docker 运行时

图 2.7 – 检查 Docker 运行时

如果你已经安装了 containerd 作为容器运行时,正如本章前面在容器运行时部分中讲解的那样,你会在 /run/containerd/containerd.sock 路径下找到 Unix 域套接字路径,具体如下:

图 2.8 – 检查 containerd 运行时

图 2.8 – 检查 containerd 运行时

当检测到 dockercontainerd 两种容器运行时,kubeadm 会选择 docker 而非 containerd 作为容器运行时。截止到写作本文时(2022 年 1 月初宣布),Kubernetes 将在即将发布的 v1.24 版本中移除 dockershim。这并不令人惊讶,因为这一消息早在 2020 年 12 月就已宣布,Kubernetes 内置的 dockershim 组件在 Kubernetes v1.20 中已被弃用。在大多数情况下,只要满足以下条件,它不会影响 Kubernetes 中运行的应用程序或容器化应用程序的构建过程:

  • 在容器内部执行 Docker 命令时,容器级别不会应用特权的 root 权限,并且它使用 systemctl 重启 docker.service

  • Docker 配置文件,如 /etc/docker/daemon.json 已被修改

此时,Kubernetes 官方文档已经发布了这篇文章,帮助用户检查 dockershim 弃用是否会影响他们。点击此处查看更多关于如何检查 Docker 依赖的方式:kubernetes.io/docs/tasks/administer-cluster/migrating-from-dockershim/check-if-dockershim-deprecation-affects-you/#find-docker-dependencies

检查 Kubernetes 所需的端口是否已开放

在安装 kubeadm 之前,我们还需要检查本地机器上是否打开了某些端口。你可以使用 telnet 命令来检查:

telnet 127.0.0.1 6443

你可以查看官方文档,确保 Kubernetes 使用的端口和协议可用,访问此链接:kubernetes.io/docs/reference/ports-and-protocols/

确保 iptables 看到桥接流量

确保你的 Linux 节点的 iptables 配置正确,以便能够监控桥接流量。你可以将 net.bridge.bridge-nf-call-iptables 参数设置为 1,就像我们在这里所做的那样:

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system

你将看到类似如下的输出:

图 2.9 – iptables 监控桥接流量

图 2.9 – iptables 监控桥接流量

上面的截图显示 iptables 中的值已更新。

检查是否已安装 kubectl

kubectl 是你可以用来与 Kubernetes 集群交互的命令行工具。使用 kubectl version 命令,你可以验证是否成功安装了 kubectl

kubectl version --client

成功安装将显示类似以下的输出:

图 2.10 – 检查 kubectl 版本

图 2.10 – 检查 kubectl 版本

在继续下一个部分之前,确保你已完成本节中的检查清单。这些工具和要求是必要的,你可以在未来根据需要使用它们。

使用 minikube 设置单节点 Kubernetes 集群

使用 minikube 创建 Kubernetes 集群是启动本地 Kubernetes 集群的最简单方法,并且可以在几分钟内完成。以下是你需要做的:

安装 minikube

按照以下步骤安装 minikube

  1. 在本地或云端的 Linux 虚拟机上,使用 curl 命令获取 minikube 二进制文件,然后将其安装到 /usr/local/bin/minikube 下,如下所示:

    curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
    sudo install minikube-linux-amd64 /usr/local/bin/minikube
    
  2. 在继续下一步之前,你可以前往 /usr/local/bin/minikube 检查是否成功安装了 minikube 二进制文件,或者你也可以通过在终端中输入以下命令进行检查:

    minikube –-help
    

使用 minikube 配置单节点 Kubernetes 集群

按照以下步骤使用 minikube 来配置单节点 Kubernetes 集群:

  1. 使用 minikube 配置单节点 Kubernetes 集群时,你可以简单地使用 minikube start 命令:

    minikube start
    
  2. 你也可以通过添加 --memory--cpus 标志来设置 CPU 核心和内存,启动你的 minikube 集群,如下所示:

    minikube start --memory 8192 --cpus 4
    

执行命令后,它将启动 minikube 集群配置过程。你将看到类似如下的输出:

图 2.11 – 启动 minikube 集群

图 2.11 – 启动 minikube 集群

最后,你将看到一条消息,告诉你我们已经准备好使用 minikube Kubernetes 集群(如前面的截图所示)。

验证 minikube 集群安装

你的 minikube 集群包含一个节点,既充当控制平面,又充当工作节点。这意味着,一旦设置好,你就可以开始在本地 Kubernetes 集群中调度工作负载。你可以使用以下命令查看该节点是否准备好使用:

kubectl get node 

你也可以使用该命令的快捷方式:

alias k=kubectl 
k get no

输出将显示如下内容:

  • 节点的状态及其是否准备好使用

  • 节点的角色

  • Kubernetes 版本

  • 节点自部署以来的时间

这是输出结果:

图 2.12 – 检查 Docker 运行时

图 2.12 – 检查 Docker 运行时

配置 minikube 集群

如果你希望配置minikube集群而不重新创建一个新的集群,你需要使用minikube stop命令停止minikube集群。

minikube config set命令将帮助你应用分配给minikube集群的 CPU 和内存等设置。在配置minikube集群后,你需要启动minikube集群,之后你将使用新配置的集群进行操作。

以下是使用更多内存和 CPU 配置minikube的过程:

minikube stop
minikube config set memory 8192
minikube config set cpus 4
minikube start

之后,你可以继续操作minikube集群。如果你对命令的使用有任何疑问,可以使用minikube config --help命令获取帮助。

删除 minikube 集群

以下命令会删除所有本地 Kubernetes 集群和所有配置文件:

minikube delete --all

你从本节学到的内容可以在每次需要本地 Kubernetes 集群时反复使用。你可以复制你在本节学到的内容,快速测试最新 Kubernetes 版本中发布说明中的大多数新功能:github.com/kubernetes/kubernetes/releases

然而,大多数企业级环境对单节点集群并不满足需求。它们大多是多节点设置。在下一节中,我们将深入探讨如何使用kubeadm创建一个 Kubernetes 多节点集群。

使用 kubeadm 安装基本 Kubernetes 集群

在本节中,我们将使用kubeadm创建一个多节点的 Kubernetes 集群。以下是我们需要执行的步骤:

  1. 安装kubeadm

  2. 启动一个主节点,控制平面将位于该节点

  3. 安装网络插件(我们将在本章后面详细介绍支持的插件,并在该部分使用 Calico 作为示例)。

  4. 启动工作节点。

  5. 将工作节点加入控制平面。

在开始之前,你需要确保你的主节点满足本章列出的所有技术要求。

我们将通过本节描述的步骤部署一个基本的 Kubernetes 集群,如图 2.7所示:

图 2.13 – 使用 kubeadm 启动基本 Kubernetes 集群的工作流程

图 2.13 – 使用 kubeadm 启动基本 Kubernetes 集群的工作流程

Kubernetes 集群将类似于图 2.14中展示的架构:

图 2.14 – 标准的多节点 Kubernetes 集群

图 2.14 – 标准的多节点 Kubernetes 集群

从现在开始,你可以按照这些指示创建一个多节点的 Kubernetes 集群。要使用kubeadm创建 Kubernetes 集群,其默认设置符合设置标准 Kubernetes 集群的最佳实践。这套最佳实践被封装为 Kubernetes 一致性测试。查看有关 Kubernetes 一致性计划的详细信息:kubernetes.io/blog/2017/10/software-conformance-certification/

安装 kubeadm

我们介绍了设置dockercontainerd作为容器运行时 —— 然后,我们可以按照以下说明安装kubeadm

  1. 通过运行以下命令,更新apt包索引,添加谷歌云公共签名密钥,并设置 Kubernetes 的apt仓库:

    sudo apt-get update
    sudo apt-get install -y apt-transport-https ca-certificates curl
    sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
    echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
    
  2. 首先更新apt包索引,然后安装kubeletkubeadm

    sudo apt-get update
    sudo apt-get install -y kubelet kubeadm 
    
  3. 如果你还没有安装kubectl,你也可以一步安装kubeletkubeadmkubectl

    sudo apt-get update
    sudo apt-get install -y kubelet kubeadm kubectl 
    
  4. 使用以下命令来固定你正在安装的工具版本:

    sudo apt-mark hold kubelet kubeadm kubectl 
    

输出显示那些包已被设置为保持,如图 2.9所示:

图 2.15 – 检查 containerd 运行时

图 2.15 – 检查 containerd 运行时

  1. 从这里,你可以通过在命令行中输入kubeadm来检查kubeadm是否已成功安装。以下是该命令的输出:

图 2.16 – 检查 containerd 运行时

图 2.16 – 检查 containerd 运行时

  1. 要验证kubelet是否存在于主节点上,你可以使用which kubelet命令,该命令会返回kubelet代理的位置:

图 2.17 – 检查 kubelet 是否存在

图 2.17 – 检查 kubelet 是否存在

由于你已经成功安装了kubeadmkubelet,现在可以开始初始化控制平面。

在这里,我们将展示一个可选操作,你可以使用images pull来预拉取设置 Kubernetes 集群所需的镜像:

sudo kubeadm config images pull

输出应该类似于以下截图:

图 2.18 – 预拉取镜像

图 2.18 – 预拉取镜像

请注意,之前的操作是可选的 —— 你可以跳过它,直接进入下一节。

引导主节点

你可以使用kubeadm init命令以普通用户身份初始化控制平面,并通过以下命令获得来自主节点机器的sudo权限:

sudo kubeadm init --pod-network-cidr=192.168.0.0/16

你将看到类似于以下的输出:

图 2.19 – 控制平面成功启动

图 2.19 – 控制平面成功启动

在 Kubernetes 的control-plane成功初始化后,你可以执行以下命令来配置kubectl

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

如果你是 root 用户,你可以使用以下命令:

export KUBECONFIG=/etc/kubernetes/admin.conf

接下来,步骤是向 Kubernetes 集群部署 pod 网络。

安装网络插件

为了让 pods 之间可以互相通信,你可以通过启用容器网络接口CNI)插件来部署网络。CNI 插件符合 CNI 规范,根据官方 Kubernetes 文档,Kubernetes 遵循 CNI 规范的 v0.4.0 版本。

有许多网络插件可以与 Kubernetes 一起使用——我们将在第七章《解密 Kubernetes 网络》中深入探讨 Kubernetes 网络。以下是一些附加选项:

  • Calico

  • Flannel

  • Weave Net

对于 Kubernetes 社区认可的所有可能选项,请查看官方文档:kubernetes.io/docs/concepts/cluster-administration/addons/。你可以通过该页面上的链接获取相应选项的安装说明。

在这里,我们将使用 Calico 插件作为 Kubernetes 集群的覆盖网络。它是一个 Kubernetes CNI 网络提供者,允许你编写网络策略,这意味着它支持一系列的网络选项,以满足不同的需求。我们将按以下方式进行操作:

  1. 部署 Tigera Calico 的kubectl create -f命令:

    kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml
    kubectl create -f https://docs.projectcalico.org/manifests/custom-resources.yaml
    
  2. 你可以使用watch命令来监视 pod 状态:

    watch kubectl get pods -n calico-system
    

或者,可以使用以下替代命令:

kubectl get pods -n calico-system -w

现在,你可以看到 pods 的状态为Running

图 2.20 – 控制平面已成功启动

图 2.20 – 控制平面已成功启动

  1. 对于通过kubeadm创建的 Kubernetes 集群,主节点默认会有污点。因此,我们需要移除污点,以便主节点可以调度 pods。要移除污点,可以使用以下命令:

    kubectl taint nodes --all node-role.kubernetes.io/master-
    

以下截图显示,主节点上的污点已成功移除:

图 2.21 – 成功移除主节点上的污点

图 2.21 – 成功移除主节点上的污点

  1. 你可以使用以下命令查看当前可用的节点:

    kubectl get no
    
  2. 要获取更多关于节点的信息,可以使用以下命令:

    kubectl get no -o wide
    

以下截图显示了示例输出:

图 2.22 – Kubernetes 节点状态

图 2.22 – Kubernetes 节点状态

从之前的命令输出中,可以看到在启用 CNI 网络后,Kubernetes 节点已经正常运行,并且分配了一个内部 IP 地址。

启动工作节点

要将更多工作节点添加到 Kubernetes 集群中,我们将通过 SSH 连接到客户端机器,并确保工作节点满足与主节点相同的技术要求。请查看本章中的安装 Kubernetes 集群的先决条件部分,并参考 kubeadm 相关信息获取更多详细信息。确保已经安装了容器运行时和kubeadm,尽管对于工作节点来说,kubectl 是可选的,因为我们通常使用主节点进行管理。

将工作节点加入控制平面

在确保工作节点和本地环境满足我们之前设定的技术要求后,您可以继续为主节点安装kubeadm,正如本节早些时候提到的那样。正如在第一章,《Kubernetes 概述》中介绍的那样,工作节点是容器化工作负载运行的地方。

您可以使用以下命令将工作节点加入 Kubernetes 集群。每次您需要加入新的工作节点时,都可以重复使用此命令:

sudo kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>

您实际上可以返回并复制主节点控制平面的输出,输出内容与以下示例命令类似:

sudo kubeadm join 172.16.16.129:6443 --token k626hm.oqwyac35h43x80mg   --discovery-token-ca-cert-hash sha256:889983
a6b87643e598b88533dbe3a68643a623b9a0ed9380561c6a7dbb93b3f0

您可以使用前述命令将工作节点加入控制平面,并使用多个工作节点设置您的 Kubernetes 集群。

使用 kubeadm 设置高可用集群

第一章,《Kubernetes 概述》中,我们介绍了集群架构,为我们提供了两种选择:设置单节点 Kubernetes 集群用于开发/测试的快速测试,或设置多节点 Kubernetes 集群用于更专业的用途,甚至用于生产环境。标准配置通常是一个主节点和多个工作节点。正如我们在上一章中提到的,Kubernetes 主节点是控制平面所在的位置。如果主节点宕机,容器化工作负载仍会在工作节点上运行,直到工作节点因某些原因下线,或者没有可用的主节点,这意味着不会有新的工作负载被调度到工作节点。

有两种可用选项来构建高可用性(HA)Kubernetes 集群:

  • 构建多个主节点:这是控制平面节点和 etcd 成员共同存在于同一主节点中的选项。图 2.16 显示了堆叠的 etcd 拓扑结构:

图 2.23 – 高可用 kubeadm 集群的堆叠 etcd 拓扑结构

图 2.23 – 高可用 kubeadm 集群的堆叠 etcd 拓扑结构

与本章中构建的基本 Kubernetes 集群架构相比,这种拓扑结构使集群更加具有弹性,这要归功于主节点的冗余性。如果一个主节点宕机,您可以轻松切换到另一个可用的主节点,以确保整个 Kubernetes 集群的健康。

然而,在一些需要管理集群并复制集群信息的情况下,外部 etcd 拓扑结构会派上用场。

  • kubeadm外部 etcd 集群的 HA 拓扑架构如图 2.24所示:

图 2.24 – 外部 etcd HA kubeadm 集群的拓扑结构

图 2.24 – 外部 etcd HA kubeadm 集群的拓扑结构

图 2.24所示,外部 etcd 是一个集群,并与每个控制平面的 API 服务器进行通信。如果控制平面节点发生故障,我们不会丢失存储在 etcd 中的所有信息。它还使控制平面更加解耦和可管理,因为我们只需添加更多的控制平面节点。控制平面节点的丧失将不会像堆叠 etcd 拓扑结构中那样产生较大影响。

总结

本章涵盖了大多数 Kubernetes 管理员的首要任务,他们正在设置一个具有单个工作节点或多个工作节点的 Kubernetes 集群。本章介绍的各种工具将帮助你在工作中的日常操作,超越考试的需求。然而,这也是 CKA 考试中最耗时的任务之一。练习、练习、再练习将帮助你掌握它。了解 Kubernetes 集群的 HA 拓扑结构也将帮助你满足作为 Kubernetes 管理员在组织中的需求。当你掌握了基本 Kubernetes 集群的设置过程后,将更容易将你的技能应用于不同的拓扑结构。

在下一章中,我们将讨论 Kubernetes 集群的维护,包括一些重要的话题,如 Kubernetes 组件的升级,这也是 Kubernetes 管理员日常工作中至关重要的任务。本章涉及外部 etcd 拓扑结构只是一个开始,因为我们将在下一章深入探讨与 etcd 相关的更有趣的工作。祝学习愉快!

模拟 CKA 场景练习测试

你有两台虚拟机,master-0worker-0。请完成以下模拟场景。

场景 1

安装最新版本的kubeadm,然后在master-0节点上创建一个基本的kubeadm集群,并获取节点信息。

场景 2

使用 SSH 连接到worker-0并将其加入master-0节点。

场景 3(可选)

设置一个本地minikube集群并调度你的第一个工作负载,名为hello Packt

你可以在本书的附录中找到所有场景解决方案,模拟 CKA 场景练习测试解决方案

常见问题

  • 我应该从哪里开始测试 Kubernetes 集群?

您可以从您的本地笔记本电脑或桌面计算机开始,在 Windows、Linux 或 Mac OS 上,并推荐使用 VMware Player 或 Hyper-V 来启动多个虚拟机,以便测试多节点场景。使用来自 Canonical 的 Multipass 也非常适合创建 Ubuntu 虚拟机,并支持 Linux、Mac 和 Windows。在这里查看:multipass.run/

另一种选择是订阅云服务,如 Microsoft Azure、AWS 或 GCP,使用它们可以通过点击体验来创建虚拟机。

  • 我在哪里可以找到最新的 Kubernetes 发布版来进行测试?

Kubernetes GitHub 仓库是您可以找到所有发布版本和更改日志的地方,您可以获取最新的发布版并自行构建:github.com/kubernetes/kubernetes

我们也可以使用 kubeadmminikube 来获取 Kubernetes,因为它们与 Kubernetes 源代码交付周期保持一致且保持更新。

第三章:维护 Kubernetes 集群

在过去几年里,Kubernetes 一直是社区中最具活力的平台,并保持了良好的发布节奏,这使得 Kubernetes 的维护变得尤为重要,以便与 Kubernetes 合作的组织能够利用其最新功能。本章介绍了维护 Kubernetes 集群的不同方法,同时提供了进行 Kubernetes 集群升级、etcd 备份和 etcd 恢复的实用教程。它涵盖了 CKA 考试内容的 25%。

在本章中,我们将讨论以下主要主题:

  • 解密 Kubernetes 集群维护

  • 使用 kubeadm 对 Kubernetes 集群进行版本升级

  • 与 etcd 配合工作

  • 备份和恢复 etcd

解密 Kubernetes 集群维护

2021 年 4 月之前,Kubernetes 一直保持着全年每季度发布的稳定节奏。尽管在社区中的增长和广泛的流行,这一节奏被缩减为每年三次发布。更少的发布仍意味着应该在组织内部安排定期的维护窗口,以便升级安全补丁,并充分利用增强功能和新特性。

一般的维护窗口包括升级的 Kubernetes 集群版本。我们可以将要执行的任务分为两部分:

  • 升级主节点,主节点包含控制平面

  • 升级工作节点

如果只有一个主节点,升级主节点是简单的。然而,大多数企业级客户可能会有多个主节点以增强弹性。我们应当意识到,作为 Kubernetes 管理员与组织合作有时比与其他组织合作更具挑战性。主节点管理的一般参考可以参见在第二章《安装与配置 Kubernetes 集群》中提到的高可用性两种类型。升级主节点的常规方式是无论当前集群中的主节点数量如何,都逐个升级。你需要升级 kubeadm 和 kubectl 版本。

关于工作节点的升级,正如我们在上一章提到的,工作节点是实际运行你工作负载的地方;因此,你需要同时升级 kubeadm 和 kubelet 版本。请记住,当当前 Kubernetes 集群中有多个工作节点时,你需要逐个升级。

如果你设置了一个独立的 etcd 集群,你需要升级 etcd 存储版本,这在 CKA 考试中没有涵盖。通常,你需要查阅官方文档,了解更多关于 Kubernetes 组件和版本兼容性的信息,链接在此:kubernetes.io/releases/version-skew-policy/

Kubernetes 集群维护的另一个常见任务是使用 etcd 存储进行备份和恢复。etcd 存储着集群数据,包括集群状态信息,如 pod 状态数据、节点状态数据,以及 Kubernetes 所需的关键配置。通常情况下,作为 Kubernetes 管理员,你需要执行以下两个关键任务:

  • 定期备份 etcd 存储

  • 从集群故障恢复 etcd

以下部分将首先带你了解使用 kubeadm 升级 Kubernetes 集群的一般流程。这是实际 CKA 考试中最耗时的问题之一。确保你多练习几次,直到掌握一般的升级流程,并能够按照 Kubernetes 官方文档执行升级任务。请注意,云供应商的托管 Kubernetes 发行版的更新政策各不相同,请查阅它们各自的官方文档。

此外,我们将了解如何备份和恢复 etcd 集群。

使用 kubeadm 升级 Kubernetes 集群

Kubernetes 版本遵循语义化版本控制,表达形式为三部分:

  1. 主版本

  2. 小版本

  3. 补丁版本

例如,Kubernetes 版本 1.23.3 表示它是 Kubernetes 1.23 的小版本,补丁号为 3。类似地,1.22 和 1.21 也是类似 1.23 的小版本。

在编写本书时,Kubernetes 1.19+有一年的路径支持。这意味着对于 2022 年 1 月发布的 Kubernetes 1.23,支持结束时间是 2023 年 2 月。而对于 Kubernetes 1.8 及更早版本,支持补丁的时间缩短至大约 9 个月。兴趣小组SIG)负责管理 Kubernetes 的发布周期,跟踪发布计划的最佳方式是关注他们的github.com/kubernetes/sig-release/tree/master/releases,并阅读github.com/kubernetes/kubernetes/tree/master/CHANGELOG上的更改日志,保持最新。

如果你想将集群升级到目标版本,可以查看kubernetes.io/releases/version-skew-policy/#supported-versions上的受支持版本。

升级主节点

在升级主节点之前,确保你了解升级的目的,并已备份任何重要组件。建议先检查当前版本,然后决定要升级到哪个版本。一旦决定,我们将执行以下操作来升级主节点,如图 2.1所示,包括使用 kubeadm 进行升级并与 Kubernetes 节点交互:

图 3.1 – 主节点升级过程

图 3.1 – 主节点升级过程

一旦我们进入主节点,就让我们开始检查当前版本,通过以下命令:

   kubeadm version
   kubectl version 

从输出中,我们知道我们当前使用的是 Kubernetes 2.23.2:

Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.2", GitCommit:"e6c093d87ea4cbb530a7b2ae91e54c0842d8308a", GitTreeState:"clean", BuildDate:"2022-02-16T12:38:05Z", GoVersion:"go1.17.7", Compiler:"gc", Platform:"linux/amd64"}

让我们通过以下命令查看可用的最新版本:

  apt update
  apt-cache madison kubeadm

现在我们知道可用的最新版本:

图 3.2 – 可用版本

图 3.2 – 可用版本

一旦我们决定了想要升级到哪个版本,就可以开始准备升级过程:

  1. 我们将从升级 kubeadm 开始,需要使用以下命令将 1.23.x-00 中的x替换为最新的补丁版本,在我们的情况下是 1.23.3:
apt-mark unhold kubeadm && \
apt-get update && apt-get install -y kubeadm=1.23.3-00 && \
apt-mark hold kubeadm

apt-mark命令的输出如下:

kubeadm set on hold.
  1. 现在我们可以使用kubeadm version命令检查 kubeadm 的版本,看看它是否是 1.23.3:
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.2", GitCommit:"e6c093d87ea4cbb530a7b2ae91e54c0842d8308a", GitTreeState:"clean", BuildDate:"2022-02-16T12:38:05Z", GoVersion:"go1.17.7", Compiler:"gc", Platform:"linux/amd64"}
  1. 我们使用kubeadm upgrade plan命令检查当前集群是否可以升级,以及它可以升级到哪些可用版本:
    kubeadm upgrade plan

图 3.3所示,我可以将 kubelet 和控制平面组件(如 API 服务器、调度器和控制器管理器)从 1.23.2 升级到 1.23.3:

图 3.3 – kubeadm 升级计划

图 3.3 – kubeadm 升级计划

  1. 如果我们决定采取行动,将当前集群从 1.23.2 升级到 1.23.3,可以使用以下命令。请注意,在apply之后,你只需替换为任何未来的可用版本,升级到你希望的版本:
    kubeadm upgrade apply v1.23.3

重要提示

为了顺利执行升级操作,我们建议你通过运行sudo su命令获取考试中的 root 权限。

在日常升级任务中,你可以使用sudo并输入密码来执行此操作。

一旦执行命令,你会看到一条消息,表明升级成功:

图 3.4 – 控制平面成功升级

图 3.4 – 控制平面成功升级

  1. 然后我们需要隔离节点,因此我们将清理工作负载以准备节点进行维护。我们用以下命令隔离一个名为cloudmelonplaysrv的节点:
kubectl drain cloudmelonplaysrv --ignore-daemonsets

它将显示一堆正在被驱逐的 Pod,意味着这些 Pod 正在从被隔离的工作节点中被移除:

图 3.5 – 在节点上清理工作负载

图 3.5 – 在节点上清理工作负载

如果你正在使用kubectl get no命令,节点将被标记为schedulingDisabled

  1. 我们使用以下命令来升级 kubelet 和 kubectl:
apt-mark unhold kubelet kubectl && \
apt-get update && apt-get install -y kubelet=1.23.3-00 kubectl=1.23.3-00 && \
apt-mark hold kubelet kubectl
  1. 重启 kubelet:
sudo systemctl daemon-reload
sudo systemctl restart kubelet
  1. 现在我们可以解锁该节点,这样正在升级的节点cloudmelonplaysrv上的工作负载就可以再次调度:
kubectl uncordon cloudmelonplaysrv

该命令将返回现在显示为uncordoned的节点。

升级工作节点

由于工作节点是实际运行工作负载的地方,我们需要逐一升级,然后将相同的操作复制到当前 Kubernetes 集群中的所有其他工作节点上。图 3.6描绘了总体的升级工作流:

图 3.6 – 在节点上排空工作负载

图 3.6 – 在节点上排空工作负载

  1. 让我们从使用以下命令将 kubeadm 从 1.23.2 升级到 1.23.3 开始:
  apt-mark unhold kubeadm && \
  apt-get update && apt-get install -y kubeadm=1.23.3-00 && \
  apt-mark hold kubeadm
  1. 对于工作节点,我们升级 kubelet,这也会升级本地 kubelet 配置,使用以下命令:
  sudo kubeadm upgrade node
  1. 同样,我们需要对节点进行 cordon 操作,这样我们就能将工作负载迁移出去,为节点的维护做准备。在这里,我们使用以下命令对一个名为cloudmelonplayclient的节点进行 cordon 操作:
kubectl drain cloudmelonplayclient --ignore-daemonsets

然后,我们可以使用kubectl get no命令来检查节点状态。它将标记为schedulingDisabled

  1. 我们使用以下命令来升级 kubelet 和 kubectl,就像我们对主节点所做的那样:
apt-mark unhold kubelet kubectl && \
apt-get update && apt-get install -y kubelet=1.23.3-00 kubectl=1.23.3-00 && \
apt-mark hold kubelet kubectl
  1. 重启 kubelet 以使更改生效:
sudo systemctl daemon-reload
sudo systemctl restart kubelet
  1. 最后,我们可以uncordon节点,它将使工作负载重新能够调度到名为cloudmelonplayclient的节点上。它将返回节点,显示为uncordoned状态:
kubectl uncordon cloudmelonplayclient

我们现在已经完成了工作节点的升级过程。在升级完成后,请确保使用kubectl get nodes命令,确保所有节点的状态为ready

与 etcd 协作

集群数据存储在 Kubernetes 集群中的一个键值存储 etcd 中。集群数据包括集群状态信息,例如 Pod 状态数据、节点状态数据和配置。由于这些数据对于 Kubernetes 将工作负载编排到期望的状态至关重要,因此合理的做法是定期备份这些数据。

要访问 Kubernetes 集群中的 etcd 集群,我们可以运行以下命令:

  kubectl get po -n kube-system

这将列出当前在kube-system命名空间中运行的所有 Pod:

图 3.7 – 检查当前 etcd Pod 状态

图 3.7 – 检查当前 etcd Pod 状态

在接下来的章节中,我们将更详细地了解 etcd 集群 Pod,并学习所有与实际 CKA 考试中有用的相关信息。

探索 ETCD 集群 Pod

要更详细地查看我们当前的 etcd Pod,请使用以下命令:

  kubectl describe po <etcd-podname> -n kube-system

例如,要获取名为etcd-cloudmelonplaysrv的 etcd Pod 的详细信息,命令如下:

  kubectl describe po etcd-cloudmelonplaysrv -n kube-system

它返回以下输出:

图 3.8 – 检查当前 etcd Pod

图 3.8 – 检查当前 etcd Pod

在图中,你可以看到关于 etcd 的以下重要信息:

      etcd
      --advertise-client-urls=https://172.16.16.129:2379
      --cert-file=/etc/ubernetes/pki/etcd/server.crt
      --client-cert-auth=true
      --data-dir=/var/lib/etcd
      --initial-advertise-peer-urls=https://172.16.16.129:2380
      --initial-cluster=cloudmelonplaysrv=https://172.16.16.129:2380
      --key-file=/etc/ubernetes/pki/etcd/server.key
      --listen-client-urls=https://127.0.0.1:2379,https://172.16.16.129:2379
      --listen-metrics-urls=http://127.0.0.1:2381
      --listen-peer-urls=https://172.16.16.129:2380
      --name=cloudmelonplaysrv
      --peer-cert-file=/etc/ubernetes/pki/etcd/peer.crt
      --peer-client-cert-auth=true
      --peer-key-file=/etc/ubernetes/pki/etcd/peer.key
      --peer-trusted-ca-file=/etc/ubernetes/pki/etcd/ca.crt
      --snapshot-count=10000
      --trusted-ca-file=/etc/ubernetes/pki/etcd/ca.crt
    State:          Running

在所有可配置参数中,以下几项在你与 etcd 的日常工作中会非常有用:

  • --advertise-client-urls告诉 etcd 接受来自客户端的请求。它接受一个 URL 列表。

  • --cert-file 是我们指定客户端服务器 TLS cert 文件路径的地方。

  • --key-file 是我们指定客户端服务器 TLS key 文件路径的地方。

  • --trusted-ca-file 是我们指定客户端服务器 TLS trusted CA cert 文件路径的地方。

这些是用于通过安全通信验证客户端请求的关键标志。您需要它们来检查 etcd 状态、备份和恢复 etcd 集群。

重要提示

访问 etcd 相当于获得集群中的 root 权限。我们确保身份验证请求仅通过 API 服务器进行。

要了解更多可配置的参数,请访问 etcd.io/docs/v3.5/op-guide/configuration/

列出 etcd 集群成员

使用从 kubectl describe pod 命令获取的信息,我们可以列出 etcd 集群的成员:

   kubectl exec etcd-cloudmelonplaysrv -n kube-system -- sh -c "ETCDCTL_API=3 etcdctl member list --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key"

它返回关于成员的信息。在我们的例子中,由于我们只使用一个主节点,因此只会有一个结果。我们的命令将如下所示:

8d1f17827821818f, started, cloudmelonplaysrv, https://172.16.16.129:2380, https://172.16.16.129:2379, false

输出描述了诸如 IDStatus 等列,这些列显示了 etcd 集群的状态、集群名称、对等节点和客户端地址。

您可以使用 --write-out=table 自动将输出以表格形式呈现。它将看起来像这样:

图 3.9 – 当前的 etcd 成员列表

图 3.9 – 当前的 etcd 成员列表

请注意,客户端地址与 kubectl describe pod 输出中的 --advertise-client-urls URL 的值相同。

检查 etcd 集群状态

您可以使用以下命令来检查 etcd 集群状态并以表格形式输出结果。请注意,在使用正确的 etcdctl API 版本时,以下示例中我们使用的是 API 版本 3:

ETCDCTL_API=3 etcdctl endpoint status

以下命令用于从 Kubernetes 集群访问 etcd pod,并查看多节点 etcd 集群中 etcd pod 的状态:

  kubectl -n kube-system exec <etcd-podname> -- sh -c "ETCDCTL_API=3 etcdctl endpoint status --write-out=table --endpoints=https://<IP1>:2379,https://<IP2>:2379,https://<IP3>:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key"

您可以在以下命令中使用从etcdctl member list获取的信息:

  • ETCDCTL_API 是 etcdctl 的版本。

  • --endpoints 是您的 etcd 成员的客户端地址,如果您有多个主节点的话。

然而,在本章中,我们展示的是一个单一的主节点,并且它只包含一个 etcd 成员。因此,从 Kubernetes 集群访问名为 etcd-cloudmelonplaysrv 的 etcd pod 并查看其状态的命令将如下所示:

  kubectl -n kube-system exec etcd-cloudmelonplaysrv -- sh -c "ETCDCTL_API=3 etcdctl endpoint status --endpoints=https://172.16.16.129:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --write-out=table"

输出将如下所示:

图 3.10 – 当前的 etcd 成员列表

图 3.10 – 当前的 etcd 成员列表

kubectl describe pod <etcd-podname> 的输出中,我们还可以看到我们有两个 listen client IP:

  --listen-client-urls=https://127.0.0.1:2379,https://172.16.16.129:2379

当我们检查 Kubernetes 集群内部的 etcd 集群状态时,我们还可以使用内部端点地址https://127.0.0.1:2379来检查 etcd 集群状态。可以使用以下命令访问名为etcd-cloudmelonplaysrv的 etcd pod,并使用内部端点检查该 pod 的状态:

  kubectl -n kube-system exec etcd-cloudmelonplaysrv -- sh -c "ETCDCTL_API=3 etcdctl endpoint status --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --write-out=table"

它会返回关于 etcd 集群的信息:

图 3.11 – 当前的 etcd 成员列表

图 3.11 – 当前的 etcd 成员列表

在接下来的章节中,我们将探讨如何从 Kubernetes 集群外部的客户端与 etcd 集群交互。

安装 etcd

要在 Kubernetes 集群外部访问 etcd,您需要安装 etcdctl。您可以按照本节中的说明进行操作。但请注意,这不属于 CKA 考试的一部分。

要开始操作,我们需要获取 etcd 二进制文件:

 wget https://github.com/etcd-io/etcd/releases/download/v3.4.18/etcd-v3.4.18-linux-amd64.tar.gz
tar xvf etcd-v3.4.18-linux-amd64.tar.gz
sudo mv etcd-v3.4.18-linux-amd64/etcd* /usr/local/bin

安装完成后,您可以使用以下命令验证当前版本:

    etcdctl version

命令返回当前的 etcdctl 客户端版本和 API 版本,格式如下:

图 3.12 – 检查 Kubernetes 集群外部的当前 etcdctl 版本

图 3.12 – 检查 Kubernetes 集群外部的当前 etcdctl 版本

同样,您可以使用以下命令检查 Kubernetes 集群中的 kubectl 版本。当您使用kubectl exec命令时,它会直接在名为etcd-cloudmelonplaysrv的 pod 上执行,该 pod 位于kube-system命名空间中。我们可以使用以下命令执行etcdctl version Bash 命令,以获取 etcd 存储的版本:

    kubectl exec etcd-cloudmelonplaysrv -n kube-system -- sh -c "etcdctl version"

返回的结果类似于 etcdctl 客户端版本和 API 版本:

图 3.13 – 检查 Kubernetes 集群中的当前 etcdctl 版本

图 3.13 – 检查 Kubernetes 集群中的当前 etcdctl 版本

同样,一旦安装了 etcdctl,您可以通过运行以下命令检查 etcd 存储状态,并获取端点状态:

    kubectl exec etcd-cloudmelonplaysrv -n kube-system -- sh -c " etcdctl --write-out=table --endpoints=$ENDPOINTS endpoint status "
If you want to make sure the etcd store is healthy, using the following command with the endpoint from the Previous command : 
   kubectl exec etcd-cloudmelonplaysrv -n kube-system -- sh -c " etcdctl --endpoints=$ENDPOINTS endpoint health "

备份 etcd

根据我们在前面章节中做的所有准备工作,我们可以将备份 etcd 过程概括如下:

  1. SSH 进入 etcd 集群节点。它可以是一个独立的节点,也可以与主节点相同。在 CKA 考试中,您很可能会从主节点开始,在该节点上安装了 etcdctl,因此这一步是可选的。

  2. 检查 etcd 状态。您可以通过kubectl describe <etcd-podname>命令获取必要的信息。

  3. 执行 etcd 备份。

  4. 退出主节点。在实际的 CKA 考试中,这一步可能不是必需的。

一般过程如下图所示:

图 3.14 – 备份 etcd 过程

图 3.14 – 备份 etcd 过程

现在让我们看看备份 etcd 的详细过程:

  1. 如果你需要连接到主节点或 etcd 集群节点,可以使用ssh master-0命令或ssh username@<nodeIP>命令。请注意,这一步是可选的。以下是一个名为packtuser的用户使用ssh连接到 IP 地址为10.10.11.20的节点的示例:
 ssh packtuser@10.10.11.20
  1. 使用以下命令从集群外部检查 etcd 状态:
sudo ETCDCTL_API=3 etcdctl endpoint status --endpoints=https://172.16.16.129:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --write-out=table

返回的输出将如下所示:

图 3.15 – 从集群外部检查 etcd 状态

图 3.15 – 从集群外部检查 etcd 状态

  1. 使用etcdctl snapshot save命令备份 etcd 集群。它将像这样:
sudo ETCDCTL_API=3 etcdctl --endpoints $ENDPOINT snapshot save snapshotdb

由于你是从 Kubernetes 集群外部进行备份操作,你需要从 API 服务器进行身份验证并使用安全通信。为此,你可以使用以下命令:

sudo ETCDCTL_API=3 etcdctl snapshot save snapshotdb
--endpoints=https://172.16.16.129:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key

返回的输出显示你已成功备份了 etcd 存储:

图 3.16 – 备份 etcd 存储

图 3.16 – 备份 etcd 存储

  1. 通过以下命令验证快照:
sudo ETCDCTL_API=3 etcdctl snapshot status snapshotdb --endpoints=https://172.16.16.129:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --write-out=table

以下图展示了 etcd 集群的状态:

图 3.17 – 使用快照备份检查 etcd 存储

图 3.17 – 使用快照备份检查 etcd 存储

恢复 etcd

要恢复 etcd 集群,你可以按照图 3.18中展示的过程进行操作。请注意,如果你有任何 API 服务器实例正在运行,你需要在执行恢复操作之前停止它们。etcd 恢复后,你可以重新启动 API 服务器实例:

  1. SSH 连接到 etcd 集群节点。

  2. 检查 etcd 状态。

  3. 恢复 etcd 备份。

  4. 退出主节点:

图 3.18 – 恢复 etcd 过程

图 3.18 – 恢复 etcd 过程

一旦你有了快照,你可以使用以下命令从之前的备份操作中恢复 etcd:

sudo ETCDCTL_API=3 etcdctl --endpoints 172.16.16.129:2379 snapshot restore snapshotdb

返回的输出显示 etcd 存储已成功恢复:

图 3.19 – 使用现有快照恢复的 etcd 存储

图 3.19 – 使用现有快照恢复的 etcd 存储

你现在已经完成了 etcd 恢复过程。

请注意,如果你想从不同的补丁版本恢复 etcd 集群,可以使用此方法。定期备份 etcd 非常重要,然后执行恢复操作以从失败的集群中恢复集群数据。要了解更多关于 Kubernetes 的 etcd 集群备份和恢复,请查看kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/

总结

本章涵盖了 Kubernetes 管理员最常见的工作之一 - 维护和升级 Kubernetes 集群。与集群安装类似,这也是 CKA 考试中最耗时的任务之一。再次强调,熟能生巧。第二章**,安装和配置 Kubernetes 集群中的 HA 拓扑将帮助您理解需要升级的内容以及如何执行。如果需要,请回顾第一章**,Kubernetes 概述,确保对 Kubernetes 组件有充分的理解。这样,您将知道如何以及需要什么来升级控制平面和工作节点。

与集群升级相比,备份和恢复 etcd 在 CKA 考试中是价值最高的问题之一,因为回答简单且分数高。彻底练习本章所学内容将帮助您在考试中克服任何挑战。

在下一章中,我们将讨论应用程序调度和生命周期管理,我们将重新审视一些重要的 Kubernetes 对象和概念,并探讨它们在 CKA 考试和实际生活中的作用。请继续关注!

模拟 CKA 场景实践测试

您有两台虚拟机,master-0worker-0。请完成以下模拟场景:

场景 1

SSH 到 master-0 节点,检查当前 kubeadm 版本,并升级到最新的 kubeadm 版本。检查当前 kubectl 版本,并升级到最新的 kubectl 版本。

场景 2

SSH 到 worker-0 节点,查看当前 kubeadm 版本,并升级到最新的 kubeadm 版本。查看当前 kubelet 版本,并升级到最新的 kubelet 版本。

场景 3

SSH 到 master-0 节点并备份 etcd 存储。

场景 4

SSH 到 master-0 节点并将 etcd 存储恢复到之前的备份。

您可以在本书的附录**- 模拟 CKA 场景实践测试解决方案中找到所有场景的解决方法。

常见问题解答

  1. 我在哪里可以找到每个发布版本的 Kubernetes 组件的兼容版本信息?

前往 Kubernetes 官方文档了解版本偏差策略:kubernetes.io/releases/version-skew-policy/

  1. 我在哪里可以了解 etcd 存储的最新发展?

前往 etcd.io/,您将找到 etcd 存储的最新发展。有关守护程序和如何开始使用 etcd,请参阅官方文档:etcd.io/docs/

  1. 升级 Kubernetes 集群的推荐官方 Kubernetes 文章是什么?

我建议将文章《升级 kubeadm》添加到书签中,在其中你将找到大部分关键命令和过程:kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/

  1. 备份和恢复 etcd 的官方推荐 Kubernetes 文章是什么?

我建议将文章《为 Kubernetes 操作 etcd 集群》添加到书签中,在其中你将找到所有关于 etcd 备份和恢复的关键命令:kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/

第二部分:Kubernetes 管理

本部分描述了如何管理部署在 Kubernetes 上的工作负载,以及如何管理 Kubernetes 集群的安全性和网络,以满足企业需求。

本书的这一部分包括以下章节:

  • 第四章**, 应用调度与生命周期管理

  • 第五章**, 揭开 Kubernetes 存储的面纱

  • 第六章**, Kubernetes 安全性

  • 第七章**, 揭开 Kubernetes 网络的面纱

第四章:应用程序调度和生命周期管理

本章描述了如何使用 Kubernetes 部署来部署 Pods、扩展 Pods、执行滚动更新和回滚、进行资源管理,并使用 ConfigMaps 配置 Pods,使用 kubectl 命令和 YAML 定义。本章涵盖了 CKA 考试内容的 15%。

本章将覆盖以下主要内容:

  • Kubernetes 工作负载基础

  • 部署和管理应用程序

  • 扩展应用程序

  • 执行滚动更新和回滚

  • 资源管理

  • 工作负载调度

  • 配置应用程序

技术要求

为了开始,我们需要确保你的本地机器满足以下技术要求:

  • 兼容的 Linux 主机——我们推荐基于 Debian 的 Linux 发行版,如 Ubuntu 18.04 或更高版本

  • 确保你的主机至少具有 2 GB 内存、2 个 CPU 核心以及约 20 GB 的空闲磁盘空间

Kubernetes 工作负载基础

Kubernetes 协调你的工作负载以实现期望的状态——一个运行在 Kubernetes 上的容器化工作负载,包括无状态、有状态和数据处理应用程序。关于云原生应用程序,有一篇有趣的白皮书深入介绍了云原生应用程序的概念和设计模式,如果你感兴趣,可以在这里查看:www.redhat.com/en/resources/cloud-native-container-design-whitepaper

任何在 Kubernetes 集群中运行的容器化工作负载的基本构建模块称为 Kubernetes API 原语或 Kubernetes 对象。它们是 Kubernetes 中定义的 API 资源类型,包括 Pods、ReplicaSets、DaemonSets、StatefulSets、Job 和 CronJob 对象,以及在第一章中提到的 Deployments,Kubernetes 概述

CKA 考试涵盖了一些主要的 Kubernetes 对象,如 Pods、Deployments、ReplicaSets 和 DaemonSets,在使用 Kubernetes 集群时,我们将在本章的下一部分深入探讨这些内容。

在开始实践之前,请确保你的本地机器符合所需的技术要求。

命令式管理与声明式管理

在 Kubernetes 中,有几种方式可以与 API 服务器进行通信——主要可以归类为命令式管理或声明式管理。你将需要同时使用 kubectl 和 YAML 定义来管理 Kubernetes 对象。kubectl 工具支持所有管理 Kubernetes 对象的管理技术,因为 Kubernetes 旨在作为期望状态的管理器。执行 kubectl 命令后,结果将当前运行在 Kubernetes 中的工作负载从实际状态转移到期望状态,该期望状态是在命令行参数或 YAML 定义的规范中定义的。

时间管理是 CKA 考试成功的关键。熟悉 kubectl 命令将帮助你在新部署时节省大量时间。对 YAML 定义的良好理解将帮助你快速更新配置。

理解 Pod

Kubernetes 中最小的可部署单元是 Pod。Pod 包含实际的应用工作负载 – 它可以是一个或多个容器。Kubernetes 中的 Pod 有一个定义的生命周期。我们将覆盖以下关于 Pod 的主题:

  • 理解 Pod

  • 理解 Pod 的健康探测

  • 理解多容器 Pod

  • 理解初始化容器

  • 理解静态 Pod

让我们先看一下 Pod。你可以使用如下命令创建一个 Pod:

kubectl run <pod-name> --image=<image-name:image-tag>

这是运行名为 ngin-pod 的 Pod 示例,镜像为 nginx,镜像标签为 alpine

kubectl run nginx-pod --image=nginx:alpine

你将看到输出返回为created,如下所示,表示你的 Pod 已经成功创建:

pod/nginx-pod created

在此过程中,你将看到 Pod 具有 ContainerCreating 状态,表示容器正在创建,你可以使用 kubectl 的描述命令查看发生了什么。我们可以使用以下命令检查 Pod 的当前状态:

kubectl describe pod nginx-pod

describe 命令的底部,你将看到事件 – 这对于你检查部署过程中是否出现问题非常有帮助。我们将在第八章中进一步探讨 Pod 故障排除,监控和日志 Kubernetes 集群与应用程序

图 4.1 – Pod 事件

图 4.1 – Pod 事件

相同的 Pod 也可以通过 YAML 定义,如下所示,结果相同:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:alpine
    ports:
    - containerPort: 80

你可以使用以下命令来部署一个 YAML 定义:

kubectl apply -f <your-spec>.yaml

类似地,我们可以通过一个命令运行 BusyBox 镜像,如下所示:

kubectl run busybox --rm -it --image=busybox /bin/sh

你还可以部署一个 nginx 镜像,然后使用 -o yaml 标志导出 YAML 定义:

kubectl run nginx --image=nginx --dry-run -o yaml > pod-sample.yaml

运行此命令后,一个示例的 yaml 文件将被导出到本地计算机 – 如果需要,你可以编辑这个 yaml 文件进行本地修改。

理解存活性、就绪性和启动探针

为了进一步探讨 Pod 的健康状态,我们来谈谈健康探针。探针让你了解 Kubernetes 如何判断容器的状态。我们来逐一看看每种探针:

  • 存活性探针表示容器是否正常运行,因为它们决定了集群何时自动重启容器。

  • 就绪性探针表示容器是否准备好接受请求。

  • kubelet 在它们启动之前会进行初始化。一旦配置完成,它们会禁用存活性检查和就绪性检查,直到容器启动完成。

我们将在 第八章 中更详细地了解这些内容,《Kubernetes 集群和应用程序的监控与日志记录》。你可以通过以下链接找到更多关于健康检查探针的细节:kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

理解多容器 Pod

多容器 Pod 就是具有多个容器一起作为一个单元工作的 Pod。当多个容器驻留在一个 Pod 中时,一个容器与另一个容器的交互有以下两种方式:

  • 共享网络:当两个容器在同一主机上运行并且它们在同一个 Pod 中时,它们可以通过简单地使用 localhost 互相访问。即使容器没有对外暴露端口,Pod 内的所有监听端口也可以被其他容器访问。

图 4.2 显示了同一 Pod 中多个容器如何共享本地网络:

图 4.2 – 多容器 Pod 的共享网络

图 4.2 – 多容器 Pod 的共享网络

  • 共享存储卷:我们可以将相同的存储卷挂载到两个不同的容器,以便它们可以共同访问相同的数据 —— 一个容器可以将数据写入存储卷,另一个容器则可以从同一存储卷读取数据。有些存储卷甚至允许并发读写。我们将在 第五章 中深入探讨多容器 Pod 的存储工作原理,《揭开 Kubernetes 存储的神秘面纱》

图 4.3 显示了同一 Pod 中多个容器如何共享本地存储:

图 4.3 – 多容器 Pod 的共享存储卷

图 4.3 – 多容器 Pod 的共享存储卷

以下是如何在 Pod 中创建多个容器的示例:

apiVersion: v1
kind: Pod
metadata:
  name: multi-app-pod
  labels:
      app: multi-app
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
  - name: busybox-sidecar
    image: busybox
    command: ['sh', '-c', 'while true; do sleep 3600; done;']

一般来说,容器与 Pod 之间最好是保持一对一的关系,这符合通过保持每个模块独立来构建微服务的原则。现实世界有时比看起来更复杂,让我们来看看多容器 Pod 的情况。

理解初始化容器

初始化容器是在 Pod 中配置的,用于在容器主机启动之前执行。它在 initContainers 部分中指定,如以下示例所示。你还可以配置多个初始化容器,这样每个初始化容器将按顺序逐个完成:

apiVersion: v1
kind: Pod
metadata:
  name: melon-pod
  labels:
    app: melonapp
spec:
  containers:
  - name: melonapp-container
    image: busybox:latest
    command: ['sh', '-c', 'echo The melonapp is running! && sleep 3600']
  initContainers:
  - name: init-melonservice
    image: busybox:latest
    command: ['sh', '-c', 'until nslookup melonservice; do echo waiting for melonservice; sleep 2; done;']
  - name: init-melondb
    image: busybox:latest
    command: ['sh', '-c', 'until nslookup melondb; do echo waiting for melondb; sleep 2; done;']

如果任何初始化容器未能完成,Kubernetes 会不断重启 Pod,直到初始化容器成功。要了解更多关于初始化容器的信息,请访问以下链接:kubernetes.io/docs/concepts/workloads/pods/init-containers/

理解静态 Pod

作为工作节点的“队长”,kubelet 代理可以独立管理一个节点,并且可以创建 pods。由 kubelet 守护进程直接管理并绑定到特定节点的 pod 称为静态 pod。与由 Kubernetes 主节点管理的 pod 相对,静态 pod 由 kubelet 代理监视,并且在失败时会重启。

配置 kubelet 使其读取 pod 定义文件的方法是将 YAML 规范添加到以下目录,该目录存储静态 pod 信息:

 /etc/kubernetes/manifests

kubelet 会定期检查该目录。此路径可以在 kubelet.service 中进行配置。

理解 Job 和 CronJob 对象

Completed 状态。

Jobs 可用于可靠地执行工作负载,直到其完成。Job 将创建一个或多个 pod。当 Job 完成时,容器将退出,pod 将进入 Completed 状态。Jobs 的一个典型使用场景是当我们想要运行特定的工作负载,并确保它只运行一次并成功执行时。

  1. 你可以使用 YAML 描述来创建一个 Job:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: pi
    spec:
      template:
        spec:
          containers:
          - name: pi
            image: perl
            command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
          restartPolicy: Never
      backoffLimit: 4
    

backoffLimit 参数表示如果失败 4 次,这就是限制。所有的 Job 在底层创建 pod 时都是一样的。虽然正常的 pod 会持续运行,但当 Job 完成时,它会进入 Completed 状态。这意味着容器不再运行,所以 pod 仍然存在,但容器已经完成。

  1. 你可以使用以下命令部署 YAML 定义:

    kubectl apply -f melon-job.yaml
    
  2. 你可以运行以下命令检查 Job 的状态:

    kubectl get job
    
  3. 当 Job 仍在运行时,你可以看到 Running 状态。当 Job 完成时,你可以看到它是完成状态,以下是一个例子:

图 4.4 – Job 已完成

图 4.4 – Job 已完成

CronJobs 基于 Job 的能力,通过允许用户按计划执行 Jobs 来增加价值。用户可以使用 cron 表达式根据需求定义特定的计划。以下是 CronJob YAML 定义的示例:

apiVersion: batch/v1
kind: CronJob
metadata:
 name: hello
spec:
 schedule: "*/1 * * * *"
 jobTemplate:
   spec:
     template:
       spec:
         containers:
         - name: hello
           image: busybox
           args:
           - /bin/sh
           - -c
           - date; echo Hello from the Kubernetes cluster
         restartPolicy: OnFailure
  1. 你可以使用以下命令部署 YAML 定义:

    kubectl apply -f melon-cronjob.yaml
    

你可以使用以下命令检查 cron 作业的状态:

kubectl get cronjob

你将获得如下输出:

图 4.5 – cron 作业显示为已完成

图 4.5 – cron 作业显示为已完成

这个 cron 作业创建了几个名为 hello 的 pod,因此我们将使用以下命令检查 Job 的日志:

kubectl get pods | grep hello

你将获得如下输出:

图 4.6 – 完成的 cron 作业 pod

图 4.6 – 完成的 cron 作业 pod

我们可以使用以下命令检查这些 pod 的日志:

kubectl logs hello-xxxx

我们可以看到 cron 作业已经执行完成:

图 4.7 – 显示 cron 作业如何完成的日志

图 4.7 – 显示 cron 作业如何完成的日志

如果你想删除 cron 作业,可以使用以下命令:

kubectl delete cronjobs hello

然后,您将看到以下输出,表示您的定时任务已被删除:

cronjob.batch "hello" deleted

CronJobs 已在 Kubernetes v1.21 中提升为正式功能。您可以在这里找到一篇关于使用 CronJob 执行自动化任务的精彩文章:kubernetes.io/docs/tasks/job/automated-tasks-with-cron-jobs

部署和管理应用程序

本章的以下部分将通过具体的实践练习,带您了解您在实际 CKA 考试中会遇到的场景,包括如何部署和扩展应用程序、执行滚动更新和回滚、管理和治理这些应用程序的资源消耗,以及如何配置它们。

部署应用程序

部署应用程序可以通过多种方式实现,例如通过 kubectl 或 YAML 定义来部署一个 pod,就像我们在本章的 Kubernetes 工作负载基础 部分中所做的那样。现在,我们将深入了解使用 Deployments 的更有效方法。本节将介绍如何部署和扩展应用程序。

Deployments

Deployment 是定义期望状态部署的便捷方式——它为我们提供了一种更好的方式来通过滚动更新无缝升级基础实例、撤销更改以及根据需要暂停和恢复更改。例如,像部署具有特定副本数的 ReplicaSet 这样的操作可以轻松实现滚动发布和回滚,更加高效。下图展示了 Deployment 的概念图:

图 4.8 – 一个 Deployment

图 4.8 – 一个 Deployment

Deployments 提供了一种定义副本 pod 所需状态的方法。您可以使用如下的 YAML 定义来定义一个 Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

以下属性对于帮助您理解前面的 YAML 定义非常重要:

  • spec.replicas 给出了副本 pod 的数量

  • spec.template 是模板 pod 描述符,用于定义将要创建的 pod

  • spec.selector 是将管理所有标签与此选择器匹配的 pod 的部署

我们可以使用以下 kubectl 命令来创建一个 Deployment:

kubectl create deployment kubeserve --image=nginx:latest

运行上述命令后,您将得到以下输出:

deployment.apps/kubeserve created

您可以使用 kubectl get deploy 查询当前命名空间中的所有 Deployments,如下所示:

kubectl get deployments

您将在输出中看到以下 Deployment 状态:

图 4.9 – 使用 kubectl 获取 Deployments

图 4.9 – 使用 kubectl 获取 Deployments

如果您知道 Deployment 的名称,可以使用以下命令来获取该 Deployment:

kubectl get deployment kubeserve

您将看到以下输出:

图 4.10 – 使用 kubectl 按名称获取 Deployment

图 4.10 – 使用 kubectl 按名称获取 Deployment

以下命令允许您获取 Deployment 的详细信息:

kubectl describe deployment kubeserve

此命令将帮助你了解 Deployment 中的配置,你将看到以下输出:

图 4.11 – kubectl 描述 Deployment

图 4.11 – kubectl 描述 Deployment

以下命令允许你进行 Deployment 的实时编辑:

kubectl edit deployment kubeserve

上述命令是一个非常有用的命令,它允许你实时编辑 Deployment。以下是示例输出,你可以进行实时编辑——它的工作方式类似于你使用 vim 编辑器创建 pod 时的操作。你可以在这里实时编辑 Deployment,然后使用 wq! 保存并退出:

图 4.12 – kubectl 描述 Deployment 进行实时编辑

图 4.12 – kubectl 描述 Deployment 进行实时编辑

然后,如果你不再需要 Deployment,可以使用 kubectl delete 命令删除它:

kubectl delete deployment melon-serve

以下输出显示 Deployment 已成功删除:

deployment.apps "kubeserve" deleted

随着 Deployment 的删除,在该 Deployment 中定义的对象也被删除,因为它们共享相同的生命周期。在我们的第三个示例中,部署的nginx pod 被删除,因为我们删除了 kubeserve Deployment。

学习 Deployment 使你能够更有效地管理应用程序,更轻松地将其作为一个整体进行更新,并将其回滚至之前的版本。在下一节中,我们将了解滚动更新和回滚。

执行滚动更新和回滚

滚动更新提供了一种更加高效、有效的方式来更新 Deployment 至较新版本。通过这种方式,你可以逐步更新 Kubernetes 对象,如副本和 pod,几乎实现零停机时间。简而言之,你可以考虑使用kubectl set image命令,或者直接更新 YAML 清单文件。在本节中,我们将介绍kubectl set image,因为它在实际的 CKA 考试中非常有效且便捷。

使用 kubectl 进行滚动更新

在这里,我们将介绍使用 kubectl 执行滚动更新的步骤:

  1. 你可以使用以下命令启动一个新的 Deployment,kubeserve

    kubectl create deployment kubeserve --image=nginx:latest
    
  2. 你可以使用kubectl按照以下方式更新容器镜像:

    kubectl set image deployment/kubeserve nginx=nginx:1.18.0 --record
    

重要说明

--record flag 用于记录更新的信息,以便以后可以回滚。你可以使用--record flag或者--record=true flag

使用上述命令后,你将看到以下输出:

deployment.apps/kubeserve image updated
  1. 你可以使用 kubectl describe 命令通过输入以下命令来再次检查容器镜像是否已成功更新:

    kubectl describe deploy kubeserve
    

你的输出应该类似于 图 4.14,其中你可以看到镜像已设置为nginx:1.18.0

图 4.13 – kubectl 描述更新镜像后的 kubeserve

图 4.13 – kubectl 描述更新镜像后的 kubeserve

kubectl describe deploy 命令在我们尝试查看关键信息时非常有用,比如容器镜像、端口和与部署相关的事件。在实际的 CKA 考试中也是如此——确保你掌握此命令的快捷方式 k describe deploy,这将帮助你在考试中更加高效地工作。

回滚

回滚使我们能够恢复到之前的状态,而部署让这一切变得非常简单:

  1. 如果需要执行回滚,你可以使用以下 kubectl rollout 命令来快速恢复:

    kubectl rollout undo deployments kubeserve
    

你的输出应如下所示:

deployment.apps/kubeserve rolled back
  1. 现在,如果你使用 kubectl describe deploy kubeserve 命令,你将看到以下输出,表明镜像已被回滚:

图 4.14 – kubectl 描述回滚后的 kubeserve

图 4.14 – kubectl 描述回滚后的 kubeserve

  1. 现在,你可能非常好奇是否可以跟踪我们部署的历史记录。你可以使用以下命令:

    kubectl rollout history deployment kubeserve
    

输出将如下所示:

图 4.15 – kubectl 描述 kubeserve

图 4.15 – kubectl 描述 kubeserve

  1. 如果你想回滚到特定版本,你可以使用 --to-revision 参数。你可以在 图 4.16 中看到,由于我们在设置镜像版本时使用了 --record 参数,我们可以使用版本 2。以下命令是回滚部署并恢复到版本 2 的示例:

    kubectl rollout undo deployment kubeserve --to-revision=2
    

你的输出应如下所示:

deployment.apps/kubeserve rolled back
  1. 现在,如果你使用 kubectl describe deploy kubeserve 命令,你将看到以下输出,表明镜像已回滚到版本 2

图 4.16 – kubectl 描述 kubeserve

图 4.16 – kubectl 描述 kubeserve

部署不仅使滚动更新和回滚过程变得更加简单,而且帮助我们轻松地进行扩缩容——在下一节中,我们将了解如何扩展应用程序,以及扩展时所有可行的选项。

扩展应用程序

当我们的应用程序变得流行时,为了处理越来越多的按需请求,我们需要启动多个应用程序实例来满足工作负载要求。

当你有一个部署时,扩展是通过更改副本数来实现的。在这里,你可以使用 kubectl scale 命令来扩展部署:

kubectl scale deployment kubeserve --replicas=6

你的输出应如下所示:

deployment.apps/kubeserve scaled

如果你现在使用 kubectl get pods 命令,你将看到更多副本的 Pods 被启动,输出如下所示:

图 4.17 – kubectl 获取 Pods 并显示更多副本

图 4.17 – kubectl 获取 Pods 并显示更多副本

除了使用 kubectl scale 命令手动扩展部署,我们还有另一种扩展部署及其 ReplicaSets 的方式,那就是 HorizontalPodAutoscalerHPA)。让我们先来看一下 ReplicaSets。

ReplicaSets

ReplicaSets 帮助 pods 实现更高的可用性,因为用户可以使用 ReplicaSet 定义一定数量的副本。ReplicaSet 的主要功能是确保集群中保持精确数量的副本在 Kubernetes 集群中运行。如果任何副本失败,它们会被新副本取代。

以下是一个 ReplicaSet 的 YAML 定义示例:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: melonapp-rs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: melonapp-rs
  template:
    metadata:
      labels:
        app: melonapp-rs
    spec:
      containers:
      - name: nginx
        image: nginx

matchLabels 选择器简单地将其下指定的标签与 pods 上的标签进行匹配。要检查你的 ReplicaSet,可以使用以下命令:

kubectl get replicaset

另外,你也可以使用以下命令:

kubectl get rs

然后,你会看到输出显示 DESIRED 副本 数量以及有多少副本处于 READY 状态:

图 4.18 – kubectl 获取 rs 命令显示 ReplicaSet 的状态

图 4.18 – kubectl 获取 rs 命令显示 ReplicaSet 的状态

一旦 ReplicaSet 部署完成,可以使用以下命令更新 ReplicaSet 的数量:

kubectl scale replicaset frontend --replicas=6

你的输出应如下所示:

replicaset.apps/frontend scaled

另外,你也可以在 YAML 定义中使用以下命令指定:

kubectl scale --replicas=6 -f replicas.yaml

你的输出应如下所示:

replicaset.apps/frontend scaled

现在,如果你想检查 ReplicaSets 的数量是否增加,可以再次使用 kubectl get rs 命令,你将能够看到以下输出:

图 4.19 – kubectl 获取 ReplicaSets

图 4.19 – 使用 kubectl 获取 ReplicaSets

如果你想删除一个 ReplicaSet,可以使用 kubectl delete 命令——在这种情况下,我们可以使用它删除名为 frontend 的 ReplicaSet:

kubectl delete replicaset frontend

你的输出应如下所示:

replicaset.apps "frontend" deleted

直接使用 ReplicaSets 不是唯一的扩展应用程序的方式。接下来让我们来看一下另一种方式,HPA。

HPA

要更新工作负载资源(例如 Deployment 或 StatefulSet),我们也可以使用 HPA——这是一个 Kubernetes API 原语,根据你的需求自动扩展工作负载。图 4.18 解释了在应用扩展的上下文中 HPA 是如何工作的:

图 4.20 – HPA

图 4.20 – HPA

从前面的图示中,我们可以看到 HPA 配置为基于 CPU 和内存使用情况从度量服务器中获取指标。这些指标由度量服务器从kubelet获取,然后通过度量 API 将它们暴露给 API 服务器。HPA 通过增加或减少副本数来扩展部署,副本数由底层的 ReplicaSet 管理。

随着按需资源请求的增加,HPA 扩展部署并增加副本数。相反,当资源请求减少时,副本数也会减少。

要创建一个 HPA,你可以使用 kubectl autoscale deployment 命令并带上以下标志来满足要求:

  • cpu-percent 表示所有 pod 的平均 CPU 利用率

  • min 提供最小副本数

  • max 提供最大副本数

你可以使用以下命令创建一个 HPA,CPU 利用率为 50%,并确保最少有 3 个副本,最多可达 10 个副本:

kubectl autoscale deployment kubeserve --cpu-percent=50 --min=3 --max=10

你的输出应该如下所示:

horizontalpodautoscaler.autoscaling/kubeserve autoscaled

要查看当前在默认命名空间中有多少个 HPA,请使用以下命令:

kubectl get hpa

输出将如下所示:

图 4.21 – 获取默认命名空间中的 HPAs

图 4.21 – 获取默认命名空间中的 HPAs

你还可以使用以下 YAML 定义来部署 HPA,这样也能达到相同的目标:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: kubeserve
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: kubeserve
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

如果你想删除一个 HPA,请使用 kubectl delete 命令。这里,我们可以按如下方式删除名为 kubeserve 的 HPA:

kubectl delete hpa kubeserve

你的输出应该如下所示:

horizontalpodautoscaler.autoscaling "kubeserve" deleted

我们接下来会介绍另一个概念:DaemonSets,它在实际中更为常用,特别是在至少需要将 pod 的一个副本均匀分布在各个工作节点的场景中。让我们直接开始吧。

DaemonSets

我们已经了解了 ReplicaSets 和 Deployments 如何帮助我们确保应用程序的多个副本在各个工作节点上正常运行。DaemonSets 会创建 pod 的几个副本,并确保至少有一个副本均匀分布在 Kubernetes 集群中的每个节点上,如 图 4.23 所示。

如果集群中新增了一个节点,该节点会自动分配一个 pod 副本。类似地,当一个节点被移除时,该 pod 会自动被移除。

图 4.22 – DaemonSets

图 4.22 – DaemonSets

你可以使用以下 YAML 定义来定义一个 DaemonSet:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
  labels:
    k8s-app: fluentd
spec:
  selector:
    matchLabels:
      name: fluentd
  template:
    metadata:
      labels:
        name: fluentd
    spec:
      containers:
      - name: fluentd
        image: fluentd:latest

你的输出将如下所示:

daemonset.apps/fluentd created

注意,这次我们在名为 kube-system 的命名空间中创建了这个 DaemonSet——这个命名空间通常保留给 Kubernetes 系统创建的对象。我们稍后会讨论命名空间。现在,你可以使用以下命令来检查 DaemonSet 是否已经创建:

kubectl get daemonsets -n kube-system

或者,我们可以简化命令:

kubectl get ds -n kube-system

你的输出将如下所示:

图 4.23 – 查看 kube-system 命名空间中的 DaemonSets

图 4.23 – 查看 kube-system 命名空间中的 DaemonSets

别忘了通过以下命令查看 DaemonSets 的详细信息:

kubectl describe daemonsets fluentd -n kube-system

你的输出将如下所示:

图 4.24 – kubectl 描述 DaemonSets

图 4.24 – kubectl 描述 DaemonSets

如果你想删除一个 DaemonSet,请使用 kubectl delete 命令。这里,我们可以按如下方式删除在 kube-system 命名空间中的名为 fluentd 的 DaemonSet:

kubectl delete ds fluentd -n kube-system 

你的输出应该如下所示:

daemonset.apps "fluentd" deleted

DaemonSets 的主要用途是将其用作每个节点上的监控代理或日志收集器,或者在其他情况下,用于在所有工作节点上运行集群存储守护进程。

使用 DaemonSets 时,你不必担心添加或删除会影响这些节点上的监控代理的节点。实际应用案例,比如 fluentd,需要在集群中的每个节点上部署代理。

工作负载调度

理解工作负载调度及其与 Kubernetes 调度器的配合将对你作为 Kubernetes 管理员的日常工作有所帮助。Kubernetes 允许你通过良好的标签、选择器和注释来定义节点亲和性规则、污点和容忍度,帮助你前进。首先,我们从命名空间的概念开始。

理解命名空间

考虑到工作负载的隔离,命名空间非常方便。命名空间是一个逻辑分隔符,用于将所有部署在单个 Kubernetes 集群中的命名空间对象隔离开来。Deployments、Services 和 Secrets 都是命名空间对象。否则,一些 Kubernetes 对象是集群范围的,例如 Nodes、StorageClass 和 PersistentVolume。资源的名称在命名空间内必须是唯一的。

你可以使用以下命令获取所有命名空间:

kubectl get namespaces

或者,你可以使用以下命令:

kubectl get ns

你将看到输出会列出当前我们 Kubernetes 集群中的所有命名空间:

图 4.25 – kubectl 获取命名空间

图 4.25 – kubectl 获取命名空间

当你定义一个 pod 或任何命名空间的 Kubernetes 对象时,可以在 YAML 定义中指定命名空间,如下所示:

   apiVersion: v1
   kind: Pod
   metadata:
    name: k8s-ns-pod
    namespace: k8s-ns
    labels:
      app: k8sapp
   spec:
    containers:
    - name: k8sapp-container
      image: busybox
      command: ['sh', '-c', 'echo Salut K8S! && sleep 3600']

如果你创建了该 pod 并指定了 pod 所属的命名空间,那么在使用 kubectl get pods 命令查询该 pod 时,可以添加 -n 标志。以下是一个示例:

kubectl get pods -n k8s-ns

类似地,如果 pod 已在该命名空间中创建,你可以使用以下命令查看:

kubectl describe pod k8s-ms-pod -n k8s-ns

如果 pod 不在默认命名空间中,你不再需要指定命名空间选项。以下示例中,你想设置一个名为 dev 的命名空间,然后使用没有 -n 标志的 kubectl get 命令:

kubectl config set-context &(kubectl config current-context) --namespace=dev

然后,你可以简单地运行以下命令,不带命名空间选项,来列出 pods:

kubectl get pods

理解命名空间将进一步帮助你在需要定义命名空间作用域的权限时,在 Kubernetes 对象分组时提供帮助。我们将在 第六章Kubernetes 安全》中进一步阐述。

标签、节点选择器和注释

标签、选择器和注释是工作负载调度时非常有用的概念。标签是附加到 Kubernetes 对象上的键值对,可以在对象描述符的 metadata.labels 部分列出。选择器用于通过标签识别和选择一组对象。请参见以下基于质量的选择器示例:

kubectl get pods -l app=my-app
kubectl get pods -l environment=production

当涉及到不等式时,你可以使用以下命令:

kubectl get pods -l environment!=production

以下示例通过使用逗号分隔的列表将多个选择器串联在一起:

kubectl get pods -l app=myapp.environment=production

要将一个 Pod 分配到节点上,我们可以使用节点选择器。你可以在 PodSpec 字段中指定一组键值对:

你可以通过以下命令开始为工作节点打标签:

kubectl label node cloudmelonplayground env=dev

输出应该如下所示:

node/cloudmelonplayground labeled

你可以使用以下命令显示工作节点的标签:

kubectl get nodes --show-labels

然后,我们应该得到以下输出:

图 4.26 – 获取节点标签

图 4.26 – 获取节点标签

然后,你可以在 YAML 定义中添加节点选择器,如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
  nodeSelector:
    env: dev

我们可以使用 metadata.annotations 部分将注解附加到对象上,以下是带有注解 imageregistry: "http://hub.docker.com/" 的配置文件:


apiVersion: v1
kind: Pod
metadata:
  name: melon-annotation
  annotations:
    imageregistry: "https://hub.docker.com/"
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80

注解类似于标签,它们可以用来存储关于对象的自定义元数据。

节点亲和性和反亲和性

节点亲和性和反亲和性是帮助 Pod 分配到正确节点的方式。与此相比,nodeSelector 是用于将 Pod 直接分配到工作节点的。以下是 YAML 配置中节点亲和性和反亲和性的示例:

 spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: topology.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: topology.kubernetes.io/zone

通过特定的标签,节点亲和性和反亲和性使我们能够创建具有逻辑和操作的匹配规则。

污点和容忍

除了节点亲和性和反亲和性之外,我们还可以通过给节点打上污点并确保没有 Pod 被调度到该节点来在节点上分配污点和在 Pod 上分配容忍。

你可以使用以下命令给节点打污点:

kubectl taint nodes melonnode app=melonapp:NoSchedule

上面的定义可以转化为 Pod 的 YAML 定义文件,以实现相同的结果,如下所示:

  apiVersion: v1
  kind: Pod
  metadata:
   name: melon-ns-pod
   namespace: melon-ns
   labels:
     app: melonapp
  spec:
   containers:
   - name: melonapp-container
     image: busybox
     command: ['sh', '-c', 'echo Salut K8S! && sleep 3600']
   tolerations:
   - key: "app"
     operator: "Equal"
     value: "melonapp"
     effect: "NoSchedule"

如果你想解除节点的污点,可以使用以下命令:

kubectl taint nodes yourworkernode   node-role.kubernetes.io/yourworkernode:NoSchedule-

在这一部分中,我们已经学会了如何在需要将工作负载从节点中驱逐时给某些节点打污点。现在,让我们来看一下资源管理。

资源管理

Kubernetes 允许我们在 Pod 规格中指定容器的资源需求,这基本上是指容器需要多少资源。

kube-scheduler 使用你为 Pod 中的容器指定的资源请求信息来决定将 Pod 调度到哪个工作节点。kubelet 负责在你为容器指定资源限制时执行这些限制,以确保运行中的容器不会超出设定的限制,并为容器保留至少请求的系统资源量。

它通常会给我们以下值:

  • resources.limits.cpu 是设置的 CPU 使用资源限制。

  • resources.limits.memory 是设置的内存使用资源限制。

  • resources.requests.cpu 是为使应用程序正常运行而请求的最小 CPU 使用量。

  • resources.requests.memory 是请求的最小内存使用量,用于确保你的应用程序能够正常运行。如果容器超出了其内存请求,运行该容器的工作节点会同时内存不足,容器所属的 Pod 很可能也会被驱逐。

  • resources.limits.ephemeral-storage 是对临时存储资源的限制。

  • resources.limits.hugepages-<size> 是对 Pod 中任何应用程序预分配的大页内存分配和消耗的限制。

资源请求是指运行容器所需的资源量,它们决定了容器将在哪个工作节点上调度。因此,当 Kubernetes 准备运行一个特定的 Pod 时,它会根据 Pod 容器的资源请求来选择一个工作节点。Kubernetes 将使用这些值来确保选择一个有足够资源来运行该 Pod 的节点。Pod 只会在拥有足够可用资源的节点上运行。以下是定义 resource requestlimits 的 YAML 示例:

apiVersion: v1
kind: Pod
metadata:
 name: melonapp-pod
spec:
 containers:
 - name: melonapp-container
   image: busybox
   command: ['sh', '-c', 'echo stay tuned! && sleep 3600']
   resources:
     requests:
       memory: "64Mi"   # 64 Megabytes
cpu: "250m" 
     limits:
       memory: "128Mi"
       cpu: "500m"

你可以使用 kubectl describe node 命令检查该节点的资源分配,以查看你的请求或限制定义是否符合当前环境下的需求:

图 4.27 – kubectl 描述节点资源

图 4.27 – kubectl 描述节点资源

如果你的集群中已安装了度量服务器,你可以使用 kubectl top 命令检查节点或 Pod 的实际资源使用情况。

配置应用程序

配置应用程序是一个简单直接的过程,得益于 ConfigMaps 和 Secrets。我们来逐一看看它们。

理解 ConfigMaps

ConfigMap 仅仅是一个 Kubernetes 对象,用来存储以键值对形式的配置信息。然后,这些配置信息可以通过配置 Pod 来使用环境变量、命令行参数或挂载包含配置文件的卷,从而配置运行在容器中的软件。

你也可以使用 YAML 定义来定义 configmap,如下所示:

  apiVersion: v1
  kind: ConfigMap
  metadata:
    name: melon-configmap
  data:
    myKey: myValue
    myFav: myHome

你的输出应如下所示:

configmap/melon-configmap created

你可以使用以下命令检查 configmap

kubectl get configmap

或者,你可以使用这个命令:

kubectl get cm

你的输出将如下所示:

图 4.28 – kubectl 获取 configmap

图 4.28 – kubectl 获取 configmap

你可以使用以下命令检查 configmap 的二进制数据:

k describe configmap melon-configmap

以下截图是前述命令的输出:

图 4.29 – configmap 二进制数据

图 4.29 – configmap 二进制数据

一旦 configmap 准备好,下面是如何配置 Pod 来使用它:

  1. 通过使用环境变量创建一个可以使用 configmap 数据的 Pod:

    apiVersion: v1
    kind: Pod
    metadata:
      name: melon-configmap
    spec:
      containers:
      - name: melonapp-container
    image: busybox
        command: ['sh', '-c', "echo $(MY_VAR) && sleep 3600"]
        env:
        - name: MY_VAR
          valueFrom:
            configMapKeyRef:
              name: melon-configmap
              key: myKey
    

你可以使用以下命令来检查configmap的值:

 kubectl logs melon-configmap

输出将类似于以下内容:

图 4.30 – configmap 挂载的值

图 4.30 – configmap 挂载的值

  1. 你可以通过卷来创建 pod 使用configmap数据。以下是一个 YAML 定义的示例:

    apiVersion: v1
    kind: Pod
    metadata:
      name: melon-volume-pod
    spec:
      containers:
       - name: myapp-container
         image: busybox
         command: ['sh', '-c', "echo $(cat /etc/config/myKey) && sleep 3600"]
         volumeMounts:
           - name: config-volume
             mountPath: /etc/config
      volumes:
        - name: config-volume
          configMap:
            name: melon-configmap
    

你可以使用kubectl logs命令检查 pod 中的挂载数据值,或者使用以下命令来检查configmap

kubectl exec melon-volume-pod -- ls /etc/config

输出将如下所示:

图 4.31 – configmap 挂载的值

图 4.31 – configmap 挂载的值

如果你想删除一个configmap,请使用kubectl delete命令:

kubectl delete cm melon-configmap

你的输出将如下所示:

configmap "melon-configmap" deleted

在这里,我们展示了如何在 Kubernetes 中使用 ConfigMap。一旦你对 ConfigMap 感到熟悉,你会发现当你处理 Secrets 时,有很多相似之处。接下来,我们将看看如何处理 Kubernetes Secrets,以便它们能被你的应用消费。

理解 Secrets

Kubernetes Secret 是一个包含敏感数据的对象,例如密码、API 令牌或密钥,这些数据会传递给 pod,而不是存储在PodSpec字段或容器中:

kubectl create melon-secret --from-literal=username=packtuser 
  --from-literal=password='S!B\*d$zDsb='

你还可以使用 YAML 定义来将configmap定义为以下形式并使用 base64:

apiVersion: v1
kind: Secret
metadata:
  name: melon-secret
type: Opaque
data:
  USER_NAME: bXl1c2VybmFtZQo=
  PASSWORD: bXlwYXNzd29yZAo=

你可以使用以下命令检查 Secrets:

kubectl get secrets

你的输出将如下所示:

图 4.32 – kubectl 获取 Secrets

图 4.32 – kubectl 获取 Secrets

创建 Secret 后,你可能希望将其附加到一个应用程序中。这时,你需要创建一个 pod 来消费该 Secret,步骤如下:

  1. 你可以创建一个 pod,通过环境变量消费 Secret:

    apiVersion: v1
    kind: Pod
    metadata:
      name: melon-secret-pod
    spec:
      containers:
        - name: test-container
          image: busybox:latest
          command: [ "/bin/sh", "-c", "env" ]
          envFrom:
          - secretRef:
              name: melon-secret
      restartPolicy: Never
    
  2. 你还可以将 Secret 作为卷来使用,如下所示——你需要定义一个secret-volume,然后将secret-volume挂载到/etc/secret-volume路径:

      volumes:
      - name: secret-volume
        secret:
          secretName: melon-secret
      containers:
      - name: mybusybox
        image: busybox:latest
        command: [ "/bin/sh", "-c", "env" ]
        volumeMounts:
        - name: secret-volume
          readOnly: true
          mountPath: "/etc/secret-volume"
    

如果你想删除一个 Secret,请使用以下kubectl delete命令:

kubectl delete secret melon-secret

你的输出将如下所示:

secret "melon-secret" deleted

请注意,如果你删除了一个 Secret,确保更新你应用的PodSpec字段,以避免出现异常。你可以通过创建一个新的 Secret,然后将其附加到你的 pod 上,或者更新你的应用使其不再需要该 Secret。

使用 kustomize 进行清单管理

从 Kubernetes 1.14 开始,提供了自定义文件,以便更顺畅地管理 Kubernetes。它支持以下用例:

  • 从其他资源生成 YAML 定义,例如生成 Kubernetes Secret 及其 YAML 定义

  • 多个 YAML 定义中的常见配置,例如为一组资源添加命名空间

  • 组合和自定义 YAML 定义集合,例如为多个 Kubernetes 对象设置资源请求和限制

这可以通过一个名为Kustomization.yaml的中央文件来实现。你可以使用以下命令查看包含在定制文件中的目录中的资源:

kubectl kustomize <targeting_kustomization_directory>

然后,你可以通过运行以下命令来应用这些资源:

kubectl apply -k <targeting_kustomization_directory>

以 Secret 生成为例,生成一个 Secret 清单文件:

# Create a password.txt file
cat <<EOF >./password.txt
username=admin
password=secret
EOF
cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: my-app
        volumeMounts:
        - name: password
          mountPath: /secrets
      volumes:
      - name: password
        secret:
          secretName: example-secret-1
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
secretGenerator:
- name: example-secret-1
  files:
  - password.txt
EOF

然后,你将能够看到在执行前述步骤后创建了两个文件:

kustomization.yaml  password.txt

如果你想查看customization.yaml文件的内容,可以使用cat customization.yaml命令,输出将如下所示:

secretGenerator:
- name: example-secret-1
  files:
  - password.txt

然后,你可以使用kubectl apply命令来部署带有挂载 Secret 的 Pod:

kubectl apply -f ./test

Kustomize 是一个非常好的方式来定制你的应用程序配置,现在它已集成到kubectl apply -k中,你可以通过访问官方文档网站,深入了解 Kustomize 的使用场景:kubectl.docs.kubernetes.io/guides/

使用 Helm 进行常见的包管理和模板化操作

Helm 是一个管理预配置 Kubernetes 对象包的工具,形式为 chart——我们称之为 Helm charts。Helm charts 允许用户更可复现、更高效地安装和管理 Kubernetes 应用程序。此外,你还可以通过以下链接,从社区中查找流行的 Helm charts,或与 Helm 社区共享你自己的应用程序:artifacthub.io/packages/search

chart 的标准文件结构如下:

  • Charts – (文件夹)

  • Chart.yaml # – 一个.yaml文件,其中包含有关 chart 的信息

  • README.md

  • requirements.lock

  • requirements.yaml – 一个可选的文件,用于列出 chart 的依赖项(这些依赖项实际上是打包在Charts文件夹中的)

  • templates – 一个包含模板的目录,这些模板与值结合生成 Kubernetes 清单文件

  • values.yaml – 包含 chart 的默认配置值(这是 Helm 从中获取清单模板中包含的参考值的地方)

要查询已部署的 Helm charts,可以使用以下命令:

helm install stable/melonchart

如果你需要搜索某个 chart,可以使用以下命令:

helm search chartname

使用以下命令删除已部署的 Helm chart:

helm delete melonchart

每当你安装一个 chart 时,都会创建一个新的发布版本。因此,同一个 chart 可以多次安装到同一个集群中。每个发布版本都可以独立管理和升级。要将发布版本升级到指定的 chart 版本或更新 chart 的值,请运行以下命令:

helm upgrade [RELEASE] [CHART_path] [flags]

要回滚到特定版本,你可以使用以下命令:

helm rollback melon-release 2

Helm charts 帮助你管理、安装和升级 Kubernetes 本地应用程序。你可以访问 Helm 的官方网站,了解更多关于 Helm 的信息:helm.sh/docs/

总结

本章中,我们介绍了 Kubernetes 管理员和开发人员最常做的任务之一——应用调度和应用生命周期管理。虽然本章内容涵盖了 CKA 考试约 15% 的内容,但作为 Kubernetes 管理员,处理 Kubernetes 对象是最重要的日常任务之一。在继续之前,确保你充分练习并掌握 kubectl 命令的快捷方式。

在下一章中,我们将讨论 Kubernetes 存储。本书的 第四章 应用调度与生命周期管理第五章 破解 Kubernetes 存储 被认为是在实际 CKA 考试中高价值且不费时的内容,敬请关注并继续学习!

模拟 CKA 情景练习测试

你有两台虚拟机,master-0worker-0。请完成以下模拟场景。

情景 1

SSH 进入 worker-0 节点,并创建一个名为 ngnix 的新 Pod,包含一个容器 nginx

情景 2

SSH 进入 worker-0,然后将 nginx 扩展为 5 个副本。

情景 3

SSH 进入 worker-0,设置一个包含用户名和密码的 ConfigMap,然后将一个新的 Pod 附加到 BusyBox。

情景 4

SSH 进入 worker-0,并创建一个包含名为 busybox 的 init 容器的 nginx Pod。

情景 5

SSH 进入 worker-0,创建一个 nginx Pod,然后在同一 Pod 中创建一个 busybox 容器。

你可以在本书的 附录 - 模拟 CKA 情景练习测试解析 中找到所有情景的解析。

常见问题

  • 在哪里可以了解 Helm charts?

访问 Helm 的官方文档,了解更多有关 Helm 的信息:helm.sh/docs/howto/charts_tips_and_tricks/

  • 在哪里可以了解 Kustomize?

访问 Helm 的官方文档,了解更多有关 Kustomize 的信息:kubectl.docs.kubernetes.io/references/kustomize/

  • 关于 init 容器,推荐的官方 Kubernetes 文章是什么?

我建议收藏这篇文章,Init 容器kubernetes.io/docs/concepts/workloads/pods/init-containers/

  • 你推荐的 Kubernetes 官方文章关于 ConfigMaps 是哪一篇?

我建议收藏这篇文章,ConfigMapskubernetes.io/docs/concepts/configuration/configmap/

  • 你推荐的 Kubernetes 官方文章关于资源管理是哪一篇?

我建议收藏这篇文章,Pod 和容器的资源管理kubernetes.io/docs/concepts/configuration/manage-resources-containers/

第五章:解密 Kubernetes 存储

本章将讨论 Kubernetes 存储的核心概念,针对有状态工作负载,并展示如何配置带挂载存储和动态持久存储的应用程序。本章内容覆盖 认证 Kubernetes 管理员 (CKA) 考试的 10%。

在本章中,我们将涵盖以下主要内容:

  • 有状态与无状态工作负载

  • Kubernetes 卷

  • Kubernetes 存储类

  • 卷模式、访问模式和卷的回收策略

  • 配置带挂载存储的应用程序

  • 配置持久存储的应用程序

技术要求

在开始之前,我们需要确保你的本地机器满足以下技术要求:

  • 兼容的 Linux 主机 – 我们推荐使用基于 Debian 的 Linux 发行版,如 Ubuntu 18.04 或更高版本

  • 确保你的主机机器至少有 2 GB 的 RAM、2 个 CPU 核心,并且有大约 20 GB 的空闲磁盘空间

有状态与无状态工作负载

Kubernetes 旨在支持有状态和无状态应用程序。为了在 Kubernetes 中保持无状态工作负载,我们可以自由删除和替换容器,无需额外考虑。有状态应用程序通常会附加存储,无论是本地存储还是远程位置的存储,因为它需要保存客户端数据。这些数据可能是短暂的或非持久存储,这意味着它们只会保留直到会话过期。例如,Kubernetes 上的 Redis 缓存就是这样的应用场景。另一个使用案例是当数据需要通过持久存储长期保存,以便可以按需使用时。后者的一个示例是 Kubernetes 上的 MongoDB 操作器。整个过程比看起来更复杂,但一切都从 Kubernetes 卷开始。

Kubernetes 卷表示 Kubernetes 中存储的概念。如在第一章《Kubernetes 概述》中提到的,Kubernetes 中的卷由存储供应商定制的存储驱动程序进行管理。在引入 容器存储接口 (CSI) 后,这部分不再是 Kubernetes 源代码的一部分。

卷可以支持本地存储、本地软件定义存储、云存储(如 blob 存储、块存储或文件存储)或 网络文件系统 (NFS),如图 5.1 所示:

图 5.1 – 一个 CSI

图 5.1 – 一个 CSI

然后,用户可以使用与 CSI 兼容的卷驱动程序和 CSI 卷,将其附加或直接挂载到 Kubernetes 集群中正在运行的 Pods。

Kubernetes 卷

瞬态卷和持久卷是 Kubernetes 中的两种主要卷类型。我们将分别了解它们。虽然其中一些可能不会在 CKA 考试中涉及,但了解它们很重要,因为你所在的组织无论如何都会与某个公共云服务提供商展开合作。

瞬态存储

目标是应用程序需要保存数据的临时卷,但如果 Pod 失败或重启,它们不关心数据丢失——临时卷的生命周期与 Pod 的生命周期一致。考虑到这一点,挂载的存储通常是临时的,因为它与容器共享相同的生命周期。只要容器在重启 Pod 的过程中停止或销毁,任何内部存储都会被完全移除。

另一个使用场景是当一个 Pod 包含多个容器时。可以将存储挂载到这些容器,并允许它们共享相同的卷,从而在共享的文件系统上进行交互。

临时卷有几种类型,我们将逐一介绍。

emptyDir

emptyDir 是最常见的临时存储类型之一,并且会出现在 CKA 考试中。它通常作为 Pod 启动时的一个空目录,并与 Pod 共享相同的生命周期,这意味着它只存在于 Pod 运行时,并且当 Pod 停止或重启时,emptyDir 中的数据会被永久删除。

当涉及到同一 Pod 中的多个容器时,它们可以共享存储卷,尽管每个容器可以将 emptyDir 挂载到不同的目录,如 图 5.2 所示:

图 5.2 – Pod 中多个容器共享存储卷

图 5.2 – Pod 中多个容器共享存储卷

以下是一个将 emptyDir 挂载到 Pod 的 YAML 示例定义:

apiVersion: v1
kind: Pod
metadata:
  name: multi-containers
spec:
  restartPolicy: Never
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: busybox-pod
    image: busybox
    command: ["/bin/sh","-c","while true; do sleep 3600; done"]
    volumeMounts:
    - name: shared-data
      mountPath: /tmp/data
  - name: nginx-pod
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /data

通过前面的示例,你可以看到如何在两个容器之间挂载共享卷,当你希望这两个容器共享相同的数据源时,这会非常有用。

CSI 临时卷

CSI 临时卷是与 CSI 驱动程序兼容的卷,作为临时存储使用。在过去的很长一段时间里,Kubernetes 中由外部存储驱动程序提供的 CSI 卷被用作持久卷,目的是不与 Pod 共享生命周期。从 Kubernetes 1.15 开始,CSI 驱动程序也可以用于此类临时内联卷。以下是使用 CSI 临时卷的示例:

kind: Pod
apiVersion: v1
metadata:
  name: my-csi-pod
spec:
  containers:
    - name: my-frontend
      image: busybox
      volumeMounts:
      - mountPath: "/data"
        name: my-csi-vol
      command: ["sleep", "1000000"]
  volumes:
    - name: my-csi-vol
      csi:
        driver: inline.storage.kubernetes.io
        volumeAttributes:
          foo: bar

这些 CSI 存储驱动程序通常是第三方的,例如 Azure Disk、Azure File、AWS EBS 和 DellEMC Unity——你可以在 kubernetes-csi.github.io/docs/drivers.xhtml 找到完整的 CSI 驱动程序列表。

通用临时卷

通用临时卷是具有一些附加功能的通用驱动程序,如快照、存储克隆、存储调整大小和存储容量跟踪。以下是使用 CSI 临时卷的示例:

kind: Pod
apiVersion: v1
metadata:
  name: my-app
spec:
  containers:
    - name: my-frontend
      image: busybox
      volumeMounts:
      - mountPath: "/cache"
        name: cache-volume
      command: [ "sleep", "1000000" ]
  volumes:
    - name: scratch-volume
      ephemeral:
        volumeClaimTemplate:
          metadata:
            labels:
              type: my-cache-volume
          spec:
            accessModes: [ "ReadWriteOnce" ]
            storageClassName: "my-cache-storage-class"
            resources:
              requests:
                storage: 1Gi

通用临时卷可以与所有支持动态供应的存储驱动程序一起使用,包括一些第三方 CSI 存储驱动程序。现在我们已经对临时卷有了较好的理解,我们将看看投影卷,并了解它们如何与 Kubernetes 一起工作。

投影卷

配置数据被挂载到 Kubernetes Pod 中——这些数据通过边车模式注入到 Pod 中。我们在第四章《应用调度与生命周期管理》中已经讨论了 ConfigMapSecret 对象,它们属于这一类别。更具体地说,它们也被称为投影卷,因为它们代表一个将多个现有卷映射到同一目录的卷。

除了 ConfigMap 和 Secret,投影卷还包括downwardAPI卷和服务账户令牌。我们将在这里通过一些示例更详细地了解它们。

downwardAPI 卷旨在使 downward API 数据对应用程序可用。同样,它也作为一个目录挂载,然后将数据以纯文本文件的形式写入。downward API 允许容器在不使用 Kubernetes API 服务器或客户端的情况下,消费集群或 Pod 信息。

以下示例展示了如何将 downwardAPI 作为投影卷挂载:

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - downwardAPI:
          items:
            - path: "labels"
              fieldRef:
                fieldPath: metadata.labels
            - path: "cpu_limit"
              resourceFieldRef:
                containerName: container-test
                resource: limits.cpu

服务账户令牌类型的投影卷旨在使 downward API 数据对应用程序可用。同样,它也作为一个目录挂载,然后将数据以纯文本文件的形式写入。

以下示例展示了如何将服务账户令牌作为投影卷挂载:

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - serviceAccountToken:
          audience: api
          expirationSeconds: 3600
          path: token

让我们总结一下本节内容,回顾我们在第四章《应用调度与生命周期管理》中学到的关于 downwardAPI 和服务账户令牌卷的内容,以及关于 ConfigMap 和 Secret 对象的知识,通过以下示例来帮助你理解如何在一次操作中处理所有这些内容:

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
            - key: username
              path: my-group/my-username
      - downwardAPI:
          items:
            - path: "labels"
              fieldRef:
                fieldPath: metadata.labels
            - path: "cpu_limit"
              resourceFieldRef:
                containerName: container-test
                resource: limits.cpu
      - configMap:
          name: myconfigmap
          items:
            - key: config
              path: my-group/my-config
      - serviceAccountToken:
          audience: api
          expirationSeconds: 3600
          path: token

所有投影卷,configMapdownwardAPIsecretemptyDir,都作为本地短暂存储提供。在每个节点上,kubelet 负责配置和管理 Pod,并管理本地短暂存储。

除了作为内部存储的挂载存储外,在某些使用场景中,我们还需要容器生命周期之外的持久数据,即使容器停止或被替换,数据依然存在。这就提出了为我们的 Pod 分配持久外部存储的需求。我们将在下一节讨论持久卷。

持久化存储

与短暂卷相比,持久卷的生命周期与 Kubernetes Pod 无关。状态持久性意味着在容器删除或替换后,某些数据或信息能够继续存在。然而,在容器运行时,它可以被容器修改或更新。

在 Kubernetes 中与持久卷(Persistent Volume)一起工作的机制利用了暴露的 API,它抽象了外部存储提供和消费的技术细节。Kubernetes 允许我们通过持久卷和持久卷声明的概念来操作持久存储:

  • 持久卷PV)是根据存储类动态供应的存储资源,具有一组功能,以满足用户的需求。

  • 持久卷声明PVC)是用户请求的 Pod 和 PV 之间的抽象层,包含一组要求,包括特定的资源级别和访问模式。

如下所示,图 5.3 中,PV 和 PVC 被定义在 Kubernetes 集群中,而物理存储则位于 Kubernetes 集群之外:

图 5.3 – PV 和 PVC

图 5.3 – PV 和 PVC

同样需要注意的是,PV 可以绑定到 PVC,它是集群范围的资源,而 PVC 是命名空间级别的资源。

在我们深入探讨如何使用之前,先来了解一些关于 PV 和 PVC 的其他重要概念。

存储类(StorageClass)

Kubernetes 中的 StorageClass 资源将 Kubernetes 存储分类。事实上,StorageClass 包含 provisionerparametersreclaimPolicy 字段。

供应器(provisioner)表示用于供应 PV 的 CSI 卷插件。不同供应器的示例包括 Azure 磁盘、AWS EBS 和 Glusterfs。你可以在这里找到支持的 StorageClass 资源的完整列表:kubernetes.io/docs/concepts/storage/storage-classes/

我们需要在 PVC 中定义存储类,存储类的定义包括供应器和回收策略。它们之间的关系如 图 5.3 所示:

图 5.4 – 一个 StorageClass 资源

图 5.4 – 一个 StorageClass 资源

注意,当回收策略(reclaim policy)未指定时,它默认为 Delete,这意味着如果用户删除了与此 PV 绑定的 PVC,则 PVC 本身也会被删除。你还可以将其设置为 Retain,这意味着它将被保留,你需要手动删除其中存储的数据。另一种情况是将其设置为 Recycle,此时 PV 将被回收、废弃,并由动态供应替代,这将取决于供应器。Kubernetes API 服务器上的 DefaultStorageClass 入站控制器也需要启用——这超出了 CKA 考试的范围,但我认为还是值得提及。

以下是一个 StorageClass 定义的示例,使用 Azure 磁盘管理的磁盘来定义一个 StorageClass 资源,并给出 YAML 定义:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/azure-disk
parameters:
  storageaccounttype: Standard_LRS
  kind: managed

有趣的是,尽管本地卷不支持动态供应,它们仍然可以在 pod 被调度时创建并绑定。我们可以将 volumeBindingMode 设置为 WaitForFirstConsumer,如下所示:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
commissions: kubernetes.io/no commissions
volumeBindingMode: WaitForFirstConsumer

了解 Kubernetes 中的存储类将帮助你在实际工作中处理不同的存储,超越当前 CKA 考试的要求。请随时查看官方文档——每当新增一个支持的存储类时,文档会进行更新,并提供有用的示例:kubernetes.io/docs/concepts/storage/storage-classes/

接下来,我们来看看另一个重要的概念——卷模式。

卷模式

卷模式表示卷的消费类型——它可以是文件系统或块设备。当 volumeMode 设置为 Filesystem 时,它作为目录挂载到 pods 中。当 volumeMode 设置为 Block 时,我们将其作为原始块使用。

访问模式

当 PV 被挂载到 pod 时,我们可以指定不同的访问模式。访问模式表示存储资源中数据的消费方式。它们可以总结如下表所示:

访问模式 定义 缩写
ReadWriteOnce 该卷可以被一个节点以读写方式挂载。 RWO
ReadOnlyMany 该卷可以被多个节点以只读方式挂载。 ROX
ReadWriteMany 该卷可以被多个节点以读写方式挂载。 RWX
ReadWriteOncePod 该卷可以被一个 pod 以读写方式挂载。这是 Kubernetes 从 1.22 版本开始支持的特性。 RWOP

要了解更多关于访问模式的信息,你可以在这里找到官方文档:kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes

了解访问模式很重要,因为在使用 Kubernetes 存储时它们是常常用到的。现在,我们来看看 PV 和 PVC,看看这些概念如何与 Kubernetes 协同工作。

一个 PV

首先,我们来看看如何创建一个 PV。你可以使用以下 YAML 定义来实现:

  apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: my-pv
  spec:
    storageClassName: local-storage
    capacity:
      storage: 1Gi
    accessModes:
      - ReadWriteOnce

要了解 PV 如何与 Kubernetes 配合工作,可以查看这篇文章:kubernetes.io/docs/concepts/storage/persistent-volumes/#persistent-volumes

仅仅了解 PV 还不够——我们需要了解 PVC 如何与其在 Kubernetes 存储中协同工作,这也是接下来要探讨的内容。

PVCs

关于 PVC 最有趣的一点是,用户无需担心存储的位置细节。用户只需要了解 StorageClassaccessMode。PVC 会自动绑定到具有兼容 StorageClassaccessMode 的 PV。以下是一个 PVC 的示例:

 apiVersion: v1
 kind: PersistentVolumeClaim
 metadata:
   name: my-pvc
 spec:
   storageClassName: local-storage
   accessModes:
       - ReadWriteOnce
   resources:
     requests:
        storage: 512Mi

你可以在 Kubernetes 官方文档中了解更多关于 PVC 的信息:kubernetes.io/docs/concepts/storage/persistent-volumes/#lifecycle-of-a-volume-and-claim

一旦你有了 PV 和 PVC 来定义 Kubernetes 存储,下一步就是将存储分配给部署在 Kubernetes 上的应用。正如我们所解释的,Kubernetes 也能够处理有状态工作负载,因此我们将看看如何将存储挂载到 Kubernetes 中的有状态应用。

破解 Kubernetes 中的有状态应用

在本节中,我们将学习如何在 Kubernetes 中处理有状态应用的存储。此部分的内容通常被认为在 CKA 考试中是高价值、低难度的。确保你不断练习这些内容,直到你自信地掌握它们:

  • 将存储挂载到有状态应用

  • 动态为有状态应用提供存储

配置带有挂载存储的应用

你需要创建一个新的 YAML 定义文件,在其中写出 Kubernetes pod 的规范,然后为该 pod 设置 emptyDir 卷。Kubernetes 在 pod 被调度到特定工作节点后,会在节点上创建空存储:

  1. 使用以下命令检查你当前是否有可用的节点来调度 pod:

    kubectl get nodes
    

或者,你也可以使用前面命令的简化版本:

alias k=kubectl
k get no

如果你任何节点的状态显示为 Ready,如以下图所示,那就意味着你可以继续执行下一步:

图 5.5 – 检查可用的节点

图 5.5 – 检查可用的节点

  1. 使用 Vim 编辑器创建一个新的 YAML 定义文件,命名为 pod-volume.yaml,并在进入 Vim 后按下键盘上的 Insert 键,将当前的 edit 模式切换为 INSERT

图 5.6 – 使用 Vim 插入 YAML 规范

图 5.6 – 使用 Vim 插入 YAML 规范

  1. 然后,将以下内容放入 YAML 定义中:

    apiVersion: v1
    kind: Pod
    metadata:
      name: my-volume-pod
    spec:
      containers:
      - image: busybox
        name: busybox
        command: ["/bin/sh","-c","while true; do sleep 3600;  done"]
        volumeMounts:
        - name: my-volume
          mountPath: /tmp/storage
      volumes:
      - name: my-volume
        emptyDir: {}
    
  2. 然后,保存你的编辑并退出 Vim。按下 Esc 键,在编辑器底部输入 :wq!,然后按 Enter 键返回终端:

图 5.7 – 在 Vim 中保存 YAML 定义

图 5.7 – 在 Vim 中保存 YAML 定义

  1. 在终端中,使用以下命令来部署 .yaml 文件:

    kubectl apply -f pod-volume.yaml
    

然后,它应该会显示一条消息,表明 pod 已成功创建,类似于以下内容:

pod/my-volume-pod created

你可以继续使用kubectl get pods命令检查 Pod 是否正在运行,命令返回以下输出:

图 5.8 – 检查 Pod 是否正在运行

图 5.8 – 检查 Pod 是否正在运行

现在,你已经部署了一个带有挂载存储的 Pod。如果你运行以下命令,你将能够查看更多细节,包括配置信息、资源要求、Pod 的标签和关于这个 Pod 及其挂载存储的事件信息:

kubectl describe pod my-volume-pod

该命令的输出应类似于以下内容:

图 5.9 – 检查 Pod 配置和状态

图 5.9 – 检查 Pod 配置和状态

从输出中,我们可以看到 Pod 已经挂载到名为my-volume的卷上,就像我们在 YAML 定义中指定的那样。Type被指定为EmptyDir,所以它是一个临时目录,和 Pod 的生命周期共享。截图底部还显示了在配置该 Pod 时的相关事件。

配置具有持久存储的应用程序

在这种情况下,你需要创建一个新的 YAML 定义文件,其中写入 Kubernetes PV 的规格——Kubernetes 将在 Pod 被调度到特定工作节点后,根据绑定到该 PV 的 PVC 来分配存储。

创建你的 PV

你可以通过使用kubectl get nodeskubectl get no来检查当前是否有可用的节点来调度 Pod。确保你的某个节点的状态是Ready,如以下所示:

图 5.10 – 检查可用的节点

图 5.10 – 检查可用的节点

从这里开始,我们通过以下步骤创建一个新的 PV:

  1. 使用 Vim 编写以下名为data-pv.yaml的 YAML 定义:

      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: data-pv
      spec:
        storageClassName: local-storage
        capacity:
          storage: 1Gi
        accessModes:
          - ReadWriteOnce
        hostPath:
          path: "/mnt/data"
    
  2. 当你在终端时,使用以下命令来部署.yaml文件:

    kubectl apply -f data-pv.yaml
    

然后,它将显示一条消息,表示 PV 已成功创建,类似于以下内容:

persistentvolume/data-pv created

前面的 YAML 定义意味着分配了 1 GB 的存储作为本地存储。你可以定义一个 1 GB 存储的 PVC,并绑定到这个 PV。然而,在理论情况下,如果你有两个 500 MB 的请求,PV 也可以在分配过程中被拆分。底层,这两个 PVC 会绑定到同一个 PV,然后共享存储容量。

  1. 使用以下命令检查 PV 的状态:

    kubectl get pv
    

你将获得以下输出:

图 5.11 – 检查 PV 是否可用

图 5.11 – 检查 PV 是否可用

注意,状态为available,意味着该 PV 目前没有绑定到 PVC,并且可以与我们将在下一步创建的新的 PVC 进行绑定。

创建你的 PVC

从这里开始,我们通过以下步骤创建一个新的 PVC:

  1. 使用 Vim 编辑以下名为 data-pvc.yaml 的 YAML 定义文件:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: data-pvc
    spec:
      storageClassName: local-storage
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 512Mi
    
  2. 当你在终端上时,使用以下命令来部署 yaml 文件:

    kubectl apply -f data-pvc.yaml
    

PVC 成功创建,并输出类似于以下内容:

persistentvolumeclaim/data-pvc created
  1. 使用以下命令检查 PVC 的状态:

    kubectl get pvc
    

你将得到以下输出:

图 5.12 – 检查 PVC

图 5.12 – 检查 PVC

你可能会注意到该 PVC 的状态为Bound,这意味着它已经绑定到了 PV。

为了再次确认它是否绑定到你想要的 PV,你可以使用kubectl get pv命令进行检查:

图 5.13 – 检查 PVC 是否绑定到 PV

图 5.13 – 检查 PVC 是否绑定到 PV

上图显示了我们 PV 的 Bound 状态,意味着它已经成功绑定。

配置 pod 以使用 PV

在这里,我们正在通过以下步骤配置 pod 以使用 PV:

  1. 使用 Vim 编辑以下名为 data-pod.yaml 的 YAML 定义文件,在这里我们将创建一个 pod 来使用目标 PV:

     apiVersion: v1
     kind: Pod
     metadata:
       name: data-pod
     spec:
       containers:
         - name: busybox
           image: busybox
           command: ["/bin/sh", "-c","while true; do sleep 3600;  done"]
           volumeMounts:
           - name: temp-data
             mountPath: /tmp/data
       volumes:
         - name: temp-data
           persistentVolumeClaim:
             claimName: data-pvc
       restartPolicy: Always
    
    1. 当你在终端上时,使用以下命令来部署 yaml 文件:
    kubectl apply -f data-pod.yaml
    

pod 已成功创建,输出类似于以下内容:

pod/data-pod created

你可以使用 kubectl get pods 命令验证 pod 是否已启动并正在运行。如果你希望命令能够持续监视 pod 的状态,可以在命令中使用 -w 标志;它应该类似于以下内容:

kubectl get pods -w

输出将类似于以下内容:

图 5.14 – 检查 pod 是否已启动并正常运行

图 5.14 – 检查 pod 是否已启动并正常运行

你可以使用以下命令进一步查看详细信息,包括配置、资源要求、pod 的标签以及此 pod 和动态分配的存储的事件信息:

kubectl describe pod data-pod

此命令的输出应类似于以下内容:

图 5.15 – 检查 pod 的详细配置和事件

图 5.15 – 检查 pod 的详细配置和事件

从输出中,我们可以看到 pod 已经动态地附加到了名为temp-data的持久存储上,这是预期的结果,因为我们在 YAML 定义中已经指定了它。截图的底部也显示了在配置此 pod 时相关的事件。

上述是使用 PVC 作为存储卷的示例——这允许 pod 通过使用声明作为存储卷来访问存储。在这种情况下,声明必须存在于 pod 将使用它们的相同命名空间中。

我们还注意到,在某些情况下,用户使用 hostPath 来挂载卷,它只是将集群中该节点的本地存储分配给 pod,使 pod 使用该存储。

重要提示

hostPath 也容易引发安全问题,因此我们应尽量避免使用它。在使用时,我们可以将 volumeMounts 指定为 ReadOnly,并仅使其对特定文件或仓库可用。

以下是一个示例:

apiVersion: v1
kind: Pod
metadata:
  name: my-pv
  namespace: default
spec:
  restartPolicy: Never
  volumes:
  - name: vol
    hostPath:
      path: /tmp/data
  containers:
  - name: my-pv-hostpath
    image: "busybox"
    command: ["/bin/sh", "-c","while true; do sleep 3600;  done"]
    volumeMounts:
    - name: vol
      mountPath: /scrub

请注意,hostPath 仅适用于单个节点,如果你使用的是多节点集群,建议使用本地卷。你可以在 kubernetes.io/docs/concepts/storage/volumes/#local 获取更多关于本地存储的详细信息。

总结

本章涵盖了 CKA 考试中的一个高价值主题——Kubernetes 存储。在过去三年中,CKA 考试对 Kubernetes 存储的关注越来越多,以前仅仅是触及表面,现在则专注于有状态应用程序部署的各种用例。学习这一部分可能现在看起来对 Kubernetes 管理员来说不是最关键的,但随着越来越多的云原生数据库被企业级客户采用,存储知识将迅速起飞。对存储有扎实的了解会为你现有的 Kubernetes 管理技能增值。如果你能自信地完成本章中的练习,它将提高你在实际 CKA 考试中的成功率,因为与其他集群维护任务相关的问题相比,存储相关的问题通常更简单但更具价值。

在下一章 Securing Kubernetes 中,我们将深入探讨一些重要的 Kubernetes 安全概念,这不仅有助于你为 CKA 考试打下坚实的基础,还可能帮助你为将来的 Kubernetes 安全认证专家CKS)考试做好准备——敬请期待!

模拟 CKA 场景化实践测试

你有两台虚拟机,master-0worker-0。请完成以下模拟场景。

场景 1

创建一个新的 PV,名为 packt-data-pv,用于存储 2 GB,并创建两个 PVC,每个请求 1 GB 的本地存储。

场景 2

pack-storage-pod 创建一个新的 Pod,并为该 Pod 分配一个可用的 PV。

你可以在本书的 附录 - 模拟 CKA 场景化实践测试解答 中找到所有的场景解决方案。

常见问题

  • 在与 Kubernetes 配合使用时,我可以在哪里找到支持的 CSI 驱动程序的最新更新?

Kubernetes CSI 特别兴趣小组SIG)有一个基于 GitHub 的文档网站,你可以在其主页找到所有最新的驱动程序教程:kubernetes-csi.github.io/docs。更具体地,你可以在以下链接找到所有可用的支持的 CSI 驱动程序:kubernetes-csi.github.io/docs/drivers.xhtml

  • 推荐参考哪篇官方 Kubernetes 文章来配置临时存储?

我建议收藏关于临时卷的官方文档:kubernetes.io/docs/concepts/storage/ephemeral-volumes/

  • 配置持久存储时,推荐参考的官方 Kubernetes 文章是什么?

我建议收藏这篇文章,配置 Pod 使用持久卷进行存储,在这里你可以找到所有关键步骤和过程:kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/

第六章:保护 Kubernetes 安全

本章将介绍 Kubernetes 认证和授权模式的工作原理,并深入探讨 Kubernetes 的基于角色的访问控制RBAC)。我们还将了解如何管理在 Kubernetes 上部署的应用的安全性。

由于在 2020 年 11 月之前发布的大多数 Kubernetes 安全相关内容已经逐渐转移到认证 Kubernetes 安全专家CKS)考试中,本章将只介绍一些必要的内容,帮助你学习 Kubernetes 安全。我们将特别关注 Kubernetes RBAC,因为它接近 CKA 考试内容的 5%。

尽管如此,深入理解 Kubernetes 安全基础知识将对 CKA 考试有很大帮助,并为你在 Kubernetes 领域的进一步发展做好准备。

在本章中,我们将涵盖以下主要内容:

  • 分层保护 Kubernetes

  • Kubernetes 认证和授权

  • Kubernetes RBAC

  • 管理 Kubernetes 应用的安全

技术要求

为了开始,你需要确保你的本地机器满足以下技术要求:

  • 一个兼容的 Linux 主机——我们推荐基于 Debian 的 Linux 发行版,如 Ubuntu 18.04 或更高版本。

  • 确保你的主机至少有 2GB 的内存、2 个 CPU 核心和约 20GB 的空闲磁盘空间。

分层保护 Kubernetes

由于平台的复杂性,Kubernetes 安全是一个广泛的话题。它包括安全的 Kubernetes 节点、网络以及 Kubernetes 对象,如 Pods。云原生计算基金会CNCF)将 Kubernetes 安全定义为分层的,他们称之为云原生安全的四个 C,将安全话题扩展到 Kubernetes 及其生态系统之外。这四个 C 分别代表集群容器代码,如以下图所示:

图 6.1 – Kubernetes 中的不同层

图 6.1 – Kubernetes 中的不同层

从前面的图示中,我们可以看到以下内容:

  • 层基于 Kubernetes 集群部署的底层基础设施——当它在云中时由云服务提供商管理,或者在私有数据中心时由组织管理。

  • 集群层更多地关注如何保护 Kubernetes 集群组件,确保每个组件都安全且正确地构建。回顾第一章Kubernetes 概述,有助于你理解这些组件是如何协同工作的。

  • 容器层包括容器漏洞扫描、托管操作系统扩展和容器特权用户。

  • 代码层关注的是应用代码。与传统的应用安全方法不同,它现在与 DevSecOps 和漏洞评估工具配合使用。这个层次虽然相关,但不在 Kubernetes 安全的范围之内。

云原生安全,或者更具体地说,Kubernetes 安全,要求组织在每一层次上解决相关问题。在本章中,我们将重点讨论以下话题:

  • Kubernetes API 安全性与准入控制器

  • Kubernetes 的认证与授权,包括 RBAC、基于属性的访问控制ABAC)以及节点授权

  • 使用安全上下文管理 Kubernetes 应用的安全性

上述话题属于集群层或容器层的安全,它们帮助我们安全地运行 Kubernetes 应用。我们将在 第七章《解密 Kubernetes 网络》部分,深入讨论 Kubernetes 网络安全和网络策略。

Kubernetes 的认证与授权

第一章《Kubernetes 概览》中,我们讨论了 Kubernetes 组件之间协同工作的典型工作流程。在这个流程中,当请求通过 Kubernetes API 服务器时,它会触发一个 API 调用。此时,API 请求需要通过 API 服务器进行认证和授权,才会发起对 Kubernetes API 资源的请求。因此,该请求要么被允许,要么被拒绝。认证过程可以如 图 6.2 所示:

图 6.2 – API Kubernetes 认证

图 6.2 – API Kubernetes 认证

你可以参考以下文章,概览 Kubernetes 认证过程是如何工作的:kubernetes.io/docs/reference/access-authn-authz/authentication/

在讨论认证和授权之前,首先让我们了解一下 Kubernetes 中的用户账户和服务账户。

服务账户与用户账户的区别

在 Kubernetes 中,我们区分正常用户账户和由 Kubernetes 管理的服务账户。账户代表用户或服务进程的身份。用户账户和服务账户之间的主要区别如下:

  • 用户账户是针对普通人类用户的。在 Kubernetes 中,RBAC 子系统用于判断用户是否有权在特定范围内执行特定操作。我们将在本章稍后的Kubernetes RBAC部分进一步探讨这一点。

  • 服务账户是为在 Kubernetes 集群中运行的 Pod 内的服务或进程提供的。服务账户是由 Kubernetes API 管理的用户。在 Kubernetes 中,可以使用客户端证书、承载令牌,甚至认证代理来通过 API 服务器进行 API 请求的认证。

接下来我们将重点讲解以下内容:

  • Kubernetes 服务账户及其使用方式

  • 如何使用 kubeconfig 作为 Kubernetes 用户组织集群访问

  • 如何配置 Kubernetes 用户访问多个集群

让我们先来看看 Kubernetes 服务账户。

Kubernetes 服务账户

在前一章中,我们使用kubectl创建了一个新的 Pod,虽然default命名空间中有一个默认的服务账户,Pod 实际上是自动分配给这个账户的。现在,让我们来看一下如何在 Kubernetes 中使用服务账户。

管理服务账户

你可以使用以下命令来获取default命名空间中的当前服务账户:

kubectl get serviceaccounts

或者,你可以简单地使用以下快捷命令:

kubectl get sa

输出将返回default命名空间中的默认服务账户:

NAME      SECRETS   AGE
default   1         5d

服务账户是一个命名空间资源——你可以使用以下命令查看当前集群中所有的服务账户:

k get sa -A

或者,你可以使用完整的命令,如下所示:

k get serviceaccounts --all-namespaces

前面命令的输出将按命名空间列出服务账户信息,类似于图 6.3所示:

图 6.3 – 按命名空间显示的服务账户信息

图 6.3 – 按命名空间显示的服务账户信息

这也意味着我们可以使用kubectl get sa命令按命名空间获取服务账户信息,然后通过指定-n标志和namespace name来获取特定命名空间的服务账户。例如,使用kubectl get sa -nkube-system将只返回kube-system命名空间中的服务账户。

kubectl create sa 命令

你可以使用kubectl create命令来创建一个新的服务账户,以下是一个示例:

kubectl create serviceaccount melon-serviceaccount

以下输出将显示服务账户已成功创建:

serviceaccount/melon-serviceaccount created

我们还可以使用kubectl create命令在不同的命名空间中创建服务账户,方法是指定-n标志。此外,我们还需要确保命名空间在创建服务账户之前已经存在。以下是使用kubectl create命令在名为melon-ns的命名空间中创建一个名为melonsa的服务账户的示例:

kubectl create ns melon-ns
kubectl create sa melonsa -n melon-ns

前面的输出显示你已经成功创建了服务账户。你也可以使用以下命令来检查服务账户是否刚刚创建:

k get –n melon-ns serviceaccounts

以下输出列出了服务账户以及它的创建时间:

NAME                   SECRETS   AGE 
melon-ssa   1         46s

类似地,如果你想查看另一个命名空间中的服务账户,可以使用kubectl get sa <service account name>命令,然后添加-n标志,例如,k get sa melonsa -n melon-ns

将服务账户分配给 Pod

创建服务账户的目的是为 Pod 中运行的进程提供身份验证。要确定 Pod 将使用哪个服务账户,可以在 Pod YAML 规格文件sa-pod.yaml中指定serviceAccountName字段,如下所示:

apiVersion: v1
kind: Pod
metadata:
   name: melon-serviceaccount-pod
spec:
   serviceAccountName: melon-serviceaccount
   containers:
   - name: melonapp-svcaccount-container
     image: busybox
     command: ['sh', '-c','echo stay tuned!&& sleep 3600']

然后,当我们使用kubectl apply -f sa-pod.yaml命令来部署这个 YAML 文件时,我们将能看到一个 Pod 启动。

kubectl delete sa 命令

你可以使用 kubectl delete sa <account name> 命令删除服务账户:

kubectl delete sa melon-serviceaccount

返回的输出显示该服务账户已被删除:

serviceaccount "melon-serviceaccount" deleted

希望你现在对如何使用本节内容操作 Kubernetes 服务账户有了更好的了解。接下来,让我们看看如何使用 kubeconfig 来组织集群访问。

使用 kubeconfig 组织集群访问

作为 Kubernetes 用户,当你使用 kubeadm 部署 Kubernetes 集群时,你会在 $HOME/.kube 目录中找到一个名为 config 的文件:

cloudmelon@cloudmelonplayground:~$ cd $HOME/.kube
cloudmelon@cloudmelonplayground:~/.kube$ ls
cache/  config

在其他情况下,kubeconfig 文件可以作为 KUBECONFIG 环境变量或 --kubeconfig 标志来设置。你可以在官方文档中找到详细的说明:kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/

kubeconfig 文件帮助组织集群、用户和命名空间信息。从 kubectl 工具的角度来看,它读取 kubeconfig 文件来定位集群信息,并与该 Kubernetes 集群的 API 服务器进行通信。

以下是一个 kubeconfig 文件的示例:

apiVersion: v1
clusters:
- cluster:
    certificate-authority: /home/cloudmelon/.minikube/ca.crt
    extensions:
    - extension:
        last-update: Wed, 11 May 2022 23:47:43 UTC
        provider: minikube.sigs.k8s.io
        version: v1.25.2
      name: cluster_info
    server: https://192.168.49.2:8443
  name: minikube
contexts:
- context:
    cluster: minikube
    extensions:
    - extension:
        last-update: Wed, 11 May 2022 23:47:43 UTC
        provider: minikube.sigs.k8s.io
        version: v1.25.2
      name: context_info
    namespace: default
    user: minikube
  name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
  user:
    client-certificate: /home/cloudmelon/.minikube/profiles/minikube/client.crt
    client-key: /home/cloudmelon/.minikube/profiles/minikube/client.key

你可以使用以下命令查看 config

kubectl config view

输出应该如下所示:

图 6.4 – kubectl config view 输出

图 6.4 – kubectl config view 输出

你可以使用 kubectl config 命令来显示 current-context

kubectl config current-context

返回的输出将是当前上下文——在我的例子中是 minikube。你可能会注意到它与前面提到的 config 文件中的 current-context 相同:

minikube

要了解更多关于如何使用 kubeconfig 组织集群访问的内容,请参考官方文章了解更多信息:

kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/

配置访问多个集群

作为 Kubernetes 用户,当涉及到多个集群时,我们还可以使用 kubectl config 命令来配置当前上下文,以便在不同的 Kubernetes 集群之间切换。要查看 kubectl config 提供的所有命令,可以使用以下命令:

kubectl config --help

以下是一个 kubeconfig 文件的示例,它包含了两个不同 Kubernetes 集群的访问信息:

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: 
 < authority data >
    server: https://xx.xx.xx.xx
  name: gke_cluster
- cluster:
    certificate-authority-data: 
 < authority data >
    server: https://xx.xx.xx.xx
  name: arctestaks
contexts:
- context:
    cluster: gke_cluster
    user: gke_cluster
  name: gke_cluster
- context:
    cluster: arctestaks
    user: clusterUser_akscluster
  name: akscluster
current-context: akscluster
kind: Config
preferences: {}
users:
- name: gke_cluster
  user:
    auth-provider:
      config:
        access-token: 
 < token data >
        cmd-args: config config-helper --format=json
        cmd-path: C:\Program Files (x86)\Google\Cloud SDK\google-cloud-sdk\bin\gcloud.cmd
        expiry: '2022-05-12T00:28:06Z'
        expiry-key: '{.credential.token_expiry}'
        token-key: '{.credential.access_token}'
      name: gcp
- name: clusterUser_akscluster
  user:
    client-certificate-data: <data>
    client-key-data: <data>
    token: 
 < token >

我们可以使用 kubectl config current-context 命令来查看当前正在使用的集群,输出将如下所示:

 gke-cluster

上述输出表明我所在的 Kubernetes 集群是 gke-cluster,并且我正在使用以下命令将我的默认上下文切换到另一个 Kubernetes 集群 akscluster

kubectl config use-context akscluster     

我们可以使用 kubectl config current-context 命令来检查我当前工作的 Kubernetes 集群,输出将如下所示:

aks-cluster

切换上下文是你在实际 CKA 考试中可以应用的一项重要技巧,执行任务时务必在目标 Kubernetes 集群中进行,这样你的得分才会准确。这项技能在你作为 Kubernetes 管理员工作时也非常有用,因为你通常需要在多个 Kubernetes 集群上进行工作。

要了解如何配置对多个集群的访问,可以查看官方文章:kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/

Kubernetes 授权

在 Kubernetes 中,必须先对请求进行身份验证,才能授权并授予访问 Kubernetes 集群资源的权限。

Kubernetes 中有四种授权模式:

  • RBAC 授权:Kubernetes 的 RBAC 更多的是根据具有特定权限的角色来规范对 Kubernetes 资源的访问,以执行特定任务,例如通过 API 请求读取、创建或修改。我们将在本节中重点介绍 Kubernetes RBAC。

  • kubelets agent。这是一种特殊的授权模式,CKA 考试中未涉及。你可以查看关于节点授权的官方文档,了解更多信息:kubernetes.io/docs/reference/access-authn-authz/node/

  • ABAC 授权:ABAC 是一种通过策略和属性(如用户属性、资源属性和对象)授予用户的访问控制。这一主题未在当前的 CKA 考试中涉及。如果你想了解更多关于使用 ABAC 模式的信息,可以参考官方文章:kubernetes.io/docs/reference/access-authn-authz/abac/

  • Webhook 授权:通过 WebHooks 的 Webhook 授权是一种由事件触发的 HTTP POST 请求。例如,Webhook 将在某些操作触发时对 URL 作出反应。本主题未在当前的 CKA 考试中涉及。如果你想了解更多关于它的内容,可以参考以下文章:kubernetes.io/docs/reference/access-authn-authz/webhook/

让我们来看看 CKA 考试中涵盖的关键领域,从 Kubernetes RBAC 开始。

Kubernetes RBAC

Kubernetes RBAC 旨在根据具有特定权限的角色来规范对 Kubernetes 资源的访问,以执行特定任务。

一旦指定,RBAC 将检查rbac.authorization.k8s.io API 组成员资格,以查看是否允许通过 Kubernetes API。

让我们来看一下 Kubernetes 中的不同角色和角色绑定。

角色与集群角色及其角色绑定

在 Kubernetes 中,我们有 Roles 和 ClusterRoles。Kubernetes RBAC 中的 Role 或 ClusterRole 表示具有一组权限的角色。简而言之,它们通过这些权限的范围有所不同:

  • Role表示在特定命名空间中的权限

  • ClusterRole表示集群中的权限——它可以是全局的、跨多个命名空间的,也可以是单个命名空间的

使用 Roles 和 ClusterRoles 时,我们有RoleBindingClusterRoleBinding的概念。绑定将角色与用户、组或服务账户等一组主体关联,正如以下图所示:

图 6.5 – Kubernetes RBAC

图 6.5 – Kubernetes RBAC

让我们在名为dev的命名空间中定义一个新的角色,名为dev-user。我们可以使用以下命令来做到这一点:

kubectl create role dev-user --verb=get --verb=list --resource=pods --namespace=dev

上述命令与以下 YAML 定义相同:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: dev
  name: dev-user
rules:
- apiGroups: [""] 
  resources: ["pods"]
  verbs: ["get", "list"]

上述命令的输出如下:

role.rbac.authorization.k8s.io/dev-user created

然后,我们可以使用kubectl get role命令检查我们刚刚创建的角色:

cloudmelon@cloudmelonplayground:~$ k get role -n dev
NAME       CREATED AT
dev-user   2022-05-13T04:14:59Z

然后,我们需要创建 RoleBinding,将此角色绑定到主体,如下所示:

kubectl create rolebinding dev-pods-binding --role=dev-user - -user=melon-dev --namespace=dev

另外,我们还可以使用以下 YAML 文件:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: dev-pods-binding
  namespace: dev
subjects:
- kind: User
  apiGroup: rbac.authorization.k8s.io
  name:melon-dev
roleRef:
kind: Role 
  name: dev-user 
  apiGroup: rbac.authorization.k8s.io

让我们定义一个新的 ClusterRole,名为secret-reader——请注意,ClusterRole 不是基于命名空间的。我们可以使用以下 YAML 定义:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list"]

然后,我们需要创建 RoleBinding,将此角色绑定到主体,如以下 YAML 定义所示:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-secrets
  namespace: development
subjects:
- kind: Group
  name: manager
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

我们可以使用以下命令获取所有命名空间中的所有角色:

cloudmelon@cloudmelonplayground:~$ kubectl get roles -A
NAMESPACE          NAME                                 CREATED AT
dev                dev-user                                        
kube-public        kubeadm:bootstrap-signer-clusterinfo             
kube-public        system:controller:bootstrap-signer               
kube-system        extension-apiserver-authentication-reader        
kube-system        kube-proxy                                       
kube-system        kubeadm:kubelet-config-1.23                      
kube-system        kubeadm:nodes-kubeadm-config                     
kube-system        system::leader-locking-kube-controller-manager   
kube-system        system::leader-locking-kube-scheduler            
kube-system        system:controller:bootstrap-signer               
kube-system        system:controller:cloud-provider                 
kube-system        system:controller:token-cleaner                  
kube-system        system:persistent-volume-provisioner             

我们可以使用以下命令获取所有命名空间中的所有 RoleBindings:

cloudmelon@cloudmelonplayground:~$ kubectl get rolebindings -A
NAMESPACE          NAME                                                
ROLE                                                  AGE
dev                dev-pods-binding                                    
Role/dev-user                                         15s
kube-public        kubeadm:bootstrap-signer-clusterinfo                
Role/kubeadm:bootstrap-signer-clusterinfo             6d
kube-public        system:controller:bootstrap-signer                  
Role/system:controller:bootstrap-signer               6d
kube-system        kube-proxy                                          
Role/kube-proxy                                       6d
kube-system        kubeadm:kubelet-config-1.23                         
Role/kubeadm:kubelet-config-1.23                      6d
kube-system        kubeadm:nodes-kubeadm-config                        
Role/kubeadm:nodes-kubeadm-config                     6d
kube-system        metrics-server-auth-reader                          
Role/extension-apiserver-authentication-reader        3h
kube-system        system::extension-apiserver-authentication-reader   Role/extension-apiserver-authentication-reader        
6d
kube-system        system::leader-locking-kube-controller-manager      Role/system::leader-locking-kube-controller-manager   6d
kube-system        system::leader-locking-kube-scheduler               
Role/system::leader-locking-kube-scheduler            6d
kube-system        system:controller:bootstrap-signer                  
Role/system:controller:bootstrap-signer               6d
kube-system        system:controller:cloud-provider                    
Role/system:controller:cloud-provider                 6d
kube-system        system:controller:token-cleaner                     
Role/system:controller:token-cleaner                  6d
kube-system        system:persistent-volume-provisioner                
Role/system:persistent-volume-provisioner             6d

了解 Roles 和 RoleBindings 在 Kubernetes 中的工作方式后,让我们看看如何实现您自己的 Kubernetes RBAC 角色和 RoleBindings。

实现 Kubernetes RBAC

要启用 RBAC,请将apiserver --authorization-mode设置为 RBAC,默认值为AlwaysAllow。其他可能的值包括nodeABACAlways denywebhook。在以下命令中,我们展示了将其设置为使用 Kubernetes RBAC 的示例:

kube-apiserver –authorization-mode=RBAC

要了解更多关于如何设置授权模式的信息,请访问以下链接:kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/

我们首先创建一个新的部署,使用当前上下文minikube

kubectl create deployment mybusybox –-image=busybox 

然后,切换到dev-user的上下文:

kubectl config use-context dev-user

由于我们的dev-user仅具有listget权限,让我们尝试使用此配置文件删除部署:

cloudmelon@cloudmelonplayground:~$ kubectl delete deployment mybusybox
Error from server (Forbidden): deployments.apps is forbidden: User "dev-user" cannot delete resource "deployments" in API group "apps" in the namespace "t

现在我们已经学会了如何管理自己的 Kubernetes RBAC 角色,让我们看看如何管理 Kubernetes 应用程序的安全性。

管理 Kubernetes 应用程序的安全性

securityContext 字段定义了在 Pod YAML 规范中 Pod 的特权和访问控制设置。如果 Pod 或容器需要以非常规方式与底层操作系统的安全机制交互,我们需要配置安全上下文。在这一节中,我们将介绍如何为 Pod 或容器配置安全上下文。

作为准备工作的一部分,你可以创建一个新用户和一个新组,如下所示:

sudo useradd -u 2000 container-user-0
sudo groupadd -g 3000 container-group-0

我们现在将登录到工作节点并创建一个名为 message.txt 的新 .txt 文件:

sudo mkdir -p /etc/message
echo "hello Packt" | sudo tee -a /etc/message/message.txt

从这里,你将看到我们从终端输入的消息:

 hello Packt

现在,我们需要调整权限以限制测试目的的权限,具体如下所示:

sudo chown 2000:3000 /etc/message/message.txt
sudo chmod 640 /etc/message/message.txt

最后,我们可以在当前的 Kubernetes 集群中部署一个新 Pod 来进行测试。securityContext 字段作为 Pod YAML 规范的一部分,定义在 pod-permission.yaml 中。通过名为 securityContext 的部分,我们可以指定安全权限信息,如以下 YAML 文件所示:

  apiVersion: v1
  kind: Pod
  metadata:
    name: melon-securitycontext-pod
  spec:
    securityContext:
      runAsUser: 2000
      fsGroup: 3000
    containers:
    - name: melonapp-secret-container
      image: busybox
      command: ['sh', '-c','cat /message/message.txt && sleep 3600']
      volumeMounts:
      - name: message-volume
        mountPath: /message
    volumes:
    - name: message-volume
      hostPath:
        path: /etc/message

在前面的 YAML 定义文件中,runAsUser 字段表示对于该 Pod 中的任何容器,所有进程都将以 2000 的用户 ID 运行。fsGroup 字段为 2000,这意味着容器的所有进程也是 ID 2000 补充组的一部分。在该卷中创建的所有文件以及卷/消息的所有者将是 ID 2000 组。

让我们继续按照以下步骤部署这个 YAML 文件:

kubectl apply -f pod-permission.yaml

然后,我们将看到 Pod 正在启动,但会很快遇到以下错误:

NAME                        READY   STATUS          RESTARTS    
AGE
melon-securitycontext-pod 0/1     CrashLoopBackOff   1 5m

从前面的示例中,我们可以看到 Pod 因缺少权限而处于 BackOff 状态。现在,让我们拉取一个类似的示例,看看我们能否解决这个问题。让我们配置一个与以下类似的 YAML 文件:

securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000

让我们使用以下 YAML 示例来部署它:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-message
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  volumes:
  - name: sec-ctx-msg
    emptyDir: {}
  containers:
  - name: sec-ctx-msg
    image: busybox:1.28
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-msg
      mountPath: /message
    securityContext:
      allowPrivilegeEscalation: false

我们可以看到这个示例现在已经在我的本地 Kubernetes 集群中运行:

cloudmelon@cloudmelonplayground:/$ kubectl get pod security-context-demo
NAME                    READY   STATUS    RESTARTS   AGE
security-context-message  1/1     Running   0          3m4s

让我们进入这个正在运行的 Pod:

kubectl exec -it security-context-message -- sh

然后,我们将进入交互式 shell,输入 id,并得到以下输出:

/ $ id
uid=1000 gid=3000 groups=2000

从输出中,我们可以看到 uid1000,与 runAsUser 字段相同;gid3000,与 runAsGroup 字段相同;fsGroup2000

要了解更多关于安全上下文的信息,请查阅官方文档:kubernetes.io/docs/tasks/configure-pod-container/security-context/

总结

本章概述了 Kubernetes 安全性,重点讨论了容器安全、RBAC 和安全上下文三个关键主题。你可以利用本章内容为你的 CKS 考试打下基础。接下来的章节《揭开 Kubernetes 网络的神秘面纱》,将帮助你全面了解与 Kubernetes 网络安全相关的概念和实践示例,帮助你作为 Kubernetes 管理员在日常工作中应用这些知识,并且这将覆盖 CKA 考试内容的 20%。让我们拭目以待!

模拟 CKA 场景练习测试

你有两台虚拟机,master-0worker-0—请完成以下模拟场景。

场景 1

在名为packt-ns的新命名空间中创建一个名为packt-sa的新服务账户。

场景 2

创建一个名为packtrole的 Role,并将其与 RoleBinding packt-clusterbinding绑定。将packt-sa服务账户映射为listget权限。

场景 3

packt-ns命名空间中,创建一个名为packt-pod的 Pod,使用busybox:1.28镜像,并暴露端口80。然后,将packt-sa服务账户分配给该 Pod。

你可以在本书的附录 - 模拟 CKA 场景练习题解答中找到所有场景的解决方案。

常见问题解答

  • 在使用 Kubernetes 时,我可以在哪里找到关于 Kubernetes 安全性的最新更新?

Kubernetes 安全特别兴趣小组SIG)有一个 GitHub 仓库,你可以在这里找到:github.com/kubernetes/community/tree/master/sig-security

  • 推荐的 Kubernetes 官方文章在哪里可以找到,用于配置临时存储?

我推荐将有关 Kubernetes RBAC 的官方文档添加书签,你可以在这里找到:kubernetes.io/docs/reference/access-authn-authz/rbac/

第七章:解密 Kubernetes 网络

本章将使用 Kubernetes 网络模型描述一些核心概念,以及如何在集群节点上配置 Kubernetes 网络和网络策略。我们还将学习如何配置 Ingress 控制器和 Ingress 资源,如何配置和利用 CoreDNS,以及如何选择合适的容器网络接口插件。本章内容占 CKA 考试的约 20%。

本章将覆盖以下主题:

  • 理解 Kubernetes 网络模型

  • 在集群节点上配置 Kubernetes 网络

  • 配置网络策略

  • 配置 Ingress 控制器和 Ingress 资源

  • 配置和利用 CoreDNS

  • 选择合适的容器网络接口插件

技术要求

首先,我们需要确保您的本地机器满足以下技术要求:

  • 一个兼容的 Linux 主机。我们推荐使用基于 Debian 的 Linux 发行版,如 Ubuntu 18.04 或更高版本。

  • 确保您的主机至少有 2 GB 的内存,2 个 CPU 核心,并且有约 20 GB 的可用磁盘空间。

理解 Kubernetes 网络模型

Kubernetes 旨在促进所需状态管理,以托管容器化工作负载——这些工作负载利用了可共享的计算资源。Kubernetes 网络解决了如何允许不同的 Kubernetes 组件相互通信以及 Kubernetes 上的应用程序如何与其他应用程序通信的问题,还包括 Kubernetes 集群外部的服务。

因此,官方文档将这些网络挑战总结为容器间、Pod 间、Pod 与服务、外部与服务、以及节点间通信。现在,我们将在本节中逐一讲解这些内容。

容器间通信

容器间通信主要指的是 Pod 内容器之间的通信——多容器 Pod 是一个很好的例子。多容器 Pod 是一个包含多个容器的 Pod,并视为一个单一单元。在 Pod 内部,每个容器共享网络,包括 IP 地址和网络端口,使得这些容器可以通过 localhost 或标准的 进程间通信 (IPC) 方式,如 SystemV 信号量或 POSIX 共享内存,彼此通信。即使这些端口没有暴露到 Pod 外部,Pod 内的其他容器仍然可以访问所有监听端口。

下图展示了这些容器如何在同一个 Pod 内部共享本地网络:

图 7.1 – 多个容器共享 Pod 网络

图 7.1 – 多个容器共享 Pod 网络

以下是一个名为multi-container-pod.yaml的示例,展示了如何在一个 Pod 中创建多个容器。在这个 Pod 中,包含了nginxbusybox两个容器,其中busybox是一个 sidecar 容器,通过localhost:上的端口80调用nginx

apiVersion: v1
kind: Pod
metadata:
  name: multi-container-pod
  labels:
      app: multi-container
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80
  - name: busybox-sidecar
    image: busybox:latest
    command: ['sh', '-c', 'while true; do sleep 3600; done;']

让我们通过使用kubectl apply -f multi-container-pod.yaml命令部署此yaml文件,以下内容显示 Pod 已创建:

pod/multi-container-pod created

我们可以使用以下命令检查是否能从 sidecar 容器busyboxnginx容器通信:

kubectl exec multi-container-pod -c busybox-sidecar -- wget http://localhost:80

以下输出证明两个容器可以互相通信:

图 7.2 – 从 sidecar 容器连接到容器

图 7.2 – 从busybox sidecar 容器连接到nginx容器

重要提示

创建单容器 Pod 的快速方式是使用以下命令:

kubectl run nginx --image=nginx:latest --port=80

然后,您可以使用kubectl get pods –o yaml命令导出 YAML 内容,并编辑yaml文件以添加另一个容器。

为了再次确认我们是否从busybox sidecar 容器获取了nginx主页,我们将使用以下命令:

kubectl exec multi-container-pod -c busybox-sidecar -- cat index.xhtml

输出应与图 7.3中显示的类似:

图 7.3 – 检查在容器中下载的 HTML 页面

图 7.3 – 检查在busybox容器中下载的 HTML 页面

要了解更多关于多容器 Pod 的信息,查看这些容器如何共享存储和网络,请参考第四章应用调度与生命周期管理

Pod 与 Pod 之间的通信

在 Kubernetes 中,每个 Pod 都被分配了基于该工作节点podCIDR范围的唯一 IP 地址。尽管这种 IP 分配不是永久性的,因为 Pod 最终会失败或重启,新 Pod 将分配一个新的 IP 地址。默认情况下,Pods 可以通过 Pod 网络与所有节点上的所有 Pods 通信,而无需设置网络地址转换NAT)。这也是我们设置主机网络的地方。所有 Pods 可以相互通信,而无需 NAT。

让我们通过使用以下命令部署一个nginx Pod:

kubectl run nginx --image=nginx –-port=8080

以下输出显示 Pod 已创建:

pod/nginx created

要验证 Pod 是否已分配 IP 地址,您可以使用kubectl get pod nginx -o wide命令检查nginx Pod 的 IP 地址。输出类似于以下内容:

  NAME    READY   STATUS             RESTARTS   AGE   IP           
NODE       NOMINATED NODE   READINESS GATES
nginx   1/1     running   0          34s   172.17.0.4   
minikube   <none>           <none>

您可以使用以下命令检查默认命名空间中所有可用 Pod 及其分配的 IP 地址:

k get pods -o wide

注意以下输出中的IP列,它指示了multi-container-pod Pod 的 IP 地址为172.17.0.3nginx Pod 的 IP 地址为172.17.0.4。这些分配给 Pod 的 IP 地址位于相同的podCIDR范围内:

图 7.4 – 检查 Pod 的 IP 地址

图 7.4 – 检查 Pod 的 IP 地址

前面的截图还显示了两个 pod 在同一节点 minikube 上,根据 NODE 列可以看到这一点。我们可以使用以下命令查看分配给 pod 的 podCIDR

kubectl get node minikube -o json | jq .spec.podCIDR

输出结果如下所示,显示了 podCIDR

10.244.0.0/24

从前面的命令输出中,我们可以看到它的 CIDR 与 pod 的 CIDR 不同。这是因为我们在 minikube 集群上进行了测试。当我们使用 minikube start 命令启动一个默认的 minikube 安装时,未指定 CNI 网络插件的其他参数,它会将默认值设置为 auto。它选择了 kindnet 插件来使用,该插件创建了一个桥接,并将主机和容器添加到桥接中。稍后在本章中,我们将学习如何设置 CNI 插件和网络策略。想了解更多关于 kindnet 的信息,可以访问以下链接:github.com/aojea/kindnet

Kubernetes 组件,如系统守护进程和 kubelet,可以与同一节点上的所有 pod 进行通信。理解 pod 之间的连接性是 CKA 考试的要求。如果你想了解更多关于集群网络的信息,可以查看官方文档:kubernetes.io/docs/concepts/cluster-administration/networking/#the-kubernetes-network-model

Pod 到服务和外部到服务的通信

有效的 pod 和服务之间的通信意味着让服务暴露在一组 pod 上运行的应用程序。该服务接受来自集群内部和外部的流量。该组 pod 可以在它们之间进行负载均衡 —— 每个 pod 都分配了自己的 IP 地址和一个 DNS。

与 pod 到服务通信类似,外部到服务的通信挑战也由服务来解决。NodePortLoadBalancer 等服务类型可以接收来自 Kubernetes 集群外部的流量。

现在让我们来看一下不同的 服务类型端点

Kubernetes 服务类型概述

在 Kubernetes 网络空间中,有几种发布服务类型非常重要。这与无头服务不同。如果你想了解有关无头服务的内容,可以访问这个链接(这不在 CKA 考试的范围内):kubernetes.io/docs/concepts/services-networking/service/#headless-services

以下是 CKA 考试中最常见的几种发布服务类型:

服务类型 描述 示例
ClusterIP Kubernetes 的默认服务类型。对于内部通信,暴露该服务使其在集群内可访问。 使用 kubectl get pod mypod -o wide 查看 pod 地址 - 内部 IP 是 172.17.0.4
NodePort 用于内部和外部通信。NodePort 在每个工作节点上暴露服务的静态端口——与此同时,会为其创建 ClusterIP,并用于内部通信,要求访问节点的 IP 地址和开放端口——例如,<nodeIP>:<port> 用于外部通信。 连接到具有公共 IP 地址 192.0.2.0 的工作节点虚拟机,端口为 80
LoadBalancer 适用于云提供商,因为它由各自的负载均衡器提供支持。在 LoadBalancer 下,创建了 ClusterIPNodePort,分别用于内部和外部通信。 查看来自云提供商的 Kubernetes 分发版的服务,如 kubectl get service mysvc -n mynamespace – 内部 IP 地址为 172.17.0.4
ExternalName 使用 CNAME 记录将服务映射到其内容,并允许外部流量通过该记录访问。 例如,my.packt.example.com

要了解有关发布服务和无头服务之间的差异,请查看此链接:https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types。现在,让我们来看看这一节中的每个服务。

ClusterIP

ClusterIP 是 Kubernetes 的默认服务类型,用于内部通信。在 pod 或 ClusterIP 的情况下,pod 只在 Kubernetes 集群内可达。然而,仍然可以通过 kube-proxy 允许外部流量访问 ClusterIP,它会创建 iptables 条目。这在一些使用场景中非常有用,比如展示 Kubernetes 仪表板。图 7.5 描述了网络流量如何负载均衡(轮询)并路由到 pod。然后,它会通过 ClusterIP 或其他服务到达 pod:

图 7.5 – ClusterIP 和 kube-proxy

图 7.5 – ClusterIP 和 kube-proxy

通过前面的图示,我们首次了解了服务如何与 pod 一起工作。接下来,让我们部署一个应用程序并深入研究。要创建一个名为 nginx 的部署,并将 replicas 数量设置为 2,请使用以下命令:

kubectl create deployment nginx --image=nginx --replicas=2

我们可以通过以下命令追踪部署过程:

kubectl get deploy nginx -o wide

一旦我们完成,我们应该能够看到以下输出:

图 7.6 – 可用的 nginx 副本数量

图 7.6 – 可用的 nginx 副本数量

从前面的输出中,我们可以看到有两个 nginx pod 正在运行,以便更好地理解这些 pod。我们可以看到这些 nginx pod 在默认命名空间中的展示情况。

请注意,为了简便起见,我们在default命名空间中进行测试。你可以添加-n标志,以便在其他命名空间中处理部署和 pods。请参阅第四章应用调度与生命周期管理,了解 Kubernetes 中应用部署的工作原理。接下来,尝试执行以下命令:

kubectl get pods

输出将返回default命名空间中所有可用的 pods:

图 7.7 – 默认命名空间中的可用 nginx pods

图 7.7 – 默认命名空间中的可用 nginx pods

现在,我们将这些 pods 暴露到 Kubernetes 集群中。我们使用以下命令创建一个名为melon-service的服务:

 kubectl expose deployment nginx --type=ClusterIP --port 8080 --name=melon-service --target-port 80

从前面的命令中,我们可以看到我们已经创建了一个ClusterIP类型的服务。我们可以指定以下标志:

  • type是服务的类型——在我们的例子中,它是ClusterIP。我们将在本章接下来的部分中了解NodePortLoadBalancer

  • port是服务提供的端口。

  • target-port是容器上将流量重定向到的端口。

重要说明

理解这些命令标志将帮助你更顺畅地使用它们;我建议你记住这个命令,以便在实际的 CKA 考试中能够快速回忆起来。你也可以参考以下链接(kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#expose)来了解其他标志是否能帮助你。

前一个命令的输出应该类似于以下内容:

service/melon-service exposed

基于这个输出,前面的命令成功执行。现在,让我们进入默认命名空间,使用kubectl get svc命令查看所有可用的服务——这将给出以下输出:

图 7.8 – 默认命名空间中的可用 nginx pods

图 7.8 – 默认命名空间中的可用 nginx pods

前面的输出显示,已创建了ClusterIP类型的服务,IP 地址为10.102.194.57,该服务在8080端口上提供服务。

我们在本节中所做的通过使用kubectl expose命令创建一个新的ClusterIP服务,也可以通过以下的 YAML 清单文件完成:

 apiVersion: v1
 kind: Service
 metadata:
   name: melon-service
 spec:
   type: ClusterIP
selector: 
     app: nginx
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 80

从前面的 YAML 定义中,我们可以看到有一个叫做selector的部分。这个部分有一个键值对app:nginx,它是一个标签选择器。通常,我们使用选择器将服务与 pods 关联起来。如果我们没有使用kubectl命令,这里是nginx部署的 YAML 定义:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

从前面的 YAML 定义中,我们可以看到有一个部分用于指定选择器,我们使用相同的键值对 app: nginx 来映射 ClusterIP 规范,以便它按预期工作。参考第四章**,应用调度与生命周期管理,了解更多关于标签选择器的信息。

重要提示

如我们之前提到的,CKA 考试强调时间管理,因此使用命令来实现目标会更加高效。

一个相应的 endpoints 对象可以在不使用选择器的情况下实现我们所讨论的内容。你可以使用以下命令来获取 melon-service 的 endpoints:

k get ep melon-service

以下是前面命令的输出:

图 7.9 – 显示默认命名空间中 nginx pods 的 endpoints

图 7.9 – 显示默认命名空间中 nginx pods 的 endpoints

如你所见,在这里我们定义的 YAML 文件中并没有什么特别的内容。我们可以通过使用以下命令导出其 YAML 定义来对比服务定义:

kubectl get svc  melon-service -o yaml

我们将能够看到导出的输出,如下所示:

图 7.10 – 默认命名空间中 nginx 服务的定义

图 7.10 – 默认命名空间中 nginx 服务的定义

将导出的定义与我们在本节中使用 kubectl 和 YAML 定义所讲解的内容进行比较,将有助于你更好地理解 Kubernetes 中的服务。现在,让我们来看看 Kubernetes 中另一个重要的服务——NodePort

NodePort

NodePort 在 Kubernetes 节点上打开端口,这些节点通常是虚拟机。NodePort 通过节点的 IP 暴露访问,并通过打开的端口使应用程序可以从 Kubernetes 集群外部访问。网络流量从端口转发到服务。kube-proxy 在每个节点上分配一个 3000032767 范围内的端口——其工作方式如图所示:

图 7.11 – Kubernetes 中的 NodePort

图 7.11 – Kubernetes 中的 NodePort

通过前面的图示,我们更深入地了解了 NodePort 如何与 pods 一起工作。接下来,我们使用以下命令创建一个名为 webfront-appreplicas 数量为 2 的部署:

kubectl create deployment webfront-app --image=nginx --replicas=2

如果创建成功,你将看到以下输出:

deployment.apps/webfront-app created

然后,我们可以使用以下命令通过 NodePort 暴露一个 Web 前端:

kubectl expose deployment webfront-app --port=8080 --target-port=80 --type=NodePort

以下输出显示我们已成功暴露 webfront-app

service/webfront-app exposed

请注意,如果你没有提供目标端口,则假定它与容器端口相同。还要注意,如果没有提供节点端口,系统会自动分配一个在 3000032767 范围内的空闲端口。

现在,让我们检查一下我们刚刚创建的所有服务。由于我们在之前的命令中没有指定名称,服务名称假定与应用程序名称相同:

kubectl get svc webfront-app -o wide

输出应如下所示:

图 7.12 – 默认命名空间中的 webfront-app NodePort

图 7.12 – 默认命名空间中的 webfront-app NodePort

从前面的输出中,我们可以看到端口暴露在31400,这个端口位于节点的3000032767范围内,目标端口是80,它在容器级别已开启。因此,使用以下命令获取节点 IP:

kubectl get node -o wide

输出的关键部分如下:

图 7.13 – webfront-app NodePort 的内部 IP

图 7.13 – webfront-app NodePort 的内部 IP

从前面的输出中,我们获取的是节点的内部 IP,由于我们在本地进行测试,所以可以将内部 IP 和端口结合使用来连接到webfront-app

192.168.65.4:31400

使用以下命令,我们来部署一个名为sandbox-nginx的新nginx pod,测试连接性:

kubectl run -it sandbox --image=nginx --rm --restart=Never -- curl -Is http://192.168.65.4:31400

输出类似于以下内容:

图 7.14 – webfront-app NodePort 的内部 IP

图 7.14 – webfront-app NodePort 的内部 IP

在实际的 CKA 考试中,你将会在几个不同的虚拟机上进行操作。如果你需要连接到该节点上部署的应用程序,可以使用以下命令获取所有节点的外部 IP:

 kubectl get nodes -o jsonpath='{.items[*].status.addresses[?( @.type=="ExternalIP")].address}'

同样地,如果你想获取所有节点的内部 IP,可以使用以下命令:

 kubectl get nodes -o jsonpath='{.items[*].status.addresses[?( @.type==" InternalIP ")].address}'

在实际考试中,你还可以使用内部 IP 连接到该节点,然后使用以下命令,它会给你相同的结果:

curl -Is http://192.168.65.4:31400

如果你有一个可以从本地环境 ping 通的节点虚拟机的公共 IP 地址,你可以使用以下命令:

curl -Is http://<node external IP>:<node port>

提示与技巧

如果你需要帮助,可以参考 Kubernetes 备忘单上的一些重要的 JSONPath 命令:kubernetes.io/docs/reference/kubectl/cheatsheet/#viewing-finding-resources

我们在本节中通过使用 kubectl expose 命令创建新 NodePort 服务的操作,也可以使用以下 YAML 清单文件来完成:

apiVersion: v1
kind: Service
metadata:
  name: webfront-app
  labels:
    app: webfront-app
spec:
  ports:
  - port: 8080
    targetPort: 80
  selector:
    app: webfrontapp
  type: NodePort

公有云服务提供商通常支持外部负载均衡器,在使用 Kubernetes 时,我们可以将其定义为 LoadBalancer。现在,让我们在以下部分了解一下它。

LoadBalancer

LoadBalancer 是一种将服务从集群外部连接的标准方式。在这种情况下,网络负载均衡器将所有外部流量重定向到一个服务,如下图所示,每个服务都有自己的 IP 地址。它允许服务在应用程序之间进行网络流量负载均衡:

图 7.15 – Kubernetes 中的 LoadBalancer

图 7.15 – Kubernetes 中的 LoadBalancer

LoadBalancer不是 CKA 考试中的热门话题,因为它仅在云环境或其他支持外部负载均衡器的环境中工作。将LoadBalancer服务部署以获取公共 IP 通常用于像 AKS 这样的托管 Kubernetes 发行版 – 以下是相关的 YAML 示例定义:

apiVersion: v1
kind: Service
metadata:
  name: packt-svc
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: my-packt-app

我们也可以使用kubectl expose命令来执行此操作:

kubectl expose deployment nginx --port=80 --target-port=8080 \
        --name=packt-svc --type=LoadBalancer

前面命令的输出如下所示:

图 7.16 – Kubernetes 中的 LoadBalancer 输出

图 7.16 – Kubernetes 中的 LoadBalancer 输出

由于我在 WSL2 上的 Docker Desktop 中测试 LoadBalancer 时,它不受支持 – 前面的输出显示EXTERNAL-IPlocalhost。然而,当我在 AKS 上工作时,它显示了真实的公共 IP 地址。请参考此链接查看对我有用的内容:docs.microsoft.com/en-us/azure/aks/load-balancer-standard

ExternalName

ExternalName将服务映射到其值的 CNAME 记录的内容。它允许外部流量访问该服务。以下是ExternalName的示例 YAML 定义:

apiVersion: v1
kind: Service
metadata:
  name: my-packt-svc
  namespace: prod
spec:
  type: ExternalName
  externalName: my.melonapp.packt.com

请注意,前面的ExternalName类型定义为my.melonapp.packt.com – 我们可以使用nslookup命令来检查my-packt-svc.default,svc.cluster.local。这将返回my.melonapp.packt.com的 CNAME 记录。我们将在本章稍后深入探讨 Kubernetes 中的 DNS 是如何工作的。

检查服务和端点

在本节中,我们已介绍了 Kubernetes 中的四种常见服务类型。如果我们需要快速检查所有命名空间中的所有服务,可以使用以下命令:

kubectl get services --all-namespaces

或者,我们可以使用以下命令:

kubectl get svcs -A

以下显示了前一个命令的输出:

图 7.17 – 获取跨不同命名空间的所有服务

图 7.17 – 获取跨不同命名空间的所有服务

前面的截图列出了跨命名空间的服务,以及它们的ClusterIP和端口信息。如果你想查看特定的服务,可以使用以下命令:

kubectl get svc <service-name> -n <namespace>

前面命令的示例是kubectl get svc kube-dns -n kube-system,它会返回服务信息。你还可以通过使用kubectl describe svc命令进一步查看详细信息:

kubectl describe svc kube-dns -n kube-system

前面命令的输出如下所示:

图 7.18 – 检查服务详细信息

图 7.18 – 检查服务详细信息

对于端点,我们可以使用以下命令来检查服务的端点:

kubectl get endpoints melon-service

它也可以如下所示:

NAME            ENDPOINTS                   AGE
melon-service   10.1.0.32:80,10.1.0.33:80   5h7m

如果我们想查看不同命名空间中的所有端点,可以使用以下命令:

kubectl get ep --all-namespaces

前面命令的输出将列出跨不同命名空间的所有端点:

图 7.19 – 获取跨不同命名空间的所有端点

图 7.19 – 获取跨不同命名空间的所有端点

同样的原理也适用于按命名空间列出所有端点。当你想查看特定服务时,可以使用以下命令:

kubectl get ep <service-name> -n <namespace>

我们已经讨论了如何在 Kubernetes 中处理服务和端点,涉及了 pod 到服务的通信。现在,让我们进入下一节,讨论节点间的通信。

节点间通信

在一个集群内,每个节点都会通过kubelet代理注册到主节点,并且每个节点会被分配一个节点 IP 地址,以便它们之间能够进行通信。

要验证这一点,可以使用kubectl get node -o wide命令查看每个节点的内部 IP。输出结果类似于以下内容,在其中你会注意到工作节点的internal-IP

图 7.20 – 查看节点 IP 及更多信息

图 7.20 – 查看节点 IP 及更多信息

从上面的截图中,我们可以看到当前节点的内部 IP 地址是192.168.49.2。如果我们有多个节点,我们可以在同一网络内从一个节点 ping 其他节点。我们需要确保主节点与工作节点之间的连通性,以便工作负载能够被调度到工作节点。因此,理解如何为 Kubernetes 节点配置托管网络非常重要。接下来,我们来看一下容器网络接口插件。

选择合适的容器网络接口插件

第二章安装与配置 Kubernetes 集群》中,我们讨论了如何使用 Calico 插件作为 Kubernetes 集群的覆盖网络。我们可以启用容器网络接口CNI)以实现 pod 到 pod 的通信。CNI 插件符合 CNI 规范。一旦 CNI 在 Kubernetes 集群中配置完成,它将为每个 pod 分配 IP 地址。

Kubernetes 中的 CNI 网络

当前市场上有多种网络插件可与 Kubernetes 配合使用,包括 Calico、Flannel、Weave Net 等流行的开源框架。更多选项,请查看官方文档:kubernetes.io/docs/concepts/cluster-administration/addons/

以 Flannel 为例,Flannel 专注于为 Kubernetes 配置一个三级网络架构,主要用于在不同容器之间路由数据包。Flannel 在每个主机上运行一个名为flanneld的单一二进制代理,负责为每个主机分配一个预配置的子网地址空间,如下所示:

图 7.21 – Kubernetes 中的 CNI 网络

图 7.21 – Kubernetes 中的 CNI 网络

上面的图演示了 Flannel CNI 网络如何工作。社区中有许多选项——让我们来看看选择 CNI 插件的决策指标。

决策指标

为了选择适合您需求的合适 CNI 插件,您可以参考下表,了解每个 CNI 提供商的不同功能:

提供商网络 封装与路由 网络策略支持 数据存储 加密 Ingress / Egress
Flannel 第三层 VxLAN ETCD
Calico 第三层 BGP, eBPF ETCD
Weavenet 第二层 VxLAN
Canal 第二层 VxLAN ETCD

对于快速测试,Flannel 的设置非常简单。Calico 和 Weave Net 是更适合企业级客户的选择,因为它们具有广泛的功能。在实际应用中,可能会在一个环境中使用多个 CNI 解决方案来满足一些复杂的网络需求。不过,这超出了 CKA 认证考试的范畴。

现在让我们看看下一部分的 Ingress 控制器。

配置 Ingress 控制器和 Ingress 资源

Kubernetes 网络管理面临的挑战之一是管理内部流量,也就是所谓的东西向流量,以及外部流量,也就是北南向流量。

有几种不同的方式将外部流量引入 Kubernetes 集群。当涉及到第七层网络时,Ingress 会在第七层路由上暴露 HTTP 和 HTTPS,将集群外部的流量路由到集群内的服务。

Ingress 和 Ingress 控制器的工作原理

Ingress 充当路由器,通过 Ingress 管理的负载均衡器将流量路由到服务,然后该服务将流量分发到不同的 Pod。从这个角度来看,同一个 IP 地址可以用于暴露多个服务。然而,随着应用程序的复杂性增加,特别是当我们需要将流量重定向到其子域名甚至是通配符域时,Ingress 就能解决这些挑战。

Ingress 与 Ingress 控制器一起评估定义的流量规则,然后确定流量如何路由。该过程如图 7.22所示:

图 7.22 – Kubernetes 中的 Ingress 资源

图 7.22 – Kubernetes 中的 Ingress 资源

除了我们在图 7.22中看到的内容,Ingress 还提供了一些关键功能,如负载均衡、SSL 终止和基于名称的虚拟主机。

我们需要在 Kubernetes 集群中部署一个 Ingress 控制器,然后创建 Ingress 资源。在本节中,我们以 ingress-nginx 为例。如今市场上有许多 Ingress 控制器可供选择。请查看这里的官方文档以获取更多细节:kubernetes.io/docs/concepts/services-networking/ingress-controllers/

使用多个 Ingress 控制器

注意,也可以通过在 Kubernetes 集群中使用 Ingress 类来部署多个 Ingress 控制器。请参考这篇文章了解更多细节:kubernetes.io/docs/concepts/services-networking/ingress-controllers/#using-multiple-ingress-controllers

与 Ingress 资源配合使用

如前所述,nginx Ingress 控制器是当今市场上最受欢迎的控制器之一,因此我们在本节中将其作为主要示例。我们需要在 Kubernetes 集群中部署一个 Ingress 控制器,并创建 Ingress 资源。

在这里,我们定义了一个最小的 nginx 资源,使用以下 YAML 定义:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: packt-nginx
  rules:
  - http:
      paths:
      - path: /packs
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

从前面的 YAML 定义中,我们知道 apiVersionkindmetadataspec 字段是必填的。接下来,我们还需要一个 Ingress 对象,其中包含有效的 DNS 子域名。

默认的 IngressClass 如下所示:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
  name: nginx-example
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx

要了解如何使用 Ingress,查看官方文档:kubernetes.io/docs/concepts/services-networking/ingress/

Ingress 注解和 rewrite-target

你可以为特定的 Ingress 对象添加 Kubernetes 注解,以便自定义它们的行为。这些注解的键和值只能是字符串。以下是如何以 nginx 为例,向 Ingress 资源添加注解的示例:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
  name: packt-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: book.packt.com
    http:
      paths:
      - path: /packt-book
        pathType: Prefix
        backend:
          service:
            name: packt-svc
            port:
              number: 80

nginx 有许多可用的注解——你可以通过访问以下页面来查看它们:kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/

不同的 Ingress 控制器提供不同的功能,通常使用注解和 rewrite-target 来重写默认行为。你可以在这里查看如何为 nginx Ingress 控制器重写行为:kubernetes.github.io/ingress-nginx/examples/rewrite/#rewrite-target

我们在本节中提到了域名和子域名。现在,是时候谈谈 Kubernetes 中 DNS 域名主机名的工作原理了。让我们在下一节中详细讨论:

配置和利用 CoreDNS

如本章前面提到的,节点、Pod 和服务在 Kubernetes 集群中会分配自己的 IP 地址。Kubernetes 运行一个 域名系统DNS)服务器实现,通过 DNS 记录将服务名称映射到其 IP 地址。因此,你可以使用一致的 DNS 名称访问服务,而无需使用其 IP 地址。在微服务架构中,这非常有用。当前 Kubernetes 集群中运行的所有微服务可以通过引用服务名称相互通信。

DNS 服务器主要支持以下三种类型的 DNS 记录,这些也是最常见的记录类型:

  • AAAAA 记录用于正向查找,将 DNS 名称映射到 IP 地址。A 记录将 DNS 名称映射到 IPv4 地址,而 AAAA 记录则允许将 DNS 名称映射到 IPv6 地址。

  • SRV 记录用于端口查找,以便在服务和主机名之间建立连接。

  • 172.0\. 0.10 将存储在 10.0\. 0.172.in-addr.arpa DNS 区域下。

了解这些基本的 DNS 概念将帮助我们更好地理解 Kubernetes 中的 DNS。

在 Kubernetes 1.21 中,kubeadm 移除了对 kube-dns 的支持,以进行 DNS 复制。CoreDNS 现在成为默认的 DNS 服务。CoreDNS 是一个可扩展的 DNS 服务器,可以作为 Kubernetes 集群的 DNS。它是过去 Kubernetes 使用的 kubeadm,可以从这里查看:github.com/coredns/deployment/blob/master/kubernetes/CoreDNS-k8s_version.md

如果你的 Kubernetes 集群还没有使用 CoreDNS,这里有一个官方的端到端指南,帮助你顺利迁移到 CoreDNS,并避免配置兼容性问题:github.com/coredns/deployment/blob/master/kubernetes/Upgrading_CoreDNS.md

检查 CoreDNS 服务器是否已启动并正常运行

Kubernetes DNS 服务器在 Kubernetes 集群中调度一个 DNS Pod 和服务,以检查 DNS 服务器是否已启动并正常运行。为此,你可以使用以下命令:

kubectl get pods -n kube-system

通常,你应该能看到类似于以下内容的输出:

图 7.23 – 当多个容器的 Pod 共享网络时

图 7.23 – 当多个容器的 Pod 共享网络时

当你确定已经切换到 CoreDNS 时,你还可以使用以下命令:

kubectl get pods -n kube-system | grep coredns

输出类似于以下内容:

 coredns-6d4b75cb6d-4xcmf          1/1     Running   0          82m
 coredns-6d4b75cb6d-kj6cq          1/1     Running   0          82m

从前面的输出中,你可能注意到我们有两个 CoreDNS Pod 的副本。其目的是在安装 CoreDNS 时将默认值设置为两个副本,以确保高可用性。为了验证这一点,你可以使用 kubectl describe 命令查看 CoreDNS 的部署设置,如下所示:

kubectl describe deploy coredns -n kube-system 

输出应该类似于以下内容:

图 7.24 – 当多个容器的 Pod 共享网络时

图 7.24 – 当多个容器共享一个网络时

由于这是一个部署,我们可以使用典型的kubectl scale命令来扩展或缩减 CoreDNS 部署。这在你想节省一些集群资源时非常有用。你可以使用以下命令将其缩减到一个副本:

kubectl scale deploy coredns -n kube-system --replicas=1

输出应如下所示:

deployment.apps/coredns scaled

然后,你可以使用kubectl get deploy命令查看当前集群中可用的副本数量:

NAME      READY   UP-TO-DATE   AVAILABLE   AGE
coredns   1/1 1 1 11h

同样地,当你想要通过调度更多副本来提高其弹性时,可以使用以下命令来获取更多副本:

kubectl scale deploy coredns -n kube-system --replicas=4

或者,我们可以使用以下命令回去检查副本的数量:

kubectl get pods -n kube-system

如下截图所示,我们成功地将coredns的副本数从 1 个增加到 4 个:

图 7.25 – 当多个容器共享一个网络时

图 7.25 – 当多个容器共享一个网络时

之前的示例还演示了那四个 CoreDNS 副本是相同的。我们可以使用kubectl describe命令仔细查看这四个coredns pod 中的任何一个。以下命令为示例:

k describe pod coredns-6d4b75cb6d-4h89j -n kube-system

输出应如下所示:

图 7.26 – 当多个容器共享一个网络时

图 7.26 – 当多个容器共享一个网络时

从上述输出中,我们可以看到 CoreDNS 正在使用Corefile进行配置。它位于以下位置:

/etc/coredns/Corefile

我们可以使用kubectl get configmaps命令来检查Corefile的内容。以下是操作步骤:

kubectl get configmaps -n kube-system

输出应如下所示:

图 7.27 – 当多个容器共享一个网络时

图 7.27 – 当多个容器共享一个网络时

前述命令显示有一个名为corednsconfigmap,所以我们可以使用kubectl describe configmap命令来查看其内容:

k describe configmap coredns -n kube-system

以下输出将展示Corefile的样子:

图 7.28 – CoreDNS 的 Corefile

图 7.28 – CoreDNS 的 Corefile

Corefile在你需要自定义 Kubernetes 集群中的 DNS 解析过程时非常有用。查看关于自定义 DNS 服务的官方文档:kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#coredns-configmap-options

请注意,Kubernetes DNS 服务已注册到kubelet代理,因此在集群上运行的 Pods 会使用 DNS 服务器的 IP 地址来解析 DNS 名称。kubelet为每个 Pod 设置/etc/resolv.conf文件——从my-packt-apps命名空间的myapp Pod 进行的 DNS 查询可以使用myapp.my-packt-appsmyapp.my-packt-apps.svc.cluster.local进行解析。现在,让我们更详细地了解 Kubernetes 集群中 Pod 的 DNS 主机名是如何工作的。

Pod IP 和 DNS 主机名

Kubernetes 为 Pod 创建 DNS 记录。你可以通过完全限定的、持续一致的 DNS 主机名来访问 Pod,而不需要使用其 IP 地址。对于 Kubernetes 中的 Pod,DNS 名称遵循以下模式:

<your-pod-ip-address>.<namespace-name>.pod.cluster.local

使用以下命令部署一个名为nginx的 Pod:

kubectl run nginx --image=nginx –-port=8080

如果你看到类似以下内容的输出,说明 Pod 已成功部署:

NAME                  READY   STATUS    RESTARTS   AGE
nginx                 1/1     Running   0          3s

让我们仔细看看这个 Pod:

kubectl get pod nginx -o wide

输出应该如下所示:

图 7.29 – 当多容器 Pod 共享一个网络时

图 7.29 – 当多容器 Pod 共享一个网络时

从图中,我们知道nginx Pod 在集群中的 IP 地址是10.1.0.9。根据前面的模式,我们可以推测这个 Pod 的 DNS 名称如下所示:

10-1-0-9.default.pod.cluster.local

重要提示

请注意,实际上,每个 StatefulSet 中的 Pod 会从 StatefulSet 的名称派生主机名。此服务管理的名称域遵循以下模式:

$(service name).$(namespace).svc.cluster.local

查阅官方文档以了解更多:kubernetes.io/docs/concepts/workloads/controllers/statefulset/#stable-network-id

另外,为了获取 nginx Pod 的 IP 地址,你可以使用 kubectl describe pod nginx 命令,这将打开你 nginx Pod 的实时详细规格。在名为 IP 的部分,你可以找到 Pod 的 IP 地址,如下图所示:

图 7.30 – 当多容器 Pod 共享一个网络时

图 7.30 – 当多容器 Pod 共享一个网络时

你可以在default命名空间中使用最新的 Busybox 容器镜像部署一个名为busybox的 Pod,然后执行nslookup命令检查nginx Pod 的 DNS 地址,如下所示:

kubectl run -it busybox --image=busybox:latest 
kubect exec busybox -- nslookup 10.1.0.9

输出应该如下所示:

图 7.31 – 当多容器 Pod 共享一个网络时

图 7.31 – 当多容器 Pod 共享一个网络时

或者,你也可以使用以下命令来实现相同的结果。请注意,我们在命令中添加了两个rm标志,这将确保一旦退出 shell,Pod 被删除。我们还使用--直接执行nslookup命令。这样,它允许我们进行快速测试,在实际的 CKA 考试中非常有用。命令如下所示:

kubectl run -it sandbox --image=busybox:latest --rm --restart=Never -- nslookup 10.1.0.9

输出应该如下所示:

图 7.32 – 当多容器 Pod 共享一个网络时

图 7.32 – 当多容器 Pod 共享一个网络时

我们注意到唯一的不同是,我们得到了pod "sandbox" deleted的消息,这表明一旦退出 shell,名为sandbox的 Pod 会被删除。前面的输出显示了nginx Pod 的 DNS 名称,并且 IP 地址为10.96.0.10。PTR 记录将这个 Pod 的 DNS 名称返回为10-1-0-9.default.pod.cluster.local,正如我们预期的那样。

现在,让我们通过以下命令获取 default 命名空间中 nginx Pod 的 A 记录:

kubectl run -it sandbox --image=busybox:latest --rm --restart=Never -- nslookup 10-1-0-9.default.pod.cluster.local

输出如下:

Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      10-1-0-9.default.pod.cluster.local
Address 1: 10.1.0.9
pod "sandbox" deleted

上面的输出证明了 DNS 服务器返回了 nginx Pod 的 A 记录。接下来,我们通过以下命令部署一个新的 nginx Pod,名为 test-nginx,来测试连接性:

$ kubectl run -it test-nginx --image=nginx --rm --restart=Never -- curl -Is 10-1-0-9.default.pod.cluster.local

输出将如下所示:

图 7.33 – 当多容器 Pod 共享网络时

图 7.33 – 当多容器 Pod 共享网络时

上面的截图显示 200 响应,证明 test-nginx Pod 与 nginx Pod 之间的连接良好,我们成功地使用 curl 命令通过 nginx Pod 的 DNS 名称访问了 nginx 的主页。

到目前为止,我们已经全面了解了在 Kubernetes 集群中,IP 地址和 DNS 是如何为 Pod 工作的。正如我们在本章前面提到的,Kubernetes 不仅为 Pod 创建 DNS 记录,还为服务创建 DNS 记录。接下来,让我们看看在 Kubernetes 中服务的 IP 和 DNS 是如何工作的。

服务 IP 和 DNS 主机名

Kubernetes 中的 DNS 服务为服务创建 DNS 记录,这样你可以通过一致的完全限定 DNS 主机名来访问服务,而不是使用 IP 地址。类似地,在 Kubernetes 中,服务的 DNS 遵循以下模式:

<service-name>.<namespace-name>.svc.cluster.local

既然 DNS 服务器位于 kube-system 命名空间中,我们可以通过以下命令查看它:

kubectl get svc -n kube-system

输出如下,其中我们可以看到 Kubernetes 中 DNS 服务器的 IP 地址:

图 7.34 – 当多容器 Pod 共享网络时

图 7.34 – 当多容器 Pod 共享网络时

上面的截图显示了 DNS 服务器的 IP 地址是 10.96.0.10。现在,让我们检查是否可以通过以下命令获取当前 DNS 服务器的 DNS 名称:

kubectl run -it sandbox --image=busybox:latest --rm --restart=Never -- nslookup 10.96.0.10

输出应如下所示:

图 7.35 – 当多容器 Pod 共享网络时

图 7.35 – 当多容器 Pod 共享网络时

上面的截图证明了 DNS 服务器的 DNS 名称遵循本节前述的模式。它看起来是这样的:

kube-dns.kube-system.svc.cluster.local

现在,让我们看看如何暴露 nginx Pod 的服务。我们使用以下命令将 nginx Pod 的 ClusterIP 服务暴露在端口 80 上:

kubectl expose pod nginx --name=nginx-svc --port 80

以下输出显示服务已成功暴露:

service/nginx-svc exposed

基于之前使用 kube-dns 服务 DNS 名称的实验,我们可以预期 nginx-svc 服务将遵循一般的服务 DNS 名称模式,结果将如下所示:

nginx-svc.default.svc.cluster.local

现在,让我们通过以下命令查看 Kubernetes 集群中 default 命名空间下当前的服务:

kubectl get svc 

我们可以看到类似如下的输出:

图 7.36 – Kubernetes 默认命名空间中的服务

图 7.36 – Kubernetes 默认命名空间中的服务

从前面的输出中,我们可以通过使用kubectl get svc nginx-svc -o wide命令更详细地查看nginx-svc。输出如下:

NAME        TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   
AGE   SELECTOR
nginx-svc   ClusterIP   10.107.75.83   <none>        80/TCP    
59m  run=nginx

前面的命令显示了nginx-svc的 IP 地址是 10.107.75.83,因此让我们使用nslookup命令来查看其 DNS 名称。使用以下命令:

kubectl run -it sandbox --image=busybox:latest --rm --restart=Never -- nslookup 10.107.75.83

前面的命令将给出以下输出:

图 7.37 – 通过查找 IP 地址返回 nginx-svc 的 DNS 名称

图 7.37 – 通过查找 IP 地址返回 nginx-svc 的 DNS 名称

根据前面的输出,我们可以看到nginx-svc的 DNS 名称是 nginx-svc.default.svc.cluster.local,这验证了我们的假设。让我们使用以下命令从默认命名空间获取nginx-service的 DNS A 记录:

kubectl run -it sandbox --image=busybox:latest --rm --restart=Never -- nslookup nginx-svc.default.svc.cluster.local

你会看到输出类似于以下内容:

Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      nginx-svc.default.svc.cluster.local
Address 1: 10.107.75.83 nginx-svc.default.svc.cluster.local
pod "sandbox" deleted

前面的输出显示了 DNS 服务器,这就是我们在本节前面看到的内容 —— kube-dns 服务,IP 地址为 10.96.0.10,DNS 名称为 kube-dns.kube-system.svc.cluster.local。同时,对于我们的nginx-svc,我们得到了返回的 IP 地址 10.107.75.83

现在,类似于我们测试nginx Pod 的方式,让我们测试nginx服务的连通性。我们可以使用名为challenge-nginx的 Pod,然后运行curl命令来查看返回的结果。完整命令如下:

kubectl run -it challenge-nginx --image=nginx --rm --restart=Never -- curl -Is http://nginx-svc.default.svc.cluster.local

前面的命令产生了以下输出:

图 7.38 – 通过查找 IP 地址返回 nginx-svc 的 DNS 名称

图 7.38 – 通过查找 IP 地址返回 nginx-svc 的 DNS 名称

前面的截图显示了 200 响应,证明了nginx-challenge Pod 和nginx-svc服务之间的连接良好,我们成功地使用了curl命令访问了nginx的主页,并且使用了nginx服务的 DNS 名称。知道了nginx服务是通过nginx Pod 暴露的,在实际应用中,我们可以部署多个该nginx Pod 的副本,并通过一个服务将其暴露。流量通过服务分发到每个 Pod。

总结

本章讲解了 Kubernetes 网络。涵盖了 Kubernetes 网络模型和核心网络概念,并介绍了如何选择 CNI 插件。通过使用 Ingress 控制器以及配置和利用 Kubernetes 中的 CoreDNS,有助于理解如何管理集群网络和控制器对 Kubernetes 中应用程序的访问。

确保你已经练习了这些示例,因为你将经常遇到它们。请注意,本章涵盖了 CKA 考试内容的 20%。练习 kubectl 命令将帮助你更好地进行时间管理,从而提高在 CKA 考试中成功的机会。结合我们将在下一章讨论的关于 Kubernetes 集群和应用程序监控与日志记录的内容,你将更好地理解如何在日常工作中管理 Kubernetes 集群。敬请期待!

模拟 CKA 基于场景的实践测试

你有两台虚拟机,master-0worker-0;请完成以下模拟场景。

场景 1

部署一个新的 nginx 部署,使用最新的 nginx 镜像,并在名为 packt-app 的命名空间中创建两个副本。容器通过端口 80 暴露。创建一个 ClusterIP 类型的服务,并在同一命名空间中创建一个 sandbox-nginx Pod,使用 curl 进行调用以验证与 nginx 服务的连接性。

场景 2

通过 NodePort 服务类型暴露 nginx 部署;容器通过端口 80 暴露。使用 test-nginx Pod 通过 curl 进行调用,以验证与 nginx 服务的连接性。

场景 3

使用 wgetcurl 从与该节点处于同一网络中的机器进行调用,以通过正确的端口验证与 nginx NodePort 服务的连接性。

场景 4

使用 sandbox-nginx Pod 和 nslookup 查询 nginx NodePort 服务的 IP 地址,查看返回的结果。

场景 5

使用 sandbox-nginx Pod 和 nslookup 查询 nginx NodePort 服务的 DNS 域名主机名,查看返回的结果。

场景 6

使用 sandbox-nginx Pod 和 nslookup 查询 nginx Pod 的 DNS 域名主机名,查看返回的结果。

你可以在本书的 附录 - 模拟 CKA 基于场景的实践测试解决方案 中找到所有场景的解决方案。

常见问题解答

  • 在哪里可以找到关于 Kubernetes 网络的最新更新,在使用 Kubernetes 时?

Kubernetes 网络 兴趣小组 (SIG) 有一个 GitHub 仓库,您可以在这里关注:github.com/kubernetes/community/blob/master/sig-network/README.md

  • 推荐的官方 Kubernetes 网络文章是什么?

我建议将以下主题的官方文档添加为书签:

第三部分:故障排除

本部分涵盖与 Kubernetes 故障排除相关的主题,包括集群级和应用程序级的日志记录和监控,集群组件和应用程序故障排除,安全性以及网络故障排除。本部分内容大约占 CKA 考试内容的 30%。

本书的这一部分包含以下章节:

  • 第八章监控和日志记录 Kubernetes 集群和应用程序

  • 第九章集群组件和应用程序故障排除

  • 第十章安全性和网络故障排除

第八章:监控和记录 Kubernetes 集群及应用

本章描述了如何监控 Kubernetes 集群组件和应用程序,并获取基础设施级、系统级和应用级日志,以供日志分析或进一步排查故障。结合接下来的两章,关于排查集群组件和应用程序故障以及排查 Kubernetes 安全性和网络问题,它覆盖了 CKA 考试内容的 30%。

本章将涵盖以下主题:

  • 在集群节点上进行监控

  • 在 Kubernetes 集群上监控应用

  • 在集群节点和 Pod 级别管理日志

  • 管理容器 stdoutstderr 日志

技术要求

要开始,您需要确保本地机器符合以下技术要求:

  • 一个兼容的 Linux 主机。我们推荐基于 Debian 的 Linux 发行版,如 Ubuntu 18.04 或更高版本。

  • 确保您的主机机器至少有 2 GB 的 RAM、2 个 CPU 核心,以及大约 20 GB 的空闲磁盘空间。

在集群节点上进行监控

监控对 Kubernetes 管理员至关重要,能够清晰地了解 Kubernetes 集群中的情况。您需要了解所有不同的指标,以帮助您评估集群组件的健康状况。您还需要确保组件按预期运行,并且部署在工作节点上的所有工作负载都能正常运行并具有足够的资源,如 CPU、内存和存储。此外,您还应该检查是否有任何工作节点可用,并且拥有足够的资源来扩展或调度更多工作负载。

在 Kubernetes 中,Metrics Server 收集 CPU/内存指标,并在某种程度上自动调整容器所需的资源。Metrics Server 每 15 秒从 kubelet 代理收集这些指标,然后通过 Metrics API 将它们暴露在 Kubernetes 主节点的 API 服务器中。此过程在以下图中描述:

图 8.1 – Metrics Server 在 Kubernetes 集群中的工作原理

图 8.1 – Metrics Server 在 Kubernetes 集群中的工作原理

用户可以使用 kubectl top 命令访问 Metrics Server 收集的指标。在撰写本章时,Metrics Server 支持扩展至 5,000 个 Kubernetes 工作节点,这是 Kubernetes 当前支持的最大节点数(Kubernetes v1.24 支持最多 5,000 个节点的集群)。有关大型 Kubernetes 集群的更多详情,请参阅此官方文章:kubernetes.io/docs/setup/best-practices/cluster-large/

检查是否安装了 Metrics Server

从您的 Kubernetes 集群开始,您可以采取以下步骤来检查您当前集群中是否可用 Metrics Server。您可以通过使用alias k=kubectl命令设置 kubectl 的别名,然后使用k get命令,如下所示,查看当前可用的工作节点:

    alias k=kubectl
    k get nodes

上述命令将显示当前集群中可用的工作节点。输出类似于以下内容:

NAME       STATUS   ROLES                  AGE   VERSION
minikube   Ready    control-plane,master   5d   v1.23.3

您可以使用k top node命令来检查名为minikube的工作节点的指标,如下所示:

k top node minikube

如果已安装 Metrics Server,上述命令的输出将显示minikube节点的资源使用情况。或者,您将看到以下内容,这仅在您当前的 Kubernetes 集群中没有 Metrics Server 可用时出现,这意味着您需要安装 Metrics Server:

error: Metrics API not available

或者,您可以直接使用以下命令来查看是否会有任何输出:

kubectl get pods -n kube-system | grep metrics-server

CKA 考试通常会预先安装 Metrics Server,因此您可以跳到步骤 3,查看kubectl top命令的用例。

在您当前的 Kubernetes 集群中安装 Metrics Server

如果您使用的是纯净的 Kubernetes 集群,可以通过部署 YAML 定义或使用 Helm 图表来安装 Metrics Server;后者需要安装 Helm。要获取最新的发布版本和说明,请访问它们的 GitHub 库:github.com/kubernetes-sigs/metrics-server

使用 YAML 清单文件

您可以使用kubectl apply -f命令,通过官方 YAML 清单文件部署 Metrics Server,如下所示:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

从 2022 年 2 月底开始,还有一个高可用性HA)版本,将 Metrics Server 的副本数从一个增加到两个。如果您在至少两个节点的集群上,可以使用以下文件:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/high-availability.yaml

您可以在这里获取有关 Metrics Server 的更多信息:github.com/kubernetes-sigs/metrics-server/releases

使用 Helm 图表

要使用 Helm 图表安装 Metrics Server,您可以前往 Artifact Hub,然后在artifacthub.io/packages/helm/metrics-server/metrics-server找到 Metrics Server Helm 图表。

由于现在广泛使用 Helm 3,您需要将 Metrics Server Helm 图表存储库添加到 Helm 中:

helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/

它将显示以下内容以确认已成功添加存储库:

"metrics-server" has been added to your repositories

添加存储库后,您可以通过以下命令安装 Helm 图表:

helm upgrade --install metrics-server metrics-server/metrics-server

上述命令的输出将显示安装是否成功。

使用 minikube 插件

如果您正在使用 minikube 集群,则 Metrics Server 以内置插件的形式提供,可以通过minikube addons命令启用和禁用。您可以使用以下命令列出当前支持的插件:

   minikube addons list

输出与以下截图中的内容类似:

图 8.2 – minikube 插件列表

图 8.2 – minikube 插件列表

从前面的截图中,我们可以看到metrics-server插件已被禁用。你也可以使用以下命令来获得更清晰的视图:

   minikube addons list | grep metrics-server

以下输出显示当前 minikube 插件已禁用

| metrics-server              | minikube | disabled     | kubernetes

你可以使用 minikube addon enable 命令来启用 Metrics Server:

   minikube addons enable metrics-server

以下输出显示 Metrics Server 插件已成功启用:

   ▪ Using image k8s.gcr.io/metrics-server/metrics-server:v0.4.2
🌟  The 'metrics-server' addon is enabled

现在,如果你使用kubectl get命令,你会看到与 Metrics Server 相关的 Pods 和 Services 在 kube-system 命名空间中已启动并运行:

   kubectl get pod,svc -n kube-system 

输出应如下所示:

图 8.3 – kube-system 命名空间中的 Metrics Server Pods 和服务

图 8.3 – kube-system 命名空间中的 Metrics Server Pods 和服务

你可以使用的另一个命令如下:

  kubectl get pods -n kube-system | grep metrics-server

输出应如下所示:

metrics-server-6b76bd68b6-rwlb9    1/1     Running   0         17h

从输出中可以看到,Metrics Server Pod 已启动并运行,这意味着你现在可以使用 kubectl top 命令。现在让我们看看它是如何工作的。

查看 CPU/内存指标

你可以使用 kubectl top 命令来查看你希望获取指标详情的工作节点。以下是一个例子,其中我们查看名为 minikube 的工作节点:

k top node minikube

输出如下所示,我们可以看到 CPU 核心数和使用的内存量:

NAME       CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
minikube 232m 11% 961Mi 24%  

这同样适用于你的 Kubernetes 集群拥有多个工作节点的情况。使用 kubectl top node <节点名称> 命令将帮助你查看该特定节点的资源使用情况。

在 Kubernetes 集群上监控应用程序

一个标准的端到端监控解决方案涵盖了基础设施监控和应用监控。在 Kubernetes 中,Metrics Server 不仅用于监控 Kubernetes 工作节点,还用于监控 Kubernetes Pods 和容器。

我们可以通过在默认命名空间中部署一个新 Pod 来测试应用监控,如下所示:

kubectl run nginx --image=nginx

执行上述命令后,在进入下一节之前,请确保你的 nginx Pod 已启动并运行。要查看 Pod 的状态,可以使用 kubectl get pod nginx 命令。

监控应用程序的资源使用情况

你可以使用 kubectl top pod <podname> 命令查看该 Pod 收集的指标,包括 Pod 的资源消耗:

kubectl top pod nginx

输出应如下所示,你可以看到 Pod 的 CPU 和内存使用情况:

NAME    CPU(cores)   MEMORY(bytes)
nginx 0m 9Mi

在我们的案例中,我们部署了一个单容器 Pod,但需要注意的是,我们也可以通过以下命令查看多容器 Pod 的 CPU 和内存使用情况:

k top pod < pod name > --containers

让我们使用相同的 kubectl top 命令来显示 nginx Pod 及其所有容器的指标:

k top pod nginx --containers

输出应如下所示,因为这是一个单容器 Pod:

POD     NAME    CPU(cores)   MEMORY(bytes)
nginx nginx 0m 9Mi

如果有多个容器,它将列出该 Pod 中容器的名称,并分别显示它们的 CPU 和内存使用情况。

牢记这一点,我们可以使用kubectl top pod,并添加-A标志或–all-namespaces,以显示跨不同命名空间的所有 Pod 的所有指标。以下命令即为此场景下的使用:

k top pod -A 

或者,你也可以使用完整的标志,如下所示:

k top pod --all-namespaces

输出应如下所示,列出所有 Pod,并分别显示它们的 CPU 和内存使用情况:

NAMESPACE     NAME                       CPU(cores)   
MEMORY(bytes)
default       nginx                                 0m           
9Mi
kube-system   kube-proxy-64jzv                      1m          
32Mi
kube-system   kube-proxy-hplp5                      1m          
28Mi
kube-system   kube-proxy-kvb96                      2m          
31Mi
kube-system   kube-proxy-kvjwh                      1m          
28Mi
kube-system   kube-proxy-rmw2r                      1m          
31Mi
kube-system   kube-proxy-tcz5m                      1m          
26Mi
kube-system   metrics-server-6576d9ccf8-z8mlg       6m           
37M

CKA 考试很可能会问你在多个 Pod 中,哪个 Pod 消耗的计算资源最多,或者类似的任务——这时–sort-by标志就派上用场了。--sort-by标志接受cpumemory作为值,结果将按ascdesc排序。该命令如下所示:

kubectl top pod --sort-by=cpu
kubectl top pod –-sort-by=memory

当我们有大量 Pod 并且要求按内存或 CPU 使用资源从最多到最少进行排序时,这会更有意义。我们可以使用以下命令来实现:

kubectl top pod -A --sort-by=memory

输出应如下所示,显示你当前 Kubernetes 集群中所有命名空间下的所有 Pod,并按资源使用情况进行排序:

kube-system   metrics-server-6576d9ccf8-z8mlg       7m          
37Mi
kube-system   kube-proxy-64jzv                      1m          
32Mi
kube-system   kube-proxy-rmw2r                      1m          
31Mi
kube-system   kube-proxy-kvb96                      1m          
31Mi
kube-system   kube-proxy-kvjwh                      1m          
28Mi
kube-system   kube-proxy-hplp5                      1m          
28Mi
kube-system   kube-proxy-tcz5m                      1m          
25Mi
default       nginx                                 0m           
9Mi

当使用–sort-by cpu标志时,该命令的工作方式类似。输出将按 CPU 使用量从高到低列出 Pod。

检查应用程序详情

你可以使用kubectl describe pod <podname>命令来查找关于分配的 CPU 和内存使用情况以及其他一些信息,如运行时版本、系统信息、容量、标签和注解:

kubectl describe pod nginx

输出应如下所示:

图 8.4 – kubectl describe pod nginx

图 8.4 – kubectl describe pod nginx

请注意,在前面的截图底部有一个Events部分,显示了与此 Pod 相关的最近事件日志。我们将进一步查看Events部分:

图 8.5 – nginx Pod 的事件

图 8.5 – nginx Pod 的事件

这里的事件包括 Kubernetes 中的一系列事件,例如这些:

  1. Pod 被调度到名为minikube的工作节点。

  2. 容器镜像从容器注册表中拉取。

  3. kubelet 代理为包含nginx容器的 Pod 提供配置。

  4. Kubelet 启动了 Pod,nginx容器开始接受流量。

分析这些事件有助于我们理解在 Pod 配置过程中发生了什么,并且它能给我们提供线索,帮助我们了解是否发生了任何异常,以及异常发生的原因,从而为我们提供潜在的解决方案。在本章的下一节,我们将更详细地查看这些事件。

如果 pod 位于默认命名空间之外,你可以在kubectl describe命令中指定-n标志来添加命名空间。以下是使用该命令描述位于kube-system命名空间中的coredns-64897985d-brqfl pod 的示例:

kubectl describe pod coredns-64897985d-brqfl -n kube-system

输出应该如下所示:

图 8.6 – kubectl 描述 kube-system 命名空间中的 coredns pod

图 8.6 – kubectl 描述 kube-system 命名空间中的 coredns pod

尽管前面的截图包含了类似的信息块,但每个 pod 的细节是不同的。你可以在命令末尾添加> mypod.yaml来导出 pod 信息以供进一步分析:

kubectl describe pod nginx > mypod.yaml

你将得到一个名为mypod.yaml的 YAML 文件,包含关键的 pod 信息。

监控集群事件

我们可以使用以下命令来获取 Kubernetes 事件:

kubectl get events

我们可以获取当前集群中记录的事件,包括在Events部分中之前记录的事件,当我们使用kubectl describe pod命令时。以下是运行kubectl get events命令后的示例输出:

图 8.7 – kubectl 获取事件

图 8.7 – kubectl 获取事件

你可以使用以下命令按时间戳列出事件:

kubectl get events --sort-by=.metadata.creationTimestamp

如果你想收集部署过程中的事件,可以在一旁运行以下命令:

kubectl get events --watch

如果你没有使用 Kubernetes Dashboard 或者像 Prometheus 与 Grafana Dashboard 这样的第三方监控框架,以上命令将帮助你更好地了解部署过程中的情况。通过监控应用层发生的事情,有时对于故障排除非常有帮助。通常我们通过分析日志和追踪异常来更好地理解问题。接下来我们来看看如何在集群节点和 pod 层级管理日志。

管理集群节点和 Pod 层级的日志

在故障排除问题时,日志非常有用。日志中收集的信息通常有助于理解发生了什么,弄清楚为什么会发生某些问题,并找到解决办法防止它们在未来再次发生。

集群级别日志

在 Kubernetes 中,集群级别日志的概念已广泛认可。这意味着日志应该存储在一个独立的后端中,因此这些日志的生命周期独立于记录到工作节点、pod,甚至容器层级的内容。

Kubernetes 本身并不提供一个全面的原生日志框架,但它可以与社区中许多第三方开源日志解决方案集成,如 Grafana Loki 或 EFK 堆栈,该堆栈包括 Elasticsearch、Fluentd 和 Kibana,用于日志搜索、查询和追踪。

Kubernetes 中的日志记录涉及一组由社区通过不同开源解决方案实现的模式。主要有以下三种模式:

  • 使用在每个节点上运行的节点级日志代理:该代理通常在 DaemonSet 中,因此它会均匀地分布在每个节点上,且该代理会将日志推送到后端。在这种情况下,应用程序无需进行代码更改。

  • 使用专用的侧车容器从同一 Pod 中的应用程序记录信息:这种情况可以与运行在节点上的日志代理或将日志流式传输出去一起使用,通常建议将日志条目以相同格式写入相同的日志流,方便处理。

  • 直接将日志从应用程序流式传输到外部后端:这可以与外部对象存储一起使用,因为这种存储支持生命周期策略,可以根据策略设置数据保留政策并归档旧日志。大多数对象存储还支持搜索框架,日志会被索引,因此容易进行搜索和查询。

若要了解更多关于 Kubernetes 日志架构的信息,可以查看这篇文章:kubernetes.io/docs/concepts/cluster-administration/logging/

查看节点详情

使用原生 Kubernetes,您可以使用 kubectl describe node <nodename> 命令来查找有关分配的 CPU 和内存使用情况以及其他信息,例如运行时版本、系统信息、容量、标签和注释。我们可以使用以下命令来描述名为 minikube 的工作节点:

kubectl describe node minikube

输出类似于以下内容:

图 8.8 – kubectl 描述节点 minikube

图 8.8 – kubectl 描述节点 minikube

了解节点规格将帮助您理解节点的先前配置情况。现在,让我们来看一下如何使用 kubectl describe node 命令获取一些快捷而有用的信息。

检查节点状态

使用 kubectl describe 命令,我们可以获得关于节点的一些常规信息。注意,它还包含一个 events 部分,通常记录节点事件。为了获取更多节点状态信息,通常使用以下命令,以名为 minikube 的节点为例:

kubectl get node minikube -o wide

输出类似于以下内容:

图 8.9 – kubectl 获取节点输出

图 8.9 – kubectl 获取节点输出

从前面的截图中,如果将 kubectl get node 命令与带有 -o wide 标志的命令进行比较,您会发现它提供了有关镜像、内核版本以及容器运行时的额外信息,这在我们需要快速获取信息时非常方便。

管理容器的标准输出(stdout)和标准错误(stderr)日志

在 Unix 和 Linux 操作系统中,有三种 I/O 流,分别为 STDINSTDOUTSTDERR。在这里,我们将讨论 Linux 容器中的 STDOUTSTDERR,这通常是 kubectl logs 命令向我们展示的内容。

STDOUT 通常是命令的正常输出,而 STDERR 则通常用于输出错误消息。Kubernetes 使用 kubectl logs <podname> 命令记录 STDOUTSTDERR。当我们使用该命令记录本章部署的 nginx Pod 时,输出如下所示:

kubectl logs nginx

输出应如下所示:

图 8.10 – kubectl logs nginx pod

图 8.10 – kubectl logs nginx pod

现在,我们将使用容器每秒写一次文本到标准输出流的频率。我们可以通过部署一个新的 Pod 来实现这一点。以下是此 Pod 的 YAML 配置文件示例:

apiVersion: v1
kind: Pod
metadata:
  name: logger
spec:
  containers:
  - name: packs
    image: busybox:1.28
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

您可以使用 kubectl logs 命令从 logger Pod 中检索日志,方法如下:

k logs logger

日志如下所示:

0: Thu May 12 04:34:40 UTC 2022
1: Thu May 12 04:34:41 UTC 2022
2: Thu May 12 04:34:42 UTC 2022
3: Thu May 12 04:34:43 UTC 2022

我们可以使用 -c 标志进入 Pod,检索特定容器的日志。让我们使用以下命令查看 logger Pod 中名为 packt 的容器日志:

k logs logger -c packt

以下输出是从 packt 容器检索到的日志:

图 8.11 – 来自 packt 容器的日志

图 8.11 – 来自 packt 容器的日志

如果您想要实时查看日志,可以使用 kubectl logs -f 命令,如下所示:

kubectl logs -f logger

您应该能够看到类似以下的输出:

图 8.12 – kubectl logs for the nginx pod

如果您想返回某段时间内(例如 1 小时内)更新的日志,请使用以下命令:

kubectl logs --since=1h 

您可以根据需要修改 –since 标志后面的值。

摘要

本章介绍了 Kubernetes 的监控和日志记录,涵盖了集群、节点和 Pod 三个级别。这章为接下来的两章奠定了基础,我们将专注于故障排除集群组件和应用程序故障,以及探索 Kubernetes 安全限制和容器网络的挑战,提供更具体的故障排除用例和端到端的故障排除场景。请继续关注!

模拟 CKA 场景练习测试

您有两台虚拟机,分别为 master-0worker-0:请完成以下模拟场景。

场景 1

列出当前集群中所有可用的 Pod,标识 CPU 消耗最高的 Pod,并将它们的名称写入 max-cpu.txt 文件。

您可以在本书的 附录 - 模拟 CKA 场景练习测试的解答 中找到所有场景的解决方案。

常见问题

  • 我在哪里可以找到有关 Kubernetes Metrics Server 最新更新的信息?

Kubernetes Metrics Server 在 GitHub 上有一个仓库,网址为 github.com/kubernetes-sigs/metrics-server

  • 我在哪里可以找到关于 Kubernetes 集群日志架构的最新信息?

访问官方 Kubernetes 文档:kubernetes.io/docs/concepts/cluster-administration/logging/

  • 我在哪里可以找到 Kubernetes 系统组件的指标?

你可以收藏此页面以获取更多信息:kubernetes.io/docs/concepts/cluster-administration/system-metrics/

第九章:故障排查集群组件和应用程序

故障排除是 Kubernetes 管理员日常工作中的主要任务之一。本章介绍了故障排除集群组件失败以及应用程序部署过程中可能发生的错误的一般方法。

在本章中,我们将涵盖以下主题:

  • Kubernetes 故障排除的一般实践

  • 故障排查集群组件

  • 故障排查应用程序

技术要求

为了开始,我们需要确保本地机器符合以下技术要求。

如果你使用的是 Linux,请参阅以下内容:

  • 一个兼容的 Linux 主机。我们推荐基于 Debian 的 Linux 发行版,如 Ubuntu 18.04 或更高版本。

  • 确保你的主机至少有 2 GB 的内存,2 个 CPU 核心,以及大约 20 GB 的空闲磁盘空间。

如果你使用的是 Windows 10 或 Windows 11,请参阅以下内容:

设置完成后,你可以使用以下命令检查你是否已设置到正确的 Kubernetes 集群:

alias k=kubectlk config current-context

上面的命令将输出当前集群的信息。在我们的案例中,类似以下输出,因为我们在 Windows 上,且使用 Docker Desktop 创建了一个 Kubernetes 本地集群:

docker desktop

如果你在本书中一直跟着我们的演示,你会注意到大多数演示都在minikube集群上进行。在这种情况下,输出将如下所示:

minikube

你可能已经使用本地机器连接到不同的 Kubernetes 集群——你可以使用kubectl config view命令查看当前连接的是哪个集群:

图 9.1 – 本地集群上下文信息

图 9.1 – 本地集群上下文信息

若要了解更多关于如何使用kubeconfig组织集群访问以及如何配置多个集群的访问,请参阅第六章保障 Kubernetes 安全性

在本章中,我们将使用docker-desktop来了解如何排查本地 Kubernetes 集群的问题。请注意,同一组命令也适用于minikube。让我们首先谈谈 Kubernetes 故障排除的一般实践。

Kubernetes 故障排除的一般实践

本书中,我们已经讨论了作为 Kubernetes 管理员日常工作的一些常见任务,尤其是在之前的章节中。在实际操作中,根据你所参与的项目阶段,Kubernetes 管理员通常会参与 Kubernetes 集群的安装与设置、应用部署,以及管理 Kubernetes 的安全性和网络方面的工作。除了上述任务,操作和维护 Kubernetes 集群及其上部署的应用也是 Kubernetes 管理员的核心职责之一。因此,掌握良好的故障排除技能在这种情况下非常有帮助。

故障排除 Kubernetes 集群是一个识别、诊断和修复问题的过程——问题陈述涉及 Kubernetes 集群组件、节点、网络和安全性。此外,问题陈述还涵盖了应用层面,比如 pods,甚至是容器层面。本章将讨论 Kubernetes 集群组件以及应用层面的故障排除,包括 pods 和容器。

采用从外到内的方法,并逐渐缩小范围以识别问题的根本原因非常重要。这意味着我们可以通过以下建议来合理化过程:

  • 监控在识别潜在问题并找出其根本原因方面起着至关重要的作用。在第八章《监控和日志记录 Kubernetes 集群和应用》中,我们讨论了如何监控 Kubernetes 集群组件以及应用,并提供了关于日志记录的指导,帮助你迈出第一步。

  • 指标分析是你发现潜在问题后进行的第一步。虽然有时问题陈述可能并不像看起来那么简单,但你可以通过从集群和节点层级分析指标来获得高层次的视图,然后再深入到应用层,来使故障排除变得更加容易。

  • 有时,指标可能无法告诉你整个故事。在这种情况下,分析日志将帮助你更好地拼凑信息。如果此时你发现自己对发生的问题有了更清晰的了解,那么是时候深入研究这些日志,找出真正的根本原因,而不是你之前认为的罪魁祸首。然而,回到更高层次看看流程中是否有什么遗漏仍然是个好主意。

  • 一旦你找到了问题,如果你想防止问题再次发生,那么需要一个可操作的修复计划,而不仅仅是对问题进行快速修复。这个步骤将为你的未来成功奠定基础,并使你日常的工作变得更轻松。维护和故障排除工作在初始设置后成为日常运营任务——这是你作为 Kubernetes 管理员日常工作的关键组成部分。

在实际的 CKA 考试中,排查问题占的比重较大,并且有些场景比较耗时,因为在有限的时间窗口内找到根本原因通常会让人感到压力很大。然而,作为考生,只要你确定自己已经在其他高价值问题(例如应用部署、网络和备份 etcd 存储等)上做得很好,你就可以有信心地提前规划好时间。排查问题的考试题通常出现在 CKA 考试的后半部分——你通常可以从分析 Kubernetes 集群组件开始。这类问题更有可能是关于工作节点上的 kubelet,然后逐步扩展到应用层。排查时要注意,在进行修复之前,一定要确保在正确的节点上执行排查和修复操作。

根据前面提到的外向内方法,首先讨论如何排查集群组件问题。

排查集群组件问题

排查集群组件包括主节点和工作节点上的 Kubernetes 系统进程。在本节中,我们将看看一些常见的排查场景,并从更高层次的角度进行讲解。

检查集群

检查集群和节点通常是检测控制平面问题的第一步。我们可以使用以下命令来实现:

kubectl cluster-info 

输出显示了控制平面组件和服务的地址:

图 9.2 – 渲染集群信息

图 9.2 – 渲染集群信息

如果你需要进一步的信息来进行调试和诊断,可以使用以下命令:

kubectl cluster-info dump

上述命令输出的内容非常庞大,包含了大量信息——因此我们只展示了以下截图中的关键信息:

图 9.3 – Kubernetes 集群日志

图 9.3 – Kubernetes 集群日志

上述截图显示了日志信息,对于找出根本原因非常有帮助。虽然我们可以从控制平面和集群日志中获取有价值的信息,但通常会经常出现与运行在其上的工作负载相关的错误,这可能是由于节点的可用性或能力问题引起的。接下来,我们将看看如何排查节点问题。

检查节点

使用以下命令检查节点,将帮助你获取当前集群和节点的状态:

kubectl get nodes

输出应如下所示:

图 9.4 – Kubernetes 节点信息

图 9.4 – Kubernetes 节点信息

上述截图显示,当前唯一的工作节点处于 Ready 状态。当你有多个节点时,输出中会列出节点信息。

ROLES 列显示的是节点的角色——它可以是 control-planeetcdworker

  • control-plane 角色运行 Kubernetes 主控组件,除了 etcd

  • etcd 角色运行 etcd 存储。请参阅 第三章维护 Kubernetes 集群,了解更多关于 etcd 存储的信息。

  • worker 角色运行 Kubernetes 工作节点——这就是容器化工作负载所在的地方。

STATUS 列显示当前正在运行节点的状态——我们最喜欢的理想状态是 Ready。可能的状态示例如下表所示:

节点状态 这是什么意思?
Ready 节点健康且准备接受 Pod。
DiskPressure 磁盘容量低。
MemoryPressure 节点内存低。
PIDPressure 节点上运行的进程过多。
NetworkUnavailable 网络配置不正确。
SchedulingDisabled 这不是 Kubernetes API 中的一个状态,而是在你对节点进行封锁(cordon)后出现的状态。请参阅 第三章维护 Kubernetes 集群,了解在需要封锁节点时,如何使用 kubeadm 对 Kubernetes 集群进行版本升级。

表 9.1 - 不同的节点状态及其含义

从前述输出中另一个非常有趣的列是 VERSION 列——它显示了当前节点上运行的 Kubernetes 版本。这里的 Kubernetes 版本指的是 Kubernetes 主控组件版本、etcd 版本或 kubelet 版本,根据节点角色的不同而有所差异。请参阅 第三章维护 Kubernetes 集群,了解如何升级 Kubernetes 节点上的版本。

如果你对某个节点有疑虑,可以使用以下命令检查节点信息:

kubectl describe node docker-desktop

输出应类似于以下内容。如你所见,与你使用 kubectl get node 命令相比,这里可以获得更详细的信息。

图 9.5 –  输出信息

图 9.5 – kubectl describe node 输出信息

为了从前面的命令中获得最大的价值,我们可以查看 Conditions 部分,其内容应如下所示:

图 9.6 – 获取节点状态信息

图 9.6 – 获取节点状态信息

前述截图展示了详细的节点状态信息,正如我们在本章中所解释的那样。你还可以从相同的输出中获取已分配的资源信息,内容如下:

图 9.7 – 获取节点资源消耗信息

图 9.7 – 获取节点资源消耗信息

上述截图中的值用于了解当前集群在 CPU、内存和存储方面的消耗情况。

相同的输出还帮助你概览当前集群中各个 Pod 的资源请求和限制,如下所示的截图所示:

图 9.8 – 获取 Pod 资源消耗信息

图 9.8 – 获取 Pod 资源消耗信息

如果你希望以更结构化的方式查看此输出,可以使用以下命令,使其看起来更像一个yaml文件:

kubectl get node docker-desktop -o yaml

输出如下:

图 9.9 – 获取节点信息的 YAML 格式

图 9.9 – 获取节点信息的 YAML 格式

在前面的输出中,特别注意名为nodeInfo的部分,它概述了操作系统镜像、架构、内核版本、kubeProxy版本、kubelet版本和操作系统:

图 9.10 – 获取 Pod 资源消耗信息

图 9.10 – 获取 Pod 资源消耗信息

如果你不需要查看 Kubernetes 节点的完整概况,而是专注于获取当前在 Kubernetes 集群中运行的进程的内存,可以在 Kubernetes 节点中运行以下命令:

top

输出已优化,应该看起来类似于以下内容:

图 9.11 – 检查进程的消耗信息

图 9.11 – 检查进程的消耗信息

正如我们在本章前面所解释的,DiskPressure也是工作节点健康状态的一个关键因素。你可以使用以下命令检查可用的磁盘存储:

df -h

输出看起来类似于以下内容:

图 9.12 – 可用磁盘信息

图 9.12 – 可用磁盘信息

检查完集群和节点信息后,我们可以进入下一步,检查 Kubernetes 组件。

检查 Kubernetes 组件

我们可以通过检查kube-system命名空间中的流程来使检查变得更简单、更有效——大多数流程都在这里,你可以导出一些有用的信息,如配置、诊断日志等。

排查系统保留进程

使用以下命令检查系统保留进程中的错误:

kubectl get pods -n kube-system

如果你有多个节点,可以添加-o wide标志,查看哪些 Pod 在哪个节点上运行:

kubectl get pods -n kube-system -o wide

正如你从前几章中了解到的,这个命令将输出系统保留进程:

图 9.13 – 系统保留进程

图 9.13 – 系统保留进程

当你看到任何不是Running状态的进程时,意味着它不健康——你可以使用kubectl describe pod命令检查它。以下是检查kube-proxy状态的示例:

k describe pod kube-proxy-9rfxs -n kube-system

前面的命令将打印出 kube-proxy-9rfxs pod 的完整描述信息。然而,由于该 pod 展示了 kube-proxy 组件,我们可以使用以下命令进一步缩小 pod 信息:

k describe pod kube-proxy-9rfxs -n kube-system |  grep Node:

输出打印出节点名称及其分配的 IP 地址:

Node:                 docker-desktop/192.168.65.4

你可以使用 kubectl get node -o wide 命令再三确认,这将打印出 docker-desktop 节点的 IP 地址。它提供与以下相同的 IP 地址(以下是部分输出):

图 9.14 – 节点相关信息

图 9.14 – 节点相关信息

kubectl describe pod 的输出中,kube-proxy-9rfxs -n kubectl,我们知道 kube-proxy 是一个 DaemonSet —— 参考 第四章应用调度与生命周期管理,以更新关于 DaemonSets 的详细信息。如果你有多个节点,并且想查看每个 pod 所在的节点,你还可以使用以下命令查看 kube-proxy DaemonSet:

kubectl describe daemonset kube-proxy -n kube-system

输出类似于以下内容,你可以在其中找到一些有用的信息,如 Pod 状态pod 模板,这将显示此 pod 的详细信息:

图 9.15 – kube-proxy DaemonSet 信息

图 9.15 – kube-proxy DaemonSet 信息

仅知道前面的输出中的 pod 配置信息还不够。当 pod 因某种原因未启动时,日志会更加有用,特别是当 Events 部分显示为 none(如前面截图所示)。我们可以使用以下命令查看 pod 日志:

kubectl logs kube-proxy-9rfxs -n kube-system

前面的命令会打印出类似以下内容的日志,这将为你提供更多关于发生了什么的详细信息:

图 9.16 – Pod 日志信息

图 9.16 – Pod 日志信息

在处理完主节点故障排除后,如果工作节点需要排查,我们应首先排查 kubelet 代理 —— 让我们在下一节深入了解这一点。

排查 kubelet 代理

在检查节点状态之后,如果你还没有登录到工作节点,可以通过 SSH 连接该工作节点,并使用以下命令检查 kubelet 状态:

systemctl status kubelet

输出应如下所示:

图 9.17 – kubelet 代理状态和日志

图 9.17 – kubelet 代理状态和日志

前面截图中的重要部分是 kubelet 的状态,以下截图中可以看到:

图 9.18 – kubelet 代理状态

图 9.18 – kubelet 代理状态

如果状态不是 active (running),我们可以使用 journalctl 获取工作节点上 kubelet 服务的日志。以下命令显示了如何执行此操作:

journalctl -u kubelet.service

输出将打印出类似以下内容的日志详情:

图 9.19 – kubelet 服务详细日志

图 9.19 – kubelet 服务详细日志

然后,你需要找出日志中主要的问题所在。以下是问题陈述的示例:

图 9.20 – kubelet 代理日志中的错误示例

图 9.20 – kubelet 代理日志中的错误示例

请参考第六章,《Kubernetes 安全性》,了解如何使用kubeconfig组织集群访问。修复问题后,应该使用以下命令重启kubelet代理:

systemctl restart kubelet

请注意,在 CKA 考试中,有时并没有实际的问题。在使用journalctl -u kubelet.service命令检查丢失的日志后,你可以通过systemctl restart kubelet命令重启kubelet代理来修复问题。

除了集群组件的问题,我们经常会遇到应用程序故障,后者在日常操作 Kubernetes 集群时可能更为常见。所以,现在让我们来看看如何排查应用程序故障。

排查应用程序故障

在本节中,我们将重点关注在 Kubernetes 集群上排查容器化应用程序的故障。通常涉及与容器化应用相关的 Kubernetes 对象问题,包括 Pods、容器、服务和 StatefulSets。在这一节中你将学到的故障排除技能将在整个 CKA 考试中都非常有用。

获取高层次的视图

要排查应用程序故障,我们必须先获取高层次的视图。以下命令是一次性获取所有信息的最佳方式:

kubectl get pods --all-namespaces

或者,我们可以使用以下方法:

kubectl get pods -A

以下输出显示了按命名空间启动并运行的 Pods,你可以轻松找到哪些 Pods 发生了故障:

图 9.21 – 按命名空间列出 Pods

图 9.21 – 按命名空间列出 Pods

为了充分利用输出信息,请注意NAMESPACEREADYSTATUS列——它们会告诉你在哪个命名空间中 Pods 正在运行以及有多少副本。如果你确定某些命名空间中的某些 Pods 发生了故障,那么可以继续查看下一节,检查命名空间事件。

检查命名空间事件

要检查命名空间事件,你可以使用以下命令查看default命名空间中部署的应用程序发生了什么:

kubectl get events

输出应如下所示:

图 9.22 – Kubernetes 事件

图 9.22 – Kubernetes 事件

在前面的截图中,我们有一些有价值的列:

  • TYPE列显示事件类型——它可以是NormalWarning

  • REASON列与事件的行为相关。

  • OBJECT列显示该事件附加到哪个对象。

  • MESSAGE列显示了特定 Pod 或容器发生了什么。

要了解更多关于事件的信息,可以查看这篇博客,帮助你从 Kubernetes 事件流中提取价值:www.cncf.io/blog/2021/12/21/extracting-value-from-the-kubernetes-events-feed/

你也可以使用以下命令按最新的事件对events列表进行排序:

kubectl get events --sort-by=.metadata.creationTimestamp

它将返回按创建时间戳排序的事件,如下所示:

图 9.23 – Kubernetes 事件按时间戳分组

图 9.23 – Kubernetes 事件按时间戳分组

类似地,如果我们想查看名为app的命名空间中的事件,可以使用以下命令:

kubectl get events -n app --sort-by=.metadata.creationTimestamp

输出应如下所示:

图 9.24 – Kubernetes 事件按时间戳分组

图 9.24 – Kubernetes 事件按命名空间和时间戳分组

上述输出证明我们能够按命名空间打印事件,并按创建时间戳进行排序。

到此为止,我们已经确定了问题发生在哪个 Pod 或容器中。接下来,让我们更仔细地查看故障的 Pod。

故障 Pod 的故障排除

一旦我们缩小范围,确定了哪个 Pod 出现故障,就可以使用命令获取该命名空间中运行的 Pod 状态。以下是获取名为old-busybox、位于app命名空间中的故障 Pod 的命令:

kubectl get pod old-busybox -n app

你的输出将类似于以下内容:

图 9.25 – 获取命名空间中的故障 Pod

图 9.25 – 获取命名空间中的故障 Pod

我们可能会注意到STATUS显示有一个镜像错误(ErrImagePull)。现在,我们可以使用kubectl describe命令获取更多细节:

kubectl describe pod old-busybox -n app

上述命令打印出故障部分的概述,如下图所示:

图 9.26 – 描述命名空间中的故障 Pod

图 9.26 – 描述命名空间中的故障 Pod

你可能会注意到有一个名为Events的部分,显示与该 Pod 相关的事件,如下所示:

图 9.27 – 故障 Pod 事件

我们还可以使用kubectl logs命令获取有关故障 Pod 的一些信息,输出会提供更详细的信息。我们以相同的例子,获取名为old-busybox的 Pod 的日志,如下所示:

kubectl logs old-busybox -n app

输出如下:

Error from server (BadRequest): container "old-busybox" in pod "old-busybox" is waiting to start: trying and failing to pull image

从前面几次输出中,我们知道镜像存在问题。由于这是一个 Pod,我们可以使用以下命令将 Pod 定义导出为名为my-old-pod.yamlyaml文件:

kubectl get pod old-busybox -n app -o yaml > my-old-pod.yaml

我们还可以使用以下命令查看此yaml文件的内容:

cat my-old-pod.yaml

上述命令为我们提供了名为old-busybox的 Pod 的完整配置。但是,我们发现这个文件的关键部分是名为image的部分,如下所示:

图 9.28 – 失败的 pod 规范

图 9.28 – 失败的 pod 规范

我们可以使用以下命令本地编辑导出的文件:

vim my-old-pod.yaml

您会看到,当您处于EDIT模式时,可以按如下方式编辑 YAML 文件:

图 9.29 – 编辑 pod 导出的 YAML 规范

图 9.29 – 编辑 pod 导出的 YAML 规范

编辑完成后,您需要使用kubectl delete命令删除旧的 pod,命令如下:

kubectl delete pod old-busybox -n app

然后,使用kubectl apply -f命令部署my-old-pod,接着您会看到 pod 已经重新启动并正常运行:

   NAME          READY   STATUS      RESTARTS         AGE
   old-busybox   1/1     Running     3(36s ago)       51s

重要提示

对于由部署启动的失败 pod,您可以使用kubectl edit deploy <您的部署>命令对 pod 进行实时编辑并修复错误。这有助于快速修复多种错误。要了解更多关于部署实时编辑的内容,请参考第四章应用调度与生命周期管理

失败的 pods 包括以下几种情况:

失败类型 如何调试?
Pending 使用kubectl describe命令—有时是由于没有可用节点或资源超限导致的调度问题。确保检查节点状态,并使用top命令查看资源分配情况。
CrashLoopBackOff 使用kubectl describekubectl log命令—有时是由集群组件引起的,因此请确保通过外部到内部的方式缩小错误范围。
Completed 使用kubectl describe命令查找发生原因,然后进行修复。
错误 使用kubectl describe命令查找发生原因,然后进行修复。
ImagePullBackOff kubectl描述并通常需要导出 YAML 文件,然后更新镜像。也可以使用set image命令。

表 9.2 - 失败的 pods 及其修复方法

了解 pod 排错非常有用,并且适用于大多数情况,特别是在微服务架构中,通常每个 pod 只有一个容器。当 pod 中有多个容器或包含 init 容器时,我们需要对 pod 执行命令进行排错—现在让我们看看这些情况。

排查 init 容器问题

在本书的第四章应用调度与生命周期管理中,我们学习了 init 容器,以下是我们在示例中部署 init 容器的过程:

apiVersion: v1
kind: Pod
metadata:
  name: packt-pod
  labels:
    app: packtapp
spec:
  containers:
  - name: packtapp-container
    image: busybox:latest
    command: ['sh', '-c', 'echo The packtapp is running! && sleep 3600']
  initContainers:
  - name: init-packtsvc
    image: busybox:latest
    command: ['sh', '-c', 'until nslookup init-packtsvc; do echo waiting for init-packtsvc;  sleep 2;  done;']

我们可以使用以下命令检查该 pod 的initContainer状态:

kubectl get pod packt-pod --template '{{. status.initContainerStatuses}}'

在我的情况下,打印的输出如下:

[map[containerID:docker://016f1176608e521b3eecde33c35dce3596
a46a483f38a69ba94ed48b8dd91f13 image:busybox:latest imageID:
docker-pullable://busybox@sha256:3614ca5eacf0a3a1bcc361c939202
a974b4902b9334ff36eb29ffe9011aaad83 lastState:map[] name:
init-packtsvc ready:false restartCount:0  state:map[running:map
[startedAt:2022-06-23T04:57:47Z]]]]

上述输出显示initContainer尚未准备好。

我们可以使用以下命令检查该 pod 的initContainer日志,以了解问题并修复它:

kubectl logs packt-pod -c init-packtsvc

同样,initContainer也有其状态—以下是常见的几种:

失败类型 这意味着什么?
Init: X/Y Pod 总共有 Y 个 init containers,已完成 X
Init: Error initContainer 执行失败
Init:CrashLoopBackOff initContainer 正在反复失败
Pending Pod 处于待处理状态,尚未开始执行 initContainer
PodInitializing initContainer 已执行,Pod 正在初始化
Running initContainer 已执行,Pod 现在正在运行

熟悉这些状态有助于你判断何时以及如何采取进一步的步骤来调试容器。

总结

本章节覆盖了从集群、节点到 Pod 层级的集群和应用故障排除——这是一种端到端的外部到内部的方法。作为 Kubernetes 管理员,掌握良好的故障排除技能将极大地帮助你为组织提供更大的价值。

在下一章节,我们将重点讨论 Kubernetes 安全性、网络故障排除用例,以及一些端到端的故障排除场景,敬请期待!

常见问题解答

  • 在哪里可以找到一个全面的集群故障排除指南?

你可以从官方 Kubernetes 文档中找到最新信息:

kubernetes.io/docs/tasks/debug/debug-cluster/

  • 在哪里可以找到一个全面的应用故障排除指南?

你可以从官方 Kubernetes 文档中找到最新信息:

kubernetes.io/docs/tasks/debug/debug-application/

第十章:排查安全性和网络问题

到目前为止,我们已经讨论了 Kubernetes 架构、应用生命周期、安全性和网络。希望本章作为最后一章,能够承接 第九章排查集群组件和应用,继续讨论安全性和网络排查。本章提供了针对 RBAC 限制或网络设置引起的错误的通用排查方法。我们在 第六章确保 Kubernetes 安全性 中讲解了如何启用 Kubernetes RBAC,在 第七章解密 Kubernetes 网络 中讲解了 Kubernetes DNS 的工作原理。在深入本章之前,务必回顾这些章节中的重要概念。我们将在本章讨论以下主要内容:

  • 排查 RBAC 故障

  • 排查网络故障

技术要求

为了开始,我们需要确保你的本地机器符合以下技术要求。

如果你使用的是 Linux,我们将演示使用 minikube 集群的示例——请查看 第二章安装和配置 Kubernetes 集群。确保你的测试环境满足以下要求:

  • 一个兼容的 Linux 主机。我们推荐使用基于 Debian 的 Linux 发行版,如 Ubuntu 18.04 或更高版本。

  • 确保你的主机至少有 2 GB 的内存、2 个 CPU 核心和约 20 GB 的空闲磁盘空间。

如果你使用的是 Windows 10 或 Windows 11,请注意以下事项:

排查 RBAC 故障

排除与 Kubernetes 安全相关的任何问题似乎有点矛盾。事实上,Kubernetes 的大多数安全层涉及使用工具来帮助保护 Kubernetes 的 4C 层,这包括安全扫描、管理和保护。要了解更多关于 4C 层的信息,请参考 第六章Kubernetes 安全性。当涉及到排除安全问题时,CKA 考试最常见的是关于 Kubernetes 的 RBAC 问题。因此,我们将在本节中重点展示如何排除 Kubernetes 中 RBAC 失败的示例。

启动 minikube 集群

这一部分不包含在 CKA 考试中,但如果你按照 第二章安装与配置 Kubernetes 集群 中的说明自己部署minikube集群时,可能会遇到这个问题。每当你尝试在全新的 Linux 虚拟机上安装一个新的minikube集群时,你需要应用我们在该章节中讨论的内容。

安装完minikube工具后,你可以使用以下命令启动本地集群:

minikube start 

你可能会在输出中看到以下错误:

图 10.1 – 驱动程序不健康

图 10.1 – 驱动程序不健康

你下意识地选择正确的驱动程序并使用 sudo 命令,如下所示:

sudo minikube start --driver=docker

结果,你可能会看到以下输出:

图 10.2 – 每个命名空间的服务账户

图 10.2 – 每个命名空间的服务账户

之前的输出是由于 Docker 根权限问题。最佳实践是以非 root 用户管理 Docker,以避免这个问题。为了实现这一点,我们需要将一个用户添加到名为docker的组中:

  1. 创建docker组:
sudo groupadd docker
  1. 将你的用户添加到名为docker的组中:
sudo usermod -aG docker $USER
  1. 在此,你需要重新登录或重启 Docker 服务器,以便重新评估你的组成员资格。然而,在 Linux 操作系统上,我们应该通过以下命令激活对组的更改:
newgrp docker 
  1. 下次登录时,如果你希望 Docker 在启动时自动启动,可以使用以下命令:
sudo systemctl enable docker.service
sudo systemctl enable containerd.service
  1. 完成上述步骤后,你应该能够通过以下命令使用 Docker 驱动程序启动minikube
minikube start --driver=docker

如果你能够看到类似以下的输出,说明之前的minikube start命令已经成功创建了minikube集群:

图 10.3 – 成功启动 minikube 集群

图 10.3 – 成功启动 minikube 集群

虽然这一部分不包括在 CKA 考试中,但强烈建议熟悉它,以防在创建minikube集群时遇到问题。一旦你的minikube集群成功启动,我们就可以进入管理minikube集群并根据需要排除 RBAC 问题。

管理 minikube 集群

当涉及到管理minikube集群时,我们在第六章《确保 Kubernetes 安全》中了解到,我们需要将apiserver --authorization-mode设置为RBAC,以启用 Kubernetes RBAC,示例如下:

kube-apiserver --authorization-mode=RBAC

确保我们的当前上下文使用的是默认的minikube,然后使用以下命令在特定命名空间中创建一个新的部署:

kubectl create ns app
kubectl create deployment rbac-nginx –-image=nginx -n app

上述两个命令创建了一个名为app的命名空间,并在该命名空间中创建了一个名为rbac-nginx的新部署。

让我们通过以下命令在名为app的命名空间中定义一个新的角色rbac-user

kubectl create role rbac-user --verb=get --verb=list --resource=pods --namespace=app

然后我们需要创建角色绑定,将这个角色绑定到相关对象,如下所示命令所示:

kubectl create rolebinding rbac-pods-binding --role=rbac-user --user=rbac-dev --namespace=app

由于rbac-user仅具备列出和获取 Pod 的权限,让我们尝试使用这个配置文件进行用户模拟,以删除该部署:

kubectl auth can-i delete deployment --as=rbac-user

输出应如下所示:

No

你可以从官方文档中了解更多关于用户模拟的信息:kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation

为了解决这个问题,我们可以在 YAML 定义中更新rbac-user的角色,如下所示:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: app
  name: rbac-user
rules:
- apiGroups: ["extensions", "apps"] 
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

我们可以使用kubectl auth reconcile命令来创建或更新包含 RBAC 对象的 YAML 清单文件。更多信息请查阅官方文档(kubernetes.io/docs/reference/access-authn-authz/rbac/#kubectl-auth-reconcile):

kubectl auth reconcile -f my-rbac-rules.yaml

RBAC 问题出现在不同开发团队共享集群资源的使用场景中——作为 Kubernetes 管理员,你很可能以完全权限访问集群。了解这部分内容将帮助你更好地管理开发团队成员之间的权限,从而提高安全性和合规性的标准。

排查网络问题

第七章《解密 Kubernetes 网络》中,我们了解到 Kubernetes DNS 服务器为 Kubernetes 中的服务和 Pod 创建 DNS 记录(A/AAAA、SRV 和 PTR 记录)。这些操作使你能够使用一致的 DNS 名称而不是 IP 地址来访问服务。Kubernetes DNS 服务器通过在 Kubernetes 集群中调度多个 DNS Pod 和服务副本来实现这一点。

在接下来的章节中,我们将讨论如何排查 Kubernetes DNS 服务的问题。

排查 Kubernetes DNS 服务器的问题

为了排查 Kubernetes 的网络问题,我们首先检查 DNS 服务器的状态。使用minikube作为本地集群时,我们使用以下命令检查 DNS 服务器是否在你的集群中运行:

kubectl get pods -n kube-system | grep dns

输出应类似于以下内容:

coredns-64897985d-brqfl 1/1 Running 1 (2d ago) 2d

从前面的输出中,我们可以看到 CoreDNS 在当前的minikube集群中正常运行。我们还可以通过使用kubectl get deploy core-dns -n kube-system命令来验证这一点。

为了获取更多细节,我们使用 kubectl describe 命令查看 CoreDNS 部署设置,如下所示:

kubectl describe deploy coredns -n kube-system 

输出如下:

图 10.4 – minikube CoreDNS 配置

图 10.4 – minikube CoreDNS 配置

如前所述,Kubernetes 的 DNS 服务会为服务创建 DNS 记录,因此你可以通过一致的 DNS 完全限定主机名来访问服务,而不是使用 IP 地址。由于它位于 kube-system 命名空间中,我们可以通过以下命令在 minikube 集群中查看:

kubectl get svc -n kube-system

输出如下,显示了 kube-dns 的集群 IP:

NAME       TYPE     CLUSTER-IP   EXTERNAL-IP      PORT(S)        
AGE
kube-dns   ClusterIP 10.96.0.10  <none>   53/UDP,53/TCP,9153/TCP 2d

为了排查 DNS 服务器的问题,我们可以使用kubectl logs命令:

kubectl logs coredns-64897985d-brqfl -n kube-system

前面的kubectl logs命令显示了名为coredns-64897985d-brqflcoredns pod 的日志,输出类似于以下内容:

图 10.5 – minikube CoreDNS 日志

图 10.5 – minikube CoreDNS 日志

输出显示 DNS 服务器是否正常运行,并会记录异常事件(如果有)。一旦我们确认 DNS 服务器正常运行,就可以继续查看如何排查 Kubernetes 集群中已部署的服务问题。

Kubernetes 中服务的故障排除

为了排查服务的问题,首先让我们部署一个新的名为svc-nginx的部署:

kubectl create deployment svc-nginx –-image=nginx -n app

以下输出显示它已成功创建:

deployment.apps/svc-nginx created

现在让我们看看如何暴露 svc-nginx 部署的服务。我们使用以下命令将 nginx pod 的 NodePort 服务暴露在 80 端口:

kubectl expose deploy svc-nginx --type=NodePort --name=nginx-svc --port 80 -n app

以下输出显示它已成功暴露:

service/nginx-svc exposed

正如我们在第七章《揭秘 Kubernetes 网络》中学到的,我们知道 nginx-svc 服务应该遵循通用的服务 DNS 名称模式,其格式如下:

nginx-svc.app.svc.cluster.local

现在,让我们通过以下命令查看当前 Kubernetes 集群中app命名空间下的服务:

kubectl get svc -n app

我们可以看到类似以下的输出:

图 10.6 – Kubernetes app 命名空间中的 nginx-svc 服务

图 10.6 – Kubernetes app 命名空间中的 nginx-svc 服务

从前面的输出中,我们可以通过以下命令更详细地查看nginx-svc

kubectl get svc nginx-svc –n app -o wide

前述命令的输出如下:

图 10.7 – 更详细地查看 nginx-svc 服务

图 10.7 – 更详细地查看 nginx-svc 服务

前面的命令显示了 nginx-svc 服务的 IP 地址为 10.101.34.154,所以我们可以使用 nslookup 命令查看它的 DNS 名称:

kubectl run -it sandbox --image=busybox:latest --rm --restart=Never -- nslookup 10.101.34.154

重要提示

上述命令会在默认命名空间中创建一个busybox pod。由于 Kubernetes 集群中的 pod 默认可以相互通信,我们可以使用一个sandbox pod 来测试与不同命名空间的连通性。

上述命令将返回以下输出:

图 10.8 – 返回 nginx-svc 的 DNS 名称

图 10.8 – 返回 nginx-svc 的 DNS 名称

如果你想通过一个与nginx-svc相同命名空间中的 pod 测试连通性,请使用以下命令:

kubectl run -it sandbox -n app --image=busybox:latest --rm --restart=Never -- nslookup 10.101.34.154

根据上述输出,我们可以看到nginx-svc的 DNS 名称是nginx-svc.app.svc.cluster.local。现在,让我们使用以下命令从app命名空间获取nginx-svc服务的 DNS 记录:

kubectl run -it sandbox --image=busybox:latest --rm --restart=Never -- nslookup nginx-svc.app.svc.cluster.local

你将看到输出类似于以下内容:

Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      nginx-svc.app.svc.cluster.local
Address 1: 10.101.34.154 nginx-svc.app.svc.cluster.local
pod "sandbox" deleted

现在,让我们测试nginx-svc服务的连通性。我们可以使用nginx-beta部署来查看使用curl返回的内容。完整的命令如下:

kubectl run -it nginx-beta -n app --image=nginx --rm --restart=Never -- curl -Is http://nginx-svc.app.svc.cluster.local

输出如下:

图 10.9 – 返回 nginx 主页面

图 10.9 – 返回 nginx 主页面

上述截图显示了 200 响应,证明nginx-beta pod 和nginx-svc服务之间的连通性正常,并且我们成功地使用curl访问了nginx的主页,并且使用了nginx服务的 DNS 名称。

我们在本节中讨论的方法在需要快速测试同一命名空间内或不同命名空间之间的连通性时非常有效。后一种方法也适用于网络策略被部署以限制不同命名空间间的 pod 连通性的场景。接下来,在以下部分中,让我们看看如何获取一个 shell 来调试 Kubernetes 网络,如果我们需要更长时间的会话。

获取一个用于故障排查的 shell

给定相同的场景,在app命名空间中的svc-nginx部署,现在让我们使用交互式 shell 来排查网络问题。

在我们找到nginx-svc的 IP 地址10.101.34.154后,让我们使用nslookup命令检查其 DNS 名称 – 使用以下命令:

kubectl run -it sandbox --image=busybox:latest --rm --restart=Never -- 

我们现在进入交互式 shell:

If you don't see a command prompt, try pressing enter.
/ # whoami
root

在这个交互式 shell 中,我们以 root 身份登录,并可以使用nslookup或其他有效的命令来排查网络问题:

nslookup 10.101.34.154

输出如下:

图 10.10 – 在 BusyBox 中的交互式 shell

图 10.10 – 在 BusyBox 中的交互式 shell

BusyBox 提供了一些命令,但curl并不在其中。所以,现在让我们获取一个支持curlnginx镜像。要了解 BusyBox 中可用的 shell 命令,请参考以下页面:hub.docker.com/_/busybox

我们可以使用以下命令进入nginx pod 的交互式 shell,并查找nginx pod:

kubectl get pods -n app | grep svc-nginx

然后,它将返回由svc-nginx部署创建的 pod 的完整名称:

svc-nginx-77cbfd944c-9wp6s    1/1     Running     0          4h14m

让我们使用 kubectl exec 命令获取交互式 shell:

kubectl exec -i -t svc-nginx-77cbfd944c-9wp6s --container nginx -n app -- /bin/bash

上述命令将为您获取交互式 shell 访问权限,然后我们可以使用相同的 curl 命令测试连通性:

root@svc-nginx-77cbfd944c-9wp6s:/# 
curl -Is http://nginx-svc.app.svc.cluster.local

这项技术在 pod 包含一个或多个容器的情况下非常有用。参考本文获取更多提示:kubernetes.io/docs/tasks/debug/debug-application/get-shell-running-container/

在本节中,我们涵盖了网络故障排除 – 本节中介绍的命令是您在实际调试会话中可以利用的参考资料。回去练习几次,确保您得到适当的理解,它将会带来回报。

总结

本章已经涵盖了 Kubernetes RBAC 和网络故障排除的方法和用例。与 第八章监控和日志记录 Kubernetes 集群和应用,以及 第九章**,故障排除集群组件和应用程序 一起,覆盖了 CKA 内容的 30%。

要充分利用本章,回头参考 第六章保护 Kubernetes,特别是关于如何启用 Kubernetes RBAC 的部分,以及 第七章揭秘 Kubernetes,以更新如何处理 Kubernetes DNS 的部分。了解如何处理 Kubernetes DNS 将帮助您奠定理解其他重要概念的基础。

确保您查看所有章节中关于 常见问题解答 部分的参考资料,以及阅读所有推荐的文档和文章。对这些材料的良好理解将有助于您在日常工作中作为 Kubernetes 管理员更加自信。

让我们保持关注!

常见问题解答

  • 我在哪里可以找到详细的 Kubernetes 服务故障排除指南?

您可以在官方 Kubernetes 文档中找到更新后的文档:

kubernetes.io/docs/tasks/debug/debug-application/debug-service/

同时强烈建议集中精力研究本章与 第九章故障排除集群组件和应用,作为一个补充资源。这将帮助您全面了解 Kubernetes 故障排除的故事。

  • 我在哪里可以找到详细的 Kubernetes 网络故障排除指南?

本书的 第七章揭秘 Kubernetes 网络,涵盖了大部分 Kubernetes 网络概念,以及故障排除示例 – 与本章一起,这将帮助您对可能出现在实际 CKA 考试中的问题更加自信。您还可以收藏官方 Kubernetes 文档中的以下文章:

kubernetes.io/docs/concepts/cluster-administration/networking/

附录 - 模拟 CKA 场景练习测试解决方案

第二章 – 安装和配置 Kubernetes 集群

你有两台虚拟机:master-0worker-0。请完成以下模拟场景。

场景 1

安装最新版本的kubeadm,然后在master-0节点上创建一个基本的 kubeadm 集群,并获取节点信息。

  1. 更新apt 软件包索引,添加 Google Cloud 公共签名密钥,并通过执行以下指令设置 Kubernetes apt 仓库:

    sudo apt-get update
    sudo apt-get install -y apt-transport-https ca-certificates curl
    sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
    echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
    
  2. 首先更新 apt 软件包索引,然后安装kubeletkubeadm

    sudo apt-get update
    sudo apt-get install -y kubelet kubeadm 
    
  3. 此时,如果你还没有安装kubectl,你也可以一并安装kubeletkubeadmkubectl

    sudo apt-get update
    sudo apt-get install -y kubelet kubeadm kubectl 
    
  4. 使用以下命令固定你正在安装的工具版本:

    sudo apt-mark hold kubelet kubeadm kubectl 
    
  5. 你可以使用kubeadm init命令以普通用户身份初始化控制平面,并通过以下命令从你的 master 节点机器获取 sudo 权限:

      sudo kubeadm init --pod-network-cidr=192.168.0.0/16
    
  6. 在 Kubernetes 控制平面初始化成功后,你可以执行以下命令来配置kubectl

     mkdir -p $HOME/.kube
     sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
     sudo chown $(id -u):$(id -g) $HOME/.kube/config
    

场景 2

SSH 登录到worker-0,并将其加入到master-0节点。

你可以使用以下命令将工作节点加入到 Kubernetes 集群中。每次你有新的工作节点需要加入时,都可以使用这个命令,并使用从kubeadm控制平面输出中获取的 token:

sudo kubeadm join --token <token>  <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>

场景 3(可选)

设置一个本地 minikube 集群,并调度一个名为hello Packt的工作负载。

注意事项

查看 第二章中的安装和配置 Kubernetes 集群部分,以设置单节点 minikube 集群。

让我们使用busybox在集群中快速运行一个名为helloPackt的应用:

kubectl run helloPackt --image=busybox

第三章 – 维护 Kubernetes 集群

你有两台虚拟机:master-0worker-0。请完成以下模拟场景。

场景 1

SSH 登录到master-0节点,检查当前kubeadm版本,并升级到最新的kubeadm版本。检查当前kubectl版本,并升级到最新的kubectl版本。

在进入 master 节点后,通过以下命令首先检查当前版本:

kubeadm version 
   kubectl version  

查看最新可用版本:

apt update 
apt-cache madison kubeadm 

使用以下命令升级kubeadm

apt-mark unhold kubeadm && \ 
apt-get update && apt-get install -y kubeadm=1.xx.x-00 && \ 
apt-mark hold kubeadm 

使用以下命令检查你的集群是否可以升级,以及集群可以升级到的可用版本:

kubeadm upgrade plan 

使用以下命令升级kubeadm

    kubeadm upgrade apply v1.xx.y 

场景 2

SSH 登录到worker-0节点,检查当前kubeadm版本,并升级到最新的kubeadm版本。检查当前kubelet版本,并升级到最新的kubelet版本。

在我们进入主节点后,首先通过以下命令检查当前版本:

   kubeadm version 
   kubectl version  

检查可用的最新版本:

  apt update 
  apt-cache madison kubeadm 

使用以下命令升级 kubelet(这也会升级本地的 kubelet 配置):

  sudo kubeadm upgrade node 

使用以下命令对节点进行封锁,以便我们将节点上的工作负载排空并准备进行维护:

kubectl drain worker-0 --ignore-daemonsets 

使用以下命令升级 kubeadm

apt-mark unhold kubeadm && \ 
apt-get update && apt-get install -y kubeadm=1.xx.x-00 && \ 
apt-mark hold kubeadm 

使用以下命令检查你的集群是否可以升级,以及可以升级到哪些可用版本:

    kubeadm upgrade plan 

使用以下命令升级 kubeadm

    kubeadm upgrade apply v1.xx.y 

重启 kubelet 使更改生效:

sudo systemctl daemon-reload 
sudo systemctl restart kubelet 

最后,我们可以解除节点的封锁,使其恢复正常,此时节点会显示为 uncordoned

kubectl uncordon worker-0 

场景 3

SSH 登录到 master-0 节点,并备份 etcd 存储。

使用以下命令检查端点状态:

sudo ETCDCTL_API=3 etcdctl endpoint status --endpoints=https://172.16.16.129:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --write-out=table 

使用以下命令备份 etcd

sudo ETCDCTL_API=3 etcdctl snapshot save snapshotdb 
--endpoints=https://172.16.16.129:2379 
--cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key 

场景 4

SSH 登录到 master-0 节点,恢复 etcd 存储到先前的备份。

使用以下命令从先前的备份操作恢复 etcd

sudo ETCDCTL_API=3 etcdctl --endpoints 172.16.16.129:2379 snapshot restore snapshotdb 

第四章 – 应用调度与生命周期管理

你有两台虚拟机:master-0worker-0,请完成以下模拟场景。

场景 1

SSH 登录到 worker-0 节点,配置一个新的 pod,名为 nginx,并且该 pod 中只包含一个 nginx 容器。

使用以下命令:

kubectl run nginx --image=nginx:alpine

场景 2

SSH 登录到 worker-0,然后将 nginx 扩展到 5 个副本。

使用以下命令:

kubectl scale deployment nginx --replicas=5

场景 3

SSH 登录到 worker-0,设置一个包含用户名和密码的 configMap,然后附加一个新的 busybox pod。

创建一个名为 packt-cm.yaml 的 yaml 定义,来定义 ConfigMap,内容如下:

  apiVersion: v1 
  kind: ConfigMap 
  metadata: 
    name: packt-configmap 
  data: 
    myKey: packtUsername 
    myFav: packtPassword

使用以下命令部署 yaml 清单:

kubectl apply -f packt-cm.yaml

使用以下命令验证 configMap

kubectl get configmap

准备好 configMap 后,创建一个 yaml 定义文件,用于配置 pod 使用该 configMap,内容如下:

apiVersion: v1 
kind: Pod 
metadata: 
  name: packt-configmap 
spec: 
  containers: 
  - name: packt-container 
    image: busybox 
    command: ['sh', '-c', "echo $(MY_VAR) && sleep 3600"] 
    env: 
    - name: MY_VAR 
      valueFrom: 
        configMapKeyRef: 
          name: packt-configmap 
          key: myKey

使用以下命令验证 configMap 的值:

kubectl logs packt-configmap

场景 4

SSH 登录到 worker-0,创建一个 nginx pod,并包含一个名为 busyboxinitContainer

创建一个名为 packt-pod.yaml 的 yaml 定义,内容如下所示:

apiVersion: v1 
kind: Pod 
metadata: 
  name: packtpod 
  labels: 
    app: packtapp 
spec: 
  containers: 
  - name: packtapp-container 
    image: busybox:latest 
    command: ['sh', '-c', 'echo The packtapp is running! && sleep 3600'] 
  initContainers: 
  - name: init-pservice 
    image: busybox:latest 
    command: ['sh', '-c', 'until nslookup packtservice; do echo waiting for packtservice; sleep 2; done;'] 

使用以下命令部署 yaml 清单:

kubectl apply -f packt-pod.yaml
Use the following command to see if the pod is up and running: 
kubectl get podpackt

场景 5

SSH 登录到 worker-0,创建一个 nginx pod,并在同一 pod 中创建一个 busybox 容器。

创建一个名为 packt-pod.yaml 的 yaml 定义,内容如下:

apiVersion: v1 
kind: Pod 
metadata: 
  name: pactk-multi-pod 
  labels: 
      app: multi-app 
spec: 
  containers: 
  - name: nginx 
    image: nginx 
    ports: 
    - containerPort: 80 
  - name: busybox-sidecar 
    image: busybox 
    command: ['sh', '-c', 'while true; do sleep 3600; done;']

使用以下命令部署 yaml 清单:

kubectl apply -f packt-pod.yaml
Use the following command to see if the pod is up and running: 
kubectl get pod pactk-multi-pod

第五章 – 解密 Kubernetes 存储

你有两台虚拟机:master-0worker-0。请完成以下模拟场景。

场景 1

创建一个新的 PV,名为 packt-data-pv,存储空间为 2GB,并且创建两个持久卷声明(PVCs),每个 PVC 需要 1GB 的本地存储。

创建一个名为 packt-data-pv.yaml 的 yaml 定义,用于持久卷,内容如下:

  apiVersion: v1 
  kind: PersistentVolume 
  metadata: 
    name: packt-data-pv
  spec: 
    storageClassName: local-storage 
    capacity: 
      storage: 2Gi 
    accessModes: 
      - ReadWriteOnce

使用以下命令部署 yaml 清单:

kubectl apply -f packt-data-pv.yaml

创建一个名为packt-data-pvc1.yaml的 yaml 定义,用于持久卷声明,内容如下:

apiVersion: v1 
 kind: PersistentVolumeClaim 
 metadata: 
   name: packt-data-pvc1
 spec: 
   storageClassName: local-storage 
   accessModes: 
       - ReadWriteOnce 
   resources: 
     requests: 
        storage: 1Gi

创建一个名为packt-data-pvc2.yaml的 yaml 定义,用于持久卷声明,内容如下:

apiVersion: v1 
 kind: PersistentVolumeClaim 
 metadata: 
   name: packt-data-pvc2
 spec: 
   storageClassName: local-storage 
   accessModes: 
       - ReadWriteOnce 
   resources: 
     requests: 
        storage: 1Gi

使用以下命令部署 yaml 清单:

kubectl apply -f packt-data-pv1.yaml,packt-data-pv2.yaml

场景 2

提供一个名为packt-storage-pod的新 pod,并为该 pod 分配一个可用的 PV。

创建一个名为packt-data-pod.yaml的 yaml 定义,如下所示:

apiVersion: v1 
 kind: Pod 
 metadata: 
   name: packt-data-pod
 spec: 
   containers: 
     - name: busybox 
       image: busybox 
       command: ["/bin/sh", "-c","while true; do sleep 3600;  done"] 
       volumeMounts: 
       - name: temp-data 
         mountPath: /tmp/data 
   volumes: 
     - name: temp-data 
       persistentVolumeClaim: 
         claimName: packt-data-pv1
   restartPolicy: Always

使用以下命令部署 yaml 清单:

kubectl apply -f packt-data-pod.yaml

使用以下命令查看 pod 是否已启动并运行:

kubectl get pod packt-data-pod

第六章 – 安全 Kubernetes

你有两台虚拟机:master-0worker-0,请完成以下模拟场景。

场景 1

在一个名为packt-ns的新命名空间中,创建一个新的服务账户,命名为packt-sa

使用以下命令在目标命名空间中创建一个新的服务账户:

kubectl create sa packt-sa -n packt-ns

场景 2

创建一个名为packt-role的 Role,并将其与 RoleBinding packt-rolebinding 绑定。为packt-sa服务账户分配listget权限。

使用以下命令在目标命名空间中创建一个集群角色:

kubectl create role packt-role --verb=get --verb=list --resource=pods --namespace=packt-ns

使用以下命令在目标命名空间中创建一个 Role 绑定:

kubectl create rolebinding packt-pods-binding --role=packt-role --user=packt-user -- namespace=packt-ns

为了实现相同的结果,你可以创建一个名为packt-role.yaml的 yaml 定义:

apiVersion: rbac.authorization.k8s.io/v1 
kind: Role 
metadata: 
  namespace: packt-ns 
  name: packt-clusterrole
rules: 
- apiGroups: [""]  
  resources: ["pods"] 
  verbs: ["get", "list"]

创建另一个名为packt-pods-binding.yaml的 yaml 定义:

apiVersion: rbac.authorization.k8s.io/v1 
kind: RoleBinding 
metadata: 
  name: packt-pods-binding
  namespace: packt-ns 
subjects: 
- kind: User 
  apiGroup: rbac.authorization.k8s.io 
  name:packt-user
roleRef: 
  kind: Role  
  name: packt-role
  apiGroup: rbac.authorization.k8s.io

使用以下命令部署 yaml 清单:

kubectl apply -f packt-role.yaml,packt-pods-binding.yaml

使用以下命令验证角色:

kubectl get roles -n packt-ns 

使用以下命令验证角色绑定:

kubectl get rolebindings -n packt-ns 

场景 3

创建一个名为 packt-pod 的新 pod,使用image busybox:1.28,并在命名空间packt-ns中暴露端口80。然后将服务账户packt-sa分配给该 pod。

使用以下命令创建一个部署:

kubectl create deployment packtbusybox –-image=busybox:1.28 -n packt-ns –port 80

将部署信息导出为 yaml 配置格式:

kubectl describe deployment packtbusybox -n packt-ns -o yaml > packt-busybox.yaml

编辑 yaml 配置,引用服务账户:

apiVersion: v1
kind: Deployment
metadata:
  name: packtbusybox
  namespace : packt-ns
spec:
  containers:
  - image: busybox
    name: packtbusybox
    volumeMounts:
    - mountPath: /var/run/secrets/tokens
      name: vault-token
  serviceAccountName: packt-sa
  volumes:
  - name: vault-token
    projected:
      sources:
      - serviceAccountToken:
          path: vault-token
          expirationSeconds: 7200
          audience: vault

查看第六章**,安全 Kubernetes中的实现 Kubernetes RBAC部分,以获取更多关于如何实现 RBAC 的信息。

第七章 – 解密 Kubernetes 网络

你有两台虚拟机:master-0worker-0,请完成以下模拟场景。

场景 1

在名为packt-app的命名空间中,部署一个新的 nginx 部署,使用最新的 nginx 镜像,副本数为 2。该容器通过端口80暴露。创建一个类型为 ClusterIP 的服务,位于相同命名空间内。部署一个 sandbox-nginx pod,并使用curl进行连接测试,验证与 nginx 服务的连通性。

使用以下命令在目标命名空间中创建 nginx 部署:

kubectl create deployment nginx --image=nginx --replicas=2 -n packt-app

使用以下命令在目标命名空间中暴露 nginx 部署,并创建 ClusterIP 类型的服务:

kubectl expose deployment nginx --type=ClusterIP --port 8080 --name=packt-svc --target-port 80 -n packt-app

使用以下命令获取内部 IP:

kubectl get nodes -o jsonpath='{.items[*].status.addresses[?( @.type=="INTERNAL-IP")].address}'

使用以下命令获取端点:

kubectl get svc packt-svc -n packt-app -o wide

使用以下命令在目标命名空间中使用你的端点部署 sandbox-nginx pod:

kubectl run -it sandbox-nginx --image=nginx -n packt-app --rm --restart=Never -- curl -Is http://192.168.xx.x (internal IP ):31400 ( endpoint )

场景 2

以 NodePort 服务类型暴露 nginx 部署;容器暴露在80端口。使用 test-nginx pod 执行curl发起请求,验证是否可以连接到 nginx 服务。

使用以下命令在目标命名空间中创建 nginx 部署:

kubectl expose deployment nginx --type=NodePort --port 8080 --name=packt-svc --target-port 80 -n packt-app

使用以下命令获取内部 IP:

kubectl get nodes -o jsonpath='{.items[*].status.addresses[?( @.type=="INTERNAL-IP")].address}'

使用以下命令获取端点:

kubectl get svc packt-svc -n packt-app -o wide

使用以下命令在目标命名空间中部署一个 test-nginx pod,使用你的端点:

kubectl run -it test-nginx --image=nginx -n packt-app --rm --restart=Never -- curl -Is http://192.168.xx.x (internal IP ):31400 ( endpoint )

场景 3

从与该节点处于同一网络中的机器使用wgetcurl发起请求,验证通过正确端口连接 nginx NodePort 服务。

使用以下命令从worker-2发起调用:

curl -Is http://192.168.xx.x (internal IP of the worker 2 ):31400 ( the port of that node  )

或者,我们可以使用wget执行以下命令:

wget http://192.168.xx.x (internal IP of the worker 2 ):31400 ( the port of that node  )

场景 4

使用 sandbox-nginx pod 执行nslookup命令查找 nginx NodePort 服务的 IP 地址,查看返回结果。

使用以下命令:

kubectl run -it sandbox-nginx --image=busybox:latest 
kubect exec sandbox-nginx -- nslookup <ip address of nginx Nodeport>

场景 5

使用 sandbox-nginx pod 执行nslookup命令查找 nginx NodePort 服务的 DNS 域名主机名,查看返回结果。

使用以下命令:

kubectl run -it sandbox-nginx --image=busybox:latest 
kubect exec sandbox-nginx -- nslookup <hostname of nginx Nodeport>

场景 6

使用 sandbox-nginx pod 执行nslookup命令查找 nginx pod 的 DNS 域名主机名,查看返回结果。

使用以下命令:

kubectl run -it sandbox-nginx --image=busybox:latest 
kubect exec sandbox-nginx -- nslookup x-1-0-9(pod ip address).pack-app.pod.cluster.local

第八章 – 监控和日志记录 Kubernetes 集群和应用程序

你有两台虚拟机:master-0worker-0。请完成以下模拟场景。

场景 1

列出当前集群中所有可用的 pod,并找出最消耗 CPU 的 pod。将其名称写入max-cpu.txt文件。

使用以下命令:

kubectl top pod -- all-namespaces --sort-by=cpu > max-cpu.txt
posted @ 2025-06-30 19:27  绝不原创的飞龙  阅读(1)  评论(0)    收藏  举报