概述

此文档仅为个人的学习笔记,记录下来是为了以后遗忘时可以翻阅。最好的 Kubernetes 文档在 kubernetes.io,请大家去官网学习!

Kubernetes 是什么?

Kubernetes 是一个可移植的、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。Kubernetes 拥有一个庞大且快速增长的生态系统。Kubernetes 的服务、支持和工具广泛可用。

名称 Kubernetes 源于希腊语,意为“舵手”或“飞行员”。Google 在 2014 年开源了 Kubernetes 项目, Kubernetes 建立在 Google 在大规模运行生产工作负载方面拥有十几年的经验 的基础上,结合了社区中最好的想法和实践。

时光回溯

让我们回顾一下为什么 Kubernetes 如此有用。

传统部署时代

早期,各个组织机构在物理服务器上运行应用程序。无法为物理服务器中的应用程序定义资源边界,这会导致资源分配问题。 例如,如果在物理服务器上运行多个应用程序,则可能会出现一个应用程序占用大部分资源的情况, 结果可能导致其他应用程序的性能下降。 一种解决方案是在不同的物理服务器上运行每个应用程序,但是由于资源利用不足而无法扩展, 并且维护许多物理服务器的成本很高。

虚拟化部署时代:

作为解决方案,引入了虚拟化。虚拟化技术允许你在单个物理服务器的 CPU 上运行多个虚拟机(VM)。 虚拟化允许应用程序在 VM 之间隔离,并提供一定程度的安全,因为一个应用程序的信息不能被另一应用程序随意访问。

虚拟化技术能够更好地利用物理服务器上的资源,并且因为可轻松地添加或更新应用程序而可以实现更好的可伸缩性,降低硬件成本等等。

每个 VM 是一台完整的计算机,在虚拟化硬件之上运行所有组件,包括其自己的操作系统。

容器部署时代:

容器类似于 VM,但是它们具有被放宽的隔离属性,可以在应用程序之间共享操作系统(OS)。 因此,容器被认为是轻量级的。容器与 VM 类似,具有自己的文件系统、CPU、内存、进程空间等。 由于它们与基础架构分离,因此可以跨云和 OS 发行版本进行移植。

容器因具有许多优势而变得流行起来。下面列出的是容器的一些好处:

  • 敏捷应用程序的创建和部署:与使用 VM 镜像相比,提高了容器镜像创建的简便性和效率。
  • 持续开发、集成和部署:通过快速简单的回滚(由于镜像不可变性),支持可靠且频繁的容器镜像构建和部署。
  • 关注开发与运维的分离:在构建/发布时而不是在部署时创建应用程序容器镜像, 从而将应用程序与基础架构分离。
  • 可观察性不仅可以显示操作系统级别的信息和指标,还可以显示应用程序的运行状况和其他指标信号。
  • 跨开发、测试和生产的环境一致性:在便携式计算机上与在云中相同地运行。
  • 跨云和操作系统发行版本的可移植性:可在 Ubuntu、RHEL、CoreOS、本地、 Google Kubernetes Engine 和其他任何地方运行。
  • 以应用程序为中心的管理:提高抽象级别,从在虚拟硬件上运行 OS 到使用逻辑资源在 OS 上运行应用程序。
  • 松散耦合、分布式、弹性、解放的微服务:应用程序被分解成较小的独立部分, 并且可以动态部署和管理 - 而不是在一台大型单机上整体运行。
  • 资源隔离:可预测的应用程序性能。
  • 资源利用:高效率和高密度。

Kubernetes 能做什么?

容器是打包和运行应用程序的最好方式。在生产环境中,你需要管理运行应用程序的容器,并确保不会停机。 例如,如果一个容器发生故障,则需要启动另一个容器。如果由系统处理此行为,会不会更容易?

这就是 Kubernetes 来解决这些问题的方法! Kubernetes 为你提供了一个可弹性运行分布式系统的框架。 Kubernetes 会满足你的扩展要求、故障转移、部署模式等。 例如,Kubernetes 可以轻松管理系统的 Canary 部署。

Kubernetes 为你提供:

  • 服务发现和负载均衡

    Kubernetes 可以使用 DNS 名称或自己的 IP 地址公开容器,如果进入容器的流量很大, Kubernetes 可以负载均衡并分配网络流量,从而使部署稳定。

  • 存储编排

    Kubernetes 允许你自动挂载你选择的存储系统,例如本地存储、公共云提供商等。

  • 自动部署和回滚

    你可以使用 Kubernetes 描述已部署容器的所需状态,它可以以受控的速率将实际状态更改为期望状态。例如,你可以自动化 Kubernetes 来为你的部署创建新容器, 删除现有容器并将它们的所有资源用于新容器。

  • 自动完成装箱计算

    Kubernetes 允许你指定每个容器所需 CPU 和内存(RAM)。 当容器指定了资源请求时,Kubernetes 可以做出更好的决策来管理容器的资源。

  • 自我修复

    Kubernetes 重新启动失败的容器、替换容器、杀死不响应用户定义的运行状况检查的容器,并且在准备好服务之前不将其通告给客户端。

  • 密钥与配置管理

    Kubernetes 允许你存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。 你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。

Kubernetes 不是什么?

Kubernetes 不是传统的、包罗万象的 PaaS(平台即服务)系统。 由于 Kubernetes 在容器级别而不是在硬件级别运行,它提供了 PaaS 产品共有的一些普遍适用的功能, 例如部署、扩展、负载均衡、日志记录和监视。 但是,Kubernetes 不是单体系统,默认解决方案都是可选和可插拔的。 Kubernetes 提供了构建开发人员平台的基础,但是在重要的地方保留了用户的选择和灵活性。

Kubernetes:

  • 不限制支持的应用程序类型。 Kubernetes 旨在支持极其多种多样的工作负载,包括无状态、有状态和数据处理工作负载。 如果应用程序可以在容器中运行,那么它应该可以在 Kubernetes 上很好地运行。

  • 不部署源代码,也不构建你的应用程序。 持续集成(CI)、交付和部署(CI/CD)工作流取决于组织的文化和偏好以及技术要求。

  • 不提供应用程序级别的服务作为内置服务,例如中间件(例如,消息中间件)、 数据处理框架(例如,Spark)、数据库(例如,mysql)、缓存、集群存储系统 (例如,Ceph)。这样的组件可以在 Kubernetes 上运行,并且/或者可以由运行在 Kubernetes 上的应用程序通过可移植机制(例如, 开放服务代理)来访问。

  • 不要求日志记录、监视或警报解决方案。 它提供了一些集成作为概念证明,并提供了收集和导出指标的机制。

  • 不提供或不要求配置语言/系统(例如 jsonnet),它提供了声明性 API, 该声明性 API 可以由任意形式的声明性规范所构成。

  • 不提供也不采用任何全面的机器配置、维护、管理或自我修复系统。

  • 此外,Kubernetes 不仅仅是一个编排系统,实际上它消除了编排的需要。 编排的技术定义是执行已定义的工作流程:首先执行 A,然后执行 B,再执行 C。 相比之下,Kubernetes 包含一组独立的、可组合的控制过程, 这些过程连续地将当前状态驱动到所提供的所需状态。 如何从 A 到 C 的方式无关紧要,也不需要集中控制,这使得系统更易于使用 且功能更强大、系统更健壮、更为弹性和可扩展。

Kubernetes 组件

当你部署完 Kubernetes, 即拥有了一个完整的集群。

一个 Kubernetes 集群由一组被称作节点的机器组成。这些节点上运行 Kubernetes 所管理的容器化应用。集群具有至少一个工作节点。

工作节点托管作为应用负载的组件的 Pod 。控制平面管理集群中的工作节点和 Pod 。 为集群提供故障转移和高可用性,这些控制平面一般跨多主机运行,集群跨多个节点运行。

本文档概述了交付正常运行的 Kubernetes 集群所需的各种组件。

控制平面组件(Control Plane Components)

控制平面的组件对集群做出全局决策(比如调度),以及检测和响应集群事件(例如,当不满足部署的 replicas 字段时,启动新的 pod)。

控制平面组件可以在集群中的任何节点上运行。 然而,为了简单起见,设置脚本通常会在同一个计算机(一般使用 master 节点)上启动所有控制平面组件,并且不会在此计算机上运行用户容器。

kube-apiserver

API 服务器是 Kubernetes 控制面的组件, 该组件公开了 Kubernetes API。 API 服务器是 Kubernetes 控制面的前端。

Kubernetes API 服务器的主要实现是 kube-apiserver。 kube-apiserver 设计上考虑了水平伸缩,也就是说,它可通过部署多个实例进行伸缩。 你可以运行 kube-apiserver 的多个实例,并在这些实例之间平衡流量。

etcd

etcd 是兼具一致性和高可用性的键值数据库,可以作为保存 Kubernetes 所有集群数据的后台数据库。

你的 Kubernetes 集群的 etcd 数据库通常需要有个备份计划。

要了解 etcd 更深层次的信息,请参考 etcd 文档

kube-scheduler

控制平面组件,负责监视新创建的、未指定运行节点(node)的 Pods,选择节点让 Pod 在上面运行。

调度决策考虑的因素包括单个 Pod 和 Pod 集合的资源需求、硬件/软件/策略约束、亲和性和反亲和性规范、数据位置、工作负载间的干扰和最后时限。

kube-controller-manager

在主节点上运行控制器的组件。

从逻辑上讲,每个控制器都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在一个进程中运行。

这些控制器包括:

  • 节点控制器(Node Controller): 负责在节点出现故障时进行通知和响应

  • 副本控制器(Replication Controller): 负责为系统中的每个副本控制器对象维护正确数量的 Pod

  • 端点控制器(Endpoints Controller): 填充端点(Endpoints)对象(即加入 Service 与 Pod)

  • 服务帐户和令牌控制器(Service Account & Token Controllers): 为新的命名空间创建默认帐户和 API 访问令牌

Node 组件

节点组件在每个节点上运行,维护运行的 Pod 并提供 Kubernetes 运行环境。

kubelet

一个在集群中每个节点(node)上运行的代理。 它保证容器(containers)都运行在 Pod 中。

kubelet 接收一组通过各类机制提供给它的 PodSpecs,确保这些 PodSpecs 中描述的容器处于运行状态且健康。 kubelet 不会管理不是由 Kubernetes 创建的容器。

kube-proxy

kube-proxy 是集群中每个节点上运行的网络代理, 实现 Kubernetes 服务(Service) 概念的一部分。

