OpenShift总体架构设计

2021-03-01 15:58:14 浏览数 (1)

“网络安全观”说明

OpenShift被其供应商Red Hat称为“ 企业级Kubernetes”。其实Kubernetes是OpenShift不可或缺的一部分,并围绕它构建了更多功能。

这里,仅小结下OpenShift与Kubernetes在安全方面的差异:

OpenShift比默认的Kubernetes具有更严格的安全策略,这可能是由于OpenShift产品的企业目标群体。例如,OpenShift禁止以根用户身份运行容器,甚至许多第三方官方镜像都不满足此要求,导致人们无法像在Kubernetes上那样运行简单的应用程序。

此外,RBAC是OpenShift不可或缺的一部分。您别无选择,必须在其上部署越来越多应用程序的过程中学习它。而Kubernetes的一些版本则没有RBAC安全性。

还有身份验证和授权模型,尤其是对外部应用程序的授权。作为OpenShift的一部分,您可以安装其他组件:内部容器厂库、基于EFK的日志记录堆栈(ElasticSearch,Fluentd,Kibana)、基于Prometheus的监控、Jenkins等。您还可使用单个帐户通过OAuth机制(作为sidecars运行的oauth——代理)对其进行身份验证,这使权限管理更加容易。当然您也可以在Kubernetes上实现相同的目标,但这需要大量工作。

综上,在OpenShift中贯彻了“默认安全”的思想

注1:本文主要目的是宣传安全技术理念,并无任何商业目的。若有侵权,请联系微信公众号“网络安全观”删除。

注2:后文摘自于《OpenShift云原生架构:原理与实践》,由微信公众号“分布式实验室”经出版方授权发布。

本文摘自于云计算/OpenShift领域资深专家和布道者山金孝、潘晓华、刘世民撰写的《OpenShift云原生架构:原理与实践》一书,将介绍OpenShift在架构设计上的哲学理念,分析其与Kubernetes在主要功能上的区别,探讨OpenShift在构建以应用为中心的PaaS平台上的设计之道,同时还将介绍其核心组件、核心概念及部署架构等内容。

OpenShift是由RedHat公司推出的企业级容器云PaaS平台,2015年,RedHat推出完全重构后基于Docker和Kubernetes的OpenShift 3.0,完善了强大的用户界面,以及诸如源代码到镜像和构建管道等OpenShift独有组件,极大简化了云原生应用的构建部署和DevOps理念文化的落地实践。2019年,RedHat推出了OpenShift v4,集成了CoreOS、Istio、Knative、Kubernetes Operator等技术,将OpenShift推向了全栈融合云和应用全生命周期自动化管理时代。可见,作为当今最为成熟和主流的容器云PaaS平台,OpenShift的架构也一直在演进。本文将基于OpenShift当前最为成熟稳定的3.11版本,介绍其设计理念和总体架构,并深入介绍和分析OpenShift网络、存储、权限控制、服务目录等核心功能,在部署实践OpenShift云原生PaaS平台前,为读者建立起完备扎实的理论基础。

OpenShift设计哲学

容器平台(Container Platform)是一种使用容器去构建、部署和编排应用的应用平台。OpenShift是一种新型容器云PaaS平台,其使用两种主要工具在容器中运行应用,即以Docker作为容器运行时(Container runtime)在Linux环境中创建容器,以Kubernetes为容器编排引擎(Container Orchestration Engine)在平台中编排容器。OpenShift在架构上具有以分层、应用为中心和功能模块解耦等主要特点。

分层架构

OpenShift采用分层架构,利用Docker、Kubernetes及其他开源技术构建起一个PaaS云计算平台。其中,Docker用于基于Linux的轻量容器镜像的打包和创建,Kubernetes 提供了集群管理和在多台宿主机上的容器编排能力。

OpenShift分层架构(来源:RedHat)

从技术堆栈的角度分析,OpenShift自下而上包含了以下几个层次:

  • 基础架构层:提供计算、网络、存储、安全等基础设施,支持在物理机、虚拟化、私有云和公有云等环境上部署OpenShift。
  • 容器引擎层:采用Docker镜像为应用打包方式,采用Docker作为容器运行时,负责容器的创建和管理。Docker利用各种Linux内核资源,为每个Docker容器中的应用提供一个隔离的运行环境。
  • 容器编排和集群管理层:为部署高可用、可扩展的应用,容器云平台需要具有跨多台服务器部署应用容器的能力。OpenShift采用Kubernetes作为其容器编排引擎,同时负责管理集群。事实上,Kuberbnetes正是OpenShift的内核。
  • PaaS服务层:OpenShift在PaaS服务层提供了丰富的开发语言、开发框架、数据库、中间件及应用支持,支持构建自动化(Build Automation)、部署自动化(Deployment Automation)、应用生命周期管理(Application Lifecycle Management,CI/CD)、服务目录(Service Catalog,包括各种语言运行时、中间件、数据库等)、内置镜像仓库等,以构建一个以应用为中心的、更加高效的容器平台。
  • 界面及工具层:向用户提供Web Console、API及命令行工具等,以便于用户使用OpenShift容器云平台。

