理解 Kubernetes:开发者指南 为什么选择 Kubernetes? 核心概念 Kubernetes 组件 Minikube Kubernetes 对象 模式 反模式 Kubernetes 云平台 Kubectl MicroK8s 面试题

2025-05-26

了解 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中的容器。

主节点(也称为控制平面)包含多个负责编排的组件。工作节点与主节点连接,并通过在其上运行的精简应用程序(KubeletK-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
Enter fullscreen mode Exit fullscreen mode

真快。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
Enter fullscreen mode Exit fullscreen mode

好了!Minikube 集群已启动并准备就绪。

如果它抱怨可用内存不足,它还会建议一种限制内存使用的方法。我们可以在启动命令中使用建议的选项,然后重试。

我们也可以直接改变分配的内存配置

$ minikube config set memory 16384
! These changes will take effect upon a minikube delete and then a minikube start
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

(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:~$
Enter fullscreen mode Exit fullscreen mode

然后全部删除。

$ minikube delete --all
* Deleting "minikube" in docker ...
* Removing /home/ubuntu/.minikube/machines/minikube ...
* Removed all traces of the "minikube" cluster.
* Successfully deleted all profiles
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

持久卷声明

持久卷由 Kubernetes 集群提供。部署会使用持久卷声明为自己申请一个。以下是 PVC 的示例 YAML

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
Enter fullscreen mode Exit fullscreen mode

服务

服务定义非常简单。

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
  clusterIP: None
Enter fullscreen mode Exit fullscreen mode

部署

这提供了生成单个 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
Enter fullscreen mode Exit fullscreen mode

模式

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 页面。然后点击“创建集群”

图像.png

接下来,我们只需提供创建新集群的基本细节。

图像.png

请注意,该角色应在 IAM 中创建。它只是一个具有 EKS 集群所需策略的基本角色。

按照其余页面的默认设置创建新集群。瞧!集群已准备就绪。它拥有简单的用户界面,可用于添加节点和其他配置。现在我们无需再费心安装和配置,只需单击按钮即可完成所有操作。

我们现在只需在 EKS 上配置各个部署即可。其余部分由我们自行管理。Azure、GCP 和 Digital Ocean 提供的 Kubernetes 托管选项非常相似。

Kubectl

正如我们之前所见,kubectl 是 Kubernetes 集群的简单命令行界面。它很简单,但功能非常强大。

kubectl 命令

Kubernetes 自带一个简单的命令行界面——kubectl。它非常强大,我们几乎可以对集群做任何操作。

kubectl 语法很简单。

kubectl <action> <optional parameters> <target> <optional parameters>
Enter fullscreen mode Exit fullscreen mode

操作可以是获取、描述、创建、删除、编辑、应用、执行......目标可以是 pod、部署、服务、容器……

得到

我们可以使用 kubectl get 来获取节点、pod 等的列表。它只是提取一个列表并显示在 STDOUT 中。

kubectl get nodes
Enter fullscreen mode Exit fullscreen mode
kubectl get pod
Enter fullscreen mode Exit fullscreen mode
kubectl get services
Enter fullscreen mode Exit fullscreen mode
kubectl get deployment
Enter fullscreen mode Exit fullscreen mode
kubectl get replicaset
Enter fullscreen mode Exit fullscreen mode

编辑

强烈建议不要这样做。如果需要,我们可以直接在集群上编辑部署 YAML 文件。但这并不建议这样做。如果这样做,很快就会陷入不一致的境地,我们不知道部署了什么。最好先在版本控制中更新 YAML 文件,然后再将其推送到集群。

kubectl edit deployment nginx-depl
Enter fullscreen mode Exit fullscreen mode

类似地,我们可以直接在命令行上创建一个新的部署。

kubectl create deployment nginx-depl --image=nginx
Enter fullscreen mode Exit fullscreen mode

这是正确的做法。

kubectl apply -f nginx-deployment.yaml
Enter fullscreen mode Exit fullscreen mode

日志

我们可以打印出正在运行的pod的日志。

kubectl logs {pod-name}
Enter fullscreen mode Exit fullscreen mode

执行官

我们还可以窥视 Pod 内部。只需进入它的 Linux Shell,看看里面的世界是什么样的。这对于调试非常有用。

kubectl exec -it {pod-name} -- bin/bash
Enter fullscreen mode Exit fullscreen mode

备忘单

一些有用的 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.logkube-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 widekubectl 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-deploymentresume
回滚到先前版本 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
容器资源监控 收集、存储和提供容器指标
集群级日志记录 将容器日志保存到具有搜索/浏览界面的中央日志存储中

工具

姓名 概括
库布克特尔 用于与 K8S 集群通信的命令行工具
kubeadm 引导集群的命令
库贝菲德 控制 Kubernetes 集群联邦的命令行

更多资源

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
Enter fullscreen mode Exit fullscreen mode

命令行比较复杂。我们只是在其中添加了几个选项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
Enter fullscreen mode Exit fullscreen mode

接下来,我们重启设备

sudo reboot
Enter fullscreen mode Exit fullscreen mode

一旦恢复,我们再次 ssh 进入它并使用简单的命令安装 MicroK8s

sudo snap install microk8s --classic
Enter fullscreen mode Exit fullscreen mode

这也会安装命令行实用程序 - 也称为 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
Enter fullscreen mode Exit fullscreen mode

主/从

一旦我们在每台服务器上安装了 microk8s,我们就可以将它们连接到集群中。我们需要选择其中一个作为主服务器,并运行以下命令:

sudo microk8s.add-node
Enter fullscreen mode Exit fullscreen mode

这会将当前节点声明为主节点,并将提供连接字符串来邀请其他工作节点。连接字符串如下所示:<master_ip>:<port>/<token>

接下来,我们转到每个(想要的)子节点并在 shell 上运行此命令

microk8s.join <master_ip>:<port>/<token>
Enter fullscreen mode Exit fullscreen mode

这样主节点和从节点就连接起来了。几秒钟后,我们可以再次检查主节点,看看集群情况。

microk8s.kubectl get node
Enter fullscreen mode Exit fullscreen mode

公用事业

有人可能会问,这样做有什么大不了的?为什么我们需要在物联网设备上运行 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
PREV
Flutter 开发者指南 Flutter:是什么?如何使用?为什么?Flutter 设置 Dart 基础知识 Flutter 基础知识 状态管理的艺术
NEXT
最佳 VSCode 主题 2020 年 50 个 VS Code 主题