kube-proxy 维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话与 Pod 进行网络通信。

如果操作系统提供了数据包过滤层并可用的话,kube-proxy 会通过它来实现网络规则。否则, kube-proxy 仅转发流量本身。

容器运行时(Container Runtime)

容器运行环境是负责运行容器的软件。

Kubernetes 支持多个容器运行环境: Docker、 containerd、CRI-O 以及任何实现 Kubernetes CRI (容器运行环境接口)

插件(Addons)

插件使用 Kubernetes 资源(DaemonSet、 Deployment等)实现集群功能。 因为这些插件提供集群级别的功能,插件中命名空间域的资源属于 kube-system 命名空间。

DNS

尽管其他插件都并非严格意义上的必需组件,但几乎所有 Kubernetes 集群都应该有集群 DNS, 因为很多示例都需要 DNS 服务。

集群 DNS 是一个 DNS 服务器,和环境中的其他 DNS 服务器一起工作,它为 Kubernetes 服务提供 DNS 记录。

Kubernetes 启动的容器自动将此 DNS 服务器包含在其 DNS 搜索列表中。

Web 界面(仪表盘)

Dashboard 是Kubernetes 集群的通用、基于 Web 的用户界面。 它使用户可以管理集群中运行的应用程序以及集群本身并进行故障排除。

Ingress Controller

为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。

容器资源监控

容器资源监控将关于容器的一些常见的时间序列度量值保存到一个集中的数据库中,并提供用于浏览这些数据的界面,参考 Prometheus 项目 结合 Grafana 项目

集群层面日志

集群层面日志 机制负责将容器的日志数据保存到一个集中的日志存储中,该存储能够提供搜索和浏览接口。

Kubernetes API

Kubernetes 控制面的核心是 API 服务器。 API 服务器负责提供 HTTP API,以供用户、集群中的不同部分和集群外部组件相互通信。

Kubernetes API 使你可以查询和操纵 Kubernetes API 中对象(例如:Pod、Namespace、ConfigMap 和 Event)的状态。

大部分操作都可以通过 kubectl 命令行接口或类似 kubeadm 这类命令行工具来执行, 这些工具在背后也是调用 API。不过,你也可以使用 REST 调用来访问这些 API。

使用 Kubernetes 对象

理解 Kubernetes 对象

本页说明了 Kubernetes 对象在 Kubernetes API 中是如何表示的,以及如何在 .yaml 格式的文件中表示。

理解 Kubernetes 对象

在 Kubernetes 系统中,Kubernetes 对象是持久化的实体。 Kubernetes 使用这些实体去表示整个集群的状态。特别地,它们描述了如下信息:

  • 哪些容器化应用在运行(以及在哪些节点上运行)
  • 可以被应用使用的资源
  • 关于应用运行时表现的策略,比如重启策略、升级策略,以及容错策略

Kubernetes 对象是 “目标性记录” —— 一旦创建对象,Kubernetes 系统将持续工作以确保对象存在。 通过创建对象,本质上是在告知 Kubernetes 系统,所需要的集群工作负载看起来是什么样子的, 这就是 Kubernetes 集群的期望状态(Desired State)。

操作 Kubernetes 对象 —— 无论是创建、修改,或者删除 —— 都需要使用 Kubernetes API。 比如,当使用 kubectl 命令行接口时,CLI 会执行必要的 Kubernetes API 调用, 也可以在程序中使用 客户端库直接调用 Kubernetes API。

对象规约(Spec)与状态(Status)

几乎每个 Kubernetes 对象包含两个嵌套的对象字段,它们负责管理对象的配置: 对象 spec(规约)和对象 status(状态) 。 对于具有 spec 的对象,你必须在创建对象时设置其内容,描述你希望对象所具有的特征: 期望状态(Desired State) 。

status 描述了对象的当前状态(Current State),它是由 Kubernetes 系统和组件设置并更新的。在任何时刻,Kubernetes 控制平面 都一直积极地管理着对象的实际状态,以使之与期望状态相匹配。

描述 Kubernetes 对象

创建 Kubernetes 对象时,必须提供对象的规约,用来描述该对象的期望状态, 以及关于对象的一些基本信息(例如名称)。 当使用 Kubernetes API 创建对象时(或者直接创建,或者基于kubectl), API 请求必须在请求体中包含 JSON 格式的信息。 大多数情况下,需要在 .yaml 文件中为 kubectl 提供这些信息。kubectl 在发起 API 请求时,会将这些信息转换成 JSON 格式。

必需字段

在想要创建的 Kubernetes 对象对应的 .yaml 文件中,需要配置如下的字段:

  • apiVersion - 创建该对象所使用的 Kubernetes API 的版本
  • kind - 想要创建的对象的类别
  • metadata - 帮助唯一性标识对象的一些数据,包括一个 name 字符串、UID 和可选的 namespace

Kubernetes 对象管理

命令式命令

用命令式命令时,用户可以在集群中的活动对象上进行操作。用户将操作传给 kubectl 命令作为参数或标志。

这是开始或者在集群中运行一次性任务的最简单方法。因为这个技术直接在活动对象上操作,所以它不提供以前配置的历史记录。

通过创建 Deployment 对象来运行 nginx 容器的实例:

kubectl run nginx --image nginx

命令式对象配置

在命令式对象配置中,kubectl 命令指定操作(创建,替换等),可选标志和至少一个文件名。指定的文件必须包含 YAML 或 JSON 格式的对象的完整定义。

创建配置文件中定义的对象:

kubectl create -f nginx.yaml

对象名称和 IDs

集群中的每一个对象都有一个名称来标识在同类资源中的唯一性。

每个 Kubernetes 对象也有一个 UID 来标识在整个集群中的唯一性。

比如,在同一个名字空间中有一个名为 myapp-1234 的 Pod, 但是可以命名一个 Pod 和一个 Deployment 同为 myapp-1234.

对于用户提供的非唯一性的属性,Kubernetes 提供了 标签(Labels)和 注解(Annotation)机制。

名字空间

Kubernetes 支持多个虚拟集群,它们底层依赖于同一个物理集群。 这些虚拟集群被称为名字空间。

何时使用多个名字空间

名字空间适用于存在很多跨多个团队或项目的用户的场景。对于只有几到几十个用户的集群,根本不需要创建或考虑名字空间。当需要名称空间提供的功能时,请开始使用它们。

名字空间为名称提供了一个范围。资源的名称需要在名字空间内是唯一的,但不能跨名字空间。 名字空间不能相互嵌套,每个 Kubernetes 资源只能在一个名字空间中。

名字空间是在多个用户之间划分集群资源的一种方法(通过资源配额)。

不需要使用多个名字空间来分隔轻微不同的资源,例如同一软件的不同版本: 使用标签来区分同一名字空间中的不同资源。

使用名字空间

名字空间的创建和删除在名字空间的管理指南文档描述。

说明: 避免使用前缀 kube- 创建名字空间,因为它是为 Kubernetes 系统名字空间保留的。

查看名字空间

你可以使用以下命令列出集群中现存的名字空间:

kubectl get namespace

Kubernetes 会创建四个初始名字空间:

  • default 没有指明使用其它名字空间的对象所使用的默认名字空间。
  • kube-system Kubernetes 系统创建对象所使用的名字空间。
  • kube-public 这个名字空间是自动创建的,所有用户(包括未经过身份验证的用户)都可以读取它。 这个名字空间主要用于集群使用,以防某些资源在整个集群中应该是可见和可读的。 这个名字空间的公共方面只是一种约定,而不是要求。
  • kube-node-lease 此名字空间用于与各个节点相关的租期(Lease)对象; 此对象的设计使得集群规模很大时节点心跳检测性能得到提升。
为请求设置名字空间

要为当前请求设置名字空间,请使用 --namespace 参数。

例如:

kubectl run nginx --image=nginx --namespace=<名字空间名称>
kubectl get pods --namespace=<名字空间名称>
设置名字空间偏好

你可以永久保存名字空间,以用于对应上下文中所有后续 kubectl 命令。

名字空间和 DNS

当你创建一个服务 时, Kubernetes 会创建一个相应的 DNS 条目。

该条目的形式是 <服务名称>.<名字空间名称>.svc.cluster.local,这意味着如果容器只使用 <服务名称>,它将被解析到本地名字空间的服务。这对于跨多个名字空间(如开发、分级和生产) 使用相同的配置非常有用。如果你希望跨名字空间访问,则需要使用完全限定域名(FQDN)。

并非所有对象都在名字空间中

大多数 kubernetes 资源(例如 Pod、Service、副本控制器等)都位于某些名字空间中。 但是名字空间资源本身并不在名字空间中。而且底层资源,例如 节点 和持久化卷不属于任何名字空间。

查看哪些 Kubernetes 资源在名字空间中,哪些不在名字空间中:

# 位于名字空间中的资源
kubectl api-resources --namespaced=true

# 不在名字空间中的资源
kubectl api-resources --namespaced=false

标签和选择算符

标签(Labels)是附加到 Kubernetes 对象(比如 Pods)上的键值对。 标签旨在用于指定对用户有意义且相关的对象的标识属性,但不直接对核心系统有语义含义。 标签可以用于组织和选择对象的子集。标签可以在创建时附加到对象,随后可以随时添加和修改。 每个对象都可以定义一组键/值标签。每个键对于给定对象必须是唯一的。

动机

标签使用户能够以松散耦合的方式将他们自己的组织结构映射到系统对象,而无需客户端存储这些映射。

服务部署和批处理流水线通常是多维实体(例如,多个分区或部署、多个发行序列、多个层,每层多个微服务)。 管理通常需要交叉操作,这打破了严格的层次表示的封装,特别是由基础设施而不是用户确定的严格的层次结构。

示例标签:

  • "release" : "stable", "release" : "canary"
  • "environment" : "dev", "environment" : "qa", "environment" : "production"
  • "tier" : "frontend", "tier" : "backend", "tier" : "cache"
  • "partition" : "customerA", "partition" : "customerB"
  • "track" : "daily", "track" : "weekly"

这些只是常用标签的例子; 你可以任意制定自己的约定。请记住,对于给定对象标签的键必须是唯一的。

语法和字符集

标签是键值对。有效的标签键有两个段:可选的前缀和名称,用斜杠(/)分隔。 名称段是必需的,必须小于等于 63 个字符,以字母数字字符([a-z0-9A-Z])开头和结尾, 带有破折号(-),下划线(_),点( .)和之间的字母数字。 前缀是可选的。如果指定,前缀必须是 DNS 子域:由点(.)分隔的一系列 DNS 标签,总共不超过 253 个字符, 后跟斜杠(/)。

