本文摘自于云计算/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)等对象。下面是通过命令行进行应用创建的过程:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[root@master1 ~]# oc new-app
openshift/wildfly:13.0~https://github.com/sammyliush/myapp-demo –name mywebapp4 –> Found image af69006 (4 months old) in image stream “openshift/wildfly” under tag “13.0” for “openshift/wildfly:13.0” * A source build using source code from https://github.com/ sammyliush/myapp-demo will be created * The resulting image will be pushed to image stream tag “mywebapp4:latest” * Use ‘start-build’ to trigger a new build * This image will be deployed in deployment config “mywebapp4” * Port 8080/tcp will be load balanced by service “mywebapp4” * Other containers can access this service through the hostname “mywebapp4” –> Creating resources … imagestream.image.openshift.io “mywebapp4” created buildconfig.build.openshift.io “mywebapp4” created deploymentconfig.apps.openshift.io “mywebapp4” created service “mywebapp4” created –> Success Build scheduled, use ‘oc logs -f bc/mywebapp4’ to track its progress. Application is not exposed. You can expose services to the outside world by executing one or more of the commands below: ‘oc expose svc/mywebapp4’ 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各组件之间的协作
- 客户端使用HTTP/HTTPS通过API向OpenShift API Server 发送(POST)YAML格式的Pod Spec。
- API Server在etcd中创建Pod对象并将Spec保存到其中。然后,API Server向客户端返回创建结果。
-
Scheduler监控到这个Pod对象的创建事件,它根据调度算法决定把这个Pod绑定到节点1,然后调用API在etcd中写入该Pod对象与节点1的绑定关系。
- 节点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命令来获取用户列表:
1
2 3 4 5 |
[root@master1 ~]# oc get user
NAME UID FULL NAME IDENTITIES admin 3fe420b5-df2c-11e9-80a7-fa163e71648a allow_all:admin cadmin 1028b3ab-e449-11e9-9b23-fa163e71648a allow_all:cadmin regadmin 9825b876-df41-11e9-80a7-fa163e71648a allow_all:regadmin |
Kubernetes的Namespace(命名空间)为集群中的资源划分了范围。OpenShift的Project(项目)基于Kubernetes的Namespace概念新增了一些功能,用于对相关对象进行分组和隔离。每个OpenShift项目对象对应一个Kubernetes命名空间对象。集群管理员可授予用户对某些项目的访问权限、允许用户创建项目,以及授予用户在项目中的权限。
通过oc new-project 命令来创建一个新项目:
1
2 3 4 |
[root@master1 ~]# oc new-project devproject –display-name=’DEV
Project’ –description=’Project for development team’ Now using project “devproject” on server “https://openshift-internal.example.com:8443”. |
通过oc get project命令获取当前环境中的所有项目:
1
2 3 4 5 6 |
[root@master1 ~]# oc get project
NAME DISPLAY NAME STATUS devproject DEV Project Active …… openshift-web-console Active 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 命令来创建Pod实例。下面是一个Pod定义文件示例。
1
2 3 4 5 6 7 8 9 10 11 |
apiVersion: v1
kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: – name: myapp-container image: busybox command: [‘sh’, ‘-c’, ‘echo Hello Kubernetes! && sleep 3600’] |
通过oc get pod命令,可查看当前项目中的Pod。列表中的第三个Pod就是使用上面的Pod定义文件创建的。
1
2 3 4 5 6 7 |
[root@master1 ~]# oc get pod
NAME READY STATUS RESTARTS AGE hello-openshift-3-5lr24 1/1 Running 0 3h hello-openshift-3-cjfbm 1/1 Running 0 3h myapp-pod 1/1 Running 0 2m mywebapp-2-5wj2t 1/1 Running 0 1h 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容器:</project名称></pod名称>
1
2 |
4b6588d15798 docker.io/openshift/origin-pod:v3.11.0
“/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来指定。在笔者的测试环境中,其配置如下:
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才会运行。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
apiVersion: v1
kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: – name: myapp-container image: busybox:1.28 command: [‘sh’, ‘-c’, ‘echo The app is running! && sleep 3600’] initContainers: – name: init-myservice image: busybox:1.28 command: [‘sh’, ‘-c’, ‘until nslookup myservice; do echo waiting for myservice; sleep 2; done;’] – name: init-mydb image: busybox:1.28 command: [‘sh’, ‘-c’, ‘until nslookup mydb; do echo waiting for mydb; sleep 2; done;’] |
Pod被创建后,如果MyService服务尚未就绪,Pod的状态为Init:0/2,表示它的两个Init容器都未成功运行:
1
2 |
[root@master1 ~]# oc get pod
myapp-pod 0/1 Init:0/2 0 6m |
在MyService服务就绪后,Pod的状态会变为Init:1/2,表示它的两个Init容器中有一个已成功运行,此时这个Init容器的状态为“Terminated”,还有一个Init容器未成功运行:
1
|
myapp-pod 0/1 Init:1/2 0 7m
|
在MyDB服务就绪后,第二个Init容器运行成功后,此时Pod状态变为PodInitializing,表明它开始进入初始化状态:
1
|
myapp-pod 0/1 PodInitializing 0 8m
|
随后,Pod中的所有主容器都运行成功,其状态变为Running:
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命令来获取当前项目中的服务实例。
1
2 3 4 |
[root@master1 ~]# oc get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) hello-openshift ClusterIP 172.30.10.229 <none> 8080/TCP,8888/TCP mywebapp ClusterIP 172.30.151.210 <none> 8080/TCP |
通过oc describe svc命令来查看某服务的具体信息。
1
2 3 4 5 6 7 8 9 10 11 |
[root@master1 ~]# oc describe svc/mywebapp
Name: mywebapp Namespace: testproject Labels: app=mywebapp Annotations: openshift.io/generated-by=OpenShiftWebConsole Selector: deploymentconfig=mywebapp Type: ClusterIP IP: 172.30.151.210 Port: 8080-tcp 8080/TCP TargetPort: 8080/TCP 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对象。
1
2 3 4 5 |
[root@master1 ~]# oc get ep
NAME ENDPOINTS hello-openshift 10.129.0.132:8888,10.130.0.140:8888 + 1 more… mywebapp 10.129.0.133:8080,10.130.0.156:8080 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命令可获取当前项目中的构建配置列表。
1
2 3 |
[root@master1 ~]# oc get bc
NAME TYPE FROM LATEST mywebapp Source Git@master 3 |
通过oc start-build命令可手动启动一次构建。通过命令oc get build 可查看所有构建。
1
2 3 4 |
[root@master1 ~]# oc get build
NAME TYPE FROM STATUS STARTED DURATION mywebapp-2 Source Git@d5837f1 Complete 34 minutes ago 7m50s mywebapp-3 Source Git@d5837f1 Complete 24 minutes ago 3m13s |
使用Docker或Source策略的构建配置的一次成功构建会创建一个容器镜像。镜像会被推送到BuildConfig定义的output部分所指定的容器镜像仓库中。如果目标仓库的类型为 ImageStreamTag,那么镜像会被推送到OpenShift容器云平台的内置镜像仓库中;如果类型为DockerImage,那么镜像会被推送到指定的镜像仓库或Docker Hub中。
1
2 3 4 5 6 |
spec:
nodeSelector: null output: to: kind: ImageStreamTag name: mywebapp:latest |
一个ImageStream及其所关联的标签为OpenShfit容器云平台内所使用到的容器镜像提供了一种抽象方法。镜像流由ImageSteam对象表示,镜像流标签由ImageSteamTag对象表示。镜像流并不包含实际镜像数据,而是使用标签指向任意数量的Docker格式的镜像。通过oc get is命令可以获取当前项目中ImageStream对象列表。
1
2 3 |
[root@master1 ~]# oc get is
NAME DOCKER REPO TAGS myApp docker-registry.default.svc:5000/testproject/myApp99 latest |
一个镜像流标签对象(ImageStreamTag)指向一个镜像,可以是本地镜像或者远程镜像。如下的名为“python”的镜像流包含两个标签,标签34指向Python v3.4镜像,标签35指向 Python v3.5镜像。
1
2 3 4 5 6 7 8 9 10 11 12 13 |
Name: python
Namespace: imagestream …… Tags: 2 34 tagged from centos/python-34-centos7 * centos/python-34-centos7@sha256:28178e2352d31f2407d8791a54d0 14 seconds ago 35 tagged from centos/python-35-centos7 * centos/python-35-centos7@sha256:2efb79ca3ac9c9145a63675fb0c09220ab3b8d4005d3 5e0644417ee552548b10 7 seconds ago |
通过oc get istag命令可查询当前项目中的镜像流标签。
1
2 3 4 5 |
[root@master1 ~]# oc get istag
NAME DOCKER REF hello-openshift:latest openshift/hello-openshift@sha256:aaeae2e mywebapp:latest 172.30.84.87:5000/testproject/mywebapp@sha256:d6cb2d64617100b7 6db176c88 |
使用ImageStream的目的是方便将一组相关联的镜像进行整合管理和使用,比如,可在新镜像被创建后自动执行指定构建或部署操作。构建和部署可以监视ImageStream,在新镜像被添加后会收到通知,并分别通过执行构建或部署来作出反应。例如,某DeploymentConfig使用一个ImageStream,当该镜像版本被更新时,应用会自动进行重新部署。
默认情况下,部署完成后,OpenShift容器平台会在OpenShift项目中创建一些镜像流供用户直接使用。通过oc get is -n openshift命令可查看这些镜像流。每次向OpenShift内置镜像仓库中推送镜像时,会自动创建一个指向该镜像的ImageSteam对象。如下手动创建一个名为“python”,标签为“3.5”的ImageStream:
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对象列表。
1
2 3 |
[root@master1 ~]# oc get dc
NAME REVISION DESIRED CURRENT TRIGGERED BY hello-openshift 1 2 2 config,image(hello-openshift:latest) |
通过oc describe dc命令可查看指定DeploymentConfig对象的相关信息。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
[root@master1 ~]# oc describe dc hello-openshift
Name: hello-openshift Namespace: testproject Created: 7 days ago Labels: app=hello-openshift Latest Version: 1 Selector: app=hello-openshift,deploymentconfig=hello-openshift Replicas: 2 Triggers: Config, Image(hello-openshift@latest, auto=true) Strategy: Rolling Pod Template: Labels: app=hello-openshift deploymentconfig=hello-openshift Annotations: openshift.io/generated-by=OpenShiftWebConsole Containers: hello-openshift: Image: openshift/hello-openshift@sha256:aaea76ff47e2e Ports: 8080/TCP, 8888/TCP Deployment #1 (latest): Name: hello-openshift-1 Created: 7 days ago Status: Complete Replicas: 2 current / 2 desired Selector: app=hello-openshift,deployment=hello-openshift-1,deploymentconfig= hello-openshift Labels: app=hello-openshift,openshift.io/deployment-config.name=hello-openshift Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed Events: <none> |
通过oc rollout latest dc/命令可手动触发该应用的一次部署过程。部署成功后,会创建一个新的ReplicationController对象。
1
2 |
[root@master1 ~]# oc rollout latest dc/hello-openshift
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对象列表。
1
2 3 |
[root@master1 ~]# oc get rc
NAME DESIRED CURRENT READY AGE hello-openshift-1 0 0 0 7d |
通过oc describe rc命令可查看指定ReplicationController对象的详细信息。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
[root@master1 ~]# oc describe rc/hello-openshift-3
Name: hello-openshift-3 Namespace: testproject Selector: app=hello-openshift,deployment=hello-openshift-3,deploymentconfig= hello-openshift Labels: app=hello-openshift openshift.io/deployment-config.name=hello-openshift Annotations: openshift.io/deployer-pod.completed-at=2019-10-02 18:45:53 +0800 CST …… openshift.io/encoded-deployment-config={“kind”:”DeploymentConfig”, “apiVersion”:”apps.openshift.io/v1″,”metadata”:{“name”:” hello-openshift”,”namespace”:”testproject”,”selfLink”:” /apis/apps.openshift.io… Replicas: 2 current / 2 desired Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed Pod Template: Labels: app=hello-openshift deployment=hello-openshift-3 deploymentconfig=hello-openshift Annotations: openshift.io/deployment-config.latest-version=3 openshift.io/deployment-config.name=hello-openshift openshift.io/deployment.name=hello-openshift-3 openshift.io/generated-by=OpenShiftWebConsole Containers: hello-openshift: Image: openshift/hello-openshift@sha256:aaea76ff622d2f8bcb32e538e7b3cd0ef6d 291953f3e7c9f556c1ba5baf47e2e Ports: 8080/TCP, 8888/TCP Host Ports: 0/TCP, 0/TCP Environment: <none> Mounts: <none> Volumes: <none> Events: Type Reason Age From Message —- —— —- —- ——- Normal SuccessfulCreate 39m replication-controller Created pod: hello-openshift-3-cjfbm Normal SuccessfulCreate 39m replication-controller Created pod: hello-openshift-3-5lr24 |
还可以在DeploymentConfig配置中定义部署触发器,在指定条件发生时即进行一次新的部署。下面是某DeploymentConfig定义的Trigger部分,设置了ImageChange触发器,使得mywebapp 镜像流的latest标签被监控,一旦该标签值发生改变(意味着有新的镜像被推送进来),即会触发一次新的部署过程。
1
2 3 4 5 6 7 8 |
triggers:
– type: “ImageChange” imageChangeParams: automatic: true from: kind: “ImageStreamTag” name: “mywebapp:latest” 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命令可获取当前项目中的所有路由规则。
1
2 3 4 5 6 |
[root@master1 ~]# oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD hello-openshift helloopenshift.svc.example.com hello-openshift 8080-tcp None mywebapp mywebapp-testproject.router.default.svc.cluster.local mywebapp 8080-tcp None |
OpenShfit容器云平台中,路由器和服务都提供负载均衡功能,但使用场景和作用不同。Router组件负责将集群外的访问请求转发给目标应用容器,而Service对象则将集群内的访问请求转发给目标应用容器。
模板(Template)
一个Template对象定义一组对象,这些对象可被参数化,经OpenShift容器化平台处理后会生成一组对象。这些对象可以是该用户在项目中有权创建的所有类型的对象,比如 Service(服务)、BuildConfiguraiton(构建配置)、DeploymentConfig(部署配置)等。
用户可以通过JSON或YAML文件来定义一个Template,再通过oc create –f 命令在OpenShift容器平台中创建该Template对象。通过oc get template命令可查看当前Project中的Template 对象列表。
1
2 3 |
[root@master1 ~]# oc get template
NAME DESCRIPTION PARAMETERS OBJECTS jenkins-ephemeral Jenkins service, without persistent storage…. 6 (1 generated) 6 |
通过oc process –f 命令或oc process 命令,可以生成模板中定义的对象。
注意:默认情况下,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节点上的服务提供负载均衡。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/309535.html