以应用为中心

下图显示了OpenShift和Kubernetes的主要功能差异。

OpenShift与Kubernetes组件对比

从图中可以看出,相对于Kubernetes,OpenShift新增的全部内容几乎都是以应用为中心来展开的,这些新增功能模块说明如下:

  • Souce to Image(S2I,源代码到镜像):OpenShift新增的一种构建方式,直接从项目源代码和基础镜像自动构建出应用镜像。
  • 内置镜像仓库:用于保存S2I生成的镜像。
  • 构建配置(BuildConfig):构建的静态定义,定义构建的源代码来源、基础镜像、生产镜像等。每次执行即开始一次构建过程。
  • 镜像流(ImageStream):镜像流中包括一个或多个标签,每个标签指向一个镜像。镜像流可用于自动执行某些操作,比如将设定DeploymentConfig的触发器为某镜像流标签,当该标签所指镜像发生变化时,即可自动触发一次部署过程。
  • 部署配置(DeploymentConfig):部署的静态定义,除了定义待部署的Pod外,还定义了自动触发部署的触发器、更新部署的策略等。
  • 路由规则(Route):将部署好的应用服务通过域名发布到集群外供用户访问。

基于上述新增功能,OpenShift支持如图所示的应用从构建到发布的全自动化的过程。

OpenShift 中的应用生命周期

下面介绍在OpenShift平台上创建应用的简要步骤。

1、创建应用。用户通过OpenShift的Web控制台或命令行oc new-app创建应用,根据用户提供的源代码仓库地址及Builder镜像,平台将生成构建配置、部署配置、镜像流和服务(Service)等对象。下面是通过命令行进行应用创建的过程:

代码语言:javascript复制
  1. [root@master1 ~]# oc new-app
  2. openshift/wildfly:13.0~https://github.com/sammyliush/myapp-demo
  3. --name mywebapp4
  4. --> Found image af69006 (4 months old) in image stream "openshift/wildfly"
  5. under tag "13.0" for "openshift/wildfly:13.0"
  6. * A source build using source code from https://github.com/
  7. sammyliush/myapp-demo will be created
  8. * The resulting image will be pushed to image stream tag
  9. "mywebapp4:latest"
  10. * Use 'start-build' to trigger a new build
  11. * This image will be deployed in deployment config "mywebapp4"
  12. * Port 8080/tcp will be load balanced by service "mywebapp4"
  13. * Other containers can access this service through the hostname
  14. "mywebapp4"
  15. --> Creating resources ...
  16. imagestream.image.openshift.io "mywebapp4" created
  17. buildconfig.build.openshift.io "mywebapp4" created
  18. deploymentconfig.apps.openshift.io "mywebapp4" created
  19. service "mywebapp4" created
  20. --> Success
  21. Build scheduled, use 'oc logs -f bc/mywebapp4' to track its progress.
  22. Application is not exposed. You can expose services to the outside
  23. world by executing one or more of the commands below:
  24. 'oc expose svc/mywebapp4'
  25. Run 'oc status' to view your app.

2、触发构建。平台实例化BuildConfig的一次构建,生成一个Build对象。Build对象生成后,平台将执行具体的S2I构建操作,包括下载源代码、实例化Builder镜像、执行编译和构建脚本等。

3、生成镜像。构建成功后将生成一个可部署的应用容器镜像,平台将把此镜像推送到内部的镜像仓库中。

4、更新镜像流。镜像推送至内部的镜像仓库后,平台将更新应用的ImageStream中的镜像流标签,使之指向最新的镜像。

5、触发部署。当ImageStream的镜像信息更新后,将触发DeploymentConfig对象进行一次部署操作,生成一个ReplicationController对象来控制和跟踪所需部署的Pod的状态。

6、部署容器。部署操作生成的ReplicationController对象会负责调度应用容器的部署,将Pod及应用容器部署到集群的计算节点中。

7、发布应用。运行oc expose svc/mywebapp4命令,生成用户通过浏览器可访问的应用域名。之后用户即可通过该域名访问应用。

8、应用更新。当更新应用时,平台将重复上述步骤。平台将用下载更新后的代码构建应用,生成新的镜像,并将镜像部署至集群中。OpenShit支持滚动更新,以保证在进行新旧实例交替时应用服务不会间断。

解耦式高扩展架构