如果省略前缀,则假定标签键对用户是私有的。 向最终用户对象添加标签的自动系统组件(例如 kube-scheduler、kube-controller-manager、 kube-apiserver、kubectl 或其他第三方自动化工具)必须指定前缀。

kubernetes.io/ 前缀是为 Kubernetes 核心组件保留的。

有效标签值必须为 63 个字符或更少,并且必须为空或以字母数字字符([a-z0-9A-Z])开头和结尾, 中间可以包含破折号(-)、下划线(_)、点(.)和字母或数字。

标签选择算符

与名称和 UID 不同, 标签不支持唯一性。通常,我们希望许多对象携带相同的标签。

通过 标签选择算符,客户端/用户可以识别一组对象。标签选择算符是 Kubernetes 中的核心分组原语。

API 目前支持两种类型的选择算符:基于等值的 和 基于集合的。 标签选择算符可以由逗号分隔的多个 需求 组成。 在多个需求的情况下,必须满足所有要求,因此逗号分隔符充当逻辑 与(&&)运算符。

空标签选择算符或者未指定的选择算符的语义取决于上下文, 支持使用选择算符的 API 类别应该将算符的合法性和含义用文档记录下来。

基于等值的需求

基于等值 或 基于不等值 的需求允许按标签键和值进行过滤。 匹配对象必须满足所有指定的标签约束,尽管它们也可能具有其他标签。 可接受的运算符有 = 、 == 和 != 三种。 前两个表示相等(并且只是同义词),而后者表示不相等。例如:

environment = production
tier != frontend
基于集合的需求

基于集合 的标签需求允许你通过一组值来过滤键。 支持三种操作符:in、notin 和 exists (只可以用在键标识符上)。例如:

environment in (production, qa)
tier notin (frontend, backend)
partition
!partition
  • 第一个示例选择了所有键等于 environment 并且值等于 production 或者 qa 的资源。
  • 第二个示例选择了所有键等于 tier 并且值不等于 frontend 或者 backend 的资源,以及所有没有 tier 键标签的资源。
  • 第三个示例选择了所有包含了有 partition 标签的资源;没有校验它的值。
  • 第四个示例选择了所有没有 partition 标签的资源;没有校验它的值。 类似地,逗号分隔符充当 与 运算符。因此,使用 partition 键(无论为何值)和 environment 不同于 qa 来过滤资源可以使用 partition, environment notin(qa) 来实现。

基于集合的标签选择算符是相等标签选择算符的一般形式,因为 environment=production 等同于 environment in(production);!= 和 notin 也是类似的。

基于集合的要求可以与基于相等的要求混合使用。例如:partition in (customerA, customerB),environment!=qa。

注解

你可以使用 Kubernetes 注解为对象附加任意的非标识的元数据。客户端程序(例如工具和库)能够获取这些元数据信息。

为对象附加元数据

你可以使用标签或注解将元数据附加到 Kubernetes 对象。 标签可以用来选择对象和查找满足某些条件的对象集合。 相反,注解不用于标识和选择对象。 注解中的元数据,可以很小,也可以很大,可以是结构化的,也可以是非结构化的,能够包含标签不允许的字符。

注解和标签一样,是键/值对:

"metadata": {
  "annotations": {
    "key1" : "value1",
    "key2" : "value2"
  }
}
语法和字符集

注解(Annotations)存储的形式是键/值对。有效的注解键分为两部分: 可选的前缀和名称,以斜杠(/)分隔。 名称段是必需项,并且必须在63个字符以内,以字母数字字符([a-z0-9A-Z])开头和结尾, 并允许使用破折号(-),下划线(_),点(.)和字母数字。 前缀是可选的。如果指定,则前缀必须是DNS子域:一系列由点(.)分隔的DNS标签, 总计不超过253个字符,后跟斜杠(/)。 如果省略前缀,则假定注解键对用户是私有的。 由系统组件添加的注解 (例如,kube-scheduler,kube-controller-manager,kube-apiserver,kubectl 或其他第三方组件),必须为终端用户添加注解前缀。

kubernetes.io/ 和 k8s.io/ 前缀是为 Kubernetes 核心组件保留的。

例如,下面是一个 Pod 的配置文件,其注解中包含 imageregistry: https://hub.docker.com/:

apiVersion: v1
kind: Pod
metadata:
  name: annotations-demo
  annotations:
    imageregistry: "https://hub.docker.com/"
spec:
  containers:
  - name: nginx
    image: nginx:1.7.9
    ports:
    - containerPort: 80

字段选择器

字段选择器(Field selectors)允许你根据一个或多个资源字段的值 筛选 Kubernetes 资源。 下面是一些使用字段选择器查询的例子:

  • metadata.name=my-service
  • metadata.namespace!=default
  • status.phase=Pending

下面这个 kubectl 命令将筛选出 status.phase 字段值为 Running 的所有 Pod:

kubectl get pods --field-selector status.phase=Running
支持的字段

你可在字段选择器中使用 =、==和 != (= 和 == 的意义是相同的)操作符。 例如,下面这个 kubectl 命令将筛选所有不属于 default 命名空间的 Kubernetes 服务:

kubectl get services  --all-namespaces --field-selector metadata.namespace!=default
链式选择器

同标签和其他选择器一样, 字段选择器可以通过使用逗号分隔的列表组成一个选择链。 下面这个 kubectl 命令将筛选 status.phase 字段不等于 Running 同时 spec.restartPolicy 字段等于 Always 的所有 Pod:

kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always
多种资源类型

你能够跨多种资源类型来使用字段选择器。 下面这个 kubectl 命令将筛选出所有不在 default 命名空间中的 StatefulSet 和 Service:

kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default

Kubernetes 架构

节点

Kubernetes 通过将容器放入在节点(Node)上运行的 Pod 中来执行你的工作负载。 节点可以是一个虚拟机或者物理机器,取决于所在的集群配置。 每个节点包含运行 Pods 所需的服务, 这些 Pods 由控制面负责管理。

通常集群中会有若干个节点;而在一个学习用或者资源受限的环境中,你的集群中也可能只有一个节点。

节点上的组件包括 kubelet、 容器运行时以及 kube-proxy。

管理

向 API 服务器添加节点的方式主要有两种:

  1. 节点上的 kubelet 向控制面执行自注册。
  2. 你,或者别的什么人,手动添加一个 Node 对象。

在你创建了 Node 对象或者节点上的 kubelet 执行了自注册操作之后, 控制面会检查新的 Node 对象是否合法。例如,如果你使用下面的 JSON 对象来创建 Node 对象:

{
  "kind": "Node",
  "apiVersion": "v1",
  "metadata": {
    "name": "10.240.79.157",
    "labels": {
      "name": "my-first-k8s-node"
    }
  }
}

Kubernetes 会在内部创建一个 Node 对象作为节点的表示。Kubernetes 检查 kubelet 向 API 服务器注册节点时使用的 metadata.name 字段是否匹配。 如果节点是健康的(即所有必要的服务都在运行中),则该节点可以用来运行 Pod。 否则,直到该节点变为健康之前,所有的集群活动都会忽略该节点。

控制面到节点通信

本文列举控制面节点(确切说是 API 服务器)和 Kubernetes 集群之间的通信路径。 目的是为了让用户能够自定义他们的安装,以实现对网络配置的加固,使得集群能够在不可信的网络上(或者在一个云服务商完全公开的 IP 上)运行。

节点到控制面

Kubernetes 采用的是中心辐射型(Hub-and-Spoke)API 模式。 所有从集群(或所运行的 Pods)发出的 API 调用都终止于 apiserver(其它控制面组件都没有被设计为可暴露远程服务)。 apiserver 被配置为在一个安全的 HTTPS 端口(443)上监听远程连接请求, 并启用一种或多种形式的客户端身份认证机制。 一种或多种客户端鉴权机制应该被启用, 特别是在允许使用匿名请求 或服务账号令牌的时候。

应该使用集群的公共根证书开通节点,这样它们就能够基于有效的客户端凭据安全地连接 apiserver。 一种好的方法是以客户端证书的形式将客户端凭据提供给 kubelet。 请查看 kubelet TLS 启动引导 以了解如何自动提供 kubelet 客户端证书。

想要连接到 apiserver 的 Pod 可以使用服务账号安全地进行连接。 当 Pod 被实例化时,Kubernetes 自动把公共根证书和一个有效的持有者令牌注入到 Pod 里。 kubernetes 服务(位于所有名字空间中)配置了一个虚拟 IP 地址,用于(通过 kube-proxy)转发 请求到 apiserver 的 HTTPS 末端。

控制面组件也通过安全端口与集群的 apiserver 通信。

这样,从集群节点和节点上运行的 Pod 到控制面的连接的缺省操作模式即是安全的, 能够在不可信的网络或公网上运行。

控制面到节点

从控制面(apiserver)到节点有两种主要的通信路径。 第一种是从 apiserver 到集群中每个节点上运行的 kubelet 进程。 第二种是从 apiserver 通过它的代理功能连接到任何节点、Pod 或者服务。

API 服务器到 kubelet

从 apiserver 到 kubelet 的连接用于:

  • 获取 Pod 日志
  • 挂接(通过 kubectl)到运行中的 Pod
  • 提供 kubelet 的端口转发功能。

这些连接终止于 kubelet 的 HTTPS 末端。 默认情况下,apiserver 不检查 kubelet 的服务证书。这使得此类连接容易受到中间人攻击, 在非受信网络或公开网络上运行也是 不安全的。

为了对这个连接进行认证,使用 –kubelet-certificate-authority 标志给 apiserver 提供一个根证书包,用于 kubelet 的服务证书。

如果无法实现这点,又要求避免在非受信网络或公共网络上进行连接,可在 apiserver 和 kubelet 之间使用 SSH 隧道。

最后,应该启用 kubelet 用户认证和/或鉴权 来保护 kubelet API。

apiserver 到节点、Pod 和服务

从 apiserver 到节点、Pod 或服务的连接默认为纯 HTTP 方式,因此既没有认证,也没有加密。 这些连接可通过给 API URL 中的节点、Pod 或服务名称添加前缀 https: 来运行在安全的 HTTPS 连接上。 不过这些连接既不会验证 HTTPS 末端提供的证书,也不会提供客户端证书。 因此,虽然连接是加密的,仍无法提供任何完整性保证。 这些连接目前还不能安全地在非受信网络或公共网络上运行。

工作负载

