OpenShift — 核心概念

OpenShift 核心概念

容器(Container)

Container 的实现基于 Linux Kernel 的 chroot、namespace、cgroups 技术

  • chroot:每个容器具有独立的文件系统。
  • namespace:每个容器具有独立的操作系统资源视图(隔离)。
  • cgroups:每个容器具有独立的操作系统资源配额(限制)。

当使用 Docker 创建 Container 时,它会为每个 Container 创建 Namespace 和 Cgroups。


镜像(Image)

而 Image 的本质就是一个包含了 Application Container 运行所需要的文件集合,如:代码、运行时环境、系统工具、系统库和设置等。

容器镜像仓库(Container Image Registry)是一种集中的存储和分发容器镜像的服务。一个 Registry 中可包含多个仓库(Repository),每个仓库可以包含多个标签(Tag),每个标签对应一个 Image。通常情况下,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是软件哪个版本的镜像。

在这里插入图片描述

用户(User)

OpenShif 主要有以下 3 类 User:

  1. Regular User(常规用户):可通过 API 创建,以 User 对象表示。
  2. System User(系统用户):大部分 System User 在 Cluter 部署完成后自动被创建,主要用于基础架构和 API 服务之间的安全通信。比如:一个集群管理员、每个节点上的一个系统用户等。
  3. Service Account(服务账户):这是 Project 内的特殊系统用户。某些 Service Account 在 Project 创建完成后自动创建,项目管理员也可以创建 Service Account。

项目(Project)

Kubernetes Namespace 为 Cluster 中的资源划分了范围。OpenShift Project 基于 Kubernetes Namespace 概念新增了一些功能,用于对相关对象进行分组和隔离。每个 OpenShift Project 对象对应一个 Kubernetes Namespace 对象。集群管理员可授予用户对某些项目的访问权限、允许用户创建项目,以及授予用户在项目中的权限。

容器沙箱(Pod)

OpenShift 引入了 Kubernetes 中的 Pod 概念。Pod 是 OpenShift 应用的最小可执行和可调度单元,即应用的一个实例。Pod 定义中包含应用的一个或多个容器、存储资源、唯一的网络 IP,以及其他定义容器如何运行的选项。OpenShift 容器云平台使用 Docker 来运行 Pod 中的容器。每个 Pod 都被分配了独立的 IP 地址,Pod 中的所有容器共享本地存储和网络,容器使用 localhost 互相通信。Pod 可拥有共享的存储卷,Pod 中的所有容器都能访问这些卷。

  • Pod 是有生命周期的,从被定义开始,到被分配到某个节点上运行,再到被释放。
  • Pod 是不可以修改的,也就是说一个运行中的 Pod 的定义无法修改。
  • Pod 是临时性的,用完即丢弃,当 Pod 中的进程结束、所在节点故障,或者资源短缺时,Pod 即会被终止。

静态 Pod(Static Pod)是一类特殊的 Pod。这种 Pod 由 Kubelet 创建和管理,仅运行在 kubelet 运行的 Node 上,不能通过 API Server 进行管理,无法与 ReplicationController 等关联。OpenShift 控制平面组件(e.g. etcd、API Server、Scheduler、Controller)会以 Static Pod 的形式运行在 Master 上,由其上的 kubelet 创建和管理。

另一类特殊的 Pod 为守护 Pod(Daemon Pod),一个 Node 上只有一个 Daemon Pod 的副本。OpenShift 的 openshift-sdn和 openvswitch 组件以 Daemon Pod 的形式运行在所有 Node 上。

正是由于 Pod 具有临时性、不可修改、无法自愈等特性,用户很少直接创建独立的 Pod,而会通过 ReplicationController 这样的控制器来对它进行控制。如果需要,可通过 oc create –f <file> 命令来创建 Pod 实例。

一个 OpenShift Pod 可能会包括以下几种容器:

  1. Infra 容器(基础设施容器):是一种特殊的容器,每个 Pod 都会运行一个 Infra 容器,它负责创建和初始化 Pod 的各种 Namespace,后续创建到 Pod 中的所有 Containers 会被加入到这些 Namespace 中。这种容器的名字以 k8s_POD_<pod名称>_<project名称> 为前缀,该容器使用的镜像通过宿主机上的 kubelet 程序的启动参数 --pod-infra-container-image 来指定。
  2. Init 容器(初始容器):也是一种特殊的容器,一个 Pod 可以没有,也可以有一个或多个 Init 容器。Init 容器在 Pod 中的主容器(应用容器)运行前运行。如果有一个 Init 容器运行失败,那么 Pod 中的主容器就不会启动。因此,可利用 Init 容器来检查是否满足主容器启动所需的前提条件。比如一个应用 Pod 中的主容器要求 MySQL 服务就绪后才能运行,那么可以在 Init 容器中检查 MySQL 服务是否就绪。例如:定义了两个 Init 容器,第一个 Init 容器会检查 MyService 服务是否就绪,第二个 Init 容器会检查 MyDB 服务是否就绪。只有在这两个服务都就绪了之后,Pod 的主容器 myapp-container 才会运行。
  3. 主容器(应用容器):通常一个 Pod 中运行一个应用程序的主容器。在某些场景下,一个 Pod 中会运行多个具有强耦合关系的主容器。比如:在一个 Pod 中以 Sidecar(边车)形式运行一个日志采集容器,用于采集该 Pod 主容器中的应用写到日志文件中的日志,并将它们输出到标准输出。

