【Docker系列】Section 2: Creating Kubernetes Development Clusters, Understanding objects, and Exposing Services②

o-O-oO / 2024-04-26 / 原文

继续上文,【Docker系列】Section 2: Creating Kubernetes Development Clusters, Understanding objects, and Exposing Services①
引言:

Section 2 中,我们将转移到Kubernetes集群和对象。
本节的第一章将解释如何使用一个流行的工具来创建库集群,称为KinD。
我们将解释如何创建不同的网络集群,其范围从single-node(单节点)集群到使用HAProxy作为工作节点的负载平衡器的 multiple-node(多节点)集群。
通过一个可工作的Kubernetes集群,我们将进入下一章,它将回顾Kubernetes基础设施组件和最常用的Kubernetes对象。

我们通过解释一个经常被误解的主题来结束这一节,即在集群中公开服务。
我们将研究Kubernetes服务类型,讨论如何使用第7层和第4层负载平衡器,以及看看如何使用外部dns为服务注册动态名称。

本书的 Section 2 包括以下章节:

• Chapter 4, Deploying Kubernetes Clusters Using KinD
• Chapter 5, Kubernetes Bootcamp -- 📍
• Chapter 6, Services, Load Balancers, and External-DNS

正文:

第五章--Kubernetes Bootcamp

我们确信你们中的许多人在某些程度上使用了库伯内特——你们可能在生产中运行集群,或者你们可能使用kubeadm、minikube或Docker Desktop。
我们写这本书的目标是超越库伯内特的基础知识,因此,我们不想重复库伯内特的所有基础知识。
相反,我们添加了这一章作为一个训练营,为任何可能是新的库伯内特或可能只是玩了一点的人。

因为这是一个训练营的章节,我们不会深入了解每个主题,但到最后,你应该对库伯内特斯有足够的基础知识来理解剩下的章节。
如果您有很强的库伯内特背景,您可能仍然会发现这一章是一个有用的复习课,我们将从Chapter 6, Services, Load Balancing, and External DNS开始讨论更复杂的主题。

在本章中,我们将介绍一个正在运行的Kubernetes集群的组件,其中包括控制平面和工作节点(s)。
我们将详细介绍每个库伯内特斯对象及其用例。
如果您过去使用过库伯特,并且习惯使用库伯特,并且完全理解库伯特对象(如DaemonSets(守护集)、StatefulSets(状态集)、ReplicaSets(复制集)等……),您可能想要跳到Chapter 6, Services, Load Balancing, and External DNS,在那里我们将使用KinD安装库伯特。

在本章中,我们将介绍以下主题:
• An overview of Kubernetes components -- 📍
• Exploring the control plane -- 📍
• Understanding the worker node components -- 📍
• Interacting with the API server -- 📍
• Introducing Kubernetes objects -- 📍

Technical requirements
本章有以下技术要求:
• 一个Ubuntu 18.04服务器,至少有4g(GB)的随机访问内存(RAM)
• A KinD Kubernetes 集群


您可以在以下GitHub存储库中访问本章的代码: https://github.com/PacktPublishing/Kubernetes-and-Docker-TheComplete-Guide.

• An overview of Kubernetes components
在任何基础设施中,了解系统如何一起工作以提供服务始终是一个好主意。
由于目前有如此多的安装程序选项,许多库伯特用户不需要了解库伯内特组件是如何集成的。
几年前,如果您想运行库伯集群,您需要手动安装和配置每个组件。
安装一个功能良好的集群是一个陡峭的学习曲线,这经常导致沮丧,导致许多人和公司说“库伯内特只是太难了”。
手动安装的优点是,您真正了解每个组件是如何交互的,如果集群在安装后遇到问题,您就知道要寻找什么。


如今,大多数人会点击云提供商上的一个按钮,并在几分钟内拥有一个功能齐全的库伯内特斯集群。
内部安装也变得同样简单,通过谷歌、红帽、牧场主等选项,消除了安装库伯内特集群的复杂性。
我们所看到的问题发生在您在安装后遇到问题或有问题时。
由于您没有配置Kubernetes组件,因此您可能无法向开发人员解释如何在工作节点上安排Pod。
最后,由于您运行的是由第三方提供的安装程序,因此它们可能会启用或禁用您不知道的特性,从而导致安装可能违反您公司的安全标准。
要理解库伯内特组件是如何一起工作的,您必须首先了解库伯特集群的不同组件。
❓下图来自Kubernetes。显示了库伯集群组件的高级概述:
如您所看到的,库伯内特斯集群是由几个组件组成的。
在本一章的过程中,我们将讨论这些组件以及它们在库伯内特集群中扮演的角色。

• Exploring the control plane

❓❓❓ The Kubernetes API server
❓❓❓ The Etcd database
❓❓❓ kube-scheduler
❓❓❓ kube-controller-manager
❓❓❓ cloud-controller-manager

顾名思义,控制平面控制着集群的每个方面。如果控制平面下降,您可能可以想象集群将会遇到问题。
如果没有控制平面,集群将不具有任何调度能力,这意味着正在运行的工作负载将继续运行,除非它们被停止并重新启动。
由于控制平面非常重要,所以总是建议您至少有三个主节点。
许多生产安装运行三个以上的主节点,但已安装的节点数量应该始终为奇数。
让我们通过检查每一个组件来看看为什么控制平面及其组件对运行的集群如此重要。

集群中要理解的第一个组件是kube-apiserver组件。
由于Kubernetes是由application programming interface(API)-driven的,所以进入集群中的每个请求都要通过API服务器。
让我们来看看使用API端点的一个简单的get节点请求,如下所示:

https://10.240.100.100:6443/api/v1/nodes?limit=500

Kubernetes的用户部署到与API服务器交互的一种常见方法是kubectl实用程序。
使用kubectl发出的每个命令都会在幕后调用一个API端点。
在前面的示例中,我们执行了一个kubectl获取节点命令,该命令向10.240.100.100端口6443上的kube-apiserver进程发送了一个API请求。
API调用请求了/api/vi/nodes端点,该端点返回了集群中的节点列表,如下屏幕截图所示:

如果没有正在运行的API服务器,对集群的所有请求都将失败。
因此,正如您所看到的,始终运行库组件是非常重要的。
通过运行三个或更多的主节点,我们可以限制丢失一个主节点所产生的任何影响。
❗注意:
当运行多个主节点时,您需要在集群前面有一个负载平衡器。
库伯内特斯的API服务器可以面向大多数标准的解决方案,包括F5、HAProxy和Seesaw。

说Etcd是你的库伯特集群。Etcd是一个快速、高可用的分布式密钥值数据库,库伯内特斯使用它来存储所有集群数据。
集群中的每个资源在数据库中都有一个键。
如果您登录到运行Etcd的节点或pod,您可以使用etcdctl可执行文件来查看数据库中的所有键。
下面的代码片段显示了一个来自运行KinD的集群的示例:

EtcdCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 
--cacert=/etc/kubernetes/pki/etcd/ca.crt --key=/etc/kubernetes/
pki/etcd/server.key --cert=/etc/kubernetes/pki/etcd/server.crt 
get / --prefix --keys-only

前面命令的输出包含太多的数据,无法在本章中列出所有数据。
一个基本的KinD集群将返回大约317个条目。
所有键都以/registry/<object>开头。
例如,返回的一个键是cluster-admin的clusterRole,如下所示:/registry/clusterrolebindings/cluster-admin
我们可以使用etcdctl实用程序,通过稍微修改我们之前的命令来检索该值,如下所示:

EtcdCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 
--cacert=/etc/kubernetes/pki/etcd/ca.crt --key=/etc/kubernetes/
pki/etcd/server.key --cert=/etc/kubernetes/pki/etcd/server.crt 
get /registry/clusterrolebindings/cluster-admin

输出将包含shell无法解释的字符,但您将得到存储在Etcd中的数据的概念。对于 cluster-admin 键,输出将显示以下内容:

我们在Etcd中解释这些条目的原因是为了提供一个关于库伯内特斯如何使用它来运行集群的背景知识。
您已经直接从数据库看到了cluster-admin 键的输出,但是在日常生活中,您将使用kubectl查询API服务器,

kubectl get clusterrolebinding cluster-admin -o yaml,它将返回以下内容:

如果您查看来自kubectl命令的输出,并将其与来自etcdctl查询的输出进行比较,您将看到匹配的信息。
当您执行kubectl命令时,请求将进入API服务器,该服务器然后在Etcd数据库中查询对象的信息。

顾名思义,kube-scheduler 组件负责调度运行的 pods。
每当在集群中启动Pod时,API服务器就会接收请求,并根据包括主机资源和集群策略在内的多个标准来决定在哪里运行工作负载。

kube-controller-manager组件实际上是包含在一个二进制文件中的多个控制器的集合。
在单个可执行文件中包含四个控制器,通过在单个进程中运行所有四个控制器,降低了复杂性。
kube-controller-manager组件中包含的四个控制器分别是节点、复制、端点、服务帐户和令牌控制器。

每个控制器都为一个集群提供了一个独特的功能,每个控制器及其功能列在这里:

每个控制器都运行一个不终止(永不结束)的控制回路。这些控制循环监视每个资源的状态,从而进行规范化资源状态所需的任何更改。
例如,如果需要将部署从一个节点扩展到三个节点,复制控制器会注意到当前状态有一个节点正在运行,而期望的状态是有三个节点正在运行。
要将当前状态移动到所需状态,复制控制器将请求另外两个端口。

这是一个您可能没有遇到的组件,这取决于集群的配置方式。
类似于 kube-controller-manager 组件,该控制器在单个二进制文件中封装四个控制器。
所包含的控制器是节点、路由、服务和卷控制器——每个控制器都负责与它们各自提供的云服务提供商进行交互。

• Understanding the worker node components

❓❓❓ kubelet
❓❓❓ kube-proxy
❓❓❓ Container runtime


顾名思义,工作节点负责运行工作负载。
当我们讨论控制平面的 kube-scheduler 组件时,我们提到,当调度一个新的Pod时,kube-scheduler 组件将决定用哪个节点运行Pod。
它使用已从工作节点发送给它的信息来执行此操作。
这些信息不断更新,以帮助在集群周围传播荚,以有效地利用资源。
下面是工作节点组件的列表。

你可能会听到一个工作节点被称为 kubelet 。kubelet 是一个在所有工作节点上运行的代理,它负责运行实际的容器。

与名称相反,kube-proxy根本不是一个代理服务器。
kube-代理负责在Pod和外部网络之间路由网络通信。

这在图中没有表示出来,但是每个节点也需要一个容器运行时。
一个容器运行时负责运行这些容器。你可能想到的是Docker。
虽然Docker是一个容器运行时,但它并不是唯一可用的运行时选项。
在过去的一年里,其他选项已经可用,并迅速取代Docker成为首选的容器运行时。
两个最突出的Docker替代品是CRI-O和集装箱。
对于书籍练习,我们将使用KinD创建一个库伯内特斯集群。
在撰写本文时,KinD只提供了对Docker作为容器运行时的官方支持,而对Podman的支持则有限。

• Interacting with the API server

❓❓❓ Using the Kubernetes kubectl utility
❓❓❓ Understanding the verbose option
❓❓❓ General kubectl commands

正如我们前面提到的,您可以使用直接API请求或kubectl实用程序与API服务器进行交互。
在本书中,我们将集中在大部分交互中使用kubectl,但我们将在适用的情况下使用直接API调用。

kubectl是一个单一的可执行文件,它允许您使用command-line interface(CLI)与Kubernetes API进行交互。
它可用于大多数主要的操作系统和体系结构,包括Linux、Windows和Mac。

大多数操作系统的安装说明都位于位于https://kubernetes.io/docs/tasks/tools/install-kubectl/.的库伯内特斯站点由于我们在书中使用Linux作为我们的练习的操作系统,所以我们将介绍在Linux机器上安装kubectl。按照以下步骤操作:

1.要下载最新版本的kubectl,您可以运行一个将下载它的curl命令,如下所示:

curl -LO https://storage.googleapis.com/kubernetesrelease/release/`curl -s https://storage.googleapis.com/
kubernetes-release/release/stable.txt`/bin/linux/amd64/
kubectl

2.下载后,您需要通过运行以下命令来使该文件可执行:

chmod +x ./kubectl

3.最后,我们将把可执行文件移动到您的路径上,如下所示:
sudo mv ./kubectl /usr/local/bin/kubectl

现在系统上有最新的kubectl实用工具,可以从任何工作目录执行kubectl命令。
库柏特每3个月更新一次。这包括对基本库伯内特集群组件和库贝特工具的升级。
您可能会遇到集群和kubectl命令之间的版本不匹配,这需要您升级或下载kubectl可执行文件。
您始终可以通过运行一个 kubectl version 命令来检查两者的版本,该命令将同时输出API服务器和kubectl客户端的版本。
版本检查的输出显示在以下代码片段中:

从输出中可以看到,kubectl客户端运行的是1.17.1版本,集群运行的是1.17.0版本。
两者之间的微小版本差异不会引起任何问题。事实上,官方支持的版本差异是在一个主要版本版本中。
因此,如果您的客户端运行的是版本1.16,而集群运行的是版本1.17,那么您将在支持的版本差异之内。
虽然可能支持此一点,但这并不意味着如果您试图使用更高版本中包含的任何新命令或对象,就不会遇到问题。
通常,您应该尽量保持集群和客户端版本的同步,以避免出现任何问题。

在本章的其余部分,我们将讨论kubernet对象以及如何与API服务器交互来管理每个对象。
但是在深入研究不同的对象之前,我们想提到kubectl实用工具中一个通常被忽视的选项:verbose的选项。

当执行kubectl命令时,默认情况下将看到的唯一输出是对命令的任何直接响应。
如果您要查看 kube-system 名称空间中的所有pods,那么您将会收到一个包含所有pods的列表。
在大多数情况下,这是所需的输出,但是如果您发出了get Pods请求并从API服务器接收了一个错误呢❓
你怎样才能得到更多关于导致错误的信息❓

verbose的选项有多个级别,范围从0到9;数字越高,你收到的输出就越多。
以下截图来自库伯内特的网站,详细介绍了每个级别和输出将包括什么:

您可以通过向任何kubectl命令添加 -v--v 选项来实验这些级别。

CLI允许您以命令式和声明性的方式与库伯内特人进行交互。
使用命令式命令需要您告诉库伯内该做什么--【eg】,kubectl run nginx –image nginx
这告诉API服务器创建一个名为nginx的新部署,运行名为nginx的映像。
虽然命令命令对于开发和快速修复或测试很有用,但您将在生产环境中更频繁地使用声明性命令。
在声明性命令中,您可以告诉库伯内特你想要什么。
要使用声明性命令,您需要向API服务器发送清单,通常用YAML Ain‘t markup language(YAML)编写,它声明您希望库伯内特创建的内容。

kubectl包括可以提供一般集群信息或有关对象的信息的命令和选项。
下表包含命令及其用途的小表。
我们将在未来的章节中使用许多这样的命令,所以您将在整个书中看到它们的实际作用:

了解了每个库伯内特组件以及如何使用命令式命令与API服务器交互,现在我们可以转向库伯内特对象以及如何使用kubectl来管理它们。

• Introducing Kubernetes objects

❓❓❓ Kubernetes manifests
❓❓❓ What are Kubernetes objects?
❓❓❓ Reviewing Kubernetes objects
❓❓❓ ConfigMaps
❓❓❓ Endpoints
❓❓❓ Events
❓❓❓ Nodes
❓❓❓ Persistent Volume Claims
❓❓❓ PVs
❓❓❓ Pods
❓❓❓ Replication controllers
❓❓❓ ResourceQuotas
❓❓❓ Secrets
❓❓❓ Service accounts
❓❓❓ Services
❓❓❓ CustomResourceDefinitions
❓❓❓ DaemonSets
❓❓❓ Deployments
❓❓❓ ReplicaSets
❓❓❓ StatefulSets
❓❓❓ HorizontalPodAutoscalers
❓❓❓ CronJobs
❓❓❓ Jobs
❓❓❓ Events
❓❓❓ Ingresses
❓❓❓ NetworkPolicies
❓❓❓ PodSecurityPolicies
❓❓❓ ClusterRoleBindings
❓❓❓ ClusterRoles
❓❓❓ RoleBindings
❓❓❓ Roles
❓❓❓ CsiDrivers
❓❓❓ CsiNodes
❓❓❓ StorageClasses

这部分将包含大量的信息,由于这是一个训练营,我们将不会深入介绍每个对象。
正如你所想象的,每个对象都可以有自己的章节,或者在一本书中的多个章节。
由于有很多关于库伯内特的书详细介绍了基础对象,我们将只涵盖每个必要的细节,以理解每一个。
在下面的章节中,我们将在使用书籍练习构建集群时包括对象的其他细节。
在我们继续了解库伯内特的物体到底是什么之前,让我们先解释一下库伯内特的表现。

我们将用于创建库伯内特斯对象的文件被称为清单。
可以使用YAML或JavaScript object notation(JSON)创建清单--大多数清单使用YAML,这是我们将在整个书中使用的格式。
清单的内容将根据创建对象的不同而不同。
至少,所有的清单都需要一个基本配置,其中包括apiVersion、对象 kinD 和 metadata 字段,可以在这里看到:

apiVersion: apps/v1
KinD: Deployment
metadata:
 labels:
 app: grafana
 name: grafana
 namespace: monitoring

上述清单并不完整;我们只显示完整部署清单的开始。
正如您在文件中所看到的,我们从所有清单都必需的三个字段开始:apiversion、KinD和元数据字段。
您可能还会注意到该文件中存在空格。
YAML是非常特定于格式的,如果任何一行的格式甚至偏离了一个空格,那么在尝试部署清单时都将收到一个错误。
这需要一段时间来适应,即使在创建了很长一段时间的清单之后,格式化问题仍然会不时地出现。

❓❓❓ What are Kubernetes objects?

当您想从集群中添加或删除某些东西时,您正在与库伯内特对象交互。
对象是集群用来保存所需状态列表的对象。
所需的状态可能是创建、删除或缩放一个对象。
根据对象的所需状态,API服务器将确保当前状态等于所需状态。

要检索集群支持的对象列表,可以使用 kubectl api-resources 命令。
API服务器将回复所有对象的列表,包括任何有效的短名称、名称空间支持和受支持的API组。
一个基本集群中包含大约53个基本对象,但下面的截图显示了最常见对象的简短列表:


由于本章是一个训练营,所以我们将简要回顾一下列表中的许多对象。
为了确保您可以遵循其余的章节,我们将提供每个对象的概述以及如何与它们交互。
一些对象也将在未来的章节中进行更详细的解释,包括Ingress, RoleBindings, ClusterRoles, StorageClasses等。

为了使这部分更容易遵循,我们将按照 kubectl api-services 命令提供的顺序呈现每个对象。
集群中的大多数对象都在名称空间中运行,为了创建/编辑/读取它们,您应该为任何kubectl命令提供 -n <名称空间>选项。
要查找接受名称空间选项的对象列表,您可以引用我们以前的 get api-server 命令的输出。
如果一个对象可以被命名空间引用,则命名空间的列将显示为true。
如果对象仅被集群级别引用,则命名空间的列将显示为false。

配置图将数据存储在键值对中,这提供了一种使配置与应用程序分离的方法。
配置映射可以包含来自文字值、文件或目录的数据。
下面是一个非常必要的例子:

kubectl create configmap <name> <data>

name选项将根据配置图的来源而有所不同。
要使用文件或目录,您需要提供 --from-file 选项以及文件或整个目录的路径,如下所示:

kubectl create configmap config-test --from-file=/apps/nginx-config/nginx.conf

这将创建一个名为 config-test 的 ConfigMap,其中nginx.conf键包含nginx.conf文件的内容作为值。
如果需要在单个配置图中添加多个键,则可以将每个文件放入一个目录中,并使用该目录中的所有文件创建配置图。
例如,在一个位于~/config/myapp的目录中有三个文件。
目录中有三个文件,每个文件都包含数据,称为config1、config2和config3。
要创建将每个文件添加到键中的配置图,需要提供 --from-file 选项并指向目录,如下所示:

这将创建一个新的ConfigMap,其中有三个关键值,分别称为config1、config2和config3。
每个键都将包含一个等于目录中每个文件的内容的值。
为了快速显示一个ConfigMap,使用示例从一个目录创建一个ConfigMap,
我们可以使用get命令,kubectl get configmaps config-test,导致以下输出:

我们可以看到,配置图包含三个键,在 DATA 列下显示为一个3。
为了更详细地看,我们可以使用相同的get命令,并作为YAML输出每个键的值,
通过添加 -o yaml 选项到YAML获得kubectl get configmaps config-test -o yaml命令,从而得到以下输出:

查看前面的输出,您可以看到每个键都与文件名匹配,并且每个键的值都包含每个文件中的数据。
您应该记住的配置映射的一个限制是,任何具有对象权限的人都很容易访问数据。
从前面的输出中可以看到,一个简单的get命令以明文形式显示数据。
由于这种设计,您不应该在配置图中存储密码等敏感信息。
在本节的后面,我们将介绍一个被设计为存储敏感信息的对象,称为Secret

端点将服务映射到 Pod 或 Pods。当我们解释 Service 对象时,这将更有意义。
现在,您只需要知道您可以使用 kubectl get endpoints 命令来检索端点。
在一个新的KinD集群中,您将在默认名称空间中看到针对Kubernetes API服务器的一个值,如下代码片段所示:

输出显示,集群有一个名为库伯特的服务,该服务在端口6443上的因特网协议(IP)地址172.17.0.2上有一个端点。
稍后,在查看端点时,您会发现它们可以用于排除服务和入口问题。

Events 对象将显示命名空间的任何事件。
要获取 kube-system 名称空间的事件列表,可以使用 kubectl get events -n kube-system 命令。

名称空间是一个将集群划分为逻辑单元的对象。
每个名称空间都允许对资源进行细粒度管理,包括权限、配额和报告。
名称空间对象用于名称空间任务,这些任务是集群级的操作。
使用名称空间对象,您可以执行包括create、delete、edit和get等命令。
该命令的语法是 kubectl <动词> ns <名称空间名称>
例如,为了描述 kube-system 的名称空间,我们将执行一个 kubectl describe namespaces kube-system 命令。这将返回名称空间的信息,包括任何标签、注释和已分配的配额,如以下代码片段所示:


在前面的输出中,您可以看到此名称空间没有分配任何标签、注释或资源配额。
本节仅打算介绍名称空间作为多租户集群中的管理单元的概念。
如果计划使用多个租户运行集群,则需要了解如何使用名称空间来保护集群。

nodes 对象是集群级资源,用于与集群节点交互的资源。
这个对象可以与各种操作一起使用,包括get、describe、label和 annotate。
要使用kubectl检索集群中所有节点的列表,您需要执行一个 kubectl get nodes 命令。
在运行简单单节点集群的新KinD集群上,将显示如下所示:

您还可以使用节点对象来使用 describe 命令获取单个节点的详细信息。
为了获得前面列出的KinD节点的描述,我们可以执行kubectl describe node KinD-control-plane,它将返回节点的详细信息,包括消耗的资源、运行的端口、IP classless inter-domain routing (无类域间路由,即CIDR)范围等等。

我们将在后面的章节中更深入地描述 Persistent Volume Claims(持久容量声明,即PVCs),但现在您只需要知道 Pod 使用 PVC 来消耗持久存储。
PVC 使用 Persistent Volume(持久性卷 ,即 PV )来映射存储资源。
与我们讨论过的大多数其他对象一样,您可以在 PVC 对象上发出获取、描述和删除命令。
由于这些都被 Pod ,所以它们是一个有名称空间的对象,并且必须在与将使用 PVC 的Pod (s)相同的名称空间中创建。

PVC使用pv来创建 PVC 和底层存储系统之间的链接。
手动维护pv是一项混乱的任务,在现实世界中应该避免维护它,因为Kubernetes包括使用 Container Storage Interface(容器存储接口,即CSI)管理大多数常见存储系统的能力。
正如在 PVC 对象部分中提到的,我们将讨论库伯内如何自动创建将链接到PVC的pv。

Pod对象用于与运行容器的Pod进行交互。
使用 kubectl 实用程序,您可以使用诸如 get 、delete 和 describe 等命令。
例如,如果您想获得 kube-system 名称空间中所有 pods 的列表,您将执行一个 kubectl get Pods -n kube-system 命令,该命令将返回名称空间中的所有pods,如下所示:

虽然您可以直接创建一个Pod,但您应该避免这样做,除非您正在使用一个Pod进行快速故障排除。
直接创建的豆荚不能使用库伯内特斯提供的许多功能,包括缩放、自动重新启动或滚动升级。
您不应该直接创建豆荚,而应该使用部署,或者在极少数情况下使用 ReplicaSet(复制集)对象或复制控制器。

复制控制器将管理运行的数量,保持所需指定的副本始终运行。
如果创建复制控制器并将副本计数设置为5,控制器将始终保持应用程序的5个端口运行。
复制控制器已被复制集对象所取代,我们将在其自己的部分中讨论。
虽然您仍然可以使用复制控制器,但您应该考虑使用部署或复制集对象。

在多个团队之间共享一个Kubernetes集群正变得非常常见,这被称为多租户集群。
由于您将有多个团队在一个集群中工作,因此您应该考虑创建配额来限制单个租户消耗集群中或节点上的所有资源的任何可能性。
可以对大多数集群对象设置限制,包括以下内容:

• Central processing unit (CPU)
• Memory
• PVCs
• ConfigMaps
• Deployments
• Pods, and more

设置限制,将停止创建任何其他对象。
如果您为一个名称空间设置了10个荚的限制,并且用户创建了一个新的部署,尝试启动11个豆荚,第11个荚将无法启动,用户将收到一个错误。
为内存和CPU创建配额的基本清单文件是这样的:

apiVersion: v1
KinD: ResourceQuota
metadata:
 name: base-memory-cpu
spec:
 hard:
 requests.cpu: "2"
 requests.memory: 8Gi
 limits.cpu: "4"
 limits.memory: 16Gi

这将设置名称空间可以用于 CPU 和内存请求的资源总量和限制。
创建了配额后,您就可以使用 kubectl describe 命令查看使用情况。

在我们的示例中,我们将资源配额命名为ResourceQuota base-memory-cpu

为了查看使用情况,我们将执行 kubectl get resourcequotas base-memory-cpu 命令,从而得到以下输出:

Name: base-memory-cpu
Namespace: default
Resource Used Hard
-------- ---- ----
limits.cpu 0 4
limits.memory 0 16Gi
requests.cpu 0 2
requests.memory 0 8Gi

ResourceQuota对象用于控制集群的资源。通过将资源分配到名称空间,您可以保证单个租户拥有运行其应用程序所需的CPU和内存,同时限制编写得不好的应用程序对其他应用程序的影响。

前面我们描述了如何使用 ConfigMap 对象来存储配置信息。
我们提到,ConfigMap(配置映射)对象不应该用于存储任何类型的敏感数据。
这是一个秘密的工作。秘密被存储为base64编码的字符串,这不是一种加密形式。
那么,为什么要将秘密与 ConfigMap 对象分开呢❓
提供一个单独的对象类型提供了一种更简单的方法来维护访问控制和使用外部系统注入敏感信息的能力。
可以使用文件、目录或从文字字符串来创建秘密。
例如,我们有一个MySQL图像,我们希望使用Secret将密码传递给Pod。
在我们的工作站上,我们当前的工作目录中有一个名为dbpwd的文件,其中有我们的密码。
使用库贝特命令,我们可以通过执行 kubectl create secret generic mysql-admin --from-file=./dbpwd 来创建一个秘密。

这将在当前名称空间中创建一个名为mysql-admin的新秘密,其中包含dbpwd文件的内容。

使用 kubectl ,我们可以通过运行 kubectl get secret mysql-admin -o yaml 命令得到秘密的输出,该命令将输出以下内容:

apiVersion: v1
data:
 dbpwd: c3VwZXJzZWNyZXQtcGFzc3dvcmQK
KinD: Secret
metadata:
 creationTimestamp: "2020-03-24T18:39:31Z"
 name: mysql-admin
 namespace: default
 resourceVersion: "464059"
 selfLink: /api/v1/namespaces/de
fault/secrets/mysql-admin
 uid: 69220ebd-c9fe-4688-829b-242ffc9e94fc
type: Opaque

查看前面的输出,您可以看到data部分包含我们的文件的名称,然后是一个base64-encoded的值,它是从文件的内容创建的。
如果我们从秘密复制Base64值并将其管道输出到Base64实用程序,我们可以很容易地解码密码,如下所示:
echo c3VwZXJzZWNyZXQtcGFzc3dvcmQK | base64 -d supersecret-password

❗提示:

当使用eche命令对Base64-编码字符串时,添加-n标志以避免添加附加\n。代替 echo 'test' | base64, 使用 echo -n 'test' | base64

所有的东西都存储在Etcd中,但我们担心有人可能能够侵入主服务器并窃取Etcd数据库的一个副本。
一旦有人拥有了数据库的副本,他们就可以很容易地使用 etcdctl 实用程序来查看内容,以检索我们所有的base64编码的秘密。
幸运的是,库伯内特添加了一个功能,在秘密被写入数据库时加密。
对于许多用户来说,启用这个特性可能相当复杂,虽然这听起来像是个好主意,但它确实提出了您在实现它之前应该考虑的一些潜在问题。

如果你想阅读关于在休息时加密你的秘密的步骤,你可以在https://kubernetes.io/docs/tasks/administercluster/encrypt-data/.的库伯内特斯网站上查看这些步骤。

另一个保护秘密的选择是使用第三方的秘密管理工具,如HashiCorp's Vault or CyberArk's Conjur。

库伯内特斯使用服务帐户来启用对工作负载的访问控制。
在创建部署时,您可能需要访问其他服务或库伯内特斯对象。
由于库伯是一个安全系统,应用程序试图访问的每个对象或服务都将评估role-based access control (RBAC)规则,以接受或拒绝请求。
使用清单创建服务帐户是一个很简单的过程,只需要清单中的几行。
以下代码片段显示了用于为Grafana部署创建服务帐户的服务帐户清单:

apiVersion: v1
KinD: ServiceAccount
metadata:
 name: grafana
 namespace: monitoring

您将服务帐户与角色绑定和角色相结合,以允许访问所需的服务或对象。

为了使在平台中运行的应用程序可于网络,您需要创建一个服务。
服务对象存储有关如何公开应用程序的信息,包括应用程序上运行哪些端口以及要到达它们的网络端口。

每个服务都有一个在创建时分配的网络类型,它们包括以下内容:

• ClusterIP:一种只能在集群本身内部访问的网络类型。这种类型仍然可以用于使用入口控制器的外部请求,这将在后面的一章中讨论。

• NodePort:一种网络类型,公开了在端口30000-32767之间的随机端口之间的服务。通过针对指定的NodePort上的任何工作节点,可以访问此端口。
一旦创建,集群中的每个节点都将接收端口信息,传入的请求将通过 kube-proxy 路由。

• LoadBalancer(负载平衡器):此类型需要一个附加组件才能在集群内使用。
如果您在公共云提供商上运行Kubernetes,则此类型将创建一个外部负载平衡器,它将为您的服务分配一个IP地址。
大多数内部的库伯内特安装不包括对负载平衡器类型的支持,但一些产品,如谷歌的Anthos确实提供了支持。
在后面的章节中,我们将解释如何将一个名为MetalLB的开源项目添加到Kubernetes集群中,以提供对负载平衡器类型的支持。

• ExternalName外部名称:此类型不同于其他三个类型。与其他三个选项不同,这种类型不会为该服务分配IP地址。
相反,它用于将内部库伯内特斯域名系统(DNS)名称映射到外部服务。

作为个例子,我们在端口上部署了一个运行Nginx的Pod。
我们希望创建一个服务,允许该Pod接收来自集群内端口80的传入请求。
这方面的代码可以在以下的代码片段中看到:

apiVersion: v1
KinD: Service
metadata:
 labels:
 app: nginx-web-frontend
 name: nginx-web
spec:
 ports:
 - name: http
 port: 80
 targetPort: 80
 selector:
 app: nginx-web

在我们的清单中,我们创建一个 app 值的标签,并分配一个 nginx-web-frontend 值。
我们称该服务本身为 nginx-web ,并在80端口上公开了该服务,目标是80的 Pod 端口。
清单的最后两行用于分配服务将转发到的端口,也称为端点。
在此清单中,在名称空间中具有 app 标签且值为 nginx-web 的任何 Pod 都将作为端点添加到服务中。

CustomResourceDefinitions(定制资源定义,即CRDs)允许任何人通过将应用程序作为标准对象集成到集群中来扩展Kubernetes。
在创建了一个CRD之后,您就可以使用一个API端点来引用它,并可以使用标准的kubectl命令与它进行交互。

DaemonSets(守护进程集)允许您在集群中的每个节点或节点的子集上部署Pod。
守护集的一个常用用途是将 FluentD 等日志转发plod部署到集群中的每个节点。
一旦部署,守护集将在所有现有节点上创建一个 FluentD 豆荚。
由于守护进程集已部署到所有节点,因此对于添加到集群中的任何其他节点,一旦该节点加入集群,就将启动一个 FluentD Pod。

我们前面提到过,您不应该直接部署 pod ,而且我们还引入了 ReplicationContoller ( 复制连接器 ) 对象,作为直接创建 pod 的替代方案。
虽然这两者都将创建您的 pod ,但每个都有以下限制:直接创建的 pod 不能缩放,也不能使用滚动更新升级。
由 ReplicationContoller 创建的 pods 可以进行缩放,并可以执行滚动更新。
但是,它们不支持回滚,并且不能以声明性地完成升级

部署为您提供了一些优势,包括一种声明性管理升级的方法,以及回滚到以前的版本的能力。
创建部署实际上是由API服务器执行的三个步骤的过程:
• 创建一个部署,
• 它创建一个复制集对象,
• 然后为应用程序创建Pod (s)。
即使您不打算使用这些特性,您也应该默认使用部署,以便在将来利用这些特性。

复制集可用于创建一个pod或一组pods(replicas)。
与 replicationcantroller 对象类似,replicaset 对象将保持在对象的副本计数中定义的端口的设置数量。
如果豆荚太少,库伯内特就会调和差异,创造出缺失的豆荚。
如果复制集的pods太多,库伯内特将删除pods,直到该数字等于对象中设置的复制计数。
通常,您应该避免直接创建复制集。
相反,您应该创建一个部署,它将创建和管理一个复制集。

状态集在创建pods时提供了一些独特的特性。它们提供了其他Pod创建方法所不能提供的特性,包括:
• Known Pod names
• Ordered Deployment and scaling
• Ordered updates
• Persistent storage creation
理解状态集优势的最好方法是回顾库伯内特网站上的一个示例,如下截图所示:

现在,我们可以查看 StatefulSet 对象所创建的对象。
该清单指定了一个名为nginx的Pod应该有三个副本。
当我们得到一个pod列表时,您将看到三个pods是使用nginx名称创建的,还有一个额外的破折号和一个递增的数字。
这就是我们在概述中提到将使用已知名称创建pod时的意思,如下面的代码片段所示:

这些豆荚也是按顺序创建的——在创建web-1之前,web-0必须完全部署,最后是web-2。
最后,对于这个示例,我们还使用清单中的 VolimeClaimTemplate (容量声明模板)向每个Pod添加了一个pvc。
如果您查看kubectl get pvc命令的输出,您会看到三个pvc使用我们期望的名称创建了(注意,由于空间原因我们删除了 VOLUME 列),如下面的代码片段所示:

在清单的VolimeClaimTemplate部分,您将看到我们将名称www分配给了pvc claim。
当您在状态集中分配一个卷时,pvc 名称将合并在声明模板中使用的名称和Pod的名称。
使用这个命名,你可以看到为什么库伯内特分配了pvc名称www-web-0,www-web-1,和www-web-2。

在库伯内特集群上运行工作负载的最大优点之一是能够轻松地扩展您的pod。
虽然您可以使用kubectl命令或通过编辑清单的副本计数进行缩放,但这些都不是自动化的,需要手动干预。

HorizontalPodAutoscalers(HPAs)提供了基于一组标准来缩放应用程序的能力。
使用CPU和内存使用量等指标,或您自己的自定义指标,当您需要更多指标时,您可以设置一个规则来扩展您的服务级别。
在一段冷却期后,库伯内特将应用程序缩减到策略中定义的最小数量。
为了快速为nginx部署创建一个HPA,我们可以使用 autoscale ( 自动缩放 ) 选项执行一个kubectl命令,如下所示:
kubectl autoscale deployment nginx --cpu-percent=50 --min=1 --max=5

您还可以创建一个库伯特清单来创建您的hpa。
使用与我们在CLI中所做的相同的选项,我们的清单将是这样的:

apiVersion: autoscaling/v1
KinD: HorizontalPodAutoscaler
metadata:
 name: nginx-deployment
spec:
 maxReplicas: 5
 minReplicas: 1
 scaleTargetRef:
 apiVersion: apps/v1
 KinD: Deployment
 name: nginx-deployment
 targetCPUUtilizationPercentage: 50

这两个选项都将创建一个HPA,当部署达到CPU利用率的50%时,它可以将 nginx-deployment 的nginx部署扩展到5个副本。
一旦部署使用量低于50%并达到冷却期(默认情况下为5分钟),副本计数将减少到1。

如果您过去使用过Linux工作,那么您已经知道Kubernetes CronJob 对象是什么了。
如果您没有Linux背景,则使用工作作业创建计划任务。
另一个例子是,如果您是Windows人员,则它类似于Windows计划的任务。
一个创建CronJob的清单示例显示在以下代码片段中:

schedule 格式遵循标准的cron格式。从左到右,每个*代表以下内容

• Minute (0 – 59)
• Hour (0 -23)
• Day (1 -31)
• Month (1 – 12)
• Day of the week (0 – 6) (Sunday = 0, Saturday = 6)
Cron作业接受步长值,它允许您创建一个可以每分钟、每2分钟或每小时执行一次的计划。

我们的示例 manifest(清单)将创建一个 cronjob,运行一个图像称为 hello-world每分钟,并输出Hello World!在pod日志中。

作业允许您执行特定数量的豆荚或豆荚的执行。
与工作对象不同,这些端口不是按照设定的cronjob 对象 运行,而是在创建后就会执行。
作业用于执行可能只需要在初始部署阶段执行的任务。
一个示例用例将是一个应用程序,它可能需要在部署主应用程序之前创建必须存在的Kubernetes CRDs的应用程序。
部署将等待作业执行成功完成。

事件对象存储关于库伯内特斯对象的事件的信息。
您不创建事件,而是只能检索事件。
例如,要检索 kube-system 名称空间的事件,将执行 kubectl get events -n kube-system

或者显示所有名称空间的事件,将执行kubectl get events --all-namespaces

您可能已经注意到,Ingresses对象在我们的 api-server 输出中列出了两次。
当发布Kubernetes升级和在API服务器中更改对象时,对象就会发生这种情况。
在Ingress的实例中,它是扩展API的原始部分,并被移动到第1.16版中的networking.k8s.io API中。
该项目将在弃用旧的API调用之前等待几个版本,因此在我们的运行Kubernetes 1.17的示例集群中,使用任何一个API都可以工作。
在版本1.18中,他们计划完全反对Ingress扩展。

NetworkPolicies(网络策略)对象允许您定义网络流量如何通过集群流动。
它们允许您使用库伯内特原生结构来定义哪些豆荚可以与其他豆荚对话。
如果您曾经使用过 Amazon Web services(AWS)中的安全组来锁定两组系统之间的访问,那么这是一个类似的概念。
eg】,以下策略将允许端口443上的流量从任何具有app.kubernetes.io/name的app.kubernetes.io/name名称空间中的端口:ingress-nginx标签(这是nginx-ingress名称空间的默认标签):

apiVersion: networking.k8s.io/v1
KinD: NetworkPolicy
metadata:
 name: allow-from-ingress
 namespace: myns
spec:
 PodSelector: {}
 policyTypes:
 - Ingress
 ingress:
 - from:
 - namespaceSelector:
 matchLabels:
 app.kubernetes.io/name: ingress-nginx ports:
 - protocol: TCP
 port: 443

网络策略对象是可用于保护集群安全的另一个对象。
它们应该在所有生产集群中使用,但在多租户集群中,它们应该被认为是保护集群中每个名称空间的必备条件。

节点安全策略(PSPs)是集群保护节点不受容器影响的方式。
它们允许您限制Pod在集群中可以执行的操作。
一些示例包括拒绝访问HostIPC和HostPath,以及以特权模式运行容器。
我们将在Chapter 10, Creating Pod Security Policies中详细介绍PSPs的细节。
关于PSPs,需要记住的关键点是,如果没有它们,容器几乎可以在节点上做任何事情。

一旦定义了 ClusterRole,就通过ClusterRoleBinding将其绑定到主题。
ClusterRole可以绑定到用户、组或服务帐户。
我们将在 Chapter 8, RBAC Policies and Auditing中探讨ClusterRoleBinding细节。

集群角色结合了一组用于与集群的API进行交互的权限。
集群角色将动词或动作与API组组合起来,以定义权限。
例如,如果您只希望连续集成/连续交付(CI/CD)管道能够为您的部署打补丁,以便它能够更新您的映像标记,那么您可以使用这样的集群角色:


apiVersion: rbac.authorization.k8s.io/v1
KinD: ClusterRole
metadata:
 name: patch-deployment
rules:
- apiGroups: ["apps/v1"]
 resources: ["deployments"]
 verbs: ["get", "list", "patch"]

集群角色可以同时应用于集群和名称空间级别的api。

角色绑定对象是您将角色或集群角色关联到主题和名称空间的方式。
例如,下面的角色绑定对象将允许 aws-codebuild 用户将patch-openunison ( 补丁开放 ) 的集群角色应用到openunison(开放统一)的名称空间:

apiVersion: rbac.authorization.k8s.io/v1
KinD: RoleBinding
metadata:
 name: patch-openunison
 namespace: openunison
subjects:
- KinD: User
 name: aws-codebuild
 apiGroup: rbac.authorization.k8s.io
roleRef:
 KinD: ClusterRole
 name: patch-deployment
 apiGroup: rbac.authorization.k8s.io

即使这引用了一个集群角色,它也只适用于开放统一的名称空间。
如果aws代码构建用户试图在另一个名称空间中修补部署补丁,API服务器将停止它。

与集群角色一样,角色将API组和操作结合起来,以定义一组可以分配给主题的权限。
群集角色和角色之间的区别在于,角色只能在名称空间级别上定义了资源,并且只能在特定的名称空间中应用。

Kubernetes使用CsiDriver对象将节点连接到存储系统。
您可以通过执行kubectl get csidriver命令来列出集群上可用的所有CSI驱动程序。
在我们的一个集群中,我们使用Netapp的SolidFire进行存储,所以我们的集群安装了三叉戟CSI驱动程序,在这里可以看到:

为了避免在节点API对象中存储存储信息,将CSINode对象添加到API服务器中,以存储由CSI驱动程序生成的信息。
存储的信息包括将Kubernetes节点名称映射到CSI节点名称、CSI驱动程序可用性和卷拓扑.

存储类用于定义存储端点。
可以为每个存储类分配标签和策略,从而允许开发人员为其持久性数据选择最佳的存储位置。
您可以为具有所有非易失性存储表达(NVMe)驱动器的后端系统创建一个存储类,为其分配名称 fast,同时使用名称 standard 为运行标准驱动器的Netapp网络文件系统(NFS)卷分配一个不同的类。

当请求一个pvc时,用户可以分配一个他们希望使用的存储类。
当API服务器接收到请求时,它会找到匹配的名称,并使用存储类配置来使用供应器在存储系统上创建卷。

在非常高的级别上,存储类清单不需要大量的信息。
下面是一个存储类的例子,使用库netes孵化器项目的提供器提供NFS自动提供的卷,名为nfs:

apiVersion: storage.k8s.io/v1
KinD: StorageClass
metadata:
 name: nfs
provisioner: nfs

存储类允许您向用户提供多种存储解决方案。
您可以为更便宜、更慢的存储创建一个类,同时提供支持高吞吐量的第二类。
通过为每个产品提供不同的类,您允许开发人员为他们的应用程序选择最佳选择。

Summary

在这一章中,你被扔进了一个库伯内特斯的训练营,他们在很短的时间内展示了大量的技术材料。
试着记住,当你更深入地进入库伯内特人的世界时,这一切都会变得更容易。
我们意识到这一章有很多关于许多物体的信息。
许多对象将在后面的章节中使用,它们将得到更详细的解释。
您了解了每个库伯内特组件,以及它们如何交互以创建集群。
有了这些知识,您就具备查看集群中的错误并确定哪个组件可能导致错误或问题。
我们覆盖了一个集群的控制平面,其中api-服务器、kube-调度器、Etcd和控制管理器运行。
控制平面是用户和服务与集群交互的方式;使用api服务器和kube调度器将决定调度哪个工作节点。
您还了解了运行库belet组件和kube代理组件的库伯内特节点,以及容器运行时。我们介绍了您将用于与集群交互的kubectl实用程序。
您还学习了一些日常使用的常用命令,包括日志和描述。

在下一章中,我们将创建一个开发库伯集群,我们将将其用作其余章节的基础集群。
在本书的其余部分中,我们将参考本章中所介绍的许多对象,并通过在现实世界的例子中使用它们来帮助解释它们。

Questions
1、库伯内特控制平面不包括以下哪些部件❓
A. api-server
B. kube-scheduler
C. Etcd
D. Ingress controller

2、保存所有集群信息的组件的名称是什么❓
A. api-server
B. Master controller
C. kubelet
D. Etcd 

3、哪个组件负责选择将运行工作负载的节点?
A. kubelet
B. api-server
C. kube-scheduler
D. Pod-scheduler


4、您将向kubectl命令添加哪个选项,以查看来自命令的附加输出❓
A. Verbose
B. -v
C. –verbose
D. -log

5、哪种服务类型创建随机生成的端口,允许进入分配端口上任何工作节点的流量访问服务❓
A. LoadBalancer
B. ClusterIP
C. None—it's the default for all services
D. NodePort

6、如果您需要在需要已知节点名的库集群上部署受控启动,您将创建哪个对象❓
A. StatefulSet
B. Deployment
C. ReplicaSet
D. ReplicationController