工作负载是在 Kubernetes 上运行的应用程序。

无论你的负载是单一组件还是由多个一同工作的组件构成,在 Kubernetes 中你可以在一组 Pods 中运行它。 在 Kubernetes 中,Pod 代表的是集群上处于运行状态的一组容器。

Kubernetes Pods 有确定的生命周期。 例如,一旦某 Pod 在你的集群中运行,Pod 运行所在的节点出现致命错误时, 所有该节点上的 Pods 都会失败。Kubernetes 将这类失败视为最终状态:即使该节点后来恢复正常运行,你也需要创建新的 Pod 来恢复应用。

不过,为了让用户的日子略微好过一些,你并不需要直接管理每个 Pod。 相反,你可以使用负载资源来替你管理一组 Pods。 这些资源配置控制器来确保合适类型的、处于运行状态的 Pod 个数是正确的,与你所指定的状态相一致。

Kubernetes 提供若干种内置的工作负载资源:

  • Deployment 和 ReplicaSet (替换原来的资源 ReplicationController)。 Deployment 很适合用来管理你的集群上的无状态应用,Deployment 中的所有 Pod 都是相互等价的,并且在需要的时候被换掉。
  • StatefulSet 让你能够运行一个或者多个以某种方式跟踪应用状态的 Pods。 例如,如果你的负载会将数据作持久存储,你可以运行一个 StatefulSet,将每个 Pod 与某个 PersistentVolume 对应起来。你在 StatefulSet 中各个 Pod 内运行的代码可以将数据复制到同一 StatefulSet 中的其它 Pod 中以提高整体的服务可靠性。
  • DaemonSet 定义提供节点本地支撑设施的 Pods。这些 Pods 可能对于你的集群的运维是非常重要的,例如作为网络链接的辅助工具或者作为网络插件的一部分等等。每次你向集群中添加一个新节点时,如果该节点与某 DaemonSet 的规约匹配,则控制面会为该 DaemonSet 调度一个 Pod 到该新节点上运行。
  • Job 和 CronJob。 定义一些一直运行到结束并停止的任务。Job 用来表达的是一次性的任务,而 CronJob 会根据其时间规划反复运行。

pods

Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。

Pod(就像在鲸鱼荚或者豌豆荚中)是一组(一个或多个) 容器; 这些容器共享存储、网络、以及怎样运行这些容器的声明。 Pod 中的内容总是并置(colocated)的并且一同调度,在共享的上下文中运行。 Pod 所建模的是特定于应用的“逻辑主机”,其中包含一个或多个应用容器, 这些容器是相对紧密的耦合在一起的。 在非云环境中,在相同的物理机或虚拟机上运行的应用类似于在同一逻辑主机上运行的云应用。

除了应用容器,Pod 还可以包含在 Pod 启动期间运行的 Init 容器。 你也可以在集群中支持临时性容器 的情况外,为调试的目的注入临时性容器。

什么是 Pod?

说明: 除了 Docker 之外,Kubernetes 支持很多其他容器运行时(容器运行时是负责运行容器的软件), Docker 是最有名的运行时, 使用 Docker 的术语来描述 Pod 会很有帮助。

Pod 的共享上下文包括一组 Linux 名字空间、控制组(cgroup)和可能一些其他的隔离方面,即用来隔离 Docker 容器的技术。 在 Pod 的上下文中,每个独立的应用可能会进一步实施隔离。

就 Docker 概念的术语而言,Pod 类似于共享名字空间和文件系统卷的一组 Docker 容器。

使用 Pod

通常你不需要直接创建 Pod,甚至单实例 Pod。 相反,你会使用诸如 Deployment 或 Job 这类工作负载资源来创建 Pod。如果 Pod 需要跟踪状态, 可以考虑 StatefulSet 资源。

Kubernetes 集群中的 Pod 主要有两种用法:

  • 运行单个容器的 Pod。“每个 Pod 一个容器” 模型是最常见的 Kubernetes 用例; 在这种情况下,可以将 Pod 看作单个容器的包装器,并且 Kubernetes 直接管理 Pod,而不是容器。
  • 运行多个协同工作的容器的 Pod。 Pod 可能封装由多个紧密耦合且需要共享资源的共处容器组成的应用程序。 这些位于同一位置的容器可能形成单个内聚的服务单元 —— 一个容器将文件从共享卷提供给公众, 而另一个单独的 “挂斗”(sidecar)容器则刷新或更新这些文件。 Pod 将这些容器和存储资源打包为一个可管理的实体。

说明: 将多个并置、同管的容器组织到一个 Pod 中是一种相对高级的使用场景。 只有在一些场景中,容器之间紧密关联时你才应该使用这种模式。

每个 Pod 都旨在运行给定应用程序的单个实例。如果希望横向扩展应用程序(例如,运行多个实例以提供更多的资源),则应该使用多个 Pod,每个实例使用一个 Pod。 在 Kubernetes 中,这通常被称为副本(Replication)。 通常使用一种工作负载资源及其控制器来创建和管理一组 Pod 副本。

工作负载资源

工作负载是指在 Kubernetes 上运行的应用程序,一个 pod 可以运行一个或多个工作负载。工作负载资源是用来管理一组 pod 的,确保这些被管理的 pod ,处于我们期望的运行状态。

Deployments

一个 Deployment 为 Pods (pod 表示集群上正在运行的一组容器) 和 ReplicaSets (下一代副本控制器)提供声明式的更新能力。

你负责描述 Deployment 中的目标状态,而 Deployment 控制器(Controller)以受控速率更改实际状态, 使其变为期望状态。你可以定义 Deployment 以创建新的 ReplicaSet,或删除现有 Deployment, 并通过新的 Deployment 收养其资源。

ReplicaSet

ReplicaSet 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合。 因此,它通常用来保证给定数量的、完全相同的 Pod 的可用性。

StatefulSets

StatefulSet 是用来管理有状态应用的工作负载 API 对象。

StatefulSet 用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。

和 Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。

DaemonSet

DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。

DaemonSet 的一些典型用法:

  • 在每个节点上运行集群守护进程
  • 在每个节点上运行日志收集守护进程
  • 在每个节点上运行监控守护进程

一种简单的用法是为每种类型的守护进程在所有的节点上都启动一个 DaemonSet。 一个稍微复杂的用法是为同一种守护进程部署多个 DaemonSet;每个具有不同的标志, 并且对不同硬件类型具有不同的内存、CPU 要求。

Jobs

Job 会创建一个或者多个 Pods,并将继续重试 Pods 的执行,直到指定数量的 Pods 成功终止。 随着 Pods 成功结束,Job 跟踪记录成功完成的 Pods 个数。 当数量达到指定的成功个数阈值时,任务(即 Job)结束。 删除 Job 的操作会清除所创建的全部 Pods。

一种简单的使用场景下,你会创建一个 Job 对象以便以一种可靠的方式运行某 Pod 直到完成。 当第一个 Pod 失败或者被删除(比如因为节点硬件失效或者重启)时,Job 对象会启动一个新的 Pod。

CronJob

CronJob 创建基于时间调度的 Jobs。

一个 CronJob 对象就像 crontab (cron table) 文件中的一行。 它用 Cron 格式进行编写, 并周期性地在给定的调度时间执行 Job。

ReplicationController

ReplicationController 确保在任何时候都有特定数量的 Pod 副本处于运行状态。 换句话说,ReplicationController 确保一个 Pod 或一组同类的 Pod 总是可用的

ReplicationController 的替代方案

ReplicaSet
ReplicaSet 是下一代 ReplicationController, 支持新的基于集合的标签选择算符。 它主要被 Deployment 用来作为一种编排 Pod 创建、删除及更新的机制。 请注意,我们推荐使用 Deployment 而不是直接使用 ReplicaSet,除非 你需要自定义更新编排或根本不需要更新。

Deployment (推荐)

Deployment 是一种更高级别的 API 对象, 它以类似于 kubectl rolling-update 的方式更新其底层 ReplicaSet 及其 Pod。 如果你想要这种滚动更新功能,那么推荐使用 Deployment,因为与 kubectl rolling-update 不同, 它们是声明式的、服务端的,并且具有其它特性。

部署 Kubernetes 集群

Kubernetes 官方提供多种部署方式:

  • 云解决方案
  • 使用部署工具安装 Kubernetes
  • Windows Kubernetes

我们这里使用部署工具来安装 Kubernetes,官方提供的工具主要有以下几个:

  • Kubeadm:官方维护的为了给创建 Kubernetes 集群提供最佳实践的一个工具,涉及集群生命周期管理等知识。

  • Kops :在 AWS 上安装 Kubernetes 集群。

  • Kubespray:Ansible 部署,OS 级别通用的部署方式,可以是裸机和云的环境。

这里我们使用 Kubeadm 部署,这是官方推荐的部署方式,Kubeadm 可用于生产级别的集群部署。

准备开始

  • 一台或多台运行着下列系统的机器:

    • Ubuntu 16.04+
    • Debian 9+
    • CentOS 7+
    • Red Hat Enterprise Linux (RHEL) 7+
    • Fedora 25+
    • HypriotOS v1.0.1+
    • Flatcar Container Linux (使用 2512.3.0 版本测试通过)
  • 每台机器 2 GB 或更多的 RAM (如果少于这个数字将会影响你应用的运行内存)

  • 2 CPU 核或更多

  • 集群中的所有机器的网络彼此均能相互连接(公网和内网都可以)

  • 节点之中不可以有重复的主机名、MAC 地址或 product_uuid。

  • 开启机器上的某些端口(Kubernetes 服务所占用的端口必须开启)

  • 禁用交换分区。为了保证 kubelet 正常工作,你必须禁用交换分区。

环境配置

节点准备

k8s-master 10.10.110.190
k8s-ndoe1  10.10.110.191
k8s-node2  10.10.110.192
Operating System: Ubuntu 18.04.5 LTS

结构图

配置节点

# 关闭防火墙
systemctl stop ufw
ufw disable

# 检查所有节点网络接口的mac地址和product_uuid唯一性
ip link
cat /sys/class/dmi/id/product_uuid

# 禁用swap分区
swapoff -a

# 设置主机名
hostnamectl set-hostname [hostname]

# 配置hosts解析
cat >> /etc/hosts << EOF
10.10.110.190 k8s-master
10.10.110.191 k8s-node1
10.10.110.192 k8s-node2
EOF

# 允许iptables检查桥接流量
    modprobe br_netfilter
lsmod | grep br_netfilter

cat </dev/null" | crontab