一方面,OpenShift利用API Server(API服务器)和各种Controller(控制器)实现了控制层面的解耦。API Server充当了消息总线角色,提供REST API,这是客户端对各资源类型(Resource Type)的对象进行操作的唯一入口。它的REST API支持对各类资源进行增删改查监控等操作,提供认证、授权、访问控制、API注册和发现等机制,并将资源对象的Spec(定义)和状态(State)等元数据保存到etcd中。各控制器使用Watch(监视)机制通过API Server来感知自己所监视的资源对象的状态变化,并在变化发生时进行相应处理,处理完成后会更新被处理对象的状态,必要时还会调用API来写入新资源的Spec。下图展示了OpenShift控制平面中的各组件。

OpenShift控制平面

以创建一个Pod为例,图所示为该创建过程。

创建Pod过程中OpenShift各组件之间的协作

  1. 客户端使用HTTP/HTTPS通过API向OpenShift API Server 发送(POST)YAML格式的Pod Spec。
  2. API Server在etcd中创建Pod对象并将Spec保存到其中。然后,API Server向客户端返回创建结果。
  3. Scheduler监控到这个Pod对象的创建事件,它根据调度算法决定把这个Pod绑定到节点1,然后调用API在etcd中写入该Pod对象与节点1的绑定关系。
  4. 节点1上的kubelet监控到有一个Pod被分配到它所在的节点上,于是调用Docker创建并运行一个Pod实例,然后调用API更新etcd中Pod对象的状态。

可见,OpenShift API Server实现了简单可靠的消息总线的功能,利用基于消息的事件链,解耦了各组件之间的耦合关系,配合Kubernetes基于声明式的对象管理方式又确保了功能的稳定性。这个层面的架构解耦使得OpenShfit具有良好的规模扩展性。

另外,OpenShift采用各种插件来实现资源层面的解耦。图2-6中展示了OpenShift所利用的各种资源。它采用身份认证程序(Identity Provider)来对接各种身份提供程序,完成身份保存和验证;通过CRI(Container Runtime Interface,容器运行时接口)实现kubelet与容器运行时的解耦,支持Docker和CRI-O等容器运行时;通过Docker Registry API,OpenShift能与各种镜像仓库对接,实现镜像的上传、保存和拉取;通过CNI(Container Network Interface)实现网络层面的解耦,支持多种网络插件实现Pod网络;通过存储插件实现存储层面的解耦,支持多种物理存储后端,为容器提供各种持久存储;通过OSB API(Open Service Broker API,开放服务中介API)实现服务目录层面的解耦,支持各种不同的服务中介,来为容器云平台用户提供丰富的服务。这个层面的架构解耦使得OpenShfit具有良好的功能扩展性。

OpenShift所利用的各种资源

OpenShift核心组件

OpenShift容器PaaS云平台的架构

Master节点

Master节点是OpenShift容器云平台的主控节点,由一台或多台主机组成,运行控制平面所有组件,比如API服务器(API Server)、各种控制器服务器(Controller Server)、etcd和Web Console等。Master节点负责管理集群状态以及集群内的所有节点,并将待创建的pod调度到合适的节点上。

Node节点

Node节点是OpenShift容器云平台的计算节点,受Master节点管理,负责运行应用容器。OpenShift支持在物理机环境、虚拟机环境和云环境中创建Node节点。

容器仓库(Container Registry)

OpenShift容器云平台支持实现Docker Registry API的多种镜像仓库,包括Docker Hub、利用第三方软件比如VMware Harbor搭建的私有镜像仓库,还提供了名为OpenShift Container Registry(OCR)的内置容器镜像仓库。OCR用于存放用户通过内置的S2I镜像构建流程所产生的Docker镜像。每当S2I完成镜像构建后,它就会向内置镜像仓库推送构建好的镜像。

路由层(Routing Layer)

为了让用户从OpenShift集群外访问部署在集群内的应用,OpenShift提供了内置的路由层。路由层是一个软件负载均衡器,包括一个路由器(Router)组件,用户可以为应用的服务定义路由规则(Route),每条路由规则将应用暴露为一个域名。访问这个域名时,路由器会将访问请求转发给服务的后端Pod。

服务层(Service Layer)

在OpenShift中,容器运行在Pod中,每个Pod都会被分配一个IP地址。当应用具有多个Pod时,在集群内部访问这些Pod是通过Service组件来实现的。Service是一个代理,也是一个内部负载均衡,它连接多个后端Pod,并将访问它的请求转发至这些Pod。

Web Console和CLI

Web Console 是从Web浏览器上访问的OpenShift容器云平台的用户界面。Web Console服务以Pod的形式运行在Master节点之上。OpenShift 还提供了命令行工具oc。用户可以从Web Console上直接下载该工具。

OpenShift Web Console 部分截图

OpenShift核心概念

OpenShift包含以下核心概念:

  • 项目(Project)和用户(User)
  • 容器(Container)和镜像(Image)
  • Pod和服务
  • 构建(Build)和镜像流
  • 部署(Deployment)
  • 路由
  • 模板(Template)

项目和用户

用户是与OpenShift容器云平台进行交互的实体,比如开发人员、集群或项目管理员等。OpenShift容器云平台中主要有以下3类用户:

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

