KCNA 认证指南(一)
第一章从云到云原生和 Kubernetes,讲述了计算技术在过去 20 多年中的演变。它解释了云是什么,它是如何出现的,以及随着容器技术的引入,IT 环境如何发生了变化。你将学习到 IaaS、PaaS、SaaS 和 FaaS 等基础概念,并初步了解 Kubernetes。第二章CNCF 和 Kubernetes 认证概述,介绍了云原生计算基金会(CNCF)及其母组织——Linux 基金会。将分享这
原文:
annas-archive.org/md5/3c59e81954943f2641132e7cf4e8e00d译者:飞龙
序言
迄今为止,Kubernetes 已发布超过 20 个版本,甚至在 2014 年首次出现几年后,仍然如火如荼。Kubernetes 与其他云原生技术一起,正在深刻重塑一些世界上最先进和最具进步性的公司中的 IT 格局。
根据Linux 基金会的第十届年度开源职位报告,云计算和容器被列为最受欢迎的技能类别。因此,精通 Kubernetes 和云原生技能对于提升你的职业生涯并在一些顶尖企业工作至关重要。获得 Kubernetes 和云原生认证助理资格可以帮助你脱颖而出,并证明你在该领域的能力。
本书将带你从零开始进行云原生之旅,教授 Kubernetes 的理论和实践方面。你将学习如何使用 Docker 构建、配置和运行容器;如何启动最小化的 Kubernetes 集群;如何使用 Kubernetes 部署、配置和管理容器化应用;如何使用 CI/CD 自动化软件交付,等等。你将打下坚实的基础,以便第一次尝试通过 KCNA 考试,并全面了解当今的行业标准。
本书分为五个部分:
-
云时代
-
执行容器编排
-
学习 Kubernetes 基础
-
探索云原生
-
KCNA 考试与下一步
在第一部分,你将了解云原生计算,解释行业如何发展以及为什么现代应用程序通常运行在容器中。接下来,你将学习 Docker,并动手在本地运行容器。
在第三部分,最大的一部分,你将学习 Kubernetes:它的特性、架构、API 和组件。你将找到最佳实践、回顾问题以及大量的实践作业来支持你的学习旅程。在第四部分,我们将重点介绍云原生架构以及来自云原生生态系统的其他技术。我们将看到如何监控、观察和交付云原生应用。最后,在第五部分,你将找到模拟考试和 KCNA 考试通过技巧,以及一些关于获得认证后如何继续前进的建议。
本书适合谁
Kubernetes 和云原生认证助理(KCNA)是一个为有意通过展示对 Kubernetes 基础知识和技能的理解而晋升到专业级别的候选人设计的预备专业认证。
一名认证的 KCNA 将确认其对整个云原生生态系统的概念性知识,特别是 Kubernetes。KCNA 将展示候选人对 Kubernetes 和云原生技术的基本知识,包括如何使用基本的kubectl命令部署应用、Kubernetes 的架构、理解云原生的全貌和项目,以及了解云原生安全的基础知识。
无论是刚毕业的 IT 专业学生、开发人员、系统管理员,还是 DevOps 工程师,不论经验如何,任何对学习 Kubernetes 和云原生技术感兴趣的人都会觉得本书既实用又易于跟随。要求具备 IT 基础知识(Git)、操作系统以及命令行界面的基础,但无需具备 Kubernetes、Docker 或云原生技术的先前知识即可开始学习。
本书内容概述
第一章,从云到云原生和 Kubernetes,讲述了计算技术在过去 20 多年中的演变。它解释了云是什么,它是如何出现的,以及随着容器技术的引入,IT 环境如何发生了变化。你将学习到 IaaS、PaaS、SaaS 和 FaaS 等基础概念,并初步了解 Kubernetes。
第二章,CNCF 和 Kubernetes 认证概述,介绍了云原生计算基金会(CNCF)及其母组织——Linux 基金会。将分享这些基金会的背景、它们的起源,以及它们生态系统中的项目。这一章将讲解 CNCF 社区、治理结构、云角色和 Kubernetes 认证路径。
第三章,容器入门,对容器技术进行了更深入的探讨,深入了解容器技术及其生态系统,并介绍了常用的 Docker 工具。本章还包括实践任务。
第四章,探索容器运行时、接口和服务网格,带你更深入地探索容器运行时、网络和接口,并了解服务网格。你将学习容器如何通过网络相互通信,Kubernetes 中有哪些容器接口,并了解服务网格及其应用。
第五章,使用 Kubernetes 编排容器,开始覆盖 KCNA 认证中最重要且可能是最难的部分——Kubernetes 基础。你将了解 Kubernetes 的功能及架构基础,它的 API、组件,以及最小的可部署单元——Pod。实践部分包括通过 minikube 本地安装 Kubernetes。
第六章,使用 Kubernetes 部署和扩展应用,将进一步探索 Kubernetes 的功能和丰富的生态系统。本章概述了其他 Kubernetes 资源及其用途;讨论如何使用 Kubernetes 实现应用的自愈和扩展,如何使用 Kubernetes 服务发现,以及如何使用 Kubernetes 运行有状态的工作负载。更多的 Kubernetes 实践练习是本章的重要组成部分。
第七章,使用 Kubernetes 进行应用部署和调试,展示了如何控制工作负载在 Kubernetes 上的部署、调度器如何工作,以及如何调试在 K8s 上运行的应用程序。本章同时涉及了 Kubernetes 基础知识和 KCNA 考试的云原生可观察性领域的内容。
第八章,遵循 Kubernetes 最佳实践,讨论了 Kubernetes 的网络和流量控制的网络策略、使用基于角色的访问控制(RBAC)限制访问、将 Helm 作为 K8s 的包管理工具等。本部分的最后一章包括了一些实践练习。
第九章,理解云原生架构,更详细地探讨了云原生的各个方面。本章分享了云原生及其架构的核心概念。此章节还涵盖了 KCNA 考试云原生架构领域的其他要求。
第十章,在云中实施遥测和可观察性,强调了基于观察来监控和优化云原生应用程序,以确保最佳性能并考虑成本的必要性。本章进一步涵盖了 KCNA 的云原生可观察性领域的要求。
第十一章,自动化云原生应用交付,讲解了云原生应用生命周期。你将学习云原生应用开发和交付的最佳实践,并了解自动化如何帮助更好地开发和更快地交付。
第十二章,通过模拟考试备考 KCNA 考试,分享了一些通过考试的技巧,并包括了两场模拟考试,帮助在最后的准备阶段测试知识。
第十三章,前进的道路,以关于如何推进和下一步该做什么的建议结束本书,帮助你成功开启云原生职业生涯。
为了最大限度地利用本书
你将需要安装多个工具,因此需要一个具有管理员权限的系统。
| 本书涵盖的软件/硬件 | 操作系统要求 |
|---|---|
| Kubernetes | Windows、macOS 或 Linux(推荐使用 macOS 或 Linux) |
| minikube | |
| Docker | |
| Prometheus |
如果你正在使用本书的数字版,我们建议你自己输入代码,或者通过本书的 GitHub 仓库访问代码(下文会提供链接)。这样做有助于避免与复制粘贴代码相关的潜在错误。
下载示例代码文件
您可以从 GitHub 下载本书的示例代码文件,链接为 github.com/PacktPublishing/Becoming-KCNA-Certified。如果代码有更新,GitHub 仓库中的代码也会进行更新。
我们还提供了来自我们丰富图书和视频目录中的其他代码包,您可以在 github.com/PacktPublishing/ 进行查看!
下载彩色图片
我们还提供了包含本书中截图和图表的彩色图像 PDF 文件,您可以在此处下载:packt.link/OnZI3。
使用的约定
本书中使用了多种文本约定。
文本中的代码:表示文本中的代码单词、表格名称、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入等。以下是一个示例:“让我们删除我们在本章开头创建的旧 nginx-deployment。”
代码块设置如下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: kcna-pv-claim
spec:
storageClassName: standard
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
当我们希望引起您对某个代码块中特定部分的注意时,相关的行或项会以粗体显示:
Normal Scheduled 85s default-scheduler Successfully assigned kcna/liveness-exec to minikube
Normal Pulled 81s kubelet Successfully pulled image "k8s.gcr.io/busybox" in 3.4078911s
Warning Unhealthy 41s (x3 over 51s) kubelet Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
Normal Killing 41s kubelet Container liveness failed liveness probe, will be restarted
Normal Pulling 11s (x2 over 85s) kubelet Pulling image "k8s.gcr.io/busybox"
粗体:表示术语、重要单词或词组。例如,重要要点可以用 粗体 显示。
提示或重要说明
如下所示
联系我们
我们欢迎读者的反馈。
一般反馈:如果您对本书的任何方面有疑问,请通过电子邮件联系我们,邮箱地址是 customercare@packtpub.com,并在邮件主题中注明书名。
勘误表:虽然我们已尽力确保内容的准确性,但仍难免出现错误。如果您在本书中发现错误,我们将非常感激您能向我们报告。请访问 www.packtpub.com/support/errata 并填写表格。
盗版:如果您在互联网上发现我们作品的任何非法版本,无论是何种形式,我们将非常感激您能提供相关位置或网站名称。请通过电子邮件 copyright@packt.com 将链接发送给我们。
如果您有兴趣成为作者:如果您在某个领域具有专业知识,并且有兴趣撰写或参与书籍的编写,请访问 authors.packtpub.com.
分享您的想法
阅读完 《成为 KCNA 认证专家》 后,我们希望听到您的想法!请点击这里直接进入此书的亚马逊评论页面 并分享您的反馈。
您的评论对我们和技术社区都非常重要,它将帮助我们确保提供优质内容。
下载本书的免费 PDF 副本
感谢您购买本书!
您是否喜欢随时随地阅读,但又无法随身携带纸质书籍?或者您的电子书购买的格式与您选择的设备不兼容?
不用担心,现在每本 Packt 的书都可以免费获得一份无 DRM 的 PDF 版本。
随时随地,在任何设备上阅读。直接从您喜爱的技术书籍中搜索、复制和粘贴代码到您的应用程序中。
这些好处还不止于此,您还可以独享折扣、新闻简报以及每天在您的收件箱里获取的优质免费内容。
按照以下简单步骤获取这些好处:
- 扫描下方的 QR 码或访问以下链接
packt.link/free-ebook/9781804613399
-
提交您的购买证明
-
就这样!我们将直接将您的免费 PDF 和其他好处发送到您的电子邮件中
第一部分:云时代
在本部分中,您将快速了解云计算自问世以来的演变,传统 IT 在云之前的运作方式,以及今天的云原生景观。我们将讨论云原生计算基金会(CNCF)、Linux 基金会,并查看它们的项目和认证。
本部分包括以下章节:
-
第一章,从云到云原生和 Kubernetes
-
第二章,CNCF 和 Kubernetes 认证概述
第一章:从云计算到云原生与 Kubernetes
本章将展示过去 20 多年中计算如何演变,云计算是什么及其如何出现,以及 IT 环境随着容器的引入发生了怎样的变化。你将学习到基础设施即服务(IaaS)、平台即服务(PaaS)、软件即服务(SaaS)和函数即服务(FaaS)等基础知识,同时还会了解从单体架构到微服务架构的过渡,并初步了解 Kubernetes。
本章并没有直接对应特定的 KCNA 考试目标,但这些主题对任何希望将自己职业与现代基础设施挂钩的人来说至关重要。如果你已经熟悉基本术语,可以通过直接查看回顾问题来快速验证你的知识。如果不熟悉,不要惊讶于本章没有深入探讨,因为这是一个入门章节,我们将在后续章节中深入探讨所有主题。
本章将涵盖以下主题:
-
云计算与云之前(B.C.)
-
云计算的演变与云原生
-
容器与容器编排
-
单体应用与微服务应用
-
Kubernetes 及其起源
让我们开始吧!
云计算与云之前(B.C.)
云计算引发了一场重大革命并加速了创新,但在我们学习云之前,先来看看云时代之前的做法。
在使用云计算这个术语之前,一台物理服务器通常只能同时运行一个操作系统(OS)。这些系统通常只承载一个应用程序,这意味着两件事:
-
如果一个应用程序不被使用,那么它运行的服务器上的计算资源就会浪费
-
如果一个应用程序非常活跃并且需要更大的服务器或更多服务器,通常需要几天甚至几周的时间来采购、交付、布线并安装新硬件。
接下来,让我们看看计算中的一个重要方面——虚拟化。
虚拟化
虚拟化技术和虚拟机(VM)最早出现在 1960 年代,但直到 2000 年代初,像 XEN 和基于内核的虚拟机(KVM)这样的虚拟化技术才开始成为主流。
虚拟化允许我们在一台物理服务器上通过使用虚拟机监控程序(hypervisor)运行多个虚拟机(VM)。虚拟机监控程序是一种作为硬件资源(如 CPU 和 RAM)模拟器的软件。它实际上通过将底层物理服务器的处理器时间和内存划分给多个虚拟机,从而允许你共享这些资源。
这意味着每个虚拟机将非常类似于物理服务器,但使用虚拟 CPU、内存、磁盘和网络卡,而非物理组件。每个虚拟机还将运行操作系统,并可以在其上安装应用程序。下图展示了在同一物理服务器上运行两个虚拟机的虚拟化部署:
图 1.1 – 传统与虚拟化部署的对比
这种在所谓的客户虚拟机之间共享硬件资源的概念,使得硬件的利用变得更加高效,减少了计算资源的浪费。也就是说,我们可能不需要为了运行另一个应用程序而购买一台全新的服务器。
虚拟化带来的明显好处如下:
-
需要更少的物理硬件
-
需要更少的数据中心人员
-
更低的采购和维护成本
-
更低的能耗
此外,配置一个新的虚拟机(VM)只需几分钟,而不是等待几天或几周来购买新硬件。然而,要超越公司数据中心已有硬件的容量,我们仍然需要订购、配置并布线新的物理服务器和网络设备——但随着云计算的引入,这一切都发生了变化。
云
从最基础的层面来看,云就是按需虚拟化。它允许我们在客户请求时,作为一种服务生成可通过网络访问的虚拟机(VM)。
云计算
这就是将计算资源作为服务提供的方式,其中实际的硬件由云服务提供商拥有和管理,而非企业的 IT 部门。
云计算引发了计算领域的重大革命。如今,构建和运行应用程序以及虚拟机不再需要购买和管理自己的硬件。云服务提供商全面负责硬件采购、安装和维护,并通过在共享硬件上为数百上千个客户提供安全的服务来确保资源的高效利用。每个客户仅需为其所使用的资源付费。如今,常见的云类型包括以下三种:
-
公共云 – 最受欢迎的类型。公共云由第三方公司运营,任何付费客户都可以使用。公共云通常同时为成千上万的组织提供服务。公共云提供商的例子包括亚马逊网络服务(AWS)、微软 Azure 和 谷歌云平台(GCP)。
-
私有云 – 仅供一个通常较大的组织或企业使用。其操作和维护可能由该组织本身或私有云提供商完成。例子包括 Rackspace 私有云和 VMware 私有云。
-
混合云 – 这是公共云与私有云的结合,适用于某个组织拥有私有云,但同时也使用一些公共云服务的情况。
然而,云不仅仅是通过网络访问的虚拟机。云服务提供商提供了数十种、数百种服务。今天,您几乎可以立即请求并使用网络附加存储、虚拟网络设备、防火墙、负载均衡器、带有 GPU 或专用硬件的虚拟机、托管数据库等服务。
现在,让我们更详细地了解云服务如何交付和消费。
云计算和云原生的发展
除了今天可以找到的各种云服务外,云服务的提供方式也有所不同。通常可以区分四种云服务交付模型,以满足不同的需求:
-
IaaS – 最灵活的模型,提供基本服务:虚拟机、虚拟路由器、块设备、负载均衡器等等。这个模型也假设客户承担更多责任。IaaS 的用户可以访问他们的虚拟机,必须配置操作系统,安装更新,并设置、管理和保护他们的应用程序。AWS 弹性计算云(EC2)、AWS 弹性块存储(EBS)和 Google Compute Engine 虚拟机都是 IaaS 的示例。
-
PaaS – 通过免去安装操作系统更新或进行任何低级别维护的需求,帮助客户专注于应用程序的开发和管理。作为 PaaS 客户,您仍然需要负责您的数据、身份与访问以及应用程序生命周期。示例包括 Heroku 和 Google App Engine。
-
SaaS – 将责任进一步移离客户。通常,这些是完全托管的应用程序,即开即用,例如 Slack 或 Gmail。
-
FaaS – 一种在 2010 年左右出现的较新交付模型。如今也被称为无服务器。FaaS 的客户负责定义由事件触发的功能。这些功能可以使用流行的编程语言编写,客户无需担心服务器或操作系统管理、部署或扩展等问题。FaaS 的示例包括 AWS Lambda、Google Cloud Functions 和 Microsoft Azure Functions。
这些模型听起来可能有点复杂,让我们用汽车和交通工具做一个简单的类比。
本地传统数据中心 就像是拥有自己的车。你购买了它,并且负责它的保险和维护、坏件的更换、定期检查等等。
IaaS 更像是租车一段时间。你支付月租费,开车,给车加油,洗车,但你实际上并不拥有这辆车,当你不再需要它时,你可以把它还回去。
PaaS 可以与共享汽车进行类比。你不拥有汽车,不需要洗车、维护,甚至大多数时候不需要加油,但你仍然自己开车。
按照这个类比,SaaS 就像打出租车。你不需要拥有车甚至不需要开车。
最后,从用户的角度来看,无服务器或FaaS可以与公共汽车进行比较。你只需上车并直达目的地——无需维护、无需驾驶、无需拥有。
希望这能让事情更加明晰。传统的本地部署设置中,公司完全负责组织、硬件维护、数据安全等方面,而在云中适用的则是所谓的共享责任模型。
共享责任模型
定义了云服务提供商与云客户的责任。这些责任取决于提供的服务——在 IaaS 服务的情况下,客户的责任比 PaaS 或 SaaS 服务更多。例如,云服务提供商始终负责防止未经授权访问数据中心设施,以及确保电力供应和底层网络连接的稳定性。
以下图示形象地展示了不同责任之间的差异:
图 1.2 – 云交付模型比较
随着过去 20 年云技术和云服务提供商的发展,运行在云上的应用架构也发生了变化;一个新术语应运而生——云原生。大多数时候,它指的是一种架构方法,但你也会经常遇到云原生应用或云原生软件。
云原生
是一种在现代、动态基础设施(如云)上构建和运行应用的方式。它强调具有高弹性、可扩展性、高度自动化、易于管理和可观察性的应用工作负载。
尽管有云这个词,但这并不意味着云原生应用必须严格运行在公共、私有或混合云中。你可以开发一个云原生应用,并在本地运行它,例如使用 Kubernetes。
云原生不应与云服务提供商(CSPs)或简称云提供商混淆,也不应与云优先混淆,请记住以下几点:
云原生 ≠ CSP ≠ 云优先
为了完整性,让我们定义一下另外两个概念。
云服务提供商(CSP)是指提供云计算服务的第三方公司,例如 IaaS、PaaS、SaaS 或 FaaS。云优先(Cloud-first)仅仅意味着一种策略,在这种策略下,云是优化现有 IT 基础设施或启动新应用的默认选择。
如果这些定义目前还没有完全理解,也不用担心——我们将专门有一节来详细解释云原生的所有方面。现在,让我们简要介绍一下容器及其编排。
容器与容器编排
从高层次来看,容器是另一种轻量级虚拟化形式,也称为操作系统级虚拟化。然而,容器与虚拟机(VM)不同,具有各自的优缺点。
主要的区别在于,使用虚拟机时,我们可以在一台物理服务器上切分并共享多个虚拟机,每个虚拟机运行自己的操作系统。而使用容器时,我们可以在多个容器之间共享操作系统内核,每个容器都有自己的虚拟操作系统。让我们更详细地看看这个过程。
容器
这些是便携式的软件单元,包含应用代码、运行时、依赖项和系统库。容器共享一个操作系统内核,但每个容器可以拥有自己的隔离操作系统环境,带有不同的包、系统库、工具、存储、网络、用户、进程和组。
便携性很重要,需要详细说明。将应用程序打包成容器镜像后,可以保证它在另一台主机上运行,因为容器包含了自己的隔离环境。在另一台主机上启动容器不会干扰其环境或被容器化的应用程序。
容器的一个主要优势是,它们比虚拟机更加轻量和高效。容器消耗的资源(CPU 和 RAM)比虚拟机少,启动几乎是即时的,因为它们不需要启动一个完整的操作系统和内核。例如,如果一台物理服务器能够运行 10 个虚拟机,那么同一台物理服务器可能能够运行 30、40,甚至更多的容器,每个容器都有自己的应用程序(具体的数量取决于许多因素,包括工作负载类型,因此这些数字仅用于演示,并不代表任何公式)。
容器的磁盘大小也比虚拟机(VM)小得多,因为它们不会打包完整的操作系统和成千上万的库。容器镜像中仅包含带有依赖项的应用程序和一组最小的操作系统包。这使得容器镜像既小巧、便携,又容易下载或分享。
容器镜像
这些基本上是容器操作系统环境的模板,我们可以使用它们来创建多个具有相同应用程序和环境的容器。每次执行镜像时,都会创建一个容器。
从数字角度来看,一个流行的 Linux 发行版的容器镜像,如Ubuntu Server 20.04,大约为 70 MB,而相同的 Ubuntu Server 的 KVM QCOW2 虚拟机镜像大约为 500 MB。专门的 Linux 容器镜像,如Alpine,可能只有 5 到 10 MB,并提供安装和运行应用程序所需的最低功能。
容器与其运行的地方无关——无论是在物理服务器、内部虚拟机还是云端,容器都可以在这些位置中的任何一个上运行,只要有容器运行时的支持。
容器运行时
容器运行时是运行容器所需的特殊软件,它负责根据下载的容器镜像创建、启动、停止和删除容器。容器运行时的例子包括 containerd、CRI-O 和 Docker Engine。
图 1.3 展示了虚拟化和容器化部署的差异:
图 1.3 – 虚拟化与容器化部署的对比
现在,你可能会问自己,如果容器如此优秀,为什么还会有人使用虚拟机(VM),而且云服务提供商为什么仍然提供这么多虚拟机类型?
下面是虚拟机相比容器有优势的场景:虚拟机由于隔离性更强,提供了更好的安全性,因为它们不会直接共享相同的宿主机内核。这意味着,如果运行在容器中的应用程序被黑客攻破,那么黑客能够攻破同一宿主机上其他容器的可能性要比常规虚拟机高得多。
我们将在后面的章节中深入探讨操作系统级虚拟化背后的技术,并探索虚拟机和容器之间的底层差异。
随着容器的流行和广泛应用,容器的管理逐渐成为一个挑战,特别是在大规模管理容器时。行业需要一些工具来编排和管理基于容器的应用程序的生命周期。
这与公司和团队必须操作的容器数量不断增加有关,因为随着基础设施工具的不断发展,应用架构也在发生变化,从大型单体架构转变为小型、分布式、松耦合的微服务架构。
单体架构与微服务架构
为了理解单体应用和基于微服务的应用之间的差异,让我们回顾一个现实中的例子。假设一家企业运营着一个在线酒店预订业务,所有预订都是客户通过公司网站进行的,并且完成支付。
这种类型的 Web 应用程序的传统单体架构会将所有功能打包到一个复杂的软件中,这个软件可能包括以下内容:
-
客户仪表盘
-
客户身份与访问管理
-
根据条件搜索酒店
-
计费与支付供应商集成
-
酒店预订系统
-
票务与支持聊天
单体应用将所有业务和用户逻辑紧密耦合(打包)在一起,并且必须一起开发和更新。这意味着,如果需要修改计费代码,整个应用程序都必须进行更新。更新后,还必须进行仔细测试,并发布到生产环境中。每一个(即便是小的)变更都可能导致整个应用崩溃,并使得业务在较长时间内不可用。
在微服务架构下,同样的应用程序可以被拆分成几个小模块,通过网络相互通信,并各自完成自己的功能。例如,计费可以通过四个较小的服务来实现:
-
汇率转换器
-
信用卡供应商集成
-
银行电汇处理
-
退款处理
本质上,微服务是一组小型应用,每个应用负责完成自己的小任务。这些小型应用通过网络互相通信,并共同协作,成为一个更大应用的一部分。
下图展示了单体架构与微服务架构之间的差异:
图 1.4 – 单体架构与微服务架构的比较
通过这种方式,Web 应用的其他部分也可以拆分成多个独立的小型应用(微服务),它们通过网络进行通信。此方法的优点包括以下几点:
-
每个微服务可以由其独立的团队开发
-
每个微服务可以单独发布和更新
-
每个微服务可以独立于其他服务进行部署和扩展
-
单个微服务的故障只会影响应用的一个小部分功能
微服务是云原生架构的重要组成部分,我们将在第九章《理解云原生架构》中详细回顾微服务的优势与挑战。暂时让我们回到容器,探讨为什么它们需要进行编排。
当每个微服务被打包成容器时,容器的总数可能会很快达到几十个甚至几百个,特别是在大型和复杂应用中。在这样的复杂分布式环境下,情况可能迅速失控。
容器编排系统帮助我们管理大量容器。它通过将应用容器分组到部署中,并自动化以下操作,简化了容器的管理:
-
根据工作负载扩展微服务
-
发布微服务的新版本及其更新
-
根据主机的使用情况和需求调度容器
-
自动重启失败的容器或将流量切换到其他容器
目前,有许多容器和工作负载编排系统可供选择,包括以下几种:
-
Kubernetes
-
OpenShift(也称为Open Kubernetes 发行版(OKD))
-
Hashicorp Nomad
-
Docker Swarm
-
Apache Mesos
正如你从书名中已经知道的,我们将仅专注于 Kubernetes,并且不会在这五种方案之间做任何比较。事实上,Kubernetes 的市场份额遥遥领先,多年来,它已成为编排容器的事实标准平台。你可以充满信心地专注于学习 Kubernetes,至少目前可以忽略其他的容器编排方案。
Kubernetes 及其起源
让我们首先从简要的历史开始。Kubernetes 的名称源自希腊语,意思是领航员或舵手 —— 一个操纵船只的人(这也是为什么标志中有一个舵轮)。舵轮上有七根条,而数字七对 Kubernetes 具有特殊意义。最初负责 Kubernetes 的团队称其为七号项目 —— 这是根据著名电视系列《星际迷航》中的七个九分之七角色而命名的。
图 1.5 – Kubernetes 标志
Kubernetes 最初由 Google 开发,并于 2014 年作为开源项目发布。当时,Google 早已运行其服务于容器中超过十年,并且 Kubernetes 的发布引发了行业的另一场小革命。那时,许多企业已经意识到使用容器的好处,并需要简化大规模容器编排的解决方案。正如我们即将看到的,Kubernetes 成为了这个解决方案。
Kubernetes(K8s)
Kubernetes 是一个用于容器编排的开源平台。Kubernetes 具有可扩展和声明式 API,允许您自动达到资源的期望状态。它支持灵活的调度、自动扩展、滚动更新和基于容器的负载自愈。
(在线和文档中,经常可以遇到更短的缩写 K8s —— 其中八是“K”和“s”之间字母的数量。)
Kubernetes 继承了许多来自 Borg 的特性和最佳理念 —— Borg 是 Google 的一个内部容器集群管理系统,多年来支持运行数千个不同的应用程序。许多 Borg 工程师参与了 Kubernetes 的开发,并能够根据多年运营大规模容器的经验来解决相关痛点。
在初版发布后不久,Kubernetes 迅速吸引了开源社区的注意,并吸引了来自全球各地的许多才华横溢的贡献者。今天,Kubernetes 是 GitHub 上三大最大的开源项目之一(github.com/kubernetes),拥有超过 80,000 个星和 3,000 名贡献者。它还是第一个从云原生计算基金会(CNCF)毕业的项目,CNCF 是一个从 Linux 基金会分拆出来的非营利组织,旨在推动容器和云原生技术的发展。
Kubernetes 最重要的特性之一是期望状态的概念。Kubernetes 的运行方式是我们定义所需的应用程序容器状态,Kubernetes 将自动确保达到这个状态。Kubernetes 不断观察所有部署容器的状态,并确保此状态与我们请求的一致。
让我们考虑以下示例。假设我们在具有三个主机的 Kubernetes 集群上运行一个简单的微服务应用程序。我们定义了一个规格,要求 Kubernetes 运行以下内容:
-
两个相同的前端容器
-
三个相同的后端容器
-
两个用于数据持久化的容器
出乎意料的是,三个主机中的一个发生故障,导致在前端和后端上运行的两个容器不可用。Kubernetes 会观察到集群中主机的数量变化以及前端和后端容器的数量减少。Kubernetes 会自动在另外两个运行中的主机上启动一个前端容器和一个后端容器,将系统恢复到预期状态。这个过程被称为自我修复。
Kubernetes 能做的不仅仅是调度和重启失败的容器——我们还可以定义一个 Kubernetes 规格,要求根据当前的需求自动增加微服务容器的数量。例如,在前面的例子中,我们可以指定,在工作负载增加时,我们希望运行五个前端副本和五个后端副本。或者,在应用需求低时,我们可以自动将每个微服务容器的数量减少到两个。这个过程被称为自动扩缩。
这个例子展示了 Kubernetes 的基本功能。在第三部分,我们将深入探索更多 Kubernetes 特性,并亲自尝试其中的一些功能。
重要提示
虽然作为一个容器编排工具,Kubernetes 本身并没有容器运行时。相反,它与流行的容器运行时(如containerd)集成,并且可以在 Kubernetes 集群内与多个运行时一起工作。
你经常会看到 Kubernetes 集群的相关内容,因为一个典型的 Kubernetes 安装会被用来管理分布在多个主机上的数百个容器。单主机的 Kubernetes 安装仅适合用于学习或本地开发,而不适用于生产环境。
总结来说,Kubernetes 为大规模容器的采用铺平了道路,并且它是一个蓬勃发展的开源生态系统,每年都有新的项目从 CNCF 毕业。在本书中,我们将深入探讨 Kubernetes 的 API、组件、资源、功能和操作方面,并了解更多可以与 Kubernetes 一起使用以扩展其功能的项目。
概述
在本章中,我们学习了云计算和容器的概念,以及过去 20 到 30 年计算领域的演变。在云计算时代之前,传统的部署方式将一个或少量应用程序部署在每台物理服务器上,这导致了很多低效和资源浪费,硬件利用率低且拥有成本高。
当虚拟化技术出现时,使用虚拟机(VMs)可以在一个物理服务器上运行多个应用程序。这解决了传统部署的缺陷,并且能够更快速且成本显著降低地交付新应用程序。
虚拟化为通过四种不同模型提供的云服务铺平了道路:IaaS、PaaS、SaaS 和 FaaS 或无服务器。根据云服务和交付模型,客户的责任有所不同。
这种进步从未停止——现在,云原生作为一种构建和运行应用程序的方法已经出现。云原生应用程序设计和构建时强调可扩展性、弹性、易管理性和高度自动化。
近年来,容器技术发展迅速并获得了动力。容器在操作系统级别使用虚拟化,每个容器代表一个虚拟操作系统环境。与 VM 相比,容器更快速、更高效、更具可移植性。
容器使我们能够基于微服务架构开发和管理现代应用程序。与传统的单体应用程序相比,微服务更先进——一体化、巨兽应用程序。
虽然容器是运行云原生应用程序最有效的方式之一,但难以管理大量容器。因此,最好使用像 Kubernetes 这样的编排器来管理容器。
Kubernetes 是一种开源容器编排系统,起源于 Google,自动化容器的许多运维方面。Kubernetes 将根据提供的规格自动安排、启动、停止和重新启动容器,并根据当前需求增加或减少容器数量。Kubernetes 使得实施自愈和根据当前需求自动扩展成为可能。
问题
在每章末尾,您将找到总结性问题,以测试您的理解。问题可能有多个正确答案。正确答案可在附录的评估部分找到:
-
以下哪项描述了物理服务器上的传统部署方式(选两个)?
-
简易维护
-
未充分利用的硬件
-
低能耗
-
高前期成本
-
-
VM 相比容器有哪些优势?
-
它们更可靠
-
它们更具可移植性
-
它们更安全
-
它们更轻量级
-
-
描述 VM 和容器之间差异的是哪两个(选两个)?
-
VM 镜像较小而容器镜像较大
-
VM 镜像较大而容器镜像较小
-
VMs 共享操作系统内核而容器不共享
-
容器共享操作系统内核而 VMs 不共享
-
-
容器在哪个层级操作?
-
编排器层级
-
虚拟化层级
-
编程语言层级
-
操作系统级别
-
-
容器镜像通常包含以下哪两个内容(选两个)?
-
操作系统内核
-
一个最小化的 OS 库和软件包集合
-
图形化桌面环境
-
一个打包的微服务
-
-
与虚拟机相比,容器有哪些优势(选择多个)?
-
它们更安全
-
它们更轻量
-
它们更具可移植性
-
启动速度更快
-
-
启动和运行容器需要哪些软件?
-
容器运行时
-
一个虚拟机监控程序
-
Kubernetes
-
VirtualBox
-
-
以下哪些可以用于编排容器?
-
containerd
-
CRI-O
-
Kubernetes
-
无服务器
-
-
以下哪些是云服务交付模型(选择多个)?
-
IaaS, PaaS
-
SaaS, FaaS
-
DBaaS
-
无服务器
-
-
以下关于云原生的说法哪一项是正确的?
-
它是一种架构方法
-
它与云提供商相同
-
它类似于云优先
-
它是仅在云中运行的软件
-
-
以下哪些描述适用于云原生应用程序(选择两个)?
-
高度自动化
-
高可扩展性和高韧性
-
只能在私有云中运行
-
只能在公有云中运行
-
-
以下关于单体应用程序的说法哪一项是正确的?
-
它们容易更新
-
它们的组件通过网络相互通信
-
它们包含所有的业务逻辑和接口
-
它们可以轻松扩展
-
-
以下哪些关于微服务的说法是正确的(选择多个)?
-
它们只能用于后端
-
它们作为更大应用的一部分协同工作
-
它们可以由多个团队开发
-
它们可以独立部署
-
-
以下哪些可以通过 Kubernetes 完成(选择多个)?
-
在故障时自我修复
-
自动扩展容器
-
启动虚拟机
-
在不同主机上调度容器
-
-
哪个项目为 Kubernetes 提供了灵感?
-
OpenStack
-
Docker
-
Borg
-
OpenShift
-
第二章:CNCF 和 Kubernetes 认证概览
在这一章中,你将了解云原生计算基金会(CNCF)及其母组织——Linux 基金会。我们将了解这些基金会背后的内容,它们是如何出现的,以及它们的生态系统中托管了哪些项目。我们还将讨论 CNCF 社区、治理结构、云角色以及 Kubernetes 认证路径。
虽然这是本书中技术性最弱的一章,但你将在此学到的内容涵盖了 KCNA 认证中的大约一半云原生架构领域的知识点,所以确保你在本章结束时回答所有的复习问题。
在这一章中,我们将涵盖以下主题:
-
开源软件(OSS)和开放标准
-
Linux 和 CNCF
-
CNCF 社区与治理结构
-
云角色与人物画像
-
Kubernetes 认证路径
让我们开始吧!
开源软件和开放标准
如果你曾经从事过任何 IT 职位,那么你很有可能接触过开源软件(OSS)这一术语,了解它的含义,并且很可能已经使用过其中的一些软件。
什么是开源软件(OSS)?
开源软件是指软件的源代码是公开可访问的,任何人都可以研究、修改并用于任何目的。
软件不能仅仅因为它可以在互联网上轻松找到或可以从暗网下载而被视为开源软件。软件被视为开源,当它是根据某种开源许可证发布的,比如 Apache 2.0 或 GNU 通用公共许可证 v3。那些许可证赋予用户修改源代码的权利,甚至可以将其用于商业用途。没错——你可以使用开源软件来构建新的软件并出售,而无需向任何人支付许可费用。或者,你也可以修改现有的开源软件,增加新特性,并将其出售或提供相关的支持服务。
虽然开源软件的历史可以追溯到计算机早期的日子,但真正诞生其中最成功的开源软件是在 90 年代初。没错,你猜对了——我们在谈论的是 Linux。
Linux 内核是 1991 年由Linus Torvalds启动的开源项目中最著名的例子之一。三十年来,该项目吸引了大量志愿者和热心的程序员,他们愿意无偿贡献自己的时间、技能和精力。截至写作时,已经有超过 15,000 人贡献了他们的时间、技能和努力,共同打造了一个核心操作系统,这个操作系统支持了全球 100%的超级计算机和大约 95%的世界服务器。
有很多成功的开源项目——Kubernetes、OpenStack、Terraform、Ansible、Prometheus 和 Django 都是开源的。即使是像谷歌这样拥有强大工程资源的公司,也意识到了开源社区的力量,以及开源生态系统如何推动项目发展。这正是 Kubernetes 项目的经历。
自 Kubernetes 早期以来,已经吸引了众多热情的工程师,并成为 CNCF 孵化的第一个项目。但在我们继续学习 CNCF 的更多内容之前,让我们讨论另一个重要话题——开放标准。
开放标准
2015 年,随着 CNCF 的成立,另一个重要事件发生了——开放容器倡议(OCI)开始启动。
OCI
OCI 是一个开放的治理结构,旨在定义围绕容器格式和运行时的开放行业标准。
OCI 拥有一个技术社区,行业参与者可以在其中贡献,帮助构建中立、可移植且开放的容器规范。
它还提供了参考实现和工具,兑现容器在应用程序可移植性方面的承诺。这些标准帮助我们避免了供应商锁定(当客户被迫继续使用某个产品或服务,因为很难切换到其他供应商时)并帮助确保不同的项目可以顺利集成与协作。例如,正如我们在上一章所学,容器运行时并不属于 Kubernetes 的一部分,因此我们需要确保不同的容器运行时与 Kubernetes 完全兼容。
在撰写本文时,OCI 为容器生态系统提供了三个重要的规范:
-
镜像规范(image-spec)定义了如何构建和打包应用程序为符合 OCI 标准的容器镜像
-
运行时规范(runtime-spec)定义了容器的生命周期和执行环境
-
分发规范(distribution-spec)定义了通过所谓的注册表促进和标准化容器镜像分发的协议
那么,OCI 标准为什么如此重要?
让我们来看一个简单的交通运输类比。假设每个汽车制造商都有自己固定车轮与车轴连接的方法。如果轮胎制造商没有进行标准化,他们会很难为每个品牌和型号生产车轮。但由于大家都同意采用特定的车轮尺寸参数,如宽度、直径、螺栓孔数量等,这使得制造商能够生产适配任何市场上汽车型号的车轮。因此,你可以根据汽车的规格购买车轮,并且有信心它们能够匹配你的车。
类似于容器,我们有镜像、运行时和分发标准,这些标准允许任何人开发 100%兼容的软件。OCI 标准的建立促进了容器生态系统的进一步扩展,带来了新的软件。在 OCI 出现之前,构建容器镜像只有一种方式——使用 Docker。今天,我们有了像Kaniko、Podman、Buildah等项目。
目前,你不需要知道这些差异的具体细节——只需要记住,OCI 通过提供开放规范,标志着容器及其生态系统发展的一个重要节点。在接下来的章节中,我们将深入探讨 Docker 和 OCI 标准的某些方面。
根据 OCI,它并不寻求成为一个营销组织,也不定义完整的技术栈或解决方案要求——它致力于避免标准化正在进行创新和讨论的技术领域(github.com/opencontainers/tob/blob/main/CHARTER.md)。帮助建立 OCI 并在其倡议中仍然发挥重要作用的组织是 Linux 基金会。与许多开源项目一样,志愿者们在该领域工作,并希望贡献他们的时间,将技术推向下一个层次,或者弥合现有项目之间的差距。
我们已经在这里提到过 CNCF 和 Linux 基金会几次。现在,让我们更深入了解它们。
Linux 和 CNCF
Linux 基金会是一个非营利组织,成立于 2000 年,源于开放源码开发实验室与自由标准组织的合并。基金会最初是为了标准化、推广和支持 Linux 的采纳而创建的,但自那时以来,它在开源社区中的作用已大大扩展。
今天,Linux 基金会的支持成员包括许多《财富》500 强公司,如谷歌、IBM、三星、Meta 等。基金会除了 Linux 内核外,还主持许多项目。这些项目包括 汽车级 Linux、Ceph(存储)、XEN(虚拟机监控器)、实时 Linux、OpenAPI Initiative(OAI)等。即使你之前从未听说过这些项目,也不必担心——你在考试中不会被问到这些内容。
近年来,Linux 基金会通过会议、认证、培训和新计划扩展了其项目。其中一个计划是 云原生计算基金会(CNCF),该基金会于 2015 年成立。
CNCF
2015 年 7 月 21 日成为整个开源社区的一个重要日期——Kubernetes 1.0 发布了。随着发布,作为 K8s 背后的关键推动力和贡献者,谷歌与 Linux 基金会合作,成立了 CNCF。Kubernetes 成为了新基金会的种子技术和第一个孵化项目。CNCF 的使命是推动容器和云原生技术的发展,并与行业对接(www.cncf.io/about/who-we-are/):
“基金会的使命是让云原生计算普及。”
云原生技术使组织能够在现代动态环境中构建和运行可扩展的应用程序,如公共云、私有云和混合云。容器、服务网格、微服务、不变基础设施和声明式 API 就是这种方法的典型例子。
这些技术使得松耦合系统变得更具弹性、可管理和可观察。结合强大的自动化,它们使工程师能够频繁且可预测地进行高影响力的变更,同时最小化工作量。
云原生计算基金会通过培育和维持一个开源、供应商中立的项目生态系统,推动这一范式的采纳。我们将最先进的模式民主化,使这些创新对每个人都可访问。”
我们在第一章《从云到云原生与 Kubernetes》中讲解了云计算、容器和微服务的基础知识,接下来的章节将深入探讨这些以及 CNCF 使命声明中提到的其他话题。
如今,CNCF 得到了超过 450 个成员的支持,并在云原生生态系统中发挥着重要作用。它提供治理并支持开源项目,使其成熟并确保其处于生产就绪状态。
说到成熟度,CNCF 有三个显著的级别:
-
沙盒:这是早期阶段项目的入口点,这些项目可以为 CNCF 使命增值。新项目如果与现有项目互补,可能会与其对齐。
-
孵化:这是一个已在生产环境中成功使用的项目,拥有持续的代码提交和贡献流,同时具有文档、规范和版本控制方案。
-
毕业:这是一个拥有来自多个组织的贡献者、遵循核心基础设施倡议最佳实践,并通过了独立第三方安全审核的项目。该项目还应定义治理和贡献者流程,并且证明其已被真实用户使用。
每个由 CNCF 托管的项目都有一个相应的成熟度级别。项目通过展示其获得了最终用户采用、健康的代码变更频率,以及来自 不同组织的贡献者,来提高其成熟度。
此外,所有 CNCF 项目必须遵守IP 政策并采用行为规范。行为规范定义了可接受的行为以及不可接受的行为,以创建一个积极和富有同理心的协作环境。另一方面,IP 政策关注知识产权,决定应适用哪种开源许可证(通常,源代码使用 Apache 2.0 许可证,文档和图片使用 Creative Commons Attribution 4.0 International 许可证)。
CNCF 社区与治理
随着 Kubernetes 等开源项目的势头不断增长,它们吸引了更多的贡献者,这总是一个好兆头。然而,较大的社区可能会失控,并在没有适当治理的情况下迅速变得 混乱。虽然 CNCF 不要求其托管的项目遵循任何特定的治理模型,但为了使项目毕业,必须明确规定治理和提交者流程。CNCF 遵循 最小可行治理 原则,这意味着项目自我治理,CNCF 机构仅在需要帮助或项目出现问题时介入。
说到其结构,CNCF 有三个主要机构:
-
管理委员会 (GB): 负责 CNCF 的市场营销、预算和其他商业决策
-
技术监督委员会 (TOC): 负责定义和维护技术愿景,批准新项目,并根据反馈调整现有项目
-
终端用户社区 (EUC): 提供来自终端用户和组织的反馈,以改进云原生生态系统中的整体体验
TOC 还决定一个项目是否达到了更高的成熟度水平。项目可以在孵化阶段无限期停留,但通常期望在 2 年内毕业。
如你所知,Kubernetes 是 CNCF 中第一个孵化的项目,也是第一个在 2018 年达到 毕业 状态的项目。没错,Kubernetes 跳过了沙盒阶段,因为它是在 CNCF 成立的同时首次发布的。自那时以来,已有 10 多个项目毕业,约有 100 个项目目前处于孵化或沙盒阶段。在下一部分,我们将探讨云和云原生中常见的角色和人物。
云角色和人物
云原生不仅仅是关于技术和架构,还涉及人员和工作环境中高效的协作。因此,在使用云基础设施和云原生生态系统的组织中,常常会有一些特定的职位和角色。了解这些角色以及它们的职责是 KCNA 考试的要求。
在采用云和云原生的现代组织中,可以遇到以下角色:
-
云架构师或云解决方案架构师
-
DevOps 工程师
-
DevSecOps 工程师
-
FinOps 工程师
-
站点可靠性工程师 (SRE)
-
云工程师
-
数据工程师
-
全栈开发工程师
这个列表并不详尽,你有时可能会看到这些角色的不同变体,但这应该能给你一个大致的概念。现在,让我们明确这些角色和职责之间的区别:
- 云(解决方案)架构师:不出所料,架构师负责设计云基础设施和云原生应用程序的架构。它必须具有高度的韧性、可观察性、可扩展性,并且具有较高的自动化程度。
云架构师通常还负责云战略和选择云提供商(公有云、私有云或混合云)以及合适的服务(IaaS/PaaS/SaaS/无服务器架构)。架构师角色要求具备广泛的技术和非技术领域知识。例如,他们必须了解资本支出或前期成本(CAPEX)与运营支出或简而言之是运行成本(OPEX)的区别。传统数据中心是高 CAPEX 的典型例子,而公有云则是零或几乎为零的 CAPEX,成本大多为运营成本(OPEX)。
-
DevOps 工程师:Dev代表开发,而Ops代表运营。DevOps 工程师是能够弥合开发人员与运营人员之间差距的人。通常,DevOps 工程师既有软件开发经验,也有系统管理员或基础设施工程师的经验。这些知识使他们能够有效地自动化云中的基础设施以及整个应用生命周期,包括开发、测试和发布。DevOps 工程师通常需要至少掌握一种编程语言(例如 Python、Ruby 或 Golang),以及一些自动化工具(如 Terraform、Ansible、Puppet 等)和持续集成与持续部署/交付(CI/CD)系统。如今,你通常也会看到 Kubernetes 经验作为 DevOps 岗位的要求之一。DevOps 文化强调学习优于责备,倡导共享责任和强有力的跨团队协作,并促进持续的反馈循环。
-
DevSecOps 工程师:这个角色与 DevOps 工程师非常相似,但更加注重安全性。在 DevSecOps 中,安全性在应用和基础设施生命周期的早期就开始介入,并且需要与安全团队密切合作。
-
FinOps 工程师:Fin代表财务,而Ops代表运营。FinOps 工程师使团队能够跟踪预算、提供透明度,并在云端进行成本优化。这个角色要求深入了解云端的各种定价模型和服务,以找到最佳和最具成本效益的解决方案。
-
网站可靠性工程师 (SRE):SRE 的职责包括维护和优化云基础设施和应用程序。SRE 在某种程度上与 DevOps 相似,但更侧重于运营部分,尤其是满足应用程序可用性要求或在服务水平协议 (SLAs) 或 服务级目标 (SLOs) 中定义的目标。SLA 规定了服务提供商与客户之间在质量、可用性和责任方面的承诺水平。SLO 是 SLA 的具体可衡量特征,如可用性或响应时间。SRE 经常参与值班轮换(即,他们随时准备在发生事故时作出反应)。
-
云工程师:这个角色与 DevOps 类似,但侧重于云服务提供商的云特性或服务。通常,DevOps 工程师的角色需要更广泛的技能,而云工程师则需要更深入了解特定的公共云服务。
-
数据工程师:在这个角色中,工程师需要处理数据保护、数据存储、性能和可用性要求,以及用于数据分类、保留和分析的各种工具。随着企业积累越来越多的数据,他们需要有效利用这些数据。因此,数据工程师目前的需求量很大。
-
全栈开发者:这是一个广泛的角色,开发者需要能够处理应用程序的前端部分(例如,用户界面 (UI))和后端部分。后端是一个通用术语,描述的是不对终端用户可见或无法访问的软件实现。有时,全栈开发者还可能涉及一些云基础设施和 DevOps 工具的基础经验。
虽然这些角色和职位在全球范围内的需求极高,但如果你来自完全不同的背景,或者刚刚从大学毕业且没有相关的工作经验,获得这些职位的机会可能并不容易。获得认证可以使你的个人资料脱颖而出,并大大提高你获得面试机会的几率。在本书的最后,你将找到一些职业建议,但现在让我们先看看 CNCF 提供了哪些 Kubernetes 认证以及认证路径是怎样的。
Kubernetes 认证路径
在撰写本文时,CNCF 提供了四个 Kubernetes 认证考试:
-
Kubernetes 和云原生 助理 (KCNA)
-
认证 Kubernetes 应用程序 开发者 (CKAD)
-
认证 Kubernetes 管理员 (CKA)
-
认证 Kubernetes 安全 专家 (CKS)
CKA 是第一个于 2017 年发布的考试,原有效期为 2 年,但后来有效期延长至 3 年。CKAD 和 KCNA 的有效期也为 3 年;只有 CKS 考试通过后有效期为 2 年。
在所有考试中,CKS 是最难的,要求考生持有有效且未过期的 CKA 认证,以证明他们已经具备足够的 K8s 专业知识。这意味着虽然你可以随时购买 CKS,但除非你先通过 CKA,否则无法参加此考试。所有其他考试都可以按任意顺序进行;然而,推荐的路径如下图所示:
图 2.1 – CNCF Kubernetes 认证路径
KCNA 是列表中最简单的考试,也是唯一不需要动手操作的考试——即,它是选择题。其他所有认证都需要动手操作,并要求你在多个 K8s 集群上通过终端完成活动。
但不要对 KCNA 考试抱有过高期望。事实上,在云领域的入门级认证中,它是一个相对较难的认证。即使你在相关领域工作了几年,没有任何 Kubernetes 经验和一些准备,通常也不太可能通过考试。在尝试实际考试之前,确保你已经回答完所有的回顾性问题,并完成了本书结尾提供的模拟考试。
以下是关于 KCNA 考试的一些重要信息:
-
60 道选择题
-
90 分钟完成考试
-
需要 75%及以上的分数才能通过
-
考试会在约 24 小时内自动评分
-
可在任何地方在线参加(需要摄像头和麦克风)
-
价格包括一次免费重考
以下是 KCNA 考试中测试的领域:
-
Kubernetes 基础知识(46%)
-
容器编排(22%)
-
云原生架构(16%)
-
云原生可观察性(8%)
-
云原生应用交付(8%)
如你所见,Kubernetes 占据了考试的主要部分。因此,本书的最大篇幅(第三部分,学习 Kubernetes 基础知识)专门讲解 K8s。除此之外,KCNA 考生应能够确认自己对云原生及其生态、项目和实践的概念性知识。这包括上一章中的高层次定义,这些内容可能会在 KCNA 中被提问。
其他三个考试专注于与 Kubernetes 相关的不同方面。CKAD 考试测试应用部署和管理方面,而 CKA 更侧重于 Kubernetes 的搭建和管理。然而,这些考试涉及许多共同领域,如果你通过了其中一个,经过一些额外的准备,你也能通过另一个。最后,CKS 聚焦于安全性,需要在 Kubernetes 方面有丰富的经验。
总结
在本章节中,我们了解了开源软件(OSS)以及开放标准在 OCI 方面的重要性。OCI 定义了镜像、运行时和分发规范,使任何人都能开发完全兼容的容器软件。例如,Kubernetes 不包括自己的容器运行时;相反,它实现了对标准化运行时接口的支持,使其能够与多种容器运行时协同工作。开放的、定义明确的标准为云原生生态系统和 CNCF 中的许多新项目铺平了道路。
接下来,我们回顾了 CNCF 和 Linux 基金会的历史。CNCF 是在 Kubernetes 的第一个版本发布时成立的,并且成为了第一个孵化的项目。CNCF 将项目成熟度分为三个级别:沙箱、孵化和毕业。
CNCF 有三个主要的组织:管理委员会(GB)、技术监督委员会(TOC)和最终用户社区(EUC)。TOC 负责决定 CNCF 项目的成熟度。一个项目达到毕业状态的要求之一是必须有定义的治理和提交者流程。
云原生需要合适的人来做工作,这一点得到了许多市场上需求量很大的角色和身份的支持。我们已经看过并比较了不同的角色,以理解云架构师的责任与 DevOps 工程师或全栈开发人员的区别。
最后,我们回顾了 Kubernetes 认证路径,并深入研究了你正在准备的 KCNA 考试。确保在进入下一个章节之前回答所有复习问题,我们将在下一个章节中探讨 Docker 和运行容器。
问题
在我们总结时,以下是一些问题,供你测试本章内容的理解。你可以在附录中的评估部分找到答案:
-
以下哪些是 CNCF 中有效的项目成熟度状态(请选择多个)?
-
沙箱状态
-
已发布状态
-
毕业状态
-
孵化状态
-
-
哪个组织是为了建立容器行业标准而成立的?
-
开放容器基金会
-
云原生容器倡议
-
云原生容器基金会
-
开放容器倡议
-
-
以下哪些要求必须满足,CNCF 项目才能达到毕业状态(请选择多个)?
-
为未来 3-5 年制定项目开发和维护计划
-
拥有实际用户和定义的治理及提交者流程
-
拥有来自多个组织的贡献者
-
遵循核心基础设施倡议的最佳实践
-
-
以下哪个 CNCF 组织决定一个项目是否达到了另一个成熟度水平?
-
最终用户社区(EUC)
-
管理委员会(GB)
-
技术监督委员会(TOC)
-
技术监督委员会(TOC)
-
-
以下哪些规范是 OCI 提供的(请选择多个)?
-
镜像规范
-
运行时规范
-
执行规范
-
分发规范
-
-
以下哪些是 CNCF 项目在任何成熟阶段所需的(选择两个)?
-
接受 CNCF 行为准则
-
接受 CNCF 知识产权政策
-
接受 GNU GPL v.3 许可证
-
接受 Linux 基金会作为项目所有者
-
-
以下哪些是 DevOps 文化强调的(选择多个)?
-
共享责任
-
学习而非责备
-
强大的跨团队协作
-
开发人员应该遵循运维团队
-
-
以下哪个组织的使命是推动容器和云原生技术的发展,并使行业保持一致?
-
Linux 基金会
-
开放容器倡议
-
云原生容器基金会
-
云原生计算基金会
-
-
以下哪些可能是云架构师的职责之一(选择两个)?
-
选择云服务提供商和合适的服务
-
设计云基础设施架构
-
将应用程序部署到生产环境
-
在云中维护应用程序
-
-
DevOps 和 DevSecOps 工程师有什么区别?
-
DevOps 只负责运维
-
DevSecOps 只处理安全方面的问题
-
DevSecOps 类似于 DevOps,但更注重安全性
-
DevSecOps 必须拥有与安全相关的认证
-
-
以下哪些描述了 SRE(选择两个)?
-
SRE 需要与云服务提供商现场合作
-
SRE 不参与任何操作
-
SRE 负责维护和优化基础设施和应用程序
-
SRE 需要确保应用程序的 SLA 和 SLO 得以满足
-
-
云工程师和 DevOps 工程师有什么不同(选择两个)?
-
DevOps 工程师对云一无所知
-
云工程师对云服务有更深入的了解
-
DevOps 工程师通常具备更广泛的技能
-
DevOps 工程师需要值班,而云工程师则不需要
-
-
拥有全栈开发人员团队有什么好处?
-
全栈开发人员可以处理前端和后端工作
-
全栈开发人员将应用程序部署到云端
-
全栈开发人员编写代码更快
-
全栈开发人员编写更简洁的代码
-
-
为什么拥有开放标准很重要(选择两个)?
-
他们帮助我们避免厂商锁定
-
它们使不同的软件兼容
-
他们确保软件没有漏洞
-
它们防止通过软件获利
-
-
以下哪些技术是 DevOps 工程师可能会使用的(选择多个)?
-
前端技术(例如,JavaScript、HTML 和 CSS)
-
自动化工具(例如,Terraform、Ansible 和 Puppet)
-
CI
-
CD
-
第二部分:执行容器编排
在本部分中,您将了解容器的起源、它们的实用性,以及导致它们在众多组织(从小型初创公司到全球企业)广泛采用的原因。我们将涵盖理论和实践方面的内容,如如何构建和运行容器,以及为何和何时需要编排。
本部分包含以下章节:
-
第三章,容器入门
-
第四章, 探索容器运行时、接口和服务网格
第三章:开始使用容器
本章我们将更深入地了解容器,深入探讨容器技术和容器生态系统,并发现常用的工具。
一句中国古老的谚语说:“我听到的,我忘记;我看到的,我记住;我做的, 我理解。”
从本章开始,我们将亲自动手,尝试构建镜像和运行容器,以获得更深入的理解和第一手实践经验。尽管 KCNA 是一项选择题考试,但亲自实践非常重要,这段经验将对你未来的学习有所帮助。不要只读代码片段——确保完全执行它们,尤其是在你没有容器使用经验的情况下。 你需要一台运行最新版本的 Linux、Windows 或 macOS 的计算机,以及一个可用的互联网连接。
本章我们将涵盖以下主题:
-
介绍 Docker
-
探索容器技术
-
安装 Docker 并运行容器
-
构建容器镜像
技术要求
本章使用的所有示例文件和代码片段已上传到本书的 GitHub 仓库:github.com/PacktPublishing/Becoming-KCNA-Certified。
介绍 Docker
Docker 已经存在多年,因此你可能听说过它。对许多人来说,Docker 这个名字本身就是 容器 的代名词。然而,叫做 Docker 的东西有很多,容易让人混淆:
-
Docker Inc.
-
Docker Engine
-
dockerd(Docker 守护进程)
-
Docker CLI
-
Docker Hub
-
Docker Registry
-
Docker Swarm
-
Docker Compose
-
Docker Desktop
-
Dockershim
让我们逐一澄清这些内容。
首先,Docker 公司(作为一家公司)并没有发明容器技术,但它创建了易于使用的工具,帮助推动了容器技术的广泛采用。该公司成立于 2008 年,最初名为 dotCloud。
Docker Engine 是一个开源软件包,用于构建和容器化应用程序。它是一种客户端-服务器软件,由一个名为 docker 的守护进程服务组成。
容器化
容器化是将软件应用代码与依赖项(如库、框架等)一起打包到容器中的过程。容器可以在不同环境之间独立移动,而不依赖于基础设施的操作系统。
当你安装 Docker 引擎时,你实际上安装了两样东西——dockerd 服务和 CLI。dockerd 会持续运行并监听命令,对容器进行操作,如启动新容器、停止现有容器、重启容器等。这些命令可以通过 docker CLI 或常用工具如 curl 来发出。本章示例将使用 docker CLI。
下一项是 Docker Hub (hub.docker.com/),一个公共容器镜像注册表。如你所知,容器镜像是一个预定义的静态模板,我们用它作为启动新容器的基础。那么,我们从哪里获取镜像呢?Docker Hub 就是其中一个地方。它是 Docker 公司提供的在线仓库服务,成千上万的容器镜像存放在其中,包含不同的环境(Ubuntu、Centos、Fedora 和 Alpine Linux),以及流行的软件如 Nginx、Postgres、MySQL、Redis 和 Elasticsearch。Docker Hub 允许你查找、分享和存储容器镜像,这些镜像可以通过互联网轻松拉取(下载)到你需要创建新容器的主机上。值得一提的是,Docker Hub 并不是唯一的此类服务——其他的服务还包括 Quay (quay.io/)、Google Container Registry (cloud.google.com/container-registry) 和 Amazon Elastic Container Registry (aws.amazon.com/ecr/)。
接下来我们来看 Docker Registry,它现在由 CNCF 管理,作为一个名为 Distribution 的项目。它是一个开源的服务器端应用程序,可以用于存储和分发 Docker 镜像。与 Docker Hub 的主要区别在于,Docker Registry 是你可以直接取来、安装并在你的组织内运行的软件,而且无需付费,而 Docker Hub 是一个 服务式注册表,提供一些额外的付费功能。Docker Registry 可用于存储和提供你的 开发 团队正在开发的容器镜像。
接下来是 Docker Swarm,它的目的在于集群管理和容器编排。Swarm 与 Kubernetes 类似;然而,它仅与 Docker 引擎兼容(意味着不支持其他容器运行时),并且与 Kubernetes 相比,功能要少得多且定制性有限。这也是它没有像 Kubernetes 那样广泛采用的原因。
Docker Compose 是另一个 Docker 工具,它允许你定义和共享多容器应用程序的规格。使用 Compose,你可以在一个 YAML 格式的文件中定义多个需要相互通信的容器,作为一个应用的一部分。例如,你可以启动一个包含数据库的 Django Web 应用,运行在两个容器中,并定义数据库必须先启动,同时暴露容器的某些端口。Compose 可能对于一些本地开发使用 Docker 很有帮助,但它与 Kubernetes 不兼容,因此我们不打算进一步讨论它。
Docker Desktop 是一个结合了 Docker 引擎、docker CLI、Docker Compose、Kubernetes 以及一些其他工具的 Windows/macOS 工具,它配有 图形用户界面(GUI)。没错——Docker Desktop 甚至将 Kubernetes 和 K8s 客户端打包在一起,供本地开发使用。Docker Desktop 对非商业用途免费,但如果在组织中使用,则需要付费。也有一个适用于 Ubuntu 和 Debian Linux 的测试版。
Dockershim 是一种软件兼容层,旨在使 Kubernetes(准确地说是其 kubelet 组件)能够与 dockerd(Docker 守护进程)进行通信。正如你可能记得的那样,Kubernetes 没有自己的容器运行时(用于执行基本容器操作的软件,如启动、停止和删除)。在早期版本中,Kubernetes 仅支持 Docker 来操作容器。随着容器生态系统的发展,dockerd 并没有一个符合 OCI 标准的接口,因此创建了 Kubernetes 与 dockerd 之间的翻译层,称为 Dockershim。自 Kubernetes 1.20 版本起,Dockershim 已被弃用,并且在 1.24 版本中完全从 K8s 中移除。
最后,我们已经到达了列表的末尾。尽管多年来出现了许多替代方案,Docker 引擎和 Docker 工具仍然被全球成千上万的开发团队和组织积极使用。下图展示了如何使用 Docker CLI 与 Docker 守护进程进行通信,后者从 Docker Registry 中获取镜像并在本地创建容器:
图 3.1 – Docker 架构
在接下来的章节中,我们将安装一些 Docker 工具,看看它的实际操作,最终亲手操作容器。
探索容器技术
在进入实践部分之前,我们仍需要弄清楚容器背后的技术以及是谁创造了它。Linux 容器背后的技术其实早在很久之前就已经开发出来,并且基于两个核心内核功能:
-
cgroups(控制组)
-
命名空间
cgroups
cgroups 是一种机制,允许将进程组织成层次结构的组。这些组如何使用资源(如 CPU、内存、磁盘 I/O 吞吐量等)可以被限制、监控和控制。
cgroups 最初由谷歌的工程师开发,并于 2007 年首次发布。自 2008 年初以来,cgroups 功能被合并到 Linux 内核中,并一直存在至今。2016 年,发布了 cgroups 的修订版本,现在称为 cgroups 版本 2。
即使在 cgroups 之前,Linux 的命名空间功能在 2002 年就已经开发出来。
Linux 内核命名空间
这个 Linux 特性允许你以某种方式划分内核资源,让一组进程看到一组资源,而另一组进程看到不同的资源。Linux 命名空间用于将进程相互隔离。
命名空间有不同类型,每种类型有其独特的属性:
-
root(超级用户)但仅限于其自身的命名空间。 -
1感谢命名空间的存在。 -
网络命名空间:这使你能够为一组进程运行独立的网络堆栈,拥有自己的路由表、IP 地址、连接跟踪等。
-
挂载命名空间:这使你能够在命名空间内拥有独立的挂载点。这意味着命名空间中的进程可以拥有不同的挂载,而不会影响主机文件系统。
-
进程间通信(IPC):这使你能够隔离 Linux 的进程间通信机制,如共享内存、信号量和消息队列。
-
UNIX 时间共享(UTS):这使你能够为不同的进程设置不同的主机名和域名。
这听起来可能很复杂,但别担心——命名空间和 cgroups 并不属于 KCNA 考试的内容,因此你不需要了解每个命名空间及其作用。然而,由于它们是容器技术的核心,了解它们的基本概念会有所帮助,而且如果你能够解释容器是如何在底层工作的,还会得到额外的加分。
总结一下,cgroups 和 命名空间 是容器的构建块。cgroups 允许你监控和控制进程(或一组进程)的计算资源,而命名空间则在不同的系统级别隔离进程。这两种功能也可以在没有容器的情况下使用,很多软件都利用了这些功能。
足够的理论,接下来让我们动手实践!在下一部分,我们将安装 Docker 工具并启动第一个容器。
安装 Docker 并运行容器
如果你使用的是 Windows 或 macOS,可以从 https://docs.docker.com/desktop/ 下载并安装 Docker Desktop。如果你使用的是最新版本的 Ubuntu Linux,也有 Docker Desktop 的版本可供下载。如果你使用的是其他 Linux 发行版,则需要安装 Docker Engine。你可以在 https://docs.docker.com/engine/install/ 上找到针对你的发行版的详细安装指南。请选择一个 稳定 版本进行安装。
如果你重新启动计算机,请确保 Docker Desktop 正在运行。在 Linux 上,你可能需要在终端中执行以下代码:
$ sudo systemctl start docker
如果你希望系统重启时自动启动,你可以运行以下命令:
$ sudo systemctl enable docker
无论你安装的是哪种操作系统或工具(桌面版或引擎版),都会随附我们将要使用的 Docker 命令行工具,简称为 docker。
首先,让我们通过检查版本来确保 Docker 已正确安装并正在运行。打开终端并输入以下命令:
$ docker --version
Docker version 20.10.10, build b485636
重要提示
如果你在 Linux 上,并且在安装后没有将你的用户添加到 docker 组中,你需要使用超级用户权限调用 Docker CLI,因此所有的 docker 命令应该加上 sudo 前缀。对于上面的示例,命令将是 sudo docker --version。
你的输出可能会稍有不同——也许你安装了更新的版本。如果之前的命令没有成功,但 Docker 已经安装,确保 Docker Desktop(如果你使用 macOS 或 Windows)或 Docker 守护进程(如果你使用 Linux)正在运行。
现在,让我们启动第一个带有 Ubuntu 22.04 的容器:
$ docker run -it ubuntu:22.04 bash
你将看到的输出应该类似于以下内容:
Unable to find image ‘ubuntu:22.04’ locally
22.04: Pulling from library/ubuntu
125a6e411906: Pull complete
Digest: sha256:26c68657ccce2cb0a31b330cb0be2b5e108d467f641c62e13ab40cbe c258c68d
Status: Downloaded newer image for ubuntu:22.04
root@d752b475a54e:/#
哇!我们现在在 Ubuntu 容器内运行 bash。镜像可能需要几秒钟才能下载,但一旦准备好,你将看到命令行提示符在新创建的容器内以 root 用户身份运行。
那么,当我们调用 docker run 时,究竟发生了什么?
docker run 在新容器内执行命令;它需要容器镜像的名称,其中命令将被执行(在上面的例子中是 ubuntu),可以选择镜像的标签(此处是 22.04),以及要执行的命令(此处是简单的 bash)。
-i 参数与 --interactive 相同,表示我们希望以交互模式运行命令。-t,即 --tty,将分配一个伪 TTY(模拟终端)。
如你所记得,镜像是容器环境的模板。我们请求了一个带有版本 22.04 的 ubuntu 环境。在输出的前几行中,我们看到该镜像在本地未找到:
Unable to find image ‘ubuntu:22.04’ locally
22.04: Pulling from library/ubuntu
125a6e411906: Pull complete
如果之前没有下载过带有特定标签的镜像,它将自动从 Docker Hub 库中下载(拉取),并且你应该能够看到下载进度。
现在,让我们退出容器并尝试重新运行它。在终端中输入 exit:
root@d752b475a54e:/# exit
exit
现在,执行我们之前做过的相同命令:
$ docker run -it ubuntu:22.04 bash
root@e5d98a473adf:/#
这次速度更快了吗?是的,因为我们已经在本地缓存了 ubuntu:22.04 镜像,所以不需要再下载它。因此,容器立即启动了。
你有没有注意到这次 root@ 后面的主机名不同了——e5d98a473adf 与 d752b475a54e?(注意:你将在这里看到你独特的容器名称。) 这是因为我们启动了一个基于相同 ubuntu 镜像的新容器。当我们启动一个新容器时,并不会修改只读源镜像;相反,我们在镜像上创建了一个新的可写文件系统层。下面的图表展示了这种分层方法:
图 3.2 – 容器层
当我们启动一个容器时,我们添加了一个新的层,这使得容器镜像的副本可以进行修改。通过这种方式,我们可以从相同的基础镜像创建任意数量的容器,而无需修改初始的只读镜像层。这种方法的主要优点是在容器层中,我们仅存储与镜像层的差异,这意味着在大规模使用时可以显著节省磁盘空间。
镜像也可以由多个层组成,其中一个层可能源自另一个层。在接下来的章节中,我们将学习如何构建新镜像并将我们喜欢的软件包含在其中。
随时探索我们的容器环境,并在完成后exit退出:
$ docker run -it ubuntu:22.04 bash
root@e5d98a473adf:/# echo “Hello World” > /root/test
root@e5d98a473adf:/# hostname
e5d98a473adf
root@e5d98a473adf:/# date
Sun May 1 13:40:01 UTC 2022
root@e5d98a473adf:/# exit
exit
当我们在第一个容器中执行exit时,它退出了;稍后,当我们再次执行docker run时,一个新的容器被创建。现在,由于两个容器都已经退出,我们在磁盘上有一个镜像层,以及基于ubuntu:22.04基础镜像的两个不同容器层。
由于容器层仅跟踪与基础镜像的差异,直到所有容器层被删除之前,我们无法删除基础镜像。让我们通过运行以下代码获取本地镜像的列表:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 22.04 d2e4e1f51132 39 hours ago 77.8MB
如果我们尝试使用docker rmi命令删除ubuntu:22.04镜像,我们会遇到一个错误:
$ docker rmi ubuntu:22.04
Error response from daemon: conflict: unable to remove repository reference “ubuntu:22.04” (must force) – container e5d98a473adf is using its referenced image d2e4e1f51132
我们还可以执行docker ps命令来查看所有正在运行的容器:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
一个空表格表示当前没有容器在运行。
最后,我们可以执行docker ps --all来查看本地系统上的所有容器,包括那些已退出的容器:
$ docker ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e5d98a473adf ubuntu:22.04 “bash” 8 minutes ago Exited (0) 2 minutes ago vibrant_jenn
d752b475a54e ubuntu:22.04 “bash” 18 minutes ago Exited (0) 12 minutes ago cool_perl
尝试使用docker rm CONTAINER ID删除那些已退出的容器:
$ docker rm d752b475a54e
d752b475a54e
$ docker rm e5d98a473adf
e5d98a473adf
现在,镜像也应该被删除:
$ docker rmi ubuntu:22.04
Untagged: ubuntu:22.04
Untagged: ubuntu@sha256:26c68657ccce2cb0a31b330cb0be2b5e108d467f641c62e13ab40cbe c258c68d
Deleted: sha256:d2e4e1f511320dfb2d0baff2468fcf0526998b73fe10c8890b4684bb 7ef8290f
Deleted: sha256:e59fc94956120a6c7629f085027578e6357b48061d45714107e79f04 a81a6f0c
sha256是镜像层的摘要;它们是唯一且不可变的标识符。如果我们为ubuntu镜像分配一个不同的标签而不是22.04,并尝试从 Docker Hub 再次拉取(下载)相同的镜像,Docker 会识别出我们已经拥有这个摘要的镜像,并且除了重新标记它之外,不会做任何事情。
让我们再试一件事——拉取另一个没有标签的 Docker 镜像。如果你仅仅pull镜像,那么不会启动容器,但这样可以节省下次从该镜像启动新容器时的下载时间:
$ docker pull centos
Using default tag: latest
latest: Pulling from library/centos
a1d0c7532777: Pull complete
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473 f432b177
Status: Downloaded newer image for centos:latest
docker.io/library/centos:latest
如你所见,如果我们没有明确指定标签,默认会使用latest。
在接下来的章节中,我们将进一步了解latest标签的含义、标签的基本概念以及如何使用 Docker 构建镜像。
构建容器镜像
现在我们知道如何启动容器和拉取镜像,我们将学习如何创建新的容器镜像。由于镜像层是不可变的,你可以通过在现有镜像之上添加新层来创建你选择的软件的新镜像。使用 Docker 可以通过两种方式来实现:
-
交互模式
-
使用 Dockerfile
交互式方法是从现有容器创建镜像。假设你启动了一个 Ubuntu 22.04 环境的容器,安装了附加包并暴露了端口80。要创建新镜像,我们可以使用docker commit命令:
$ docker commit CONTAINER_ID [REPOSITORY[:TAG]]
镜像名称将采用REPOSITORY:TAG格式。如果没有指定标签,则会自动添加latest。如果没有指定仓库,则镜像名称将是唯一标识符(UUID)。标签和名称(与镜像仓库的名称相同)可以在构建后更改或应用。
虽然交互式方法快捷且简单,但在正常情况下不应使用它,因为它是一个手动、容易出错的过程,生成的镜像可能会更大,且包含许多不必要的层。
构建镜像的第二个、更好的选择是使用 Dockerfile。
Dockerfile
Dockerfile 是一个包含构建镜像指令的文本文件。它支持运行 shell 脚本、安装附加包、添加和复制文件、定义默认执行的命令、暴露端口等。
让我们来看一个简化版的 Dockerfile:
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y curl vim
LABEL description=”My first Docker image”
正如你可能已经猜到的,FROM指令定义了我们将要构建的镜像的基础镜像和标签。基础镜像也可以是我们之前构建的本地镜像,或者是来自镜像仓库的镜像。RUN指令执行apt-get update,然后安装curl和vim包。LABEL是你想要添加到镜像中的任何元数据。如果你将前面的内容复制到一个名为Dockerfile的文件中,你就可以通过在同一文件夹中调用docker build来构建一个新镜像:
$ docker build . -t myubuntuimage
[+] Building 11.2s (6/6) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 153B 0.0s
=> [internal] load .dockerignore
0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:22.04 0.0s
=> [1/2] FROM docker.io/library/ubuntu:22.04 0.0s
=> [2/2] RUN apt-get update && apt-get install -y curl vim 9.9s
=> exporting to image 1.1s
=> => exporting layers
1.1s
=> => writing image sha256:ed53dcc2cb9fcf7394f8b03818c02e0ec4 5da57e89b550b68fe93c5fa9a74b53 0.0s
=> => naming to docker.io/library/myubuntuimage 0.0s
使用-t myubuntuimage,我们指定了镜像的名称,但没有实际的标签。这意味着默认会为镜像应用latest标签:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myubuntuimage latest ed53dcc2cb9f 6 minutes ago 176MB
centos latest 5d0da3dc9764 7 months ago 231MB
关于latest标签,我们需要澄清一些内容,因为它可能会产生误导:
-
如果在构建过程中没有指定标签,
latest会默认应用。 -
如果在镜像下载或容器运行时没有指定标签,默认会拉取
latest。 -
latest不会动态更新;你可以将任何镜像标记为latest——即使是同一镜像的旧版本。
因此,最佳实践是使用更具描述性的标签来标记镜像,而不是依赖latest。例如,可以使用打包应用程序的递增版本(如 v.0.32、v.1.7.1 等)作为标签,甚至可以使用构建时间戳。时间戳使我们能够确定镜像的构建时间,而无需检查每个镜像的元数据。
让我们快速回顾一下 Dockerfile 中支持的指令。我们已经了解了FROM、RUN和LABEL,但还有更多:
-
ADD:用于将文件和目录添加到Docker 镜像中(从构建位置或远程 URL)。 -
COPY:用于将文件复制到Docker 镜像内部。 -
CMD:用于定义 Docker 镜像的默认可执行文件(只有最后一个CMD指令会被尊重)。CMD可以在容器运行时被轻松覆盖。 -
ENTRYPOINT:类似于CMD,允许我们在启动容器时定义镜像的可执行文件。它可以与CMD一起使用。 -
EXPOSE:告诉我们,镜像中的应用程序在运行时会监听特定的网络端口。 -
ENV:用于设置镜像中的任何环境变量。 -
USER:设置RUN、CMD或ENTRYPOINT指令的用户名。 -
VOLUME:用于创建一个挂载点并标记其与外部挂载卷一起使用(例如,从启动容器的主机中)。 -
WORKDIR:设置RUN、CMD和ENTRYPOINT指令的工作(当前)目录。
关于 CMD 和 ENTRYPOINT 的简要说明:它们相似,但并不相同。我们可以在 Dockerfile 中指定 CMD、ENTRYPOINT 或两者。如果同时指定,则 CMD 作为 ENTRYPOINT 的参数。由于 CMD 在运行时更容易被覆盖,因此通常 ENTRYPOINT 是可执行文件,而 CMD 是参数。例如,我们可以将 ENTRYPOINT 设置为 /bin/cat,并使用 CMD 给出要连接的文件路径(如 /etc/hosts、/etc/group 等)。对于 Docker Hub 上的许多公共镜像,ENTRYPOINT 默认设置为 /bin/sh -c。
这个列表并不是 Dockerfile 支持的指令的完整参考,但它列出了涵盖 99% 场景的最常用指令。此外,你通常不会在笔记本电脑或本地工作站上构建容器;相反,你会使用现代 CI/CD 系统或 Docker Hub 上的自动构建作为替代。
现在,让我们了解当容器被使用时,开发工作流可能是什么样子的:
- 软件开发人员使用自己选择的编程语言编写应用程序代码——例如,Python、Java、Ruby、Node.js、Golang 或其他任何语言。
重要说明
无需学习新的编程语言——任何在 Linux 环境中运行的软件也可以在容器中运行。
-
代码经过测试并推送到 GitHub 仓库或其他版本控制系统。当源代码发生变化时,CI/CD 或第三方解决方案会被触发,应用程序会根据定义的 Dockerfile 打包到容器镜像中。
-
Dockerfile 指令用于将代码复制并在容器镜像层内运行和安装。根据选择的语言和操作系统环境,这些指令有所不同。例如,Node.js 应用程序可能需要运行
yarn install,而 Python 应用程序则需要使用pip命令进行安装,等等。 -
镜像被构建、标记并推送(上传)到镜像注册中心。这可能是例如 Docker Hub 中的私有仓库,云服务提供商提供的仓库,甚至是你公司内部维护的注册中心。
-
此时,镜像可以由容器编排工具,如 Kubernetes,或者具有容器运行时的服务器,甚至只是由安装了 Docker 工具的其他团队成员下载并运行。
如你所记得,容器的主要特点之一是 可移植性——一个在某个主机上运行的容器也能在另一个主机上运行。这意味着你可以有一个包含 Alpine Linux 的容器镜像,并在你的 Fedora Linux 笔记本电脑上或基于 Ubuntu 的 Kubernetes 集群上运行它。
等等——我们能在 Windows 上运行 Linux 容器,或者反过来吗?其实不能。首先,我们需要区分 Linux 容器和 Windows 容器。
重要提示
本书的所有内容和 KCNA 考试本身都仅涉及 Linux 容器。
即使你在 Windows 上运行 Docker Desktop,它在后台也使用了一个最小化的 Linuxkit 虚拟机。Windows 容器是不同的,可能使用今天在微软操作系统中可用的两种隔离模式之一(WSL 2 或 Hyper-V)。如果你在 Windows 上运行,Docker Desktop 允许你在 Windows 容器 和 Linux 容器 之间切换。但请记住,全球超过 90% 的服务器运行的是 Linux,因此除非你打算在容器中运行仅限 Windows 的应用程序,否则你只需要学习和使用 Linux 容器。
总结
在这一章中,我们通过 (Linux) 容器获得了经验,并了解到容器背后的技术已经存在多年,基于 cgroups 和内核 namespaces。
Docker 推出了旨在为开发人员和工程师提供一种通用且简单的方式来打包和共享应用程序的工具。在容器出现之前,应用程序通常能够在开发环境中正常工作,但在生产环境中由于未满足的依赖关系或安装了不正确的版本而无法运行。容器通过将应用程序与所有依赖项和系统包捆绑在一个称为容器镜像的模板中解决了这个问题。
容器镜像可以存储在支持私有和公有仓库的注册中心,并允许你与不同的团队共享它们。Docker Hub、Quay 和 Google Container Registry(GCR)是目前一些最知名的容器镜像注册中心,可以通过互联网访问。被推送(上传)到注册中心的镜像可以通过容器编排工具,如 Kubernetes,或者仅通过具有容器运行时的服务器,通过互联网进行拉取(下载)。
镜像用于创建容器,因此容器是镜像的运行实例。当容器以可写的文件系统启动时,会在不可变镜像层之上创建一层。容器和镜像可以有多个层,并且我们可以从单个镜像启动任意多个容器。与虚拟机相比,容器更加轻量,并且启动速度非常快。
我们还学习了,使用 Docker 构建容器镜像时,可以利用交互式或 Dockerfile 方法。通过 Dockerfile,我们定义一组指令来构建包含容器化应用程序的镜像。
在下一章中,我们将继续探索容器,学习 Kubernetes 提供的运行时和可插拔接口。
问题
总结时,以下是一些问题,供你测试本章内容的知识。你可以在评估部分的附录中找到答案:
-
以下哪项特性描述了容器(选择两个)?
-
环境之间的可移植性
-
镜像体积大
-
镜像体积小
-
高安全性
-
-
以下哪些是正确的(选择两个)?
-
应用程序在容器中打包容易
-
应用程序在虚拟机中打包容易
-
容器镜像易于共享
-
虚拟机镜像易于共享
-
-
开发者需要学习哪种编程语言来在容器中运行他们的代码?
-
Dockerlang
-
Golang
-
Python
-
无 – 容器允许与操作系统环境支持的语言相同的语言
-
-
以下哪些问题是容器解决的(选择两个)?
-
环境之间的不满足依赖关系
-
应用程序代码中的错误
-
需要测试应用程序代码
-
长时间的虚拟机启动时间
-
-
以下哪项是容器使用的(选择两个)?
-
cgroups -
hwmon -
acpi -
kernel namespaces
-
-
以下哪些可以用于共享容器镜像(选择两个)?
-
Docker Hub
-
Docker Swarm
-
Docker Registry
-
Docker Compose
-
-
以下哪些关于容器镜像的说法是正确的(选择两个)?
-
它们只能通过 Dockerfile 构建
-
它们包含不可变的文件系统层
-
最新的镜像始终标记为 latest
-
它们可以通过交互式方式构建
-
-
以下哪些适用于启动新容器时(选择两个)?
-
会创建一个新的可写文件系统层
-
请求的镜像总是被拉取
-
如果本地找不到请求的标签(SHA 摘要),则会拉取镜像
-
加载新的 Linux 内核
-
-
以下哪些关于容器镜像标签的说法是正确的(选择两个)?
-
每个镜像必须有标签
-
在构建时,最新标签会自动应用,除非被覆盖
-
同一镜像不能有多个标签
-
同一镜像可以有多个名称(仓库)和标签
-
-
如何使用 Docker 工具创建新容器?
-
docker run -
docker exec -
docker spawn -
docker launch
-
进一步阅读
本章提供了容器生态系统的概述以及通过 KCNA 考试所需的知识,但并没有涵盖 Docker 工具的所有功能,也没有详细描述 cgroups 和 namespaces。如果你希望更进一步,建议你查阅以下资源:
-
cgroups v1:
www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt -
Docker 入门:
docs.docker.com/get-started/ -
编写 Dockerfile 的最佳实践:
docs.docker.com/develop/develop-images/dockerfile_best-practices/
第四章:探索容器运行时、接口和服务网格
在本章中,我们将进一步探讨容器运行时、网络、接口,并学习服务网格。我们将了解现存的运行时实现及其差异,学习容器如何通过网络相互通信,Kubernetes 中存在哪些容器接口,并了解什么是服务网格及其应用。我们还将通过之前安装的 Docker 工具进行一些额外的练习,以支持我们的学习之旅。
本章内容将涉及 KCNA 认证的容器编排领域,这部分是考试中第二大部分,因此请确保回答本章末尾的所有问题。
以下是我们将要涵盖的主题:
-
容器运行时
-
容器网络
-
容器存储
-
容器安全
-
介绍服务网格
开始吧!
容器运行时
正如你从前几章了解到的,容器可以在虚拟机、云端、本地、裸金属服务器上运行,甚至仅仅在你的笔记本电脑上运行。负责执行基本操作的软件,如从镜像仓库下载镜像并创建、启动、停止或删除容器,被称为容器运行时。我们已经学习了 Docker 工具和运行时,但实际上还有更多的运行时存在,包括以下几种:
-
Containerd
-
CRI-O
-
kata
-
gVisor
在深入了解运行时细节之前,我们需要理解什么是容器运行时接口(CRI)。
CRI
CRI 是一个插件接口,允许 Kubernetes 使用不同的容器运行时。在引入 CRI 之前的 Kubernetes 早期版本中,只能使用 Docker 作为运行时。
如你所记得,Kubernetes 没有自己的运行时来执行基本的容器操作,因此它需要一个运行时来管理容器,并且该运行时必须是 CRI 兼容的。例如,Docker 引擎不支持 CRI,但大多数其他运行时,包括containerd或CRI-O,都支持 CRI。本质上,CRI 定义了 Kubernetes 与所选运行时之间的通信协议,使用的是gRPC(高性能的远程过程调用框架),如图 4.1所示:
图 4.1 – 容器运行时与 CRI 的集成
最初,Kubernetes 中没有 CRI 实现,但随着新容器运行时的开发,将它们全部集成到 Kubernetes 中变得越来越困难。因此,解决方案是定义一个标准接口,以便与任何运行时兼容。Kubernetes 版本 1.5 中引入 CRI 后,允许在单个 K8s 集群中使用多个容器运行时,并且也使得开发兼容的运行时变得更容易。今天,containerd是 Kubernetes 新版本中最常用的运行时。
但为什么你需要在同一个集群中运行不同的容器运行时呢?这是一个相对高级的场景,背后的主要原因是某些容器运行时可以为更敏感的容器工作负载提供更好的安全性。因此,当我们讨论容器及其运行时时,我们需要区分三种主要类型:
-
命名空间 – 最快且最常用的类型,它基于 Linux 内核的 cgroups 和 namespaces 功能,这些我们在上一章中已介绍。此类型共享相同的内核来运行多个容器,因此被认为是所有容器类型中安全性最低的。例子包括 Docker、containerd 和 CRI-O。
-
虚拟化 – 最慢的容器类型,实际上需要像虚拟机一样使用虚拟机监控程序。每个容器都在其自己的轻量级虚拟机中启动,拥有自己的专用内核。此类型被认为是最安全的,因为它为容器工作负载提供了最大程度的隔离。虚拟化容器的启动速度仍然比虚拟机快,而且它们相对于传统虚拟机的优势在于它们能够轻松与容器编排系统(如 Kubernetes)集成。Kata 项目是虚拟化容器的一个例子。
-
沙盒 – 这是一种介于其他两者之间的容器类型,提供比命名空间容器更好的安全性,同时又比虚拟化容器更快。更好的安全性通过另一个隔离层来实现,这一层拦截来自容器工作负载的系统调用。gVisor 是一个来自 Google 的开源项目,允许创建沙盒容器。
虽然这听起来可能非常复杂,但在 KCNA 考试的范围内,你不需要了解容器运行时的所有细节。如果你以后参加 CKS 考试或有特殊用途使用 沙盒 或 虚拟化 容器,这些知识才会用得上。现在,请确保记住哪些容器运行时是存在的,并且在大多数场景中,命名空间 容器是常用的。同时,不要把 CRI 和 OCI 混淆,我们在 第二章中已经讲过,CNCF 和 Kubernetes 认证概述。
重要提示
开放容器倡议 (OCI) 提供了容器行业规范(镜像、运行时和分发规范),而 CRI 是 Kubernetes 的一部分,它使得可以以可插拔的方式与 K8s 一起使用不同的容器运行时。
在实际操作中,你并不会直接与容器运行时进行交互,而是使用像 Kubernetes 或 Docker Swarm 这样的编排系统。我们还可以使用 CLI 来与容器运行时进行交互,就像我们使用 Docker CLI,或者在使用containerd运行时时,你可以使用ctr或nerdctl CLI。
接下来,在以下部分中,我们将学习更多关于容器网络的内容。
容器网络
到目前为止,我们只尝试过创建单个容器。然而,在现实世界中,我们需要处理成十上百个容器。随着微服务架构的广泛应用,应用程序被拆分为多个较小的部分,这些部分通过网络互相通信。一个应用程序可能由前端部分、多个后端服务和数据库层组成,其中前端接收到的用户请求会触发与后端的通信,而后端则会与数据库进行交互。当每个组件都运行在自己独立的容器中,并且跨多个服务器时,理解它们如何相互通信是至关重要的。网络是容器和 Kubernetes 的一个重要部分,理解这些如何工作是非常具有挑战性的。目前,我们只会简单触及容器间通信的表面,更多关于暴露容器和 K8s 细节的内容将在后续章节中介绍。
让我们回到前一章中安装的 Docker 工具,并尝试启动另一个 Ubuntu 容器。
重要提示
在尝试启动容器之前,请确保 Docker Desktop 正在运行。如果你之前没有启用自动启动,可能需要手动启动它。在 Linux 的 Docker Engine 上,你可能需要执行$ sudo systemctl start docker。
打开终端并运行以下命令:
$ docker run -it ubuntu:22.04 bash
因为该镜像被精简到最小化以节省空间,所以没有预安装像net-tools这样的基本软件包。我们可以通过调用apt update和apt install来在容器内安装这些包:
root@5919bb5d37e3:/# apt update; apt -y install net-tools
… SOME OUTPUT OMITTED …
Reading state information... Done
The following NEW packages will be installed:
net-tools
… SOME OUTPUT OMITTED …
Unpacking net-tools (1.60+git20181103.0eebece-1ubuntu5) ...
Setting up net-tools (1.60+git20181103.0eebece-1ubuntu5) ...
root@5919bb5d37e3:/#
现在我们已经安装了net-tools,可以在容器内使用ifconfig工具。你看到的输出应该类似于此:
root@5919bb5d37e3:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
RX packets 14602 bytes 21879526 (21.8 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3127 bytes 174099 (174.0 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 5 bytes 448 (448.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5 bytes 448 (448.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
我们还可以通过在容器内调用route工具来查看容器的路由表。输出将类似于以下内容:
root@9fd192b5897d:/# route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
正如我们所看到的,我们的容器具有eth0接口,IP 地址是172.17.0.2。在你的情况下,地址可能不同,但重要的是,我们的容器默认会有自己独立的网络堆栈,包括自己的(虚拟)接口、路由表、默认网关等等。
如果我们现在打开另一个终端窗口并执行docker network ls,我们将看到使用哪些驱动程序支持哪些网络类型。输出将类似于以下内容:
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
c82a29c5280e bridge bridge local
83de399192b0 host host local
d4c7b1acbc0d none null local
有三种基本的网络类型:
-
bridge– 这是我们创建的 Docker 容器的默认类型和驱动程序。它允许连接到同一主机上的桥接网络的容器相互通信,并与其他容器隔离(这些容器也可以连接到自己的桥接网络)。借助主机的网络地址转换(NAT)和IPtables,容器可以与外界进行通信。 -
host- 这是一种类型,用于在不需要网络隔离时创建容器。使用主机网络生成的容器不会与创建它的主机系统的网络隔离。例如,您可以启动一个带有 Apache Web 服务器监听80端口的容器,并且它将立即可以从同一网络中的任何其他主机访问,除非受到防火墙的保护。 -
none- 这是一个很少使用的选项,意味着容器的所有网络都将被禁用。
注意,docker network ls 输出中的这些类型具有 local 范围,这意味着它们可以用于我们在 Docker 中生成容器的单个主机上。但是它们不会允许在一个服务器上创建的容器直接与在另一个服务器上创建的容器通信(除非使用主机网络,这类似于在没有涉及容器的情况下直接在主机上运行应用程序)。
为了在多个主机之间建立容器相互通信的网络,我们需要所谓的覆盖网络。覆盖网络将多个服务器连接在一起,允许位于不同主机上的容器之间进行通信。
覆盖网络
一个覆盖网络是在另一个网络顶部运行的虚拟网络,通常使用数据包封装 - 覆盖网络数据包位于另一个数据包内,该数据包被转发到特定主机。
无论您是在运行 Kubernetes、Docker Swarm 还是其他解决方案来编排容器,实际上,您总是会为您的工作负载运行多个主机,并且运行在这些主机上的容器需要使用覆盖网络相互通信。
谈到 Kubernetes,类似于 CRI,它实现了容器网络接口(CNI),允许以可插拔的方式使用不同的覆盖网络。
CNI
CNI 是一个允许 Kubernetes 使用不同的覆盖网络插件的接口。
CNI 的引入使得第三方能够开发符合 Kubernetes 并提供其自身独特特性的解决方案,例如容器网络中的流量加密或网络策略(防火墙规则)。
今天在 Kubernetes 中使用的一些 CNI 网络插件包括flannel、Cilium、Calico 和 Weave,仅举几例。Kubernetes 还支持同时使用多个插件,如Multus(一个多网络插件);但这是 KCNA 考试范围外的高级主题。在书籍的第三部分,学习 Kubernetes 基础中,我们将进一步深入了解 Kubernetes 中的网络,但现在是时候深入研究容器存储了。
容器存储
容器设计时是轻量级的,正如我们之前看到的那样,即使是ifconfig和ping等基本工具也可能不包含在容器镜像中。这是因为容器代表了操作系统环境的最小版本,我们只安装我们要容器化的应用及其依赖项。通常,容器镜像中不需要预安装很多包或工具,除非是应用程序运行所必需的。
容器默认不保持状态,这意味着如果你在容器文件系统内放置了一些文件,并且在删除容器后,这些文件将完全消失。因此,通常我们称容器为无状态的,而容器中的磁盘文件则是短暂的。
这并不意味着我们不能使用容器来存储重要数据,以便在容器失败或应用退出时能够持久化数据。
注意
如果容器中运行的应用失败、崩溃或简单地终止,容器默认也会停止。
通过使用外部存储系统,确实可以保留容器中的重要数据。
外部存储可以是一个通过协议如iSCSI附加到容器的块卷,也可以是一个网络文件系统(NFS)挂载,举个例子,或者外部存储也可以仅仅是容器主机上的一个本地目录。外部存储有很多选择,但我们通常将外部容器存储称为卷。
一个容器可以有多个附加的卷,而这些卷可以由不同的技术、协议和硬件支持。卷还可以在容器之间共享,或者从一个容器中分离并附加到另一个容器。卷的内容存在于容器生命周期之外,使我们能够解耦容器和应用数据。卷使我们能够在容器中运行需要写入磁盘的有状态应用程序,无论是数据库、应用程序还是其他任何文件。
让我们回到带有 Docker 工具的计算机,尝试在终端中运行以下命令:
$ docker run -it --name mycontainer --mount source=myvolume,target=/app ubuntu:22.04 bash
当我们运行并将tty附加到容器时,我们应该能够看到我们的新myvolume挂载在容器的/app目录下:
root@e642a068d4f4:/# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 126G 7.9G 112G 7% /
tmpfs 64M 0 64M 0% /dev
tmpfs 3.0G 0 3.0G 0% /sys/fs/cgroup
shm 64M 0 64M 0% /dev/shm
/dev/vda1 126G 7.9G 112G 7% /app
tmpfs 3.0G 0 3.0G 0% /proc/acpi
tmpfs 3.0G 0 3.0G 0% /sys/firmware
root@e642a068d4f4:/# cd /app/
root@e642a068d4f4:/app#
发生的情况是,Docker 在开始时自动为我们的容器创建并附加了一个local卷。Local 意味着该卷由容器启动时所在主机上的一个目录提供支持。
重要提示
本地存储可以用于测试或某些开发工作,但绝不适用于生产工作负载和关键业务数据!
如果我们现在在/app写入任何文件,它们将会持久化:
root@e642a068d4f4:/app# echo test > hello_world
root@e642a068d4f4:/app# cat hello_world
test
root@e642a068d4f4:/app# exit
exit
即使我们通过调用docker rm来删除容器:
$ docker rm mycontainer
mycontainer
通过调用docker volume ls,我们可以看到当前在主机上存在的卷:
$ docker volume ls
DRIVER VOLUME NAME
local myvolume
要获取有关卷的更多详细信息,我们可以使用docker volume inspect命令:
$ docker volume inspect myvolume
[
{
"CreatedAt": "2022-05-15T18:00:06Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/myvolume/_data",
"Name": "myvolume",
"Options": null,
"Scope": "local"
}
]
此时,你可以自由地自己尝试更多关于卷的操作。例如,你可以创建一个新容器并附加现有卷,以确保数据仍然存在:
$ docker run -it --name mycontainer2 --mount source=myvolume,target=/newapp ubuntu:22.04 bash
root@fc1075366787:/# ls /newapp/
hello_world
root@fc1075366787:/# cat /newapp/hello_world
test
现在,说到 Kubernetes,你可能已经猜到了——类似于 CRI 和 CNI,K8s 实现了 Container Storage Interface(CSI)。
CSI
CSI 允许使用可插拔的存储层。外部存储系统可以通过 CSI 以标准化的方式集成到 Kubernetes 中使用。
CSI 允许供应商和云服务提供商为他们的存储服务或硬件设备实现支持。例如,有一个Amazon Elastic Block Store(EBS)CSI 驱动程序,它允许你通过 Kubernetes 完全管理 AWS 云中 EBS 卷的生命周期。还有一个NetApp Trident CSI 项目,它支持各种 NetApp 存储设备,容器可以在 Kubernetes 中使用这些设备。现在,还有许多其他与 CSI 兼容的存储解决方案。
Kubernetes 在管理存储方面非常强大;它可以在集群中的主机和容器之间自动配置、附加和重新附加卷。我们将在 第六章,使用 Kubernetes 部署和扩展应用程序 中更详细地学习 Kubernetes 在有状态应用程序中的功能,现在让我们继续学习容器安全性。
容器安全性
容器安全性是一个高级且复杂的话题,即使是入门级的 KCNA 认证,你也需要了解一些基础知识。正如我们所学,命名空间容器是最常用的容器,它们共享底层操作系统的内核。这意味着在一个容器中运行的进程无法看到其他容器中运行的进程或主机上运行的进程。然而,在同一主机上运行的所有进程仍然使用相同的内核。如果其中一个容器被攻破,那么主机和其他所有容器都有可能被攻破。
让我们回到 Docker 配置,进行快速演示。启动一个 Ubuntu 容器,像之前一样运行 uname -r 命令,查看使用的是哪个内核版本:
$ docker run -it ubuntu:22.04 bash
root@4a3db7a03ccf:/# uname -r
5.10.47-linuxkit
你看到的输出取决于你的主机操作系统和内核版本。如果你看到另一个版本,不要感到惊讶。例如,你可能会看到这样的输出:
5.13.0-39-generic
现在,退出容器,启动另一个较旧版本的 Ubuntu:16.04:
$ docker run -it ubuntu:16.04 bash
Unable to find image 'ubuntu:16.04' locally
16.04: Pulling from library/ubuntu
58690f9b18fc: Pull complete
b51569e7c507: Pull complete
da8ef40b9eca: Pull complete
fb15d46c38dc: Pull complete
Digest: sha256:20858ebbc96215d6c3c574f781133ebffdc7c18d98af 4f294cc4c04871a6fe61
Status: Downloaded newer image for ubuntu:16.04
root@049e8a43181f:/# uname -r
5.10.47-linuxkit
root@049e8a43181f:/#
看到了吗?我们使用了一个已经有超过 5 年历史的Ubuntu:16.04镜像,但所使用的内核版本与第一个容器中的完全相同。即使你使用的是不同版本的 Linux,主机操作系统的内核版本也会被使用。
那么,我们如何保护运行命名空间容器的主机内核呢?也许最著名的两项技术是AppArmor(适用于 Ubuntu)和Security-Enhanced Linux(SELinux,适用于 Red Hat 和 CentOS Linux 家族)。从本质上讲,这些项目允许你为所有用户应用程序和系统服务强制执行访问控制策略。还可以限制对特定文件或网络资源的访问。SELinux 还有一个特殊工具,可以帮助为运行在容器中的应用程序生成安全配置文件(github.com/containers/udica)。Kubernetes 与 AppArmor 和 SELinux 都有集成,允许你将配置文件和策略应用于由 K8s 管理的容器。
接下来,作为root用户运行容器被视为一种不良实践且具有安全风险。在 Linux 中,root用户是一个 ID 为0、组 ID 为0的用户(UID=0,GID=0)。在我们的所有动手练习中,我们都在容器内使用了root用户:
root@4a3db7a03ccf:/#
root@4a3db7a03ccf:/# id -u
0
在实际生产环境中,你应该考虑以非root用户运行应用程序,因为root本质上是一个超级管理员,可以在系统中执行任何操作。现在有趣的部分来了——容器中的root用户也可以是主机上运行容器的root用户(非常不好的做法!)。或者,得益于 Linux 内核的命名空间功能,容器内的root用户可以映射到主机操作系统上的另一个用户 ID(例如UID=1001)。这仍然不是完美的,但如果容器被攻破,容器中的root不会自动获得主机操作系统上的root权限。
注意
在镜像构建过程中,可以指定容器中应用程序使用的用户和组。你可以简单地在Dockerfile中添加USER mynewuser指令来定义要使用的用户。你可能需要在此之前先创建该用户,可以通过在指令上方添加如下内容:RUN useradd -r -u 1001 mynewuser
最后但同样重要的是,请记住你在环境中使用的容器镜像。如果你访问 Docker Hub(hub.docker.com/)或任何其他在线容器注册表,你会发现许多第三方镜像,任何人都可以下载并运行。你可能会遇到一个恰好满足你需求的镜像。例如,某个镜像可能打包了你想尝试的工具或应用程序(例如,监控你正在运行的数据库)。但是,它也可能打包了恶意代码。因此,确保只在容器中运行可信的代码。
最好自己构建镜像并将其存储在自己的仓库中,因为第三方公共镜像仓库完全不受你的控制。它们的所有者可能随时删除或替换镜像,甚至将仓库设为私有。你可能不会立即注意到这一点,这可能导致在镜像无法下载时发生事故。最后,现在有很多工具可以执行容器镜像扫描以检测安全漏洞(Clair、Dagda 和 Anchore 等工具)。这些工具可以集成到镜像构建过程中,以减少使用过时软件包或安装已知存在安全漏洞的软件的风险。
现在我们对容器安全性和网络有了更多了解,我们将研究服务网格——一种用于管理流量和保护云原生应用的新兴技术。
介绍服务网格
在深入了解服务网格的定义之前,我们先快速回顾一下之前学习的关于云原生应用架构的内容。
现代的云原生应用依赖于微服务,这些微服务作为更大应用的一部分协同工作,并通过网络相互通信。这些微服务被打包为容器镜像,并借助如 Kubernetes 这样的编排系统运行。云原生应用的特点是高度动态的,运行的容器数量根据当前的负载和基础设施事件或故障发生变化。
假设你负责运行公司开发的一个由 20 个不同微服务组成的应用。你已经为所有服务实现了自动扩展,并且在高负载时,运行的容器数量超过一百个(例如,每个服务的多个容器副本分布在多个云实例上)。即使使用 Kubernetes 有效地编排这些容器,你仍然希望确保应用可靠运行,基础设施安全,并且在出现问题时能够及时检测并迅速采取行动。这时,服务网格就派上用场了。
服务网格
服务网格是一个专用的基础设施层,用于确保服务之间的通信是安全、可观察和可靠的。
服务网格是一个特殊的层,用于处理服务与服务之间的通信。这里的服务通常是运行在容器中并由 Kubernetes 编排的微服务。从技术上讲,服务网格可以在没有 Kubernetes 甚至容器的情况下使用,但实际上,大多数情况下,服务网格是与由 Kubernetes 编排的容器一起使用的。以下是一些服务网格的例子:
-
Linkerd
-
Istio
-
Open Service Mesh (OSM)
-
Consul Connect Service Mesh
列表中的前三个实际上是开源的 CNCF 项目,尽管它们的成熟度不同。
那么,在服务网格的背景下,安全通信意味着什么呢?
在前面的部分,我们介绍了容器安全的基础知识,但我们尚未深入探讨如何保护容器之间的网络通信。保护网络通信通常是所谓零信任安全方法的一部分。
零信任
零信任是一种方法,其中无论是在网络内部还是外部,默认情况下任何人都不被信任。必须经过验证才能访问连接到网络的服务。
传统的网络安全方法是基于保护基础设施的边界,也就是说,外部很难获得网络访问权限,但在网络内部,每个人默认都是受信任的。显然,如果攻击者能够突破边界安全并访问内部网络,他们很可能会获得其他地方的访问权限,包括机密数据。这就是为什么越来越多的企业正在实施零信任(Zero Trust)方法,服务网格在这一过程中非常有帮助。
服务网格的主要优势之一是,您不需要对应用程序代码进行任何更改就可以使用服务网格及其功能。服务网格是在平台层上实现的,这意味着一旦在平台上安装,所有应用程序(例如容器中的微服务)都可以受益于其特性。通过服务网格,容器之间的所有流量可以自动加密和解密,且运行在其中的应用程序不需要修改任何一行代码。
在没有服务网格的情况下,传统方法实现这一目标可能需要管理 SSL 证书,在过期时请求并续期,甚至可能需要进一步修改应用程序或基础设施层。
实际上,上述列表中的所有服务网格都为连接到网格的容器之间的所有 TCP 流量提供相互认证的传输层安全(mTLS)。它与常规的TLS相似,后者通过证书展示服务器身份,区别在于mTLS的情况下,双方都必须自我认证才能开始通信。这意味着客户端也需要提供一个证书,服务器会进行验证。在我们的示例中,客户端和服务器是连接到服务网格的容器中的两个服务。同样,mTLS 可以完全自动启用,无需在应用程序部分做任何额外的工作。
在探讨其他特性之前,我们首先更好地了解一下服务网格是如何工作的。服务网格层通过一系列轻量级网络代理与微服务进行接口对接,所有微服务之间的流量都会通过这些代理在其自己的基础设施层中进行路由。通常,代理与每个服务一起运行在所谓的sidecar容器中,这些 sidecar 代理一起形成了服务网格网络,如图 4.2所示。
图 4.2 – 服务网格概述
服务网格通常由两部分组成:
-
数据平面 – 由运行在微服务容器旁边的网络代理组成。例如,在Linkerd服务网格中,使用的是linkerd-proxy,而在Istio中,使用的是Envoy代理的扩展版本。
-
控制平面 – 由多个组件组成,负责配置网络代理、服务发现、证书管理和其他功能。
为了让服务网格与 Kubernetes 兼容,它必须支持 K8s 服务网格 接口(SMI)。
SMI
这是一个定义标准、通用且可移植的 API 集合的规范,旨在以供应商无关的方式平滑集成服务网格。SMI的作用与CRI、CNI和CSI相同,但用于服务网格。
在可观察性方面,服务网格提供了网格内所有通信的详细遥测数据。所有代理自动收集的指标使得操作人员和工程师能够排查故障、维护和优化他们的应用程序。通过服务网格,我们可以追踪调用和服务依赖关系,并检查流量流动和单个请求。这些信息对于审计服务行为、响应时间以及在复杂的分布式系统中检测异常非常有帮助。
最后,服务网格提供了流量管理和可靠性功能。具体的功能可能因项目而异,因此一个服务网格提供的某些功能可能另一个没有提供。为了举例说明,让我们看看Linkerd网格提供了什么:
-
负载均衡 – 适用于HTTP、HTTP/2和gRPC请求以及TCP连接。服务网格还可以自动检测最快的服务端点,并将请求发送到那里。
-
自动重试和超时 – 这允许你通过透明地进行重试,优雅地处理暂时性的服务故障。
-
流量拆分 – 这允许你动态地将一部分服务流量从一个服务切换到另一个服务,以实现新的服务版本的复杂发布策略。
-
故障注入 – 这允许你人为地引入错误和故障,以测试系统或连接的服务的影响。
总的来说,服务网格是一个复杂且高级的话题,我们仅仅触及了最低要求的基础知识,以便通过 KCNA 考试。如果你对更多的内容感兴趣,建议查看进一步阅读部分。
你此时可能会问自己一个问题:覆盖网络和服务网格有什么区别?我们为什么需要两者?
简短的回答是,大多数覆盖网络运行在开放系统互联(OSI)模型的较低层(网络层 3),而服务网格则运行在 OSI 模型的第 7 层,专注于服务和高层应用协议(如果你不熟悉 OSI 模型,可以查看进一步阅读部分)。二者的功能并不是互相替代的,服务网格仍在获得势头,这意味着并不是每个基于微服务或容器化的 Kubernetes 应用都会使用服务网格。从技术上讲,我们也并不总是必须与容器一起使用覆盖网络,正如我们在 Docker 的练习中所看到的,但在接下来的章节中,我们将看到为什么使用它是有利的。
总结
在本章中,我们学到了很多关于容器运行时、容器接口和服务网格的知识。容器运行时是管理基本容器操作的底层软件,如镜像下载、容器启动或删除。Kubernetes 没有自己的运行时,但它提供了接口,允许你使用不同的运行时、不同的网络插件、不同的存储解决方案以及不同的服务网格。这些接口分别被称为 CRI、CNI、CSI 和 SMI,它们的引入使得在使用 K8s 时具有了很大的灵活性。
我们还了解了容器运行时类型及其区别。命名空间容器是最流行和轻量的,但它们的安全性不如其他类型的容器。虚拟化容器是最慢的,但它们提供最大安全性,因为每个容器使用独立的 Linux 内核。沙箱容器填补了两者之间的空白——它们比命名空间容器更安全,比虚拟化容器更快。
在容器网络方面,有很多选择。对于集群中的容器间通信,我们通常会使用覆盖网络。Kubernetes 通过 CNI 支持第三方网络插件,这些插件提供了不同的功能和能力。也可以在非隔离的网络环境中运行容器,例如直接在启动容器的主机的网络命名空间中运行。
容器本身是无状态的,意味着它们默认情况下不会保留磁盘上的数据。要在容器中运行一个有状态的应用程序,我们需要挂载外部存储卷,这些存储卷可以是从 iSCSI 块设备到特定厂商或云提供商的解决方案,甚至是简单的本地磁盘。Kubernetes 通过可插拔的 CSI 提供了很大的灵活性,使得将外部存储集成到由 K8s 管理的容器中变得非常方便。
我们还触及了容器安全性的基础知识。命名空间容器共享相同的内核,这就是为什么确保没有容器被攻破如此重要的原因。有一些安全扩展,如AppArmor和SELinux,它们通过可配置的配置文件为内核增加额外的保护层,此外还有一些最佳实践帮助最小化风险。
其中一种做法是使用常规的*(非 root)*用户帐户在容器中运行,另一种做法是确保在容器中执行受信任的代码。建议构建自己的镜像并将其保存在自己的注册中心,而不是使用来自未知第三方仓库的镜像。此外,您还可以将自动漏洞扫描作为镜像构建过程的一部分。
最后,我们学习了服务网格——一种特殊的基础设施层,它允许在不修改应用程序代码的情况下保护服务之间的网络通信。服务网格还提供了丰富的可观察性和流量管理功能,甚至允许你自动重试请求并拆分流量。
在接下来的章节中,我们将进入 KCNA 考试和本书的一个重要部分——即 Kubernetes 容器编排。现在,确保回答以下所有回顾问题来测试你的知识。
问题
在我们总结时,这里有一组问题供您测试关于本章节内容的知识。您将在附录的评估部分找到答案:
-
以下哪项是负责启动和停止容器的软件?
-
容器虚拟化管理程序
-
容器守护进程
-
Kubernetes
-
容器运行时
-
-
以下哪些是有效的容器类型(可多选)?
-
超空间的 -
沙箱化的 -
命名空间 -
虚拟化的
-
-
以下哪项是沙箱化容器的示例?
-
Kata
-
gVisor
-
Docker
-
containerd
-
-
以下哪项是虚拟化容器的示例?
-
Docker
-
containerd
-
gVisor
-
Kata
-
-
以下哪项允许你在 Kubernetes 中使用不同的容器运行时?
-
CSI
-
SMI
-
CNI
-
CRI
-
-
以下哪项允许你在 Kubernetes 中使用不同的服务网格?
-
CRI
-
SMI
-
CNI
-
CSI
-
-
为什么命名空间容器被认为不太安全?
-
它们使用旧的内核功能
-
他们需要 Kubernetes 来运行
-
它们共享主机内核
-
它们共享主机网络
-
-
哪种容器类型被认为是最轻量和最快的?
-
虚拟化的
-
沙箱化的
-
命名空间
-
超空间的
-
-
以下哪些存储解决方案可以与 Kubernetes 一起使用?
-
任何支持 NFS v4.1 的
-
任何与 CSI 兼容的
-
任何与 CNI 兼容的
-
任何第三方云提供商存储
-
-
服务网格正常运行需要改变应用程序代码的哪些部分?
-
应用程序必须用 Golang 重新编写
-
应用程序需要公开 SMI
-
应用程序必须是无状态的
-
无需应用程序更改
-
-
以下哪项是服务网格的特性(可多选)?
-
mTLS
-
流量管理
-
可观察性
-
流量压缩
-
-
服务网格数据平面包含哪些组件?
-
轻量级网络防火墙
-
轻量级网络代理
-
轻量级负载均衡器
-
轻量级 Web 服务器
-
-
以下哪些是服务网格(选择多个)?
-
Istio
-
Prometheus
-
Falco
-
Linkerd
-
-
以下哪项被认为是容器安全的最佳实践(选择多个)?
-
以
UID=0身份运行应用程序 -
扫描容器镜像以发现漏洞
-
以非 root 身份运行应用程序
-
使用 Kubernetes 运行容器
-
-
以下哪些技术可以用来提升容器安全性(选择多个)?
-
AppArmor
-
Ansible
-
SELinux
-
Firewalld
-
-
使用公共容器镜像仓库时,你可能会遇到哪些潜在问题(选择多个)?
-
第三方镜像可能随时被删除
-
第三方镜像可能因速率限制而无法下载
-
第三方镜像可能包含恶意软件
-
第三方镜像在开发环境中可能工作正常,但在生产环境中可能会失败
-
-
Kubernetes 可以生成哪些容器?
-
命名空间容器
-
K8s 不会生成容器;是运行时生成容器
-
虚拟化容器
-
沙箱容器
-
-
通常用于多主机容器网络的是哪种技术?链接
进一步阅读
若要了解更多本章涉及的主题,请查看以下资源:
-
Linkerd 概述:
linkerd.io/2.12/overview/ -
关于 Istio:
istio.io/latest/about/service-mesh/ -
开放系统互联(OSI):
en.wikipedia.org/wiki/OSI_model
第三部分:学习 Kubernetes 基础
在本部分中,你将从基础开始学习 Kubernetes:架构、资源和组件、特性以及使用案例。你将安装 Kubernetes,并通过 minikube 获得实际操作经验。你将学习如何运行无状态和有状态的工作负载,调试应用程序,并遵循 Kubernetes 的最佳实践。
本部分包含以下章节:
-
第五章,使用 Kubernetes 编排容器
-
第六章,使用 Kubernetes 部署和扩展应用程序
-
第七章,使用 Kubernetes 进行应用部署与调试
-
第八章,遵循 Kubernetes 最佳实践
更多推荐


所有评论(0)