安装 Container Runtime

为了在 Pod 中运行容器,Kubernetes 需要使用容器运行时。

# 卸载旧版本
apt-get remove docker docker-engine docker.io containerd runc

# 更新apt包索引和安装包
apt-get update
apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

# 添加Docker的官方GPG密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# 设置稳定存储库
echo \
    "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装Docker引擎
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io

# 配置docker镜像加速,kubernetes官方建议docker驱动采用systemd,如果不修改kubeadm init时会有warning提示
cat <

安装 kubeadm、kubelet 和 kubectl

你需要在每台机器上安装以下的软件包:

  • kubeadm:用来初始化集群的指令。
  • kubelet:在集群中的每个节点上用来启动 Pod 和容器等。
  • kubectl:用来与集群通信的命令行工具。

kubeadm 不能帮你安装或者管理 kubelet 或 kubectl,所以你需要确保它们与通过 kubeadm 安装的控制平面的版本相匹配。

apt-get update && apt-get install -y apt-transport-https curl

curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -

cat <

初始化控制平面节点

控制平面节点是运行控制平面组件的机器, 包括 etcd (集群数据库) 和 API Server(命令行工具 kubectl 与之通信)。

在所有 master 节点执行

kubeadm init \
  --apiserver-advertise-address=10.10.110.190 \
  --image-repository registry.aliyuncs.com/google_containers \
  --kubernetes-version v1.20.4 \
  --service-cidr=10.96.0.0/12 \
  --pod-network-cidr=10.244.0.0/16 \
  --ignore-preflight-errors=all
  • –apiserver-advertise-address 服务器所公布的其正在监听的 IP 地址
  • –image-repository 默认拉取镜像地址为 k8s.gcr.io(国内网络无法拉取),这里指定阿里云镜像仓库地址
  • –kubernetes-version 指定 k8s 安装版本
  • –service-cidr 集群内部虚拟网络,pod 统一访问入口
  • –pod-network-cidr 指明 pod 网络可以使用的 IP 地址段

拷贝 kubectl 连接 k8s 所使用的认证文件到当前用户的默认路径

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

安装 Pod 网络附加组件

你必须部署一个基于 Pod 网络插件的容器网络接口 (CNI),以便你的 Pod 可以相互通信。 在安装网络之前,集群 DNS (CoreDNS) 将不会启动。这也是为什么 node 的状态其实是 NotReady 的原因。Kubernetes 常用的网络插件包括 Calico、Flannel、Canal 和 Weave,这里我们使用 Calico 来为 Kubernetes 集群提供网络策略支持

你可以使用以下命令在控制平面节点或具有 kubeconfig 凭据的节点上安装 Pod 网络附加组件:

# 下载 calico 官方配置文件(国内网络可能会下载失败)
wget https://docs.projectcalico.org/manifests/calico.yaml

# 修改 calico 配置文件
- name: CALICO_IPV4POOL_CIDR
  value: "10.244.0.0/16"    # 修改为kubeadm init时指定的--pod-network-cidr网段

# 应用配置文件
kubectl apply -f calico.yaml

# 查看 pods 运行状态
kubectl get pods -n kube-system

加入节点

节点是你的工作负载(容器和 Pod 等)运行的地方。要将新节点添加到集群,请对每台计算机执行以下操作:

  • SSH 到机器
  • 成为 root (例如 sudo su -
  • 运行 kubeadm init 输出的命令。例如:
kubeadm join 10.10.110.190:6443 --token 54sx6k.gi533yr3f4yimvky \
    --discovery-token-ca-cert-hash sha256:3f16fb0f5c1ed611af164b8f5df6891ee60bba760286b860d125d08a304ed4b0

执行 kubeadm init 之后,默认生成的 token 有效期为 24 小时,过期之后就需要重新创建 token,操作如下:

# 列出token列表
kubeadm token list

# 创建token
kubeadm token create
bvw33z.dd7p7h2t151vc6ej  # 这里是新生成的token

# 获取CA证书公钥哈希值
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
3f16fb0f5c1ed611af164b8f5df6891ee60bba760286b860d125d08a304ed4b0

# 使用新的token和公钥哈希值加入节点
kubeadm join 10.10.110.190:6443 --token $(新生成的token) \
    --discovery-token-ca-cert-hash sha256:$(新生成的公钥哈希值)

部署 Dashboard

Dashboard 是基于网页的 Kubernetes 用户界面。 你可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中,也可以对容器应用排错,还能管理集群资源。 你可以使用 Dashboard 获取运行在集群中的应用的概览信息,也可以创建或者修改 Kubernetes 资源 (如 Deployment,Job,DaemonSet 等等)。 例如,你可以对 Deployment 实现弹性伸缩、发起滚动升级、重启 Pod 或者使用向导创建新的应用。

默认情况下不会部署 Dashboard。可以通过以下命令部署:

# 下载dashboard配置清单文件
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml

# dashboard默认的service是ClusterIP类型,我们需要修改为NodePort类型,才能让外部访问到我们的dashboard
---
kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  ports:
    - port: 443
      targetPort: 8443
      nodePort: 30023
  selector:
    k8s-app: kubernetes-dashboard
  type: NodePort
---

# 访问地址
https://nodeip:30023

访问 Dashboard 管理页面所需的 token,可以通过以下命令创建:

# 我们首先在kubernetes-dashboard命名空间中创建名为admin-user的service account
kubectl create serviceaccount admin-user -n kubernetes-dashboard
kubectl get serviceaccounts -n kubernetes-dashboard

# 创建集群角色绑定,给admin-user用户授权
kubectl create clusterrolebinding admin-user --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:admin-user

# 获取kubernetes-dashboard命名空间下admin-user用户的登录token
kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}')

设置 k8s 命令自动补全

apt-get install -y bash-completion
locate bash_completion
source /usr/share/bash-completion/bash_completion
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc

Kubelet 概述

我们可以使用 Kubectl 命令行工具管理 Kubernetes 集群。默认情况下,kubectl$HOME/.kube 目录下查找名为 config 的文件。 你可以通过设置 KUBECONFIG 环境变量或者设置 --kubeconfig参数来指定其他 kubeconfig 文件。使用 kubeconfig 文件来组织有关集群、用户、命名空间和身份认证机制的信息。kubectl 命令行工具使用 kubeconfig 文件来查找选择集群所需的信息,并与集群的 API 服务器进行通信。

说明:用于配置集群访问的文件称为 kubeconfig 文件。这是引用配置文件的通用方法。这并不意味着有一个名为 kubeconfig 的文件。

支持多集群、用户和身份认证机制

假设您有多个集群,并且您的用户和组件以多种方式进行身份认证。比如:

  • 正在运行的 kubelet 可能使用证书在进行认证。
  • 用户可能通过令牌进行认证。
  • 管理员可能拥有多个证书集合提供给各用户。

使用 kubeconfig 文件,您可以组织集群、用户和命名空间。您还可以定义上下文,以便在集群和命名空间之间快速轻松地切换。

上下文(Context)

通过 kubeconfig 文件中的 context 元素,使用简便的名称来对访问参数进行分组。每个上下文都有三个参数:cluster、namespace 和 user。默认情况下,kubectl 命令行工具使用当前上下文中的参数与集群进行通信。

选择当前上下文

kubectl config use-context

KUBECONFIG 环境变量

KUBECONFIG 环境变量包含一个 kubeconfig 文件列表。 对于 Linux 和 Mac,列表以冒号分隔。对于 Windows,列表以分号分隔。 KUBECONFIG 环境变量不是必要的。 如果 KUBECONFIG 环境变量不存在,kubectl 使用默认的 kubeconfig 文件,$HOME/.kube/config

如果 KUBECONFIG 环境变量存在,kubectl 使用 KUBECONFIG 环境变量中列举的文件合并后的有效配置。

语法

使用以下语法 kubectl 从终端窗口运行命令:

kubectl [command] [TYPE] [NAME] [flags]
  • command:指定要对一个或多个资源执行的操作,例如 creategetdescribedelete
  • TYPE:指定资源类型。资源类型不区分大小写, 可以指定单数、复数或缩写形式。
  • NAME:指定资源的名称。名称区分大小写。 如果省略名称,则显示所有资源的详细信息 kubectl get pods
  • flags: 指定可选的参数。

Kubectl –help

基本指令(初级)

  • create:从文件或标准输入创建资源。
  • expose:将资源作为新的 Kubernetes 服务公开。
  • run:在集群上运行指定的镜像。
  • set:设置对象的特定功能。

基本指令(中级)

  • explain:解释文档参考资料。
  • get:显示一个或多个资源。
  • edit:编辑服务器上的资源。
  • delete:通过文件名、标准输入、资源和名称或通过资源和标签选择器删除资源。

部署命令

  • rollout:管理资源的发布。
  • scale:对 deployment、ReplicaSet、Replication Controller 或 StatefulSet 扩容缩容。
  • autoscale:自动扩容缩容。

集群管理命令

  • certificate:修改证书资源。
  • cluster-info:显示集群信息。
  • top:显示资源(CPU/内存/存储)使用情况。
  • cordon:将节点标记为不可调度。
  • uncordon:将节点标记为可调度的。
  • drain:更新一个或多个节点上的污点。
  • taint:驱逐节点上的应用准备下线维护。

疑难解答和调试命令

  • describe:描述显示特定资源或资源组的详细信息。
  • logs:打印一个容器的日志。
  • attach:连接到正在运行的容器上。
  • exec:在容器中执行命令。
  • port-forward:转发一个或多个本地端口到一个 pod。
  • proxy:运行到 Kubernetes API 服务器的代理。
  • cp:在容器和容器之间复制文件和目录。
  • auth:授权检查。
  • debug:使用交互式调试容器调试群集资源。

高级命令

  • diff:由文件名或标准输入指定当前联机配置和应用时的配置差异。
  • apply:通过文件名或标准输入对资源应用配置。
  • patch:使用补丁更新资源的字段。
  • replace:用文件名或标准输入替换资源。
  • wait:等待一个或多个资源的特定条件。
  • kustomize:从 kustomization.yaml 文件中的指令生成一组 API 资源。

设置命令

  • label:更新资源上的标签。
  • annotate:更新一个或多个资源上的注解。
  • completion:kubectl 命令自动补全。

其他命令

  • api-resources:打印受支持的 API 资源。
  • api-versions:打印受支持的 API 版本。
  • config:修改 Kubeconfig 文件。
  • plugin:提供与插件交互的实用程序。
  • version:打印当前上下文客户端和服务版本信息。