通过命令oc get user命令来获取用户列表:

代码语言:javascript复制
  1. [root@master1 ~]# oc get user
  2. NAME UID FULL NAME IDENTITIES
  3. admin 3fe420b5-df2c-11e9-80a7-fa163e71648a allow_all:admin
  4. cadmin 1028b3ab-e449-11e9-9b23-fa163e71648a allow_all:cadmin
  5. regadmin 9825b876-df41-11e9-80a7-fa163e71648a allow_all:regadmin

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

通过oc new-project <project_name>命令来创建一个新项目:

代码语言:javascript复制
  1. [root@master1 ~]# oc new-project devproject --display-name='DEV
  2. Project' --description='Project for development team'
  3. Now using project "devproject" on server
  4. "https://openshift-internal.example.com:8443".

通过oc get project命令获取当前环境中的所有项目:

代码语言:javascript复制
  1. [root@master1 ~]# oc get project
  2. NAME DISPLAY NAME STATUS
  3. devproject DEV Project Active
  4. ……
  5. openshift-web-console Active
  6. testproject Test Project Active

容器和镜像

容器是一个应用层抽象,用于将代码和依赖资源打包在一起。Linux容器技术是一种轻量级进程隔离技术,使得运行在同一台宿主机上的众多容器中的应用拥有独立的进程、文件、网络等空间。因此,多个容器可以在同一台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行。实际上,多年以前Linux 内核中就应用了容器相关技术。

Docker为方便地管理容器提供了管理接口。Docker是一种容器运行时,还是一个工具,负责在所在主机上创建、管理和删除容器。除Docker外,OpenShift还支持另一种容器运行时——CRI-O。OpenShift调用Docker去创建和管理容器,提供了在多个宿主机上编排Docker容器的能力。

当使用Docker创建容器时,它会为每个容器创建命名空间(Namespace)和控制群组(Control Groups)。命名空间包括Mount(用于隔离挂载点)、PID(用于隔离进程ID)、Network(用于隔离网络设备)、IPC(用于隔离进程间通信)、UTS(用于隔离主机名和域名)和UID(用于隔离用户和用户组ID)等。Docker还支持在同一个命名空间中运行多个容器。下图中左图表示一个用Docker创建的Nginx容器,右图表示共享命名空间的Nginx容器和Confd容器,其中Confd容器负责维护Nginx的配置文件。

Docker容器示例

容器镜像是轻量的、可执行的独立软件包,包含软件运行所需的所有内容,如代码、运行时环境、系统工具、系统库和设置等。OpenShift 容器云平台中运行的容器是基于Docker格式的容器镜像。

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

OpenShift中的镜像相关概念

Pod和Service

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容器云平台的控制平面组件(包括etcd、API Server 和 Controller)会以静态Pod的形式运行在Master节点上,由其上的kubelet创建和管理。另一类特殊的Pod为守护Pod(Daemon Pod),一个节点上只有一个守护Pod的副本。OpenShift容器云平台的openshift-sdn和 openvswitch 组件以守护Pod的形式运行在所有节点上。

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

代码语言:javascript复制
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: myapp-pod
  5. labels:
  6. app: myapp
  7. spec:
  8. containers:
  9. - name: myapp-container
  10. image: busybox
  11. command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']

通过oc get pod命令,可查看当前项目中的Pod。列表中的第三个Pod就是使用上面的Pod定义文件创建的。

代码语言:javascript复制
  1. [root@master1 ~]# oc get pod
  2. NAME READY STATUS RESTARTS AGE
  3. hello-openshift-3-5lr24 1/1 Running 0 3h
  4. hello-openshift-3-cjfbm 1/1 Running 0 3h
  5. myapp-pod 1/1 Running 0 2m
  6. mywebapp-2-5wj2t 1/1 Running 0 1h
  7. mywebapp-2-sr84n 1/1 Running 0 56m

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

1、Infra容器

每个Pod都会运行一个Infra容器(基础容器),它负责创建和初始化Pod的各个命名空间,随后创建的Pod中的所有容器会被加入这些命名空间中。这种容器的名字以“k8s_POD_<pod名称>_<project名称>”开头,下面是在项目testproject中的名为myapp-pod-with-init-containers的Pod中的Infra容器:

代码语言:javascript复制
  1. 4b6588d15798 docker.io/openshift/origin-pod:v3.11.0
  2. "/usr/bin/pod" About an hour ago Up About an hour k8s_POD_myapp-pod-with-init-containers_testproject_25435bc3-003f-11ea-9877-fa163e71648a_0

该容器使用的镜像通过宿主机上的kubelet程序的启动参数--pod-infra-container-image来指定。在笔者的测试环境中,其配置如下:

代码语言:javascript复制
  1. --pod-infra-container-image=docker.io/openshift/origin-pod:v3.11.0

2、Init容器

