前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OpenShift总体架构设计

OpenShift总体架构设计

作者头像
网络安全观
发布2021-03-01 15:58:14
1.1K0
发布2021-03-01 15:58:14
举报
文章被收录于专栏:网络安全观网络安全观

“网络安全观”说明

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云原生架构:原理与实践》,由微信公众号“分布式实验室”经出版方授权发布。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 网络安全观 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档