Kubernetes应用程序开发认证(CKAD)学习指南-第2章 核心概念
第2章 核心概念
“核心概念”,CKAD课程是指Kubernetes的基本概念、它的API和在Kubernetes上操作应用程序的命令。在本章中,我们将讨论Kubernetes原语的基本结构以及与它们交互的主要入口点:基于命令行的客户端kubectl。
Pod是Kubernetes原语,用于在容器中运行应用程序。我们将谈到POD的主要方面,并简要讨论Kubernetes采用的容器技术:Docker。
在本章的最后,您将了解如何命令式地和声明式地创建Kubernetes对象,并了解如何创建Pod并定义其最基本的配置。
在较高层次上,本章涵盖以下概念:
- 容器的概念
- POD
- 命名空间
Kubernetes原语
Kubernetes原语是构建在Kubernetes体系结构中的基本块,用于在平台上创建和操作应用程序。即使是Kubernetes的初学者,也可能听说过Pod、Deployment和Service这些术语,它们都是Kubernetes的原语。在Kubernetes体系结构中,还有更多的服务于特定的目的的原语。
为了做一个类比,请回想一下面向对象编程的概念。在面向对象的编程语言中,类定义了现实世界功能的蓝图:属性和行为。Kubernetes原语相当于类。在面向对象编程中,类的实例是一个对象,它管理自己的状态,并具有与系统的其他部分通信的能力。无论何时创建Kubernetes对象,都会生成这样一个实例。
例如,Kubernetes中的Pod是一个类,其中可以有许多具有自身标识的实例。每个Kubernetes对象都有一个系统生成的惟一标识符(也称为UID),以清楚地区分系统的实体。稍后,我们将查看Kubernetes对象的属性。图2-1说明了Kubernetes原语和对象之间的关系。
图2-1 Kubernetes对象标识
每个Kubernetes原语都遵循一个通用的结构,如果你深入观察一个对象的清单,你可以观察到这个结构,如图2-2所示。Kubernetes清单中使用的主要标记语言是YAML。
图2-2 Kubernetes对象结构
让我们讨论一下Kubernetes系统中的每个部分及其相关性:
API version
Kubernetes API版本定义了原语的结构,并使用它来验证数据的正确性。API版本的作用类似于XML模式与XML文档或JSON模式与JSON文档的作用。版本通常会经历一个成熟过程,例如,从alpha到beta再到最终版。有时你会看到不同的前缀用斜杠分隔,例如apps。您可以通过运行kubectl api-versions命令列出与集群版本兼容的API版本。
Kind
Kind类型定义了原语的类型,例如,一种POD或Service类型。它最终回答了这个问题:“我们在这里处理的是什么类型的对象?”
Metadata
元数据描述关于对象的更高级的信息,例如,它的名称、它所在的名称空间,或者它是否定义了标签和注释。本节还定义了UID。
Spec
规范(简称“spec”)声明了所需的状态,例如:,这个对象创建后应该是什么样子?哪个镜像应该在容器中运行,或者应该设置哪些环境变量?
Status
状态描述对象的实际状态。Kubernetes控制器及其协调不断尝试将Kubernetes对象从期望状态转换为实际状态。如果YAML状态显示值为{},则该对象还没有被物化。
了解了这个基本结构之后,让我们看看如何在kubectl的帮助下创建Kubernetes对象。
使用kubectl与Kubernetes集群交互
kubectl是通过命令行与Kubernetes集群交互的主要工具。CKAD考试只关注kubectl的使用。因此,最重要的是要了解它的来龙去脉,并大量使用它。
在本节中,我将向您简要介绍它的典型使用模式。让我们首先来看看运行命令的语法。kubectl执行由命令、资源类型、资源名称和可选的命令行标志组成:
$ kubectl [command] [TYPE] [NAME] [flags]
该命令指定了您计划运行的操作。典型的命令是像create、get、describe或delete这样的动词。接下来,您需要提供正在处理的资源类型,可以是完整的资源类型,也可以是它的简短形式。例如,可以在这里处理一个服务,或者使用简短的形式svc。资源的名称标识,面向用户的对象标识符,实际上就是YAML表示中的metadata.name的值。请注意,对象名称与UID不同。UID是一个自动生成的kubernetes内部对象引用,通常不需要与之交互。在一个命名空间内,具有相同资源类型的所有对象的对象名称之间必须是唯一的。最后,您可以提供从零到多个命令行标志来描述额外的配置行为。命令行标志的一个典型例子是--port标志,它公开Pod的容器端口。
图2-3显示了一个完整的kubectl命令。
图2-3 Kubectl使用模式
在本书的过程中,我们将探索kubectl命令,这些命令将使您在CKAD考试期间最有效率。然而,还有很多,而且它们通常超出了应用程序开发人员日常使用的范围。接下来,我们将深入了解create命令,这是创建Kubernetes对象的命令式方法。我们还将比较命令式对象创建方法和声明式对象创建方法。
对象管理
可以用两种方式在Kubernetes集群中创建对象:命令式或声明式。下面的部分将描述每种方法,包括它们的优点、缺点和用例。
命令式的方法
创建对象的命令式方法不需要清单定义。您可以使用kubectl run或kubectl create命令动态创建对象。运行时需要的任何配置都由命令行选项提供。这种方法的好处是快速的周转时间,而不需要纠结于YAML结构:
$ kubectl run frontend --image=nginx --restart=Never --port=80
pod/frontend created
声明式方法
声明式方法使用kubectl create或kubectl apply命令从清单文件(在大多数情况下是一个YAML文件)创建对象。使用声明式方法的好处是可再现性和可维护,因为在大多数情况下文件被检入版本控制。在生产环境中,推荐使用声明式方法创建对象:
$ vim pod.yaml
$ kubectl create -f pod.yaml
pod/frontend created
混合方法
有时,您可能想要采用混合方法。您可以首先使用命令式方法生成清单文件,而不需要实际创建对象。通过使用命令行选项-o yaml和--dry-run=client来执行kubectl run命令:
$ kubectl run frontend --image=nginx --restart=Never --port=80 -o yaml --dry-run=client > pod.yaml
$ vim pod.yaml
$ kubectl create -f pod.yaml
pod/frontend created
$ kubectl describe pod frontend
Name: frontend
Namespace: default
Priority: 0
...
使用哪种方法?
在早期的Kubernetes版本中,您仍然能够使用kubectl run命令创建除Pods之外的其他对象。例如,通过正确的命令行选项组合,可以创建Deployments和CronJobs,但是kubectl run会显示一条弃用消息,提醒您在未来的版本中将不再支持它。
Kubernetes 1.18现在只允许使用run命令创建pod。您必须使用kubectl create命令强制地创建任何其他原语。您肯在web上找到许多仍然使用kubectl运行模式的CKAD准备材料。这在考试环境中已经不起作用了,因为Kubernetes版本已经升级到1.18版本之上。
在命令式地创建对象以优化周转时间时,在实践中,您肯定更希望使用声明式方法。YAML清单文件表示Kubernetes对象的最终真相来源。可以审计、共享版本控制的文件,并存储更改历史,以备您需要恢复到以前的版本。
其他值得注意的命令
到目前为止,我们只讨论了使用run和create命令使用命令式和声明式方法创建对象。kubectl可执行文件提供了对象管理范围中其他值得注意的命令。
删除对象
在任何时间,您都可以删除Kubernetes对象。在考试过程中,如果你在解决问题时犯了错误,想要从头开始,可以删除对象,重新开始。在工作环境中,可以删除不再需要的对象。delete命令提供了两个选择:通过提供名称来删除对象,或者通过指向创建对象的YAML清单来删除对象。
$ kubectl delete pod frontend
pod "frontend" deleted
$ kubectl delete -f pod.yaml
pod "frontend" deleted
修改(edit)活动对象
假设您已经创建了一个对象,并且希望对活动对象进行进一步的修改。可以在终端上使用edit命令修改对象。保存对象定义之后,Kubernetes将尝试在活动对象中启用这些更改。
$ kubectl edit pod frontend
替换(replace)活动对象
有时,您只是想以声明的方式替换现有对象的定义。replace命令用提供的YAML清单中的配置覆盖当前配置。提供给命令的YAML清单必须是一个完整的资源定义,正如create命令所用的那样。
$ kubectl replace -f pod.yaml
更新(apply)一个活动对象
最后,我想简要解释一下apply命令和create命令的主要区别。create命令实例化一个新对象。尝试为已有对象执行create命令将产生错误。apply命令用于完整地或增量地更新现有对象。这就是为什么提供的YAML清单可能是一个对象的完整定义,也可能是一个部分定义,例如,一个部署的副本的数量。请注意,如果对象还不存在,apply命令的行为与create命令类似,但是,YAML清单将需要包含对象的完整定义。
$ kubectl apply -f pod.yaml
pod/frontend configured
在下一节中,我们将通过创建Pods和与Pods交互来实践我们的知识。
理解Pods
Kubernetes API中最重要的原语是Pod。Pod允许您运行一个容器化的应用程序。在实践中,经常会遇到Pod和容器之间的一对一映射,然而,我们将在后面的章节中讨论一些用例,这些用例受益于在单个Pod中声明多个容器。
除了运行容器之外,Pod还可以使用其他服务,如持久存储、配置数据等。因此,可以将Pod看作是一个包装器,用于运行包含协调功能和其他Kubernetes对象的容器。
在深入了解Pod之前,让我们先看看遵从OCI的容器运行时环境在Kubernetes生态系统中所扮演的角色。我们将使用Docker守护进程作为这样一个容器运行时环境的例子。
容器化的过程
Kubernetes是一个容器编排器,它使用容器运行时环境来实例化Pods中的容器。默认情况下,这个容器运行时环境是Docker。虽然在考试中没有必要完全理解Docker,但至少应该熟悉它的基本知识。在本节中,我们将讨论Docker的基本概念和命令。如果你已经熟悉Docker,可以跳过这一节。
容器的概念
一个容器将一个应用程序打包成一个软件单元,包括它的运行时环境和配置。这个软件单元通常包括操作系统、应用程序的源代码或二进制文件、应用程序的依赖项和其他必要的系统工具。容器声明的目标是将运行时环境与应用程序解耦,以避免“在我的机器上可以工作”的问题。
将应用程序捆绑到容器中的过程通常称为“容器化”。容器化工作基于所谓Dockerfile中定义的指令。Dockerfile明确地说明了构建软件时需要做什么。操作的结果是一个镜像。镜像通常发布到注册中心供公众使用。Docker Hub是供公众使用的Docker镜像的主注册表。其他公共注册中心如GCR和Quay也可以使用。图2-4说明了在应用程序容器化上下文中的概念。
图2-4 容器化过程
总而言之,Dockerfile是如何构建软件的蓝图,镜像是容器化过程生成的工件,容器是服务于应用程序的镜像的一个正在运行的实例。让我们看一个更具体的例子。
示例:将基于java的应用程序容器化
让我们假设,我们想要容器化一个用Java编写的web应用程序。应用程序不是从头开始编写核心功能,而是使用Spring Boot框架作为外部库。此外,我们还希望通过环境变量的帮助来控制运行时行为。例如,您可能希望提供url和凭据来连接到其他服务(如数据库)。我们将一步一步地讨论这个过程,并从命令行终端上执行相关的Docker命令。如果您想继续学习,可以从project generator Spring Initalizr下载一个示例应用程序。
在创建镜像之前,我们必须编写Dockerfile。Dockerfile可以驻留在任何目录中,本质上是一个纯文本文件。下面的说明使用Java 11的OpenJDK发行版作为基本镜像。基本镜像包含操作系统和必要的工具,在本例中是Java。此外,我们还将二进制文件(可执行Java存档(JAR))包含到镜像的/app目录中。最后,我们定义执行程序的Java命令,并公开端口8080,以便在容器中运行时可以访问应用程序。例2-1概述了一个示例Dockerfile。
例2-1 Dockerfile用于构建Java应用程序
FROM openjdk:11-jre-slim
WORKDIR /app
COPY target/java-hello-world-0.0.1.jar java-hello-world.jar
ENTRYPOINT ["java", "-jar", "/app/java-hello-world.jar"]
EXPOSE 8080
有了Dockerfile,我们就可以继续创建镜像了。下面的命令提供镜像和标记的名称。最后一个参数指向上下文目录。一个上下文目录包含Dockerfile以及应该包含在镜像中的任何目录和文件。在这里,上下文目录是我们驻留的当前目录,由“.”引用。
$ docker build -t java-hello-world:1.0.0 .
Sending build context to Docker daemon 8.32MB
Step 1/5 : FROM openjdk:11-jre-slim
---> 973c18dbf567
Step 2/5 : WORKDIR /app
---> Using cache
---> 31f9c5f2a019
Step 3/5 : COPY target/java-hello-world-0.0.1.jar java-hello-world.jar
---> Using cache
---> 6a1deee17e9d
Step 4/5 : ENTRYPOINT ["java", "-jar", "/app/java-hello-world.jar"]
---> Using cache
---> 52a91ca70d86
Step 5/5 : EXPOSE 8080
---> Using cache
---> 3e9c22451a17
Successfully built 3e9c22451a17
Successfully tagged java-hello-world:1.0.0
如命令行终端输出所示,已经创建了镜像。您可能已经注意到,基本镜像已作为过程的一部分下载。运行以下命令,可以在本地Docker引擎环境中找到这两个镜像。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
java-hello-world 1.0.0 3e9c22451a17 About a minute ago 213MB
openjdk 11-jre-slim 973c18dbf567 20 hours ago 204MB
现在该在容器中运行应用程序了。run命令指向一个镜像并在容器中执行其逻辑。
$ docker run -d -p 8080:8080 java-hello-world:1.0.0
b0ee04accf078ea7c73cfe3be0f9d1ac6a099ac4e0e903773bc6bf6258acbb66
我们告诉该命令将本地主机上可访问的端口8080转发到容器端口8080。这意味着我们现在应该能够从本地机器解析应用程序的入口。正如您在下面的命令中所看到的,curl将输出消息“Hello World!”。
$ curl localhost:8080
Hello World!
要将镜像发布到注册中心,您可能需要做一些准备工作。大多数注册中心都要求您提供一个前缀,将用户名或主机名作为映像名的一部分。例如,Docker Hub要求您提供用户名。我的用户名是bmuschko,因此在发布到注册中心之前,我必须重新标记我的镜像。如果注册中心受到保护,您还必须提供凭据。对于Docker Hub,我们使用用户名登录。
$ docker login --username=bmuschko
Password: *****
WARNING! Your password will be stored unencrypted in /Users/bmuschko/
.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
$ docker tag java-hello-world:1.0.0 bmuschko/java-hello-world:1.0.0
$ docker push bmuschko/java-hello-world:1.0.0
The push refers to repository [docker.io/bmuschko/java-hello-world]
be6f48684f94: Pushed
ff3b0a3f736e: Pushed
a346421f0657: Mounted from library/openjdk
cab8f1f311d9: Mounted from library/openjdk
0a71386e5425: Mounted from library/openjdk
ffc9b21953f4: Mounted from library/openjdk
1.0.0: digest: sha256:aafd2ab53ba3ff66fe66d7ffc118c7a8ea993472132d1bdf417a \
62e212f3dcfd size: 1578
您经历了最常见的开发人员工作流之一:将应用程序容器化并将镜像发布到注册中心。关于Docker还有更多需要了解的东西,但这超出了本书的范围,我们在这里就不再深入了。更多信息请参考Docker文档。
创建Pods
在本章中,我们将只讨论如何创建运行单个容器的Pod。如果您想了解关于运行多个容器的pod的更多信息,请直接跳到第4章,多容器pod。该章解释了可应用的设计模式,以及如何使用kubectl与单个容器交互。
Pod定义需要为每个容器声明一个镜像。在创建Pod对象时,以命令方式或声明方式,容器运行时引擎(CRI)将检查容器镜像是否已经在本地存在。如果镜像不存在,CRI将从容器镜像注册中心下载它。默认情况下,注册中心是Docker Hub。一旦镜像在节点上存在,容器就会被实例化并运行。图2-5演示了执行流程。
图2-5 CRI与Docker镜像交互
run命令是命令式创建Pods的中心入口点。让我们讨论一下它的用法,以及你应该记住和练习的最重要的命令行选项。假设您想在一个Pod中运行一个Hazelcast实例。容器应该使用最新的Hazelcast镜像,公开端口5701,并定义一个环境变量。此外,我们还需要为Pod分配两个标签。下面的命令组合了这些信息,不需要对活跃对象进行任何进一步的编辑。
$ kubectl run hazelcast --image=hazelcast/hazelcast --restart=Never --port=5701 --env="DNS_DOMAIN=cluster" --labels="app=hazelcast,env=prod"
run命令提供了丰富的命令行选项。执行kubectl run --help命令或参考Kubernetes文档获得广泛的概述。对于CKAD考试,您不需要理解每一个命令。表2-1列出了最常用的选项。
表2-1 重要的kubectl run 命令行选项
选项 |
示例 |
说明 |
--image |
nginx |
要运行的容器镜像。 |
--port |
8080 |
此容器公开的端口。 |
--rm |
true |
如果为true,则删除该命令为附加容器创建的资源。 |
--env |
PROFILE=dev |
要在容器中设置的环境变量。 |
--labels |
app=frontend |
应用到Pod的以逗号分隔的标签列表。 |
一些开发人员更习惯于从YAML清单创建Pods。可能您已经习惯了声明式方法,因为您正在工作中使用它。可以打开编辑器,从Kubernetes在线文档中复制Pod YAML代码片段,并根据需要对其进行修改,从而为Hazelcast Pod表达相同的配置。例2-2显示了保存在文件Pod.yaml中的Pod清单。
例子2-2 Pod YAML清单
apiVersion: v1
kind: Pod
metadata:
name: hazelcast
labels:
app: hazelcast
env: prod
spec:
containers:
value: cluster
image: hazelcast/hazelcast
name: hazelcast
ports:
restartPolicy: Never
从清单创建Pod很简单。简单地使用create或apply命令,在“对象管理”和“其他值得注意的命令”部分章节解释。
$ kubectl create -f pod.yaml
pod/hazelcast created
列出Pods
既然已经创建了一个Pod,就可以进一步检查它的运行时信息了。kubectl命令提供了一个命令,用于列出集群中运行的所有pod: get pods。下面的命令列出名为hazelcast的Pod:
NAME READY STATUS RESTARTS AGE
hazelcast 1/1 Running 0 17s
真实世界的Kubernetes集群可以同时运行数百个pod。如果您知道感兴趣的Pod的名称,那么按名称查询通常会更容易一些。此时我们仍然只能看到一个pod。
$ kubectl get pods hazelcast
NAME READY STATUS RESTARTS AGE
hazelcast 1/1 Running 0 17s
POD生命周期阶段
因为Kubernetes是一个带有异步控制循环的状态引擎,所以在列出Pod时,Pod的状态可能不会立即显示运行状态。检索镜像和启动容器通常需要几秒钟的时间。在创建Pod时,对象会经历几个生命周期阶段,如图2-6所示。
图2-6 POD生命周期阶段
理解每个阶段的含义是很重要的,因为它可以让您了解POD的操作状态。例如,在考试期间,可能会要求您识别有问题的Pod,并进一步调试对象。Pod生命周期各阶段的说明如表2-2所示。
表2-2 POD生命周期阶段
选项 |
描述 |
Pending |
Kubernetes系统已经接受了Pod,但是还没有创建一个或多个容器镜像。 |
Running |
至少有一个容器仍在运行,或正在启动或重新启动。 |
Succeeded |
Pod中的所有容器都成功终止。 |
Failed |
Pod中的容器终止,因为至少有一个容器出现了错误。 |
Unknown |
无法获取Pod的状态。 |
查看 Pod 详细信息
get命令生成的呈现表提供了关于Pod的高级信息。但是,如果您需要更深入地了解这些细节呢?describe命令可以提供帮助。
$ kubectl describe pods hazelcast
Name: hazelcast
Namespace: default
Priority: 0
PriorityClassName: <none>
Node: docker-desktop/192.168.65.3
Start Time: Wed, 20 May 2020 19:35:47 -0600
Labels: app=hazelcast
env=prod
Annotations: <none>
Status: Running
IP: 10.1.0.41
Containers:
...
Events:
...
命令行终端输出包含Pod的元数据信息、它运行的容器和事件日志,比如Pod被调度时的故障。上面的示例输出经过了缩减,只显示了元数据部分。输出可能会很长。
有一种方法可以使你想要查看的信息更加具体。可以将describe命令与Unix grep命令组合使用。假设想要识别在容器中运行的镜像。
$ kubectl describe pods hazelcast | grep Image:
Image: hazelcast/hazelcast
访问Pod日志
作为应用程序开发人员,我们非常清楚在我们实现的应用程序产生的日志文件中会出现什么。在容器中操作应用程序时,可能会发生运行时故障。logs命令用来下载容器的日志输出信息。下面的输出表明Hazelcast服务器成功启动。
$ kubectl logs hazelcast
...
May 25, 2020 3:36:26 PM com.hazelcast.core.LifecycleService
INFO: [10.1.0.46]:5701 [dev] [4.0.1] [10.1.0.46]:5701 is STARTED
一旦容器接收到来自最终用户的通信,很可能会产生更多的日志内容。可以使用命令行选项 -f 将日志流化。如果想实时查看日志,这个选项很有帮助。
Kubernetes尝试在某些条件下重启容器,比如第一次尝试时无法解析镜像。当容器重新启动时,你将无法访问前一个容器的日志;logs命令只呈现当前容器的日志。但是,仍然可以通过添加 -p 命令行选项返回到前一个容器的日志。可以使用该选项来识别触发容器重新启动的根本原因。
在容器中执行命令
有些情况需要登录到容器中并探索文件系统。也许您想检查应用程序的配置或调试应用程序的当前状态。可以使用exec命令在容器中打开一个shell,以交互方式探索它,如下所示。
注意,您不必提供资源类型。此命令仅对Pod有效。两个破折号(--)将exec命令及其选项与要在容器中运行的命令分开。
也可以只在容器中执行单个命令。假设只需要查看对容器可用的环境变量,而不需要登录。只需删除交互标志 -it,并在两个破折号后提供相关命令。
...
DNS_DOMAIN=cluster
删除一个Pod
有很多场景需要删除一个Pod。在考试过程中,你可能会被要求移除一个Pod。或者,可能犯了一个配置错误,希望从头开始这个问题。
$ kubectl delete pod hazelcast
pod "hazelcast" deleted
请记住,Kubernetes试图优雅地删除一个Pod。这意味着Pod将尝试完成对Pod的活动请求,以避免对最终用户造成不必要的中断。一个优雅的删除操作可能需要5-30秒的时间,这是你不想在考试中浪费的时间。关于如何加速这个过程的更多信息,请参见第1章。
删除Pod的另一种方法是将delete命令指向用于创建Pod的YAML清单。效果是一样的。
$ kubectl delete -f pod.yaml
pod "hazelcast" deleted
配置Pods
CKAD课程希望您能够熟练地将YAML显示为文件或活动对象的形式,进行编辑。在这一节中,我想向您介绍一些在考试中可能会遇到的典型配置场景。后面的章节将通过接触其他配置方面来加深您的知识。
声明环境变量
应用程序需要公开一种使其运行时行为可配置的方法。例如,您可能希望将URL注入到外部web服务或声明数据库连接的用户名。环境变量是提供此运行时配置的常见选项。
避免为每个环境创建容器镜像 人们可能会忍不住说:“嘿,让我们为我需要的任何目标部署环境创建一个容器镜像,包括它的配置”。那是个坏主意。持续交付的一个实践和12因素应用原则是只为一次提交构建一个可部署的工件。在这样的场景中,工件是容器镜像。通过在实例化容器时注入运行时信息,可以控制偏离配置的运行时行为。您可以根据需要使用环境变量来控制行为。 |
在Pod YAML清单中定义环境变量相对容易。添加或增强容器的 env部分的配置。每个环境变量都由一个键-值对组成,由属性名和值表示。Kubernetes不强制或简化环境变量键的典型命名约定。建议遵循使用大写字母和下划线(_)分隔单词的标准。
为了演示一组环境变量,请看示例2-3。该代码片段描述了一个使用Spring Boot框架运行基于java的应用程序的Pod。
例子2-3 定义环境变量的Pod的YAML清单
apiVersion: v1
kind: Pod
metadata:
name: spring-boot-app
spec:
containers:
- image: bmuschko/spring-boot-app:1.5.3
name: spring-boot-app
env:
- name: SPRING_PROFILES_ACTIVE
value: prod
- name: VERSION
value: '1.5.3'
第一个名为SPRING_PROFILES_ACTIVE的环境变量定义了一个指向所谓概要文件的指针。一个概要文件包含特定于环境的属性。在这里,我们指向配置生产环境的概要文件。环境变量VERSION指定应用程序的版本。它的值对应于镜像的标记,并且可以由正在运行的应用程序公开,以便在用户接口中显示该值。
定义带有参数的命令
许多容器镜像已经定义了一个ENTRYPOINT或CMD指令。分配给该指令的命令将作为容器启动过程的一部分自动执行。例如,我们前面使用的Hazelcast镜像定义指令CMD ["/opt/ Hazelcast /start-hazelcast.sh"]。
在Pod定义中,可以重新定义镜像ENTRYPOINT和CMD指令,或者为容器指定一个命令来执行(如果镜像没有指定的话)。可以在命令和容器的args属性的帮助下提供这些信息。command属性覆盖了镜像的ENTRYPOINT指令。args属性替换镜像的CMD指令。
假设您想向一个还没有提供命令的镜像提供命令。通常有两种不同的方法,命令式的和声明式的。我们将在run命令的帮助下生成YAML清单。Pod应该使用busybox镜像并执行shell命令,以每10秒在无限循环中呈现当前日期。
$ kubectl run mypod --image=busybox -o yaml --dry-run=client --restart=Never > pod.yaml -- /bin/sh -c "while true; do date; sleep 10; done"
您可以在生成的简要pod.yaml文件中看到。如示例2-4中显示的,该命令已被转换为args属性。Kubernetes在单独行中指定每个参数。
例子2-4 包含args属性的YAML清单
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- args:
- /bin/sh
- -c
- while true; do date; sleep 10; done
image: busybox
name: mypod
restartPolicy: Never
如果您要手工编写YAML清单,则可以通过command和args属性的组合实现相同的效果。例2-5展示了不同的方法。
例子2-5 包含命令和args属性的YAML文件
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- command: ["/bin/sh"]
args: ["-c", "while true; do date; sleep 10; done"]
image: busybox
name: mypod
restartPolicy: Never
可以快速验证声明的命令是否确实完成了它的工作。首先,我们创建Pod实例,然后跟踪日志。
Fri May 29 00:49:06 UTC 2020
Fri May 29 00:49:16 UTC 2020
Fri May 29 00:49:26 UTC 2020
Fri May 29 00:49:36 UTC 2020
...
理解命名空间Namespaces
命名空间是一种API构造,以避免命名冲突,并表示对象名称的作用域。命名空间的一个很好的用例是按团队或职责隔离对象。CKAD考试中的大多数问题都要求您在设置的特定命名空间中执行命令。下面几节将简要介绍处理命名空间所需的基本操作。
列出命名空间
Kubernetes集群从几个初始命名空间开始。您可以使用以下命令列出它们。
$ kubectl get namespaces
NAME STATUS AGE
default Active 157d
kube-node-lease Active 157d
kube-public Active 157d
kube-system Active 157d
默认的命名空间hosts对象还没有分配给显式命名空间。以前缀kube-开头的命名空间不被认为是终端用户命名空间。它们是由Kubernetes系统创造的。作为应用程序开发人员,不必与它们交互。
创建和使用命名空间
需要创建一个新的命名空间,使用create namespace命令。下面的命令使用了code-red的名称。
$ kubectl create namespace code-red
namespace/code-red created
$ kubectl get namespace code-red
NAME STATUS AGE
code-red Active 16s
YAML清单的相应表示如下所示。
apiVersion: v1
kind: Namespace
metadata:
name: code-red
命名空间就位后,就可以在其中创建对象。可以使用命令行选项--namespace或其缩写形式-n来实现这一点。以下命令在命名空间code-red中创建一个新的Pod,然后列出命名空间中可用的Pod。
$ kubectl run pod --image=nginx --restart=Never -n code-red
pod/pod created
$ kubectl get pods -n code-red
NAME READY STATUS RESTARTS AGE
pod 1/1 Running 0 13s
删除一个命名空间
删除命名空间会对其中存在的对象产生级联效果。删除命名空间将自动删除其对象。
$ kubectl delete namespace code-red
namespace "code-red" deleted
$ kubectl get pods -n code-red
No resources found in code-red namespace.
总结
Kubernetes在原语的帮助下展示了它用于部署和操作云原生应用程序的功能。每个原语遵循一个通用结构:API版本、kind、metedata和所需的资源状态(也称为spec)。在创建或修改对象时,Kubernetes调度程序自动尝试确保对象的实际状态遵循定义的规范。每个活动对象都可以被检查、编辑和删除。
课程的“核心概念”部分强调了Pod的概念。Pod是一个Kubernetes原语,负责在容器中运行应用程序。Kubernetes使用Docker作为默认的容器运行时技术。一个Pod可以定义一个或多个使用容器镜像的容器。在创建容器镜像时,解析容器镜像并使用它引导应用程序。每个Pod都可以用相关的YAML配置进一步定制。
Kubectl充当基于cli的客户端,与Kubernetes集群交互。可以使用它的命令和flags来管理Kubernetes对象。
考试要点
了解如何管理Kubernetes对象
在Kubernetes中,可以使用命令式或声明式方法创建对象。命令式方法是最省时的创建对象的方法。对于pod,使用命令kubectl run,对于任何其他资源,使用命令kubectl create。此外,练习使用kubectl edit编辑活动对象,并了解如何通过kubectl delete删除它们。
知道如何与Pods交互
Pod在容器中运行应用程序。通过使用kubectl get或kubectl description命令检查对象,可以检查Pod的状态和配置。熟悉Pod的生命周期阶段,以便能够快速诊断错误条件。kubectl logs命令可以用于下载容器日志信息,而不必到到容器中运行shell。使用命令kubectl exec进一步探索容器环境,例如检查进程或检查文件。
高级Pod配置选项
有时,您必须从Pod的YAML清单开始,然后以声明的方式创建Pod。如果想向容器提供环境变量或声明一个自定义命令,则可能会出现这种情况。通过复制粘贴Kubernetes文档中的相关代码片段来实践不同的配置选项。
示例练习
这些练习的答案可以在附录A中找到。
- 创建一个名为nginx的新Pod,运行镜像nginx:1.17.10。公开容器端口80。Pod应该位于名为ckad的命名空间中。
- 获取Pod的详细信息,包括它的IP地址。
- 创建一个使用busybox镜像的临时Pod,在容器内执行wget命令。wget命令应该访问nginx容器公开的endpoint。您应该在终端中看到呈现的HTML响应体。
- 获取nginx容器的日志。
- 将环境变量DB_URL=postgresql://mydb:5432和DB_USERNAME=admin添加到nginx Pod的容器中。
- 打开nginx容器的shell,检查当前目录ls -l的内容。
- 为一个名为loop的Pod创建一个YAML清单,该loop在容器中运行busybox镜像。容器应该运行以下命令:for i in {1..10};do echo "Welcome $i times";done。从YAML清单创建Pod。Pod的状态是什么?
- 编辑名为loop的Pod。将命令更改为在一个无无限循环中运行。每次迭代都应该返回当前日期。
- 检查事件和Pod loop的状态。
- 删除名称空间ckad及其Pods。