Init容器(初始容器)是一种特殊的容器,一个Pod可以没有,也可以有一个或多个Init容器。Init容器在Pod的主容器(应用容器)运行前运行。如果有一个Init容器运行失败,那么Pod中的主容器就不会启动。因此,可利用Init容器来检查是否满足主容器启动所需的前提条件。比如一个应用Pod中的主容器要求MySQL服务就绪后才能运行,那么可以在Init容器中检查MySQL服务是否就绪。在下面的Pod声明示例中,定义了两个Init容器,第一个Init容器会检查MyService服务是否就绪,第二个Init容器会检查MyDB服务是否就绪。只有在这两个服务都就绪了之后,Pod的主容器myapp-container才会运行。

代码语言:javascript复制
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: myapp-pod
  5. labels:
  6. app: myapp
  7. spec:
  8. containers:
  9. - name: myapp-container
  10. image: busybox:1.28
  11. command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  12. initContainers:
  13. - name: init-myservice
  14. image: busybox:1.28
  15. command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  16. - name: init-mydb
  17. image: busybox:1.28
  18. command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

Pod被创建后,如果MyService服务尚未就绪,Pod的状态为Init:0/2,表示它的两个Init容器都未成功运行:

代码语言:javascript复制
  1. [root@master1 ~]# oc get pod
  2. myapp-pod 0/1 Init:0/2 0 6m

在MyService服务就绪后,Pod的状态会变为Init:1/2,表示它的两个Init容器中有一个已成功运行,此时这个Init容器的状态为“Terminated”,还有一个Init容器未成功运行:

代码语言:javascript复制
  1. myapp-pod 0/1 Init:1/2 0 7m

在MyDB服务就绪后,第二个Init容器运行成功后,此时Pod状态变为PodInitializing,表明它开始进入初始化状态:

代码语言:javascript复制
  1. myapp-pod 0/1 PodInitializing 0 8m

随后,Pod中的所有主容器都运行成功,其状态变为Running:

代码语言:javascript复制
  1. myapp-pod 1/1 Running 0 8m

3、主容器

主容器即应用容器,通常一个Pod中运行一个应用程序的主容器。在某些场景下,一个Pod中会运行多个具有强耦合关系的主容器。比如,在一个Pod中以sidecar(边车)形式运行一个日志采集容器,用于采集该Pod主容器中的应用写到日志文件中的日志,并将它们输出到标准输出。

因此,Pod 是一个或多个容器组成的集合,这些容器共享同一个运行环境。OpenShift默认利用Docker作为容器运行时来创建和管理容器,Pod内的所有容器共享命名空间。Docker首先为Pod创建Infra容器,为该容器创建命名空间和控制组,然后依次创建和运行Init容器,等到所有Init容器都运行后,再创建和运行主容器。这些容器都共享Infra容器的命名空间。图2-11是Pod中的容器示意图。实际上,一个Pod中的所有容器中的进程都仿佛运行在同一台“机器”上。Pod中的所有容器共享网络空间,因此可以通过localhost互相直接通信;它们还使用同样的主机名(hostname),以及共享Pod的存储卷。

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

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

OpenShift Pod中的容器

Pod的状态(status)和Pod的阶段(phase)不是一一对应的。在Pending阶段,Pod的状态通常为“ContainerCreating”。在Running阶段,Pod的状态可能为“Running”,表示它在正常运行;也可能为“Error”,比如某个容器失败了。在Succeeded阶段,Pod的状态通常为“Completed”。通过oc get pod命令可查询当前项目中所有Pod的状态。下图显示了一个具有两个Init容器和两个主容器的Pod启动过程中,各个容器的启动顺序和对应Pod的状态,以及Pod终止时和终止后的状态。

OpenShift Pod的主要生命周期阶段

由于Pod是临时性的,因此它的IP:Port也是动态变化的。这将导致以下问题:如果一组后端Pod作为服务提供方,供一组前端Pod调用,那么服务调用方怎么使用不断变化的后端Pod的IP呢?为了解决此问题,OpenShift引入了Kubernetes中的Service概念。一个Service可被看作OpenShift容器云平台的一个内部负载均衡器。它定位一组Pod,并将网络流量导入其中。可以通过oc get svc命令来获取当前项目中的服务实例。

代码语言:javascript复制
  1. [root@master1 ~]# oc get svc
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
  3. hello-openshift ClusterIP 172.30.10.229 <none> 8080/TCP,8888/TCP
  4. mywebapp ClusterIP 172.30.151.210 <none> 8080/TCP

通过oc describe svc命令来查看某服务的具体信息。

代码语言:javascript复制
  1. [root@master1 ~]# oc describe svc/mywebapp
  2. Name: mywebapp
  3. Namespace: testproject
  4. Labels: app=mywebapp
  5. Annotations: openshift.io/generated-by=OpenShiftWebConsole
  6. Selector: deploymentconfig=mywebapp
  7. Type: ClusterIP
  8. IP: 172.30.151.210
  9. Port: 8080-tcp 8080/TCP
  10. TargetPort: 8080/TCP
  11. Endpoints: 10.129.0.108:8080,10.130.0.142:8080

