1 - 创建集群
了解 Kubernetes 集群 并使用 Minikube
创建一个简单的集群。
1.1 - 使用 Minikube 创建集群
了解 Kubernetes 集群。
了解 Minikube。
启动一个 Kubernetes 集群。
目标
了解 Kubernetes 集群。
了解 Minikube。
在你的电脑上开启一个 Kubernetes 集群。
Kubernetes 集群
Kubernetes 协调一个高可用计算机集群,每个计算机作为独立单元互相连接工作。
Kubernetes 中的抽象允许你将容器化的应用部署到集群,而无需将它们绑定到某个特定的独立计算机。
为了使用这种新的部署模型,应用需要以将应用与单个主机分离的方式打包:它们需要被容器化。
与过去的那种应用直接以包的方式深度与主机集成的部署模型相比,容器化应用更灵活、更可用。
Kubernetes 以更高效的方式跨集群自动分发和调度应用容器。
Kubernetes 是一个开源平台,并且可应用于生产环境。
一个 Kubernetes 集群包含两种类型的资源:
控制面 调度整个集群
节点(Nodes) 负责运行应用
Kubernetes 是一个生产级别的开源平台,可协调在计算机集群内和跨计算机集群的应用容器的部署(调度)和执行.
控制面负责管理整个集群。
控制面协调集群中的所有活动,例如调度应用、维护应用的所需状态、应用扩容以及推出新的更新。
节点是一个虚拟机或者物理机,它在 Kubernetes 集群中充当工作机器的角色。
每个节点都有 Kubelet,它管理节点而且是节点与控制面通信的代理。
节点还应该具有用于处理容器操作的工具,例如 containerd 或 CRI-O 。
处理生产级流量的 Kubernetes 集群至少应具有三个节点,因为如果只有一个节点,出现故障时其对应的
etcd 成员和控制面实例都会丢失,
并且冗余会受到影响。你可以通过添加更多控制面节点来降低这种风险。
在 Kubernetes 上部署应用时,你告诉控制面启动应用容器。
控制面就编排容器在集群的节点上运行。
节点使用控制面暴露的 Kubernetes API
与控制面通信。 终端用户也可以使用 Kubernetes API 与集群交互。
Kubernetes 既可以部署在物理机上也可以部署在虚拟机上。你可以使用 Minikube 开始部署 Kubernetes 集群。
Minikube 是一种轻量级的 Kubernetes 实现,可在本地计算机上创建 VM 并部署仅包含一个节点的简单集群。
Minikube 可用于 Linux、macOS 和 Windows 系统。Minikube CLI 提供了用于引导集群工作的多种操作,
包括启动、停止、查看状态和删除。
现在你已经知道 Kubernetes 是什么了,在你的电脑上访问你好 Minikube ,
试试。
2.1 - 使用 kubectl 创建 Deployment
教程目标
学习应用的部署。
使用 kubectl 在 Kubernetes 上部署第一个应用。
Kubernetes Deployment
说明:
本教程使用了一个需要 AMD64 架构的容器。如果你在使用 Minikube
的计算机上使用了不同的 CPU 架构,可以尝试使用能够模拟 AMD64
的 Minikube 驱动程序。例如,Docker Desktop 驱动程序可以实现这一点。
一旦运行了 Kubernetes 集群 ,
就可以在其上部署容器化应用。为此,你需要创建 Kubernetes Deployment 。
Deployment 指挥 Kubernetes 如何创建和更新应用的实例。
创建 Deployment 后,Kubernetes 控制平面将 Deployment
中包含的应用实例调度到集群中的各个节点上。
创建应用实例后,Kubernetes Deployment 控制器会持续监视这些实例。
如果托管实例的节点关闭或被删除,则 Deployment 控制器会将该实例替换为集群中另一个节点上的实例。
这提供了一种自我修复机制来解决机器故障维护问题。
在没有 Kubernetes 这种编排系统之前,安装脚本通常用于启动应用,
但它们不允许从机器故障中恢复。通过创建应用实例并使它们在节点之间运行,
Kubernetes Deployment 提供了一种与众不同的应用管理方法。
部署你在 Kubernetes 上的第一个应用
应用需要打包成一种受支持的容器格式,以便部署在 Kubernetes 上。
你可以使用 Kubernetes 命令行界面 kubectl
创建和管理 Deployment。
kubectl 使用 Kubernetes API 与集群进行交互。在本单元中,你将学习创建在 Kubernetes
集群上运行应用的 Deployment 所需的最常见的 kubectl 命令。
创建 Deployment 时,你需要指定应用的容器镜像以及要运行的副本数。
你可以稍后通过更新 Deployment 来更改该信息;
模块 5
和模块 6
讨论了如何扩展和更新 Deployment。
对于你第一次部署,你将使用打包在 Docker 容器中的 hello-node 应用,该应用使用 NGINX 回显所有请求。
(如果你尚未尝试创建 hello-node 应用并使用容器进行部署,则可以首先按照
Hello Minikube 教程 中的说明进行操作)。
你也需要安装好 kubectl。如果你需要安装 kubectl,
参阅安装工具 。
现在你已经了解了部署的内容,让我们部署第一个应用!
kubectl 基础知识
kubectl 命令的常见格式是:kubectl action resource
。
这会对指定的资源 (类似 node
或 deployment
)执行指定的操作 (类似
create
、describe
或 delete
)。
你可以在子命令之后使用 --help
获取可能参数相关的更多信息
(例如:kubectl get nodes --help
)。
通过运行 kubectl version
命令,查看 kubectl 是否被配置为与你的集群通信。
查验 kubectl 是否已安装,你能同时看到客户端和服务器版本。
要查看集群中的节点,运行 kubectl get nodes
命令。
你可以看到可用的节点。稍后 Kubernetes 将根据节点可用的资源选择在哪里部署应用。
部署一个应用
让我们使用 kubectl create deployment
命令在 Kubernetes 上部署第一个应用。
我们需要提供 Deployment 命令以及应用镜像位置(包括托管在 Docker Hub
之外的镜像的完整仓库地址)。
kubectl create deployment kubernetes-bootcamp --image= gcr.io/google-samples/kubernetes-bootcamp:v1
很好!你刚刚通过创建 Deployment 部署了第一个应用。这个过程中执行了以下一些操作:
搜索应用实例可以运行的合适节点(我们只有一个可用的节点)
调度应用在此节点上运行
配置集群在需要时将实例重新调度到新的节点上
要列出你的 Deployment,使用 kubectl get deployments
命令:
我们看到有 1 个 Deployment 运行应用的单个实例。这个实例运行在节点上的一个容器内。
查看应用
在 Kubernetes 内运行的 Pod
运行在一个私有的、隔离的网络上。
默认这些 Pod 可以从同一 Kubernetes 集群内的其他 Pod 和服务看到,但超出这个网络后则看不到。
当我们使用 kubectl
时,我们通过 API 端点交互与应用进行通信。
kubectl proxy
命令可以创建一个代理,将通信转发到集群范围的私有网络。
按下 Ctrl-C 此代理可以被终止,且在此代理运行期间不会显示任何输出。
你需要打开第二个终端窗口来运行此代理。
现在我们在主机(终端)和 Kubernetes 集群之间有一个连接。此代理能够从这些终端直接访问 API。
你可以看到通过代理端点托管的所有 API。
例如,我们可以使用以下 curl
命令直接通过 API 查询版本:
curl http://localhost:8001/version
说明:
如果 Port 8001 不可访问,确保你上述启动的 kubectl proxy
运行在第二个终端中。
API 服务器将基于也能通过代理访问的 Pod 名称为每个 Pod 自动创建端点。
首先我们需要获取 Pod 名称,我们将存储到环境变量 POD_NAME
中:
export POD_NAME = $( kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' )
echo Name of the Pod: $POD_NAME
你可以运行以下命令通过代理的 API 访问 Pod:
curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME :8080/proxy/
为了不使用代理也能访问新的 Deployment,需要一个 Service,
这将在下一个模块 4
中讲述。
接下来
3.1 - 查看 Pod 和节点
学习如何使用 kubectl get、kubectl describe、kubectl logs 和
kubectl exec 排除 Kubernetes 应用故障。
目标
了解 Kubernetes Pod。
了解 Kubernetes 节点。
对已部署的应用故障排除。
Kubernetes Pod
在模块 2 中创建 Deployment 时,
Kubernetes 创建了一个 Pod 来托管你的应用实例。Pod 是 Kubernetes 抽象出来的,
表示一组一个或多个应用容器(如 Docker),以及这些容器的一些共享资源。这些资源包括:
共享存储,当作卷
网络,作为唯一的集群 IP 地址
有关每个容器如何运行的信息,例如容器镜像版本或要使用的特定端口
Pod 为特定于应用的“逻辑主机”建模,并且可以包含相对紧耦合的不同应用容器。
例如,Pod 可能既包含带有 Node.js 应用的容器,也包含另一个不同的容器,
用于提供 Node.js 网络服务器要发布的数据。Pod 中的容器共享 IP 地址和端口,
始终位于同一位置并且共同调度,并在同一节点上的共享上下文中运行。
Pod 是 Kubernetes 平台上的原子单元。当我们在 Kubernetes 上创建 Deployment 时,
该 Deployment 会在其中创建包含容器的 Pod(而不是直接创建容器)。
每个 Pod 都与调度它的节点绑定,并保持在那里直到终止(根据重启策略)或删除。
如果节点发生故障,则会在集群中的其他可用节点上调度相同的 Pod。
Pod 是一个或多个应用容器(例如 Docker)的组合,包括共享存储(卷)、IP 地址和有关如何运行它们的信息。
节点
Pod 总是运行在节点 上。节点是 Kubernetes 中参与计算的机器,可以是虚拟机或物理计算机,具体取决于集群。
每个节点由控制面管理。节点可以有多个 Pod,Kubernetes 控制面会自动处理在集群中的节点上调度 Pod。
控制面的自动调度考量了每个节点上的可用资源。
每个 Kubernetes 节点至少运行:
Kubelet,负责 Kubernetes 控制面和节点之间通信的进程;它管理机器上运行的 Pod 和容器。
容器运行时(如 Docker)负责从镜像仓库中提取容器镜像、解压缩容器以及运行应用。
如果容器紧耦合并且需要共享磁盘等资源,则只应将其编排在一个 Pod 中。
使用 kubectl 进行故障排除
在模块 2 中,
你使用了 kubectl 命令行界面。你将继续在第 3 个模块中使用 kubectl 来获取有关已部署应用及其环境的信息。
最常见的操作可以使用以下 kubectl 子命令完成:
kubectl get - 列出资源
kubectl describe - 显示有关资源的详细信息
kubectl logs - 打印 Pod 中容器的日志
kubectl exec - 在 Pod 中的容器上执行命令
你可以使用这些命令查看应用的部署时间、当前状态、运行位置以及配置。
现在我们了解了有关集群组件和命令行的更多信息,让我们来探索一下我们的应用。
节点是 Kubernetes 中负责计算的机器,可能是虚拟机或物理计算机,具体取决于集群。
多个 Pod 可以在一个节点上运行。
检查应用配置
让我们验证之前场景中部署的应用是否在运行。我们将使用 kubectl get
命令查看现存的 Pod:
kubectl get pods
如果没有 Pod 在运行,请等几秒,让 Pod 再次列出。一旦看到一个 Pod 在运行,就可以继续操作。
接下来,要查看 Pod 内有哪些容器以及使用了哪些镜像来构建这些容器,我们运行 kubectl describe pods
命令:
kubectl describe pods
我们在这里看到了 Pod 的容器相关详情:IP 地址、使用的端口以及 Pod 生命期有关的事件列表。
describe 子命令的输出宽泛,涵盖了一些我们还未讲到的概念,但不用担心,
这节课结束时你就会熟悉这些概念了。
注: describe 子命令可用于获取有关大多数 Kubernetes 原语的详细信息,
包括节点、Pod 和 Deployment。describe 的输出设计为人类可读的信息,而不是脚本化的信息。
在终端中显示应用
回想一下,Pod 运行在隔离的、私有的网络中,因此我们需要代理访问它们,这样才能进行调试和交互。
为了做到这一点,我们将使用 kubectl proxy
命令在第二个终端 中运行一个代理。
打开一个新的终端窗口,在这个新的终端中运行以下命令:
kubectl proxy
现在我们再次获取 Pod 命令并直接通过代理查询该 Pod。
要获取 Pod 命令并将其存到 POD_NAME 环境变量中,运行以下命令:
export POD_NAME="$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')"
echo Name of the Pod: $POD_NAME
要查看应用的输出,运行 curl
请求:
curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME:8080/proxy/
URL 是到 Pod API 的路由。
查看容器日志
应用通常发送到标准输出的所有内容都成为 Pod 内容器的日志。
我们可以使用 kubectl logs
命令检索这些日志:
kubectl logs "$POD_NAME"
注: 我们不需要指定容器名称,因为在 Pod 内只有一个容器。
在容器上执行命令
一旦 Pod 启动并运行,我们就可以直接在容器上执行命令。
为此,我们使用 exec
子命令,并将 Pod 的名称作为参数。让我们列出环境变量:
kubectl exec "$POD_NAME" -- env
另外值得一提的是,由于 Pod 中只有一个容器,所以容器本身的名称可以被省略。
接下来,让我们在 Pod 的容器中启动一个 bash 会话:
kubectl exec -ti $POD_NAME -- bash
现在我们在运行 Node.js 应用的容器上有一个打开的控制台。该应用的源代码位于 server.js 文件中:
cat server.js
你可以通过运行 curl 命令查看应用是否启动:
curl http://localhost:8080
注: 在这里我们使用了 localhost ,因为我们在 NodeJS Pod 内执行了此命令。
如果你无法连接到 localhost:8080,请确保你已经运行了 kubectl exec
命令,并且从 Pod 内启动了该命令。
要关闭你的容器连接,键入 exit
。
4.1 - 使用 Service 暴露你的应用
了解 Kubernetes 中的 Service。
理解标签和选择算符如何关联到 Service。
在 Kubernetes 集群外暴露应用。
目标
了解 Kubernetes 中的 Service
了解标签(Label)和选择算符(Selector)如何与 Service 关联
在 Kubernetes 集群外用 Service 暴露应用
Kubernetes Service 总览
Kubernetes Pod 是转瞬即逝的。 Pod 拥有 生命周期 。
当一个工作节点挂掉后, 在节点上运行的 Pod 也会消亡。 ReplicaSet 会自动地通过创建新的 Pod 驱动集群回到目标状态,以保证应用正常运行。
换一个例子,考虑一个具有 3 个副本的用作图像处理的后端程序。
这些副本是可替换的。前端系统不应该关心后端副本,即使某个 Pod 丢失或被重新创建。
此外,Kubernetes 集群中的每个 Pod 都有一个唯一的 IP 地址,即使是在同一个 Node 上的 Pod 也是如此,
因此需要一种方法来自动协调 Pod 之间的变化,以便应用保持运行。
Kubernetes 中的服务(Service)是一种抽象概念,它定义了 Pod 的逻辑集和访问 Pod 的协议。
Service 使从属 Pod 之间的松耦合成为可能。
和所有 Kubernetes 对象清单一样, Service 用 YAML 或者 JSON 来定义。
Service 下的一组 Pod 通常由一个 标签选择算符 来标记
(请参阅下面的说明为什么你可能想要一个 spec 中不包含 selector
的 Service)。
尽管每个 Pod 都有一个唯一的 IP 地址,但是如果没有 Service,这些 IP 不会被公开到集群外部。
Service 允许你的应用接收流量。
通过设置 Service 的 spec
中的 type
,你可以用不同的方式公开 Service:
ClusterIP (默认)- 在集群的内部 IP 上公开 Service。这种类型使得 Service 只能从集群内访问。
NodePort - 使用 NAT 在集群中每个选定 Node 的相同端口上公开 Service 。使用<NodeIP>:<NodePort>
从集群外部访问 Service。是 ClusterIP 的超集。
LoadBalancer - 在当前云中创建一个外部负载均衡器(如果支持的话),并为 Service 分配一个固定的外部IP。是 NodePort 的超集。
ExternalName - 将 Service 映射到 externalName
字段的内容(例如 foo.bar.example.com
),通过返回带有该名称的 CNAME
记录实现。不设置任何类型的代理。这种类型需要 kube-dns
的 v1.7 或更高版本,或者 CoreDNS 的 0.8 或更高版本。
关于不同 Service 类型的更多信息可以在使用源 IP 教程找到。
也请参阅 使用 Service 连接到应用 。
另外,需要注意的是有一些 Service 的用例不需要在 spec 中定义 selector
。
一个创建时未设置 selector
的 Service 也不会创建相应的 Endpoint 对象。
这允许用户手动将 Service 映射到特定的端点。
没有 selector 的另一种可能是你在严格使用 type: ExternalName
服务。
总结
将 Pod 暴露给外部通信
跨多个 Pod 的负载均衡
使用标签(Label)
Kubernetes 的 Service 是一个抽象层,它定义了一组 Pod 的逻辑集,并为这些 Pod 支持外部流量暴露、负载平衡和服务发现。
Service 为一组 Pod 提供流量路由。Service 是一种抽象,允许 Kubernetes 中的 Pod 死亡和复制,而不会影响应用。
在依赖的 Pod(如应用中的前端和后端组件)之间进行发现和路由是由 Kubernetes Service 处理的。
Service 通过标签和选择算符 来匹配一组 Pod,
它们是允许对 Kubernetes 中的对象进行逻辑操作的一种分组原语。
标签是附加在对象上的键/值对,可以以多种方式使用:
指定用于开发、测试和生产的对象
嵌入版本标记
使用标记将对象分类
标签可以在对象创建时或之后附加到对象上。它们可以随时被修改。现在使用 Service 发布我们的应用并添加一些标签。
第一步:创建新 Service
让我们来验证我们的应用正在运行。我们将使用 kubectl get
命令并查找现有的 Pod:
kubectl get pods
如果没有 Pod 正在运行,则意味着之前教程中的对象已被清理。这时,
请返回并参考 使用 kubectl 创建 Deployment 教程重新创建 Deployment。
请等待几秒钟,然后再次列举 Pod。一旦看到一个 Pod 正在运行,你就可以继续了。
接下来,让我们列举当前集群中的 Service:
kubectl get services
我们有一个名为 kubernetes 的 Service ,它在 minikube 启动集群时默认创建。
要创建一个新的 Service 然后暴露给外部流量,我们将使用 expose
命令,并将 NodePort 作为参数。
kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
让我们再次运行 get services
子命令:
kubectl get services
我们现在有一个运行中的 Service 名为 kubernetes-bootcamp。
这里我们看到 Service 收到了一个唯一的集群内 IP(cluster-IP),一个内部端口和一个外部 IP
(external-IP)(Node 的 IP)。
要得到外部打开的端口号(对于 type: NodePort 的服务),我们需要运行 describe service
子命令:
kubectl describe services/kubernetes-bootcamp
创建一个名为 NODE_PORT 的环境变量,它的值为所分配的 Node 端口:
export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')"
echo "NODE_PORT=$NODE_PORT"
现在我们可以使用 curl
、Node 的 IP 地址和对外暴露的端口,来测试应用是否已经被公开到了集群外部:
curl http://"$(minikube ip):$NODE_PORT"
说明: 如果你正在使用 Docker Desktop 作为容器驱动来运行 minikube, 需要使用 minikube tunnel。
这是因为 Docker Desktop 内部的容器和宿主机是隔离的。
在另一个终端窗口中,执行:
minikube service kubernetes-bootcamp --url
输出结果如下:
http://127.0.0.1:51082 ! Because you are using a Docker driver on darwin, the terminal needs to be open to run it.
然后使用提供的 URL 访问应用:
curl 127.0.0.1:51082
然后我们就会收到服务器的响应。Service 已经被暴露。
第二步:使用标签
Deployment 自动给我们的 Pod 创建了一个标签。通过 describe deployment
子命令你可以看到那个标签的名称(对应 key ):
kubectl describe deployment
让我们使用这个标签来查询 Pod 列表。我们将使用 kubectl get pods
命令和 -l 参数,后面给出标签值:
kubectl get pods -l app=kubernetes-bootcamp
你可以用同样的方法列出现有的 Service:
kubectl get services -l app=kubernetes-bootcamp
获取 Pod 的名称,然后存放到 POD_NAME 环境变量:
export POD_NAME="$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')"
echo "Name of the Pod: $POD_NAME"
要应用一个新的标签,我们使用 label
子命令,接着是对象类型、对象名称和新的标签:
kubectl label pods "$POD_NAME" version=v1
这将会在我们的 Pod 上应用一个新标签(我们把应用版本锁定到 Pod 上),然后我们可以通过 describe pods
命令检查它:
kubectl describe pods "$POD_NAME"
我们可以看到现在标签已经被附加到我们的 Pod 上。我们可以通过新的标签来查询 Pod 列表:
kubectl get pods -l version=v1
我们看到了对应的 Pod。
第三步:删除一个 Service
要删除一个 Service 你可以使用 delete service
子命令。这里也可以使用标签:
kubectl delete service -l app=kubernetes-bootcamp
确认对应的 Service 已经消失:
kubectl get services
这里确认了我们的 Service 已经被删除。要确认路由已经不再被公开,你可以 curl 之前公开的 IP 和端口:
curl http://"$(minikube ip):$NODE_PORT"
这证明了集群外部已经不再可以访问应用。
你可以通过在 Pod 内部运行 curl 确认应用仍在运行:
kubectl exec -ti $POD_NAME -- curl http://localhost:8080
这里我们看到应用是运行状态。这是因为 Deployment 正在管理应用。
要关闭应用,你还需要删除 Deployment。