因此,Pod 是一个或多个容器组成的集合,这些容器共享同一个运行环境。OpenShift 默认利用 Docker 作为容器运行时来创建和管理容器,Pod 内的所有容器共享命名空间。Docker 首先为 Pod 创建 Infra 容器,为该容器创建命名空间和控制组,然后依次创建和运行 Init 容器,等到所有 Init 容器都运行后,再创建和运行主容器。这些容器都共享 Infra 容器的命名空间。

下图是 Pod 中的容器示意图。实际上,一个 Pod 中的所有容器中的进程都仿佛运行在同一台 “机器” 上。Pod 中的所有容器共享网络空间,因此可以通过 localhost 互相直接通信;它们还使用同样的主机名(hostname),以及共享 Pod 的存储卷。

在这里插入图片描述

Pod 具有其生命周期,其声明中的 “phase” 字段表示其当前所处的运行阶段。Pod 的主要运行阶段包括:

  1. Pending:OpenShift API Server 已经创建好了 Pod 对象,但还未被调度到某个节点上,或者还在下载 Pod 所需镜像。
  2. Running:Pod 被调度到了 OpenShift 集群的某个节点上,Pod 中所有的主容器都已经被创建出来,而且至少有一个在运行中。
  3. Failed:Pod 中所有容器都已被终止,而且至少有一个容器终止失败。
  4. Succeeded:Pod 中所有容器都已被终止,而且都终止成功了。

Pod 的状态(status)和 Pod 的阶段(phase)不是一一对应的:

  • 在 Pending 阶段,Pod 的状态通常为 “ContainerCreating”。
  • 在 Running 阶段,Pod 的状态可能为 “Running”,表示它在正常运行;也可能为 “Error”,比如某个容器失败了。
  • 在 Succeeded 阶段,Pod 的状态通常为 “Completed”。

通过 oc get pod 命令可查询当前项目中所有 Pod 的状态。下图显示了一个具有两个 Init 容器和两个主容器的 Pod 启动过程中,各个容器的启动顺序和对应 Pod 的状态,以及 Pod 终止时和终止后的状态。

在这里插入图片描述

部署(Deployment)

为了更好地管理应用开发和部署生命周期,OpenShift 在 Kubernetes 的 Replication Controller 的基础上增加了 DeploymentConfig 的概念。DeploymentConfig 对象定义了部署的元数据,包括 ReplicationController 的定义、自动进行新部署的触发器、在部署之间进行状态转换的方法(Rolling Strategy),以及生命周期钩子(Life Cycle Hook)。

通过 oc get dc 命令可查看当前 Project 中的 DeploymentConfig 对象列表。通过 oc rollout latest dc/XXX 命令可手动触发该应用的一次部署过程。部署成功后,会创建一个新的 ReplicationController 对象。每次部署时都会创建一个 ReplicationController 对象,并由它创建所需 Pod。Replication Controller 确保在任何时间上运行 Pod 的 “replicas” 数为定义中的数量。

  • 如果 Pod 超过指定的数量,ReplicationController 会终止多余的 Pod;
  • 如果 Pod 少于指定数量,它将启动更多Pod。

与手动创建的 Pod 不同,如果有 Pod 失败、被删除或被终止,ReplicationController 会自动维护并替代这些 Pod。通过 oc get rc 命令可查看当前项目中的 ReplicationController 对象列表。

还可以在 DeploymentConfig 配置中定义部署触发器,在指定条件发生时即进行一次新的部署。下面是某 DeploymentConfig 定义的 Trigger 部分,设置了 ImageChange 触发器,使得 mywebapp 镜像流的 latest 标签被监控,一旦该标签值发生改变(意味着有新的镜像被推送进来),即会触发一次新的部署过程。

triggers:
  - type: "ImageChange"
    imageChangeParams:
      automatic: true
      from:
        kind: "ImageStreamTag"
        name: "mywebapp:latest"
        namespace: "myproject

服务(Service)

由于 Pod 是临时性的,因此它的 IP:Port 也是动态变化的。这将导致以下问题:如果一组后端 Pod 作为服务提供方,供一组前端 Pod 调用,那么服务调用方怎么使用不断变化的后端 Pod 的 IP 呢?