该服务对象的名称为“mywebapp”,其后端通过Selector(筛选器)筛选出来,本例中服务的后端是包含“deploymentconfig=mywebapp”的所有Pod,其IP和端口号分别为10.129.0.108:8080和10.130.0.142:8080。该服务被分配了IP地址172.30.151.210,端口号为8080。有了此服务后,服务使用方就可以使用服务的IP:Port来访问后端服务了。

那服务是如何将网络流量导入后端Pod的呢?OpenShift支持两种服务路由实现。默认是基于iptables的,使用iptables规则将发送到服务IP的请求转发到服务的后端Pod。另一种较早期的实现是基于用户空间进程,它将收到的请求转发给一个可用后端Pod。相比之下,基于iptables的实现效率更高,但要求每个Pod都能接收请求;用户空间进程实现方式的速度较慢一些,但会尝试后端Pod直到找到一个可用Pod为止。因此,如果有完善的Pod可用性检查机制(Readiness Check),那基于iptables的方案是最佳选择;否则,基于用户空间代理进程的方案会比较安全。可在Ansible清单文件中设置openshift_node_proxy_mode来选择以哪种方式实现,默认值为iptables,可设置为userspace以使用用户空间代理进程方式。

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

代码语言:javascript复制
  1. [root@master1 ~]# oc get ep
  2. NAME ENDPOINTS
  3. hello-openshift 10.129.0.132:8888,10.130.0.140:8888 1 more...
  4. mywebapp 10.129.0.133:8080,10.130.0.156:8080
  5. mywebappv2 10.130.0.157:8080

构建和镜像流

构建表示根据输入参数构建出目标对象的过程。在OpenShift容器云平台上,该过程用于将源代码转化为可运行的容器镜像。OpenShift支持4种构建方式:Docker构建、S2I构建、Pipeline构建和自定义构建。

Docker构建会调用docker build命令,基于所提供的Dockerfile文件和所提供的内容来构建Docker镜像。

S2I构建是OpenShift的原创,它根据指定的构建镜像(Builder Image)和源代码(Source Code),构建生成可部署Docker镜像,并推送到OpenShift内部集成镜像库中。

Pipeline构建方式允许开发者定义Jenkins Pipeline。在项目首次使用该构建方式时,OpenShift 容器云平台会启动一个Jenkins服务,然后再将该Pipeline 交由它来执行,并负责启动、监控和管理该构建。BuildConfig对象中可以直接包含Jenkins Pipeline的内容,或者包含其Git仓库地址。

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

代码语言:javascript复制
  1. [root@master1 ~]# oc get bc
  2. NAME TYPE FROM LATEST
  3. mywebapp Source Git@master 3

通过oc start-build命令可手动启动一次构建。通过命令oc get build 可查看所有构建。

代码语言:javascript复制
  1. [root@master1 ~]# oc get build
  2. NAME TYPE FROM STATUS STARTED DURATION
  3. mywebapp-2 Source Git@d5837f1 Complete 34 minutes ago 7m50s
  4. mywebapp-3 Source Git@d5837f1 Complete 24 minutes ago 3m13s

使用Docker或Source策略的构建配置的一次成功构建会创建一个容器镜像。镜像会被推送到BuildConfig定义的output部分所指定的容器镜像仓库中。如果目标仓库的类型为 ImageStreamTag,那么镜像会被推送到OpenShift容器云平台的内置镜像仓库中;如果类型为DockerImage,那么镜像会被推送到指定的镜像仓库或Docker Hub中。

代码语言:javascript复制
  1. spec:
  2. nodeSelector: null
  3. output:
  4. to:
  5. kind: ImageStreamTag
  6. name: mywebapp:latest

一个ImageStream及其所关联的标签为OpenShfit容器云平台内所使用到的容器镜像提供了一种抽象方法。镜像流由ImageSteam对象表示,镜像流标签由ImageSteamTag对象表示。镜像流并不包含实际镜像数据,而是使用标签指向任意数量的Docker格式的镜像。通过oc get is命令可以获取当前项目中ImageStream对象列表。

代码语言:javascript复制
  1. [root@master1 ~]# oc get is
  2. NAME DOCKER REPO TAGS
  3. myApp docker-registry.default.svc:5000/testproject/myApp99 latest

一个镜像流标签对象(ImageStreamTag)指向一个镜像,可以是本地镜像或者远程镜像。如下的名为“python”的镜像流包含两个标签,标签34指向Python v3.4镜像,标签35指向 Python v3.5镜像。

