了解 Kubernetes:开发者指南
为什么选择 Kubernetes?
核心概念
Kubernetes 组件
迷你库贝
Kubernetes 对象
模式
反模式
云端的 Kubernetes
Kubectl
MicroK8s
面试问题
Kubernetes 是一个很酷的词,在你的架构文档中出现,在你的简历上也同样引人注目。无论是在本地还是在云端,每个人都希望将工作负载部署在 Kubernetes 中。在我们追随潮流的同时,了解 Kubernetes 的核心内容也至关重要。在使用 Kubernetes 时,有哪些模式和反模式?这篇博客将为你提供理解和使用 Kubernetes 所需的一切。
- 为什么选择 Kubernetes
- 核心概念
- 迷你库贝
- Kubernetes 对象
- 模式与反模式
- 云端 Kubernetes - EKS
- Kubectl
- MicroK8s
- 面试问题
为什么选择 Kubernetes?
为什么?$%^#
当我读到关于 Kubernetes 的文章时,我的第一个念头就是:为什么我们需要如此复杂的系统?一个简单的 java -jar 文件就足以在 Linux 服务器上运行我的微服务!我编写了优秀的代码,处理了所有异常,确保万无一失。即使失败了,我也可以编写一个简单的 Bash 脚本来重启我的服务。我的代码可以处理水平扩展和故障转移。我有一个优秀的防火墙和日志框架。所有这些只需要几行额外的代码——打包成一个简单的 Jar 文件,并添加到我所有的微服务中。我为什么要在这些疯狂的东西上浪费宝贵的计算资源、时间和资源?坦白说,我现在仍然这么想。
很多系统确实不需要 Kubernetes。但有些系统需要。对于关键任务型企业级应用程序来说,停机成本可能非常高昂,我们需要的不仅仅是一个内部库——还要负责服务编排。我们往往低估了这类应用程序所需的强化和弹性水平。如果我们为世界上的每个应用程序都重新设计轮子,这既不可能,也不值得。我们需要一个由全球开发者测试和强化的系统。那就是 Kubernetes。
是的,Kubernetes 提供的远不止上述功能,这对某些人来说可能显得有些浪费。但我们也可以将其视为一个机会,来改进和扩展我们的服务,使其超越我们目前的需求。这只需要付出一点努力去学习新知识,就能在可扩展性和稳定性方面获得丰厚的回报。
Kubernetes 最初是一款大型多节点应用程序。但其受欢迎程度和易用性催生了多种变体,例如 MiniKube(适用于单台 PC)和 MicroK8s(适用于物联网)。Kubernetes 并不局限于 Peta 级应用程序,它已成功应用于各种其他平台和架构。
核心概念
一个简单的谷歌搜索就能找到:“Kubernetes 是一个可移植、可扩展、开源的容器编排工具”。听起来很酷,不是吗?我们刚刚解决了第一个面试问题。现在我们来看看剩下的问题。
简单来说
Kubernetes 旨在实现极高的可用性、可扩展性、性能和灾难恢复能力。这得益于一个基于多节点架构构建的生态系统。高可用性和灾难恢复能力要求应用程序分布在多台服务器(或云端的虚拟机)上。
每个服务器称为一个节点 (Node)。我们有主节点 (Master Node)和工作节点 (Worker Node)。顾名思义,主节点负责编排,而工作节点则真正处理工作。Kubernetes 并不要求这种区分。我们可以让主节点的组件在工作节点上运行。但将它们分开是一个好的做法。
显然,主节点 (Master Node) 是核心组件,我们不能只依赖一个主节点。我们至少需要两个。我们可以有多个工作节点 (Worker Node) 来承担容器的工作负载。一个节点上可以运行多个Pod中的容器。
主节点(也称为控制平面)包含多个负责编排的组件。工作节点与主节点连接,并通过在其上运行的精简应用程序(Kubelet和K-proxy)管理各个 Pod 。另一方面,主节点拥有一组精心设计的组件来处理编排。
Kubernetes 组件
让我们详细了解一下这些组件。在我一一列举这些组件的名称之前,我们先来思考一下如何实现像 Kubernetes 这样的应用程序。我们会在其中添加哪些组件?
我们上面看到,它在主节点中有一个控制面板,并在一组工作节点中有一个。这种应用程序最重要的组件应该是调度器——它负责为任何要运行的容器分配合适的工作节点。该调度器应该能够监控每个工作节点的状态,以便做出明智的调度决策。容器本身应该由一个实体来监控其进度,并在其运行不正常时发出警报。
我们需要一种方法来存储整个集群的状态,以便管理灾难恢复。同样,每个容器也应该有一种方法来存储任何持久数据。
最重要的是,我们需要一种方式,让外界能够连接到集群中的服务。最好有一个简洁的用户界面,帮助开发者访问内部组件,一个 API,供其他应用程序使用。最后,还要有一个 CLI,供那些喜欢复杂操作的宅男宅女使用。
所有这些都应该以极致的抽象来实现,这样任何组件都不会被锁定在其他组件中。现在让我们看看 Kubernetes 是如何实现这些的。
豆荚
在最底层,我们有容器(主要是 Docker)来运行我们的微服务。如果您正在阅读本文,显然您知道什么是 Docker 或容器,所以我就不再赘述了。如果您不了解,您可以将其视为一个封装良好的、可以独立运行的微服务。
Kubernetes Pod 是这个容器的糖衣,它负责管理集群中某个工作节点上运行的容器。它监控容器,如果容器发生故障,Pod 会负责重新启动它。Pod 会提取容器生成的日志并将其推送到集群日志中。它向集群报告容器的健康状况,从而充当 Kubernetes 和容器本身之间的桥梁。此外,如果需要紧密耦合,一个 Pod 可以包含多个容器。
每个 Pod 在集群内部 IP 空间中都有一个独立的 IP 地址。这允许集群中的微服务之间进行通信
服务
Pod 拥有独立的 IP 地址,以便 Pod 之间相互通信。但是,我们遇到了一个问题。Pod 并非永久存在。它们可能会被终止、扩容,甚至可能由于各种原因而失效——这些原因超出了我们的控制范围。Kubernetes 会负责妥善地替换它们。但是,当新的 Pod 被创建时,它会获得另一个 IP 地址。此外,随着我们水平扩展,同一个微服务可能会有多个 Pod。
如果一个微服务调用另一个微服务,我们不能使用 IP 地址——这会增加调用者的复杂性。调用者必须负责识别 Pod 的当前 IP 地址,如果该微服务有多个 Pod,它还必须负责选择其中一个。这太荒谬了!
为了避免这个问题,我们需要一个实体来封装给定微服务的所有 Pod,并提供一个一致的 URL 来调用该服务。在内部,它负责将请求映射到正确的 Pod,并考虑负载均衡、扩展等各种因素。
部署
正如我们所见,Pod 可以动态地被替换、扩容、终止和创建。Deployment 可以被视为一个模板,它定义了如何为给定的服务创建新的 Pod。它包含有关 Docker 镜像、应用程序属性、副本数量以及 Pod 所需资源的所有详细信息。它还包含 Pod 如何与外界通信的详细信息……以及 Kubernetes 创建 Pod 并确保 Pod 数量正确所需的一切。
库贝莱特
所有这些都在工作节点 (Worker Node) 上进行,并由运行在主节点 (Master Node) 上的控制面板 (Control Panel) 控制。当然,我们需要一种连接工作节点 (Worker Node) 和主节点 (Master Node) 的应用程序来监控工作节点 (Worker Node) 上的活动,并将状态报告给主节点 (Master Node);同时,从主节点 (Master Node) 接收命令,并在工作节点 (Worker Node) 上执行。这就是 Kubelet。
它本身的智能程度很低。它只负责报告和执行指令。但它会详细跟踪节点的状态——工作节点的 CPU、内存利用率,以及节点上运行的每个 Pod 的状态。它还会定期对每个 Pod 进行健康检查,以确保其处于活动状态。
控制平面可以指示其终止指定的 Pod 或创建新的 Pod。Kubelet 负责执行这些指令。
Kubeproxy
其中一些微服务可能会暴露外部 API,或者访问一些外部 API。这由 KubeProxy 实现。顾名思义,它充当节点上的一个简单代理服务器。
API 服务器
这是 Kubernetes 集群面向外部世界的门面。它是所有 API 调用的入口。许多客户端都通过它进行通信,包括仪表盘 UI、自动化技术 API 和命令行工具。它基于kurbe-apiserver。需要注意的是,它是 Kubernetes 集群本身的入口。各个 Pod 与外界的通信无需通过它,而是通过各个工作节点上的 KubeProxy 进行。
调度器
调度程序负责将 Pod 调度到工作节点上。如果现有 Pod 挂掉,我们需要将其移除并创建一个新的。同样,如果需要扩展部署,也需要创建新的 Pod。这些 Pod 应该在集群中找到新的位置,并被分配给特定的工作节点。
选择工作节点时,需要考虑几个因素。我们无法将部署中的所有 Pod 都部署在同一个节点上。这违背了构建包含多个工作节点的集群的初衷。Pod 应该分散部署,以便即使任何工作节点崩溃,集群的其余部分也能继续工作。此外,还必须确保负载在各个工作节点之间均匀分布。
为此,它应该具备做出明智分配决策所需的一切条件。它应该了解每个工作节点上每个 Pod 的状态。它应该知道每个节点的负载。为此,它应该连接到每个工作节点上运行的 Kubelet。
控制器管理器
顾名思义,这是管理整个集群的控制器。它跟踪集群中发生的所有事件。Kubelet 会将每个 Pod 的状态报告给此控制器管理器。如果某个 Pod 挂掉,状态会报告给控制器。如有必要(可能是因为缩容而明确终止了 Pod,在这种情况下无需替换),控制器会触发调度程序来创建一个替代 Pod。
etcd
这是集群的当前状态。(从 Kubernetes 管理员的角度来看,指的是集群,而不是单个微服务的状态)。etcd 保存着当前系统及其预期状态的详细信息。其他组件可以参考它来确定下一步的操作。
这在灾难恢复的情况下非常有用——新集群中的控制器管理器可以直接选择 etcd 来重新创建新集群。
但需要注意的是,Kubernetes 不负责将 etcd 持久化到磁盘上。如果我们想使用它进行灾难恢复,管理员必须自行处理。
入口
Kubernetes 集群并非一个独立的实体。它与外界保持着活跃的连接。它调用外部 API,也向外界导出 API。Kubernetes 外部服务负责提供 IP 地址。但这并非正确做法。一个好的系统不应该导出 IP 地址,而应该导出 URL。这是通过 Ingress 实现的。
Ingress 拥有一套精心设计的机制,可以为每个服务导出特定的 URL。我们可以提供外部导出 URL 到服务内部 API 所需 URL 的映射。
配置图
我们都喜欢将常量从应用程序代码中提取到 Properties 文件中。Kubernetes 提供了一个简洁的框架来处理此类属性文件。事实上,Kubernetes 在这方面更进了一步——让我们能够自行配置配置文件。
属性文件分为两组:配置映射 (Config Maps) 和剧本 (Playbook)。配置映射可以被视为属性文件的模板。实际值来自剧本。通常,我们为每个服务提供一个配置映射,并有多个剧本,以处理多个环境和版本。因此,我们可以让相同的应用程序代码引用不同的属性文件(这些文件在部署时生成)。
秘密
许多开发人员都会这样做,但我们绝不应该将密码存储在属性文件中。它们不应该被人眼看到。Kubernetes 提供了 Secrets 的概念来解决这个问题。我们可以在这里保存加密信息,这些信息仅在部署时解密,以确保其始终安全可靠。
体积
这是一个重要的概念。在 Kubernetes 中,Pod 和容器是短暂的。它们随时可能被终止或重启。自然,它们应该是无状态的。微服务架构原则建议服务尽可能保持无状态。但有些服务需要状态。
例如,数据库必须将数据保存到磁盘。即使容器或 Pod 重置,这些数据也不会被遗忘。Kubernetes 提供了卷的概念来实现这种持久性。秉承 Kubernetes 的理念,卷具有极高的可配置性和抽象性,因此能够容忍更改。
它通过存储类 (SC)、持久卷 (PV) 和持久卷声明 (PVC) 工作。顾名思义,SC 特定于一类存储。它可以是 AWS EBS、本地磁盘或本地远程服务器。此配置由管理员定义并限制在 SC 上。
开发人员配置部署并声明此持久卷。此 PVC 从请求的存储类中声明一个存储卷。Kubernetes 负责在新 Pod 启动时以 PV 的形式分配此存储卷。
命名空间
小型团队和小型应用程序无需命名空间(使用默认命名空间)即可运行。但是,在企业级应用程序中,我们有数百个微服务部署在同一个 Kubernetes 集群上。我们有多个团队并行开发这些微服务。
Kubernetes 的实体太多了。确保这些实体的名称不冲突至关重要。我们不可能设立一个共识委员会,按需为每个开发人员分配名称。那样太疯狂了。更好的做法是将企业划分成更小的应用程序,每个应用程序都有一个命名空间。这样可以隔离各个团队,避免混淆。
命名空间并非安全措施。它只是简化了规则。
舵
Helm 并非 Kubernetes 的开箱即用组件,但它在 Kubernetes 中被广泛使用。我们上面提到的所有概念以及 Kubernetes 集群中的实际实体都包含在 Helm 中。Kubernetes 中的所有内容本质上都是 YAML 文件,仅此而已。我们只需使用适当的语法和所需信息创建 YAML 文件即可。然后,Kubernetes 会将这些 YAML 文件赋予生命,从而构建一个功能强大的企业级应用程序。
可以想象,我们有大量 YAML 文件。每个微服务至少包含十几个 YAML 文件,有时甚至几十个。对于任何管理员来说,这自然是一项艰巨的任务。当我们在整个企业中对大多数服务进行类似的部署,并且某些服务在全球范围内也有类似的部署(例如 ELK 堆栈)时,这会令人沮丧。
Helm 以 Helm Charts 的形式提供了一个简单的解决方案,可以系统地将不同的 YAML 文件组织到 Helm Charts 中。
迷你库贝
我们可以将 Minikube 视为 Kubernetes 的小型玩具版本——可以安装在本地机器上。这对于验证我们的部署非常有用,也有助于学习和完善我们的技能。
让我们从安装 Minikube 开始。我有一个诚挚的请求。我对微软没有任何(任何)不满。Windows 是一个(并非)很棒的操作系统。如果你真的想认真对待 DevOps,最好还是放弃它。切换到 Ubuntu。或者你也可以创建一个小型 EC2 实例用于学习。(完成后记得终止 EC2)。
安装
$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 65.2M 100 65.2M 0 0 149M 0 --:--:-- --:--:-- --:--:-- 148M
$ sudo install minikube-linux-amd64 /usr/local/bin/minikube
真快。Minikube 现在已经部署在 Ubuntu 上了。
开始
现在,我们继续启动集群。这同样简单。
$ minikube start
* minikube v1.23.0 on Ubuntu 20.04
* Automatically selected the docker driver. Other choices: none, ssh
* Starting control plane node minikube in cluster minikube
* Pulling base image ...
* Downloading Kubernetes v1.22.1 preload ...
> preloaded-images-k8s-v12-v1...: 515.04 MiB / 515.04 MiB 100.00% 138.61 M
> gcr.io/k8s-minikube/kicbase: 355.82 MiB / 355.82 MiB 100.00% 51.85 MiB p
* Creating docker container (CPUs=2, Memory=1949MB) ...
* Preparing Kubernetes v1.22.1 on Docker 20.10.8 ...
- Generating certificates and keys ...
- Booting up control plane ...
- Configuring RBAC rules ...
* Verifying Kubernetes components...
- Using image gcr.io/k8s-minikube/storage-provisioner:v5
* Enabled addons: default-storageclass, storage-provisioner
* kubectl not found. If you need it, try: 'minikube kubectl -- get pods -A'
* Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
好了!Minikube 集群已启动并准备就绪。
如果它抱怨可用内存不足,它还会建议一种限制内存使用的方法。我们可以在启动命令中使用建议的选项,然后重试。
我们也可以直接改变分配的内存配置
$ minikube config set memory 16384
! These changes will take effect upon a minikube delete and then a minikube start
Kubectl
我们可以使用 kubectl 连接到该集群。让我们尝试查询所有正在运行的 Pod。
$ minikube kubectl -- get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-78fcd69978-wf5wx 1/1 Running 0 3m57s
kube-system etcd-minikube 1/1 Running 0 4m8s
kube-system kube-apiserver-minikube 1/1 Running 0 4m11s
kube-system kube-controller-manager-minikube 1/1 Running 0 4m8s
kube-system kube-proxy-l8j4q 1/1 Running 0 3m57s
kube-system kube-scheduler-minikube 1/1 Running 0 4m11s
kube-system storage-provisioner 1/1 Running 1 (3m25s ago) 4m6s
(Minikube 可以为我们获取正确版本的 kubectl。为此,我们使用 minikube kubectl 命令。如果尚未安装,这也会安装 kubectl。)
正如我们在第 2/10 部分中讨论的那样,这些是与控制器平面相对应的 pod。
通过此安装,我们可以完成在真实 Kubernetes 集群中可以完成的大部分操作。因此,它为开发和实践提供了良好的实用性。
清理
最后,我们可以在离开之前清理一下。我们可以停止集群
$ minikube stop
* Stopping node "minikube" ...
* Powering off "minikube" via SSH ...
* 1 nodes stopped.
ubuntu@ip-172-31-1-122:~$
然后全部删除。
$ minikube delete --all
* Deleting "minikube" in docker ...
* Removing /home/ubuntu/.minikube/machines/minikube ...
* Removed all traces of the "minikube" cluster.
* Successfully deleted all profiles
Kubernetes 对象
Kubernetes 集群中的任何对象都是一个简单的 YAML 文件。我们通过这样的 YAML 文件向 Kubernetes 描述对象,然后它会尽力遵循该规范。作为一个功能极其丰富的工具,我们可以预料到它的配置会非常复杂——事实也确实如此。
但它比原本应该的要简单得多。配置被直观地分解成多个组件,管理起来更加方便。如果我们能够理解这种结构以及每个组件的含义,那么整个配置应该不会太难。
Kubernetes 文档非常简洁,搜索所需内容非常容易。因此无需记住任何语法。我们先来看一个简单的 MySQL 部署示例,它能让我们大致了解 YAML 结构。
持久卷
数据库部署需要一个持久卷,用于在 Pod 生命周期结束后仍保留数据。此类持久卷应使用类似以下 YAML 进行配置。各个字段的含义非常直观。
此配置定义了一个基于本地手动存储类的持久卷。它分配了 20GB 的存储空间,并分配了
kind: PersistentVolume
apiVersion: v1
metadata:
name: mysql-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
持久卷声明
持久卷由 Kubernetes 集群提供。部署会使用持久卷声明为自己申请一个。以下是 PVC 的示例 YAML
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
服务
服务定义非常简单。
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
clusterIP: None
部署
这提供了生成单个 Pod 的模板以及具体的配置细节。
它引用了 PV、PVC 以及使用指定容器构建 Pod 所需的 secret。
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
# Use secret in real usage
- name: MYSQL_ROOT_PASSWORD
value: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
模式
Kubernetes 模式是一个庞大的话题,篇幅太长,无法在这篇小博客中一一涵盖。不过我可以推荐一本很棒的书,里面对这个主题进行了非常详细的讲解。https ://github.com/solegaonkar/KubernetesPatterns
它提供了对各种模式的详细理解,包括
- 基础模式
- 行为模式
- 结构模式
- 配置模式
- 高级模式
反模式
如果使用不当,强大的工具也可能带来灾难。Kubernetes 也是如此。仅仅在应用程序中使用 Kubernetes 并不能使其成为完美的弹性系统。我们应该知道如何使用它。让我们来看看一些让我们的生活苦不堪言的反模式。
最新标签
有些开发人员喜欢处于边缘。他们在部署过程中也秉持着同样的原则。创建 Docker 容器时,应该在 Dockerfile 中提供一个独立的版本号。否则,我们永远不知道什么时候某个升级会毁掉我们的集群。
容器内部配置
我犯过这个错误,所以我非常清楚这一点。老派的类开发人员把常量提取到 Constants.java 文件中时会感到很满足。这是一个很好的做法。但这还不够。每个配置都必须提取到属性文件中,然后再提取到配置中。任何与环境相关的内容都必须进一步提取到剧本中。
过度使用 Kubernetes 功能
Kubernetes 非常适合容器编排。我们应该限制它在这方面的表现。避免使用它的功能,或者依赖它的功能来处理业务逻辑。业务逻辑应该限制在我们的应用程序代码中——在容器中。
使用 kubectl edit/patch
这些功能非常适合在实验室中使用,但不应在实际应用中使用。任何部署都必须经过正确的流程,即更新deployment.yml文件——否则,我们很容易失去对部署内容的控制,无法确定哪些功能有效,哪些无效。
缺少内存/CPU 限制
在部署中指定内存/CPU 限制非常重要。这有助于我们定位故障。我们应该充分了解应用程序的需求,这有助于我们指定这些限制。即使一项服务出现问题,也不应该影响应用程序的其余部分。
过度使用 Kubernetes
Kubernetes 在某些方面确实是一个很棒的工具。但它并非解决所有问题的万能方案。有些服务应该位于 Kubernetes 之外,我们不应该尝试将它们推入集群。例如,数据库就有权位于集群之外。
云端的 Kubernetes
管理 Kubernetes 集群是一项艰巨的任务,这一点显而易见。它需要大量细致的步骤,一旦疏忽任何一个细节,都可能让我们抓狂。最好还是找个人来帮我们做。
此外,高性能和高弹性无法在本地实现。因此,选择云端现成的 Kubernetes 集群非常合理。还有什么比 AWS 更好的呢?那就是弹性 Kubernetes 服务。
易克斯
Amazon ECS 集群是一个或多个容器实例的区域分组,您可以在其上运行任务请求。每个账户在您首次使用 Amazon ECS 服务时都会收到一个默认集群。集群可能包含多种 Amazon EC2 实例类型。
首先,导航到 AWS 控制台上的 EKS 页面。然后点击“创建集群”
接下来,我们只需提供创建新集群的基本细节。
请注意,该角色应在 IAM 中创建。它只是一个具有 EKS 集群所需策略的基本角色。
按照其余页面的默认设置创建新集群。瞧!集群已准备就绪。它拥有简单的用户界面,可用于添加节点和其他配置。现在我们无需再费心安装和配置,只需单击按钮即可完成所有操作。
我们现在只需在 EKS 上配置各个部署即可。其余部分由我们自行管理。Azure、GCP 和 Digital Ocean 提供的 Kubernetes 托管选项非常相似。
Kubectl
正如我们之前所见,kubectl 是 Kubernetes 集群的简单命令行界面。它很简单,但功能非常强大。
kubectl 命令
Kubernetes 自带一个简单的命令行界面——kubectl。它非常强大,我们几乎可以对集群做任何操作。
kubectl 语法很简单。
kubectl <action> <optional parameters> <target> <optional parameters>
操作可以是获取、描述、创建、删除、编辑、应用、执行......目标可以是 pod、部署、服务、容器……
得到
我们可以使用 kubectl get 来获取节点、pod 等的列表。它只是提取一个列表并显示在 STDOUT 中。
kubectl get nodes
kubectl get pod
kubectl get services
kubectl get deployment
kubectl get replicaset
编辑
强烈建议不要这样做。如果需要,我们可以直接在集群上编辑部署 YAML 文件。但这并不建议这样做。如果这样做,很快就会陷入不一致的境地,我们不知道部署了什么。最好先在版本控制中更新 YAML 文件,然后再将其推送到集群。
kubectl edit deployment nginx-depl
类似地,我们可以直接在命令行上创建一个新的部署。
kubectl create deployment nginx-depl --image=nginx
这是正确的做法。
kubectl apply -f nginx-deployment.yaml
日志
我们可以打印出正在运行的pod的日志。
kubectl logs {pod-name}
执行官
我们还可以窥视 Pod 内部。只需进入它的 Linux Shell,看看里面的世界是什么样的。这对于调试非常有用。
kubectl exec -it {pod-name} -- bin/bash
备忘单
一些有用的 kubectl 命令集合
常用命令
姓名 | 命令 |
---|---|
临时运行 curl 测试 | kubectl run --generator=run-pod/v1 --rm mytest --image=yauritux/busybox-curl -it |
临时运行 wget 测试 | kubectl run --generator=run-pod/v1 --rm mytest --image=busybox -it wget |
运行具有 2 个副本的 nginx 部署 | kubectl run my-nginx --image=nginx --replicas=2 --port=80 |
运行 nginx pod 并暴露它 | kubectl run my-nginx --restart=Never --image=nginx --port=80 --expose |
运行 nginx 部署并公开 | kubectl run my-nginx --image=nginx --port=80 --expose |
列出已验证的上下文 | kubectl config get-contexts=, =~/.kube/config |
设置命名空间首选项 | kubectl config set-context <context_name> --namespace=<ns_name> |
列出包含节点信息的 Pod | kubectl get pod -o wide |
列出所有内容 | kubectl get all --all-namespaces |
获取所有服务 | kubectl get service --all-namespaces |
获取所有部署 | kubectl get deployments --all-namespaces |
显示带有标签的节点 | kubectl get nodes --show-labels |
获取带有 json 输出的资源 | kubectl get pods --all-namespaces -o json |
使用试运行验证 yaml 文件 | kubectl create --dry-run --validate -f pod-dummy.yaml |
启动临时 Pod 进行测试 | kubectl run --rm -i -t --image=alpine test-$RANDOM -- sh |
kubectl 运行 shell 命令 | kubectl exec -it mytest -- ls -l /etc/hosts |
通过 configmap 获取系统配置 | kubectl -n kube-system get cm kubeadm-config -o yaml |
获取部署 yaml | kubectl -n denny-websites get deployment mysql -o yaml |
解释资源 | kubectl explain pods=, =kubectl explain svc |
观看播客 | kubectl get pods -n wordpress --watch |
查询健康检查端点 | curl -L http://127.0.0.1:10250/healthz |
在 pod 中打开 bash 终端 | kubectl exec -it storage sh |
检查 pod 环境变量 | kubectl exec redis-master-ft9ex env |
启用 kubectl shell 自动完成 | echo "source <(kubectl completion bash)" >>~/.bashrc 并重新加载 |
在笔记本电脑中使用 minikube dockerd | eval $(minikube docker-env) ,不再需要推送docker hub |
Kubectl 应用一个 yaml 文件夹 | kubectl apply -R -f . |
获取按名称排序的服务 | kubectl get services --sort-by=.metadata.name |
按重启次数对 Pod 进行排序 | kubectl get pods --sort-by='.status.containerStatuses[0].restartCount' |
列出 Pod 和镜像 | kubectl get pods -o='custom-columns=PODS:.metadata.name,Images:.spec.containers[*].image' |
检查性能
姓名 | 命令 |
---|---|
获取节点资源使用情况 | kubectl top node |
获取 pod 资源使用情况 | kubectl top pod |
获取给定 pod 的资源使用情况 | kubectl top <podname> --containers |
列出所有容器的资源利用率 | kubectl top pod --all-namespaces --containers=true |
资源删除
姓名 | 命令 |
---|---|
删除 Pod | kubectl delete pod/<pod-name> -n <my-namespace> |
强制删除 Pod | kubectl delete pod/<pod-name> --grace-period=0 --force |
通过标签删除 Pod | kubectl delete pod -l env=test |
按标签删除部署 | kubectl delete deployment -l app=wordpress |
删除所有按标签过滤的资源 | kubectl delete pods,services -l name=myLabel |
删除命名空间下的资源 | kubectl -n my-ns delete po,svc --all |
按标签删除持久卷 | kubectl delete pvc -l app=wordpress |
仅删除状态完整集(不删除 Pod) | kubectl delete sts/<stateful_set_name> --cascade=false |
日志和会议文件
姓名 | 评论 |
---|---|
配置文件夹 | /etc/kubernetes/ |
证书文件 | /etc/kubernetes/pki/ |
API 服务器的凭证 | /etc/kubernetes/kubelet.conf |
超级用户凭据 | /etc/kubernetes/admin.conf |
kubectl 配置文件 | ~/.kube/config |
Kubernetes 工作目录 | /var/lib/kubelet/ |
Docker 工作目录 | /var/lib/docker/ ,/var/log/containers/ |
Etcd 工作目录 | /var/lib/etcd/ |
网络 cni | /etc/cni/net.d/ |
日志文件 | /var/log/pods/ |
登录工作节点 | /var/log/kubelet.log ,/var/log/kube-proxy.log |
登录主节点 | kube-apiserver.log ,,kube-scheduler.log kube-controller-manager.log |
环境 | /etc/systemd/system/kubelet.service.d/10-kubeadm.conf |
环境 | 导出 KUBECONFIG=/etc/kubernetes/admin.conf |
荚
姓名 | 命令 |
---|---|
列出所有 Pod | kubectl get pods |
列出所有命名空间的 pod | kubectl get pods -all-namespaces |
列出所有关键 Pod | kubectl get -n kube-system pods -a |
列出包含更多信息的 Pod | kubectl get pod -o wide ,kubectl get pod/<pod-name> -o yaml |
获取 Pod 信息 | kubectl describe pod/srv-mysql-server |
列出所有带有标签的 Pod | kubectl get pods --show-labels |
列出正在运行的 Pod | kubectl get pods --field-selector=status.phase=Running |
获取 Pod initContainer 状态 | kubectl get pod --template '{{.status.initContainerStatuses}}' <pod-name> |
kubectl 运行命令 | kubectl exec -it -n "$ns" "$podname" -- sh -c "echo $msg >>/dev/err.log" |
观看播客 | kubectl get pods -n wordpress --watch |
通过选择器获取 pod | kubectl get pods --selector="app=syslog" -o jsonpath='{.items[*].metadata.name}' |
列出 Pod 和镜像 | kubectl get pods -o='custom-columns=PODS:.metadata.name,Images:.spec.containers[*].image' |
列出 Pod 和容器 | kubectl get pods -o='custom-columns=PODS:.metadata.name,CONTAINERS:.spec.containers[*].name' |
标签和注释
姓名 | 命令 |
---|---|
按标签过滤 Pod | kubectl get pods -l owner=denny |
手动为 Pod 添加标签 | kubectl label pods dummy-input owner=denny |
移除标签 | kubectl label pods dummy-input owner- |
手动向 Pod 添加注释 | kubectl annotate pods dummy-input my-url=https://dennyzhang.com |
部署与规模
姓名 | 命令 |
---|---|
横向扩展 | kubectl scale --replicas=3 deployment/nginx-app |
在线滚动升级 | kubectl rollout app-v1 app-v2 --image=img:v2 |
回滚备份 | kubectl rollout app-v1 app-v2 --rollback |
“列表”卷展栏 | kubectl get rs |
检查更新状态 | kubectl rollout status deployment/nginx-app |
检查更新历史记录 | kubectl rollout history deployment/nginx-app |
暂停/恢复 | kubectl rollout pause deployment/nginx-deployment ,resume |
回滚到先前版本 | kubectl rollout undo deployment/nginx-deployment |
配额、限制和资源
姓名 | 命令 |
---|---|
列出资源配额 | kubectl get resourcequota |
列表限制范围 | kubectl get limitrange |
自定义资源定义 | kubectl set resources deployment nginx -c=nginx --limits=cpu=200m |
自定义资源定义 | kubectl set resources deployment nginx -c=nginx --limits=memory=512Mi |
服务
姓名 | 命令 |
---|---|
列出所有服务 | kubectl get services |
列出服务端点 | kubectl get endpoints |
获取服务详情 | kubectl get service nginx-service -o yaml |
获取服务集群ip | kubectl get service nginx-service -o go-template='{{.spec.clusterIP}}' |
获取服务集群端口 | kubectl get service nginx-service -o go-template='{{(index .spec.ports 0).port}}' |
将部署公开为 lb 服务 | kubectl expose deployment/my-app --type=LoadBalancer --name=my-service |
将服务公开为 lb 服务 | kubectl expose service/wordpress-1-svc --type=LoadBalancer --name=ns1 |
秘密
姓名 | 命令 |
---|---|
列出机密 | kubectl get secrets --all-namespaces |
生成秘密 | echo -n 'mypasswd', then redirect to base64 --decode |
获取秘密 | kubectl get secret denny-cluster-kubeconfig |
获取机密的特定字段 | kubectl get secret denny-cluster-kubeconfig -o jsonpath="{.data.value}" |
从 cfg 文件创建秘密 | kubectl create secret generic db-user-pass --from-file=./username.txt |
StatefulSet
姓名 | 命令 |
---|---|
列出 statefulset | kubectl get sts |
仅删除 statefulset(不删除 Pod) | kubectl delete sts/<stateful_set_name> --cascade=false |
扩展 StatefulSet | kubectl scale sts/<stateful_set_name> --replicas=5 |
数量和数量声明
姓名 | 命令 |
---|---|
列出存储类别 | kubectl get storageclass |
检查已安装的卷 | kubectl exec storage ls /data |
检查持久卷 | kubectl describe pv/pv0001 |
将本地文件复制到 pod | kubectl cp /tmp/my <some-namespace>/<some-pod>:/tmp/server |
复制pod文件到本地 | kubectl cp <some-namespace>/<some-pod>:/tmp/server /tmp/my |
事件和指标
姓名 | 命令 |
---|---|
查看所有活动 | kubectl get events --all-namespaces |
按时间戳排序的列表事件 | kubectl get events --sort-by=.metadata.creationTimestamp |
节点维护
姓名 | 命令 |
---|---|
将节点标记为不可调度 | kubectl cordon $NODE_NAME |
将节点标记为可调度 | kubectl uncordon $NODE_NAME |
排空节点以准备维护 | kubectl drain $NODE_NAME |
命名空间和安全
姓名 | 命令 |
---|---|
列出已验证的上下文 | kubectl config get-contexts=, =~/.kube/config |
设置命名空间首选项 | kubectl config set-context <context_name> --namespace=<ns_name> |
切换上下文 | kubectl config use-context <context_name> |
从配置文件加载上下文 | kubectl get cs --kubeconfig kube_config.yml |
删除指定上下文 | kubectl config delete-context <context_name> |
列出所有定义的命名空间 | kubectl get namespaces |
列出证书 | kubectl get csr |
检查用户权限 | kubectl --as=system:serviceaccount:ns-denny:test-privileged-sa -n ns-denny auth can-i use pods/list |
网络
姓名 | 命令 |
---|---|
临时添加端口转发 | kubectl port-forward redis-134 6379:6379 |
添加部署端口转发 | kubectl port-forward deployment/redis-master 6379:6379 |
为副本集添加端口转发 | kubectl port-forward rs/redis-master 6379:6379 |
为服务添加端口转发 | kubectl port-forward svc/redis-master 6379:6379 |
获取网络策略 | kubectl get NetworkPolicy |
修补
姓名 | 概括 |
---|---|
将服务补丁到负载均衡器 | kubectl patch svc $svc_name -p '{"spec": {"type": "LoadBalancer"}}' |
扩展
姓名 | 概括 |
---|---|
枚举可用的资源类型 | kubectl api-resources |
列出 API 组 | kubectl api-versions |
列出所有 CRD | kubectl get crd |
列出存储类别 | kubectl get storageclass |
组件和服务
主节点上的服务
姓名 | 概括 |
---|---|
kube-api服务器 | API 网关。从主节点公开 Kubernetes API |
etcd | 所有 k8s 集群数据的可靠数据存储 |
kube-scheduler | 安排 Pod 在选定节点上运行 |
kube-控制器-管理器 | 协调状态。节点/复制/端点/令牌控制器和服务帐户等 |
工作节点上的服务
姓名 | 概括 |
---|---|
库贝莱特 | 节点代理确保容器在 pod 中运行 |
kube-proxy | 管理容器的网络连接。例如 iptable、ipvs |
容器运行时 | Kubernetes 支持的运行时:dockerd、cri-o、runc 和任何OCI 运行时规范实现。 |
附加组件:实现集群功能的 Pod 和服务
姓名 | 概括 |
---|---|
DNS | 为 Kubernetes 服务提供 DNS 记录 |
Web 用户界面 | Kubernetes 集群的通用 Web UI |
容器资源监控 | 收集、存储和提供容器指标 |
集群级日志记录 | 将容器日志保存到具有搜索/浏览界面的中央日志存储中 |
工具
更多资源
MicroK8s
曾经有一段时间,Kubernetes 意味着在超级服务器中投入大量的计算和巨大的开销。多年来,它已经变得轻薄,以至于 Kubernetes 也开始渗透到物联网设备中!让我们试着在 Raspberry Pi 上安装它吧!
为此,我们需要一个 Ubuntu 桌面——Windows 系统不够用。微软的粉丝如果舍不得 Windows,可以将 Ubuntu 安装在虚拟机里。
我们至少需要两台 Raspberry Pi(用于搭建集群)。在这个例子中我用了三台。当然,我们还需要一根 Micro USB 数据线来连接每台 Raspberry Pi。每台 Raspberry Pi 还需要一张至少 8GB 的 microSD 卡,并刷入 Ubuntu Server 镜像。
安装 MicroK8s
实际安装过程并不困难。我们只需分别通过 ssh 进入设备,然后按照以下步骤操作:
sudo vi /boot/firmware/cmdline.txt
命令行比较复杂。我们只是在其中添加了几个选项cgroup_enable=memory cgroup_memory=1
我的 Pi 上的完整配置如下所示。具体配置会根据版本不同而有所差异:
cgroup_enable=memory cgroup_memory=1 net.ifnames=0 dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
接下来,我们重启设备
sudo reboot
一旦恢复,我们再次 ssh 进入它并使用简单的命令安装 MicroK8s
sudo snap install microk8s --classic
这也会安装命令行实用程序 - 也称为 microk8s。以下是一些简单的命令来介绍它:
# Start the cluster
microk8s.start
# Inspect status of services
microk8s.inspect
# Stop all Kubernetes services
microk8s.stop
# Enable addons - eg kubedns addon
microk8s.enable dns
# Get status of the cluster
microk8s.kubectl cluster-info
主/从
一旦我们在每台服务器上安装了 microk8s,我们就可以将它们连接到集群中。我们需要选择其中一个作为主服务器,并运行以下命令:
sudo microk8s.add-node
这会将当前节点声明为主节点,并将提供连接字符串来邀请其他工作节点。连接字符串如下所示:<master_ip>:<port>/<token>
接下来,我们转到每个(想要的)子节点并在 shell 上运行此命令
microk8s.join <master_ip>:<port>/<token>
这样主节点和从节点就连接起来了。几秒钟后,我们可以再次检查主节点,看看集群情况。
microk8s.kubectl get node
公用事业
有人可能会问,这样做有什么大不了的?为什么我们需要在物联网设备上运行 Kubernetes?但这很有意义,比真正的服务器有意义得多。物联网设备非常脆弱,我们肯定会遇到一些问题。如果我们在现场运行一个关键任务应用程序,那么让多个设备相互覆盖是很有用的——以确保应用程序持续运行。
面试问题
我们已经讲解了大部分基本概念。是时候温习一下面试题了,这样你就能完美胜任这份工作了!
什么是 Kubernetes
这是最简单的。读过这篇博文的人肯定知道这一点。但是,我们来回顾一下容易记住的内容:
“Kubernetes 是一个开源容器管理工具,负责容器部署、容器的扩容和缩容以及负载均衡。作为 Google 的创意,它拥有优秀的社区,并与所有云提供商完美协作。因此,我们可以说 Kubernetes 不是一个容器化平台,而是一个多容器管理解决方案。”
什么是容器编排
我们说过 Kubernetes 负责容器编排。但是这个容器编排到底是什么呢?
设想一下,一个应用程序包含 5-6 个微服务。现在,这些微服务被放置在单独的容器中,但如果没有容器编排,它们就无法通信。因此,正如编排意味着所有乐器融合在一起,共同奏出和谐的音乐一样,容器编排意味着所有位于单独容器中的服务协同工作,以满足单个服务器的需求。
Heapster 是什么?
现在来谈谈一些重要的事情……Heapster 是一个集群范围的数据聚合器,由 Kubelet 提供,运行在每个节点上。这个容器管理工具在 Kubernetes 集群上原生支持,并以 Pod 的形式运行,就像集群中的任何其他 Pod 一样。因此,它基本上可以发现集群中的所有节点,并通过机器上的 Kubernetes 代理从集群中的 Kubernetes 节点查询使用情况信息。
Kubernetes 架构的组件有哪些?
Kubernetes 架构主要包含两个组件:主节点和工作节点。如下图所示,主节点和工作节点内部包含许多内置组件。主节点包含 kube-controller-manager、kube-apiserver、kube-scheduler 和 etcd。而工作节点则在每个节点上运行 kubelet 和 kube-proxy。
什么是 Kube-Proxy
Kube-proxy 可以在每个节点上运行,并能够跨后端网络服务进行简单的 TCP/UDP 数据包转发。因此,它本质上是一个网络代理,可以反映每个节点上 Kubernetes API 中配置的服务。因此,Docker-linkable 兼容的环境变量提供了代理开放的集群 IP 和端口。
什么是 Kube API Server?
kube-apiserver 遵循横向扩展架构,是主节点控制面板的前端。它暴露 Kubernetes 主节点组件的所有 API,并负责建立 Kubernetes 节点和 Kubernetes 主组件之间的通信。
什么是 Kube Scheduler?
kube-scheduler 负责在工作节点上分配和管理工作负载。因此,它会根据资源需求选择最合适的节点来运行未调度的 Pod,并跟踪资源利用率。它确保工作负载不会调度到已满的节点上。
什么是 Kubernetes 控制器管理器?
多个控制器进程在主节点上运行,但被编译在一起,作为单个进程运行,该进程即 Kubernetes 控制器管理器。因此,控制器管理器是一个守护进程,它嵌入控制器并执行命名空间创建和垃圾收集。它负责与 API 服务器通信,以管理端点。
控制器管理器有哪些不同类型?
主节点上运行的不同类型的控制器管理器有:
- 节点控制器:管理节点的状态(创建、更新、删除)
- 复制控制器:维护每个复制对象的 Pod 数量
- 令牌控制器:为新命名空间创建默认帐户和 API 访问令牌
- 端点控制器:负责管理 Pod 和服务等端点对象
- 路由控制器:管理底层云基础设施中的流量路由
- 卷控制器:管理存储卷并与云提供商交互以协调卷
什么是 etcd?
Etcd 是用 Go 编程语言编写的,是一个用于协调分布式工作的分布式键值存储。因此,Etcd 存储 Kubernetes 集群的配置数据,表示集群在任何给定时间点的状态。
Kubernetes 中有哪些不同类型的服务?
- 集群 IP:将服务暴露在集群内部 IP 上。这使得服务在集群内部可以访问。这是默认的服务类型
- 节点端口:这将服务通过每个节点的 IP 以静态端口公开。节点端口服务将路由到的集群 IP 服务将自动创建。
- 负载均衡器:使用云提供的负载均衡器将服务公开到外部。外部负载均衡器将自动创建路由到的服务
- 外部名称:通过返回带有其值的 CNAME 记录将服务映射到外部名称字段的内容。
Kubernetes 负载均衡器
负载均衡器是公开服务最常见、最标准的方式之一。根据工作环境,负载均衡器有两种类型:内部负载均衡器和外部负载均衡器。内部负载均衡器自动平衡负载并根据所需配置分配 Pod,而外部负载均衡器则将外部负载的流量引导至后端 Pod。
什么是 Ingress 网络?
Ingress 网络是一组规则,充当 Kubernetes 集群的入口点。它允许入站连接,这些连接可以配置为通过可访问的 URL、负载均衡流量或提供基于名称的虚拟主机来向外部提供服务。因此,Ingress 是一个 API 对象,用于管理对集群中服务的外部访问(通常通过 HTTP 访问),并且是公开服务的最有效方式。
配置图与机密
理想情况下,配置映射 (Config Map) 以纯文本格式存储应用程序配置,而 Secrets 则以加密格式存储密码等敏感数据。配置映射和 Secret 都可以用作卷 (Volume),并通过 Pod 定义文件挂载到 Pod 内部。
多容器 Pod 模式
- sidecar:一个 pod 规范,它运行主容器和一个执行一些实用工作的辅助容器,但这对于主容器的工作来说并不是必需的。
- 适配器:适配器容器将检查应用程序文件的内容,进行某种重组和重新格式化,并将正确格式的输出写入该位置。
- ambassador:它将容器与外界连接起来。它是一个代理,允许其他容器连接到本地主机的端口。
Kubernetes 中的 API 安全性
使用 API 服务器 authorization-mode=Node、RBAC 的正确身份验证模式,确保所有流量都受到 TLS 保护,使用 API 身份验证(较小的集群可能使用证书,但较大的多租户可能需要 AD 或某些 OIDC 身份验证)。
使 kubeless 通过 authorization-mode=Webhook 保护其 API。确保 kube-dashboard 使用限制性 RBAC 角色策略。监控 RBAC 故障。删除默认的 ServiceAccount 权限。过滤到云 API 元数据 API 的出口。过滤掉进入 kube-system 命名空间的所有流量(DNS 除外)。
对所有命名空间的所有入站流量采用默认拒绝策略是一种良好做法。您可以明确允许每个部署。使用 podsecurity 策略来限制容器并保护节点,并保持 kube 版本为最新版本。
工作节点:
Kubernetes 工作节点是部署工作负载的机器。工作负载以容器化应用程序的形式存在,因此集群中的每个节点都必须运行容器运行时(例如 Docker)才能运行这些工作负载。您可以将多个主节点映射到多个工作节点,也可以将单个主节点映射到单个工作节点。此外,工作节点之间不会进行闲聊或领导者选举,也不会发生任何会导致奇数数量的活动。容器运行时的作用是启动和管理容器。kubelet 负责维护每个节点的运行状态,并从主节点接收命令并执行相应的工作。它还会对节点进行健康检查,确保它们处于健康状态。Kubelet 还负责收集 Pod 的指标数据。kube-proxy 是一个管理主机子网划分并使服务可供其他组件使用的组件。
无需重启 Pod 即可重新加载配置图
我们需要某种方式来触发重新加载。您可以每分钟检查一次,或者为 API 设置一个重新加载端点,或者将配置映射投射到卷中,这样可以使用 inotify 来感知更改。这取决于容器如何使用配置映射。如果是环境变量,则不需要。如果是卷挂载,则容器中的文件会更新,并准备好供服务使用,但需要重新加载文件。
容器不会重启。如果配置映射以卷的形式挂载,则会动态更新。如果是环境变量,则会保持旧值,直到容器重启。将配置映射以卷的形式挂载到 Pod 中,投影文件会定期更新,而非实时更新。然后,让应用程序识别磁盘上的配置是否已更改并重新加载。
文章来源:https://dev.to/solegaonkar/understanding-kubernetes-developer-s-guide-3np