为了解决此问题,OpenShift 引入了 Kubernetes 中的 Service 概念。一个 Service 可被看作 OpenShift 的一个内部负载均衡器。它定位一组 Pod,并将网络流量导入其中。可以通过 oc get svc 命令来获取当前项目中的 Service 实例。

Service 的后端 Pods 通过 Selector(筛选器)筛选出来,例如:后端 Pod 是包含 “deploymentconfig=mywebapp” 的所有 Pods,其 IP: Port 分别为 10.129.0.108:8080 和 10.130.0.142:8080,而该 Service 则被分配了 IP 地址 172.30.151.210,端口号为 8080。有了此 Service 之后,客户端就就可以使用该 Service 的 IP:Port 来访问后端 Pods 提供的应用业务了。

那 Service 是如何将网络流量导入后端 Pods 的呢?OpenShift 支持两种服务路由实现:

  1. 默认是基于 iptables 的,使用 iptables 规则将发送到 Service 的 Cluster IP 的请求转发到服务的后端 Pods;
  2. 较早期的实现是基于用户空间进程,它将收到的请求转发给一个可用后端 Pods。

相比之下,基于 iptables 的实现效率更高,但要求每个 Pods 都能接收请求;用户空间进程(userspace)实现方式的速度较慢一些,但会尝试后端 Pods 直到找到一个可用 Pod 为止。因此,如果有完善的 Pod 可用性检查机制(Readiness Check),那基于 iptables 的方案是最佳选择;否则,基于用户空间代理进程的方案会比较安全。

Service 的后端服务器被称为端点,以 Endpoints 对象表示,其名称和服务相同。当服务的后端是 Pod 时,通常在 Service 的定义中指定 Label 选择器来指定将哪些 Pod 作为 Service 的后端,然后 OpenShift 会自动创建一个 Endpoints 指向这些 Pods。通过 oc get ep 命令查询当前项目中的 Endpoints 对象。

路由(Router)

Service 提供了一个通往 Backend Pods 的稳定入口,但是 Service 的 IP 地址只是集群内部节点及容器可见。对于外部的应用或者用户来说,这个地址是不可达的。

为了从集群外部能访问到部署在 OpenShift 内部的应用,OpenShift 提供了 Router(路由器)组件。Router 是一个重要组件,是从集群外部访问集群内的容器应用的入口。集群外部请求都会到达 Router,再由它分发到具体应用容器中。Router 组件由集群管理员负责部署和配置,以插件形式实现,OpenShift 支持多种 Router 插件,默认采用了 HAProxy 实现。

Router 组件就绪之后,用户可创建 Route(路由规则)。每个 Route 对象会将关联的 Service 以域名的形式暴露到集群外部,使得从集群外部能通过域名访问到该 Service。每个 Route 对象包含 Name、DomainName、Service Selector 和可选的安全参数等配置。Route 被创建后会被 Router 加载,Router 通过 Route 的 Service Selector 定位到该 Route 的所有 Backend Services,并将其所有后端更新到自身的配置之中。同时,Router 还能动态地跟踪该服务后端的变化,并直接更新自己的配置。

当用户访问 Domain 时,首先会被 DNS 服务器解析并指向 Router 所在 Node 的 IP 地址。Router 获取该请求后,根据 Route 规则,将请求转发给该 Backend Service,最终再转发到 Service 所关联的 Pods 容器实例。通过 oc get route 命令可获取当前项目中的所有路由规则。

注意,OpenShfit 中,Route 和 Service 都提供了负载均衡功能,但使用场景和作用不同。Router(对外)负责将集群外的访问请求转发给 Backend Services,而 Service(对内)则负责将集群内的访问请求转发给 Backend Pods。

在这里插入图片描述

Pod、Service、Deployment 和 Router 的关系如上图所示:

  • DeploymenetConfig:是部署的静态定义,每次部署操作都会产生一个 Replication Controller 对象。
  • ReplicationController:对象负责维护在 DeploymenetConfig 中定义的 Pod 副本数。Pod 是 OpenShift 中最小的可调度单元,在其中运行应用容器。
  • Service:是集群内部负载均衡器,本身带有 IP 地址和端口,以 Pod 作为其后端,将对自身的请求转发至这些后端 Pod。
  • Router:中包含多个 Route,每个 Route 对应一个 Service,将其以域名形式暴露到集群外。

持久化存储(Persistent Storage)

容器默认是非持久化的,所有的修改在容器销毁时都会丢失。但现实是传统的应用大多都是有状态的,因此要求某些容器内的数据必须持久化,容器云平台必须为容器提供持久化存储(persistent storage)。