代码语言:javascript复制
  1. Name: python
  2. Namespace: imagestream
  3. ……
  4. Tags: 2
  5. 34
  6. tagged from centos/python-34-centos7
  7. * centos/python-34-centos7@sha256:28178e2352d31f2407d8791a54d0
  8. 14 seconds ago
  9. 35
  10. tagged from centos/python-35-centos7
  11. * centos/python-35-centos7@sha256:2efb79ca3ac9c9145a63675fb0c09220ab3b8d4005d3
  12. 5e0644417ee552548b10
  13. 7 seconds ago

通过oc get istag命令可查询当前项目中的镜像流标签。

代码语言:javascript复制
  1. [root@master1 ~]# oc get istag
  2. NAME DOCKER REF
  3. hello-openshift:latest openshift/hello-openshift@sha256:aaeae2e
  4. mywebapp:latest 172.30.84.87:5000/testproject/mywebapp@sha256:d6cb2d64617100b7
  5. 6db176c88

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

默认情况下,部署完成后,OpenShift容器平台会在OpenShift项目中创建一些镜像流供用户直接使用。通过oc get is -n openshift命令可查看这些镜像流。每次向OpenShift内置镜像仓库中推送镜像时,会自动创建一个指向该镜像的ImageSteam对象。如下手动创建一个名为“python”,标签为“3.5”的ImageStream:

代码语言:javascript复制
  1. oc import-image python:3.5 --from=centos/python-35-centos7 --confirm

OpenShift 构建与部署相关概念之间的关系

  • BuildConfig是构建的静态定义,每次运行后会启动一次Build。
  • Build完成后产生的镜像会被推送到镜像仓库中,并产生ImageStream和ImageStream-Tag。
  • DeploymentConfig是部署的静态定义,它关联某个ImageStreamTag。每当Image-StreamTag所指向的镜像发生变化,都会自动触发一次部署动作,生成一个ReplicationController对象。

部署

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

通过oc get dc命令可查看当前Project中的DeploymentConfig对象列表。

代码语言:javascript复制
  1. [root@master1 ~]# oc get dc
  2. NAME REVISION DESIRED CURRENT TRIGGERED BY
  3. hello-openshift 1 2 2 config,image(hello-openshift:latest)

通过oc describe dc命令可查看指定DeploymentConfig对象的相关信息。

代码语言:javascript复制
  1. [root@master1 ~]# oc describe dc hello-openshift
  2. Name: hello-openshift
  3. Namespace: testproject
  4. Created: 7 days ago
  5. Labels: app=hello-openshift
  6. Latest Version: 1
  7. Selector: app=hello-openshift,deploymentconfig=hello-openshift
  8. Replicas: 2
  9. Triggers: Config, Image(hello-openshift@latest, auto=true)
  10. Strategy: Rolling
  11. Pod Template:
  12. Labels: app=hello-openshift
  13. deploymentconfig=hello-openshift
  14. Annotations: openshift.io/generated-by=OpenShiftWebConsole
  15. Containers:
  16. hello-openshift:
  17. Image: openshift/hello-openshift@sha256:aaea76ff47e2e
  18. Ports: 8080/TCP, 8888/TCP
  19. Deployment #1 (latest):
  20. Name: hello-openshift-1
  21. Created: 7 days ago
  22. Status: Complete
  23. Replicas: 2 current / 2 desired
  24. Selector: app=hello-openshift,deployment=hello-openshift-1,deploymentconfig= hello-openshift
  25. Labels: app=hello-openshift,openshift.io/deployment-config.name=hello-openshift
  26. Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed
  27. Events: <none>

通过oc rollout latest dc/命令可手动触发该应用的一次部署过程。部署成功后,会创建一个新的ReplicationController对象。

代码语言:javascript复制
  1. [root@master1 ~]# oc rollout latest dc/hello-openshift
  2. deploymentconfig.apps.openshift.io/hello-openshift rolled out

每次部署时都会创建一个ReplicationController对象,并由它创建所需Pod。Replication-Controller确保在任何时间上运行Pod的 “replicas”数为定义中的数量。如果Pod 超过指定的数量,ReplicationController会终止多余的Pod;如果Pod少于指定数量,它将启动更多Pod。与手动创建的Pod不同,如果有Pod失败、被删除或被终止,ReplicationController会自动维护并替代这些Pod。通过oc get rc命令可查看当前项目中的ReplicationController对象列表。

代码语言:javascript复制
  1. [root@master1 ~]# oc get rc
  2. NAME DESIRED CURRENT READY AGE
  3. hello-openshift-1 0 0 0 7d

通过oc describe rc命令可查看指定ReplicationController对象的详细信息。

