CKA-备考指南-全-
CKA 备考指南(全)
原文:
annas-archive.org/md5/0240fbdeb5414fd034b97d8d79373123译者:飞龙
前言
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.io和github.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 的目标是不是记忆,而是实际操作技能;知道如何找到正确的路径并解决挑战才是关键。你可以在以下领域为文档添加书签:
-
Kubernetes 官方文档:
kubernetes.io/docs/ -
Kubernetes 博客:
kubernetes.io/blog/ -
Kubernetes GitHub 仓库:
github.com/kubernetes/
我通常推荐人们收藏的第一页是 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 集群架构
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 – 虚拟机与容器
上图展示了虚拟机(VM)和容器之间的区别。与虚拟机相比,容器更加高效,且更易于管理。
容器镜像
容器将应用程序及其所有依赖项、库、二进制文件和配置文件隔离开来。应用程序的包,连同其依赖项、库、二进制文件和配置,称为容器镜像。一旦容器镜像构建完成,镜像的内容就变得不可变。所有代码更改和依赖项更新都需要构建新的镜像。
容器注册表
为了存储容器镜像,我们需要一个容器注册表。容器注册表可以位于本地机器、内部网络,或者有时在云端。你需要认证才能访问容器注册表的内容,以确保安全性。大多数公共注册表,如 DockerHub 和 quay.io,允许广泛的非限制性容器镜像分发:

图 1.3 – 容器镜像
整个机制的优点在于,它允许开发人员专注于编码和配置,这正是他们工作中的核心价值,而无需担心底层基础设施或管理安装在主机节点上的依赖项和库,正如上图所示。
容器运行时
容器运行时负责运行容器,也称为容器引擎。这是一个运行在主机操作系统上的软件虚拟化层,用于运行容器。像 Docker 这样的容器运行时可以从容器注册表拉取容器镜像,并使用 CLI 命令管理容器生命周期,在这种情况下,使用 Docker CLI 命令,正如下图所示:

图 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 集群基本工作流程
了解基本的 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 插件模型
深入理解 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 多租户与多集群
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 Service(AKS),Amazon Web Service(AWS)提供 Elastic Kubernetes Service(EKS),而 Google Cloud Platform(GCP)则以其 Google Kubernetes Engine(GKE)为荣。
其他流行的 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,可以按照以下说明进行操作:
-
首先,你需要更新
apt包索引,然后,你需要按顺序运行以下命令来安装使用 Kubernetesapt仓库所需的软件包:sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl -
下载 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 -
现在,你可以开始了。确保你再次更新
apt包索引并使用apt-get install命令安装kubectl工具:sudo apt-get update sudo apt-get install -y kubectl -
在完成之前的步骤后,你可以通过运行以下命令来验证
kubectl是否已经成功安装:kubectl version --client
如果你成功安装了kubectl,你将看到类似下面的输出:

图 2.1 – kubectl 安装成功
有关在不同环境中安装kubectl的说明,请参考kubernetes.io/docs/tasks/tools/。
容器运行时
现在,我们将按照这些指示设置containerd作为我们的容器运行时:
-
更新
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 -
安装 Docker 引擎和
containerd.io:sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io -
使用以下命令验证 Docker 是否已经成功安装:
sudo docker ps #optional - running your first docker container sudo docker run hello-world
如果你成功安装了kubectl,你将看到类似下面的输出:

图 2.2 – Docker 已启动并运行
-
如果你准备将
containerd配置为容器运行时,可以使用以下命令并将配置设置为default:sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml -
重启
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)的说明进行操作:
-
更新
apt包索引:curl https://baltocdn.com/helm/signing.asc | sudo apt-key add - sudo apt-get install apt-transport-https --yes -
使用以下命令安装 Helm
apt仓库的相关软件包:echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list -
确保再次使用新的仓库更新
apt包索引,然后使用apt-get install命令安装 Helm:sudo apt-get update sudo apt-get install helm -
使用以下 Helm 命令验证其安装是否成功:
helm version
你将看到类似于以下内容的输出:

图 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:
-
获取
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 -
然后,你可以从官方网站获取一个
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 – 一个 kompose convert 命令将 Docker compose 转换为 Kubernetes 本地的 YAML 定义文件
-
然后,使用以下命令将这些 YAML 文件部署到本地 Kubernetes 集群:
kubectl apply -f .
你的输出将类似于以下内容:

图 2.5 – Kubernetes Pods 启动成功
上面的截图显示了在 Kubernetes 集群中运行的 Redis Pods。
仪表盘
您可以为 Kubernetes 集群安装一个基于 Web 的用户界面(UI)。它不仅显示集群状态并展示 Kubernetes 集群的运行情况,还允许您部署容器化应用程序、进行故障排除,并管理集群及其相关资源。
以下是一个示例控制面板:

图 2.6 – Kubernetes 控制面板
控制面板有时对从 UI 快速监控集群状态很有帮助,且对不熟悉kubectl命令的人来说,界面友好,便于协作。
安装和配置 Kubernetes 集群
本节重点介绍 Kubernetes 集群的安装及其相关配置。通过在第一章中获得的良好理解,您已经了解了 Kubernetes 集群架构和 Kubernetes 工具,接下来您将通过minikube和kubeadm来进行 Kubernetes 集群安装,并且更新集群版本。
请注意,使用minikube启动单节点集群并不包含在 CKA 考试范围内,但它在你想在本地机器上测试 Kubernetes 时非常有用。同样,使用kubeadm安装 Kubernetes 多节点集群,以及设置高可用(HA)Kubernetes 集群也不在考试范围内。
我们希望您通过两种方式学习,但更侧重于通过kubeadm进行的动手实验。从下一个章节开始,我们将带您逐步完成安装新的 Kubernetes 集群及其配置的过程。
安装 Kubernetes 集群的先决条件
为了开始,我们需要确保您的本地机器满足以下技术要求,以便同时支持minikube和kubeadm:
-
一个兼容的 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 运行时
如果你已经安装了 containerd 作为容器运行时,正如本章前面在容器运行时部分中讲解的那样,你会在 /run/containerd/containerd.sock 路径下找到 Unix 域套接字路径,具体如下:

图 2.8 – 检查 containerd 运行时
当检测到 docker 和 containerd 两种容器运行时,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 监控桥接流量
上面的截图显示 iptables 中的值已更新。
检查是否已安装 kubectl
kubectl 是你可以用来与 Kubernetes 集群交互的命令行工具。使用 kubectl version 命令,你可以验证是否成功安装了 kubectl:
kubectl version --client
成功安装将显示类似以下的输出:

图 2.10 – 检查 kubectl 版本
在继续下一个部分之前,确保你已完成本节中的检查清单。这些工具和要求是必要的,你可以在未来根据需要使用它们。
使用 minikube 设置单节点 Kubernetes 集群
使用 minikube 创建 Kubernetes 集群是启动本地 Kubernetes 集群的最简单方法,并且可以在几分钟内完成。以下是你需要做的:
安装 minikube
按照以下步骤安装 minikube:
-
在本地或云端的 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 -
在继续下一步之前,你可以前往
/usr/local/bin/minikube检查是否成功安装了minikube二进制文件,或者你也可以通过在终端中输入以下命令进行检查:minikube –-help
使用 minikube 配置单节点 Kubernetes 集群
按照以下步骤使用 minikube 来配置单节点 Kubernetes 集群:
-
使用
minikube配置单节点 Kubernetes 集群时,你可以简单地使用minikube start命令:minikube start -
你也可以通过添加
--memory和--cpus标志来设置 CPU 核心和内存,启动你的minikube集群,如下所示:minikube start --memory 8192 --cpus 4
执行命令后,它将启动 minikube 集群配置过程。你将看到类似如下的输出:

图 2.11 – 启动 minikube 集群
最后,你将看到一条消息,告诉你我们已经准备好使用 minikube Kubernetes 集群(如前面的截图所示)。
验证 minikube 集群安装
你的 minikube 集群包含一个节点,既充当控制平面,又充当工作节点。这意味着,一旦设置好,你就可以开始在本地 Kubernetes 集群中调度工作负载。你可以使用以下命令查看该节点是否准备好使用:
kubectl get node
你也可以使用该命令的快捷方式:
alias k=kubectl
k get no
输出将显示如下内容:
-
节点的状态及其是否准备好使用
-
节点的角色
-
Kubernetes 版本
-
节点自部署以来的时间
这是输出结果:

图 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 集群。以下是我们需要执行的步骤:
-
安装
kubeadm。 -
启动一个主节点,控制平面将位于该节点
-
安装网络插件(我们将在本章后面详细介绍支持的插件,并在该部分使用 Calico 作为示例)。
-
启动工作节点。
-
将工作节点加入控制平面。
在开始之前,你需要确保你的主节点满足本章列出的所有技术要求。
我们将通过本节描述的步骤部署一个基本的 Kubernetes 集群,如图 2.7所示:

图 2.13 – 使用 kubeadm 启动基本 Kubernetes 集群的工作流程
Kubernetes 集群将类似于图 2.14中展示的架构:

图 2.14 – 标准的多节点 Kubernetes 集群
从现在开始,你可以按照这些指示创建一个多节点的 Kubernetes 集群。要使用kubeadm创建 Kubernetes 集群,其默认设置符合设置标准 Kubernetes 集群的最佳实践。这套最佳实践被封装为 Kubernetes 一致性测试。查看有关 Kubernetes 一致性计划的详细信息:kubernetes.io/blog/2017/10/software-conformance-certification/。
安装 kubeadm
我们介绍了设置docker或containerd作为容器运行时 —— 然后,我们可以按照以下说明安装kubeadm:
-
通过运行以下命令,更新
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 -
首先更新
apt包索引,然后安装kubelet和kubeadm:sudo apt-get update sudo apt-get install -y kubelet kubeadm -
如果你还没有安装
kubectl,你也可以一步安装kubelet、kubeadm和kubectl:sudo apt-get update sudo apt-get install -y kubelet kubeadm kubectl -
使用以下命令来固定你正在安装的工具版本:
sudo apt-mark hold kubelet kubeadm kubectl
输出显示那些包已被设置为保持,如图 2.9所示:

图 2.15 – 检查 containerd 运行时
- 从这里,你可以通过在命令行中输入
kubeadm来检查kubeadm是否已成功安装。以下是该命令的输出:

图 2.16 – 检查 containerd 运行时
- 要验证
kubelet是否存在于主节点上,你可以使用which kubelet命令,该命令会返回kubelet代理的位置:

图 2.17 – 检查 kubelet 是否存在
由于你已经成功安装了kubeadm和kubelet,现在可以开始初始化控制平面。
在这里,我们将展示一个可选操作,你可以使用images pull来预拉取设置 Kubernetes 集群所需的镜像:
sudo kubeadm config images pull
输出应该类似于以下截图:

图 2.18 – 预拉取镜像
请注意,之前的操作是可选的 —— 你可以跳过它,直接进入下一节。
引导主节点
你可以使用kubeadm init命令以普通用户身份初始化控制平面,并通过以下命令获得来自主节点机器的sudo权限:
sudo kubeadm init --pod-network-cidr=192.168.0.0/16
你将看到类似于以下的输出:

图 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 网络提供者,允许你编写网络策略,这意味着它支持一系列的网络选项,以满足不同的需求。我们将按以下方式进行操作:
-
部署 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 -
你可以使用
watch命令来监视 pod 状态:watch kubectl get pods -n calico-system
或者,可以使用以下替代命令:
kubectl get pods -n calico-system -w
现在,你可以看到 pods 的状态为Running:

图 2.20 – 控制平面已成功启动
-
对于通过
kubeadm创建的 Kubernetes 集群,主节点默认会有污点。因此,我们需要移除污点,以便主节点可以调度 pods。要移除污点,可以使用以下命令:kubectl taint nodes --all node-role.kubernetes.io/master-
以下截图显示,主节点上的污点已成功移除:

图 2.21 – 成功移除主节点上的污点
-
你可以使用以下命令查看当前可用的节点:
kubectl get no -
要获取更多关于节点的信息,可以使用以下命令:
kubectl get no -o wide
以下截图显示了示例输出:

图 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 拓扑结构
与本章中构建的基本 Kubernetes 集群架构相比,这种拓扑结构使集群更加具有弹性,这要归功于主节点的冗余性。如果一个主节点宕机,您可以轻松切换到另一个可用的主节点,以确保整个 Kubernetes 集群的健康。
然而,在一些需要管理集群并复制集群信息的情况下,外部 etcd 拓扑结构会派上用场。
kubeadm外部 etcd 集群的 HA 拓扑架构如图 2.24所示:

图 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-0和worker-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。
我们也可以使用 kubeadm 或 minikube 来获取 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 版本遵循语义化版本控制,表达形式为三部分:
-
主版本
-
小版本
-
补丁版本
例如,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 – 主节点升级过程
一旦我们进入主节点,就让我们开始检查当前版本,通过以下命令:
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 – 可用版本
一旦我们决定了想要升级到哪个版本,就可以开始准备升级过程:
- 我们将从升级 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.
- 现在我们可以使用
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"}
- 我们使用
kubeadm upgrade plan命令检查当前集群是否可以升级,以及它可以升级到哪些可用版本:
kubeadm upgrade plan
如图 3.3所示,我可以将 kubelet 和控制平面组件(如 API 服务器、调度器和控制器管理器)从 1.23.2 升级到 1.23.3:

图 3.3 – kubeadm 升级计划
- 如果我们决定采取行动,将当前集群从 1.23.2 升级到 1.23.3,可以使用以下命令。请注意,在
apply之后,你只需替换为任何未来的可用版本,升级到你希望的版本:
kubeadm upgrade apply v1.23.3
重要提示
为了顺利执行升级操作,我们建议你通过运行sudo su命令获取考试中的 root 权限。
在日常升级任务中,你可以使用sudo并输入密码来执行此操作。
一旦执行命令,你会看到一条消息,表明升级成功:

图 3.4 – 控制平面成功升级
- 然后我们需要隔离节点,因此我们将清理工作负载以准备节点进行维护。我们用以下命令隔离一个名为
cloudmelonplaysrv的节点:
kubectl drain cloudmelonplaysrv --ignore-daemonsets
它将显示一堆正在被驱逐的 Pod,意味着这些 Pod 正在从被隔离的工作节点中被移除:

图 3.5 – 在节点上清理工作负载
如果你正在使用kubectl get no命令,节点将被标记为schedulingDisabled。
- 我们使用以下命令来升级 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
- 重启 kubelet:
sudo systemctl daemon-reload
sudo systemctl restart kubelet
- 现在我们可以解锁该节点,这样正在升级的节点
cloudmelonplaysrv上的工作负载就可以再次调度:
kubectl uncordon cloudmelonplaysrv
该命令将返回现在显示为uncordoned的节点。
升级工作节点
由于工作节点是实际运行工作负载的地方,我们需要逐一升级,然后将相同的操作复制到当前 Kubernetes 集群中的所有其他工作节点上。图 3.6描绘了总体的升级工作流:

图 3.6 – 在节点上排空工作负载
- 让我们从使用以下命令将 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
- 对于工作节点,我们升级 kubelet,这也会升级本地 kubelet 配置,使用以下命令:
sudo kubeadm upgrade node
- 同样,我们需要对节点进行 cordon 操作,这样我们就能将工作负载迁移出去,为节点的维护做准备。在这里,我们使用以下命令对一个名为
cloudmelonplayclient的节点进行 cordon 操作:
kubectl drain cloudmelonplayclient --ignore-daemonsets
然后,我们可以使用kubectl get no命令来检查节点状态。它将标记为schedulingDisabled。
- 我们使用以下命令来升级 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
- 重启 kubelet 以使更改生效:
sudo systemctl daemon-reload
sudo systemctl restart kubelet
- 最后,我们可以
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 状态
在接下来的章节中,我们将更详细地了解 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
在图中,你可以看到关于 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
输出描述了诸如 ID 和 Status 等列,这些列显示了 etcd 集群的状态、集群名称、对等节点和客户端地址。
您可以使用 --write-out=table 自动将输出以表格形式呈现。它将看起来像这样:

图 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 成员列表
从 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 成员列表
在接下来的章节中,我们将探讨如何从 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 版本
同样,您可以使用以下命令检查 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 版本
同样,一旦安装了 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 过程概括如下:
-
SSH 进入 etcd 集群节点。它可以是一个独立的节点,也可以与主节点相同。在 CKA 考试中,您很可能会从主节点开始,在该节点上安装了 etcdctl,因此这一步是可选的。
-
检查 etcd 状态。您可以通过
kubectl describe <etcd-podname>命令获取必要的信息。 -
执行 etcd 备份。
-
退出主节点。在实际的 CKA 考试中,这一步可能不是必需的。
一般过程如下图所示:

图 3.14 – 备份 etcd 过程
现在让我们看看备份 etcd 的详细过程:
- 如果你需要连接到主节点或 etcd 集群节点,可以使用
ssh master-0命令或ssh username@<nodeIP>命令。请注意,这一步是可选的。以下是一个名为packtuser的用户使用ssh连接到 IP 地址为10.10.11.20的节点的示例:
ssh packtuser@10.10.11.20
- 使用以下命令从集群外部检查 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 状态
- 使用
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 存储
- 通过以下命令验证快照:
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 存储
恢复 etcd
要恢复 etcd 集群,你可以按照图 3.18中展示的过程进行操作。请注意,如果你有任何 API 服务器实例正在运行,你需要在执行恢复操作之前停止它们。etcd 恢复后,你可以重新启动 API 服务器实例:
-
SSH 连接到 etcd 集群节点。
-
检查 etcd 状态。
-
恢复 etcd 备份。
-
退出主节点:

图 3.18 – 恢复 etcd 过程
一旦你有了快照,你可以使用以下命令从之前的备份操作中恢复 etcd:
sudo ETCDCTL_API=3 etcdctl --endpoints 172.16.16.129:2379 snapshot restore snapshotdb
返回的输出显示 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-0 和 worker-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 场景实践测试解决方案中找到所有场景的解决方法。
常见问题解答
- 我在哪里可以找到每个发布版本的 Kubernetes 组件的兼容版本信息?
前往 Kubernetes 官方文档了解版本偏差策略:kubernetes.io/releases/version-skew-policy/。
- 我在哪里可以了解 etcd 存储的最新发展?
前往 etcd.io/,您将找到 etcd 存储的最新发展。有关守护程序和如何开始使用 etcd,请参阅官方文档:etcd.io/docs/。
- 升级 Kubernetes 集群的推荐官方 Kubernetes 文章是什么?
我建议将文章《升级 kubeadm》添加到书签中,在其中你将找到大部分关键命令和过程:kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/。
- 备份和恢复 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 事件
相同的 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 的共享网络
- 共享存储卷:我们可以将相同的存储卷挂载到两个不同的容器,以便它们可以共同访问相同的数据 —— 一个容器可以将数据写入存储卷,另一个容器则可以从同一存储卷读取数据。有些存储卷甚至允许并发读写。我们将在 第五章 中深入探讨多容器 Pod 的存储工作原理,《揭开 Kubernetes 存储的神秘面纱》。
图 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 的一个典型使用场景是当我们想要运行特定的工作负载,并确保它只运行一次并成功执行时。
-
你可以使用 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 仍然存在,但容器已经完成。
-
你可以使用以下命令部署 YAML 定义:
kubectl apply -f melon-job.yaml -
你可以运行以下命令检查 Job 的状态:
kubectl get job -
当 Job 仍在运行时,你可以看到
Running状态。当 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
-
你可以使用以下命令部署 YAML 定义:
kubectl apply -f melon-cronjob.yaml
你可以使用以下命令检查 cron 作业的状态:
kubectl get cronjob
你将获得如下输出:

图 4.5 – cron 作业显示为已完成
这个 cron 作业创建了几个名为 hello 的 pod,因此我们将使用以下命令检查 Job 的日志:
kubectl get pods | grep hello
你将获得如下输出:

图 4.6 – 完成的 cron 作业 pod
我们可以使用以下命令检查这些 pod 的日志:
kubectl logs hello-xxxx
我们可以看到 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
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
如果您知道 Deployment 的名称,可以使用以下命令来获取该 Deployment:
kubectl get deployment kubeserve
您将看到以下输出:

图 4.10 – 使用 kubectl 按名称获取 Deployment
以下命令允许您获取 Deployment 的详细信息:
kubectl describe deployment kubeserve
此命令将帮助你了解 Deployment 中的配置,你将看到以下输出:

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

图 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 执行滚动更新的步骤:
-
你可以使用以下命令启动一个新的 Deployment,
kubeserve:kubectl create deployment kubeserve --image=nginx:latest -
你可以使用
kubectl按照以下方式更新容器镜像:kubectl set image deployment/kubeserve nginx=nginx:1.18.0 --record
重要说明
--record flag 用于记录更新的信息,以便以后可以回滚。你可以使用--record flag或者--record=true flag。
使用上述命令后,你将看到以下输出:
deployment.apps/kubeserve image updated
-
你可以使用
kubectl describe命令通过输入以下命令来再次检查容器镜像是否已成功更新:kubectl describe deploy kubeserve
你的输出应该类似于 图 4.14,其中你可以看到镜像已设置为nginx:1.18.0:

图 4.13 – kubectl 描述更新镜像后的 kubeserve
kubectl describe deploy 命令在我们尝试查看关键信息时非常有用,比如容器镜像、端口和与部署相关的事件。在实际的 CKA 考试中也是如此——确保你掌握此命令的快捷方式 k describe deploy,这将帮助你在考试中更加高效地工作。
回滚
回滚使我们能够恢复到之前的状态,而部署让这一切变得非常简单:
-
如果需要执行回滚,你可以使用以下
kubectl rollout命令来快速恢复:kubectl rollout undo deployments kubeserve
你的输出应如下所示:
deployment.apps/kubeserve rolled back
- 现在,如果你使用
kubectl describe deploy kubeserve命令,你将看到以下输出,表明镜像已被回滚:

图 4.14 – kubectl 描述回滚后的 kubeserve
-
现在,你可能非常好奇是否可以跟踪我们部署的历史记录。你可以使用以下命令:
kubectl rollout history deployment kubeserve
输出将如下所示:

图 4.15 – kubectl 描述 kubeserve
-
如果你想回滚到特定版本,你可以使用
--to-revision参数。你可以在 图 4.16 中看到,由于我们在设置镜像版本时使用了--record参数,我们可以使用版本2。以下命令是回滚部署并恢复到版本2的示例:kubectl rollout undo deployment kubeserve --to-revision=2
你的输出应如下所示:
deployment.apps/kubeserve rolled back
- 现在,如果你使用
kubectl describe deploy kubeserve命令,你将看到以下输出,表明镜像已回滚到版本2:

图 4.16 – kubectl 描述 kubeserve
部署不仅使滚动更新和回滚过程变得更加简单,而且帮助我们轻松地进行扩缩容——在下一节中,我们将了解如何扩展应用程序,以及扩展时所有可行的选项。
扩展应用程序
当我们的应用程序变得流行时,为了处理越来越多的按需请求,我们需要启动多个应用程序实例来满足工作负载要求。
当你有一个部署时,扩展是通过更改副本数来实现的。在这里,你可以使用 kubectl scale 命令来扩展部署:
kubectl scale deployment kubeserve --replicas=6
你的输出应如下所示:
deployment.apps/kubeserve scaled
如果你现在使用 kubectl get pods 命令,你将看到更多副本的 Pods 被启动,输出如下所示:

图 4.17 – kubectl 获取 Pods 并显示更多副本
除了使用 kubectl scale 命令手动扩展部署,我们还有另一种扩展部署及其 ReplicaSets 的方式,那就是 HorizontalPodAutoscaler(HPA)。让我们先来看一下 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 的状态
一旦 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
如果你想删除一个 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
从前面的图示中,我们可以看到 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
你还可以使用以下 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
你可以使用以下 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
别忘了通过以下命令查看 DaemonSets 的详细信息:
kubectl describe daemonsets fluentd -n kube-system
你的输出将如下所示:

图 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 获取命名空间
当你定义一个 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 – 获取节点标签
然后,你可以在 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 request 和 limits 的 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 描述节点资源
如果你的集群中已安装了度量服务器,你可以使用 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
你可以使用以下命令检查 configmap 的二进制数据:
k describe configmap melon-configmap
以下截图是前述命令的输出:

图 4.29 – configmap 二进制数据
一旦 configmap 准备好,下面是如何配置 Pod 来使用它:
-
通过使用环境变量创建一个可以使用
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 挂载的值
-
你可以通过卷来创建 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 挂载的值
如果你想删除一个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
创建 Secret 后,你可能希望将其附加到一个应用程序中。这时,你需要创建一个 pod 来消费该 Secret,步骤如下:
-
你可以创建一个 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 -
你还可以将 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-0 和 worker-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 是哪一篇?
我建议收藏这篇文章,ConfigMaps:kubernetes.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
然后,用户可以使用与 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 中多个容器共享存储卷
以下是一个将 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 中。我们在第四章《应用调度与生命周期管理》中已经讨论了 ConfigMap 和 Secret 对象,它们属于这一类别。更具体地说,它们也被称为投影卷,因为它们代表一个将多个现有卷映射到同一目录的卷。
除了 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
所有投影卷,configMap、downwardAPI、secret 和 emptyDir,都作为本地短暂存储提供。在每个节点上,kubelet 负责配置和管理 Pod,并管理本地短暂存储。
除了作为内部存储的挂载存储外,在某些使用场景中,我们还需要容器生命周期之外的持久数据,即使容器停止或被替换,数据依然存在。这就提出了为我们的 Pod 分配持久外部存储的需求。我们将在下一节讨论持久卷。
持久化存储
与短暂卷相比,持久卷的生命周期与 Kubernetes Pod 无关。状态持久性意味着在容器删除或替换后,某些数据或信息能够继续存在。然而,在容器运行时,它可以被容器修改或更新。
在 Kubernetes 中与持久卷(Persistent Volume)一起工作的机制利用了暴露的 API,它抽象了外部存储提供和消费的技术细节。Kubernetes 允许我们通过持久卷和持久卷声明的概念来操作持久存储:
-
持久卷(PV)是根据存储类动态供应的存储资源,具有一组功能,以满足用户的需求。
-
持久卷声明(PVC)是用户请求的 Pod 和 PV 之间的抽象层,包含一组要求,包括特定的资源级别和访问模式。
如下所示,图 5.3 中,PV 和 PVC 被定义在 Kubernetes 集群中,而物理存储则位于 Kubernetes 集群之外:

图 5.3 – PV 和 PVC
同样需要注意的是,PV 可以绑定到 PVC,它是集群范围的资源,而 PVC 是命名空间级别的资源。
在我们深入探讨如何使用之前,先来了解一些关于 PV 和 PVC 的其他重要概念。
存储类(StorageClass)
Kubernetes 中的 StorageClass 资源将 Kubernetes 存储分类。事实上,StorageClass 包含 provisioner、parameters 和 reclaimPolicy 字段。
供应器(provisioner)表示用于供应 PV 的 CSI 卷插件。不同供应器的示例包括 Azure 磁盘、AWS EBS 和 Glusterfs。你可以在这里找到支持的 StorageClass 资源的完整列表:kubernetes.io/docs/concepts/storage/storage-classes/。
我们需要在 PVC 中定义存储类,存储类的定义包括供应器和回收策略。它们之间的关系如 图 5.3 所示:

图 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 最有趣的一点是,用户无需担心存储的位置细节。用户只需要了解 StorageClass 和 accessMode。PVC 会自动绑定到具有兼容 StorageClass 和 accessMode 的 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 被调度到特定工作节点后,会在节点上创建空存储:
-
使用以下命令检查你当前是否有可用的节点来调度 pod:
kubectl get nodes
或者,你也可以使用前面命令的简化版本:
alias k=kubectl
k get no
如果你任何节点的状态显示为 Ready,如以下图所示,那就意味着你可以继续执行下一步:

图 5.5 – 检查可用的节点
- 使用 Vim 编辑器创建一个新的 YAML 定义文件,命名为
pod-volume.yaml,并在进入 Vim 后按下键盘上的 Insert 键,将当前的edit模式切换为INSERT:

图 5.6 – 使用 Vim 插入 YAML 规范
-
然后,将以下内容放入 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: {} -
然后,保存你的编辑并退出 Vim。按下 Esc 键,在编辑器底部输入
:wq!,然后按 Enter 键返回终端:

图 5.7 – 在 Vim 中保存 YAML 定义
-
在终端中,使用以下命令来部署
.yaml文件:kubectl apply -f pod-volume.yaml
然后,它应该会显示一条消息,表明 pod 已成功创建,类似于以下内容:
pod/my-volume-pod created
你可以继续使用kubectl get pods命令检查 Pod 是否正在运行,命令返回以下输出:

图 5.8 – 检查 Pod 是否正在运行
现在,你已经部署了一个带有挂载存储的 Pod。如果你运行以下命令,你将能够查看更多细节,包括配置信息、资源要求、Pod 的标签和关于这个 Pod 及其挂载存储的事件信息:
kubectl describe pod my-volume-pod
该命令的输出应类似于以下内容:

图 5.9 – 检查 Pod 配置和状态
从输出中,我们可以看到 Pod 已经挂载到名为my-volume的卷上,就像我们在 YAML 定义中指定的那样。Type被指定为EmptyDir,所以它是一个临时目录,和 Pod 的生命周期共享。截图底部还显示了在配置该 Pod 时的相关事件。
配置具有持久存储的应用程序
在这种情况下,你需要创建一个新的 YAML 定义文件,其中写入 Kubernetes PV 的规格——Kubernetes 将在 Pod 被调度到特定工作节点后,根据绑定到该 PV 的 PVC 来分配存储。
创建你的 PV
你可以通过使用kubectl get nodes或kubectl get no来检查当前是否有可用的节点来调度 Pod。确保你的某个节点的状态是Ready,如以下所示:

图 5.10 – 检查可用的节点
从这里开始,我们通过以下步骤创建一个新的 PV:
-
使用 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" -
当你在终端时,使用以下命令来部署
.yaml文件:kubectl apply -f data-pv.yaml
然后,它将显示一条消息,表示 PV 已成功创建,类似于以下内容:
persistentvolume/data-pv created
前面的 YAML 定义意味着分配了 1 GB 的存储作为本地存储。你可以定义一个 1 GB 存储的 PVC,并绑定到这个 PV。然而,在理论情况下,如果你有两个 500 MB 的请求,PV 也可以在分配过程中被拆分。底层,这两个 PVC 会绑定到同一个 PV,然后共享存储容量。
-
使用以下命令检查 PV 的状态:
kubectl get pv
你将获得以下输出:

图 5.11 – 检查 PV 是否可用
注意,状态为available,意味着该 PV 目前没有绑定到 PVC,并且可以与我们将在下一步创建的新的 PVC 进行绑定。
创建你的 PVC
从这里开始,我们通过以下步骤创建一个新的 PVC:
-
使用 Vim 编辑以下名为
data-pvc.yaml的 YAML 定义文件:apiVersion: v1 kind: PersistentVolumeClaim metadata: name: data-pvc spec: storageClassName: local-storage accessModes: - ReadWriteOnce resources: requests: storage: 512Mi -
当你在终端上时,使用以下命令来部署
yaml文件:kubectl apply -f data-pvc.yaml
PVC 成功创建,并输出类似于以下内容:
persistentvolumeclaim/data-pvc created
-
使用以下命令检查 PVC 的状态:
kubectl get pvc
你将得到以下输出:

图 5.12 – 检查 PVC
你可能会注意到该 PVC 的状态为Bound,这意味着它已经绑定到了 PV。
为了再次确认它是否绑定到你想要的 PV,你可以使用kubectl get pv命令进行检查:

图 5.13 – 检查 PVC 是否绑定到 PV
上图显示了我们 PV 的 Bound 状态,意味着它已经成功绑定。
配置 pod 以使用 PV
在这里,我们正在通过以下步骤配置 pod 以使用 PV:
-
使用 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 -
- 当你在终端上时,使用以下命令来部署
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 是否已启动并正常运行
你可以使用以下命令进一步查看详细信息,包括配置、资源要求、pod 的标签以及此 pod 和动态分配的存储的事件信息:
kubectl describe pod data-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-0 和 worker-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 中的不同层
从前面的图示中,我们可以看到以下内容:
-
云层基于 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 认证
你可以参考以下文章,概览 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 – 按命名空间显示的服务账户信息
这也意味着我们可以使用kubectl get sa命令按命名空间获取服务账户信息,然后通过指定-n标志和namespace name来获取特定命名空间的服务账户。例如,使用kubectl get sa -n和kube-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 输出
你可以使用 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 时,我们有RoleBinding和ClusterRoleBinding的概念。绑定将角色与用户、组或服务账户等一组主体关联,正如以下图所示:

图 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。其他可能的值包括node、ABAC、Always deny和webhook。在以下命令中,我们展示了将其设置为使用 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仅具有list和get权限,让我们尝试使用此配置文件删除部署:
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
从输出中,我们可以看到 uid 为 1000,与 runAsUser 字段相同;gid 为 3000,与 runAsGroup 字段相同;fsGroup 为 2000。
要了解更多关于安全上下文的信息,请查阅官方文档:kubernetes.io/docs/tasks/configure-pod-container/security-context/
总结
本章概述了 Kubernetes 安全性,重点讨论了容器安全、RBAC 和安全上下文三个关键主题。你可以利用本章内容为你的 CKS 考试打下基础。接下来的章节《揭开 Kubernetes 网络的神秘面纱》,将帮助你全面了解与 Kubernetes 网络安全相关的概念和实践示例,帮助你作为 Kubernetes 管理员在日常工作中应用这些知识,并且这将覆盖 CKA 考试内容的 20%。让我们拭目以待!
模拟 CKA 场景练习测试
你有两台虚拟机,master-0和worker-0—请完成以下模拟场景。
场景 1
在名为packt-ns的新命名空间中创建一个名为packt-sa的新服务账户。
场景 2
创建一个名为packtrole的 Role,并将其与 RoleBinding packt-clusterbinding绑定。将packt-sa服务账户映射为list和get权限。
场景 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 网络
以下是一个名为multi-container-pod.yaml的示例,展示了如何在一个 Pod 中创建多个容器。在这个 Pod 中,包含了nginx和busybox两个容器,其中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 容器busybox与nginx容器通信:
kubectl exec multi-container-pod -c busybox-sidecar -- wget http://localhost:80
以下输出证明两个容器可以互相通信:

图 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 – 检查在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.3,nginx Pod 的 IP 地址为172.17.0.4。这些分配给 Pod 的 IP 地址位于相同的podCIDR范围内:

图 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 到服务通信类似,外部到服务的通信挑战也由服务来解决。NodePort 或 LoadBalancer 等服务类型可以接收来自 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 下,创建了 ClusterIP 和 NodePort,分别用于内部和外部通信。 |
查看来自云提供商的 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
通过前面的图示,我们首次了解了服务如何与 pod 一起工作。接下来,让我们部署一个应用程序并深入研究。要创建一个名为 nginx 的部署,并将 replicas 数量设置为 2,请使用以下命令:
kubectl create deployment nginx --image=nginx --replicas=2
我们可以通过以下命令追踪部署过程:
kubectl get deploy nginx -o wide
一旦我们完成,我们应该能够看到以下输出:

图 7.6 – 可用的 nginx 副本数量
从前面的输出中,我们可以看到有两个 nginx pod 正在运行,以便更好地理解这些 pod。我们可以看到这些 nginx pod 在默认命名空间中的展示情况。
请注意,为了简便起见,我们在default命名空间中进行测试。你可以添加-n标志,以便在其他命名空间中处理部署和 pods。请参阅第四章,应用调度与生命周期管理,了解 Kubernetes 中应用部署的工作原理。接下来,尝试执行以下命令:
kubectl get pods
输出将返回default命名空间中所有可用的 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。我们将在本章接下来的部分中了解NodePort和LoadBalancer。 -
port是服务提供的端口。 -
target-port是容器上将流量重定向到的端口。
重要说明
理解这些命令标志将帮助你更顺畅地使用它们;我建议你记住这个命令,以便在实际的 CKA 考试中能够快速回忆起来。你也可以参考以下链接(kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#expose)来了解其他标志是否能帮助你。
前一个命令的输出应该类似于以下内容:
service/melon-service exposed
基于这个输出,前面的命令成功执行。现在,让我们进入默认命名空间,使用kubectl get svc命令查看所有可用的服务——这将给出以下输出:

图 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
如你所见,在这里我们定义的 YAML 文件中并没有什么特别的内容。我们可以通过使用以下命令导出其 YAML 定义来对比服务定义:
kubectl get svc melon-service -o yaml
我们将能够看到导出的输出,如下所示:

图 7.10 – 默认命名空间中 nginx 服务的定义
将导出的定义与我们在本节中使用 kubectl 和 YAML 定义所讲解的内容进行比较,将有助于你更好地理解 Kubernetes 中的服务。现在,让我们来看看 Kubernetes 中另一个重要的服务——NodePort。
NodePort
NodePort 在 Kubernetes 节点上打开端口,这些节点通常是虚拟机。NodePort 通过节点的 IP 暴露访问,并通过打开的端口使应用程序可以从 Kubernetes 集群外部访问。网络流量从端口转发到服务。kube-proxy 在每个节点上分配一个 30000 到 32767 范围内的端口——其工作方式如图所示:

图 7.11 – Kubernetes 中的 NodePort
通过前面的图示,我们更深入地了解了 NodePort 如何与 pods 一起工作。接下来,我们使用以下命令创建一个名为 webfront-app、replicas 数量为 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
请注意,如果你没有提供目标端口,则假定它与容器端口相同。还要注意,如果没有提供节点端口,系统会自动分配一个在 30000 到 32767 范围内的空闲端口。
现在,让我们检查一下我们刚刚创建的所有服务。由于我们在之前的命令中没有指定名称,服务名称假定与应用程序名称相同:
kubectl get svc webfront-app -o wide
输出应如下所示:

图 7.12 – 默认命名空间中的 webfront-app NodePort
从前面的输出中,我们可以看到端口暴露在31400,这个端口位于节点的30000到32767范围内,目标端口是80,它在容器级别已开启。因此,使用以下命令获取节点 IP:
kubectl get node -o wide
输出的关键部分如下:

图 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
在实际的 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
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 输出
由于我在 WSL2 上的 Docker Desktop 中测试 LoadBalancer 时,它不受支持 – 前面的输出显示EXTERNAL-IP为localhost。然而,当我在 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 – 获取跨不同命名空间的所有服务
前面的截图列出了跨命名空间的服务,以及它们的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 – 检查服务详细信息
对于端点,我们可以使用以下命令来检查服务的端点:
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 – 获取跨不同命名空间的所有端点
同样的原理也适用于按命名空间列出所有端点。当你想查看特定服务时,可以使用以下命令:
kubectl get ep <service-name> -n <namespace>
我们已经讨论了如何在 Kubernetes 中处理服务和端点,涉及了 pod 到服务的通信。现在,让我们进入下一节,讨论节点间的通信。
节点间通信
在一个集群内,每个节点都会通过kubelet代理注册到主节点,并且每个节点会被分配一个节点 IP 地址,以便它们之间能够进行通信。
要验证这一点,可以使用kubectl get node -o wide命令查看每个节点的内部 IP。输出结果类似于以下内容,在其中你会注意到工作节点的internal-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 网络
上面的图演示了 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中看到的内容,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 定义中,我们知道 apiVersion、kind、metadata 和 spec 字段是必填的。接下来,我们还需要一个 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 记录,这些也是最常见的记录类型:
-
A 或 AAAA 记录用于正向查找,将 DNS 名称映射到 IP 地址。A 记录将 DNS 名称映射到 IPv4 地址,而 AAAA 记录则允许将 DNS 名称映射到 IPv6 地址。
-
SRV 记录用于端口查找,以便在服务和主机名之间建立连接。
-
172.0\. 0.10将存储在10.0\. 0.172.in-addr.arpaDNS 区域下。
了解这些基本的 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 共享网络时
当你确定已经切换到 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 – 当多个容器共享一个网络时
由于这是一个部署,我们可以使用典型的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 – 当多个容器共享一个网络时
之前的示例还演示了那四个 CoreDNS 副本是相同的。我们可以使用kubectl describe命令仔细查看这四个coredns pod 中的任何一个。以下命令为示例:
k describe pod coredns-6d4b75cb6d-4h89j -n kube-system
输出应如下所示:

图 7.26 – 当多个容器共享一个网络时
从上述输出中,我们可以看到 CoreDNS 正在使用Corefile进行配置。它位于以下位置:
/etc/coredns/Corefile
我们可以使用kubectl get configmaps命令来检查Corefile的内容。以下是操作步骤:
kubectl get configmaps -n kube-system
输出应如下所示:

图 7.27 – 当多个容器共享一个网络时
前述命令显示有一个名为coredns的configmap,所以我们可以使用kubectl describe configmap命令来查看其内容:
k describe configmap coredns -n kube-system
以下输出将展示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-apps或myapp.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 共享一个网络时
从图中,我们知道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 共享一个网络时
你可以在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 共享一个网络时
或者,你也可以使用以下命令来实现相同的结果。请注意,我们在命令中添加了两个rm标志,这将确保一旦退出 shell,Pod 被删除。我们还使用--直接执行nslookup命令。这样,它允许我们进行快速测试,在实际的 CKA 考试中非常有用。命令如下所示:
kubectl run -it sandbox --image=busybox:latest --rm --restart=Never -- nslookup 10.1.0.9
输出应该如下所示:

图 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 共享网络时
上面的截图显示 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 共享网络时
上面的截图显示了 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 共享网络时
上面的截图证明了 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 默认命名空间中的服务
从前面的输出中,我们可以通过使用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 名称
根据前面的输出,我们可以看到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 名称
前面的截图显示了 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-0 和 worker-0;请完成以下模拟场景。
场景 1
部署一个新的 nginx 部署,使用最新的 nginx 镜像,并在名为 packt-app 的命名空间中创建两个副本。容器通过端口 80 暴露。创建一个 ClusterIP 类型的服务,并在同一命名空间中创建一个 sandbox-nginx Pod,使用 curl 进行调用以验证与 nginx 服务的连接性。
场景 2
通过 NodePort 服务类型暴露 nginx 部署;容器通过端口 80 暴露。使用 test-nginx Pod 通过 curl 进行调用,以验证与 nginx 服务的连接性。
场景 3
使用 wget 或 curl 从与该节点处于同一网络中的机器进行调用,以通过正确的端口验证与 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.io/docs/concepts/services-networking/service/ -
Ingress:
kubernetes.io/docs/concepts/services-networking/ingress/
第三部分:故障排除
本部分涵盖与 Kubernetes 故障排除相关的主题,包括集群级和应用程序级的日志记录和监控,集群组件和应用程序故障排除,安全性以及网络故障排除。本部分内容大约占 CKA 考试内容的 30%。
本书的这一部分包含以下章节:
-
第八章,监控和日志记录 Kubernetes 集群和应用程序
-
第九章,集群组件和应用程序故障排除
-
第十章,安全性和网络故障排除
第八章:监控和记录 Kubernetes 集群及应用
本章描述了如何监控 Kubernetes 集群组件和应用程序,并获取基础设施级、系统级和应用级日志,以供日志分析或进一步排查故障。结合接下来的两章,关于排查集群组件和应用程序故障以及排查 Kubernetes 安全性和网络问题,它覆盖了 CKA 考试内容的 30%。
本章将涵盖以下主题:
-
在集群节点上进行监控
-
在 Kubernetes 集群上监控应用
-
在集群节点和 Pod 级别管理日志
-
管理容器
stdout和stderr日志
技术要求
要开始,您需要确保本地机器符合以下技术要求:
-
一个兼容的 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 集群中的工作原理
用户可以使用 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 插件列表
从前面的截图中,我们可以看到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 和服务
你可以使用的另一个命令如下:
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标志接受cpu或memory作为值,结果将按asc或desc排序。该命令如下所示:
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
请注意,在前面的截图底部有一个Events部分,显示了与此 Pod 相关的最近事件日志。我们将进一步查看Events部分:

图 8.5 – nginx Pod 的事件
这里的事件包括 Kubernetes 中的一系列事件,例如这些:
-
Pod 被调度到名为
minikube的工作节点。 -
容器镜像从容器注册表中拉取。
-
kubelet 代理为包含
nginx容器的 Pod 提供配置。 -
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
尽管前面的截图包含了类似的信息块,但每个 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 获取事件
你可以使用以下命令按时间戳列出事件:
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
了解节点规格将帮助您理解节点的先前配置情况。现在,让我们来看一下如何使用 kubectl describe node 命令获取一些快捷而有用的信息。
检查节点状态
使用 kubectl describe 命令,我们可以获得关于节点的一些常规信息。注意,它还包含一个 events 部分,通常记录节点事件。为了获取更多节点状态信息,通常使用以下命令,以名为 minikube 的节点为例:
kubectl get node minikube -o wide
输出类似于以下内容:

图 8.9 – kubectl 获取节点输出
从前面的截图中,如果将 kubectl get node 命令与带有 -o wide 标志的命令进行比较,您会发现它提供了有关镜像、内核版本以及容器运行时的额外信息,这在我们需要快速获取信息时非常方便。
管理容器的标准输出(stdout)和标准错误(stderr)日志
在 Unix 和 Linux 操作系统中,有三种 I/O 流,分别为 STDIN、STDOUT 和 STDERR。在这里,我们将讨论 Linux 容器中的 STDOUT 和 STDERR,这通常是 kubectl logs 命令向我们展示的内容。
STDOUT 通常是命令的正常输出,而 STDERR 则通常用于输出错误消息。Kubernetes 使用 kubectl logs <podname> 命令记录 STDOUT 和 STDERR。当我们使用该命令记录本章部署的 nginx Pod 时,输出如下所示:
kubectl logs nginx
输出应如下所示:

图 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 容器的日志
如果您想要实时查看日志,可以使用 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-0 和 worker-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,请参阅以下内容:
-
我们建议更新 Docker Desktop 到最新版本,并创建一个 Docker Desktop 本地 Kubernetes 集群。查看这篇文章,了解如何使用 Docker Desktop 设置本地 Kubernetes 集群:
docs.docker.com/desktop/kubernetes/。 -
我们还建议使用Windows Subsystem for Linux 2(WSL 2)来测试环境。请参考这篇文章,了解如何安装 WSL(
docs.microsoft.com/en-us/windows/wsl/install),以及这篇文章,了解如何设置 Docker Desktop 的 WSL 2 后端:docs.docker.com/desktop/windows/wsl/。
设置完成后,你可以使用以下命令检查你是否已设置到正确的 Kubernetes 集群:
alias k=kubectlk config current-context
上面的命令将输出当前集群的信息。在我们的案例中,类似以下输出,因为我们在 Windows 上,且使用 Docker Desktop 创建了一个 Kubernetes 本地集群:
docker desktop
如果你在本书中一直跟着我们的演示,你会注意到大多数演示都在minikube集群上进行。在这种情况下,输出将如下所示:
minikube
你可能已经使用本地机器连接到不同的 Kubernetes 集群——你可以使用kubectl config view命令查看当前连接的是哪个集群:

图 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 – 渲染集群信息
如果你需要进一步的信息来进行调试和诊断,可以使用以下命令:
kubectl cluster-info dump
上述命令输出的内容非常庞大,包含了大量信息——因此我们只展示了以下截图中的关键信息:

图 9.3 – Kubernetes 集群日志
上述截图显示了日志信息,对于找出根本原因非常有帮助。虽然我们可以从控制平面和集群日志中获取有价值的信息,但通常会经常出现与运行在其上的工作负载相关的错误,这可能是由于节点的可用性或能力问题引起的。接下来,我们将看看如何排查节点问题。
检查节点
使用以下命令检查节点,将帮助你获取当前集群和节点的状态:
kubectl get nodes
输出应如下所示:

图 9.4 – Kubernetes 节点信息
上述截图显示,当前唯一的工作节点处于 Ready 状态。当你有多个节点时,输出中会列出节点信息。
ROLES 列显示的是节点的角色——它可以是 control-plane、etcd 或 worker:
-
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 – kubectl describe node 输出信息
为了从前面的命令中获得最大的价值,我们可以查看 Conditions 部分,其内容应如下所示:

图 9.6 – 获取节点状态信息
前述截图展示了详细的节点状态信息,正如我们在本章中所解释的那样。你还可以从相同的输出中获取已分配的资源信息,内容如下:

图 9.7 – 获取节点资源消耗信息
上述截图中的值用于了解当前集群在 CPU、内存和存储方面的消耗情况。
相同的输出还帮助你概览当前集群中各个 Pod 的资源请求和限制,如下所示的截图所示:

图 9.8 – 获取 Pod 资源消耗信息
如果你希望以更结构化的方式查看此输出,可以使用以下命令,使其看起来更像一个yaml文件:
kubectl get node docker-desktop -o yaml
输出如下:

图 9.9 – 获取节点信息的 YAML 格式
在前面的输出中,特别注意名为nodeInfo的部分,它概述了操作系统镜像、架构、内核版本、kubeProxy版本、kubelet版本和操作系统:

图 9.10 – 获取 Pod 资源消耗信息
如果你不需要查看 Kubernetes 节点的完整概况,而是专注于获取当前在 Kubernetes 集群中运行的进程的内存,可以在 Kubernetes 节点中运行以下命令:
top
输出已优化,应该看起来类似于以下内容:

图 9.11 – 检查进程的消耗信息
正如我们在本章前面所解释的,DiskPressure也是工作节点健康状态的一个关键因素。你可以使用以下命令检查可用的磁盘存储:
df -h
输出看起来类似于以下内容:

图 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 – 系统保留进程
当你看到任何不是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 – 节点相关信息
从 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 信息
仅知道前面的输出中的 pod 配置信息还不够。当 pod 因某种原因未启动时,日志会更加有用,特别是当 Events 部分显示为 none(如前面截图所示)。我们可以使用以下命令查看 pod 日志:
kubectl logs kube-proxy-9rfxs -n kube-system
前面的命令会打印出类似以下内容的日志,这将为你提供更多关于发生了什么的详细信息:

图 9.16 – Pod 日志信息
在处理完主节点故障排除后,如果工作节点需要排查,我们应首先排查 kubelet 代理 —— 让我们在下一节深入了解这一点。
排查 kubelet 代理
在检查节点状态之后,如果你还没有登录到工作节点,可以通过 SSH 连接该工作节点,并使用以下命令检查 kubelet 状态:
systemctl status kubelet
输出应如下所示:

图 9.17 – kubelet 代理状态和日志
前面截图中的重要部分是 kubelet 的状态,以下截图中可以看到:

图 9.18 – kubelet 代理状态
如果状态不是 active (running),我们可以使用 journalctl 获取工作节点上 kubelet 服务的日志。以下命令显示了如何执行此操作:
journalctl -u kubelet.service
输出将打印出类似以下内容的日志详情:

图 9.19 – 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
为了充分利用输出信息,请注意NAMESPACE、READY和STATUS列——它们会告诉你在哪个命名空间中 Pods 正在运行以及有多少副本。如果你确定某些命名空间中的某些 Pods 发生了故障,那么可以继续查看下一节,检查命名空间事件。
检查命名空间事件
要检查命名空间事件,你可以使用以下命令查看default命名空间中部署的应用程序发生了什么:
kubectl get events
输出应如下所示:

图 9.22 – Kubernetes 事件
在前面的截图中,我们有一些有价值的列:
-
TYPE列显示事件类型——它可以是Normal或Warning。 -
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 事件按时间戳分组
类似地,如果我们想查看名为app的命名空间中的事件,可以使用以下命令:
kubectl get events -n app --sort-by=.metadata.creationTimestamp
输出应如下所示:

图 9.24 – Kubernetes 事件按命名空间和时间戳分组
上述输出证明我们能够按命名空间打印事件,并按创建时间戳进行排序。
到此为止,我们已经确定了问题发生在哪个 Pod 或容器中。接下来,让我们更仔细地查看故障的 Pod。
故障 Pod 的故障排除
一旦我们缩小范围,确定了哪个 Pod 出现故障,就可以使用命令获取该命名空间中运行的 Pod 状态。以下是获取名为old-busybox、位于app命名空间中的故障 Pod 的命令:
kubectl get pod old-busybox -n app
你的输出将类似于以下内容:

图 9.25 – 获取命名空间中的故障 Pod
我们可能会注意到STATUS显示有一个镜像错误(ErrImagePull)。现在,我们可以使用kubectl describe命令获取更多细节:
kubectl describe pod old-busybox -n app
上述命令打印出故障部分的概述,如下图所示:

图 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.yaml的yaml文件:
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 规范
我们可以使用以下命令本地编辑导出的文件:
vim my-old-pod.yaml
您会看到,当您处于EDIT模式时,可以按如下方式编辑 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 describe和kubectl 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,请注意以下事项:
-
我们推荐将 Docker Desktop 更新到最新版本,并创建一个本地
docker-desktopKubernetes 集群。参考这篇文章了解如何使用 Docker Desktop 设置本地 Kubernetes 集群:docs.docker.com/desktop/kubernetes/. -
我们还推荐使用Windows Subsystem for Linux 2(WSL 2)来测试环境——参考这篇文章了解如何安装 WSL 2(
docs.microsoft.com/en-us/windows/wsl/install)以及以下文章了解如何设置 Docker Desktop WSL 2 后端(docs.docker.com/desktop/windows/wsl/)。
排查 RBAC 故障
排除与 Kubernetes 安全相关的任何问题似乎有点矛盾。事实上,Kubernetes 的大多数安全层涉及使用工具来帮助保护 Kubernetes 的 4C 层,这包括安全扫描、管理和保护。要了解更多关于 4C 层的信息,请参考 第六章,Kubernetes 安全性。当涉及到排除安全问题时,CKA 考试最常见的是关于 Kubernetes 的 RBAC 问题。因此,我们将在本节中重点展示如何排除 Kubernetes 中 RBAC 失败的示例。
启动 minikube 集群
这一部分不包含在 CKA 考试中,但如果你按照 第二章,安装与配置 Kubernetes 集群 中的说明自己部署minikube集群时,可能会遇到这个问题。每当你尝试在全新的 Linux 虚拟机上安装一个新的minikube集群时,你需要应用我们在该章节中讨论的内容。
安装完minikube工具后,你可以使用以下命令启动本地集群:
minikube start
你可能会在输出中看到以下错误:

图 10.1 – 驱动程序不健康
你下意识地选择正确的驱动程序并使用 sudo 命令,如下所示:
sudo minikube start --driver=docker
结果,你可能会看到以下输出:

图 10.2 – 每个命名空间的服务账户
之前的输出是由于 Docker 根权限问题。最佳实践是以非 root 用户管理 Docker,以避免这个问题。为了实现这一点,我们需要将一个用户添加到名为docker的组中:
- 创建
docker组:
sudo groupadd docker
- 将你的用户添加到名为
docker的组中:
sudo usermod -aG docker $USER
- 在此,你需要重新登录或重启 Docker 服务器,以便重新评估你的组成员资格。然而,在 Linux 操作系统上,我们应该通过以下命令激活对组的更改:
newgrp docker
- 下次登录时,如果你希望 Docker 在启动时自动启动,可以使用以下命令:
sudo systemctl enable docker.service
sudo systemctl enable containerd.service
- 完成上述步骤后,你应该能够通过以下命令使用 Docker 驱动程序启动
minikube:
minikube start --driver=docker
如果你能够看到类似以下的输出,说明之前的minikube start命令已经成功创建了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 配置
如前所述,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-brqfl的coredns pod 的日志,输出类似于以下内容:

图 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 服务
从前面的输出中,我们可以通过以下命令更详细地查看nginx-svc:
kubectl get svc nginx-svc –n app -o wide
前述命令的输出如下:

图 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 名称
如果你想通过一个与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 主页面
上述截图显示了 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
BusyBox 提供了一些命令,但curl并不在其中。所以,现在让我们获取一个支持curl的nginx镜像。要了解 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-0和worker-0。请完成以下模拟场景。
场景 1
安装最新版本的kubeadm,然后在master-0节点上创建一个基本的 kubeadm 集群,并获取节点信息。
-
更新
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 -
首先更新 apt 软件包索引,然后安装
kubelet和kubeadm:sudo apt-get update sudo apt-get install -y kubelet kubeadm -
此时,如果你还没有安装
kubectl,你也可以一并安装kubelet、kubeadm和kubectl:sudo apt-get update sudo apt-get install -y kubelet kubeadm kubectl -
使用以下命令固定你正在安装的工具版本:
sudo apt-mark hold kubelet kubeadm kubectl -
你可以使用
kubeadm init命令以普通用户身份初始化控制平面,并通过以下命令从你的 master 节点机器获取 sudo 权限:sudo kubeadm init --pod-network-cidr=192.168.0.0/16 -
在 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-0和worker-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-0 和 worker-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,并包含一个名为 busybox 的 initContainer。
创建一个名为 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-0 和 worker-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-0和worker-0,请完成以下模拟场景。
场景 1
在一个名为packt-ns的新命名空间中,创建一个新的服务账户,命名为packt-sa。
使用以下命令在目标命名空间中创建一个新的服务账户:
kubectl create sa packt-sa -n packt-ns
场景 2
创建一个名为packt-role的 Role,并将其与 RoleBinding packt-rolebinding 绑定。为packt-sa服务账户分配list和get权限。
使用以下命令在目标命名空间中创建一个集群角色:
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-0和worker-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
从与该节点处于同一网络中的机器使用wget或curl发起请求,验证通过正确端口连接 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-0和worker-0。请完成以下模拟场景。
场景 1
列出当前集群中所有可用的 pod,并找出最消耗 CPU 的 pod。将其名称写入max-cpu.txt文件。
使用以下命令:
kubectl top pod -- all-namespaces --sort-by=cpu > max-cpu.txt


浙公网安备 33010602011771号