Docker 本身提供了持久化卷挂载的能力。OpenShift 除了支持 Docker 持久化的挂载方式外,还提供了一种持久化供给模型,即 Persistent Volume(持久化卷,PV)及 Persistent Volume Claim(持久化卷请求,PVC)模型。在 PV 和 PVC 模型中,集群管理员会创建大量大小不同和不同特性的 PV。

用户在部署应用时,先是声明对持久化的需求,创建 PVC,用户在 PVC 中定义所需存储的大小、访问方式(只读或可读可写、独占或是共享)。OpenShift Cluster 会自动寻找符合要求的 PV(数据的存储目录)和 PVC(数据需求的请求)自动对接。通过 PV 和 PVC 模型,OpenShift 为用户提供了一种灵活的方式来消费存储资源。

OpenShift 对持久化后端的支持比较广泛,除了 NFS、iSCSI 外,还支持 Ceph、GluterFS 等分布式存储,以及 Amazon Web Service 和 Google Compute Engine 的云硬盘。

模板(Template)

一个 Template 对象定义了一组对象,这些对象可被参数化,经 OpenShift 处理后会生成一组对象。这些对象可以是该用户在项目中有权创建的所有类型的对象,比如:Service(服务)、BuildConfiguraiton(构建配置)、DeploymentConfig(部署配置)等。

用户可以通过 JSON/YAML 文件来定义一个 Template,再通过 oc create –f <filename> 命令在 OpenShif 创建该 Template 对象。通过 oc get template 命令可查看当前 Project 中的 Template 对象列表。

默认情况下,OpenShfit 会在 Project 中创建一些 Template 供用户使用。通过 oc get templates -n openshift 命令可查看这些模板。

构建(Build)和镜像流(ImageStream)

Build 表示根据输入参数构建出目标对象的过程。在 OpenShift 上,该过程用于将源代码转化为可运行的容器镜像。OpenShift 支持 4 种构建方式:

  1. Docker 构建
  2. S2I 构建:是 OpenShift 的原创,它根据指定的构建镜像(Builder Image)和源代码(Source Code),构建生成可部署 Docker 镜像,并推送到 OpenShift 内部集成镜像库中。
  3. Pipeline 构建:允许开发者定义 Jenkins Pipeline。在项目首次使用该构建方式时,OpenShift 会启动一个 Jenkins 服务,然后再将该 Pipeline 交由它来执行,并负责启动、监控和管理该构建。BuildConfig 对象中可以直接包含 Jenkins Pipeline 的内容,或者包含其 Git 仓库地址。
  4. 自定义构建

Build 建的配置由一个 BuildConfig 对象表示,其定义了构建策略和各种参数,以及触发一次新构建的触发器(Trigger)。通过 oc get bc 命令可获取当前项目中的构建配置列表。

使用 Docker 或 S2I 策略的构建配置的一次成功构建会创建一个容器镜像。镜像会被推送到 BuildConfig 定义的 output 部分所指定的容器镜像仓库中:

  • 如果目标仓库的类型为 ImageStreamTag,那么镜像会被推送到 OpenShift 的内置镜像仓库中;
  • 如果类型为 DockerImage,那么镜像会被推送到指定的镜像仓库或 Docker Hub 中。

一个镜像流标签对象(ImageStreamTag)指向一个镜像,可以是本地镜像或者远程镜像。例如:名为 “python” 的镜像流包含两个 Label,标签 34 指向 Python v3.4 镜像,标签 35 指向 Python v3.5 镜像。通 过oc get istag 命令可查询当前 Project 中的镜像流标签。

使用 ImageStream 的目的是方便将一组相关联的镜像进行整合管理和使用,比如:可在新镜像被创建后自动执行指定构建或部署操作。构建和部署可以监视 ImageStream,在新镜像被添加后会收到通知,并分别通过执行构建或部署来作出反应。例如:某 DeploymentConfig 使用一个 ImageStream,当该镜像版本被更新时,应用会自动进行重新部署。

默认情况下,部署完成后,OpenShift 在 Project 中创建一些镜像流供用户直接使用。通过 oc get is -n openshift 命令可查看这些镜像流。每次向 OpenShift 内置镜像仓库中推送镜像时,会自动创建一个指向该镜像的 ImageSteam 对象。

在这里插入图片描述

OpenShift Build 和 Deployment 的关系如上图:

  • BuildConfig 是 Build 的静态定义,每次运行后会启动一次 Build。
  • Build 完成后产生的镜像会被推送到镜像仓库中,并产生 ImageStream 和 ImageStream-Tag。
  • DeploymentConfig 是 Deployment 的静态定义,它关联某个 ImageStreamTag。每当 Image-StreamTag 所指向的镜像发生变化,都会自动触发一次部署动作,生成一个 ReplicationController 对象。
发布于 2023-06-06 20:33・IP 属地北京