Kubectl 管理应用生命周期

创建

kubectl create deployment myapp-deployment --image=ikubernetes/myapp:v1 --replicas=3

发布

kubectl expose deployment myapp-deployment --name=myapp-service --type=NodePort --port=8000 --target-port=80 --protocol=TCP

升级

kubectl set image deployment/myapp-deployment myapp=ikubernetes/myapp:v2
kubectl rollout status deployment myapp-deployment # 查看升级状态

回滚

kubectl rollout history deployment myapp-deployment # 查看版本发布历史记录
kubectl rollout history deployment myapp-deployment --revision=3 # 查看指定版本发布的详细信息
kubectl rollout undo deployment myapp-deployment # 回滚到上一个版本
kubectl rollout undo deployment myapp-deployment --to-revision=2 # 回滚到指定的版本

删除

kubectl delete deployments.apps myapp-deployment
kubectl delete service myapp-service

对象类资源格式

Kubernetes API 仅支持接受及响应 JSON 格式的数据(JSON 对象),同时,为了便于使用,它也允许用户提供 YAML 格式的 POST 对象,但 API Server 接受和返回的所有 JSON 对象都遵循同一个模式,它们都具有 kind 和 apiVersion 字段,用于表示对象所属的资源类型、API 群组及相关版本。

大多数的对象或列表类型的资源还需要具有三个嵌套型的字段 metadata、spec、status。其中 metadata 字段为资源提供元数据信息,如名称、资源隶属的名称空间和标签等。spec 则用于定义用户期望的状态,不同的资源类型,其状态的意义也各不相同。status 则记录着活动对象的当前状态信息,它由 Kubernetes 系统自行维护,对用户来说为只读状态。

Kubectl 的命令可以分为三类:陈述式命令(Imperative commands)、陈述式对象配置(Imperative object configuration)和声明式对象配置(Declarative object configuration)。

陈述式命令就是此前管理应用生命周期用到的 run、expose 和 delete 等命令,它们直接作用于 Kubernetes 系统上的活动对象,简单易用,但是不支持代码复用、修改复审及审计日志的功能。对于新手来说,更容易上手学习。

陈述式对象配置管理方式支持使用 create、delete、get 和 replace 等命令,与陈述式命令不同之处在于,它通过资源配置清单读取需要管理的目标资源对象。陈述式对象配置管理操作同样直接作用于 Kubernetes 系统上的活动对象,即便修改配置清单中极小的一部分内容,使用 replace 命令进行的对象更新也会导致整个对象被替换。

声明式对象配置并不直接指明要进行的对象管理操作,而是提供配置清单文件给 Kubernetes 系统,并委托系统跟踪活动对象的状态变动。资源对象的创建、删除及修改操作可全部通过唯一的 apply 命令来完成。并且每次操作时,提供给命令的配置信息都存放于对象的注解信息中,并通过比对检查活动对象的当前状态、注解中的配置信息及资源清单中的配置信息三方进行变更合并,从而实现仅修改变动字段的高级补丁机制。

资源配置清单

我们前面使用 Kubectl 管理应用生命周期,使用的是陈述式命令。下面我们将以资源配置清单的格式来创建活动对象。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ikubernetes-deployment
  labels:
    app: ikubernetes-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ikubernetes
  template:
    metadata:
      labels:
        app: ikubernetes
    spec:
      containers:
      - name: ikubernetes
        image: ikubernetes/myapp:v1
        ports:
        - containerPort: 80 

在该活动对象实例中:

  • 创建名为 ikubernetes-deployment (由 .metadata.name 字段标明)的 Deployment

  • 该 Deployment 创建三个(由 replicas 字段标明) Pod 副本

  • selector 字段定义 Deployment 如何查找要管理的 Pods。这里只需选择在 Pod 模板中定义的标签(app: ikubernetes)

  • template 字段包含以下子字段:

    • Pod 被使用 labels 字段打上 app: ikubernetes 标签
    • Pod 模板规约 (即 .template.spec 字段)指示 Pods 运行一个 ikubernetes 容器,并指定容器运行版本的镜像

    Kubernetes API 标准的资源组织格式由五个核心字段组成:

  • apiVersion:定义这个资源使用的 API 版本

  • kind:定义这个资源的类型

  • metadata:资源的元数据

  • spec:资源的规约,描述所期望的对象应有的状态

  • status:记录对象在系统上的当前状态

在编写资源配置清单时如果对资源的字段不确定,可以使用 Kubernetes 内置的 explain 命令列出受支持资源的字段:

# 获取资源及其字段的文档
kubectl explain pods

# 获取资源的特定字段
kubectl explain pods.spec.containers

工作负载资源使用

Deployment

一个 Deployment 为 PodsReplicaSets 提供声明式的更新能力。

下面是 Deployment 示例。其中创建了一个 ReplicaSet,负责启动三个 nginx Pods:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

开始之前,请确保的 Kubernetes 集群已启动并运行。 按照以下步骤创建上述 Deployment :

通过运行以下命令创建 Deployment :

kubectl apply -f nginx-deployment.yaml

运行 kubectl get deployments.apps -o wide 检查 Deployment 是否已创建:

NAME                     READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS    IMAGES                 SELECTOR
nginx-deployment         3/3     3            3           34s   nginx         nginx:1.14.2           app=nginx

在检查集群中的 Deployment 时,所显示的字段有:

  • NAME 列出了集群中 Deployment 的名称。
  • READY 显示应用程序的可用的副本数,显示的模式是“就绪个数/期望个数”。
  • UP-TO-DATE 显示为了达到期望状态已经更新的副本数。
  • AVAILABLE 显示应用可供用户使用的副本数。
  • AGE 显示应用程序运行的时间。

请注意期望副本数是根据 .spec.replicas 字段设置 3。

运行 kubectl rollout status deployment nginx-deployment 查看 Deployment 上线状态:

deployment "nginx-deployment" successfully rolled out

要查看 Deployment 创建的 ReplicaSet(rs),运行 kubectl get rs。 输出类似于:

NAME                                DESIRED   CURRENT   READY   AGE
nginx-deployment-66b6c48dd5         3         3         3       5m5s

要查看每个资源自动生成的标签,运行 kubectl get pods --show-labels。返回以下输出:

NAME                                      READY   STATUS    RESTARTS   AGE     LABELS
nginx-deployment-66b6c48dd5-5jd4w         1/1     Running   0          7m20s   app=nginx,pod-template-hash=66b6c48dd5
nginx-deployment-66b6c48dd5-js2bx         1/1     Running   0          7m20s   app=nginx,pod-template-hash=66b6c48dd5
nginx-deployment-66b6c48dd5-mqsnj         1/1     Running   0          7m20s   app=nginx,pod-template-hash=66b6c48dd5

所创建的 ReplicaSet 确保总是存在三个 nginx Pod。

ReplicaSet

ReplicaSet 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合。 因此,它通常用来保证给定数量的、完全相同的 Pod 的可用性。

ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行。 然而,Deployment 是一个更高级的概念,它管理 ReplicaSet,并向 Pod 提供声明式的更新以及许多其他有用的功能。 因此,我们建议使用 Deployment 而不是直接使用 ReplicaSet ,除非你需要自定义更新业务流程或根本不需要更新。

这实际上意味着,你可能永远不需要操作 ReplicaSet 对象:而是使用 Deployment,并在 spec 部分定义 ReplicaSet 管理你的应用。

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      ti: fr
  template:
    metadata:
      labels:
        ti: fr
    spec:
      containers:
      - name: nginx
        image: nginx

你可以看到当前被部署的 ReplicaSet:

kubectl get rs nginx

并看到你所创建的前端:

NAME    DESIRED   CURRENT   READY   AGE
nginx   3         3         2       11s

你也可以查看 ReplicaSet 的状态:

kubectl describe rs nginx

你会看到类似如下的输出:

Name:         nginx
Namespace:    default
Selector:     ti=fr
Labels:       app=nginx
Annotations:  
Replicas:     3 current / 3 desired
Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  ti=fr
  Containers:
   nginx:
    Image:        nginx
    Port:         
    Host Port:    
    Environment:  
    Mounts:       
  Volumes:        
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  113s  replicaset-controller  Created pod: nginx-7hxhm
  Normal  SuccessfulCreate  113s  replicaset-controller  Created pod: nginx-mnq9v
  Normal  SuccessfulCreate  113s  replicaset-controller  Created pod: nginx-w9cfq

最后可以查看启动了的 Pods:

kubectl get pods

你会看到类似如下的 Pod 信息:

NAME          READY   STATUS    RESTARTS   AGE
nginx-7hxhm   1/1     Running   0          3m2s
nginx-mnq9v   1/1     Running   0          3m2s
nginx-w9cfq   1/1     Running   0          3m2s

StatefulSets

DaemonSet

DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。

你可以在 YAML 文件中描述 DaemonSet。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: default
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
      nodeSelector:
        type: ssd

如果指定了 .spec.template.spec.nodeSelector,DaemonSet 控制器将在能够与 Node 选择算符匹配的节点上创建 Pod。类似这种情况,可以指定 .spec.template.spec.affinity,之后 DaemonSet 控制器将在能够与节点亲和性匹配的节点上创建 Pod。 如果根本就没有指定,则 DaemonSet Controller 将在所有节点上创建 Pod。

Jobs

下面是一个 Job 配置示例。它负责计算 π 到小数点后 2000 位,并将结果打印出来。 此计算大约需要 10 秒钟完成。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

要查看 Job 对应的已完成的 Pods,可以执行 kubectl get pods

要以机器可读的方式列举隶属于某 Job 的全部 Pods,你可以使用类似下面这条命令:

pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods

输出类似于:

pi-ntb4l

这里,选择算符与 Job 的选择算符相同。--output=jsonpath 选项给出了一个表达式, 用来从返回的列表中提取每个 Pod 的 name 字段。

查看其中一个 Pod 的标准输出:

kubectl logs $pods

类似于:

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901

Service 资源

创建和销毁 Kubernetes Pod 以匹配集群状态。 Pod 是非永久性资源。 如果你使用 Deployment 来运行你的应用程序,则它可以动态创建和销毁 Pod。

每个 Pod 都有自己的 IP 地址,但是在 Deployment 中,在同一时刻运行的 Pod 集合可能与稍后运行该应用程序的 Pod 集合不同。

这导致了一个问题: 如果一组 Pod(称为“后端”)为集群内的其他 Pod(称为“前端”)提供功能, 那么前端如何找出并跟踪要连接的 IP 地址,以便前端可以使用提供工作负载的后端部分?