代码语言:javascript复制
  1. [root@master1 ~]# oc describe rc/hello-openshift-3
  2. Name: hello-openshift-3
  3. Namespace: testproject
  4. Selector: app=hello-openshift,deployment=hello-openshift-3,deploymentconfig= hello-openshift
  5. Labels: app=hello-openshift
  6. openshift.io/deployment-config.name=hello-openshift
  7. Annotations: openshift.io/deployer-pod.completed-at=2019-10-02 18:45:53 0800 CST
  8. ……
  9. openshift.io/encoded-deployment-config={"kind":"DeploymentConfig", "apiVersion":"apps.openshift.io/v1","metadata":{"name":" hello-openshift","namespace":"testproject","selfLink":" /apis/apps.openshift.io...
  10. Replicas: 2 current / 2 desired
  11. Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed
  12. Pod Template:
  13. Labels: app=hello-openshift
  14. deployment=hello-openshift-3
  15. deploymentconfig=hello-openshift
  16. Annotations: openshift.io/deployment-config.latest-version=3
  17. openshift.io/deployment-config.name=hello-openshift
  18. openshift.io/deployment.name=hello-openshift-3
  19. openshift.io/generated-by=OpenShiftWebConsole
  20. Containers:
  21. hello-openshift:
  22. Image: openshift/hello-openshift@sha256:aaea76ff622d2f8bcb32e538e7b3cd0ef6d 291953f3e7c9f556c1ba5baf47e2e
  23. Ports: 8080/TCP, 8888/TCP
  24. Host Ports: 0/TCP, 0/TCP
  25. Environment: <none>
  26. Mounts: <none>
  27. Volumes: <none>
  28. Events:
  29. Type Reason Age From Message
  30. ---- ------ ---- ---- -------
  31. Normal SuccessfulCreate 39m replication-controller Created pod: hello-openshift-3-cjfbm
  32. Normal SuccessfulCreate 39m replication-controller Created pod: hello-openshift-3-5lr24

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

代码语言:javascript复制
  1. triggers:
  2. - type: "ImageChange"
  3. imageChangeParams:
  4. automatic: true
  5. from:
  6. kind: "ImageStreamTag"
  7. name: "mywebapp:latest"
  8. namespace: "myproject

OpenShift 部署、Pod及服务之间的关系

其中:

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

路由器

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

路由器组件就绪之后,用户可创建路由规则(Route)。每个路由规则对象将某服务以域名形式暴露到集群外部,使得从集群外部能通过域名访问到该服务。每个Route对象包含名字、公共域名、服务选择器(Service Selector)和可选的安全配置等配置。路由规则被创建后会被路由器加载,路由器通过路由规则的服务选择器定位到该服务的所有后端,并将其所有后端更新到自身的配置之中。同时,路由器还能动态地跟踪该服务后端的变化,并直接更新自己的配置。当用户访问域名时,域名首先会被域名系统(Domain Name System,DNS)解析并指向Router所在节点的IP地址。Router服务获取该请求后,根据路由规则,将请求转发给该服务的后端所关联的Pod容器实例。通过oc get route命令可获取当前项目中的所有路由规则。

代码语言:javascript复制
  1. [root@master1 ~]# oc get route
  2. NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
  3. hello-openshift helloopenshift.svc.example.com
  4. hello-openshift 8080-tcp None
  5. mywebapp mywebapp-testproject.router.default.svc.cluster.local
  6. mywebapp 8080-tcp None

OpenShfit容器云平台中,路由器和服务都提供负载均衡功能,但使用场景和作用不同。Router组件负责将集群外的访问请求转发给目标应用容器,而Service对象则将集群内的访问请求转发给目标应用容器。

模板(Template)

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

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

代码语言:javascript复制
  1. [root@master1 ~]# oc get template
  2. NAME DESCRIPTION PARAMETERS OBJECTS
  3. jenkins-ephemeral Jenkins service, without persistent storage.... 6 (1 generated) 6

通过oc process –f <filename>命令或oc process <template_name>命令,可以生成模板中定义的对象。

注意:默认情况下,OpenShfit容器平台会在OpenShift项目中创建一些Template供用户使用。通过oc get templates -n openshift命令可查看这些模板。

OpenShift部署架构

OpenShift可以在多种环境中部署,包括物理机、私有云、虚拟化环境和公有云环境。图是基于RedHat Linux虚拟机部署OpenShift容器云生产环境的示例架构图。

OpenShift在VMware环境中的示例部署架构(来源:RedHat公司)

该部署架构说明如下:

  • 采用3台虚拟机作为Master节点,每个节点上均运行API、控制器、调度器、etcd等集群管理服务。
  • 采用3台虚拟机作为Infra节点,每个节点上均运行路由器、内置镜像仓库、监控(Prometheus)和日志(EFK)等集群基础架构组件。
  • 采用多台虚拟机作为Node节点,每个节点上均运行应用Pod。
  • 采用外置存储作为持久存储,比如GlusterFS、NFS、Ceph或者SAN存储。
  • 采用企业级负载均衡器为Master节点上的服务和Infra节点上的服务提供负载均衡。

注:本文摘自于《OpenShift云原生架构:原理与实践》,由微信公众号“分布式实验室”经出版方授权发布。

0 人点赞