于是就有了 Services。

定义 Service

例如,假定有一组 Pod,它们对外暴露了 6379 端口,同时还被打上 app=redis 标签:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-deployment
  labels:
    app: redis-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis
        ports:
        - containerPort: 6379

我们创建名称为 “my-service” 的 Service 对象,它会将请求代理到使用 TCP 端口 6379,并且具有标签 app=redis 的 Pod 上:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: redis
  ports:
    - protocol: TCP
      port: 80
      targetPort: 6379

通过执行 kubectl get endpoints my-service 命令可以看到 Service 后端所代理的 Pod:

NAME         ENDPOINTS                                                   AGE
my-service   10.244.169.179:6379,10.244.169.180:6379,10.244.36.82:6379   6m3s

Service 虚拟 IP 和服务代理

在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。 kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。简单来讲,一个 Service 对象就是工作节点上的一些 iptables 或 ipvs 规则,用于将到达 Service 对象 IP 地址的流量调度转发至相应的 Endpoint 对象指向的 IP 地址和端口之上。

userspace 代理模型

这种模式,kube-proxy 会监视 Kubernetes 控制平面对 Service 对象和 Endpoints 对象的添加和移除操作。 对每个 Service,它会在本地 Node 上打开一个端口(随机选择)。 任何连接到“代理端口”的请求,都会被代理到 Service 的后端 Pods 中的某个上面(如 Endpoints 所报告的一样)。 使用哪个后端 Pod,是 kube-proxy 基于 SessionAffinity 来确定的。

最后,它配置 iptables 规则,捕获到达该 Service 的 clusterIP(是虚拟 IP) 和 Port 的请求,并重定向到代理端口,代理端口再代理请求到后端Pod。

默认情况下,用户空间模式下的 kube-proxy 通过轮转算法选择后端。

iptables 代理模型

这种模式,kube-proxy 会监视 Kubernetes 控制节点对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会配置 iptables 规则,从而捕获到达该 Service 的 clusterIP 和端口的请求,进而将请求重定向到 Service 的一组后端中的某个 Pod 上面。 对于每个 Endpoints 对象,它也会配置 iptables 规则,这个规则会选择一个后端组合。

默认的策略是,kube-proxy 在 iptables 模式下随机选择一个后端。

使用 iptables 处理流量具有较低的系统开销,因为流量由 Linux netfilter 处理, 而无需在用户空间和内核空间之间切换。 这种方法也可能更可靠。

如果 kube-proxy 在 iptables 模式下运行,并且所选的第一个 Pod 没有响应, 则连接失败。 这与用户空间模式不同:在这种情况下,kube-proxy 将检测到与第一个 Pod 的连接已失败, 并会自动使用其他后端 Pod 重试。

你可以使用 Pod 就绪探测器 验证后端 Pod 可以正常工作,以便 iptables 模式下的 kube-proxy 仅看到测试正常的后端。 这样做意味着你避免将流量通过 kube-proxy 发送到已知已失败的 Pod。

ipvs 代理模型

ipvs 模式下,kube-proxy 监视 Kubernetes 服务和端点,调用 netlink 接口相应地创建 IPVS 规则, 并定期将 IPVS 规则与 Kubernetes 服务和端点同步。 该控制循环可确保IPVS 状态与所需状态匹配。访问服务时,IPVS 将流量定向到后端 Pod 之一。

IPVS代理模式基于类似于 iptables 模式的 netfilter 挂钩函数, 但是使用哈希表作为基础数据结构,并且在内核空间中工作。 这意味着,与 iptables 模式下的 kube-proxy 相比,IPVS 模式下的 kube-proxy 重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。 与其他代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。

IPVS 提供了更多选项来平衡后端 Pod 的流量。 这些是:

  • rr:轮替(Round-Robin)
  • lc:最少链接(Least Connection),即打开链接数量最少者优先
  • dh:目标地址哈希(Destination Hashing)
  • sh:源地址哈希(Source Hashing)
  • sed:最短预期延迟(Shortest Expected Delay)
  • nq:从不排队(Never Queue)

Service 类型

对于一些应用的某些部分(如前端),可能希望将其暴露给 Kubernetes 集群外部的 IP 地址。

Kubernetes ServiceTypes 允许指定你所需要的 Service 类型,默认是 ClusterIP

Type 的取值以及行为如下:

  • ClusterIP:通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。 这也是默认的 ServiceType
  • NodePort:通过每个节点上的 IP 和静态端口(NodePort)暴露服务。 NodePort 服务会路由到自动创建的 ClusterIP 服务。 通过请求 <节点 IP>:<节点端口>,你可以从集群的外部访问一个 NodePort 服务。
  • LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。
  • ExternalName:通过返回 CNAME 和对应值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。 无需创建任何类型代理。

你也可以使用 Ingress 来暴露自己的服务。 Ingress 不是一种服务类型,但它充当集群的入口点。 它可以将路由规则整合到一个资源中,因为它可以在同一IP地址下公开多个服务。

NodePort 类型

如果你将 type 字段设置为 NodePort,则 Kubernetes 控制平面将在 --service-node-port-range 标志指定的范围内分配端口(默认值:30000-32767)。 每个节点将那个端口(每个节点上的相同端口号)代理到你的服务中。 你的服务在其 .spec.ports[*].nodePort 字段中要求分配的端口。

如果需要特定的端口号,你可以在 nodePort 字段中指定一个值。 控制平面将为你分配该端口或报告 API 事务失败。 这意味着你需要自己注意可能发生的端口冲突。 你还必须使用有效的端口号,该端口号在配置用于 NodePort 的范围内。

使用 NodePort 可以让你自由设置自己的负载均衡解决方案, 配置 Kubernetes 不完全支持的环境, 甚至直接暴露一个或多个节点的 IP。

例如:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app: MyApp
  ports:
      # 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值
    - port: 80
      targetPort: 80
      # 可选字段
      # 默认情况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号(默认:30000-32767)
      nodePort: 30007

Service 启用 ipvs 代理模型

Kubernetes 的 1.11 版本后,默认使用 ipvs,如果节点的内核不支持或没有开启 ipvs 则 kubernetes 会自动降级为使用 iptables 规则。

查看 kube-proxy 的启动日志,这里默认使用的是 iptables 代理模型

[root@k8s-master ~]$ kubectl logs -f kube-proxy-hjxcq -n kube-system 
...
I0323 03:36:31.452070       1 node.go:172] Successfully retrieved node IP: 10.10.110.192
I0323 03:36:31.452837       1 server_others.go:142] kube-proxy node IP is an IPv4 address (10.10.110.192), assume IPv4 operation
W0323 03:36:31.577520       1 server_others.go:578] Unknown proxy mode "", assuming iptables proxy
I0323 03:36:31.579263       1 server_others.go:185] Using iptables Proxier.
I0323 03:36:31.580249       1 server.go:650] Version: v1.20.0
...

在所有 Kubernetes 节点开启 ipvs 支持

modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4

查看内核模块是否加载

lsmod | grep ip_vs

修改 ConfigMap 的 kube-system/kube-proxy 的配置文件为 ipvs

kubectl edit configmaps kube-proxy -n kube-system
...
ipvs:      
      excludeCIDRs: null
      minSyncPeriod: 0s
      scheduler: ""
      strictARP: false
      syncPeriod: 0s
      tcpFinTimeout: 0s
      tcpTimeout: 0s
      udpTimeout: 0s
    kind: KubeProxyConfiguration
    metricsBindAddress: ""
    mode: "ipvs" # 修改此处为ipvs
...

删除所有 kube-proxy 的 pod

kubectl get pod -n kube-system | grep 'kube-proxy' | awk '{print $1}' | xargs -I {} kubectl delete pod {} -n kube-system

再次查看 kube-proxy 的日志

[root@k8s-master ~]$ kubectl logs -f kube-proxy-pnglg -n kube-system 
...
I0410 09:05:52.455879       1 node.go:172] Successfully retrieved node IP: 10.10.110.192
I0410 09:05:52.459403       1 server_others.go:142] kube-proxy node IP is an IPv4 address (10.10.110.192), assume IPv4 operation
I0410 09:05:52.599981       1 server_others.go:258] Using ipvs Proxier.
W0410 09:05:52.606702       1 proxier.go:445] IPVS scheduler not specified, use rr by default
I0410 09:05:52.608073       1 server.go:650] Version: v1.20.0
...

查看 ipvs 相关规则

apt install -y ipvsadm 

[root@k8s-master ~]$ ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.17.0.1:30023 rr
  -> 10.244.36.72:8443            Masq    1      0          0         
TCP  10.10.110.190:30023 rr
  -> 10.244.36.72:8443            Masq    1      0          0         
TCP  10.96.0.1:443 rr
  -> 10.10.110.190:6443           Masq    1      0          0         
TCP  10.96.0.10:53 rr
  -> 10.244.36.73:53              Masq    1      0          0         
  -> 10.244.169.160:53            Masq    1      0          0         
TCP  10.96.0.10:9153 rr
  -> 10.244.36.73:9153            Masq    1      0          0         
  -> 10.244.169.160:9153          Masq    1      0          0         
TCP  10.97.105.36:80 rr
TCP  10.98.47.133:443 rr
  -> 10.244.36.72:8443            Masq    1      0          0         
TCP  10.110.221.168:8000 rr
  -> 10.244.169.159:8000          Masq    1      0          0         
TCP  10.244.235.192:30023 rr
  -> 10.244.36.72:8443            Masq    1      0          0         
TCP  127.0.0.1:30023 rr
  -> 10.244.36.72:8443            Masq    1      0          0         
UDP  10.96.0.10:53 rr
  -> 10.244.36.73:53              Masq    1      0          0         
  -> 10.244.169.160:53            Masq    1      0          0

Ingress

Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。

Ingress 可以提供负载均衡、SSL 终结和基于名称的虚拟托管。

Ingress 是什么?

Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。

下面是一个将所有流量都发送到同一 Service 的简单 Ingress 示例:

可以将 Ingress 配置为服务提供外部可访问的 URL、负载均衡流量、终止 SSL/TLS,以及提供基于名称的虚拟主机等能力。 Ingress 控制器 通常负责通过负载均衡器来实现 Ingress,尽管它也可以配置边缘路由器或其他前端来帮助处理流量。

Ingress 不会公开任意端口或协议。 将 HTTP 和 HTTPS 以外的服务公开到 Internet 时,通常使用 Service.Type=NodePortService.Type=LoadBalancer 类型的服务。

环境准备

你必须具有 Ingress 控制器 才能满足 Ingress 的要求。 仅创建 Ingress 资源本身没有任何效果。

你可能需要部署 Ingress 控制器,例如 ingress-nginx。 你可以从许多 Ingress 控制器 中进行选择。

理想情况下,所有 Ingress 控制器都应符合参考规范。但实际上,不同的 Ingress 控制器操作略有不同。

Ingress 资源

一个最小的 Ingress 资源示例:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

与所有其他 Kubernetes 资源一样,Ingress 需要使用 apiVersionkindmetadata 字段。 Ingress 对象的命名必须是合法的 DNS 子域名名称。 有关使用配置文件的一般信息,请参见部署应用配置容器管理资源。 Ingress 经常使用注解(annotations)来配置一些选项,具体取决于 Ingress 控制器,例如 重写目标注解。 不同的 Ingress 控制器 支持不同的注解。查看文档以供你选择 Ingress 控制器,以了解支持哪些注解。

Ingress 规约 提供了配置负载均衡器或者代理服务器所需的所有信息。 最重要的是,其中包含与所有传入请求匹配的规则列表。 Ingress 资源仅支持用于转发 HTTP 流量的规则。

Ingress 规则

每个 HTTP 规则都包含以下信息:

  • 可选的 host。在此示例中,未指定 host,因此该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了 host(例如 foo.bar.com),则 rules 适用于该 host
  • 路径列表 paths(例如,/testpath),每个路径都有一个由 serviceNameservicePort 定义的关联后端。 在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容。
  • backend(后端)是 Service 文档 中所述的服务和端口名称的组合。 与规则的 hostpath 匹配的对 Ingress 的 HTTP(和 HTTPS )请求将发送到列出的 backend

通常在 Ingress 控制器中会配置 defaultBackend(默认后端),以服务于任何不符合规约中 path 的请求。

DefaultBackend

没有 rules 的 Ingress 将所有流量发送到同一个默认后端。 defaultBackend 通常是 Ingress 控制器 的配置选项,而非在 Ingress 资源中指定。

如果 hostspaths 都没有与 Ingress 对象中的 HTTP 请求匹配,则流量将路由到默认后端。

资源后端

Resource 后端是一个 ObjectRef,指向同一命名空间中的另一个 Kubernetes,将其作为 Ingress 对象。Resource 与 Service 配置是互斥的,在二者均被设置时会无法通过合法性检查。Resource 后端的一种常见用法是将所有入站数据导向带有静态资产的对象存储后端。

路径类型

Ingress 中的每个路径都需要有对应的路径类型(Path Type)。未明确设置 pathType 的路径无法通过合法性检查。当前支持的路径类型有三种:

  • ImplementationSpecific:对于这种路径类型,匹配方法取决于 IngressClass。 具体实现可以将其作为单独的 pathType 处理或者与 PrefixExact 类型作相同处理。
  • Exact:精确匹配 URL 路径,且区分大小写。
  • Prefix:基于以 / 分隔的 URL 路径前缀匹配。匹配区分大小写,并且对路径中的元素逐个完成。 路径元素指的是由 / 分隔符分隔的路径中的标签列表。 如果每个 p 都是请求路径 p 的元素前缀,则请求与路径 p 匹配。

说明: 如果路径的最后一个元素是请求路径中最后一个元素的子字符串,则不会匹配 (例如:/foo/bar 匹配 /foo/bar/baz, 但不匹配 /foo/barbaz)。

示例

类型 路径 请求路径 匹配与否?
Prefix / (所有路径)
Exact /foo /foo
Exact /foo /bar
Exact /foo /foo/
Exact /foo/ /foo
Prefix /foo /foo, /foo/
Prefix /foo/ /foo, /foo/
Prefix /aaa/bb /aaa/bbb
Prefix /aaa/bbb /aaa/bbb
Prefix /aaa/bbb/ /aaa/bbb 是,忽略尾部斜线
Prefix /aaa/bbb /aaa/bbb/ 是,匹配尾部斜线
Prefix /aaa/bbb /aaa/bbb/ccc 是,匹配子路径
Prefix /aaa/bbb /aaa/bbbxyz 否,字符串前缀不匹配
Prefix /, /aaa /aaa/ccc 是,匹配 /aaa 前缀
Prefix /, /aaa, /aaa/bbb /aaa/bbb 是,匹配 /aaa/bbb 前缀
Prefix /, /aaa, /aaa/bbb /ccc 是,匹配 / 前缀
Prefix /aaa /ccc 否,使用默认后端
混合 /foo (Prefix), /foo (Exact) /foo 是,优选 Exact 类型

最基本的 Ingress 资源

注意:在此示例中,未指定 host,因此该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了 host(例如 foo.bar.com),则 rules 适用于该 host

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wildcard-host
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - pathType: Prefix
        path: "/nginx"
        backend:
          service:
            name: nginx-service
            port:
              number: 80
  defaultBackend:
    service:
      name: default-http-backend
      port:
        number: 80    

基于 URL 路由代理服务

配置根据请求的 HTTP URI 将来自同一 IP 地址的流量路由到多个 Service。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wildcard-host
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: "www.ingress.com"
    http:
      paths:
      - pathType: Prefix
        path: "/nginx"
        backend:
          service:
            name: nginx-service
            port:
              number: 80
      - pathType: Prefix
        path: "/tomcat"
        backend:
          service:
            name: tomcat-service
            port:
              number: 80
  defaultBackend: # 如果hosts或paths都没有与Ingress对象中的HTTP请求匹配,则流量将路由到默认后端
    service:
      name: default-http-backend
      port:
        number: 80

基于名称的虚拟托管

基于名称的虚拟主机支持将针对多个主机名的 HTTP 流量路由到同一 IP 地址上。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wildcard-host
spec:
  rules:
  - host: "www.nginx.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: nginx-service
            port:
              number: 80
  - host: "www.tomcat.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: tomcat-service
            port:
              number: 80
  defaultBackend:
    service:
      name: default-http-backend
      port:
        number: 80

TLS

可以通过设定包含 TLS 私钥和证书的 Secret 来保护 Ingress。 Ingress 只支持单个 TLS 端口 443,并假定 TLS 连接终止于 Ingress 节点 (与 Service 及其 Pod 之间的流量都以明文传输)。 如果 Ingress 中的 TLS 配置部分指定了不同的主机,那么它们将根据通过 SNI TLS 扩展指定的主机名 (如果 Ingress 控制器支持 SNI)在同一端口上进行复用。 TLS Secret 必须包含名为 tls.crttls.key 的键名。 这些数据包含用于 TLS 的证书和私钥。例如:

apiVersion: v1
kind: Secret
metadata:
  name: testsecret-tls
  namespace: default
data:
  tls.crt: base64 编码的 cert
  tls.key: base64 编码的 key
type: kubernetes.io/tls

在 Ingress 中引用此 Secret 将会告诉 Ingress 控制器使用 TLS 加密从客户端到负载均衡器的通道。 你需要确保创建的 TLS Secret 创建自包含 https-example.foo.com 的公用名称(CN)的证书。 这里的公共名称也被称为全限定域名(FQDN)。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wildcard-host
spec:
  tls:
  - hosts:
      - www.missf.top
    secretName: www-missf-top
  rules:
  - host: www.missf.top
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: nginx-service
            port:
              number: 80
  defaultBackend:
    service:
      name: default-http-backend
      port:
        number: 80

生成 tls 自签证书

CFSSL 是 CloudFlare 开源的一款 PKI/TLS 工具。 CFSSL 包含一个命令行工具和一个用于签名、验证并且捆绑 TLS 证书的 HTTP API 服务,使用 Go 语言编写。

  1. 安裝 CFSSL 工具

    curl -s -L -o /usr/local/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
    curl -s -L -o /usr/local/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
    curl -s -L -o /usr/local/bin/cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
    chmod +x /usr/local/bin/cfssl*
  2. 生成配置证书生成策略文件

    cat > ca-config.json <<EOF
    {
      "signing": {
        "default": {
          "expiry": "87600h"
        },
        "profiles": {
          "kubernetes": {
             "expiry": "87600h",
             "usages": [
                "signing",
                "key encipherment",
                "server auth",
                "client auth"
            ]
          }
        }
      }
    }
    EOF

    这个策略,有一个默认的配置和一个 profile,这里的 profile 是 kubernetes。

    signing:表示该证书可用于签名其它证书。

    server auth:表示 client 可以用该 CA 对 server 提供的证书进行验证。

    client auth:表示 server 可以用该 CA 对 client 提供的证书进行验证。

    expiry:表示证书的有效期。

  3. 生成 CA 证书和私钥配置文件

    cat > ca-csr.json <<EOF
    {
        "CN": "kubernetes",
        "key": {
            "algo": "rsa",
            "size": 2048
        },
        "names": [
            {
                "C": "CN",
                "ST": "GuangDong",
                "L": "ShenZhen",
                "O": "Kubernetes",
                "OU": "Devops"
            }
        ]
    }
    EOF

    CN:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name),浏览器使用该字段验证网站是否合法

    C:Country, 国家

    ST:State,州,省

    L:Locality,地区,城市

    O:Organization,kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group)

    OU:Organization Unit Name,组织单位名称,公司部门

  4. 生成 CA 证书、CA 私钥、CSR 文件

    cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
    
    # 得到如下文件
    [root@k8s-master ~/kubernetes/ssl]$ ll
    total 28
    -rw-r--r-- 1 root root  294 Apr 17 15:46 ca-config.json
    -rw-r--r-- 1 root root 1013 Apr 17 15:48 ca.csr
    -rw-r--r-- 1 root root  274 Apr 17 15:47 ca-csr.json
    -rw------- 1 root root 1679 Apr 17 15:48 ca-key.pem
    -rw-r--r-- 1 root root 1387 Apr 17 15:48 ca.pem
  5. 生成服务端的证书信息

    cat > www.missf.top-csr.json <<EOF
    {
      "CN": "www.missf.top",
      "hosts": [],
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [
        {
           "C": "CN",
           "ST": "GuangDong",
           "L": "ShenZhen",
           "O": "Kubernetes",
           "OU": "System"
        }
      ]
    }
    EOF
  6. 使用 ca 证书签发证书

    cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes www.missf.top-csr.json | cfssljson -bare www.missf.top
  7. 使用证书创建 secret 资源

kubectl create secret tls www-missf-top --cert=www.missf.top.pem --key=www.missf.top-key.pem