OpenShift

OpenShift

安装 openshift/origin v3 异常

hzchenkj 回复了问题 • 2 人关注 • 1 个回复 • 1296 次浏览 • 2018-12-14 09:52 • 来自相关话题

DockOne微信分享(一九五):智融集团基于OpenShift的容器化PaaS平台实践

大卫 发表了文章 • 1 个评论 • 1887 次浏览 • 2018-12-07 16:32 • 来自相关话题

【编者的话】当前,是不是使用容器已经不是一个被讨论的重点;热点已然成为企业如何高效使用容器、如何利用容器给企业带来切实的收益。从底层的Docker到优秀的容器编排Kubernetes,都给我们带来了令人心动的基础。今天分享的OpenShift实践(基于Dock ...查看全部
【编者的话】当前,是不是使用容器已经不是一个被讨论的重点;热点已然成为企业如何高效使用容器、如何利用容器给企业带来切实的收益。从底层的Docker到优秀的容器编排Kubernetes,都给我们带来了令人心动的基础。今天分享的OpenShift实践(基于Docker和Kubernetes的容器PaaS云平台),希望可以给大家基于Docker和Kubernetes构建自己的容器云带来一点儿思路。
#概述与实践背景
OpenShift是红帽的云开发平台即服务(PaaS)。 通过OpenShift,企业可以快速搭建稳定、安全、高效的容器应用平台。在这个平台上:

* 可以构建企业内部的容器应用市场,为开发人员快速提供应用开发所依赖的中间件、数据库等服务。
* 通过自动化的流程,开发人员可以快速进行应用的构建、容器化及部署。
* 通过OpenShift,用户可以贯通从应用开发到测试,再到上线的全流程,开发、测试和运维等不同的角色可以在一个平台上进行协作。
* 支持LDAP用户权限管理,支持细粒度的权限资源管理。
* OpenShift可以提高应用从研发到上线的效率和速度,缩短产品上市的时间,可以有效地帮助企业推进DevOps,提高资源利用率,提升生产效率。

当前公司业务和产品处于快速迭代和扩展阶段,每个月平均新增10多个新服务,迭代速度越来越快,同时废弃的老服务服务也越来越多。新技术发展也比较快,在旧方式的基础上我们尝试新技术的成本也比较高,维护成本也比较高。如何提高我们的生产效率,如何提高我们适应新时代的能力,越来越重要。

在此背景下,我们先列出以下几个比较明显的问题:

  1. 阿里云上的资源利用率比较低
  2. 临时扩容比较慢,在运营活动的时候经常资源不足,需要升级配置,活动结束后资源利用率回归较低水平
  3. 新服务增加快,新机器、SLB等越来越多,越来越不好维护
  4. 微服务治理的重要性越来越突出

为了解决这些问题,我们调研了多个解决方案,最终选择了OpenShift作为PaaS平台;截止目前已有70多个项目、100多个服务、上千个pod运行在PaaS平台里(包括Python27、Python36、Java、Golang、Nodejs等类型服务)。
#OpenShift结构
1.png

2.png

3.png

4.png

5.png

OpenShift层级自底而上,分为:基础架构层、容器引擎层、容器编排层、PaaS服务层、界面及工具层。
##Docker
优势:构建一个隔离的、稳定的、安全的、高性能的容器运行环境;很多开源项目都做了自己的官方镜像,使用和维护更方便。
##Kubernetes
容器编排。

* 容器调度:按业务的需求快速部署容器到指定的目标
* 弹性伸缩:按业务的需求快速扩展或收缩容器的运行实例数量
* 异常自愈:当容器实例运行异常,集群能自动感知、处理并恢复服务状态
* 持久化卷:为散布在集群不同机器上的容器提供持久化的智能对接
* 服务发现:为业务为服务化提供服务发现及负载均衡等功能
* 配置管理:为业务应用提供灵活的配置管理及分发规则

##OpenShift
容器云,在Docker和Kubernetes的基础上提供了各种功能,以满足业务应用、研发用户及运维用户在生产效率上的诉求。

1)应用开发框架及中间件

OpenShift提供了丰富的开箱即用的编程开发框架及中间件,如Java、PHP、Ruby、Python、JBossEAP、Tomcat、MySQL、MongoDB及JBoss系列中间件等。

2)应用及服务目录

OpenShift提供了如软件市场式的服务及应用目录,可以实现用户一键部署各类应用及服务,比如一键部署Hadoop集群和Spark集群。

3)自动化流程及工具

OpenShift内置了自动化流程工具S2I(SourcetoImage),帮助用户自动化完成代码的编译、构建及镜像发布。

4)软件自定义网络

通过OpenVSwitch,OpenShift为用户提供了灵活强健的软件定义网络。实现跨主机共享网络及多租户隔离网络模式。

5)性能监控及日志管理

OpenShift提供了开箱可用的性能监控及日志管理的组件。通过平台,业务能快速获取运行状态指标,对业务日志进行收集及分析。

6)多用户接口

OpenShift提供了友好的Web用户界面、命令行工具及RESTfulAPI。

7)自动化集群部署及管理

OpenShift通过Ansible实现了集群的自动化部署,为集群的自动化扩容提供了接口。
#OpenShift核心流程
6.png

##一、应用构建
第1步,部署应用。流程的开始是用户通过OpenShift的Web控制台或命令行ocnew-app创建应用。根据用户提供的源代码仓库地址及Builder镜像,平台将生成构建配置(BuildConfig)、部署配置(DeploymentConfig)、Service及Route等对象。

第2步,触发构建。应用相关的对象创建完毕后,平台将触发一次S2I构建。

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

第4步,生成镜像。构建成功后将生成一个可供部署的应用容器镜像。平台将把此镜像推送到内部的镜像仓库组件Registry中。

第5步,更新ImageStream。镜像推送至内部的仓库后,平台将创建或更新应用的ImageStream的镜像信息,使之指向最新的镜像。
##二、应用部署
第6步,触发镜像部署。当ImageStream的镜像信息更新后,将触发平台部署S2I构建生成的镜像。

第7步,实例化镜像部署。DeploymentConfig对象记录了部署的定义,平台将依据此配置实例化一次部署,生成一个Deploy对象跟踪当次部署的状态。

第8步,生成ReplicationController。平台部署将实例化一个ReplicationController,用以调度应用容器的部署。

第9步,部署容器。通过ReplicationController,OpenShift将Pod及应用容器部署到集群的计算节点中。
##三、请求处理
第10步,用户访问。用户通过浏览器访问Route对象中定义的应用域名。

第11步,请求处理并返回。请求到Router组件后,Router根据Route定义的规则,找到请求所含域名相关联的Service的容器,并将请求转发给容器实例。容器实例除了请求后返回数据,还会通过Router将数据返回给调用的客户端。
##四、应用更新
在应用更新时,平台将重复上述流程的第1步至第9步。平台将用下载更新后的代码构建应用,生成新的镜像,并将镜像部署至集群中。值得注意的是,OpenShit支持滚动更新。在第9步时,平台将通过滚动更新的方式,保证应用在新老实例交替时服务不间断。
#部署结构
7.png

8.png

OpenShift集群:

  1. 所有服务组件都部署在阿里云上,即VPC网络的ECS机器上,分布在多个可用区
  2. 3个Master节点,3个etcd节点,1个Harbor节点,N个Node节点
  3. Node节点分布,根据业务类型打tag标签区分,分别为:

* Base集群,主要运行Router服务和Registry镜像仓库
* Inf集群,主要运行内部PyPI的Python内部源、Nexus服务(主要作为Maven的内部源)和Trace系统等
* Gateway集群,主要部署OpenResty的API网关服务,作为流量从公网域名进入集群的入口
* Service集群,主要部署公司的业务应用
* Bigdata集群,主要部署公司内部大数据服务

  1. 变更平台,通过调用OpenShift的API,来完成build镜像、通过Deployment的模板来更新容器,并记录当前Pod的代码版本。
回滚时选择代码版本对应的一个Deployment配置文件来进行重新部署(注意:Deployment是使用的img的对应hash值,启动参数主要通过ConfigMap、配置中心Apollo来管理)。

#服务镜像自动化及部署模板
SourcetoImage(S2I),S2I是OpenShit的一个重要功能。容器镜像是容器云的应用交付格式。容器镜像中包含了应用及其所依赖的运行环境。通常的做法必须基于外部的基础镜像构建包含企业自身开发代码的应用。这个镜像的构建过程是必须的,要么由企业的IT人员手工完成,要么使用某种工具实现自动化。

作为一个面向应用的平台,OpenShift提供了S2I的流程,使得企业内容器的构建变得标准化和自动化,从而提高了软件从开发到上线的效率。

一个典型的S2I流程包含了以下几个步骤。

  1. 用户输入源代码仓库的地址。
  2. 用户选择S2I构建的基础镜像(又称为Builder镜像)。Builder镜像中包含了操作系统、编程语言、框架等应用所需的软件及配置。OpenShift默认提供了多种编程语言的Builder镜像,如Java、PHP、Ruby、Python、Perl等。用户也可以根据自身需求定制自己的Builder镜像,并发布到服务目录中供用户选用。
  3. 用户或系统触发S2I构建。OpenShift将实例化S2I构建执行器。
  4. S2I构建执行器将从用户指定的代码仓库下载源代码。
  5. S2I构建执行器实例化Builder镜像。代码将会被注入Builder镜像中。
  6. Builder镜像将根据预定义的逻辑执行源代码的编译、构建并完成部署。
  7. S2I构建执行器将完成操作的Builder镜像并生成新的Docker镜像。
  8. S2I构建执行器将新的镜像推送到OpenShift内部的镜像仓库。
  9. S2I构建执行器更新该次构建相关的ImageStream信息。

S2I构建完成后,根据用户定义的部署逻辑,OpenShift将把镜像实例化部署到集群中。

除了接受源代码仓库地址作为输入外,S2I还接受Dockerfile以及二进制文件作为构建的输入。甚至可以完全自定义构建逻辑来满足特殊的需求。
#部署模板
通过自定义模板,可以创建我们自己应用发布时的快速部署模板。包括应用所需的多个Build Config、Deployment Config、Service及Route等对象。OpenShift的Template还有一个重要的特性就是参数化。用户可以在Template中定义参数,这些参数在用户部署模板时将显示在Web控制台的界面上,供用户输入。用户的输入最终以环境变量的方式传递到容器内部。一个OpenShift的Template在结构上主要分为三个组成部分:元信息、对象列表及参数列表。
9.png

10.png

#服务访问拓扑
11.png

容器里的服务都是阿里云VPC的网络,旧服务都是部署在阿里云经典网络的ECS机器上,互相访问时经典网络SLB是一个问题。需要将原来经典网络的SLB换成VPC的SLB。

* 容器互访:通过Hostname+Port方式,后端是Service Ip
* 容器访问经典网络服务:将经典网络的SLB,替换成VPC的SLB
* 经典网络的服务访问容器:a、HTTP服务,通过内网域名;b、RPC服务,通过映射的Node+Port
* 外网访问容器里的服务:外网域名——>外网SLB——>API网关——>容器service的HOSTNAME——>Pod

#踩过的坑

日志搜集:因为Pod来回漂移,我们采用了阿里云的NAS存储(NFS)作为PV;日志文件会被共享,并被抢占文件句柄,导致日志里有乱码;我们的处理方式是日志中间加一个hostname,例如:interface.paris-interface-service-c-44-bx52z.debug,日志收集时flume匹配interface..debug。
* 监控:OpenShift自身的监控是使用Hawkular+Cassandra+Heapster,只有容器的CPU、内存和网络的监控。Pod版本的一致性、events监控(包括imge的生命周期、Pod的生命周期、服务健康检查等)、PV的监控等,都需要通过API来获取并加入到自建的监控平台
* CronJob不太稳定,crash时间比较长的情况下,Job下一次执行时间延迟过久,导致不再执行。这种情况只能删除后重新部署Job。
* 版本的控制,Deployment需要通过配置里的image的hash值,找到对应的build(build信息里记录了代码版本信息,包括commit、commit message、更新人和提交时间等);回滚的时候要选择代码版本对应的build,然后再找到build对应的Deployment配置进行部署,这些都通过自建的变更平台来管理。

#我们正在做的事情

  1. 将API网关替换成Kong,目前已经测试了大部分的功能,但Docker版本的稳定性比较差,QPS在很小的情况下也有很多5XX的错误。解决方案:在Kubernetes的Node节点上部署
  2. 由于现在对外业务发展比较快,计划通过模板、环境变量和配置中心来创建一整套的Applications,类似于SaaS服务。
  3. 打通QA,研发人员从提交代码转换到交付一个线上可用的image

#OpenShift对容器化PaaS平台实践的借鉴意义

  1. S2I自动构建服务镜像,实现自动化的流程。
  2. 部署模板,根据某一类型服务进行快速部署deployment(例如Python的模板、Golang的模板等);在每一个版本的deployment配置中,img会使用镜像的hash值(例如:docker-registry.default.svc:5000/XXX/XXX@sha256:899007bXXXXXXXX95f561203cba);回滚时选择对应版本的deployment配置进行重新部署即可达到回滚的目的。
  3. Router服务使用haproxy(支持AB部署,及流量分配),Router根据Route定义的规则,找到请求所含域名相关联的Service的容器,并将请求转发给容器实例。容器实例除了请求后返回数据,还会通过Router将数据返回给调用的客户端。
  4. LDAP用户管理等多用户管理,通过clusterrole/clusterrolebinding(全局资源权限)、role/rolebinding、serviceaccount、oauthaccesstoken等来实现权限分配。

#Q&A
Q:请问通过OpenShift怎么做高可用?

A:OpenShift的高可用是跟Kubernetes的高可用一致的,就是延用了Kubernetes的Service IP来做服务发现和负载均衡;另外,前面还有DNS服务,(例如:Hostname: service.abc-platform.svc,后端就是指向的Cluster IP)。



Q:image的版本管理怎么做?配置的版本管理怎么做?

A:OpenShift本身会创建一个内部的Registry,S2I会根据Build来更新image,image的版本是通过sha256的值对应Build的版本号(例如:Build版本#24对应registry.intra.XXXXX.com/abc/abc-platform@sha256:b41c8XXXXX)。



Q:OpenShift的网络方案性能方面有多少损耗?与Kubernetes的其他网络方案如Calico相比如何?

A:OpenShift的网络方案是选择的OVS,性能上肯定是不如Calico,但对于我们的服务足够了。目前跑了有1000+个常驻Pod,没有出现网络瓶颈。



Q:如果要做多机房部署,OpenShift能搞吗?

A:多机房,只要网络能通并比较稳定,是没有问题的。我们的部署方案就是在阿里云上的多个可用区部署的。而且,实际部署中也是鼓励多机房部署的,服务容灾要求一个服务要分布在多个可用区(或者机房)。



Q:你们平台性能怎么测试的,如何集成?

A:平台性能测试分两部分。一个是在集群内部不经过Nginx直接压测SVC,另外一个是在集群外部压测内网域名或者公网域名。



Q:我们之前用自己的镜像仓库,会报tls认证错误,不知道你们有没有遇到过?

A:我们自己创建的镜像仓库是用Harbor来建的,只是存储了内部服务的基础镜像。OpenShift集群内的镜像仓库有自己的认证,是通过Secrets管理对应权限的token(用户级别的权限是通过LDAP统一认证的)。S2I创建服务镜像的时候就是通过这个来获取权限的。还是比较稳定的,没有出现过tls的问题。



Q:生产环境的节点磁盘是怎么分区分配的?

A:生产环境的节点磁盘分为两类,一个是系统盘(40G),另一个是数据盘(100G);Docker的所有镜像、实例等存储都是放在数据盘,服务的日志是挂载的PV,PV是使用的阿里云的NAS存储。



Q:我看你们在集群访问前加了个API Gateway,用了OpenResty,这个能详细介绍下不?

A:可以简单理解为一个自建的Ingress,底层是Nginx+Lua实现的;我们已经在逐渐使用Kong来代替OpenResty了。Kong底层是Nginx+Lua+PostgreSQL,界面管理是使用的Konga。选择Kong和Konga的理由是:1、Kong的管理和插件比较方便,还支持自定义插件;2、Konga的界面更友好,管理、更新都比较方面。



Q:看到你们是3 Master高可用,请问Kubernetes–Master挂了一台,OpenShift如何恢复,etcd是否有做备份?

A:我们做过演练,Master挂一台或者两台,甚至三台全挂,也不会影响已经运行的服务。只要etcd没有问题,重启Master或者新增一台Master都可以快速恢复集群。etcd的备份是做的全量备份,上传到我们的备份存储中。



Q:内网域名是怎么跨VPC和经典网络的?

A:内网域名的DNSPod解析是解析到VPC网络的一台ECS机器上,通过Nginx来管理。经典网络的ECS服务集群可以在前面挂载一个VPC网络的SLB,Nginx的对应Server Name的Upstream设为这个VPC的SLB即可。



Q:监控和告警好像没有提到,感觉OC这块似乎缺失?
A:OpenShift本身有很多服务状态,image的Build、pull、push等状态,以及Pod的各种状态;另外还有强大events。我们是通过OpenShift的API来获取这些信息,同步到Open-Falcon上来处理报警信息的。

Q:日志统一生产、采集用了什么方式能介绍一下吗?

A:日志是一个比较疑难的问题;我们的解决方案是,所有的日志都打到同一个NaS盘上,服务名来做该服务的日志路径。多个Pod打同一个日志的时候,会出现乱码。我们的解决方案是在日志中间加hostname,例如haadeers.melon-crm-b-9-nb9d5.log,melon-crm-b-9-nb9d5为Pod的hostname。日志收集,是几台ECS机器挂载改NAS盘,启动flume来收集。



Q:业务应用的自动化发布你们是怎么设计的,能否详细说说思路?

A:我们是自己定义了S2I的流程。Build镜像统一在代码仓库里加一个Makefile,定义一个assemble的role,来做初始化(依赖包的安装、nodejs的Build等等,这里还是比较灵活的)。自动发布:我们是自定义了部署模板,分享的内容里有(只需要简单的几步就可以部署一套服务)。



以上内容根据2018年12月4日晚微信群分享内容整理。分享人徐义义,智融集团容器化PaaS平台负责人,从事云计算开发、设计6年(其中4年私有云,2年公有云<京东云>),目前负责公司内部PaaS平台的建设与应用的变更交付。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesd,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

Openshift使用openvswitch实现多租户网络隔离的理解

yekaifeng 发表了文章 • 0 个评论 • 1515 次浏览 • 2018-09-29 20:06 • 来自相关话题

前 言 我们在使用openshift的过程中,当调整网络,偶尔会遇到曾经能通的namespace突然间不通了。这个问题一直困扰着我们。后来通过调研,发现这是网络隔离方式导致的。这篇文章希望能对遇到类似困惑的同学带来帮助。 ...查看全部
前 言

我们在使用openshift的过程中,当调整网络,偶尔会遇到曾经能通的namespace突然间不通了。这个问题一直困扰着我们。后来通过调研,发现这是网络隔离方式导致的。这篇文章希望能对遇到类似困惑的同学带来帮助。

正 文

当前我们使用openshift提供的ovs-multitenant plugin作为网络解决方案。它具备基于VXLAN的多租户隔离能力。每个项目(projects)被分配一个唯一的ID,对应VXLAN的VNID, 缺省项目之间的POD是不能互相通信的。用户可以通过oc adm pod-network 子命令对租户网络进行管理。

pic1.jpg


每个project对应一个NETID (即VXLAN VNID),创建新project也会分配一个新的VNID。

    # oc get netnamespaces
NAME NETID EGRESS IPS
project1 5910689 []
project2 4296943 []
project3 8988879 []


要打通两个project之间的网络连接,使用join-projects 子命令。执行后它们的NETID设置成相同, project1 的NETID被改成project3的。这样具备相同VNID的两个项目就可以互相通信了。

pic2.jpg


原来
    # oc adm pod-network join-projects --to=project2 project1
# oc get netnamespaces|grep project
project1 4296943 []
project2 4296943 []
project3 8988879 []


改变后
    # oc adm pod-network join-projects --to=project3 project1
# oc get netnamespaces|grep project
project1 8988879 []
project2 4296943 []
project3 8988879 []


然而, 这种处理方式有可能导致原来与project1互通的项目,无法继续通信了。比如project1与project2原来是互通的,有一样的NETID。如果让project1加入project3, 后果是1跟2变成不通了。解决办法是把2也加入3。也许我们本来并不希望2, 3之间能够相互可见。所以在操作时,必须对这种组网方式有所了解。在规划网络时,应该以环境类型作为隔离标准,把具有相互通信需求的项目规划使用相同的NETID, 区分 开发/测试/预发布/正式环境。

要把两个project重新隔离起来, 使用isolate-projects 子命令。执行后 project3 NETID不变, project1 被分配新的NETID。假如还有一个或多个project在使用这个NETID,那么project1, project3都被分配新的NETID。

    # oc adm pod-network isolate-projects project1 project3
# oc get netnamespaces|grep project
project1 14486461 []
project2 4296943 []
project3 8988879 []


假如某个项目(例如日志),需要被所有项目访问,那么使用 make-projects-global 子命令。执行后把项目的NETID设成0, 相当于可被VXLAN无条件转发。

pic3.jpg


    # oc adm pod-network make-projects-global project1
# oc get netnamespaces|grep project
project1 0 []
project2 4296943 []
project3 8988879 []


集群服务基础组件适用这种场景,例如日志,统计,调用链,等等。

当红炸子鸡区块链,如何实现企业级部署?

博云BoCloud 发表了文章 • 0 个评论 • 839 次浏览 • 2018-05-25 15:50 • 来自相关话题

随着区块链技术从概念到落地的发展趋势,从2017年开始区块链技术已经有了初步的与现实商业场景结合的可能,这个热度在2018年升到顶端,区块链技术在全球开始部署应用。而2018年的中国,舆论和资本在区块链领域双管齐下,各行业掀起了一场区块链技术应用的创新运动。 ...查看全部
随着区块链技术从概念到落地的发展趋势,从2017年开始区块链技术已经有了初步的与现实商业场景结合的可能,这个热度在2018年升到顶端,区块链技术在全球开始部署应用。而2018年的中国,舆论和资本在区块链领域双管齐下,各行业掀起了一场区块链技术应用的创新运动。


目前,众多区块链应用企业用户的选择之一是基于Docker+Kubernetes来实现 Hyperledge 应用部署,然而搭建一套 Hyperledge 的应用环境其实并不是一件轻松的事情。

在这个搭建过程中,工程师要面临一系列繁琐的工作。首先,环境的搭建要进行 Kubernetes 环境配置,其次包括 Hyperledge Fabric 应用的下载、文件合并、NFS 等服务器的搭建,并配置 Template 以及 Pythony 等应用的安装,以及 Samples 测试的应用部署也是同样需要应用文件准备、修改环境变量、应用安装、实例化操作、数据初始化等等一系列的操作,都是极为繁琐的工作内容,所以当工程师一步步在敲击命令的过程中一不小心就可能出错。

为支持各行业企业基于区块链的创新业务场景,BoCloud博云容器云平台BeyondContainer 1.7.1版本提供了基于 helm 的 Chart 实例部署,支持企业级区块链业务部署和管理,助力企业实现业务创新。

容器云平台BeyondContainer 1.7.1同样基于 Docker+Kubernetes 实现 Hyperledger 应用部署。并且以上那些繁琐的工作,平台已经帮用户进行了简化,把工程师从繁琐枯燥的命令行敲击中解脱出来。BoCloud博云容器云平台的应用商店内嵌入了区块链 Chart 类型应用,即IBM的开源区块链项目 Hyperledge Fabric,同时平台内也嵌入了镜像列表供用户选择。



BeyondContainer 1.7.1版本通过 helm 方式简化 Kubernetes 应用部署,支持一键式区块链应用部署、支持编排 helm 文件执行自定义部署、支持区块链应用的集群化部署。同时,可视化图形界面提供的自动化部署也大大简化了用户操作,平台提供给用户可替换的变量如镜像的地址和版本、Orderer 信息、NFS 信息、端口等。应用部署的整个执行过程流水线式自动完成,避免误操作带来的各种困扰,并且支持实时查看执行日志,这无疑是更高效、更安全的选择。

同时,BeyondContainer 1.7.1版本在支持 GPU 资源调度、用户管理、应用生命周期管理、企业流程管理、用户认证、系统可靠及安全性功能等方面提升了产品能力。

BeyondContainer

BeyondContainer是BoCloud博云容器云产品,产品基于微服务思想和 DevOps 理念,利用 Docker 基础平台、DevOps 组件、微服务 PaaS 中间件等功能模块提供对应用的构建、测试、部署、更新、运维管理的应用全生命周期管理功能。实现基于容器的应用持续集成和快速部署、秒级弹性伸缩、业务可用性保障等关键特性,以帮助企业应对互联网时代业务快速发展的需要。

BeyondContainer 1.7.1

BeyondContainer 1.7.1版技术底层基于OpenShift 3.6版本,同时兼容支持Kubernetes 1.8及以上版本、Docker 17之后更新的社区版。新版本在用户管理方面提供了更友好的操作界面,提供多 OpenShift 集群管理能力,使用户能够基于一个统一管理界面对多集群进行管理,简化平台日常运维工作,实现容器平台的统一管理。在应用生命周期管理方面,基于 Openshift 的 Templates 模板能力实现了复杂多模块的应用的可视化编排部署,实现了系统级(而非容器级)的应用管理能力。

作为企业级的容器云平台,BeyondContainer通过产品业务价值,帮助企业提升其核心业务的商业效率。BeyondContainer具备平台运维、平台运营、应用运维三大方面的能力,可满足企业应用运维、持续集成、微服务开发运维等多种应用场景需求。产品主要功能包括:


  • 支持区域->租户->项目->应用的层级管理
  • 基于角色的仪表盘
  • 基于 Jenkins 的 CI/CD 的流水线
  • 资源管理及对存储、集群、主机、仓库的全方位可视化管理
  • 集群,主机,容器,应用,审计,操作级日志
  • 集群,主机,组件,容器监控告警
  • 基于租户,项目的配额和计量计费
  • 全方位的镜像,容器,应用管理
  • 支持区块链应用
  • 支持 GPU 资源管理


BoCloud博云容器云平台BeyondContainer已在金融、电力、能源等行业中落地,帮助企业大幅缩短应用迭代周期,实现应用全生命周期管理,提升资源利率,产品可靠性、稳定性、安全性久经考验。博云同时为企业客户提供 DevOps 及微服务咨询服务,从开发、测试、部署、运维,帮助企业实现 DevOps 开发运维一体化,响应企业客户对业务需求和IT系统的快速升级的需求。


推荐阅读

BoCloud博云@2018 F5应用服务高峰论坛,容器云解决方案升高赋能
助力AI:博云容器云平台BeyondContainer支持GPU
QCon2018 | 微服务落地关键:设计比技术框架更重要

如何用EOS和OpenShift部署一个BaaS系统

alex_wang2 发表了文章 • 0 个评论 • 1712 次浏览 • 2018-05-21 22:00 • 来自相关话题

现在区块链技术很是热门,很多大公司都纷纷开辟了自己关于区块链的相关业务部门,比如数字金融,产品溯源等,那么什么是区块链技术呢?区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。在区块链技术的迭代过程中,大家公认比特币是区块链1. ...查看全部
现在区块链技术很是热门,很多大公司都纷纷开辟了自己关于区块链的相关业务部门,比如数字金融,产品溯源等,那么什么是区块链技术呢?区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。在区块链技术的迭代过程中,大家公认比特币是区块链1.0版本的应用,以太坊是2.0版本,现在在链圈炒的最为火热的就是自称是区块链3.0版本的EOS。它以BFT+DPos的公式机制和石墨烯网络为基础,号称公网上线以后能够达到3000-4000左右的TPS(比特币为6-7笔,以太坊是30-40笔,但是笔者亲测EOS的3.0版本是1000tps左右,可能是受环境和主机性能所限吧)。越来越多的人开始关注区块链技术,越来越多的企业级应用开始搬上区块链,现在我就带领大家搭建一套最简单的BaaS系统(block chain as a service:区块链即服务)。

当人们一说到某某即服务的时候总是想到PaaS、SaaS或者IaaS,没错!这次搭建的BaaS也和前面几个类似采用PaaS的设计架构,即用容器的方式搭建BaaS平台。提到容器,就不得不想到现在最流行的Docker+Kubernetes的方式,Docker提供了单机条件下的容器运行环境,Kubernetes提供了一套高可用的容器编排管理系统。本次使用的就是在代码级对Kubernetes做了升级和改造的,由Red Hat公司推出的OpenShift,它使用Kuberenetes作为其内核,对它网络功能做了补充,增加了Web界面,又对原有的Ingress做了优化和升级,所以我们选择它作为本次BaaS的基础平台。

BaaS平台部署图如下图所示:
server.png

1、首先开始制作EOS镜像

1)下面是EOS代码获取方法:
git clone https://github.com/EOSIO/eos --recursive

然后进入代码目录执行:
cd eos
./eosio_build.sh

2)在代码的EOS目录下会生成一个build目录,把EOS的二进制文件NodeOS、keosd、cleos等复制到要制作镜像的子目录下,下面是Dockerfile文件的内容,其中基础镜像选用了CentOS,这个系统的版本好要和刚才编译EOS的一致,否则可能会出现NodeOS无法运行的错误。
From docker.io/centos
ADD cleos /cleos
ADD nodeos /nodeos
ADD genesis.json /genesis.json
ADD config.ini /config.ini
ADD nodeosd.sh /opt/eosio/bin/
USER 0
CMD /opt/eosio/bin/nodeosd.sh

3)除了有必要的二进制文件外,我们还要为NodeOS启动配置必要的启动脚本和两个配置文件。

下面是启动脚本:
#!/bin/sh
mkdir -p /opt/eosio/config-dir
mkdir -p /opt/eosio/data-dir
mkdir -p /opt/eosio/bin
cp /config.ini /opt/eosio/config-dir
cp /genesis.json /opt/eosio/config-dir
cp /cleos /opt/eosio/bin
cp /nodeos /opt/eosio/bin
cp -r /contracts /opt/eosio/bin

CONFIG_DIR="--config-dir=/opt/eosio/config-dir"
DATA_DIR="--data-dir=/opt/eosio/data-dir"
#replace agent name
sed -i 's/AGENT-NAME/'$HOSTNAME'/g' /opt/eosio/config-dir/config.ini

#replace producer name
sed -i 's/EOSIO/'$BP'/g' /opt/eosio/config-dir/config.ini
#replace p2p-peer-address
sed -i 's/EOSIO/'$BP'/g' /opt/eosio/config-dir/config.ini

#replace p2p-server-address
sed -i 's/EOSIO/'$BP'/g' /opt/eosio/config-dir/config.ini

sed -i 's/P2P_PEER_ADDRESS/'${BP_LIST}'/g' /opt/eosio/config-dir/config.ini

sed -i 's/P2P_SERVER_ADDRESS/'${HOSTNAME}'/g' /opt/eosio/config-dir/config.ini

#replace keys
sed -i 's/PRIVATE-KEY/'${PRIVATE_KEY}'/g' /opt/eosio/config-dir/config.ini
sed -i 's/PUBLIC-KEY/'${PUBLIC_KEY}'/g' /opt/eosio/config-dir/config.ini

exec /opt/eosio/bin/nodeos -e $CONFIG_DIR $DATA_DIR $@

genesis.json文件内容:
# cat genesis.json
{
"initial_timestamp": "2018-03-02T12:00:00.000",
"initial_key": "EOSxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"initial_configuration": {
"max_block_net_usage": 1048576,
"target_block_net_usage_pct": 1000,
"max_transaction_net_usage": 524288,
"base_per_transaction_net_usage": 12,
"net_usage_leeway": 500,
"context_free_discount_net_usage_num": 20,
"context_free_discount_net_usage_den": 100,
"max_block_cpu_usage": 100000,
"target_block_cpu_usage_pct": 500,
"max_transaction_cpu_usage": 50000,
"min_transaction_cpu_usage": 100,
"max_transaction_lifetime": 3600,
"deferred_trx_expiration_window": 600,
"max_transaction_delay": 3888000,
"max_inline_action_size": 4096,
"max_inline_action_depth": 4,
"max_authority_depth": 6,
"max_generated_transaction_count": 16
},
"initial_chain_id": "0000000000000000000000000000000000000000000000000000000000000000"
}

# cat config.ini
# File to read Genesis State from (eosio::chain_plugin)
genesis-json = "genesis.json"

# override the initial timestamp in the Genesis State file (eosio::chain_plugin)
# genesis-timestamp =

# the location of the block log (absolute path or relative to application data dir) (eosio::chain_plugin)
block-log-dir = "blocks"

# Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints. (eosio::chain_plugin)
# checkpoint =

# Override default WASM runtime (eosio::chain_plugin)
# wasm-runtime =

# Maximum size MB of database shared memory file (eosio::chain_plugin)
shared-memory-size-mb = 1024

# Track only transactions whose scopes involve the listed accounts. Default is to track all transactions. (eosio::history_plugin)
# filter_on_accounts =

# The local IP and port to listen for incoming http connections; set blank to disable. (eosio::http_plugin)
http-server-address = 0.0.0.0:8888

# The local IP and port to listen for incoming https connections; leave blank to disable. (eosio::http_plugin)
# https-server-address =

# Filename with the certificate chain to present on https connections. PEM format. Required for https. (eosio::http_plugin)
# https-certificate-chain-file =

# Filename with https private key in PEM format. Required for https (eosio::http_plugin)
# https-private-key-file =

# Specify the Access-Control-Allow-Origin to be returned on each request. (eosio::http_plugin)
# access-control-allow-origin =

# Specify the Access-Control-Allow-Headers to be returned on each request. (eosio::http_plugin)
# access-control-allow-headers =

# Specify if Access-Control-Allow-Credentials: true should be returned on each request. (eosio::http_plugin)
access-control-allow-credentials = false

# The actual host:port used to listen for incoming p2p connections. (eosio::net_plugin)
p2p-listen-endpoint = 0.0.0.0:9876

# An externally accessible host:port for identifying this node. Defaults to p2p-listen-endpoint. (eosio::net_plugin)
p2p-peer-address = P2P_PEER_ADDRESS:9876

# The public endpoint of a peer node to connect to. Use multiple p2p-peer-address options as needed to compose a network. (eosio::net_plugin)
p2p-server-address = P2P_SERVER_ADDRESS

# The name supplied to identify this node amongst the peers. (eosio::net_plugin)
agent-name = "AGENT-NAME"

# Can be 'any' or 'producers' or 'specified' or 'none'. If 'specified', peer-key must be specified at least once. If only 'producers', peer-key is not required. 'producers' and 'specified' may be combined. (eosio::net_plugin)
allowed-connection = any

# Optional public key of peer allowed to connect. May be used multiple times. (eosio::net_plugin)
# peer-key =

# Tuple of [PublicKey, WIF private key] (may specify multiple times) (eosio::net_plugin)
# peer-private-key =

# Maximum number of clients from which connections are accepted, use 0 for no limit (eosio::net_plugin)
max-clients = 25

# number of seconds to wait before cleaning up dead connections (eosio::net_plugin)
connection-cleanup-period = 30

# True to require exact match of peer network version. (eosio::net_plugin)
network-version-match = 0

# number of blocks to retrieve in a chunk from any individual peer during synchronization (eosio::net_plugin)
sync-fetch-span = 100

# maximum sizes of transaction or block messages that are sent without first sending a notice (eosio::net_plugin)
max-implicit-request = 1500

# Enable block production, even if the chain is stale. (eosio::producer_plugin)
enable-stale-production = false

# Limits the maximum time (in milliseconds) that is allowed a pushed transaction's code to execute before being considered invalid (eosio::producer_plugin)
max-transaction-time = 30

# Percent of producers (0-100) that must be participating in order to produce blocks (eosio::producer_plugin)
required-participation = 33

# ID of producer controlled by this node (e.g. inita; may specify multiple times) (eosio::producer_plugin)
producer-name = EOSIO

# Tuple of [public key, WIF private key] (may specify multiple times) (eosio::producer_plugin)
private-key = ["PUBLIC-KEY","PRIVATE-KEY"]

# Lag in number of blocks from the head block when selecting the reference block for transactions (-1 means Last Irreversible Block) (eosio::txn_test_gen_plugin)
txn-reference-block-lag = 0

# The path of the wallet files (absolute path or relative to application data dir) (eosio::wallet_plugin)
wallet-dir = "."

# Timeout for unlocked wallet in seconds (default 900 (15 minutes)). Wallets will automatically lock after specified number of seconds of inactivity. Activity is defined as any wallet command e.g. list-wallets. (eosio::wallet_plugin)
unlock-timeout = 900

# eosio key that will be imported automatically when a wallet is created. (eosio::wallet_plugin)
# eosio-key =

# Plugin(s) to enable, may be specified multiple times
# plugin =
plugin = eosio::producer_plugin
plugin = eosio::chain_api_plugin
plugin = eosio::wallet_api_plugin
plugin = eosio::http_plugin
plugin = eosio::history_api_plugin
plugin = eosio::history_plugin

4)运行下面命令生成image并推送到私有的Registry。
docker build . -t registryhost:5000/eos/eos
docker push registryhost:5000/eos/eos

2、EOS节点部署

1)当镜像推送到Registry后,我们就可以进行Yaml文件的编写并且配置了,下面是Yaml文件的内容:
at nodeos.yaml 
apiVersion: v1
kind: Template
metadata:
name: nodeos
annotations:
openshift.io/display-name: nodeos
description: nodeos of eos.
iconClass: icon-wildfly
tags: nodeos

objects:
[list]
[*]apiVersion: v1[/*]
[/list] kind: Service
metadata:
annotations:
description: Exposes and load balances the application pods
name: ${BP}
spec:
ports:
- name: http-8888-tcp
port: 8888
protocol: TCP
targetPort: 8888
- name: admin-9876-tcp
port: 9876
protocol: TCP
targetPort: 9876
selector:
name: ${BP}
type: ClusterIP

[list]
[*]apiVersion: v1[/*]
[/list] kind: Route
metadata:
name: nodeos-http
spec:
host: nodeos
to:
kind: Service
name: ${BP}
port:
targetPort: http-8888-tcp

[list]
[*]apiVersion: v1[/*]
[/list] kind: DeploymentConfig
metadata:
annotations:
description: Defines how to deploy the nodeos
name: ${BP}
spec:
replicas: 1
selector:
name: ${BP}
strategy:
type: Rolling
template:
metadata:
labels:
name: ${BP}
spec:
containers:
- image: "192.168.20.2:5000/eosio/eos"
env:
[list]
[*]name: BP[/*]
[/list] value: ${BP}
[list]
[*]name: PUBLIC_KEY[/*]
[/list] value: ${PUBLIC_KEY}
[list]
[*]name: PRIVATE_KEY[/*]
[/list] value: ${PRIVATE_KEY}
[list]
[*]name: BP_LIST[/*]
[/list] value: ${BP_LIST}

imagePullPolicy: Always
name: nodeos-bp
ports:
- containerPort: 8888
protocol: "TCP"
- containerPort: 9876
protocol: "TCP"
resources:
requests:
memory: 4096Mi
cpu: 2000m
dnsPolicy: ClusterFirst
restartPolicy: Always
securityContext:
- runAsUser: 0

parameters:
[list]
[*]name: BP[/*]
[/list] description: Block Producer
displayName: Block Producer
value: eosio
[list]
[*]name: PUBLIC_KEY[/*]
[/list] description: PUBLIC_KEY.
displayName: PUBLIC_KEY
value: EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
[list]
[*]name: PRIVATE_KEY[/*]
[/list] description: PRIVATE_KEY.
displayName: PRIVATE_KEY
value: 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
[list]
[*]name: BP_LIST[/*]
[/list] description: A IP list of Block Producer
displayName: A IP list of Block Producer
value: bp's hostname and port

2)有了Yaml文件,我们就可以把我们的Yaml文件推送到OpenShift中当成模版,然后通过模版进行EOS节点的实例化,这里面有一个问题需要注意,所有节点的公私钥不可能一样,需要通过模版的参数在实例化的时候传入容器里,然后启动NodeOS的时候,调用NodeOS启动脚本把传入的变量(每一个传入变量都会生成一个环境变量)替换到config.ini文件中,然后启动EOS的主进程NodeOS。当然,这里还有一个问题也需要注意,那就是4.1的EOS配置文件里需要配置两个参数,一个是自己自身的IP(p2p-server-address),和对端IP(p2p-peer-address)第一个地址可以用这个Pod的hostname代替,第二个可以通过指定之前节点的hostname来填充。

3)使用`oc create -f eos.yaml` 进行模版的部署,然后通过Web界面,选择刚才的模版,输入变量的内容,最后点击生成,如下图所示:
eos_template.png

3、既然环境已经设置完成,现在我们来初始化整个EOS的环境。

1)首先通过cleos wallet create创建一个默认的钱包,如果-n可以指定钱包的名字,这个钱包默认的存储路径是/root/eosio-wallet/目录下,看下面的输出:
# cleos wallet create 
"/usr/local/bin/keosd" launched
Creating wallet: default
Save password to use in the future to unlock this wallet.
Without password imported keys will not be retrievable.
"PW5J2SGNDmhZFiVnbZY6V669XLEksGs13KnhCnkuqQZScj5ieNV5j"

其中下面的是这个钱包的密码,需要记住,因为现在4.1版本的钱包900秒后就会自动上锁,需要这个密码来解锁。

2)创建一对公私钥,创建账户,并部署合约。

创建私钥公钥:
# cleos create key 
"/usr/local/bin/keosd" launched Private key: 5Kgh744izqDCNyuRZcBu4nMjgJbBwWBdqQZuQi53qPB9L69Cj9X Public key: EOS6CYWY1q4AAsVV3rzcT1GGQmZcg7yDnA6jZx2KUjHvmZWPv8DQg

创建代币账户:
#cleos -u http://nodeos:80 create account eosio eosio.token EOS6CYWY1q4AAsVV3rzcT1GGQmZcg7yDnA6jZx2KUjHvmZWPv8DQg EOS6CYWY1q4AAsVV3rzcT1GGQmZcg7yDnA6jZx2KUjHvmZWPv8DQg

部署代币合约:
#cleos -u http://nodeos:80  set contract eosio.token  eos/build/contracts/eosio.token/
Reading WAST/WASM from /eos_code/dawn-v4.1.0/eos/build/contracts/eosio.token/eosio.token.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 9ff87dffe3eb2eeac29a4afd8eec8faa563f6be43cf5142e71cd61d0c4b66244 8072 bytes 976 us
# eosio <= eosio::setcode {"account":"eosio.token","vmtype":0,"vmversion":0,"code":"0061736d01000000017f1560037f7e7f0060057f7e...
# eosio <= eosio::setabi {"account":"eosio.token","abi":"0e656f73696f3a3a6162692f312e30010c6163636f756e745f6e616d65046e616d65...
warning: transaction executed locally, but may not be confirmed by the network yet

创建代币:
#cleos -u http://nodeos:80  push action eosio.token create '["eosio","1000000000.0000 SYS",0,0,0]' -p eosio.token
executed transaction: 8d7e472fa7309abfa051eed367c552e3e3e8f283530dc3375761d7da2cb298e9 120 bytes 453 us
# eosio.token <= eosio.token::create {"issuer":"eosio","maximum_supply":"1000000000.0000 SYS"}
[quote]> warning: transaction executed locally, but may not be confirmed by the network yet

发布代币:
#cleos -u http://nodeos:80 push action eosio.token issue '["eosio","1000000000.0000 SYS","issue"]' -p eosio
cleos -u http://nodeos:80 push action eosio.token issue '["eosio","1000000000.0000 SYS","issue"]' -p eosio
executed transaction: bdf099816554de5849f030a1d26cc40ade29b74c87aff4180796c7c26507d677 128 bytes 343 us
# eosio.token <= eosio.token::issue {"to":"eosio","quantity":"1000000000.0000 SYS","memo":"issue"}
warning: transaction executed locally, but may not be confirmed by the network yet

查询代币余额:
#cleos -u http://nodeos:80 get currency balance eosio.token eosio
1000000000.0000 SYS

创建账号:
#cleos -u http://nodeos:80  create account eosio alex EOS6CYWY1q4AAsVV3rzcT1GGQmZcg7yDnA6jZx2KUjHvmZWPv8DQg EOS6CYWY1q4AAsVV3rzcT1GGQmZcg7yDnA6jZx2KUjHvmZWPv8DQg

EOS转账 :
#cleos -u http://nodeos:80  push action eosio.token transfer '["eosio", "alex", "10000.0000 SYS", ""]' -p eosio
[/quote]

3)创建BP,所谓BP就是EOS上的出块节点(block producer),到6月2日 EOS主网上线,全网应该一共21个BP节点,每个节点都要经过EOS的投票选举产生,现在我们的环境里还没有一个被选举的BP节点,所以需要通过抵押EOS的方式进行投票,当投票超过150M个EOS的时候,得票最多的前21个节点就是BP节点了,当然我们的BaaS受部署规模限制,不一样定要到21个节点,所以我们选举4个BP节点,执行如下命令:
部署系统合约
# cleos -u http://nodeos:80 set contract eosio eos/build/contracts/eosio.system
Reading WAST/WASM from ./build/contracts/eosio.system/eosio.system.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: cd1975d3628c1306f1fb58e49d5474d61c829bad42bffb4aeca7488bb041bd4d 40488 bytes 3969 us
# eosio <= eosio::setcode {"account":"eosio","vmtype":0,"vmversion":0,"code":"0061736d0100000001ba022f60027f7e0060067f7e7e7f7f...
# eosio <= eosio::setabi {"account":"eosio","abi":"0e656f73696f3a3a6162692f312e30050c6163636f756e745f6e616d65046e616d650f7065...
[quote]> warning: transaction executed locally, but may not be confirmed by the network yet

创建账户bp1作为出块节点:
cleos -u http://nodeos:80 system newaccount eosio bp1 EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH --stake-net '500.00 SYS' --stake-cpu '500.00 SYS'
1697946ms thread-0 main.cpp:419 create_action ] result: {"binargs":"0000000000ea3055000000000000423d00200000"} arg: {"code":"eosio","action":"buyrambytes","args":{"payer":"eosio","receiver":"bp1","bytes":8192}}
1697948ms thread-0 main.cpp:419 create_action ] result: {"binargs":"0000000000ea3055000000000000423d404b4c00000000000453595300000000404b4c0000000000045359530000000000"} arg: {"code":"eosio","action":"delegatebw","args":{"from":"eosio","receiver":"bp1","stake_net_quantity":"500.0000 SYS","stake_cpu_quantity":"500.0000 SYS","transfer":false}}
executed transaction: bbdeb7aa13953746e93c47b53729ce36ca2d89f16d87b872ebbacd71c2f4fc6b 336 bytes 1831 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"bp1","owner":{"threshold":1,"keys":[{"key":"EOS7FNpU6P3yhT7bkf3fs7aNUxb3y...
>>
# eosio <= eosio::buyrambytes {"payer":"eosio","receiver":"bp1","bytes":8192}
>>
# eosio <= eosio::delegatebw {"from":"eosio","receiver":"bp1","stake_net_quantity":"500.0000 SYS","stake_cpu_quantity":"500.0000 ...
>> warning: transaction executed locally, but may not be confirmed by the network yet

导入刚才bp1的私钥:
# cleos -u http://nodeos:80 wallet import 5Je4PsFWinBPXgWQasz7usbv8MLu5AbkcjU41JQr4Suhh34yKPu
imported private key for: EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH

注册bp1为bp节点:
# cleos -u http://nodeos:80 system regproducer bp1 EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH http://bp1:8888
2155333ms thread-0 main.cpp:419 create_action ] result: {"binargs":"000000000000423d000336d1c7309f8c40f9a2d7bf11b6ba3f6b1c699a67d2ad26c003e5313ff1ce1c940f687474703a2f2f6270313a383838380000"} arg: {"code":"eosio","action":"regproducer","args":{"producer":"bp1","producer_key":"EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH","url":"http://bp1:8888","location":0}}
executed transaction: 294568630bd6c733db853ba2aac2245ffeb199d47767b80ddadc1cbf421c6d46 152 bytes 623 us
# eosio <= eosio::regproducer {"producer":"bp1","producer_key":"EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH","url":"http...
>> warning: transaction executed locally, but may not be confirmed by the network yet

eosio抵押EOS,投票给bp1:
#cleos -u http://nodeos:80 system delegatebw bp1 bp1 '100000000.0000 SYS' '50000000.0000 SYS' --transfer[/quote]


460701ms thread-0 main.cpp:1083 operator() ] act_payload: {"from":"eosio","receiver":"bp1","stake_net_quantity":"100000000.0000 SYS","stake_cpu_quantity":"50000000.0000 SYS","transfer":true}
2460702ms thread-0 main.cpp:419 create_action ] result: {"binargs":"0000000000ea3055000000000000423d0010a5d4e800000004535953000000000088526a74000000045359530000000001"} arg: {"code":"eosio","action":"delegatebw","args":{"from":"eosio","receiver":"bp1","stake_net_quantity":"100000000.0000 SYS","stake_cpu_quantity":"50000000.0000 SYS","transfer":true}}
executed transaction: 98314189243cf8a38619e9f887c5430d9285f19a6a2977eccc07d838611dd17a 144 bytes 676 us
# eosio <= eosio::delegatebw {"from":"eosio","receiver":"bp1","stake_net_quantity":"100000000.0000 SYS","stake_cpu_quantity":"500...
[quote]> warning: transaction executed locally, but may not be confirmed by the network yet

用抵押的EOS投票给bp1:
#cleos -u http://nodeos:80 system voteproducer prods bp1 bp1
2531150ms thread-0 main.cpp:419 create_action ] result: {"binargs":"000000000000423d000000000000000001000000000000423d"} arg: {"code":"eosio","action":"voteproducer","args":{"voter":"bp1","proxy":"","producers":["bp1"]}}
executed transaction: 15c7cd6bd19f41654abb2d4f52f23953f80fd76b2698ef2a713d67c6fa022ee7 120 bytes 905 us
# eosio <= eosio::voteproducer {"voter":"bp1","proxy":"","producers":["bp1"]}
>> warning: transaction executed locally, but may not be confirmed by the network yet

到这一步一个最最基本的EOS BaaS就算部署完毕了。[/quote]

此时你就可以在你的终端上看到类似如下输出:
3302000ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 00000d279a442987... #3367 @ 2018-05-22T15:55:02.000 signed by bp1 [trxs: 0, lib: 3366, confirmed: 0]

hello-openshift origin

mgnetwork 发表了文章 • 0 个评论 • 1281 次浏览 • 2018-03-15 21:27 • 来自相关话题

环境 : 三台centos 7.4 mast 4G 内存 4 VCPU 200.com node 2G 内存 2vcpu 201.com 202.com systemc ...查看全部
环境 :
三台centos 7.4
mast 4G 内存 4 VCPU 200.com
node 2G 内存 2vcpu 201.com 202.com

systemctl disable firewalld
systemctl stop firewalld
环境1.png


环境2.png


环境3.png


安装ansible 、ssh互信、配置ansible hosts 、etcd安装

yum install -y ansible
ssh互信 (三台centos一样)
ssh-keygen -t srt
ssh-copy-id 200.com
ssh-copy-id 201.com
ssh-copy-id 202.com
etcd安装
yum install -y etcd
systemctl enable etcd.service
systemctl start etcd.service
我这里是只有一台etcd,多台请用源码安装


配置ansible hosts
vim /etc/ansible/hosts
[abcd]
# 这个我一做测试的
200.com
201.com
202.com

[OSEv3:children]
masters
nodes
etcd

[OSEv3:vars]
ansible_ssh_user=root
openshift_deployment_type=origin
openshift_disable_check=disk_availability,docker_storage,memory_availability,docker_image_availability,package_version
#disable-check这里是不检查项目,mast要求默认16G内存、node8G内存,现在docker要1.13版本,我#这里把检测关了,所以还是用1.12.6版本

[masters]
200.com

[nodes]
200.com openshift_schedulable=True openshift_node_labels="{'region': 'infra'}"
201.com openshift_node_labels="{'region': 'infra', 'zone': 'east'}"
202.com openshift_node_labels="{'region': 'infra', 'zone': 'west'}"

[etcd]
# etcd主机
200.com
保存退出

测试ansible

测试1.png


我使用的openshift-ansible-3.7.0-0.198.1.tar.gz 这个包 ,可以在github 可以找到
tar -zxvf openshift-ansible-3.7.0-0.198.1.tar.gz

运行安装脚本
ansible-playbook /abcd/openshift-ansible-openshift-ansible-3.7.0-0.198.1/playbooks/byo/config.yml

这里会自动安装,如果有问题会 飘红 (我安装时没有问题,有问题也是 检测问题,如果是可以在ansible hosts 里把那个关了),必须连网哦。

成功后 查看一下集群

成功1.png


https://200.com:8443
前提 能正常解析到200.com这个地址。不能就用ip或者在windos hosts文件里加一条

web登录 用户名密码 dev

创建一个项目



项目名.png



项目名2.png



项目3.png





项目4.png



这里我们可以看到 运行详细信息 如地址和端口

直接在某台宿主机上访问


成功1.png



Hello OpenShift!












DockOne微信分享(一五七):一个可供参考的企业应用容器化实践案例

alex_wang2 发表了文章 • 2 个评论 • 2978 次浏览 • 2018-01-10 21:37 • 来自相关话题

【编者的话】本次分享分为两个部分,第一部分比较常规,介绍如何用OpenShift搭建自动化测试、开发环境。第二部分介绍了在容器使用过程中遇到的问题,以及应对方案。作者在原有的OpenShift Router仅支持7层协议的基础上,对其进行支持4层协议的扩充,联 ...查看全部
【编者的话】本次分享分为两个部分,第一部分比较常规,介绍如何用OpenShift搭建自动化测试、开发环境。第二部分介绍了在容器使用过程中遇到的问题,以及应对方案。作者在原有的OpenShift Router仅支持7层协议的基础上,对其进行支持4层协议的扩充,联合CoreDNS,让平台的使用者不用记录灵活多变的IP地址,仅需自己定义自己熟悉的hostname(或者由平台自动生成)就访问对应资源,让使用者几乎没有感知的使用容器还是物理机或者虚拟机。

在这里跟大家分享一下容器使用方面的经验,说的不好,肯定有不对的地方,还希望大家多多批评、指正。
#OpenShift简介
我们都知道在容器编排领域,有3个最著名的编排系统,它们分别是Docker Swarm,Mesos和Kubernetes。其中Docker Swarm是Docker公司自己推出的容器管理和编排系统,由于起步较晚,目前大规模的应用尚不多见。

Mesos是Apache下的开源分布式资源管理框架,它被称为是分布式系统的内核。Mesos最初是由加州大学伯克利分校的AMPLab开发的,后在Twitter得到广泛使用。

Kubernetes和Mesos同样是起源于Google的Borg,是现在应用最广,用户数和社区活跃度最高的容器编排系统。OpenShift也是出于这个考虑,最终选择Kubernetes作为底层架构。OpenShift是一个开源容器云平台,他是基于主流的容器技术Docker和Kubernetes构建的云平台,作为一个开源项目,他已经有5年的历史,其最早的定位是一个应用云平台(Platform as a Service)。在Docker时代来临之前,各个厂商和社区项目倾向构建自己的容器标准,比如Cloud Foundry的Warden、OpenShift的Gear,但是在Docker成为主流及社区的技术发展方向之后,OpenShift快速的拥抱了Docker,并推出了市场上第一个基于Docker及Kubernetes的容器PaaS解决方案。当然很多人会有疑问,OpenShift与Docker及Kubernetes的关系究竟是什么

OpenShift是基于容器技术构建的一个云平台。这里所指的容器技术即包含Docker及Kubernetes。如下图所示:
93045234accd1ea8fcaa59f8776f326f.png

OpenShift底层以Docker作为容器引擎驱动,以Kubernetes作为容器编排引擎组件。OpenShift提供了开发语言、中间件、自动化流程工具及界面等元素,提供了一套完整的基于容器的应用云平台。OpenShift有三个版本,分别是企业版,社区版和在线版,我们这里使用社区版(origin版)用做我们企业内部的部署
#部署介绍
我们的架构比较典型,前端有2个负载均衡器(HAProxy),两个负载均衡通过Heartbeat共享一个VIP,后面连接3个master node,每个master node上分别运行了一个etcd,后面连接了N个slave node,因为集群中所有的状态都会持久化到etcd中,所以api server基本上是无状态运行,当有节点需要和master node打交道时,先访问这个VIP,然后连接到vip后面的一个haproxy上,haproxy再选择其中一个master node上的api server进行与etcd通信,下面是我的HAProxy的配置文件`haproxy.conf`:
backend atomic-openshift-api
balance source
mode tcp
server master0 10.10.xx.xx:8443 check
server master1 10.10.xx.xx:8443 check
server master2 10.10.xx.xx:8443 check

同时,其实我们如果集群里面有其他的高可用的需求,比如我们使用了Harbor做为私有镜像仓库,三个镜像仓库配置了Replication规则,我们通过VIP推送一个镜像到3个中的任意一个仓库,其他两个仓库也会存在我们的同样镜像,从而实现高可用。下面是Haproxy的配:
`harpoxy.conf`

backend harbor-proxy
balance source
mode tcp
server harbor1 10.10.xx.xx:80 check
server harbor2 10.10.xx.xx:80 check
server harbor3 10.10.xx.xx:80 check

#运行流程
第一个是每天定时的作业,这个比较简单,我们快速过一下:首先在Jenkins里做定时任务,时间定为0:30,从代码下载代码,在编译之前进行代码风格检查(snoar)和执行单元测试(junit),然后进行编译,把编译出的二进制包和事先写好的`Dockerfile`进行docker build,然后把输出的docker image push到Harbor的镜像仓库,同时这个编译的jenkins job会触发,测试部署job,测试部署job执行时会触发OpenShift里的test project,我们把PullPolicy设置成alway是,每次执行从新部署,都会触发新的镜像的拉去,从而达到从新部署的目的,测试项目分为两个阶段,自动化测试和压力测试,自动化测试采用selenume和robot结合的方式,在测试结束之后生成测试报告。压力测试使用jmeter。结束之后也会以邮件的方式发送给订阅者。如果所有测试通过之后,测试job会先打给这个image打一个tag,然后push到Harbor的ready项目中。同时发送通知邮件。

待第二天测试人员需要测试的时候,它们会通过我们的release manager去拉去ready项目中最新的image,然后部署到它们自己的project里,进行特定功能的手工测试。整体流程大概如此。

第二个是针对开发测试环境的,由于资源有限,我们的开发人员也用容器的方式部署每个人的开发环境,这里OpenShift/Kubernetes多租户的优势就体现出来了。在我们的容器平台上,每一个开发人员对应一个账号,每一个账号就是我们OpenShift里的一个project,由于每一个project都可以做资源限制,所以只要给大家按照需求分配好固定份额,就可以做到资源的共享和互不干涉。举个例子,比如说,我们有一个项目,它分为前端和后端。前端人员开发的时候,他只关心前端代码,后端代码基本仅仅是调用而不做修改,那么这个时候我们就可以把前端打成一个docker image,后端打成一个docker image(这里是举个例子,实际情况可能比较复杂,可能前后端都不止一个image),然后在前端开发人员写完前端代码后,把自己的代码通过NFS共享给Release Manager,(NFS挂在可以是实现挂载好的,Windows、Linux、MacOS均可以,代码就可以直接存在上面),然后点击Relea se Manager上面的前端发布按钮,就会触发Jenkins中的一个front-end job,它会完成代码编译,打包和镜像推送,最后在这个开发人员对应的project里从新部署前端代码的整个过程,接着这个开发人员就可以通过url(比如http://test/frontend-dev1)去访问它刚刚部署的页面。如果是后端人员也会有同样的流程,具体就不在赘述了。具体说明一点,由于OpenShift集成了Router功能,也就是类似于Kubernetes里的Ingress,但是它是用Haproxy实现的,并且能够在Yaml文件中对Router进行配置,刚才引用的url我们可以配置成http://{集群名}/{项目名},所有整个集群都是用7层协议通过Router对外提供访问。

生产和预发布环境,这个环境的配置除了要求高可用之外,就是环境的硬件配置,一般意义上来将,预发布的环境因该不高于正式生产环境。当决定对测试通过的镜像版本进行上线时,首先会用docker tag把它换成tag换成pre-release,经过压力测试和手工最后的verify就可以发布到正式环境中了。
#调试问题的解决
下面我们着重讲一下在实际部署和使用容器过程中,遇到了哪些问题,以及是如何去解决:

1、有一个问题也许很多刚刚使用容器的公司都会遇到,就是开发人员喜欢把容器当成虚拟机用,在遇到程序bug的时候,很多开发者都喜欢SSH到容器里,亲自看看log,或者是尝试替换一下它们程序的debug版(这里的所谓debug版,就是开发人员在代码里加入一些调试信息或者print一下log),然后重新启动应用。这里我们不推荐给容器内部安装SSHD,因为首先容器的IP是临时分配的,我们无法确定的告诉开发者它这一次的IP地址是多少,即使告诉了他也不一定能够访问的到(我们的容器系统网络层和外界不是一个网段),那么如何解决这个问题呢?我们开始也是尽量说服开发者,学会用log去debug,因为我们前面已经把log通过ES进行了收集,用Kabina可以去查看,但是没有办法,有些开发人员还是习惯自己去cat或者vim打开日志文件。所以这里我们就用到了OpenShift里提供的一个oc子命令oc get pod和oc exec,前者用来得到当前用户所在的项目中的Pod列表,后在类似docker exec命令可以直接跳进容器里(Kubernetes中也提供类似的命令),当开发人员需要把里面的日志文件拷贝出来,后者是拷贝一个debug版本的程序到容器里去运行时,可以用oc cp(同样这个在Kubernetes里也有类似的命令)。虽然这几个命令据我观察有一些bug,比如拷贝的目标目录不太准确,而且对容器里的tar命令有一些依赖。但这都是小问题,不影响使用,如果觉得几条命令结合起来使用有些麻烦,可以自己用Python脚本进行一个简单的封装,这样当开发人员使用的时候会更加简单。

2、每一个系统几乎都或多或少的使用了一些第三方工具,比如MySQL、MyCAT、Redis、ZooKeeper,这些组件我们都把它们进行了容器化,以便实现资源的整合和方便部署。那么这就引发了另一个问题,开发人员在碰到程序bug的时候,往往需要直接去连接这些第三方组件,去改修改和查看里面的信息。比如他需要查看Redis的键值是否存在,查看数据是否写入到了数据库里。这个时候,由于所有组件在容器中,你不知道它的准确IP,你可能很容易想到用OpenShift提供的router功能去像刚才的url那样提供外界的访问,但是由于这些中间件是4层的协议,而现有OpenShift的Router功能仅仅支持7层协议,所以我们为了解决这个问题就必须实现OpenShift的4层代理功能。通过修改Openshift的源代码haproxy-templte.conf和router部分的相关代码,然后通过yaml route的annotation段,定义一个规则,把对应的端口传进router的配置文件,让后端的第三方应用程序通过router节点对应的端口(Haproxy里的mode tcp)从而实现router代理4层协议的目的,但是这会导致另一个问题,因为router的每一个端口只能映射给一个后台应用,比如3306端口,只能映射给一个MySQL,如果有两个开发人员都要调试MySQL,那第二个开发者的MySQL的映射端口肯定就得用除了3306以外的端口(比如3307、第三个人3308等)那么就会产生一个问题router的映射端口如何告诉开发人员呢?这个问题有两个解决办法,第一个是通过一个Web UI,去显示的告诉开发人员他所有的资源对应的router节点的端口号,但是这有一个不方便的地方,如果的资源对应的Pod被重置了,那么他的端口号也就会被改变,即使端口号不改变,记起来也比较麻烦(大型的项目可能要用到5、6个中间件产品,每一个开发人员都要记自己的那套环境的端口号,还要学会如何用自己的工具去修改默认的端口号显然给开发者添加了许多不必要的麻烦。那么基于此问题的考虑,我们使用了第二种方法,即我们内部实现了一个DNS,开发者每个人的资源的IP都可以用DNS查到,比如developer1,他有Redis、MySQL、MyCAT、ZooKeeper、PostgreSQL等资源,那他的这些资源对应的DNS域名为:developer1.redis developer1.mysql developer1.mycat developer1.zookeeper等,但是由于DNS只能返回IP地址,无法返回端口号,我们还是得不到router节点对应资源的端口号。为了解决这个问题,我们可以用Haproxy加别名的方式,比如运行命令ifconfig eth0:1 10.10.xx.xx意思就是给eth0的NIC,加上了一个虚拟的IP10.10.xx.xx,那么此时,这个网卡eth0就有两2个IP,并且此时都能ping通。(当然实际实现的时候,我们不可能用是ifconfig命令完成这个网卡别名的方式,那样太low,也太不可靠了,我查看了ifconfig的源代码,把其中设置IP地址的代码集成到了我们修改过的OpenShift里)。然后router的haproxy.conf做bind的时候就需要指定这个IP,端口号还用原来这个应用默认的端口号如下图所示,这样开发人员不用每次都记住不同的端口号,仅仅配置一个DNS,不管环境发生了什么样的改变,都可以用默认端口和hostname去连接自己的资源进行调试。

3、配置Docker容器的时候,默认使用的是DeviceMapper方式,然而这种方式有众多的限制,可以参考https://docs.openshift.org/3.6/install_config/install/host_preparation.html#configuring-docker-storage中的详细配置说明,在生产环境中我们采用的是第2种方式。

#Q&A
Q:所有开发人员都是用一套OpenShift集群测试吗?CI/CD也是同一套环境吗?

A:我们是按业务分的,原则上,一套业务线(一个业务部门)用一套系统,这样成本上也好分摊。



Q:OpenShift也是用Go编写的?

A:是的,OpenShift用的是源码级别的和Kubernetes的集成,不是通过client-go或者REST API的方式,所以我们可以看到,Kubernetes发型的版本总是比OpenShift的快1 到2个版本。



Q:对于OpenShift比较适合多大规模的团队?

A:这个怎么说呢,其实引入DevOps或者CI/CD的流程就是为了给企业减少人员成本,让有些能够自动化的东西通过计算机运行起来。所以因该是人员越少越好,但是人员如果少,就要求每个人的技术能里比较强,开源的东西往往用起来不难,但是真到出了问题的时候就需要看代码去解决了。所以如果人少的话,可能每个人就要求懂得就比较多一些。



Q:router本身是否具备HAProxy?

A:OpenShift的Router就是用HAProxy实现的,当然我现在用的是3.6.1的版本,我不知道以后会不会支持Nginx或者其他别的LB,因为我看到代码里已经有关于Nginx的相关配置了,但是没有激活。OpenShift使用HAProxy的大致流程就是通过一个Yaml或者jason文件,配置一条route信息,然后通过api-server持久化到etcd中,router的代码启动一个goroutine,去通过api-server watch etcd,然后根据配置信息和环境变量,通过haproxy-template模版,去生成 haproxy.conf,然后去动态reload。



Q:OpenShift的project和Kubernetes的namespace是一对一的关系么?project可以设置资源配额么?怎么设的?

A:是一对一关系,当然有一些namespace 是有一些特殊意义的,不建议在里面跑应用。project可以设置资源配额,具体怎么设置就比较复杂了,建议参考一下官方文档,简单的说就是可以根据CPU内存做资源的限定,这个和Kubernetes是一样的。



Q:OpenShift中原生性能指标监控服务的Pod总挂有没有相应的解决办法?

A:解决Pod总挂的问题就得具体问题具体分析了,我记得它做性能监控的那个Pod比较吃资源,其实可以对他进行一下限定,比如:oc env rc hawkular-cassandra-1 MAX_HEAP_SIZE=1024M -n openshift-infra。



Q:OpenShift中的router默认情况下是跑在Pod里的,那么当service特别多,route规则也特别多的时候,如何解决router服务的性能问题的?

A:这是一个好问题,但其实我觉得这个和HAProxy有很大的关系,跟在不在Pod中关系不大,因为router这个容器其实用的是主机网络,那么这个问题其实就转化成了如何提升HAProxy的性能,这种情况其实有很多可以参考的方案,比如前面在加一层LVS负载,或者用DNS做域名解析的时候进行一定的负载功能。



以上内容根据2018年1月9日晚微信群分享内容整理。 分享人王晓轩,某传统互联网公司基础技术部技术经理,关注容器技术及DevOps PaaS领域,2014年关注容器技术,深度接触过Cloud Foundry等开源PaaS产品, 现主要研究OpenShift及周边技术。 DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesa,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

如何用S2I创建一个builder image

alex_wang2 发表了文章 • 0 个评论 • 5258 次浏览 • 2017-12-08 00:25 • 来自相关话题

【编者的话】众所周知,在容器编排领域,Kubernetes的市场占有率遥遥领先。但编者认为Kubernetes是面向运维人员的分布式系统,所以Redhat公司在Kubernetes的基础上推出了OpenShift,除了支持Kubernetes原有的所有功能外, ...查看全部
【编者的话】众所周知,在容器编排领域,Kubernetes的市场占有率遥遥领先。但编者认为Kubernetes是面向运维人员的分布式系统,所以Redhat公司在Kubernetes的基础上推出了OpenShift,除了支持Kubernetes原有的所有功能外,针对Paas平台的特点,对Kubernetes进行了深度开发。文中介绍的S2I就是OpenShift之所以区别于Kubernetets,能成为PaaS平台的实现中一个主要环节。通过选择对应的image,编写assmble和run的脚本,最终生成包含依靠源代码生成的可以执行文件的docker image,并利用OpenShift现有的体系架构(BC和DC)直接发布,完成所有的应用的生命周期管理,让开发者真正仅仅关注与代码本身,这才是一个真正的Paas平台需要实现的。
openshift.png

Source-To-Image(S2I)是一个在创建builder image时可以独立运行的常用的工具。在OpenShiftV3里,它还是一个创建应用程序的主要手段。这是因为它具备如下优势:

- 速度快 —— 用S2I在应用程序编译的过程中可以在不创建新的layer的情况下执行大量的复杂操作,导致编译速度加快。
- 可以打补丁 —— 比如当你发现你的image存在一个安全问题时,S2I可以帮助你持续不断的rebuild你的应用程序。
- 用户效率高 —— S2I防止开发人员随意的用yum对image进行安装操作,因为这种操作会拖慢开发迭代的效率。
- 生态性 —— S2I鼓励共享一个生态系统的image,你可以利用它去运行你的应用程序。

这篇文档就是关于如何去创建一个简单的S2I的builder image。

总的来说,S2I利用源代码和builder Docker image去生成一个新的Docker image。S2I这个项目本身其实已经包含了一些常用的Docker builder image,比如用来创建PythonRuby的builder image,当然你也可以根据需要去扩展它。

在讨论如何去创建一个builder image之前,我们先来解释一下S2I是如何工作的以及build image过程中builder image的角色。S2I(Source-To-Image),从名字上可以知道,它其实是负责把你应用程序源代码转换成可以在OpenShiftV3或者用`docker run`运行的Docker Image。所以这个Builder image包含了一些能够生成可执行镜像的所需的能力(也成为:build artifact)。整个的build过程包含对生成的image的调用,分几个步骤完成。在这边文章里我们用一个最简单的流程说明:

1. 下载S2I脚本(或者用builder image 里面的)
2. 下载你的应用程序代码
3. S2I stream脚本们和应用程序代码到builder image的容器里
4. 在Build image里执行定于好的编译脚本(这个脚本就是上一步stream进去的)
5. 保存这个容器到一个新的image

当然,为了让builder image完成所有这些工作,我们还需给它一些传入一些content。首先,因为builder image 负责实际意义上的build 你的源代码,所以它必须包含build和运行这个应用程序所依赖的libray和tools。比如,一个tomcat的builder image需要包含Tomcat,JDK和Maven。一个Python builder image包含Gunicorn或者cherry.py,SciPy的二进制包和pip。其次,它还需要包含一些执行build和run操作的逻辑脚本。这部分被下面两个S2I的脚本调用:

  • `assemble`——负责build一个应用程序
  • `run`——负责运行一个应用程序

下面的我将介绍如何利用lighttpd server的docker image构造一个builder image去运行一个静态的html页面。

当然,这其中还包括了用GitHub存储创建过程中产生的文件。

现在,我们开始我的旅程,首先从https://github.com/openshift/source-to-image/releases/ 获取最近版本的S2I(作者在写此篇文章时的最新版是1.0.9,译者翻译时是1.1.8)
#步骤 1
S2I 是一个独立的命令,能够创建一个builder image所需要的目录结构,从这里可以得到更多关于它的信息,现在我们用下面命令创建出所必须的结构:
s2i create lighttpd-centos7 s2i-lighttpd

我调用了`s2i create`命令传入了我想要的builder image的名字(`lighttpd-centos7`)它的目录结构也已经被创建完成(`s2i-lighttpd`)。如果这个目录不存在,这个命令就会为你创建它。所以,运行上面那个命令你将得到下面这个目录结构:
s2i-lighttpd/
├── Dockerfile
├── Makefile
├── README.md
├── s2i
│ └── bin
│ ├── assemble
│ ├── run
│ ├── save-artifacts
│ └── usage
└── test
├── run
└── test-app
└── index.html


#步骤 2
为了定制builder image,我们需要修改那个刚刚用S2I命令生成的`Dockerfile`。
# We are basing our builder image on openshift base-centos7 image
FROM openshift/base-centos7

# Inform users who's the maintainer of this builder image
MAINTAINER Maciej Szulik

# Inform about software versions being used inside the builder
ENV LIGHTTPD_VERSION=1.4.35

# Set labels used in OpenShift to describe the builder images
LABEL io.k8s.description="Platform for serving static HTML files" \
io.k8s.display-name="Lighttpd 1.4.35" \
io.openshift.expose-services="8080:http" \
io.openshift.tags="builder,html,lighttpd"

# Install the required software, namely Lighttpd and
RUN yum install -y lighttpd && \
# clean yum cache files, as they are not needed and will only make the image bigger in the end
yum clean all -y

# Defines the location of the S2I
# Although this is defined in openshift/base-centos7 image it's repeated here
# to make it clear why the following COPY operation is happening
LABEL io.openshift.s2i.scripts-url=image:///usr/local/s2i
# Copy the S2I scripts from ./.s2i/bin/ to /usr/local/s2i when making the builder image
COPY ./.s2i/bin/ /usr/local/s2i

# Copy the lighttpd configuration file
COPY ./etc/ /opt/app-root/etc

# Drop the root user and make the content of /opt/app-root owned by user 1001
RUN chown -R 1001:1001 /opt/app-root

# Set the default user for the image, the user itself was created in the base image
USER 1001

# Specify the ports the final image will expose
EXPOSE 8080

# Set the default CMD to print the usage of the image, if somebody does docker run
CMD ["usage"]

#步骤 3
一旦`Dockerfile`修改完成,我们就可以填充builder image的剩余部分,也就是S2I的那些脚本。首先我们从`assemble`脚本开始, 它是负责build我们的应用。在这个例子里,它仅仅负责拷贝源代码到Lighttpd server的目录下:
#!/bin/bash -e
#
# S2I assemble script for the 'lighttpd-centos7' image.
# The 'assemble' script builds your application source ready to run.
#
# For more information refer to the documentation:
# https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md
#

echo "---> Installing application source"
cp -Rf /tmp/src/. ./

默认情况下,`s2i build`命令将源代码放在`/tmp/src`目录下,在build过程中这个目录下的源代码文件和其他相关文件会被使用。当然你也可以通过修改`io.openshift.s2i.destination`这个label或者传入`--destination`这个参数去修改它,如果是那样的话源代码会被放在你指定的目录下的`src`子目录下。在相面cp命令中的第二个参数`./`是用来设置在`openshift/base-centos7`中的工作目录,也就是`/opt/app-root/src`。

好,现在让我去处理第二个脚本文件`run`,故名思义它负责运行我们的应用程序。在我们的例子中,它会启动lighttd server:
#!/bin/bash -e
#
# S2I run script for the 'lighttpd-centos7' image.
# The run script executes the server that runs your application.
#
# For more information see the documentation:
# https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md
#

exec lighttpd -D -f /opt/app-root/etc/lighttpd.conf

在上面的命令中我们用`exec`去`run`lighttpd的进程。这是为了所有由Docker发送给lighttpd的信号都会输出到`stdout`和`stderr`。

因为在我们的例子中,我们不关注增量build,所以我们可以去掉`save-artifacts`这个脚本。最后我们写一些关于如何使用这个build image的方法在`usage`脚本中:
#!/bin/bash -e

cat < This is the lighttpd-centos7 S2I image:
To use it, install S2I: https://github.com/openshift/source-to-image

Sample invocation:

s2i build https://github.com/soltysh/sti-lighttpd.git --context-dir=test/test-app/ lighttpd-centos7 sample-app

You can then run the resulting image via:
docker run -p 8080:8080 sample-app
EOF

注意:修改这个脚本的执行权限,推荐为`755`。
#步骤 4
我们builder image还剩最后的一部分,一个关于lighttpd的配置文件。这个配置文件会被`Dockerfile`和`run`脚本去使用。如果没有这个配置文件,lighttpd恐怕不能够正常启动。我用下面的配置为lighttpd生成配置文件,将其保存在`s2i-lighttpd/etc/lighttpd.conf`。
# directory where the documents will be served from
server.document-root = "/opt/app-root/src"

# port the server listens on
server.port = 8080

# default file if none is provided in the URL
index-file.names = ( "index.html" )

# configure specific mimetypes, otherwise application/octet-stream will be used for every file
mimetype.assign = (
".html" => "text/html",
".txt" => "text/plain",
".jpg" => "image/jpeg",
".png" => "image/png"
)

这是一个关于lighttpd的最简单配置文件,它仅仅设置了:

* lighttpd处理的源代码路径(`/opt/app-root/src`)
* lighttpd监听的端口号(`8080`)
* 默认文件的路径(`index.html`)
* mimetype影射属性

到这步,我们的builder image已经做完了。我们能够确定的是`s2i-lighttpd`目录下的`make`命令会在适当的时候去build它,当然你已经发现了这是因为里面有一个`Makefile`,里面调用了`docker build`命令。
#步骤 5
现在我们用一个简单的应用程序去测试这个builder image。我创建了一个`index.html`在`s2i-lighttpd/test/test-app`目录下:(译者注:在现在的1.18版本中,这个测试文件会被自动创建)



test-app


Hello from lighttpd served index.html!



有了这个文件,我们现在可以运行s2i build了。在`s2i-lighttpd`目录下执行下面的命令:
s2i build test/test-app/ lighttpd-centos7 sample-app

我们从`test/test-app`目录下创建了一个应用程序,用了我们刚刚的build image(`lighttpd-centos7`)。生成了一个叫`sample-app`的image。默认的S2I build 会打印出所有的由`assemble`产生的输出,所以你会看到下面的输出在你的终端上(你可以忽略关于用本地builder image和非git目录产生的warning):
---> Installing application source

现在我们实际的测试一下生成的image。运行这个image,暴露容器的8080端口到主机上:
docker run -p 8080:8080 sample-app

现在你可以通过http://localhost:8080/访问到`index.html`的内容了。
#步骤 6
这里面还有一些需要补充的部分:tests,大多数情况下可以用`s2i-lighttpd/test/run`这个脚本去处理,假设你在`Dockerfile`里选择默认的端口。在这个情况下,你可以在`s2i-lighttpd`目录下运行下面这个命令:
make test

注意:设置test/run的执行权限,建议设置成700。

这个脚本会运行`s2i build`(请却保`s2i`在你的环境变量path里)用`test-app`作为应用程序源代码,并且执行一些测试以确保image的可用性。这些测试包括:

* 检查`s2i build`运行没有出错
* 检查`usage`脚本运行正确
* 测试最后生成的image
* 检查运行应用程序的容器有正确的响应

在这个例子里我没有增加对`test/run`的说明,因为那个文件太长了。感兴趣的读者可以去看一下https://github.com/soltysh/sti-lighttpd/。提交的历史可以准确地显示本文中给出的每一个步骤。

现在祝贺你,已经有了一个可以使用的S2I builder image,使用S2I(Source Build strategy in OpenShift),处理一个在git代码仓库里的html文件,并且生成一个新的image去对外提供访问。总的来说,通过修改`assemble`和`run`这两个脚本可以很容易的定义其他类型的builder image,然后用相应的image去运行对应类型的应用程序。

原文连接:How to Create an S2I Builder Image (翻译:王晓轩)

DockOne微信分享(一四二):容器云在万达的落地经验

qianggezhishen 发表了文章 • 3 个评论 • 7635 次浏览 • 2017-09-22 11:39 • 来自相关话题

【编者的话】容器生态是现在非常火热的技术生态之一,个人认为它主要囊括着四个方面的技术栈:一是容器核心技术栈(包括 Docker、rkt 及第三方公司自主研发的容器 Engine 等);二是容器基础技术栈(包括容器网络、存储、安全及服务发现等);三是容器编排技术 ...查看全部
【编者的话】容器生态是现在非常火热的技术生态之一,个人认为它主要囊括着四个方面的技术栈:一是容器核心技术栈(包括 Docker、rkt 及第三方公司自主研发的容器 Engine 等);二是容器基础技术栈(包括容器网络、存储、安全及服务发现等);三是容器编排技术栈(包括 Mesos/Marathon、Swarm、Kubernetes 及 OpenShift 等);四是容器应用技术栈(主要包括 CI/CD、监控、日志及微服务框架等)。而 Docker 和 Kubernetes 分别又是目前容器与容器编排界的小宠儿,所以我将带着小宠从这四方面分享一些容器云在万达的落地经验。

万达容器云平台支持快速部署、弹性伸缩、负载均衡、灰度发布、高效运维及微服务等特性。用户可以基于 Dashboard 简单方便地上线和管理着各自业务。目前在我们的容器云平台上,平稳高效运行着近 400 款包括支付、酒店等核心业务,管理着公司上万个容器。经历住了双旦、618 及双 11 等大型活动的考验。
# 一、容器云的平台高可用架构与部署
“经济基础决定上层建筑”,对于整个容器云平台来说,Kubernetes 平台就是我们的“经济基础”,所以在建设之初我们为了保证平台的稳定、高可用还是做了不少工作。先来看看 Kubernetes 平台的建设涉及到的几个技术点:

* 组件的高可用性如何保证?
* 组件以何种方式部署安装?
* 集群以何种方式快速扩缩容?
* 如何实现环境的批量配置及组件的批量升级?

为了较好的描述这些,我画了一张我们容器云中 Kubernetes 各组件高可用架构与部署图,请见:
1-K8S_组件高可用架构与部署.png

Kubernetes 所有组件都容器化了,而不采用二进制安装的方式。这样对于组件的部署、配置更新和升级等都比较容易,只需要先安装好 Docker,提前做好镜像,然后直接 `docker run --restart=always --name xxx_component xxx_component_image:xxx_version` 就可以了,这里 `--restart=always` 非常重要,用来保证主机重启后或由于突发状况引起的非错误性退出后组件服务自动恢复。

注意在升级过程中,为了减少组件服务的宕机时间,需要提前下载好新制作的镜像版本,因为如果镜像挺大的话,在 `docker restart` 进行更新前会耗费一些时间在 `docker pull` 上面。

在批量部署方面,我们采用 `Ansible` 工具。由于 Kubernetes 集群部署的文档网上蛮多的,所以这里就简要介绍一下组件的高可用部分。我们都知道 Kubernetes 实现了应用的高可用,而针对 Kubernetes 自身的 HA,主要还是在 Etcd 和 Kubernetes Master 组件上面。
##1、 Etcd 的高可用性部署
Etcd 使用的是 V3(3.0.3)的版本,比 V2 版本性能强很多。Etcd 组件作为一个高可用强一致性的服务发现存储仓库,它的 HA 体现在两方面:一方面是 Etcd 集群自身需要以集群方式部署,以实现 Etcd 数据存储的冗余、备份与高可用;另一方面是 Etcd 存储的数据需要考虑使用可靠的存储设备。

为了展示一下组件容器化在`部署方面`带来的一些便利效果,以 Etcd 的部署为例,先在 Ansible `inventory` 文件中规划好 Etcd 的分组信息,一般是采用 3 台做为集群即可。例如:
2-etcd-host.png

然后编写好类似如下的 `template` 文件,就可以用 Ansible Playbook 实现一键秒级的部署 Etcd 集群了,注意我这里使用了主机 IP 作为 Etcd 的名字。
3-template.png

如果需要升级则只需要修改一下 `etcd_version` 并提前拉取新版本镜像,重新跑一下 `ansible-playbook --limit=etcds -i hosts/cd-prod k8s.yaml --tags etcd` 即可完成秒级升级,这样一般显得简洁易用。当然 Etcd 在升级过程中还涉及到数据的迁移备份等,这个可以参考官方文档进行。
## 2、Kubernetes Master 的高可用性部署
Kubernetes 的版本我们更新的不频繁,目前使用的是 V1.6.6。Master 的三个组件是以 `Static Pod` 的形式启动并由 `kubelet` 进行监控和自动重启的,而 `kubelet` 自身也以容器的方式运行。

对于 `kube-apiserver` 的 HA,我们会配置一个 `VIP`,通过 `haproxy` 和 `keepalived` 来实现,其中 `haproxy` 用于负载均衡,而 `keepalived` 负责对 `haproxy` 做监控以保证它的高可用性。后面就可以通过 `VIP:Port` 来访问 `apiserver`。

对于 `kube-controller-manager` 和 `kube-scheduler` 的 HA,由于它们会修改集群的状态,所以对于这两个组件,高可用不仅要保证有多个实例,还需要保证在多个实例间实现 `leader` 的选举,因此在启动参数中需要分别设置 `--leader-elect=true`。

对于 Kubernetes 集群的扩缩容,只需要采用 Ansible 进行批量操作或者 `kubectl` 命令(一般需要用到 `drain`, `cordon` , `uncordon` 等)完成即可。另外我们还需要注意一下图中标注的各组件启动时依赖的参数配置,这些比较容易搞混。
# 二、容器云的技术架构介绍
目前使用的容器云架构方案如图所示,我将其分为四个部分,每个部分包含对应的容器技术栈,下面对于主流的技术介绍如下:
4-云容器技术架构.png

##1、存储方案
后端存储主要采用 `ceph` 驱动,这里主要介绍它在`有状态服务`和 `docker-registry` 两方面的应用。

`有状态`是应用程序的高级需求,它们需要在 Pod 挂了特别是飘移到其他 Node 后,可以持续的访问后端存储。因此,Kubernetes 通过网络存储提供了丰富的 `Persistent Volume` 支持,比如:GCE 的 pd,AWS 的 ebs,还有 GlusterFS,Ceph 等先进的分布式文件系统。我们目前使用了 Ceph 的 `rbd` 来支持有状态服务。Kubernetes 集群环境中,由于所有的组件采用容器化部署,因此除了在主机上安装 `ceph-common` 之外,还需要在 `kubelet`、`kube-controller-manager` 容器中安装它,而且在启动时挂载如下三个 volume,其他的与二进制方式差不多:
5-rbd-volume.png

具体 ceph rbd 配置,详见:Persistent Volume Provisioning 官网

rbd 支持动态供应,支持单节点读写,多节点读,但不支持多节点写。如果有业务需要多节点写的话,rbd 就比较受限制。目前由于只有 `GlusterFS` 既允许动态供应,又支持单节点和多节点读写,所以我们也正在调研其相关使用。

`docker-registry` 做为容器的核心部分,起初我们采用 `Swift` 作为后端存储,为了提高 `push/pull` 的效率,采用 `Redis` 作为 Metadata 缓存,然后直接以容器的方式运行官方提供的镜像,比如:
6-docker-regsitry.png

具体的 `config.yml` 配置,详见:docker-registry 官方配置。但后来为了保证 `docker-registry` 的高可用,我们采用 `Harbor` 做 HA,并以 `pod` 的形式运行在 Kubernetes 集群上,镜像数据以及 `Harbor-db` 全部通过 `Ceph` 的 `PV` 来挂载,这样就保证在 `Harbor` 主机挂了或者 `Pod` 故障后,`Harbor` 也可以 HA 了,同时我们也不需要额外维护 Swift 了。

另外注意一个问题,由于 `PV`,`StorageClass` 都局限于单个 `Namespace` 下,所以对于想通过 `Namespace` 来区分多租户使用动态存储的情况目前是不满足的。
##2、网络方案
底层容器网络我们最初使用的是官方推荐的 `Flannel`,现在部分集群已经由 `Flannel` 切换成了 `OVS` 。 `Flannel` 可以很容易的实现 Pod 跨主机通信,但不能实现多租户隔离,也不能很好的限制 Pod 网络流量,所以我们网络同事开发了 `K8S-OVS` 组件来满足这些需求。它是一个使用 `Open VSwitch` 为 Kubernetes 提供 SDN 功能的组件。该组件基于 `OpenShift SDN` 的原理进行开发。由于 OpenShift 的 SDN 网络方案和 OpenShift 自身的代码耦合在一起,无法像 Flannel 和 Calico 等网络方案以插件的方式独立的为 Kubernetes 提供服务,所以开发了 K8S-OVS 插件,它拥有 OpenShift 优秀的 SDN 功能,又可以独立为 Kubernetes 提供服务。

K8S-OVS 支持单租户模式和多租户模式,主要实现了如下功能:

* 单租户模式直接使用 Openvswitch+Vxlan 将 Kubernetes 的 Pod 网络组成一个大二层,所有 Pod 可以互通。
* 多租户模式也使用 Open vSwitch+Vxlan 来组建 Kubernetes 的 Pod 网络,但是它可以基于 Kubernetes 中的 Namespace 来分配虚拟网络从而形成一个网络独立的租户,一个 Namespace 中的 Pod 无法访问其他 Namespace 中的 Pod 和 Service。
* 多租户模式下可以对一些 Namespace 进行设置,使这些 Namespace 中的 Pod 可以和其他所有 Namespace 中的 Pods 和 Services 进行互访。
* 多租户模式下可以合并某两个 Namespace 的虚拟网络,让他们的 Pods 和 Services 可以互访。
* 多租户模式下也可以将上面合并的 Namespace 虚拟网络进行分离。
* 单租户和多租户模式下都支持 Pod 的流量限制功能,这样可以保证同一台主机上的 Pod 相对公平的分享网卡带宽,而不会出现一个 Pod 因为流量过大占满了网卡导致其他 Pod 无法正常工作的情况。
* 单租户和多租户模式下都支持外联负载均衡。

下面举例解释一下:

合并是指两个不同租户的网络变成一个虚拟网络从而使这两个租户中的所有 Pod 和 Service 能够互通;分离是指针对合并的两个租户,如果用户希望这两个租户不再互通了则可以将他们进行分离;全网化是指有一些特殊的服务需要能够和其他所有的租户互通,那么通过将这种特殊的租户进行全网化操作就可以实现。

不同租户的网络隔离是通过为每个 Kubernetes 命名空间分配一个 VNI(VXLAN 中的概念)来实现的,在 VXLAN 中不同的 VNI 可以隔离不同的网络空间。K8S-OVS 将具体的 Kubernetes 命名空间和 VNI 的对应关系存储在 Etcd 中,如下:
7-network1.png

这是在我们通过 Kubernetes 创建 Namespace 时,K8S-OVS 自动检测并为我们创建的。其中 `NetName` 是指租户的 Kubernetes 命名空间;`NetID` 是指为该租户分配的VNI;`Action` 是指可以对该租户网络进行的操作,它包括 `join` :合并, `isolate` :分离, `global` :全网化,其中 `join` 需要指定上面的第四个参数 Namespace,用于表示需要和哪个租户进行合并,其他两个操作则不需要设置 Namespace。
8-network2.png

合并之后观察 helloworld1 和 helloworld2 ,发现两个租户的 `NetID` 变为相同的了。这样两个网络的 Pod 和 Service 就可以相互访问了。其他场景一样,通过 etcdctl update 来控制 Action 从而改变 NetID 来实现租户的隔离与互通。这里不过多演示。

在应用方面,我们实现了 LVS 的四层,Ingress(基于 Nginx+Ingress Controller) 的七层负载均衡,各 Kubernetes 集群环境中陆续弃用了 Kubernetes 自带的 kube-proxy 及 Service 方案。
##3、CI/CD 方案
CI/CD(持续集成与部署)模块肩负着 DevOps 的重任,是开发与运维人员的桥梁,它实现了业务(应用)从代码到服务的自动上线,满足了开发过程中一键的持续集成与部署的需求。

我们采用了前端基于 Opads(一个比较成熟的在线打包平台)和后端 Pluto(一个将 Kubernetes APIServer 的接口进行封装,并提供 RestfulAPI 服务的库项目)的方案。目前通过该平台上线的业务有近 400 款。其架构图大致如下:
9-CICD.png

业务代码从公司内部的 GitLab/Gerrit 中拉取后,用户选择不同的分支版本(比如:sit/uat/prod)经过 Jenkins 构建后,生成相应的镜像并 Push 到相应的镜像仓库中,底层 `CD` 模块从相应的仓库上线到相应的 Kubernetes 集群(sit/uat/prod)中,而不需要 care 底层平台实现。

在整个 CI/CD 过程中,基础镜像的制作比较关键,我们按不同的业务分类提前制作不同的应用镜像(如:Tomcat、PHP、Java、NodeJS等)并打上所需的版本号,后面源代码既可以 `mount` 到相应的容器目录,也可以利用在 Dockerfile 的 `ONBUILD` 命令完成业务代码的加载。挂载的方式优点是比较灵活,也不需要重复构建,上线效率高;缺点是对于基础镜像环境的依赖较高,如果一个业务的基础镜像一直未改动,但代码中又有了对新组件库的调用或者依赖,则很容易失败。onbuild 的方式和 mount 方式恰好相反,它每次都进行 build,解决了环境的依赖,后面版本回滚也方便,缺点是需要每次进行镜像构建,效率低。这些看业务自己的选择。

我们提供的基础镜像版本在业务上线的时候,业务开发者可以提前搜索确定基础镜像是否存在,如果不存在需要提 Jira 单子后,交由我们进行制作,部分截图如下:
10-opads1.png

之后,业务可以通过选择代码分支、版本等上线服务。之后就可以看到上线的业务详情了,包括 Pod 副本个数,存活时间,名字,创建时间,所在的 Kubernetes 节点及 node selector 等。也可以通过基于 Gotty 实现的 Web console 登录查看业务容器,如图所示。
11-opads2.png

我们还实现了服务扩缩容,弹性伸缩(HPA)、负载均衡、灰度发布等,也加入了代码质量检查(Sonar)、自动化测试及性能测试插件等,这些都是 CI/CD PaaS 平台的重要组成部分。
##4、容器监控与告警方案
容器监控的对象主要包括 Kubernetes 集群(各组件)、应用服务、Pod、容器及网络等。这些对象主要表现为以下三个方面:

  • Kubernetes 集群自身健康状态监控(5 个基础组件、Docker、Etcd、Flannel/OVS 等)
  • 系统性能的监控,比如:CPU、内存、磁盘、网络、filesystem 及 processes 等;
  • 业务资源状态监控,主要包括:rc/rs/deployment、Pod、Service 等;

Kubernetes 组件相关的监控,我们写了相关的 shell 脚本,通过 crond 开启后监控各组件状态。

容器相关的监控,我们采用传统的 Heapster+InfluxDB+Grafana 方案:
12-监控与告警架构.png

Heapster 首先从 Kubernetes Master 获取集群中所有 Node 的信息,每个 Node 通过 `kubelet` 调用 `cAdvisor API` 来采集所有容器的数据信息(资源使用率和性能特征等)。这样既拿到了 Node 级别的资源使用状况信息,又拿到了容器级别的信息,它可以通过标签来分组这些信息,之后聚合所有监控数据,一起 `sink` 到 `Heapster` 配置的后端存储中(`Influxdb`),通过 `Grafana` 来支持数据的可视化。所以需要为 Heapster 设置几个重要的启动参数,一个是 `--source` 用来指定 Master 的 URL 作为数据来源,一个是 `--sink` 用来指定使用的后端存储系统(InfluxDB),还有就是 `--metric_resolution` 来指定性能指标的精度,比如:`30s` 表示将过去 30 秒的数据进行聚合并存储。

这里说一下 Heapster 的后端存储,它有两个,一个是 `metricSink`,另一个是 `influxdbSink`。metricSink 是存放在本地内存中的 metrics 数据池,会默认创建,当集群比较大的时候,内存消耗会很大。Heapster API 获取到的数据都是从它那获取的。而 influxdbSink 接的是我们真正的数据存储后端,在新版本中支持多后端数据存储,比如可以指定多个不同的 influxDB 。

通过 Grafana, 用户可以使用各种正则表达式或者选项查看自己的业务监控详情,还可以按系统性能(CPU、内存、磁盘、网络、filesystem)进行排序查看等等。
13-监控示例.png

当监控到一定的数量级,超过某个阈值时,将产生告警。虽然 Grafana 目前支持邮件进行一些简单的告警,但我们还是通过制定一些监控点、告警机制、告警等级等,然后接入公司内部现有 Zabbix 平台来进行告警。

邮件告警示例如下:
14-邮件告警.png

##5、日志方案:
容器平台的日志系统一般包括:Kubernetes 组件的日志,资源的事件日志及容器所运行的应用的日志。所以一个好的日志系统至少需要 cover 到这几块。

日志这块一直是我们头痛的地方,之前我们主要关心容器中的业务日志,所以是直接将其日志对接到公司的统一日志平台(Hippo)中。他们需要我们采用 Flume 来进行日志采集,每个业务以 Pod 的方式运行 Flume ,在 Flume 中配置好 `source`, `channel` 和 `sink` 参数。`source` 用来指定业务挂载的日志目录,然后通过 `channel` 进行输送,最后 `sink` 到后端的 Hippo 所使用的 Kafka 中。业务需要查看日志,则需要登录到 Hippo 平台中。

这个方案有一些不如意的地方,比如:每个业务需要单独额外运行一个 Flume 的 Pod,浪费资源;业务需要登录到另一个系统查看,由于不是对接到我们的平台中,不方便;另外由于 Hippo 是针对公司的统一的日志平台,不是容器云专用的,经常会在高峰期响应慢,很难处理过来。所以我们决定设计一个全新的平台,采用的方案是 `Kubernetes` 官方推荐的(Fluentd+Kafka+ES+ 自定义界面),具体架构如下:
15-日志方案.png

容器中输出的日志都会以 `*-json.log` 的命名方式保存在 `/var/lib/docker/containers/` 中,系统日志都在 `/var/log` 中。Fluentd 以 `Daemon Set` 运行于所有的 Node 中进行数据采集,为了保证性能,引进了 Kafka 作为消息队列及日志转发,由于不想维护多个组件,中间转发不用 Logstash ,所以需要引入了两个 Fluentd 的插件 `fluentd-kafka` 及 `fluentd-es`,前者用于推送数据到 Kafka,后者用于将数据推到 ElasticSearch 中。最后实现一个 `Log API Engine` 用于供上层 `Log GUI` 调用。这个 Engine 封装了实时日志、历史日志下载和查询、应用日志占比、日志等级占比等 Restful API。下面是我们的部分截图:
16-日志示例.png

# 三、容器云的填坑实践
下面挑几个坑说一下,并分享一下解决方法。
## Docker 相关
早期由于混合使用了 Deployment 和 RC,此时如果使用了同名 Pod label 则会产生冲突,RC 会删除 Deployment 后创建的 Pod,造成 Pod 的反复创建与删除,最终导致 Node 上的 Docker daemon 挂僵掉。原因是 Docker device mapper 互锁而触发 Linux kernel bug(特别是有大量的 outbound traffic 时),解决方法是升级内核到 3.10.0-327.22.2,或者添加内核补丁
## Kubernetes 相关
业务应用在升级过程中,如果 Docker 删除出错, 偶偶会导致 device mapper busy,则会显示 Pod 一直在销毁,但其实它的 Deployment 已经被删除了,这种我们没有找到很好的处理方法,现有 workaround 是先重启 docker daemon,如果不能解决,再 reboot 主机。一般的做法是先 `drain` 掉所有的 pod,然后待重启解决后,再 `uncordon` 回来。

在使用容器的方式部署 kubelet 时,我们发现删除 Pod 时,在 apiserver log 中一直会出现 `UnmountVolume TearDown secrect` 资源失败的错误。其中是当时在挂载 `/var/lib/kubelet` 时采用了 `rw` 的方式,这个问题困扰我们很久了,解决方法是加上 `shared` 即 `--volume=/var/lib/kubelet:/var/lib/kubelet:rw,shared`。
## 存储相关
当某一 Pod 挂载 Ceph rbd 的 Volume 时,如果删除 Pod,再重新创建,由于 PVC 被 lock 导致无法挂载,会出现 volume 死锁问题。由于我们的 kubelet 是容器部署,而 ceph 组件是以挂载的方式开启的,所以猜测可能是由于 kubelet 容器部署引起,但后面改用二进制方式部署并升级到 `V1.6.6` 版本后就可以解决。当时的版本是 V1.5.2。
## 网络相关
在业务上线过程中,一定要进行规范化约束,比如,当时将 Flannel 升级到 K8S-OVS 网络升级过程中出现有些业务采用 Service 进行负载均衡的情况,这种依赖于 kube-dns,由于 Flannel 和 OVS 的网段不一样,Service 层又没有打通,导致以 Service 运行并通过 kube-dns 解析时会出问题,且网络不通,本可以采用新建一个以 OVS 同网段的 kube-dns 来完成不同网段的兼容,但最后面发现该业务是根本不使用 Service,而是直接利用了 Pod,这样非规范化上线的业务很容易导致升级相关的故障出现,猜测可能当时平台建设初期手动上过业务,其实这方面我们也可以加些监控。

网络不稳定的时候,偶偶发现业务访问突然就慢起来了,然后发现其 `TIME_WAIT` 会出现过高,这个是没有对网络内核进行优化处理,此时需要设置 `net.ipv4.tcp_tw_recycle = 1` 和 `net.ipv4.tcp_tw_reuse = 1` ,前者表示开启 TCP 连接中 `TIME-WAIT Sockets` 的快速回收,后者表示允许将 `TIME-WAIT Sockets` 重新用于新的 TCP 连接。当然还有其他网络内核优化。
## 告警相关
CPU load 是通过每个核的 `running queue`(待运行进程队列)计算出来的,某些情况下 running queue 会变成 -1 也就是 4294967295。由于在 cpu 过载的时候,我们设置了告警,所以会被触发,但其实这时的 CPU 负载是正常的。此时,如果通过 `sar -q ,top,w,uptime` 等看到的 running queue 都是有问题后的值,只有用 vmstat 查看才是真实的。解决方法是重启 Node,因为这个值只有在重启后才会重置为零,或者升级内核补丁
#Q&A
Q:Grafana 是实时显示数据的,请问他如何能做到告警?就是 grafana 达到一定告警阈值时做告警?

A:Grafana 新版本中添加了简单的告警功能,在 Notification Channels 页面有个新建通道,在里面设置一下,具体可以看下官方的文档。



Q:请问如何实现容器限速的?

A:你是说容器的网络限速吗?流量限制功能我们是通过在 pod 的 annotations 字段设置 kubernetes.io/ingress-bandwidth (设置输入流量带宽)和 kubernetes.io/egress-bandwidth (设置输出流量带宽)来实现。



Q:请问使用什么操作系统部署 Kubernetes,有什么考虑?

A:用的 CentOS 7,企业一般的用法,还有就是它稳定,不容易出问题,Kubernetes 和 Docker 的支持比较好。



Q:如何把所有告警信息全部递给 Zabbix,Zabbix 自身是否也获取了监控值信息了?

A:全部推送压力大,先将 APIserver、Heapster 中相关的信息放 MySQL,中间做个数据库。



Q:etcd 3 的容灾和安全做了吗?

A:etcd 非常关键,我们会在升级和定期用 etcdctl 做 backup。升级时需将 --initial-cluster-state 改为 existing ,安全方面还没有。



Q:做灰度发布或 HPA 自动扩容时,实现了不影响正在提供服务的服务吗?

A:灰度发布不会影响服务,我们使用了 Ingress + Nginx 来保证 Pod 的变化不受影响。HPA 这块我们不敢上线,功能完成了,但没有经过大量测试。



Q:使用 rbd 作为后端存储,当 pod 发生迁移到另外一个节点后如何再次挂载这个 rbd?

A:将 PVC 的 volume.beta.kubernetes.io/storage-class 和 StorageClass 的 name 名字一样就可。不需要管后面 Pod。



Q:etcd 3 在哪些方面不如 etcd 2?

A:没有去做对比,etcd 3 是通过搜集了 etcd 2 用户的反馈和实际扩展 etcd 2 的经验基础上全新设计了 API 的产品。etcd 3 在效率,可靠性和并发控制上改进比较多。etcd 2 支持多语言客户端驱动,etcd 3 由于采用 gRPC,很多需要自己实现驱动。



Q:请问有状态的 pod 迁移,使用 ceph pv 是怎么保证分到同一个 volume?

A:我们用的是 StorageClass,在 PVC 时指定和 StorageClass 名字一样就可。通过 volume.beta.kubernetes.io/storage-class 来指定该名字。



Q:请问运行在不同的 Node 上面的 Pod 如何共享 Volume 存储,比如要共享一份代码?

A:不同 Node 间的 Pod 卷共享方式比较多,但一般需要网络存储,比如:NFS,GlusterFS,CephFS,Ceph rbd,当然还包括很多大厂如:GCE 的 pd,AWS 的 ebs 等。甚至可以使用 ConfigMap 来共享,然后 mount 到相应的目录即可。



Q:请问有没有对比过共有的容器云和私有的容器云的优缺点?

A:公有云比较难做,我们之前是做私有云(物理资源隔离,用户数据更安全可控;独占资源,不受干扰;自行规划灵活调整资源复用比例,成本更优),公有云(公有云弹性,自如应对业务变化;具备跨机房、跨地区的容灾能力)我们也在做,正在和 IBM 合作。



Q:请教多 Master 下,当某个 Master down 掉,default/kubernetes endpoints 中的 IP 没更新的问题,你们是如何处理的?

A:这个主要是 Endpoints 控制器负责 Endpoints 对象的创建,更新。新 leader master 掌管后,Kubernetes 会用 checkLeftoverEndpoints 来删除 没有响应的服务的 endpoints,重启一下 kube-controller-manager 试试。



Q:做过集群联盟吗?

A:有测试过,但目前 Kubernetes 可以支持达 1000 节点了,能满足我们目前的需求,所以还没有上。



Q:HPA不是Kubernetes支持的吗?你们对其做了哪些二次开发?支持蓝绿部署吗?

A:对的,目前是支持 CPU 还有一些应用程序提供的 metrics 了,之前在社区还没有的时候,我们有自己开发,主要是通过 heapster 监控 qps 还提供自定义的一些度量来完成 HPA。但 HPA 这个一旦出问题会不可控,所以暂时还不敢上线。蓝绿部署比较耗硬件资源,相当于要多一新版本备份,目前我们还不支持蓝绿部署。



Q:如果想看日志文件有没有好的办法,感觉在ES重被切割了不友好?

A:日志文件可以通过在启动的时候新建一个以应用名字命名的目录挂载到本地或者网络存储中,然后应用的标准或错误输出会直接输出到 docker daemon 的日志目录下,如果应用有自己的专门的文件输出方式,则可以用 tail -f 方式进行转发与 docker daemon 对接。



Q:还有就是基础容器是用的CentOS镜像吗?它默认就接近200m。开发语言用的Go的话有啥优化容器的地方?

A:基础容器一般 CentOS 的多些,有些会直接采用 docker hub 提供的原始镜像,然后做些自定义组件添加并重打包。一般的会比较大一些,镜像可以对 Dockerfile 进行优化来变小。可以用 pprof 来分析 Go 代码性能,容器的优化还主要在 Dockerfile。



Q:请问你们对于用户体验方面是如何监控的? 比如每个点击在不同服务层面上的延时各是多少,超时报警等?

A:这是个不错的想法,我们还没有做这块,不过可以通过应用提供的url,对其监控HTTP get 的 response 时间来控制。



Q:前端基于 Opads和后端 Pluto实现CI,有具体的文档可以参考吗?

A:这两个都是自己内部开发的模块,一个基于 PHP,一个基于 Python,文档不方便分享。



Q:目前大规模落地云平台是建议上容器云吗?

A:建议上。



Q:服务启动依赖和应用版本控制如何做的?

A:这块我们做的不大好,一般可以将每个服务注册到发现服务,然后记录它们的依赖,在启动时进行服务发现及启动,这个在微服务框架中有些。我们在应用程序版本控制方面有自己的约束规范,但后面会有 helm 来试试。



Q:etcd 集群为什么不直接用Compose启动?

A:这个我们为了ansible部署方便



Q:Node 节点采用虚拟机还是物理机部署的?

A:物理机。



以上内容根据2017年09月21日晚微信群分享内容整理。 分享人陈强,万达网络资深工程师,毕业于华东师范大学。目前在万达网络科技集团云公司基础架构部负责Kubernetes与Docker的落地与实践工作。曾先后就职于Intel、IBM和爱奇艺。在云计算领域长年搬砖,对Mesos/Kubernetes/Docker等有较深入的研究。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesa,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

PaaS平台OpenShift企业部署的“脑图”

范彬 发表了文章 • 0 个评论 • 3324 次浏览 • 2017-08-17 14:33 • 来自相关话题

【编者的话】这篇文章是来自红帽,是关于OpenShift企业部署的“蓝图”,通过“脑图”帮助客户实现企业级部署OpenShift。 【烧脑式Kubernetes实战训练营】本次培训理论结合实践,主要包括:Kubernetes架构和资源 ...查看全部
【编者的话】这篇文章是来自红帽,是关于OpenShift企业部署的“蓝图”,通过“脑图”帮助客户实现企业级部署OpenShift。

【烧脑式Kubernetes实战训练营】本次培训理论结合实践,主要包括:Kubernetes架构和资源调度原理、Kubernetes DNS与服务发现、基于Kubernetes和Jenkins的持续部署方案 、Kubernetes网络部署实践、监控、日志、Kubernetes与云原生应用、在CentOS中部署Kubernetes集群、Kubernetes中的容器设计模式、开发Kubernetes原生应用步骤介绍等。

如下是构建高分布式平台的大部分依赖:

  • 战略目标 (Strategy)
  • 存储计划(Storage)
  • 业务操作(Operation)
  • 业务连续性弹性与灾难恢复 (BCR&DR)
  • 应用开发 (AppDev)
  • 安全(Security)
  • 自动化(Automation)
  • 网络(Networking)
  • 提供(Provisioning)
  • 外部依赖 (External dependencies)
其中每类都包含多个子区域。下面我们将逐一说明。即使你熟悉这个话题,你也会发现脑图的价值。如果你想要更多,点击放大图片或查看https://github.com/mangirdaz/ocp-mindmap
appcoup-pod-150-150-768x1159.png
# 策略目标(Strategy)开始采用新平台之前,你的策略应该是你想到的第一件事情。你需要知道谁将使用你建立的这个平台。利益相关者是谁,最后谁负责?确定利益相关者通常是构建策略的最简单的部分。建立实践社区(CoP)将有助于制定战略和方向。一个精心设计和维护的CoP可以帮助您利用自己组织力量推动实现“正确解决方案”的进程。封闭式解决方案往往会错过组织需求。考虑对CoP进行一些研究。其次,你需要“微服务标准化”策略。在将服务放在新平台之前,强烈建议您定义一些标准。这里的挑战是旧的标准不适用于这个新的世界。尝试“复制 粘贴”,某些功能可能无法正常工作。# 存储计划(Storage)计划构建一个成功的平台,存储或持久性是关键考虑。你不仅需要简单的存储,而且需要可以高扩展的存储空间。你将需要为平台内部组件提供存储,如:日志、度量和容器镜像仓库。当然,你可以根据将要构建的解决方案选择不同的实现方式,如:对象存储、基于消息传递的日志服务,有时可能不需要考虑存储,但你无法完全消除对存储的需求。一旦我们开始说“持久性”,就意味着存储。开始思考如何做,谁将为你提供,以及你将拥有的存储空间(SSD,Magnetic,SAN,NAS等)。# 业务操作(Operation)操作部分与本文的下一节紧密相连。业务主要由以下两个领域组成:通常和无计划的行为(BCR&DR)。我们将在下一节中介绍无计划的行为。通常业务操作包括出口(egress)路由器配置、平台维护、补丁、调度规则创建、平台管理以及对平台上发生的事件的主动反应等内容。所有这一切,你将需要一个非常好的日志和监视。没有日志和监控,你将会“失明”,在高分布式系统中变得盲目。在分布式系统中,事情可能会从坏到更坏变得非常快,与“独立服务器”的基础架构相比,系统变得更糟糕。例如,如果出现故障,你的容器将重新调度(假设你没有自动伸缩功能,即使可以自动伸缩,你也可能花费巨大的费用)。在当前配置中重新调度=高密度,将意味着会遇到滚动失败情况。因此,你需要为运营定义标准运行手册,创建仪表盘,确保你了解你的环境,这是运营中最重要的事情之一。# 业务连续性弹性与灾难恢复(BCR&DR)我将业务连续性和弹性(BCR)和灾难恢复(DR)从业务中分开,它是非常重要的。即使在提供生产流量之前,我建议你知道你的“失败域”,及如何失败恢复。你需要制定一个计划。例如:如果你在分布式、可靠的、键值存储集群(如:在OpenShift / Kubernetes的etcd)中丢失了仲裁,或者知道外部DNS或存储提供发生故障。同时,存在多种不同的场景,如:不同的环境有所不同,某些内部/外部依赖关系具有较少或更成熟的提供,这些都取决于你所在的组织。# 应用开发(AppDev)你也需要考虑开发者。一些组织忘记了平台将主要由开发人员使用。只有开发者才知道他们想要在一个平台上运行什么。尽管事实上你将有很多工具,但你可能希望对模式和蓝图进行标准化。你应该考虑如下:
  • 应用开发人员将如何监控应用程序和节点性能?
  • 他们将如何推广和部署?
  • CI / CD管道(现有和新的)将如何与平台集成。
  • 你将如何推广来自环境的镜像?
  • 如何进行配置推广?
  • 最后,开发人员将会使用哪些开发工具?
开发人员的经验非常重要。得到这个权利,你的平台将被使用。错了,没有人会想使用它。# 自动化(Automation)这个特定的领域与其他领域有许多关系。你将需要使用CI / CD工具自动化你的应用程序、进行镜像测试和推广等。此外,你的基础设施应该被视为与你的应用程序相同 - 自动化无处不在。为此,你可以使用配置管理工具或仅依赖于部署工具。但是,如果你从一开始就以自动化的思想开始构建,那么即使在开始时它看起来很慢,它会变得想要的更快。# 网络(Networking)你需要考虑如何访问应用程序、出口(egress)和入口(ingress)流量,以及如何配置负载平衡器。决定是否将运行 “主动-被动” 或 “主动-主动”,及如何负载均衡所有的堆栈。容器是“网络中的第一个公民”或依赖SDN抽象?如何处理DNS?是否需要相互SSL?你的集群在2 - 5年内有多大?一个节点上运行多少个容器?所有这些问题都将定义你的平台的网络设计。# 安全(Security)尽管平台构建要考虑是安全的,但你仍然会有很多公开的问题。例如,你如何管理你的私密凭据(密码,证书)以及如何更新它们?你将如何将你的应用程序暴露给外界?最后,最重要的是,如何验证你正在运行的镜像?运行多租户、基于微服务的应用程序,镜像扫描和生命周期是关键问题。# 提供(Provisioning)提供非常广泛,与你的组织中的如下有很高的联系:
  • 配置管理:使用哪一个配置管理,以及它如何与平台支持的工具一起使用?一旦你确定了所使用的配置管理后,需要选择使用什么工具?你是否需要什么额外的工具?你是否需要红帽的订阅和生命周期管理吗?是否需要CloudForms控制台、能力和主动管理?需要什么可视化提供商及其功能?
  • 基础设施本身:你是否使用容器化部署或基于包/ RPM?如何满足你组织的补丁策略?例如,如果你的组织是基于RPM的,你选择了基于容器化/OSTree的平台,你的运维工程师如何知道它们的生命周期?
最后,你需要为平台做什么定制(预先及后期操作),使其符合你的需求?# 外部依赖(External dependencies)你会有很多外部依赖,请确保这些外部依赖的弹性和它们提供的SLA。外部依赖将包括如下:
  • 记录和监控:日志如何归档?集成哪些外部日志和监控解决方案?提供哪些元数据?
  • 存储:你将如何使用它?是否足够快?每秒输入/输出操作(IOPS)?如何扩缩?
  • 容器镜像仓库:如果你计划运行全局分布式集群,确定你的镜像的一致性?镜像仓库是否需要外部存储?如果是,什么格式?
  • 身份验证和授权:你将如何验证你的用户和应用程序?首选的身份验证提供商?如何进行基于角色的访问控制(RBAC)?
  • ITSM / CMDB:你需要在配置管理数据库中注册你的应用程序吗?如何自动化(或自动化解决方案?)。如何考虑变化?

# 架构
最后,一旦你知道你想去哪里(策略),你需要开始考虑更广泛的架构,例如基础设施密度、数据中心和可用性区域。已经覆盖的大部分领域与平台本身的架构有关。你需要在开始时做出正确的选择,因为分布式平台(OpenShift / Kubernetes等)构建和使用数百甚至数千个应用程序,难以修改。如果你网络错误,以后再进行更改是不可能的。如果平台不适应外部依赖关系与平台扩展的能力,将遇到瓶颈。

至此,我们介绍了这个旅程,你需要考虑什么。需要明白一点:公共云不能解决所有这些问题。这些可能看起来很难、不值得。但是,最终的结果就是全云不可知、水平和垂直可扩展的自助服务基础设施 ,这些将使开发人员开心。

原文出处:Enterprise OpenShift Deployment: What do you need to know?(翻译:范彬)

===============================================================
译者介绍:范彬,从事微服务、Docker和Kubernetes容器技术等方面的工作。可以关注译者的微信公众号:范范米饭。

DockOne微信分享(一九五):智融集团基于OpenShift的容器化PaaS平台实践

大卫 发表了文章 • 1 个评论 • 1887 次浏览 • 2018-12-07 16:32 • 来自相关话题

【编者的话】当前,是不是使用容器已经不是一个被讨论的重点;热点已然成为企业如何高效使用容器、如何利用容器给企业带来切实的收益。从底层的Docker到优秀的容器编排Kubernetes,都给我们带来了令人心动的基础。今天分享的OpenShift实践(基于Dock ...查看全部
【编者的话】当前,是不是使用容器已经不是一个被讨论的重点;热点已然成为企业如何高效使用容器、如何利用容器给企业带来切实的收益。从底层的Docker到优秀的容器编排Kubernetes,都给我们带来了令人心动的基础。今天分享的OpenShift实践(基于Docker和Kubernetes的容器PaaS云平台),希望可以给大家基于Docker和Kubernetes构建自己的容器云带来一点儿思路。
#概述与实践背景
OpenShift是红帽的云开发平台即服务(PaaS)。 通过OpenShift,企业可以快速搭建稳定、安全、高效的容器应用平台。在这个平台上:

* 可以构建企业内部的容器应用市场,为开发人员快速提供应用开发所依赖的中间件、数据库等服务。
* 通过自动化的流程,开发人员可以快速进行应用的构建、容器化及部署。
* 通过OpenShift,用户可以贯通从应用开发到测试,再到上线的全流程,开发、测试和运维等不同的角色可以在一个平台上进行协作。
* 支持LDAP用户权限管理,支持细粒度的权限资源管理。
* OpenShift可以提高应用从研发到上线的效率和速度,缩短产品上市的时间,可以有效地帮助企业推进DevOps,提高资源利用率,提升生产效率。

当前公司业务和产品处于快速迭代和扩展阶段,每个月平均新增10多个新服务,迭代速度越来越快,同时废弃的老服务服务也越来越多。新技术发展也比较快,在旧方式的基础上我们尝试新技术的成本也比较高,维护成本也比较高。如何提高我们的生产效率,如何提高我们适应新时代的能力,越来越重要。

在此背景下,我们先列出以下几个比较明显的问题:

  1. 阿里云上的资源利用率比较低
  2. 临时扩容比较慢,在运营活动的时候经常资源不足,需要升级配置,活动结束后资源利用率回归较低水平
  3. 新服务增加快,新机器、SLB等越来越多,越来越不好维护
  4. 微服务治理的重要性越来越突出

为了解决这些问题,我们调研了多个解决方案,最终选择了OpenShift作为PaaS平台;截止目前已有70多个项目、100多个服务、上千个pod运行在PaaS平台里(包括Python27、Python36、Java、Golang、Nodejs等类型服务)。
#OpenShift结构
1.png

2.png

3.png

4.png

5.png

OpenShift层级自底而上,分为:基础架构层、容器引擎层、容器编排层、PaaS服务层、界面及工具层。
##Docker
优势:构建一个隔离的、稳定的、安全的、高性能的容器运行环境;很多开源项目都做了自己的官方镜像,使用和维护更方便。
##Kubernetes
容器编排。

* 容器调度:按业务的需求快速部署容器到指定的目标
* 弹性伸缩:按业务的需求快速扩展或收缩容器的运行实例数量
* 异常自愈:当容器实例运行异常,集群能自动感知、处理并恢复服务状态
* 持久化卷:为散布在集群不同机器上的容器提供持久化的智能对接
* 服务发现:为业务为服务化提供服务发现及负载均衡等功能
* 配置管理:为业务应用提供灵活的配置管理及分发规则

##OpenShift
容器云,在Docker和Kubernetes的基础上提供了各种功能,以满足业务应用、研发用户及运维用户在生产效率上的诉求。

1)应用开发框架及中间件

OpenShift提供了丰富的开箱即用的编程开发框架及中间件,如Java、PHP、Ruby、Python、JBossEAP、Tomcat、MySQL、MongoDB及JBoss系列中间件等。

2)应用及服务目录

OpenShift提供了如软件市场式的服务及应用目录,可以实现用户一键部署各类应用及服务,比如一键部署Hadoop集群和Spark集群。

3)自动化流程及工具

OpenShift内置了自动化流程工具S2I(SourcetoImage),帮助用户自动化完成代码的编译、构建及镜像发布。

4)软件自定义网络

通过OpenVSwitch,OpenShift为用户提供了灵活强健的软件定义网络。实现跨主机共享网络及多租户隔离网络模式。

5)性能监控及日志管理

OpenShift提供了开箱可用的性能监控及日志管理的组件。通过平台,业务能快速获取运行状态指标,对业务日志进行收集及分析。

6)多用户接口

OpenShift提供了友好的Web用户界面、命令行工具及RESTfulAPI。

7)自动化集群部署及管理

OpenShift通过Ansible实现了集群的自动化部署,为集群的自动化扩容提供了接口。
#OpenShift核心流程
6.png

##一、应用构建
第1步,部署应用。流程的开始是用户通过OpenShift的Web控制台或命令行ocnew-app创建应用。根据用户提供的源代码仓库地址及Builder镜像,平台将生成构建配置(BuildConfig)、部署配置(DeploymentConfig)、Service及Route等对象。

第2步,触发构建。应用相关的对象创建完毕后,平台将触发一次S2I构建。

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

第4步,生成镜像。构建成功后将生成一个可供部署的应用容器镜像。平台将把此镜像推送到内部的镜像仓库组件Registry中。

第5步,更新ImageStream。镜像推送至内部的仓库后,平台将创建或更新应用的ImageStream的镜像信息,使之指向最新的镜像。
##二、应用部署
第6步,触发镜像部署。当ImageStream的镜像信息更新后,将触发平台部署S2I构建生成的镜像。

第7步,实例化镜像部署。DeploymentConfig对象记录了部署的定义,平台将依据此配置实例化一次部署,生成一个Deploy对象跟踪当次部署的状态。

第8步,生成ReplicationController。平台部署将实例化一个ReplicationController,用以调度应用容器的部署。

第9步,部署容器。通过ReplicationController,OpenShift将Pod及应用容器部署到集群的计算节点中。
##三、请求处理
第10步,用户访问。用户通过浏览器访问Route对象中定义的应用域名。

第11步,请求处理并返回。请求到Router组件后,Router根据Route定义的规则,找到请求所含域名相关联的Service的容器,并将请求转发给容器实例。容器实例除了请求后返回数据,还会通过Router将数据返回给调用的客户端。
##四、应用更新
在应用更新时,平台将重复上述流程的第1步至第9步。平台将用下载更新后的代码构建应用,生成新的镜像,并将镜像部署至集群中。值得注意的是,OpenShit支持滚动更新。在第9步时,平台将通过滚动更新的方式,保证应用在新老实例交替时服务不间断。
#部署结构
7.png

8.png

OpenShift集群:

  1. 所有服务组件都部署在阿里云上,即VPC网络的ECS机器上,分布在多个可用区
  2. 3个Master节点,3个etcd节点,1个Harbor节点,N个Node节点
  3. Node节点分布,根据业务类型打tag标签区分,分别为:

* Base集群,主要运行Router服务和Registry镜像仓库
* Inf集群,主要运行内部PyPI的Python内部源、Nexus服务(主要作为Maven的内部源)和Trace系统等
* Gateway集群,主要部署OpenResty的API网关服务,作为流量从公网域名进入集群的入口
* Service集群,主要部署公司的业务应用
* Bigdata集群,主要部署公司内部大数据服务

  1. 变更平台,通过调用OpenShift的API,来完成build镜像、通过Deployment的模板来更新容器,并记录当前Pod的代码版本。
回滚时选择代码版本对应的一个Deployment配置文件来进行重新部署(注意:Deployment是使用的img的对应hash值,启动参数主要通过ConfigMap、配置中心Apollo来管理)。

#服务镜像自动化及部署模板
SourcetoImage(S2I),S2I是OpenShit的一个重要功能。容器镜像是容器云的应用交付格式。容器镜像中包含了应用及其所依赖的运行环境。通常的做法必须基于外部的基础镜像构建包含企业自身开发代码的应用。这个镜像的构建过程是必须的,要么由企业的IT人员手工完成,要么使用某种工具实现自动化。

作为一个面向应用的平台,OpenShift提供了S2I的流程,使得企业内容器的构建变得标准化和自动化,从而提高了软件从开发到上线的效率。

一个典型的S2I流程包含了以下几个步骤。

  1. 用户输入源代码仓库的地址。
  2. 用户选择S2I构建的基础镜像(又称为Builder镜像)。Builder镜像中包含了操作系统、编程语言、框架等应用所需的软件及配置。OpenShift默认提供了多种编程语言的Builder镜像,如Java、PHP、Ruby、Python、Perl等。用户也可以根据自身需求定制自己的Builder镜像,并发布到服务目录中供用户选用。
  3. 用户或系统触发S2I构建。OpenShift将实例化S2I构建执行器。
  4. S2I构建执行器将从用户指定的代码仓库下载源代码。
  5. S2I构建执行器实例化Builder镜像。代码将会被注入Builder镜像中。
  6. Builder镜像将根据预定义的逻辑执行源代码的编译、构建并完成部署。
  7. S2I构建执行器将完成操作的Builder镜像并生成新的Docker镜像。
  8. S2I构建执行器将新的镜像推送到OpenShift内部的镜像仓库。
  9. S2I构建执行器更新该次构建相关的ImageStream信息。

S2I构建完成后,根据用户定义的部署逻辑,OpenShift将把镜像实例化部署到集群中。

除了接受源代码仓库地址作为输入外,S2I还接受Dockerfile以及二进制文件作为构建的输入。甚至可以完全自定义构建逻辑来满足特殊的需求。
#部署模板
通过自定义模板,可以创建我们自己应用发布时的快速部署模板。包括应用所需的多个Build Config、Deployment Config、Service及Route等对象。OpenShift的Template还有一个重要的特性就是参数化。用户可以在Template中定义参数,这些参数在用户部署模板时将显示在Web控制台的界面上,供用户输入。用户的输入最终以环境变量的方式传递到容器内部。一个OpenShift的Template在结构上主要分为三个组成部分:元信息、对象列表及参数列表。
9.png

10.png

#服务访问拓扑
11.png

容器里的服务都是阿里云VPC的网络,旧服务都是部署在阿里云经典网络的ECS机器上,互相访问时经典网络SLB是一个问题。需要将原来经典网络的SLB换成VPC的SLB。

* 容器互访:通过Hostname+Port方式,后端是Service Ip
* 容器访问经典网络服务:将经典网络的SLB,替换成VPC的SLB
* 经典网络的服务访问容器:a、HTTP服务,通过内网域名;b、RPC服务,通过映射的Node+Port
* 外网访问容器里的服务:外网域名——>外网SLB——>API网关——>容器service的HOSTNAME——>Pod

#踩过的坑

日志搜集:因为Pod来回漂移,我们采用了阿里云的NAS存储(NFS)作为PV;日志文件会被共享,并被抢占文件句柄,导致日志里有乱码;我们的处理方式是日志中间加一个hostname,例如:interface.paris-interface-service-c-44-bx52z.debug,日志收集时flume匹配interface..debug。
* 监控:OpenShift自身的监控是使用Hawkular+Cassandra+Heapster,只有容器的CPU、内存和网络的监控。Pod版本的一致性、events监控(包括imge的生命周期、Pod的生命周期、服务健康检查等)、PV的监控等,都需要通过API来获取并加入到自建的监控平台
* CronJob不太稳定,crash时间比较长的情况下,Job下一次执行时间延迟过久,导致不再执行。这种情况只能删除后重新部署Job。
* 版本的控制,Deployment需要通过配置里的image的hash值,找到对应的build(build信息里记录了代码版本信息,包括commit、commit message、更新人和提交时间等);回滚的时候要选择代码版本对应的build,然后再找到build对应的Deployment配置进行部署,这些都通过自建的变更平台来管理。

#我们正在做的事情

  1. 将API网关替换成Kong,目前已经测试了大部分的功能,但Docker版本的稳定性比较差,QPS在很小的情况下也有很多5XX的错误。解决方案:在Kubernetes的Node节点上部署
  2. 由于现在对外业务发展比较快,计划通过模板、环境变量和配置中心来创建一整套的Applications,类似于SaaS服务。
  3. 打通QA,研发人员从提交代码转换到交付一个线上可用的image

#OpenShift对容器化PaaS平台实践的借鉴意义

  1. S2I自动构建服务镜像,实现自动化的流程。
  2. 部署模板,根据某一类型服务进行快速部署deployment(例如Python的模板、Golang的模板等);在每一个版本的deployment配置中,img会使用镜像的hash值(例如:docker-registry.default.svc:5000/XXX/XXX@sha256:899007bXXXXXXXX95f561203cba);回滚时选择对应版本的deployment配置进行重新部署即可达到回滚的目的。
  3. Router服务使用haproxy(支持AB部署,及流量分配),Router根据Route定义的规则,找到请求所含域名相关联的Service的容器,并将请求转发给容器实例。容器实例除了请求后返回数据,还会通过Router将数据返回给调用的客户端。
  4. LDAP用户管理等多用户管理,通过clusterrole/clusterrolebinding(全局资源权限)、role/rolebinding、serviceaccount、oauthaccesstoken等来实现权限分配。

#Q&A
Q:请问通过OpenShift怎么做高可用?

A:OpenShift的高可用是跟Kubernetes的高可用一致的,就是延用了Kubernetes的Service IP来做服务发现和负载均衡;另外,前面还有DNS服务,(例如:Hostname: service.abc-platform.svc,后端就是指向的Cluster IP)。



Q:image的版本管理怎么做?配置的版本管理怎么做?

A:OpenShift本身会创建一个内部的Registry,S2I会根据Build来更新image,image的版本是通过sha256的值对应Build的版本号(例如:Build版本#24对应registry.intra.XXXXX.com/abc/abc-platform@sha256:b41c8XXXXX)。



Q:OpenShift的网络方案性能方面有多少损耗?与Kubernetes的其他网络方案如Calico相比如何?

A:OpenShift的网络方案是选择的OVS,性能上肯定是不如Calico,但对于我们的服务足够了。目前跑了有1000+个常驻Pod,没有出现网络瓶颈。



Q:如果要做多机房部署,OpenShift能搞吗?

A:多机房,只要网络能通并比较稳定,是没有问题的。我们的部署方案就是在阿里云上的多个可用区部署的。而且,实际部署中也是鼓励多机房部署的,服务容灾要求一个服务要分布在多个可用区(或者机房)。



Q:你们平台性能怎么测试的,如何集成?

A:平台性能测试分两部分。一个是在集群内部不经过Nginx直接压测SVC,另外一个是在集群外部压测内网域名或者公网域名。



Q:我们之前用自己的镜像仓库,会报tls认证错误,不知道你们有没有遇到过?

A:我们自己创建的镜像仓库是用Harbor来建的,只是存储了内部服务的基础镜像。OpenShift集群内的镜像仓库有自己的认证,是通过Secrets管理对应权限的token(用户级别的权限是通过LDAP统一认证的)。S2I创建服务镜像的时候就是通过这个来获取权限的。还是比较稳定的,没有出现过tls的问题。



Q:生产环境的节点磁盘是怎么分区分配的?

A:生产环境的节点磁盘分为两类,一个是系统盘(40G),另一个是数据盘(100G);Docker的所有镜像、实例等存储都是放在数据盘,服务的日志是挂载的PV,PV是使用的阿里云的NAS存储。



Q:我看你们在集群访问前加了个API Gateway,用了OpenResty,这个能详细介绍下不?

A:可以简单理解为一个自建的Ingress,底层是Nginx+Lua实现的;我们已经在逐渐使用Kong来代替OpenResty了。Kong底层是Nginx+Lua+PostgreSQL,界面管理是使用的Konga。选择Kong和Konga的理由是:1、Kong的管理和插件比较方便,还支持自定义插件;2、Konga的界面更友好,管理、更新都比较方面。



Q:看到你们是3 Master高可用,请问Kubernetes–Master挂了一台,OpenShift如何恢复,etcd是否有做备份?

A:我们做过演练,Master挂一台或者两台,甚至三台全挂,也不会影响已经运行的服务。只要etcd没有问题,重启Master或者新增一台Master都可以快速恢复集群。etcd的备份是做的全量备份,上传到我们的备份存储中。



Q:内网域名是怎么跨VPC和经典网络的?

A:内网域名的DNSPod解析是解析到VPC网络的一台ECS机器上,通过Nginx来管理。经典网络的ECS服务集群可以在前面挂载一个VPC网络的SLB,Nginx的对应Server Name的Upstream设为这个VPC的SLB即可。



Q:监控和告警好像没有提到,感觉OC这块似乎缺失?
A:OpenShift本身有很多服务状态,image的Build、pull、push等状态,以及Pod的各种状态;另外还有强大events。我们是通过OpenShift的API来获取这些信息,同步到Open-Falcon上来处理报警信息的。

Q:日志统一生产、采集用了什么方式能介绍一下吗?

A:日志是一个比较疑难的问题;我们的解决方案是,所有的日志都打到同一个NaS盘上,服务名来做该服务的日志路径。多个Pod打同一个日志的时候,会出现乱码。我们的解决方案是在日志中间加hostname,例如haadeers.melon-crm-b-9-nb9d5.log,melon-crm-b-9-nb9d5为Pod的hostname。日志收集,是几台ECS机器挂载改NAS盘,启动flume来收集。



Q:业务应用的自动化发布你们是怎么设计的,能否详细说说思路?

A:我们是自己定义了S2I的流程。Build镜像统一在代码仓库里加一个Makefile,定义一个assemble的role,来做初始化(依赖包的安装、nodejs的Build等等,这里还是比较灵活的)。自动发布:我们是自定义了部署模板,分享的内容里有(只需要简单的几步就可以部署一套服务)。



以上内容根据2018年12月4日晚微信群分享内容整理。分享人徐义义,智融集团容器化PaaS平台负责人,从事云计算开发、设计6年(其中4年私有云,2年公有云<京东云>),目前负责公司内部PaaS平台的建设与应用的变更交付。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesd,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

DockOne微信分享(一五七):一个可供参考的企业应用容器化实践案例

alex_wang2 发表了文章 • 2 个评论 • 2978 次浏览 • 2018-01-10 21:37 • 来自相关话题

【编者的话】本次分享分为两个部分,第一部分比较常规,介绍如何用OpenShift搭建自动化测试、开发环境。第二部分介绍了在容器使用过程中遇到的问题,以及应对方案。作者在原有的OpenShift Router仅支持7层协议的基础上,对其进行支持4层协议的扩充,联 ...查看全部
【编者的话】本次分享分为两个部分,第一部分比较常规,介绍如何用OpenShift搭建自动化测试、开发环境。第二部分介绍了在容器使用过程中遇到的问题,以及应对方案。作者在原有的OpenShift Router仅支持7层协议的基础上,对其进行支持4层协议的扩充,联合CoreDNS,让平台的使用者不用记录灵活多变的IP地址,仅需自己定义自己熟悉的hostname(或者由平台自动生成)就访问对应资源,让使用者几乎没有感知的使用容器还是物理机或者虚拟机。

在这里跟大家分享一下容器使用方面的经验,说的不好,肯定有不对的地方,还希望大家多多批评、指正。
#OpenShift简介
我们都知道在容器编排领域,有3个最著名的编排系统,它们分别是Docker Swarm,Mesos和Kubernetes。其中Docker Swarm是Docker公司自己推出的容器管理和编排系统,由于起步较晚,目前大规模的应用尚不多见。

Mesos是Apache下的开源分布式资源管理框架,它被称为是分布式系统的内核。Mesos最初是由加州大学伯克利分校的AMPLab开发的,后在Twitter得到广泛使用。

Kubernetes和Mesos同样是起源于Google的Borg,是现在应用最广,用户数和社区活跃度最高的容器编排系统。OpenShift也是出于这个考虑,最终选择Kubernetes作为底层架构。OpenShift是一个开源容器云平台,他是基于主流的容器技术Docker和Kubernetes构建的云平台,作为一个开源项目,他已经有5年的历史,其最早的定位是一个应用云平台(Platform as a Service)。在Docker时代来临之前,各个厂商和社区项目倾向构建自己的容器标准,比如Cloud Foundry的Warden、OpenShift的Gear,但是在Docker成为主流及社区的技术发展方向之后,OpenShift快速的拥抱了Docker,并推出了市场上第一个基于Docker及Kubernetes的容器PaaS解决方案。当然很多人会有疑问,OpenShift与Docker及Kubernetes的关系究竟是什么

OpenShift是基于容器技术构建的一个云平台。这里所指的容器技术即包含Docker及Kubernetes。如下图所示:
93045234accd1ea8fcaa59f8776f326f.png

OpenShift底层以Docker作为容器引擎驱动,以Kubernetes作为容器编排引擎组件。OpenShift提供了开发语言、中间件、自动化流程工具及界面等元素,提供了一套完整的基于容器的应用云平台。OpenShift有三个版本,分别是企业版,社区版和在线版,我们这里使用社区版(origin版)用做我们企业内部的部署
#部署介绍
我们的架构比较典型,前端有2个负载均衡器(HAProxy),两个负载均衡通过Heartbeat共享一个VIP,后面连接3个master node,每个master node上分别运行了一个etcd,后面连接了N个slave node,因为集群中所有的状态都会持久化到etcd中,所以api server基本上是无状态运行,当有节点需要和master node打交道时,先访问这个VIP,然后连接到vip后面的一个haproxy上,haproxy再选择其中一个master node上的api server进行与etcd通信,下面是我的HAProxy的配置文件`haproxy.conf`:
backend atomic-openshift-api
balance source
mode tcp
server master0 10.10.xx.xx:8443 check
server master1 10.10.xx.xx:8443 check
server master2 10.10.xx.xx:8443 check

同时,其实我们如果集群里面有其他的高可用的需求,比如我们使用了Harbor做为私有镜像仓库,三个镜像仓库配置了Replication规则,我们通过VIP推送一个镜像到3个中的任意一个仓库,其他两个仓库也会存在我们的同样镜像,从而实现高可用。下面是Haproxy的配:
`harpoxy.conf`

backend harbor-proxy
balance source
mode tcp
server harbor1 10.10.xx.xx:80 check
server harbor2 10.10.xx.xx:80 check
server harbor3 10.10.xx.xx:80 check

#运行流程
第一个是每天定时的作业,这个比较简单,我们快速过一下:首先在Jenkins里做定时任务,时间定为0:30,从代码下载代码,在编译之前进行代码风格检查(snoar)和执行单元测试(junit),然后进行编译,把编译出的二进制包和事先写好的`Dockerfile`进行docker build,然后把输出的docker image push到Harbor的镜像仓库,同时这个编译的jenkins job会触发,测试部署job,测试部署job执行时会触发OpenShift里的test project,我们把PullPolicy设置成alway是,每次执行从新部署,都会触发新的镜像的拉去,从而达到从新部署的目的,测试项目分为两个阶段,自动化测试和压力测试,自动化测试采用selenume和robot结合的方式,在测试结束之后生成测试报告。压力测试使用jmeter。结束之后也会以邮件的方式发送给订阅者。如果所有测试通过之后,测试job会先打给这个image打一个tag,然后push到Harbor的ready项目中。同时发送通知邮件。

待第二天测试人员需要测试的时候,它们会通过我们的release manager去拉去ready项目中最新的image,然后部署到它们自己的project里,进行特定功能的手工测试。整体流程大概如此。

第二个是针对开发测试环境的,由于资源有限,我们的开发人员也用容器的方式部署每个人的开发环境,这里OpenShift/Kubernetes多租户的优势就体现出来了。在我们的容器平台上,每一个开发人员对应一个账号,每一个账号就是我们OpenShift里的一个project,由于每一个project都可以做资源限制,所以只要给大家按照需求分配好固定份额,就可以做到资源的共享和互不干涉。举个例子,比如说,我们有一个项目,它分为前端和后端。前端人员开发的时候,他只关心前端代码,后端代码基本仅仅是调用而不做修改,那么这个时候我们就可以把前端打成一个docker image,后端打成一个docker image(这里是举个例子,实际情况可能比较复杂,可能前后端都不止一个image),然后在前端开发人员写完前端代码后,把自己的代码通过NFS共享给Release Manager,(NFS挂在可以是实现挂载好的,Windows、Linux、MacOS均可以,代码就可以直接存在上面),然后点击Relea se Manager上面的前端发布按钮,就会触发Jenkins中的一个front-end job,它会完成代码编译,打包和镜像推送,最后在这个开发人员对应的project里从新部署前端代码的整个过程,接着这个开发人员就可以通过url(比如http://test/frontend-dev1)去访问它刚刚部署的页面。如果是后端人员也会有同样的流程,具体就不在赘述了。具体说明一点,由于OpenShift集成了Router功能,也就是类似于Kubernetes里的Ingress,但是它是用Haproxy实现的,并且能够在Yaml文件中对Router进行配置,刚才引用的url我们可以配置成http://{集群名}/{项目名},所有整个集群都是用7层协议通过Router对外提供访问。

生产和预发布环境,这个环境的配置除了要求高可用之外,就是环境的硬件配置,一般意义上来将,预发布的环境因该不高于正式生产环境。当决定对测试通过的镜像版本进行上线时,首先会用docker tag把它换成tag换成pre-release,经过压力测试和手工最后的verify就可以发布到正式环境中了。
#调试问题的解决
下面我们着重讲一下在实际部署和使用容器过程中,遇到了哪些问题,以及是如何去解决:

1、有一个问题也许很多刚刚使用容器的公司都会遇到,就是开发人员喜欢把容器当成虚拟机用,在遇到程序bug的时候,很多开发者都喜欢SSH到容器里,亲自看看log,或者是尝试替换一下它们程序的debug版(这里的所谓debug版,就是开发人员在代码里加入一些调试信息或者print一下log),然后重新启动应用。这里我们不推荐给容器内部安装SSHD,因为首先容器的IP是临时分配的,我们无法确定的告诉开发者它这一次的IP地址是多少,即使告诉了他也不一定能够访问的到(我们的容器系统网络层和外界不是一个网段),那么如何解决这个问题呢?我们开始也是尽量说服开发者,学会用log去debug,因为我们前面已经把log通过ES进行了收集,用Kabina可以去查看,但是没有办法,有些开发人员还是习惯自己去cat或者vim打开日志文件。所以这里我们就用到了OpenShift里提供的一个oc子命令oc get pod和oc exec,前者用来得到当前用户所在的项目中的Pod列表,后在类似docker exec命令可以直接跳进容器里(Kubernetes中也提供类似的命令),当开发人员需要把里面的日志文件拷贝出来,后者是拷贝一个debug版本的程序到容器里去运行时,可以用oc cp(同样这个在Kubernetes里也有类似的命令)。虽然这几个命令据我观察有一些bug,比如拷贝的目标目录不太准确,而且对容器里的tar命令有一些依赖。但这都是小问题,不影响使用,如果觉得几条命令结合起来使用有些麻烦,可以自己用Python脚本进行一个简单的封装,这样当开发人员使用的时候会更加简单。

2、每一个系统几乎都或多或少的使用了一些第三方工具,比如MySQL、MyCAT、Redis、ZooKeeper,这些组件我们都把它们进行了容器化,以便实现资源的整合和方便部署。那么这就引发了另一个问题,开发人员在碰到程序bug的时候,往往需要直接去连接这些第三方组件,去改修改和查看里面的信息。比如他需要查看Redis的键值是否存在,查看数据是否写入到了数据库里。这个时候,由于所有组件在容器中,你不知道它的准确IP,你可能很容易想到用OpenShift提供的router功能去像刚才的url那样提供外界的访问,但是由于这些中间件是4层的协议,而现有OpenShift的Router功能仅仅支持7层协议,所以我们为了解决这个问题就必须实现OpenShift的4层代理功能。通过修改Openshift的源代码haproxy-templte.conf和router部分的相关代码,然后通过yaml route的annotation段,定义一个规则,把对应的端口传进router的配置文件,让后端的第三方应用程序通过router节点对应的端口(Haproxy里的mode tcp)从而实现router代理4层协议的目的,但是这会导致另一个问题,因为router的每一个端口只能映射给一个后台应用,比如3306端口,只能映射给一个MySQL,如果有两个开发人员都要调试MySQL,那第二个开发者的MySQL的映射端口肯定就得用除了3306以外的端口(比如3307、第三个人3308等)那么就会产生一个问题router的映射端口如何告诉开发人员呢?这个问题有两个解决办法,第一个是通过一个Web UI,去显示的告诉开发人员他所有的资源对应的router节点的端口号,但是这有一个不方便的地方,如果的资源对应的Pod被重置了,那么他的端口号也就会被改变,即使端口号不改变,记起来也比较麻烦(大型的项目可能要用到5、6个中间件产品,每一个开发人员都要记自己的那套环境的端口号,还要学会如何用自己的工具去修改默认的端口号显然给开发者添加了许多不必要的麻烦。那么基于此问题的考虑,我们使用了第二种方法,即我们内部实现了一个DNS,开发者每个人的资源的IP都可以用DNS查到,比如developer1,他有Redis、MySQL、MyCAT、ZooKeeper、PostgreSQL等资源,那他的这些资源对应的DNS域名为:developer1.redis developer1.mysql developer1.mycat developer1.zookeeper等,但是由于DNS只能返回IP地址,无法返回端口号,我们还是得不到router节点对应资源的端口号。为了解决这个问题,我们可以用Haproxy加别名的方式,比如运行命令ifconfig eth0:1 10.10.xx.xx意思就是给eth0的NIC,加上了一个虚拟的IP10.10.xx.xx,那么此时,这个网卡eth0就有两2个IP,并且此时都能ping通。(当然实际实现的时候,我们不可能用是ifconfig命令完成这个网卡别名的方式,那样太low,也太不可靠了,我查看了ifconfig的源代码,把其中设置IP地址的代码集成到了我们修改过的OpenShift里)。然后router的haproxy.conf做bind的时候就需要指定这个IP,端口号还用原来这个应用默认的端口号如下图所示,这样开发人员不用每次都记住不同的端口号,仅仅配置一个DNS,不管环境发生了什么样的改变,都可以用默认端口和hostname去连接自己的资源进行调试。

3、配置Docker容器的时候,默认使用的是DeviceMapper方式,然而这种方式有众多的限制,可以参考https://docs.openshift.org/3.6/install_config/install/host_preparation.html#configuring-docker-storage中的详细配置说明,在生产环境中我们采用的是第2种方式。

#Q&A
Q:所有开发人员都是用一套OpenShift集群测试吗?CI/CD也是同一套环境吗?

A:我们是按业务分的,原则上,一套业务线(一个业务部门)用一套系统,这样成本上也好分摊。



Q:OpenShift也是用Go编写的?

A:是的,OpenShift用的是源码级别的和Kubernetes的集成,不是通过client-go或者REST API的方式,所以我们可以看到,Kubernetes发型的版本总是比OpenShift的快1 到2个版本。



Q:对于OpenShift比较适合多大规模的团队?

A:这个怎么说呢,其实引入DevOps或者CI/CD的流程就是为了给企业减少人员成本,让有些能够自动化的东西通过计算机运行起来。所以因该是人员越少越好,但是人员如果少,就要求每个人的技术能里比较强,开源的东西往往用起来不难,但是真到出了问题的时候就需要看代码去解决了。所以如果人少的话,可能每个人就要求懂得就比较多一些。



Q:router本身是否具备HAProxy?

A:OpenShift的Router就是用HAProxy实现的,当然我现在用的是3.6.1的版本,我不知道以后会不会支持Nginx或者其他别的LB,因为我看到代码里已经有关于Nginx的相关配置了,但是没有激活。OpenShift使用HAProxy的大致流程就是通过一个Yaml或者jason文件,配置一条route信息,然后通过api-server持久化到etcd中,router的代码启动一个goroutine,去通过api-server watch etcd,然后根据配置信息和环境变量,通过haproxy-template模版,去生成 haproxy.conf,然后去动态reload。



Q:OpenShift的project和Kubernetes的namespace是一对一的关系么?project可以设置资源配额么?怎么设的?

A:是一对一关系,当然有一些namespace 是有一些特殊意义的,不建议在里面跑应用。project可以设置资源配额,具体怎么设置就比较复杂了,建议参考一下官方文档,简单的说就是可以根据CPU内存做资源的限定,这个和Kubernetes是一样的。



Q:OpenShift中原生性能指标监控服务的Pod总挂有没有相应的解决办法?

A:解决Pod总挂的问题就得具体问题具体分析了,我记得它做性能监控的那个Pod比较吃资源,其实可以对他进行一下限定,比如:oc env rc hawkular-cassandra-1 MAX_HEAP_SIZE=1024M -n openshift-infra。



Q:OpenShift中的router默认情况下是跑在Pod里的,那么当service特别多,route规则也特别多的时候,如何解决router服务的性能问题的?

A:这是一个好问题,但其实我觉得这个和HAProxy有很大的关系,跟在不在Pod中关系不大,因为router这个容器其实用的是主机网络,那么这个问题其实就转化成了如何提升HAProxy的性能,这种情况其实有很多可以参考的方案,比如前面在加一层LVS负载,或者用DNS做域名解析的时候进行一定的负载功能。



以上内容根据2018年1月9日晚微信群分享内容整理。 分享人王晓轩,某传统互联网公司基础技术部技术经理,关注容器技术及DevOps PaaS领域,2014年关注容器技术,深度接触过Cloud Foundry等开源PaaS产品, 现主要研究OpenShift及周边技术。 DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesa,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

如何用S2I创建一个builder image

alex_wang2 发表了文章 • 0 个评论 • 5258 次浏览 • 2017-12-08 00:25 • 来自相关话题

【编者的话】众所周知,在容器编排领域,Kubernetes的市场占有率遥遥领先。但编者认为Kubernetes是面向运维人员的分布式系统,所以Redhat公司在Kubernetes的基础上推出了OpenShift,除了支持Kubernetes原有的所有功能外, ...查看全部
【编者的话】众所周知,在容器编排领域,Kubernetes的市场占有率遥遥领先。但编者认为Kubernetes是面向运维人员的分布式系统,所以Redhat公司在Kubernetes的基础上推出了OpenShift,除了支持Kubernetes原有的所有功能外,针对Paas平台的特点,对Kubernetes进行了深度开发。文中介绍的S2I就是OpenShift之所以区别于Kubernetets,能成为PaaS平台的实现中一个主要环节。通过选择对应的image,编写assmble和run的脚本,最终生成包含依靠源代码生成的可以执行文件的docker image,并利用OpenShift现有的体系架构(BC和DC)直接发布,完成所有的应用的生命周期管理,让开发者真正仅仅关注与代码本身,这才是一个真正的Paas平台需要实现的。
openshift.png

Source-To-Image(S2I)是一个在创建builder image时可以独立运行的常用的工具。在OpenShiftV3里,它还是一个创建应用程序的主要手段。这是因为它具备如下优势:

- 速度快 —— 用S2I在应用程序编译的过程中可以在不创建新的layer的情况下执行大量的复杂操作,导致编译速度加快。
- 可以打补丁 —— 比如当你发现你的image存在一个安全问题时,S2I可以帮助你持续不断的rebuild你的应用程序。
- 用户效率高 —— S2I防止开发人员随意的用yum对image进行安装操作,因为这种操作会拖慢开发迭代的效率。
- 生态性 —— S2I鼓励共享一个生态系统的image,你可以利用它去运行你的应用程序。

这篇文档就是关于如何去创建一个简单的S2I的builder image。

总的来说,S2I利用源代码和builder Docker image去生成一个新的Docker image。S2I这个项目本身其实已经包含了一些常用的Docker builder image,比如用来创建PythonRuby的builder image,当然你也可以根据需要去扩展它。

在讨论如何去创建一个builder image之前,我们先来解释一下S2I是如何工作的以及build image过程中builder image的角色。S2I(Source-To-Image),从名字上可以知道,它其实是负责把你应用程序源代码转换成可以在OpenShiftV3或者用`docker run`运行的Docker Image。所以这个Builder image包含了一些能够生成可执行镜像的所需的能力(也成为:build artifact)。整个的build过程包含对生成的image的调用,分几个步骤完成。在这边文章里我们用一个最简单的流程说明:

1. 下载S2I脚本(或者用builder image 里面的)
2. 下载你的应用程序代码
3. S2I stream脚本们和应用程序代码到builder image的容器里
4. 在Build image里执行定于好的编译脚本(这个脚本就是上一步stream进去的)
5. 保存这个容器到一个新的image

当然,为了让builder image完成所有这些工作,我们还需给它一些传入一些content。首先,因为builder image 负责实际意义上的build 你的源代码,所以它必须包含build和运行这个应用程序所依赖的libray和tools。比如,一个tomcat的builder image需要包含Tomcat,JDK和Maven。一个Python builder image包含Gunicorn或者cherry.py,SciPy的二进制包和pip。其次,它还需要包含一些执行build和run操作的逻辑脚本。这部分被下面两个S2I的脚本调用:

  • `assemble`——负责build一个应用程序
  • `run`——负责运行一个应用程序

下面的我将介绍如何利用lighttpd server的docker image构造一个builder image去运行一个静态的html页面。

当然,这其中还包括了用GitHub存储创建过程中产生的文件。

现在,我们开始我的旅程,首先从https://github.com/openshift/source-to-image/releases/ 获取最近版本的S2I(作者在写此篇文章时的最新版是1.0.9,译者翻译时是1.1.8)
#步骤 1
S2I 是一个独立的命令,能够创建一个builder image所需要的目录结构,从这里可以得到更多关于它的信息,现在我们用下面命令创建出所必须的结构:
s2i create lighttpd-centos7 s2i-lighttpd

我调用了`s2i create`命令传入了我想要的builder image的名字(`lighttpd-centos7`)它的目录结构也已经被创建完成(`s2i-lighttpd`)。如果这个目录不存在,这个命令就会为你创建它。所以,运行上面那个命令你将得到下面这个目录结构:
s2i-lighttpd/
├── Dockerfile
├── Makefile
├── README.md
├── s2i
│ └── bin
│ ├── assemble
│ ├── run
│ ├── save-artifacts
│ └── usage
└── test
├── run
└── test-app
└── index.html


#步骤 2
为了定制builder image,我们需要修改那个刚刚用S2I命令生成的`Dockerfile`。
# We are basing our builder image on openshift base-centos7 image
FROM openshift/base-centos7

# Inform users who's the maintainer of this builder image
MAINTAINER Maciej Szulik

# Inform about software versions being used inside the builder
ENV LIGHTTPD_VERSION=1.4.35

# Set labels used in OpenShift to describe the builder images
LABEL io.k8s.description="Platform for serving static HTML files" \
io.k8s.display-name="Lighttpd 1.4.35" \
io.openshift.expose-services="8080:http" \
io.openshift.tags="builder,html,lighttpd"

# Install the required software, namely Lighttpd and
RUN yum install -y lighttpd && \
# clean yum cache files, as they are not needed and will only make the image bigger in the end
yum clean all -y

# Defines the location of the S2I
# Although this is defined in openshift/base-centos7 image it's repeated here
# to make it clear why the following COPY operation is happening
LABEL io.openshift.s2i.scripts-url=image:///usr/local/s2i
# Copy the S2I scripts from ./.s2i/bin/ to /usr/local/s2i when making the builder image
COPY ./.s2i/bin/ /usr/local/s2i

# Copy the lighttpd configuration file
COPY ./etc/ /opt/app-root/etc

# Drop the root user and make the content of /opt/app-root owned by user 1001
RUN chown -R 1001:1001 /opt/app-root

# Set the default user for the image, the user itself was created in the base image
USER 1001

# Specify the ports the final image will expose
EXPOSE 8080

# Set the default CMD to print the usage of the image, if somebody does docker run
CMD ["usage"]

#步骤 3
一旦`Dockerfile`修改完成,我们就可以填充builder image的剩余部分,也就是S2I的那些脚本。首先我们从`assemble`脚本开始, 它是负责build我们的应用。在这个例子里,它仅仅负责拷贝源代码到Lighttpd server的目录下:
#!/bin/bash -e
#
# S2I assemble script for the 'lighttpd-centos7' image.
# The 'assemble' script builds your application source ready to run.
#
# For more information refer to the documentation:
# https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md
#

echo "---> Installing application source"
cp -Rf /tmp/src/. ./

默认情况下,`s2i build`命令将源代码放在`/tmp/src`目录下,在build过程中这个目录下的源代码文件和其他相关文件会被使用。当然你也可以通过修改`io.openshift.s2i.destination`这个label或者传入`--destination`这个参数去修改它,如果是那样的话源代码会被放在你指定的目录下的`src`子目录下。在相面cp命令中的第二个参数`./`是用来设置在`openshift/base-centos7`中的工作目录,也就是`/opt/app-root/src`。

好,现在让我去处理第二个脚本文件`run`,故名思义它负责运行我们的应用程序。在我们的例子中,它会启动lighttd server:
#!/bin/bash -e
#
# S2I run script for the 'lighttpd-centos7' image.
# The run script executes the server that runs your application.
#
# For more information see the documentation:
# https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md
#

exec lighttpd -D -f /opt/app-root/etc/lighttpd.conf

在上面的命令中我们用`exec`去`run`lighttpd的进程。这是为了所有由Docker发送给lighttpd的信号都会输出到`stdout`和`stderr`。

因为在我们的例子中,我们不关注增量build,所以我们可以去掉`save-artifacts`这个脚本。最后我们写一些关于如何使用这个build image的方法在`usage`脚本中:
#!/bin/bash -e

cat < This is the lighttpd-centos7 S2I image:
To use it, install S2I: https://github.com/openshift/source-to-image

Sample invocation:

s2i build https://github.com/soltysh/sti-lighttpd.git --context-dir=test/test-app/ lighttpd-centos7 sample-app

You can then run the resulting image via:
docker run -p 8080:8080 sample-app
EOF

注意:修改这个脚本的执行权限,推荐为`755`。
#步骤 4
我们builder image还剩最后的一部分,一个关于lighttpd的配置文件。这个配置文件会被`Dockerfile`和`run`脚本去使用。如果没有这个配置文件,lighttpd恐怕不能够正常启动。我用下面的配置为lighttpd生成配置文件,将其保存在`s2i-lighttpd/etc/lighttpd.conf`。
# directory where the documents will be served from
server.document-root = "/opt/app-root/src"

# port the server listens on
server.port = 8080

# default file if none is provided in the URL
index-file.names = ( "index.html" )

# configure specific mimetypes, otherwise application/octet-stream will be used for every file
mimetype.assign = (
".html" => "text/html",
".txt" => "text/plain",
".jpg" => "image/jpeg",
".png" => "image/png"
)

这是一个关于lighttpd的最简单配置文件,它仅仅设置了:

* lighttpd处理的源代码路径(`/opt/app-root/src`)
* lighttpd监听的端口号(`8080`)
* 默认文件的路径(`index.html`)
* mimetype影射属性

到这步,我们的builder image已经做完了。我们能够确定的是`s2i-lighttpd`目录下的`make`命令会在适当的时候去build它,当然你已经发现了这是因为里面有一个`Makefile`,里面调用了`docker build`命令。
#步骤 5
现在我们用一个简单的应用程序去测试这个builder image。我创建了一个`index.html`在`s2i-lighttpd/test/test-app`目录下:(译者注:在现在的1.18版本中,这个测试文件会被自动创建)



test-app


Hello from lighttpd served index.html!



有了这个文件,我们现在可以运行s2i build了。在`s2i-lighttpd`目录下执行下面的命令:
s2i build test/test-app/ lighttpd-centos7 sample-app

我们从`test/test-app`目录下创建了一个应用程序,用了我们刚刚的build image(`lighttpd-centos7`)。生成了一个叫`sample-app`的image。默认的S2I build 会打印出所有的由`assemble`产生的输出,所以你会看到下面的输出在你的终端上(你可以忽略关于用本地builder image和非git目录产生的warning):
---> Installing application source

现在我们实际的测试一下生成的image。运行这个image,暴露容器的8080端口到主机上:
docker run -p 8080:8080 sample-app

现在你可以通过http://localhost:8080/访问到`index.html`的内容了。
#步骤 6
这里面还有一些需要补充的部分:tests,大多数情况下可以用`s2i-lighttpd/test/run`这个脚本去处理,假设你在`Dockerfile`里选择默认的端口。在这个情况下,你可以在`s2i-lighttpd`目录下运行下面这个命令:
make test

注意:设置test/run的执行权限,建议设置成700。

这个脚本会运行`s2i build`(请却保`s2i`在你的环境变量path里)用`test-app`作为应用程序源代码,并且执行一些测试以确保image的可用性。这些测试包括:

* 检查`s2i build`运行没有出错
* 检查`usage`脚本运行正确
* 测试最后生成的image
* 检查运行应用程序的容器有正确的响应

在这个例子里我没有增加对`test/run`的说明,因为那个文件太长了。感兴趣的读者可以去看一下https://github.com/soltysh/sti-lighttpd/。提交的历史可以准确地显示本文中给出的每一个步骤。

现在祝贺你,已经有了一个可以使用的S2I builder image,使用S2I(Source Build strategy in OpenShift),处理一个在git代码仓库里的html文件,并且生成一个新的image去对外提供访问。总的来说,通过修改`assemble`和`run`这两个脚本可以很容易的定义其他类型的builder image,然后用相应的image去运行对应类型的应用程序。

原文连接:How to Create an S2I Builder Image (翻译:王晓轩)

DockOne微信分享(一四二):容器云在万达的落地经验

qianggezhishen 发表了文章 • 3 个评论 • 7635 次浏览 • 2017-09-22 11:39 • 来自相关话题

【编者的话】容器生态是现在非常火热的技术生态之一,个人认为它主要囊括着四个方面的技术栈:一是容器核心技术栈(包括 Docker、rkt 及第三方公司自主研发的容器 Engine 等);二是容器基础技术栈(包括容器网络、存储、安全及服务发现等);三是容器编排技术 ...查看全部
【编者的话】容器生态是现在非常火热的技术生态之一,个人认为它主要囊括着四个方面的技术栈:一是容器核心技术栈(包括 Docker、rkt 及第三方公司自主研发的容器 Engine 等);二是容器基础技术栈(包括容器网络、存储、安全及服务发现等);三是容器编排技术栈(包括 Mesos/Marathon、Swarm、Kubernetes 及 OpenShift 等);四是容器应用技术栈(主要包括 CI/CD、监控、日志及微服务框架等)。而 Docker 和 Kubernetes 分别又是目前容器与容器编排界的小宠儿,所以我将带着小宠从这四方面分享一些容器云在万达的落地经验。

万达容器云平台支持快速部署、弹性伸缩、负载均衡、灰度发布、高效运维及微服务等特性。用户可以基于 Dashboard 简单方便地上线和管理着各自业务。目前在我们的容器云平台上,平稳高效运行着近 400 款包括支付、酒店等核心业务,管理着公司上万个容器。经历住了双旦、618 及双 11 等大型活动的考验。
# 一、容器云的平台高可用架构与部署
“经济基础决定上层建筑”,对于整个容器云平台来说,Kubernetes 平台就是我们的“经济基础”,所以在建设之初我们为了保证平台的稳定、高可用还是做了不少工作。先来看看 Kubernetes 平台的建设涉及到的几个技术点:

* 组件的高可用性如何保证?
* 组件以何种方式部署安装?
* 集群以何种方式快速扩缩容?
* 如何实现环境的批量配置及组件的批量升级?

为了较好的描述这些,我画了一张我们容器云中 Kubernetes 各组件高可用架构与部署图,请见:
1-K8S_组件高可用架构与部署.png

Kubernetes 所有组件都容器化了,而不采用二进制安装的方式。这样对于组件的部署、配置更新和升级等都比较容易,只需要先安装好 Docker,提前做好镜像,然后直接 `docker run --restart=always --name xxx_component xxx_component_image:xxx_version` 就可以了,这里 `--restart=always` 非常重要,用来保证主机重启后或由于突发状况引起的非错误性退出后组件服务自动恢复。

注意在升级过程中,为了减少组件服务的宕机时间,需要提前下载好新制作的镜像版本,因为如果镜像挺大的话,在 `docker restart` 进行更新前会耗费一些时间在 `docker pull` 上面。

在批量部署方面,我们采用 `Ansible` 工具。由于 Kubernetes 集群部署的文档网上蛮多的,所以这里就简要介绍一下组件的高可用部分。我们都知道 Kubernetes 实现了应用的高可用,而针对 Kubernetes 自身的 HA,主要还是在 Etcd 和 Kubernetes Master 组件上面。
##1、 Etcd 的高可用性部署
Etcd 使用的是 V3(3.0.3)的版本,比 V2 版本性能强很多。Etcd 组件作为一个高可用强一致性的服务发现存储仓库,它的 HA 体现在两方面:一方面是 Etcd 集群自身需要以集群方式部署,以实现 Etcd 数据存储的冗余、备份与高可用;另一方面是 Etcd 存储的数据需要考虑使用可靠的存储设备。

为了展示一下组件容器化在`部署方面`带来的一些便利效果,以 Etcd 的部署为例,先在 Ansible `inventory` 文件中规划好 Etcd 的分组信息,一般是采用 3 台做为集群即可。例如:
2-etcd-host.png

然后编写好类似如下的 `template` 文件,就可以用 Ansible Playbook 实现一键秒级的部署 Etcd 集群了,注意我这里使用了主机 IP 作为 Etcd 的名字。
3-template.png

如果需要升级则只需要修改一下 `etcd_version` 并提前拉取新版本镜像,重新跑一下 `ansible-playbook --limit=etcds -i hosts/cd-prod k8s.yaml --tags etcd` 即可完成秒级升级,这样一般显得简洁易用。当然 Etcd 在升级过程中还涉及到数据的迁移备份等,这个可以参考官方文档进行。
## 2、Kubernetes Master 的高可用性部署
Kubernetes 的版本我们更新的不频繁,目前使用的是 V1.6.6。Master 的三个组件是以 `Static Pod` 的形式启动并由 `kubelet` 进行监控和自动重启的,而 `kubelet` 自身也以容器的方式运行。

对于 `kube-apiserver` 的 HA,我们会配置一个 `VIP`,通过 `haproxy` 和 `keepalived` 来实现,其中 `haproxy` 用于负载均衡,而 `keepalived` 负责对 `haproxy` 做监控以保证它的高可用性。后面就可以通过 `VIP:Port` 来访问 `apiserver`。

对于 `kube-controller-manager` 和 `kube-scheduler` 的 HA,由于它们会修改集群的状态,所以对于这两个组件,高可用不仅要保证有多个实例,还需要保证在多个实例间实现 `leader` 的选举,因此在启动参数中需要分别设置 `--leader-elect=true`。

对于 Kubernetes 集群的扩缩容,只需要采用 Ansible 进行批量操作或者 `kubectl` 命令(一般需要用到 `drain`, `cordon` , `uncordon` 等)完成即可。另外我们还需要注意一下图中标注的各组件启动时依赖的参数配置,这些比较容易搞混。
# 二、容器云的技术架构介绍
目前使用的容器云架构方案如图所示,我将其分为四个部分,每个部分包含对应的容器技术栈,下面对于主流的技术介绍如下:
4-云容器技术架构.png

##1、存储方案
后端存储主要采用 `ceph` 驱动,这里主要介绍它在`有状态服务`和 `docker-registry` 两方面的应用。

`有状态`是应用程序的高级需求,它们需要在 Pod 挂了特别是飘移到其他 Node 后,可以持续的访问后端存储。因此,Kubernetes 通过网络存储提供了丰富的 `Persistent Volume` 支持,比如:GCE 的 pd,AWS 的 ebs,还有 GlusterFS,Ceph 等先进的分布式文件系统。我们目前使用了 Ceph 的 `rbd` 来支持有状态服务。Kubernetes 集群环境中,由于所有的组件采用容器化部署,因此除了在主机上安装 `ceph-common` 之外,还需要在 `kubelet`、`kube-controller-manager` 容器中安装它,而且在启动时挂载如下三个 volume,其他的与二进制方式差不多:
5-rbd-volume.png

具体 ceph rbd 配置,详见:Persistent Volume Provisioning 官网

rbd 支持动态供应,支持单节点读写,多节点读,但不支持多节点写。如果有业务需要多节点写的话,rbd 就比较受限制。目前由于只有 `GlusterFS` 既允许动态供应,又支持单节点和多节点读写,所以我们也正在调研其相关使用。

`docker-registry` 做为容器的核心部分,起初我们采用 `Swift` 作为后端存储,为了提高 `push/pull` 的效率,采用 `Redis` 作为 Metadata 缓存,然后直接以容器的方式运行官方提供的镜像,比如:
6-docker-regsitry.png

具体的 `config.yml` 配置,详见:docker-registry 官方配置。但后来为了保证 `docker-registry` 的高可用,我们采用 `Harbor` 做 HA,并以 `pod` 的形式运行在 Kubernetes 集群上,镜像数据以及 `Harbor-db` 全部通过 `Ceph` 的 `PV` 来挂载,这样就保证在 `Harbor` 主机挂了或者 `Pod` 故障后,`Harbor` 也可以 HA 了,同时我们也不需要额外维护 Swift 了。

另外注意一个问题,由于 `PV`,`StorageClass` 都局限于单个 `Namespace` 下,所以对于想通过 `Namespace` 来区分多租户使用动态存储的情况目前是不满足的。
##2、网络方案
底层容器网络我们最初使用的是官方推荐的 `Flannel`,现在部分集群已经由 `Flannel` 切换成了 `OVS` 。 `Flannel` 可以很容易的实现 Pod 跨主机通信,但不能实现多租户隔离,也不能很好的限制 Pod 网络流量,所以我们网络同事开发了 `K8S-OVS` 组件来满足这些需求。它是一个使用 `Open VSwitch` 为 Kubernetes 提供 SDN 功能的组件。该组件基于 `OpenShift SDN` 的原理进行开发。由于 OpenShift 的 SDN 网络方案和 OpenShift 自身的代码耦合在一起,无法像 Flannel 和 Calico 等网络方案以插件的方式独立的为 Kubernetes 提供服务,所以开发了 K8S-OVS 插件,它拥有 OpenShift 优秀的 SDN 功能,又可以独立为 Kubernetes 提供服务。

K8S-OVS 支持单租户模式和多租户模式,主要实现了如下功能:

* 单租户模式直接使用 Openvswitch+Vxlan 将 Kubernetes 的 Pod 网络组成一个大二层,所有 Pod 可以互通。
* 多租户模式也使用 Open vSwitch+Vxlan 来组建 Kubernetes 的 Pod 网络,但是它可以基于 Kubernetes 中的 Namespace 来分配虚拟网络从而形成一个网络独立的租户,一个 Namespace 中的 Pod 无法访问其他 Namespace 中的 Pod 和 Service。
* 多租户模式下可以对一些 Namespace 进行设置,使这些 Namespace 中的 Pod 可以和其他所有 Namespace 中的 Pods 和 Services 进行互访。
* 多租户模式下可以合并某两个 Namespace 的虚拟网络,让他们的 Pods 和 Services 可以互访。
* 多租户模式下也可以将上面合并的 Namespace 虚拟网络进行分离。
* 单租户和多租户模式下都支持 Pod 的流量限制功能,这样可以保证同一台主机上的 Pod 相对公平的分享网卡带宽,而不会出现一个 Pod 因为流量过大占满了网卡导致其他 Pod 无法正常工作的情况。
* 单租户和多租户模式下都支持外联负载均衡。

下面举例解释一下:

合并是指两个不同租户的网络变成一个虚拟网络从而使这两个租户中的所有 Pod 和 Service 能够互通;分离是指针对合并的两个租户,如果用户希望这两个租户不再互通了则可以将他们进行分离;全网化是指有一些特殊的服务需要能够和其他所有的租户互通,那么通过将这种特殊的租户进行全网化操作就可以实现。

不同租户的网络隔离是通过为每个 Kubernetes 命名空间分配一个 VNI(VXLAN 中的概念)来实现的,在 VXLAN 中不同的 VNI 可以隔离不同的网络空间。K8S-OVS 将具体的 Kubernetes 命名空间和 VNI 的对应关系存储在 Etcd 中,如下:
7-network1.png

这是在我们通过 Kubernetes 创建 Namespace 时,K8S-OVS 自动检测并为我们创建的。其中 `NetName` 是指租户的 Kubernetes 命名空间;`NetID` 是指为该租户分配的VNI;`Action` 是指可以对该租户网络进行的操作,它包括 `join` :合并, `isolate` :分离, `global` :全网化,其中 `join` 需要指定上面的第四个参数 Namespace,用于表示需要和哪个租户进行合并,其他两个操作则不需要设置 Namespace。
8-network2.png

合并之后观察 helloworld1 和 helloworld2 ,发现两个租户的 `NetID` 变为相同的了。这样两个网络的 Pod 和 Service 就可以相互访问了。其他场景一样,通过 etcdctl update 来控制 Action 从而改变 NetID 来实现租户的隔离与互通。这里不过多演示。

在应用方面,我们实现了 LVS 的四层,Ingress(基于 Nginx+Ingress Controller) 的七层负载均衡,各 Kubernetes 集群环境中陆续弃用了 Kubernetes 自带的 kube-proxy 及 Service 方案。
##3、CI/CD 方案
CI/CD(持续集成与部署)模块肩负着 DevOps 的重任,是开发与运维人员的桥梁,它实现了业务(应用)从代码到服务的自动上线,满足了开发过程中一键的持续集成与部署的需求。

我们采用了前端基于 Opads(一个比较成熟的在线打包平台)和后端 Pluto(一个将 Kubernetes APIServer 的接口进行封装,并提供 RestfulAPI 服务的库项目)的方案。目前通过该平台上线的业务有近 400 款。其架构图大致如下:
9-CICD.png

业务代码从公司内部的 GitLab/Gerrit 中拉取后,用户选择不同的分支版本(比如:sit/uat/prod)经过 Jenkins 构建后,生成相应的镜像并 Push 到相应的镜像仓库中,底层 `CD` 模块从相应的仓库上线到相应的 Kubernetes 集群(sit/uat/prod)中,而不需要 care 底层平台实现。

在整个 CI/CD 过程中,基础镜像的制作比较关键,我们按不同的业务分类提前制作不同的应用镜像(如:Tomcat、PHP、Java、NodeJS等)并打上所需的版本号,后面源代码既可以 `mount` 到相应的容器目录,也可以利用在 Dockerfile 的 `ONBUILD` 命令完成业务代码的加载。挂载的方式优点是比较灵活,也不需要重复构建,上线效率高;缺点是对于基础镜像环境的依赖较高,如果一个业务的基础镜像一直未改动,但代码中又有了对新组件库的调用或者依赖,则很容易失败。onbuild 的方式和 mount 方式恰好相反,它每次都进行 build,解决了环境的依赖,后面版本回滚也方便,缺点是需要每次进行镜像构建,效率低。这些看业务自己的选择。

我们提供的基础镜像版本在业务上线的时候,业务开发者可以提前搜索确定基础镜像是否存在,如果不存在需要提 Jira 单子后,交由我们进行制作,部分截图如下:
10-opads1.png

之后,业务可以通过选择代码分支、版本等上线服务。之后就可以看到上线的业务详情了,包括 Pod 副本个数,存活时间,名字,创建时间,所在的 Kubernetes 节点及 node selector 等。也可以通过基于 Gotty 实现的 Web console 登录查看业务容器,如图所示。
11-opads2.png

我们还实现了服务扩缩容,弹性伸缩(HPA)、负载均衡、灰度发布等,也加入了代码质量检查(Sonar)、自动化测试及性能测试插件等,这些都是 CI/CD PaaS 平台的重要组成部分。
##4、容器监控与告警方案
容器监控的对象主要包括 Kubernetes 集群(各组件)、应用服务、Pod、容器及网络等。这些对象主要表现为以下三个方面:

  • Kubernetes 集群自身健康状态监控(5 个基础组件、Docker、Etcd、Flannel/OVS 等)
  • 系统性能的监控,比如:CPU、内存、磁盘、网络、filesystem 及 processes 等;
  • 业务资源状态监控,主要包括:rc/rs/deployment、Pod、Service 等;

Kubernetes 组件相关的监控,我们写了相关的 shell 脚本,通过 crond 开启后监控各组件状态。

容器相关的监控,我们采用传统的 Heapster+InfluxDB+Grafana 方案:
12-监控与告警架构.png

Heapster 首先从 Kubernetes Master 获取集群中所有 Node 的信息,每个 Node 通过 `kubelet` 调用 `cAdvisor API` 来采集所有容器的数据信息(资源使用率和性能特征等)。这样既拿到了 Node 级别的资源使用状况信息,又拿到了容器级别的信息,它可以通过标签来分组这些信息,之后聚合所有监控数据,一起 `sink` 到 `Heapster` 配置的后端存储中(`Influxdb`),通过 `Grafana` 来支持数据的可视化。所以需要为 Heapster 设置几个重要的启动参数,一个是 `--source` 用来指定 Master 的 URL 作为数据来源,一个是 `--sink` 用来指定使用的后端存储系统(InfluxDB),还有就是 `--metric_resolution` 来指定性能指标的精度,比如:`30s` 表示将过去 30 秒的数据进行聚合并存储。

这里说一下 Heapster 的后端存储,它有两个,一个是 `metricSink`,另一个是 `influxdbSink`。metricSink 是存放在本地内存中的 metrics 数据池,会默认创建,当集群比较大的时候,内存消耗会很大。Heapster API 获取到的数据都是从它那获取的。而 influxdbSink 接的是我们真正的数据存储后端,在新版本中支持多后端数据存储,比如可以指定多个不同的 influxDB 。

通过 Grafana, 用户可以使用各种正则表达式或者选项查看自己的业务监控详情,还可以按系统性能(CPU、内存、磁盘、网络、filesystem)进行排序查看等等。
13-监控示例.png

当监控到一定的数量级,超过某个阈值时,将产生告警。虽然 Grafana 目前支持邮件进行一些简单的告警,但我们还是通过制定一些监控点、告警机制、告警等级等,然后接入公司内部现有 Zabbix 平台来进行告警。

邮件告警示例如下:
14-邮件告警.png

##5、日志方案:
容器平台的日志系统一般包括:Kubernetes 组件的日志,资源的事件日志及容器所运行的应用的日志。所以一个好的日志系统至少需要 cover 到这几块。

日志这块一直是我们头痛的地方,之前我们主要关心容器中的业务日志,所以是直接将其日志对接到公司的统一日志平台(Hippo)中。他们需要我们采用 Flume 来进行日志采集,每个业务以 Pod 的方式运行 Flume ,在 Flume 中配置好 `source`, `channel` 和 `sink` 参数。`source` 用来指定业务挂载的日志目录,然后通过 `channel` 进行输送,最后 `sink` 到后端的 Hippo 所使用的 Kafka 中。业务需要查看日志,则需要登录到 Hippo 平台中。

这个方案有一些不如意的地方,比如:每个业务需要单独额外运行一个 Flume 的 Pod,浪费资源;业务需要登录到另一个系统查看,由于不是对接到我们的平台中,不方便;另外由于 Hippo 是针对公司的统一的日志平台,不是容器云专用的,经常会在高峰期响应慢,很难处理过来。所以我们决定设计一个全新的平台,采用的方案是 `Kubernetes` 官方推荐的(Fluentd+Kafka+ES+ 自定义界面),具体架构如下:
15-日志方案.png

容器中输出的日志都会以 `*-json.log` 的命名方式保存在 `/var/lib/docker/containers/` 中,系统日志都在 `/var/log` 中。Fluentd 以 `Daemon Set` 运行于所有的 Node 中进行数据采集,为了保证性能,引进了 Kafka 作为消息队列及日志转发,由于不想维护多个组件,中间转发不用 Logstash ,所以需要引入了两个 Fluentd 的插件 `fluentd-kafka` 及 `fluentd-es`,前者用于推送数据到 Kafka,后者用于将数据推到 ElasticSearch 中。最后实现一个 `Log API Engine` 用于供上层 `Log GUI` 调用。这个 Engine 封装了实时日志、历史日志下载和查询、应用日志占比、日志等级占比等 Restful API。下面是我们的部分截图:
16-日志示例.png

# 三、容器云的填坑实践
下面挑几个坑说一下,并分享一下解决方法。
## Docker 相关
早期由于混合使用了 Deployment 和 RC,此时如果使用了同名 Pod label 则会产生冲突,RC 会删除 Deployment 后创建的 Pod,造成 Pod 的反复创建与删除,最终导致 Node 上的 Docker daemon 挂僵掉。原因是 Docker device mapper 互锁而触发 Linux kernel bug(特别是有大量的 outbound traffic 时),解决方法是升级内核到 3.10.0-327.22.2,或者添加内核补丁
## Kubernetes 相关
业务应用在升级过程中,如果 Docker 删除出错, 偶偶会导致 device mapper busy,则会显示 Pod 一直在销毁,但其实它的 Deployment 已经被删除了,这种我们没有找到很好的处理方法,现有 workaround 是先重启 docker daemon,如果不能解决,再 reboot 主机。一般的做法是先 `drain` 掉所有的 pod,然后待重启解决后,再 `uncordon` 回来。

在使用容器的方式部署 kubelet 时,我们发现删除 Pod 时,在 apiserver log 中一直会出现 `UnmountVolume TearDown secrect` 资源失败的错误。其中是当时在挂载 `/var/lib/kubelet` 时采用了 `rw` 的方式,这个问题困扰我们很久了,解决方法是加上 `shared` 即 `--volume=/var/lib/kubelet:/var/lib/kubelet:rw,shared`。
## 存储相关
当某一 Pod 挂载 Ceph rbd 的 Volume 时,如果删除 Pod,再重新创建,由于 PVC 被 lock 导致无法挂载,会出现 volume 死锁问题。由于我们的 kubelet 是容器部署,而 ceph 组件是以挂载的方式开启的,所以猜测可能是由于 kubelet 容器部署引起,但后面改用二进制方式部署并升级到 `V1.6.6` 版本后就可以解决。当时的版本是 V1.5.2。
## 网络相关
在业务上线过程中,一定要进行规范化约束,比如,当时将 Flannel 升级到 K8S-OVS 网络升级过程中出现有些业务采用 Service 进行负载均衡的情况,这种依赖于 kube-dns,由于 Flannel 和 OVS 的网段不一样,Service 层又没有打通,导致以 Service 运行并通过 kube-dns 解析时会出问题,且网络不通,本可以采用新建一个以 OVS 同网段的 kube-dns 来完成不同网段的兼容,但最后面发现该业务是根本不使用 Service,而是直接利用了 Pod,这样非规范化上线的业务很容易导致升级相关的故障出现,猜测可能当时平台建设初期手动上过业务,其实这方面我们也可以加些监控。

网络不稳定的时候,偶偶发现业务访问突然就慢起来了,然后发现其 `TIME_WAIT` 会出现过高,这个是没有对网络内核进行优化处理,此时需要设置 `net.ipv4.tcp_tw_recycle = 1` 和 `net.ipv4.tcp_tw_reuse = 1` ,前者表示开启 TCP 连接中 `TIME-WAIT Sockets` 的快速回收,后者表示允许将 `TIME-WAIT Sockets` 重新用于新的 TCP 连接。当然还有其他网络内核优化。
## 告警相关
CPU load 是通过每个核的 `running queue`(待运行进程队列)计算出来的,某些情况下 running queue 会变成 -1 也就是 4294967295。由于在 cpu 过载的时候,我们设置了告警,所以会被触发,但其实这时的 CPU 负载是正常的。此时,如果通过 `sar -q ,top,w,uptime` 等看到的 running queue 都是有问题后的值,只有用 vmstat 查看才是真实的。解决方法是重启 Node,因为这个值只有在重启后才会重置为零,或者升级内核补丁
#Q&A
Q:Grafana 是实时显示数据的,请问他如何能做到告警?就是 grafana 达到一定告警阈值时做告警?

A:Grafana 新版本中添加了简单的告警功能,在 Notification Channels 页面有个新建通道,在里面设置一下,具体可以看下官方的文档。



Q:请问如何实现容器限速的?

A:你是说容器的网络限速吗?流量限制功能我们是通过在 pod 的 annotations 字段设置 kubernetes.io/ingress-bandwidth (设置输入流量带宽)和 kubernetes.io/egress-bandwidth (设置输出流量带宽)来实现。



Q:请问使用什么操作系统部署 Kubernetes,有什么考虑?

A:用的 CentOS 7,企业一般的用法,还有就是它稳定,不容易出问题,Kubernetes 和 Docker 的支持比较好。



Q:如何把所有告警信息全部递给 Zabbix,Zabbix 自身是否也获取了监控值信息了?

A:全部推送压力大,先将 APIserver、Heapster 中相关的信息放 MySQL,中间做个数据库。



Q:etcd 3 的容灾和安全做了吗?

A:etcd 非常关键,我们会在升级和定期用 etcdctl 做 backup。升级时需将 --initial-cluster-state 改为 existing ,安全方面还没有。



Q:做灰度发布或 HPA 自动扩容时,实现了不影响正在提供服务的服务吗?

A:灰度发布不会影响服务,我们使用了 Ingress + Nginx 来保证 Pod 的变化不受影响。HPA 这块我们不敢上线,功能完成了,但没有经过大量测试。



Q:使用 rbd 作为后端存储,当 pod 发生迁移到另外一个节点后如何再次挂载这个 rbd?

A:将 PVC 的 volume.beta.kubernetes.io/storage-class 和 StorageClass 的 name 名字一样就可。不需要管后面 Pod。



Q:etcd 3 在哪些方面不如 etcd 2?

A:没有去做对比,etcd 3 是通过搜集了 etcd 2 用户的反馈和实际扩展 etcd 2 的经验基础上全新设计了 API 的产品。etcd 3 在效率,可靠性和并发控制上改进比较多。etcd 2 支持多语言客户端驱动,etcd 3 由于采用 gRPC,很多需要自己实现驱动。



Q:请问有状态的 pod 迁移,使用 ceph pv 是怎么保证分到同一个 volume?

A:我们用的是 StorageClass,在 PVC 时指定和 StorageClass 名字一样就可。通过 volume.beta.kubernetes.io/storage-class 来指定该名字。



Q:请问运行在不同的 Node 上面的 Pod 如何共享 Volume 存储,比如要共享一份代码?

A:不同 Node 间的 Pod 卷共享方式比较多,但一般需要网络存储,比如:NFS,GlusterFS,CephFS,Ceph rbd,当然还包括很多大厂如:GCE 的 pd,AWS 的 ebs 等。甚至可以使用 ConfigMap 来共享,然后 mount 到相应的目录即可。



Q:请问有没有对比过共有的容器云和私有的容器云的优缺点?

A:公有云比较难做,我们之前是做私有云(物理资源隔离,用户数据更安全可控;独占资源,不受干扰;自行规划灵活调整资源复用比例,成本更优),公有云(公有云弹性,自如应对业务变化;具备跨机房、跨地区的容灾能力)我们也在做,正在和 IBM 合作。



Q:请教多 Master 下,当某个 Master down 掉,default/kubernetes endpoints 中的 IP 没更新的问题,你们是如何处理的?

A:这个主要是 Endpoints 控制器负责 Endpoints 对象的创建,更新。新 leader master 掌管后,Kubernetes 会用 checkLeftoverEndpoints 来删除 没有响应的服务的 endpoints,重启一下 kube-controller-manager 试试。



Q:做过集群联盟吗?

A:有测试过,但目前 Kubernetes 可以支持达 1000 节点了,能满足我们目前的需求,所以还没有上。



Q:HPA不是Kubernetes支持的吗?你们对其做了哪些二次开发?支持蓝绿部署吗?

A:对的,目前是支持 CPU 还有一些应用程序提供的 metrics 了,之前在社区还没有的时候,我们有自己开发,主要是通过 heapster 监控 qps 还提供自定义的一些度量来完成 HPA。但 HPA 这个一旦出问题会不可控,所以暂时还不敢上线。蓝绿部署比较耗硬件资源,相当于要多一新版本备份,目前我们还不支持蓝绿部署。



Q:如果想看日志文件有没有好的办法,感觉在ES重被切割了不友好?

A:日志文件可以通过在启动的时候新建一个以应用名字命名的目录挂载到本地或者网络存储中,然后应用的标准或错误输出会直接输出到 docker daemon 的日志目录下,如果应用有自己的专门的文件输出方式,则可以用 tail -f 方式进行转发与 docker daemon 对接。



Q:还有就是基础容器是用的CentOS镜像吗?它默认就接近200m。开发语言用的Go的话有啥优化容器的地方?

A:基础容器一般 CentOS 的多些,有些会直接采用 docker hub 提供的原始镜像,然后做些自定义组件添加并重打包。一般的会比较大一些,镜像可以对 Dockerfile 进行优化来变小。可以用 pprof 来分析 Go 代码性能,容器的优化还主要在 Dockerfile。



Q:请问你们对于用户体验方面是如何监控的? 比如每个点击在不同服务层面上的延时各是多少,超时报警等?

A:这是个不错的想法,我们还没有做这块,不过可以通过应用提供的url,对其监控HTTP get 的 response 时间来控制。



Q:前端基于 Opads和后端 Pluto实现CI,有具体的文档可以参考吗?

A:这两个都是自己内部开发的模块,一个基于 PHP,一个基于 Python,文档不方便分享。



Q:目前大规模落地云平台是建议上容器云吗?

A:建议上。



Q:服务启动依赖和应用版本控制如何做的?

A:这块我们做的不大好,一般可以将每个服务注册到发现服务,然后记录它们的依赖,在启动时进行服务发现及启动,这个在微服务框架中有些。我们在应用程序版本控制方面有自己的约束规范,但后面会有 helm 来试试。



Q:etcd 集群为什么不直接用Compose启动?

A:这个我们为了ansible部署方便



Q:Node 节点采用虚拟机还是物理机部署的?

A:物理机。



以上内容根据2017年09月21日晚微信群分享内容整理。 分享人陈强,万达网络资深工程师,毕业于华东师范大学。目前在万达网络科技集团云公司基础架构部负责Kubernetes与Docker的落地与实践工作。曾先后就职于Intel、IBM和爱奇艺。在云计算领域长年搬砖,对Mesos/Kubernetes/Docker等有较深入的研究。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesa,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

PaaS平台OpenShift企业部署的“脑图”

范彬 发表了文章 • 0 个评论 • 3324 次浏览 • 2017-08-17 14:33 • 来自相关话题

【编者的话】这篇文章是来自红帽,是关于OpenShift企业部署的“蓝图”,通过“脑图”帮助客户实现企业级部署OpenShift。 【烧脑式Kubernetes实战训练营】本次培训理论结合实践,主要包括:Kubernetes架构和资源 ...查看全部
【编者的话】这篇文章是来自红帽,是关于OpenShift企业部署的“蓝图”,通过“脑图”帮助客户实现企业级部署OpenShift。

【烧脑式Kubernetes实战训练营】本次培训理论结合实践,主要包括:Kubernetes架构和资源调度原理、Kubernetes DNS与服务发现、基于Kubernetes和Jenkins的持续部署方案 、Kubernetes网络部署实践、监控、日志、Kubernetes与云原生应用、在CentOS中部署Kubernetes集群、Kubernetes中的容器设计模式、开发Kubernetes原生应用步骤介绍等。

如下是构建高分布式平台的大部分依赖:

  • 战略目标 (Strategy)
  • 存储计划(Storage)
  • 业务操作(Operation)
  • 业务连续性弹性与灾难恢复 (BCR&DR)
  • 应用开发 (AppDev)
  • 安全(Security)
  • 自动化(Automation)
  • 网络(Networking)
  • 提供(Provisioning)
  • 外部依赖 (External dependencies)
其中每类都包含多个子区域。下面我们将逐一说明。即使你熟悉这个话题,你也会发现脑图的价值。如果你想要更多,点击放大图片或查看https://github.com/mangirdaz/ocp-mindmap
appcoup-pod-150-150-768x1159.png
# 策略目标(Strategy)开始采用新平台之前,你的策略应该是你想到的第一件事情。你需要知道谁将使用你建立的这个平台。利益相关者是谁,最后谁负责?确定利益相关者通常是构建策略的最简单的部分。建立实践社区(CoP)将有助于制定战略和方向。一个精心设计和维护的CoP可以帮助您利用自己组织力量推动实现“正确解决方案”的进程。封闭式解决方案往往会错过组织需求。考虑对CoP进行一些研究。其次,你需要“微服务标准化”策略。在将服务放在新平台之前,强烈建议您定义一些标准。这里的挑战是旧的标准不适用于这个新的世界。尝试“复制 粘贴”,某些功能可能无法正常工作。# 存储计划(Storage)计划构建一个成功的平台,存储或持久性是关键考虑。你不仅需要简单的存储,而且需要可以高扩展的存储空间。你将需要为平台内部组件提供存储,如:日志、度量和容器镜像仓库。当然,你可以根据将要构建的解决方案选择不同的实现方式,如:对象存储、基于消息传递的日志服务,有时可能不需要考虑存储,但你无法完全消除对存储的需求。一旦我们开始说“持久性”,就意味着存储。开始思考如何做,谁将为你提供,以及你将拥有的存储空间(SSD,Magnetic,SAN,NAS等)。# 业务操作(Operation)操作部分与本文的下一节紧密相连。业务主要由以下两个领域组成:通常和无计划的行为(BCR&DR)。我们将在下一节中介绍无计划的行为。通常业务操作包括出口(egress)路由器配置、平台维护、补丁、调度规则创建、平台管理以及对平台上发生的事件的主动反应等内容。所有这一切,你将需要一个非常好的日志和监视。没有日志和监控,你将会“失明”,在高分布式系统中变得盲目。在分布式系统中,事情可能会从坏到更坏变得非常快,与“独立服务器”的基础架构相比,系统变得更糟糕。例如,如果出现故障,你的容器将重新调度(假设你没有自动伸缩功能,即使可以自动伸缩,你也可能花费巨大的费用)。在当前配置中重新调度=高密度,将意味着会遇到滚动失败情况。因此,你需要为运营定义标准运行手册,创建仪表盘,确保你了解你的环境,这是运营中最重要的事情之一。# 业务连续性弹性与灾难恢复(BCR&DR)我将业务连续性和弹性(BCR)和灾难恢复(DR)从业务中分开,它是非常重要的。即使在提供生产流量之前,我建议你知道你的“失败域”,及如何失败恢复。你需要制定一个计划。例如:如果你在分布式、可靠的、键值存储集群(如:在OpenShift / Kubernetes的etcd)中丢失了仲裁,或者知道外部DNS或存储提供发生故障。同时,存在多种不同的场景,如:不同的环境有所不同,某些内部/外部依赖关系具有较少或更成熟的提供,这些都取决于你所在的组织。# 应用开发(AppDev)你也需要考虑开发者。一些组织忘记了平台将主要由开发人员使用。只有开发者才知道他们想要在一个平台上运行什么。尽管事实上你将有很多工具,但你可能希望对模式和蓝图进行标准化。你应该考虑如下:
  • 应用开发人员将如何监控应用程序和节点性能?
  • 他们将如何推广和部署?
  • CI / CD管道(现有和新的)将如何与平台集成。
  • 你将如何推广来自环境的镜像?
  • 如何进行配置推广?
  • 最后,开发人员将会使用哪些开发工具?
开发人员的经验非常重要。得到这个权利,你的平台将被使用。错了,没有人会想使用它。# 自动化(Automation)这个特定的领域与其他领域有许多关系。你将需要使用CI / CD工具自动化你的应用程序、进行镜像测试和推广等。此外,你的基础设施应该被视为与你的应用程序相同 - 自动化无处不在。为此,你可以使用配置管理工具或仅依赖于部署工具。但是,如果你从一开始就以自动化的思想开始构建,那么即使在开始时它看起来很慢,它会变得想要的更快。# 网络(Networking)你需要考虑如何访问应用程序、出口(egress)和入口(ingress)流量,以及如何配置负载平衡器。决定是否将运行 “主动-被动” 或 “主动-主动”,及如何负载均衡所有的堆栈。容器是“网络中的第一个公民”或依赖SDN抽象?如何处理DNS?是否需要相互SSL?你的集群在2 - 5年内有多大?一个节点上运行多少个容器?所有这些问题都将定义你的平台的网络设计。# 安全(Security)尽管平台构建要考虑是安全的,但你仍然会有很多公开的问题。例如,你如何管理你的私密凭据(密码,证书)以及如何更新它们?你将如何将你的应用程序暴露给外界?最后,最重要的是,如何验证你正在运行的镜像?运行多租户、基于微服务的应用程序,镜像扫描和生命周期是关键问题。# 提供(Provisioning)提供非常广泛,与你的组织中的如下有很高的联系:
  • 配置管理:使用哪一个配置管理,以及它如何与平台支持的工具一起使用?一旦你确定了所使用的配置管理后,需要选择使用什么工具?你是否需要什么额外的工具?你是否需要红帽的订阅和生命周期管理吗?是否需要CloudForms控制台、能力和主动管理?需要什么可视化提供商及其功能?
  • 基础设施本身:你是否使用容器化部署或基于包/ RPM?如何满足你组织的补丁策略?例如,如果你的组织是基于RPM的,你选择了基于容器化/OSTree的平台,你的运维工程师如何知道它们的生命周期?
最后,你需要为平台做什么定制(预先及后期操作),使其符合你的需求?# 外部依赖(External dependencies)你会有很多外部依赖,请确保这些外部依赖的弹性和它们提供的SLA。外部依赖将包括如下:
  • 记录和监控:日志如何归档?集成哪些外部日志和监控解决方案?提供哪些元数据?
  • 存储:你将如何使用它?是否足够快?每秒输入/输出操作(IOPS)?如何扩缩?
  • 容器镜像仓库:如果你计划运行全局分布式集群,确定你的镜像的一致性?镜像仓库是否需要外部存储?如果是,什么格式?
  • 身份验证和授权:你将如何验证你的用户和应用程序?首选的身份验证提供商?如何进行基于角色的访问控制(RBAC)?
  • ITSM / CMDB:你需要在配置管理数据库中注册你的应用程序吗?如何自动化(或自动化解决方案?)。如何考虑变化?

# 架构
最后,一旦你知道你想去哪里(策略),你需要开始考虑更广泛的架构,例如基础设施密度、数据中心和可用性区域。已经覆盖的大部分领域与平台本身的架构有关。你需要在开始时做出正确的选择,因为分布式平台(OpenShift / Kubernetes等)构建和使用数百甚至数千个应用程序,难以修改。如果你网络错误,以后再进行更改是不可能的。如果平台不适应外部依赖关系与平台扩展的能力,将遇到瓶颈。

至此,我们介绍了这个旅程,你需要考虑什么。需要明白一点:公共云不能解决所有这些问题。这些可能看起来很难、不值得。但是,最终的结果就是全云不可知、水平和垂直可扩展的自助服务基础设施 ,这些将使开发人员开心。

原文出处:Enterprise OpenShift Deployment: What do you need to know?(翻译:范彬)

===============================================================
译者介绍:范彬,从事微服务、Docker和Kubernetes容器技术等方面的工作。可以关注译者的微信公众号:范范米饭。

OpenShift V3 应用发布部署的简单场景演示

alex_wang2 发表了文章 • 2 个评论 • 15883 次浏览 • 2017-03-23 22:29 • 来自相关话题

【编者的话】本文是一篇关于OpenShift如何应用在DevOps方面的文章。全文由浅入深,由易到难,通过3种不同场景下的部署,给读者一个对OpenShift功能的全新了解。译者以前和有些做容器开发,部署相关工作的同学认为:OpenShift仅仅是在Kuber ...查看全部
【编者的话】本文是一篇关于OpenShift如何应用在DevOps方面的文章。全文由浅入深,由易到难,通过3种不同场景下的部署,给读者一个对OpenShift功能的全新了解。译者以前和有些做容器开发,部署相关工作的同学认为:OpenShift仅仅是在Kubernetes基础之上架设了自己的WebUI,对API和CLI等接口进行了自己的封装(很多kubectl的命令都被oc继承)。但是在接触了OpenShift 1个月后(翻译本文时,我正好接触OpenShift一个月整),我对OpenShift有了全新的认识,特别是读过此文原文后,让我认识到OpenShift之所以能够作为一个PaaS的真正价值所在。

我以前有幸接触过IBM的Bulemix(CloudFoundry),了解过一些基础概念和它大概的运行原理。这段时间经过我对OpenShift的了解,似乎找到了他们之间都作为PaaS的共通之处,也就是它区别于Kubernetes这种容器编排系统的最主要的地方。

【上海站|3天烧脑式微服务架构训练营】培训内容包括:DevOps、微服务、Spring Cloud、Eureka、Ribbon、Feign、Hystrix、Zuul、Spring Cloud Config、Spring Cloud Sleuth等。

1.jpg

#概览
如今,对很多公司来说最困难的事情之一就是发布管理。当然我们有很多的方法、工具和技术,但是如何把它们联系在一起并且跨组织功能的工作呢?一个产品的发布不是一个team的事,它会涉及到公司的每一个人。很多公司挣扎于此但效果并不好。这个问题在过去还可以勉强容忍,但现就在就没那么幸运了。当今的公司赖以生存的不仅是创新的能力,而且还有把创新变成产品的能力。我认为创新是比较容易的部分, 在受控的方式下,提供这些创新的产品和服务才是真正的挑战。

通过转移到微服务(micro-services)架构和基于容器技术例如Docker,可以简化或者合理化很多方面的技术。但在OpenShift以前没有一个平台能把它们结合到一起,OpenShift的出现就像隧道尽头的一束光,它允许开发和运维团队在一起工作,而且仍然让他们维护各自关注和控制的领域。在这篇文章中,我们将会看用OpenShift部署应用程序的三个场景,每个场景都会涉及到开发和运维。并且你会从中得到如何使用OpenShift的启发。这些仅仅是一些基本的场景我们也只是蜻蜓点水般带过,但却可以作为在容器世界里应用程序部署或者发布管理的一个起点。
# 场景 1:开发团队使用运维团队提供的镜像
大多数的运维团队都想控制应用程序的运行环境。并且确保应用程序的运行环境符合所有的安全规则,提供需要的功能并且定期更新。

开发团队关注于创新通过应用程序的功能性,稳定性和性能。

OpenShift允许在两个团队关注各自核心职责的同时,提供一个方法去整合不同团队的输入和输出,最后提供一个端到端的产品发布流程。

在OpenShift里有很多的方法去整合DevOps(开发运维)团队。一个简单的方法就是把开发和运维分开到不同的项目,并且允许开发的应用程序运行在运维提供的环境中,我们将用一个ruby写的hello-world程序作为例子去说明这种情况。
##创建项目
为ruby应用创建开发和运维的项目
# oc login -u admin
# oc new-project ruby-ops
# oc new-project ruby-dev

##创建用户
 # htpasswd /etc/origin/master/htpasswd dev
# htpasswd /etc/origin/master/htpasswd ops

##设置权限
创建三个组ops-edit、dev-view和dev-edit,授予运维组编辑ruby-ops项目的权限,授予开发查看ruby-ops项目和编辑ruby-dev项目的权限。并且让ruby-dev项目可以从拉取ruby-ops项目拉取images。

创建用户和其对应的组
# oadm groups new ops-edit && oadm groups new dev-view && oadm groups new dev-edit
# oadm groups add-users ops-edit ops && oadm groups add-users dev-view dev && \
oadm groups add-users dev-edit dev

关联组到相关的项目,并授权ruby-dev项目可以从ruby-ops拉取images。
 # oadm policy add-role-to-group edit ops-edit -n ruby-ops && \
# oadm policy add-role-to-group view dev-view -n ruby-ops && \
# oadm policy add-role-to-group edit dev-edit -n ruby-dev && \
# oadm policy add-role-to-group system:image-puller system:serviceaccounts:ruby-dev -n ruby-ops

##Ruby的运行环境
作为运维用户用测试代码创建ruby运行时的image。
 # oc login -u ops
# oc project ruby-ops
# oc new-app centos/ruby-22-centos7~https://github.com/openshift/ruby-hello-world.git

创建应用程序需要的MySQL数据库,并把服务名设置为database。
# oc new-app mysql-ephemeral -p DATABASE_SERVICE_NAME=database
# oc env dc database --list | oc env dc ruby-hello-world -e -

#场景 2:生产项目拉取开发项目产生的镜像
如果开发的应用程序根据所需的功能有不同的版本,并且通过了所有的测试,那么就可以部署到其他环境。通常先是质量和测试,然后生成最终的生产版本。在这个简单的例子中,我们用ticket-moster程序,把它从开发环境直接部署到生产环境中。

我们用到了一些场景1中熟悉的概念,这个技术依靠生产环境能够拉取在开发环境中产生的images。在这个场景中我们创建一个了配置并设置一个触发器,它能够在开发更新image之后自动的部署这个带有特定名字或者标签的images到生产环境中。场景1中,这个过程是手动完成的,在场景2中是自动的,在场景3中,我们将看到如何用Jenkins去部署一个比较复杂的,带逐层审批的pipeline流程。
##创建项目并设置拉取权限
# oc new-project ticket-monster-dev
# oc new-project ticket-monster-prod
# oc policy add-role-to-group system:image-puller system:serviceaccounts:ticket-monster-prod -n ticket-monster-dev

##创建ticket monster项目的开发环境模板
# vi monster.yaml
kind: Template
apiVersion: v1
metadata:
name: monster
annotations:
tags: instant-app,javaee
iconClass: icon-jboss
description: |
Ticket Monster is a moderately complex application that demonstrates how
to build modern applications using JBoss web technologies

parameters:
[list]
[*]name: GIT_URI[/*]
[/list] value: git://github.com/kenthua/ticket-monster-ose
[list]
[*]name: MYSQL_DATABASE[/*]
[/list] value: monster
[list]
[*]name: MYSQL_USER[/*]
[/list] value: monster
[list]
[*]name: MYSQL_PASSWORD[/*]
[/list] from: '[a-zA-Z0-9]{8}'
generate: expression


objects:
[list]
[*]kind: ImageStream[/*]
[/list] apiVersion: v1
metadata:
name: monster

-kind: BuildConfig
apiVersion: v1
metadata:
name: monster
spec:
triggers:
- type: Generic
generic:
secret: secret
- type: ImageChange
- type: ConfigChange
strategy:
type: Source
sourceStrategy:
from:
kind: ImageStreamTag
name: jboss-eap64-openshift:latest
namespace: openshift
source:
type: Git
git:
uri: ${GIT_URI}
ref: master
output:
to:
kind: ImageStreamTag
name: monster:latest

-kind: DeploymentConfig
apiVersion: v1
metadata:
name: monster
spec:
replicas: 1
selector:
deploymentConfig: monster
template:
metadata:
labels:
deploymentConfig: monster
name: monster
spec:
containers:
- name: monster
image: monster
ports:
- name: http
containerPort: 8080
- name: jolokia
containerPort: 8778
- name: debug
containerPort: 8787
readinessProbe:
exec:
command:
- /bin/bash
- -c
- /opt/eap/bin/readinessProbe.sh
env:
- name: DB_SERVICE_PREFIX_MAPPING
value: monster-mysql=DB
- name: TX_DATABASE_PREFIX_MAPPING
value: monster-mysql=DB
- name: DB_JNDI
value: java:jboss/datasources/MySQLDS
- name: DB_DATABASE
value: ${MYSQL_DATABASE}
- name: DB_USERNAME
value: ${MYSQL_USER}
- name: DB_PASSWORD
value: ${MYSQL_PASSWORD}
- name: JAVA_OPTS
value: "-Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.logmanager -Djava.awt.headless=true -Djboss.modules.policy-permissions=true"
- name: DEBUG
value: "true"
triggers:
- type: ImageChange
imageChangeParams:
automatic: true
containerNames:
- monster
from:
kind: ImageStream
name: monster

-kind: DeploymentConfig
apiVersion: v1
metadata:
name: monster-mysql
spec:
triggers:
- type: ImageChange
imageChangeParams:
automatic: true
containerNames:
- monster-mysql
from:
kind: ImageStreamTag
name: mysql:latest
namespace: openshift
replicas: 1
selector:
deploymentConfig: monster-mysql
template:
metadata:
labels:
deploymentConfig: monster-mysql
name: monster-mysql
spec:
containers:
- name: monster-mysql
image: mysql
ports:
- containerPort: 3306
env:
[list]
[*]name: MYSQL_USER[/*]
[/list] value: ${MYSQL_USER}
[list]
[*]name: MYSQL_PASSWORD[/*]
[/list] value: ${MYSQL_PASSWORD}
[list]
[*]name: MYSQL_DATABASE[/*]
[/list] value: ${MYSQL_DATABASE}

-kind: Service
apiVersion: v1
metadata:
name: monster
spec:
ports:
- name: http
port: 8080
selector:
deploymentConfig: monster

-kind: Service
apiVersion: v1
metadata:
name: monster-mysql
spec:
ports:
- port: 3306
selector:
deploymentConfig: monster-mysql

-kind: Route
apiVersion: v1
metadata:
name: monster
spec:
to:
name: monster

 # oc create -n openshift -f monster.yaml

##创建ticket monster项目的生产环境模板
当开发环境中用monster:prod触发了image stream,下面这个触发器将会把image stream部署到生产环境中。
# vi monster-prod.yaml
kind: Template
apiVersion: v1
metadata:
name: monster-prod
annotations:
tags: instant-app,javaee
iconClass: icon-jboss
description: |
Ticket Monster is a moderately complex application that demonstrates how
to build modern applications using JBoss web technologies. This template
is for "production deployments" of Ticket Monster.

parameters:
[list]
[*]name: MYSQL_DATABASE[/*]
[/list] value: monster
[list]
[*]name: MYSQL_USER[/*]
[/list] value: monster
[list]
[*]name: MYSQL_PASSWORD[/*]
[/list] from: '[a-zA-Z0-9]{8}'
generate: expression

objects:
[list]
[*]kind: DeploymentConfig[/*]
[/list] apiVersion: v1
metadata:
name: monster
spec:
replicas: 3
selector:
deploymentConfig: monster
template:
metadata:
labels:
deploymentConfig: monster
name: monster
spec:
containers:
- name: monster
image: monster
ports:
- name: http
containerPort: 8080
- name: jolokia
containerPort: 8778
readinessProbe:
exec:
command:
- /bin/bash
- -c
- /opt/eap/bin/readinessProbe.sh
env:
- name: DB_SERVICE_PREFIX_MAPPING
value: monster-mysql=DB
- name: TX_DATABASE_PREFIX_MAPPING
value: monster-mysql=DB
- name: DB_JNDI
value: java:jboss/datasources/MySQLDS
- name: DB_DATABASE
value: ${MYSQL_DATABASE}
- name: DB_USERNAME
value: ${MYSQL_USER}
- name: DB_PASSWORD
value: ${MYSQL_PASSWORD}
- name: JAVA_OPTS
value: "-Xmx512m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.logmanager -Djava.awt.headless=true -Djboss.modules.policy-permissions=true"
triggers:
- type: ImageChange
imageChangeParams:
automatic: true
containerNames:
- monster
from:
kind: ImageStreamTag
name: monster:prod
namespace: ticket-monster-dev

-kind: DeploymentConfig
apiVersion: v1
metadata:
name: monster-mysql
spec:
triggers:
- type: ImageChange
imageChangeParams:
automatic: true
containerNames:
- monster-mysql
from:
kind: ImageStreamTag
name: mysql:latest
namespace: openshift
replicas: 1
selector:
deploymentConfig: monster-mysql
template:
metadata:
labels:
deploymentConfig: monster-mysql
name: monster-mysql
spec:
containers:
- name: monster-mysql
image: mysql
ports:
- containerPort: 3306
env:
[list]
[*]name: MYSQL_USER[/*]
[/list] value: ${MYSQL_USER}
[list]
[*]name: MYSQL_PASSWORD[/*]
[/list] value: ${MYSQL_PASSWORD}
[list]
[*]name: MYSQL_DATABASE[/*]
[/list] value: ${MYSQL_DATABASE}

-kind: Service
apiVersion: v1
metadata:
name: monster
spec:
ports:
- name: http
port: 8080
selector:
deploymentConfig: monster

-kind: Service
apiVersion: v1
metadata:
name: monster-mysql
spec:
ports:
- port: 3306
selector:
deploymentConfig: monster-mysql

-kind: Route
apiVersion: v1
metadata:
name: monster
spec:
to:
name: monster

# oc create -n openshift -f monster-prod.yaml

##部署ticket-monster开发环境
用WebUI你可以选择monster的模板。
2.jpg

##部署ticket-monster到生产环境
部署ticket-monster生产环境模板(monster-prod)。
3.jpg

你可以看到在开发环境中程序被创建,并且可以通过 http://monster-ticket-monster-dev.apps.lab访问到它。
4.jpg

你可以看到在生产环境中,虽然数据库已经运行起来了,并且创建了一个服务,但是ticket monster应用却没有启动。这是因为我们创建的生产环境模板会从开发环境中自动拉取这个带有特定名字或者标签images。在开发环境中只部署的规模是1,但是生产环境中部署的规模是4。
##部署应用程序的开发版本到生产环境
拉取images stream的spec。
# oc get is monster -o yaml
apiVersion: v1
kind: ImageStream
metadata:
annotations:
openshift.io/image.dockerRepositoryCheck: 2016-08-09T13:37:47Z
creationTimestamp: 2016-08-09T13:14:53Z
generation: 7
name: monster
namespace: ticket-monster-dev
resourceVersion: "107170"
selfLink: /oapi/v1/namespaces/ticket-monster-dev/imagestreams/monster
uid: 42740a3d-5e33-11e6-aa8d-001a4ae42e01
spec:
tags:
- annotations: null
from:
kind: ImageStreamImage
name: monster@sha256:3a48a056a58f50764953ba856d90eba73dd0dfdee10b8cb6837b0fd9461da7f9
generation: 7
importPolicy: {}
name: prod
status:
dockerImageRepository: 172.30.139.50:5000/ticket-monster-dev/monster
tags:
- items:
- created: 2016-08-09T13:26:04Z
dockerImageReference: 172.30.139.50:5000/ticket-monster-dev/monster@sha256:3a48a056a58f50764953ba856d90eba73dd0dfdee10b8cb6837b0fd9461da7f9
generation: 1
image: sha256:3a48a056a58f50764953ba856d90eba73dd0dfdee10b8cb6837b0fd9461da7f9
tag: latest

一旦你有了一个拉取image stream的说明(就像上面的那个),标记这个images stream为monster:prod。
 # oc tag monster@sha256:3a48a056a58f50764953ba856d90eba73dd0dfdee10b8cb6837b0fd9461da7f9 monster:prod 

你可以效验一下这个image stream是否已经生效
# oc get is
NAME DOCKER REPO TAGS UPDATED
monster 172.30.139.50:5000/ticket-monster-dev/monster prod,latest 2 minutes ago

只要这个images stream在ticket-monster-dev中被标记成monster:prod,他就会被部署到生产环境中,并且按照上面的描述把部署规模设置成4。
5.jpg

# 场景 3:用Jenkins实现AB(应用的名字叫AB)部署
在这个场景中我们将看到如何用Jenkins实现一个简单应用AB的部署。这个场景中用到了在场景1和2中学到的东西。在这个场景中我们会创建一个稍稍有点复杂的三个环境:开发、集成和生产。我们会给应用程序创建2个版本,v1和v2在开发环境里。用Jenkins部属V2版本的应用到集成生产环境。最终我们将在生产环境中展示这个应用从V2版本回滚到V1版本。我很感谢我的同事Torben Jaeger,下面很多内容是他编写的。
##创建项目
# oc new-project dev && \
oc new-project int && \
oc new-project prod

##设置拉取(pull)的权限
允许int项目从dev项目中拉取images,并且允许prod项目从dev项目和int项目中拉取images。
# oc policy add-role-to-group system:image-puller system:serviceaccounts:int -n dev

# oc policy add-role-to-group system:image-puller system:serviceaccounts:prod -n int

# oc policy add-role-to-group system:image-puller system:serviceaccounts:prod -n dev

##在开发项目中设置Jenkins
在WebUI中进入dev项目,选择添加jenkins-ephemeral。
6.jpg

##克隆Github的repository
# git clone https://github.com/ktenzer/openshift-demo.git

##更新Jenkins的认证tokens
从OpenShift命令获得认证token,这个token可以让Jenkins访问OpenShift环境。你需要更新它
# oc login -u admin
# oc whoami -t
DMzhKyEN87DZiDYV6i1d8L8NL2e6gFVFPpT5FnozKtU

更新下面的Jenkins任务,并且用上面的token替换其中的authToken和destinationAuthToken
# ls jenkins-jobs/
promote-int.xml promote-prod.xml rel-v2.xml rollback-prod.xml

##配置三个环境
用你的token更新三个模板并创建三个环境。
# cd openshift-demo

# oc create -f template.json -n dev

# oc create -f acceptance.template.json -n int

# oc create -f production.template.json -n prod

##在开发项目中部署nodejs 的hello-world应用
把环境
# oc new-app -f template.json -n dev

这个模板创建了两个版本的应用v1-ab和v2-ab。
7.jpg

##在开发项目中测试应用程序
通过http或者curl工具去连接v1-ab因该可以打印出“hello World!”。如果连接v2-ab可以打印出”hello World,welcome to Frankfurt!“。
##把V1从开发项目部署到集成项目
把v1的image打一个标记(tag)部署到集成项目。当镜像acceptance:lastest被更新时触发器会部署它到集成项目里。你会看到v1版本的pod被启动并运行起来。
oc tag v1:latest v1:1.0 -n dev
oc tag dev/v1:1.0 acceptance:v1 -n int
oc tag acceptance:v1 acceptance:latest -n int

##把v1从集成项目部署到生产项目
把v1打一个标记(tag)部署到生产项目。当镜像production:latest被更新时生产环境中的触发器会部署它到生产项目中。
oc tag int/acceptance:v1 production:v1 -n prod
oc tag production:v1 production:latest -n prod

注意应用被署了4个pod并且代码版本是v1。再次说明一下,这里所有的定义都来自之前的模板。
8.jpg

##配置Jenkins
你已经知道了如何手动的,用打标记和设置触发器的方法去部署一个应用程序。下面我们来一点复杂的,通过在jenkins里安装OpenShift的插件去从新编排一下刚才的任务。这次我们将部署v2版本到集成项目和开发项目

这里可以得到jenkins中的4个job:第一个的作用是创建v2版的应用程序,第二个用来从开发部署集成,第三个实现了从集成到生产,第四个可以在生产项目中把应用从v2回滚到v1。
# curl -k -u admin:password -XPOST -d @jenkins-jobs/rel-v2.xml 'https://jenkins-dev.apps.lab/createItem?name=rel-v2' -H "Content-Type: application/xml"
# curl -k -u admin:password -XPOST -d @jenkins-jobs/promote-int.xml 'https://jenkins-dev.apps.lab/createItem?name=promote-int' -H "Content-Type: application/xml"
# curl -k -u admin:password -XPOST -d @jenkins-jobs/promote-prod.xml 'https://jenkins-dev.apps.lab/createItem?name=promote-prod' -H "Content-Type: application/xml"
# curl -k -u admin:password -XPOST -d @jenkins-jobs/rollback-prod.xml 'https://jenkins-dev.apps.lab/createItem?name=rollback-prod' -H "Content-Type: application/xml"

创建4个job,并且登陆到Jenkins,用户名和密码是:admin和password。
9.jpg

##这里可以修改一点v2的代码并且开始build
为了让这个代码工作,你需要在Github上fork一个nodejs-ex项目到你自己的账户下,并且更新template.json文件,然后用这个文件从新部署一下开发环境。
 "source": {
"type": "Git",
"git": {
"uri": "https://github.com/ktenzer/nodejs-ex.git",
"ref": "master"
},

# git clone https://github.com/ktenzer/nodejs-ex

# cd nodejs-ex

##Checkout到v2的branch并且commit
# git checkout v2
# vi index.html
Hello World, welcome to Munich!

##提交修改
# git commit -a -m "updated to munich"
# git push origin v2

在Jenkins里运行rhel-v2这个build。
10.jpg

如果你克隆了nodejs-ex的repository,这时你可以通过URL看到你的修改已经生效了。
##把v2从开发项目部署到集成项目
在Jenkins里运行promote-int这个build,你会看到有一个v2版本的pod在v1版本后启动。
11.jpg

##从集成环境部署v2到生产环境
我们在这里深入的看一下在hood的后面实际发生了些什么。

可以用curl观察到应用程序是如何从v1切换到v2的。
# for i in {1..10000};do curl prod-ab.apps.lab; sleep 1;done

在Jenkins里运行promote-prod这个build。
12.jpg

部署已经开始了,v2版本在v1版本之后启动。此时应用的服务仍然还是v1。
13.jpg

两个v2版本的pod运行起来了,探针(readiness)会去检查这两个pod,以确保v2应用提供的服务可用。然后把v1的pod逐个关闭。
14.jpg

当v2版本的4个pod都运行起来的时候,v1版本的pod的数量是0。在这个过程中我们可以看到有一段时间,v1和v2是同时响应请求的。
15.jpg

现在只有v2版本的pod在运行了,同时AB的部署也完成了。
16.jpg

##回滚
现在假设我们不满意v2版本,想回到旧的v1版本去。

在Jenkins里运行rollback-prod这个build。
17.jpg

现在我们观察到了同样的事情,只不过是v2切换到了v1。
18.jpg

可以用下面的命令把集成环境中的v2回滚到v1。
# oc tag dev/v1:latest acceptance:latest -n int

#总结
在这片文章中我们学到了如何在OpenShift中部署一个简单的应用程序并且和Jenkins集成去管理应用的发布。OpenShift有很多的功能值得你使用它,我们紧紧是用了其中的一小部分。使用Jenkins你可以创建一个非常复杂的build pipelines,它能让你不仅能够控制而且可以看到应用部署的过程。我们看到了一种典型的部署方式就是部署应用AB,但还有很多其他的方式比如blue-green或者canary。在以后的文章中我会演示OpenShift中其他的部署模式。请分享你的经验和成果。我希望这篇文章能够帮到你。

Happy OpenShifting!

原文链接:OpenShift v3: Basic Release Deployment Scenarios(翻译:王晓轩,校对:许小平)

红帽产品总监回顾容器与PaaS的发展历程

hokingyang 发表了文章 • 1 个评论 • 5828 次浏览 • 2015-12-25 11:08 • 来自相关话题

【编者的话】本文作者是红帽的产品经理Daniel Riek,他有17年以上的软件开发和开源工作经验。文章回顾了OpenShift的整个发展历程,介绍了OpenShift从2011年到现在遇到的一些机遇和挑战。虽然有点软,但也是全面梳理了PaaS这几年的情况,以 ...查看全部
【编者的话】本文作者是红帽的产品经理Daniel Riek,他有17年以上的软件开发和开源工作经验。文章回顾了OpenShift的整个发展历程,介绍了OpenShift从2011年到现在遇到的一些机遇和挑战。虽然有点软,但也是全面梳理了PaaS这几年的情况,以及与容器的结合点。

@Container容器技术大会将于2016年1月24日在北京举行,来自华为、CoreOS、红帽、SAE、网易蜂巢、七牛、爱奇艺、微博、腾讯、去哪儿网、美团云、京东、蘑菇街、惠普、暴走漫画、光音网络、领科云等知名公司的技术负责人将分享他们的容器应用案例。

又是一年将近,又到了回顾和总结的时候。过去的一年对容器生态系统来说有着特殊的意义,因此要特别感谢RedHat和OpenShift。RedHat在2015年表现优异;而对OpenShift来说,无论从产品本身和市场来看,2015年都是一个转折点。
回顾
OpenShift是红帽开源的PaaS云平台软件,发布于2011年春季,与Cloud Foundry(成立于同一时间)一同提供Platform-as-a-Service(PaaS)解决方案。在那时,早期的PaaS领域被Heroku,GAE和其他一些PaaS产品统治着。OpenShift和CF则以开源方式向企业用户提供混合云方案,这种方案可以运行于笔记本电脑,数据中心或者公有云。随着PaaS周边生态环境扩大,新竞争者加入,客户可以从更多选择中获益。

到了2013年春季,开发者通过在OpenShift开发新应用以及商业解决方案OpenShift Enterprise(RedHat通过自己支持的软件在数据中心运行私有PaaS平台)和OpenShift Online(红帽支持的在公有云中部署PaaS服务)的发布,红帽获得了很大的成功。这一趋势通过社区开发者和早期企业用户得到证明。CF看到了同样的趋势,本年晚些时候,VMware收购Pivotal作为独立运作公司,集中于PaaS领域的机会。
转折点
2013年春季有一些重要事情发生,3月21日在PyCon,dotCloud的Solomon Hykes第一次演示了Docker技术。Docker提供了一种在Linux容器中运行应用的新方式。容器技术本身并不陌生,实际上OpenShift、CF、dotCloud和其他PaaS平台核心都是基于容器技术的,容器技术可以追溯到2000年。但是在红帽,产品团队意识到Docker可以帮助标准化容器应用打包和部署,而且看到快速增长中的Docker生态系统的潜力;因此,RedHat宣布九月份将Docker嵌入RHEL中,并完全重建了OpenShift。

在决定Docker标准化过程中,我们和Google、Twitter和其他公司讨论了容器的未来以及容器调度扩展的问题。Google作为RedHat最重要的合作伙伴,有多年的容器使用经验。在和Google讨论新技术过程中,我们了解到Google有一项新的容器调度和集群管理技术并且希望开源它,这个技术就是Google和RedHat于2014年7月在开源社区发布的Kubernetes项目。
Docker Comes to Enterprise Linux
2014年中,RedHat发布了RHEL7支持Docker容器,同时也发布了RHEL Atomic Host,一个基于容器优化的最小linux发行版。Red Hat Enterprise Linux 7 / Atomic + Docker + Kubernetes 将会演变成新的OpenShift基础平台。OpenShift,诞生于PaaS生态系统中,现在则完全演变为快速发展的容器生态系统中一员。

同时,Pivotal也忙于构建一套新的开源基础来监控CF项目,看起来是基于OpenStack,意图则是控制开源PaaS生态系统。当 Cloud Foundry基金会发布时,OpenShift3正处于进展良好的状态,RedHat决定不参与其中。我们当时有一篇博客 ,解释了为什么我们不参与其中,其中最有趣的当属,业界当大如EMC、IBM、HP等巨头参与这种新PaaS基础平台时,RedHat已经和Docker、Google、CoreOS等创新公司专心于开源容器生态系统了。
OpenShift 3 创生
时间很快来到2015年6月,OpenShift Enterprise 3在RedHat峰会上发布了,此时OpenShift平台被重新改造,完全内嵌了Docker和Kubernetes,而此时他们都成为了最火的开源项目。于此基础之上,随后又推出了OpenShift Enterprise 3.1、Atomic Enterprise Platform Public Preview 和 OpenShift Dedicated 公有云服务。

最近行业报道和文章都强调了这个成就,并且将OpenShift Enterprise 3.1 称为“典型容器平台”,为之称道的不仅是Docker和Kubernetes,还有相应的功能:包括自动映像创建、自动部署服务、应用生命周期管理、语言框架访问、中间件和数据库等等。这些新特性使得使用Java、Node.js、Ruby、Python、PHP甚至Microsoft.NET的用户都可以从中受益,加快应用开发和部署。从PaaS到容器应用平台的转型顺利完成。

生态系统比较
OpenShift所处其中的容器生态系统目前就像龙卷风,推动着新技术,新概念和新方案快速发展,我们相信变革是必然的。

以Cloud Foundry基金会为代表的PaaS生态系统,由若干个供应商主导,其中最主要的是Pivotal,这个生态系统围绕着CF容器环境(Garden),最近为了支持prototype,转到了Open Container Initiative发布的runC标准(RedHat在此标准中作为维护者)。围绕CF buildpacks打包格式的生态系统面临着来自Docker Hub成千上万镜像的竞争。CF调度引擎(diego)现在不仅面临Kubernetes的竞争,而且还需要面对Docker Swarm和Apache Mesos的挑战,而这两个平台目前发展迅速,并且提供了更多的功能。

Docker生态系统目前有超过1200 GitHub贡献者和27000明星项目。Kubernetes生态系统有将近600贡献者,很多用户已经使用它作为生产系统调度器。这些新技术,RedHat作为领导者之一,不仅支撑着OpenShift,而且还包括GAE和很多其他供应商方案。新的基础平台,例如Open Container Initiative 和Cloud Native Computing Foundation(CNCF) ,都是为了管理这些新技术而生,而且都在很多客户那里运行良好。目前看起来任何闭源产品都难于跟开源大环境相竞争。

前景
从平台技术角度来开,OpenShift从容器生态环境的变革中得到巨大益处,使得OpenShift可以支持更广泛的企业级应用,包括无状态,云应用和传统应用,而这个特点,对于在现有应用投入巨大的传统用户来说,无疑提供了更加容易些的转型方法。

仍然有很多事情要完善,我们展望2016,能看到许多激动人心的东西呼之欲出,例如OpenShift Enterprise 和 OpenShift Dedicated 以及 OpenShift Online等新版本推出。Red Hat Atomic Container 基础架构平台将会在容器网络、存储、企业服务Registry、内置ELK栈扩展日志能力等多方面迎来新的进展。

红帽JBoss中间件团队将会扩展支持Red Hat Mobile和 Business Process Management 方案。OpenShift内置Docker自动创建能力将会增强,使得用户可以从源码,二进制或者现存系统直接创建容器;我们还计划通过cloudforms扩展容器管理和操作管理,增强对OpenStack和公有云例如Amazon、Google和Microsoft Azure的支持;我们还将丰富OpenShift Reference Architectures 内容,提供部署和操作中的最佳实践。

总而言之,我们坚信Openshift3将会提供一个目前和未来最佳的方案,展望未来,欣喜无限。

Guten Rutsch und Frohes 2016!

原文链接:Containers & PaaS: A Tale of Two Ecosystems(翻译:杨峰)

OpenShift 3 : 基于Docker的私有PaaS平台

jacksonlee 发表了文章 • 1 个评论 • 23332 次浏览 • 2015-08-05 13:52 • 来自相关话题

【编者的话】OpenShift是一个非常有前途的私用PaaS解决方案,它可以减少从项目开始到自动构建应用和部署的时间,它支持绝大多数的Web架构,将成为基于Docker的私有PaaS平台领域的参照。 OpenShift是一个私有的Pa ...查看全部
【编者的话】OpenShift是一个非常有前途的私用PaaS解决方案,它可以减少从项目开始到自动构建应用和部署的时间,它支持绝大多数的Web架构,将成为基于Docker的私有PaaS平台领域的参照。

OpenShift是一个私有的PaaS(Platform-as-a-Service)解决方案,主要用来在容器中搭建、部署以及运行应用程序。它是基于Apache 2.0许可的开源软件, 并且发行了两个版本, 一个是社区版, 一个是企业版。
#第三版的起源
从2014年7月开始,OpenShift就己经着力于研究一个非常出色的项目,该项目是将技术架构和与Docker、Kubernetes整合到一起(现在这是一件很常见的事)。

一年前启动这个项目对于OpenShift来说是一个大胆而且充满风险的的决策。确实如此,当时云平台的竞争处于白热化的巅峰时期,而此时OpenShift就决定冒着风险启动这样一个非常重要的重建项目,这个风险主要来自于他们需要停止新特性的开发并妥协旧版本之间的兼容性问题。但是现在,我们相信他们作了一个正确的决定。

到目前为止,社区版本联合了86名GitHub上(GitHub是红帽上10个最活跃项目之一)的开发志愿者。在12个月中己经进行了16次的迭代,并且刚刚发行了第一版。

尽管这个方案主要是由红帽在推动,但它非常依赖Kubernetes(来源于Google)。由此引发了到底由谁来支配和主导的问题,该问题依赖于两个公司之间的协作和版本基准,Google开发出新特性后会发生什么?他们会关注Kubernetes还是OpenShift?很显然,这个问题还没有答案。

但是,Google的技术支持部门己经确认,由两家公司同时主导只会给OpenShift带来更多益处。它将成为比Docker企业版更具竟争力的产品(机器、组建和群)。
#自动构建和部署应用:这怎么实现
OpenShift V3提供了3种来自动构建应用的方法。

  • Docker-File模式:通过向OpenShift提供指向Docker-File以及其依附关系的源码管理器的URI来自动构建一个Docker容器。
Openshift3-Docker-Build-1024x290.png
  • Source-To-Image模式(STI):允许通过提交应用的源码到OpenShift来自动构建一个应用。(就像Heroku中的buildpacks)。
Openshift3-STI-Build-1024x430.png
  • 自定义构建模式:允许提供自己的应用来构建逻辑,这是通过提供一个OpenShift Docker的镜像实现的。
Openshift3-Custom-Build-1024x430.png
当注册表有新的应用映像版本发布或者应用配置有了更新时,OpenShift 3还允许定义一个自动的部署策略。
Openshift3-Deployment-Strategy-1024x430.png
为了完成这些构建和部署特性,OpenShift 3提供了把它自己的应用蓝图定义成用JSON或Yaml格式的模板文件的功能。这些蓝图描述了应用的架构拓扑和容器的部署策略。下面的图表描述了在OpenShit中,如何为3层应用将模板的不同组件进行组合的。
Architecture-OpenShift-v3-OCTO-Technology-Edouard-DEVOUGE-1024x695.png
在“模板”中组合组件,一定程度上继承了Kubernetes的概念,需要记住以下主要的对象。
  • 一个“POD”是一个Docker容器的运行环境(如果需要共享本地的资源, 我们将在单独的POD中部署两种类别的容器)
  • 一个“服务”是一个入口,抽象出一个均衡访问负载到一组相同的容器,理论上,最少是一个服务对应一个架构层。
  • 一个“服务部署者”或“部署配置”是一个对象,用来描述基于触发器的容器的部署策略。(比如,当Docker注册表中有新版本的映象时,需重新部署)。
  • 一个“复制控制器”是一个技术组件,主要负责POD的弹性。
  • 一个“路由”是用来显露一个应用的入口(域名解析,主机名或VIP)
通过它的多重部署机制和设置自身“蓝图”的能力,OpenShift第三版适用于大多数的复杂应用架构。#引擎盖下的优雅架构OpenShift 3的架构可以作为单机模式部署(使用Docker镜像“openshift/origin”),也可以作为分布式模式部署。在随后的案例当中,用了两种服务器角色,主服务器和节点。“主服务器”节点的功能是:
  • 处理来自于命令行或Web界面的API请求。
  • 构建映象和部署容器。
  • 确保POD复制的弹性。
“主服务器”依赖于基于etcd的分布式目录,主要用来提供配置共享和服务发现。“节点”主要用来作为PODS的宿主和运行容器(应用和注册表)。
Architecture-OpenShift-v3-OCTO-Technology-1024x619.png
架构是分布式的、可扩展的和具有弹性的。但是平台自身暂时还不支持自动扩展能力:目前可提供的和底层服务器的容量计划只能通过手动调整。可通过自身的REST API、CLI、Web portal来访问和管理平台。
Portail-OpenShift-v3-OCTO.png
#OCTO的观点OpenShift 3在介于“平台即服务”和“容器即服务”的世界间架起了一个有趣的桥,红帽提出了一个大胆的解决方案和一个最先进的架构,我们非常感谢“蓝图”的规格格式,来定义架构的需求格式和部署的编排。在Beta 3版本中,OpenShift平台并没有强烈关注平台的可操作性,暂时并不建议将它应用在生产环境中,但是用户的各种问题是可以在路线图中找到的。
我们相信在OpenShift 3中建模一个应用将会是一份新的工作,它需要新的技能,以便可以提出适当的问题,比如:如何组织容器?是否应该使用路由或服务?如何处理数据(一致性、复制、备份)?如何管理多重租用?如何集成开发和部署软件工厂?

总而言之,OpenShift是一个非常有前途的私用PaaS解决方案,它可以减少从项目开始到自动构建应用和部署的时间,它支持绝大多数复杂的Web架构,即使是数据的管理和外部服务的集成还没有得到完全应用。

我们相信OpenShift 3对一切都尽在掌握,它将成为基于Docker的私有PaaS平台领域的参照。

原文链接:OpenShift 3 : private PaaS with Docker(翻译:Jackson LEE)

OpenShift 3,红帽将容器的力量交给了开发者

Nego_Wang 发表了文章 • 1 个评论 • 4603 次浏览 • 2015-06-30 22:39 • 来自相关话题

【编者的话】近期红帽发布了两个新品:一是OpenShift 3,还有就是红帽Atomic Enterprise平台,更加的注重容器化技术在产品上的应用,同时红帽将容器技术使用的对象重点放在了开发人员身上,使容器化技术成为开发者的一个工具。 ...查看全部
【编者的话】近期红帽发布了两个新品:一是OpenShift 3,还有就是红帽Atomic Enterprise平台,更加的注重容器化技术在产品上的应用,同时红帽将容器技术使用的对象重点放在了开发人员身上,使容器化技术成为开发者的一个工具。

随着新版的OpenShift和全新的Atomic Enterprise平台的发布,红帽正逐渐地将容器化技术变成一个开发者的工具,而不仅仅是管理员的工具。

如果之前红帽不算是一个“容器化的公司”的话,那么现在应该算是了,容器化技术不仅仅让管理员来使用红帽的产品。

在本周的红帽峰会上,该公司发布了一系列公告,其中一个最重大的公告是关于红帽即将成为一个容器化的平台,不仅用容器技术来扩展红帽平台现有的应用,而且该平台还针对应用程序开发人员,而不仅仅是那些负责维护红帽产品的管理员。

红帽峰会上,首先发布的是OpenShift 3,最新版的OpenShift 3经过改良来适应容器的时代,并且它的"cartridges" 和"gears"退居让位给了Docker和Kubernetes。来自InfoWorld的Martin Heller发现使用OpenShift的用户体验很好,并称OpenShift正在吸收其它的技术以让平台更容易使用。其中OpenShift的关键点是它以开发人员为中心,可以用```git push```来部署,CI 可以用 Jenkins来做,现在,Docker和Jenkins已经能够紧密结合了,所以新版的OpenShift增强了这方面的互补性。

峰会上的另一个重点是,红帽Atomic Enterprise平台,被称为OpenShift Lite,它是建立在红帽Linux Atomic 主机上的,它支持和OpenShift一样的堆栈,并减去了上层语言运行的环境和中间件。为此,红帽Atomic Enterprise平台对于想要在某些环境中运行容器是非常有用的,它不需要OpenShift来提供额外的工具。

红帽早就决定是容器技术称为他们公司产品的基石,由此引发一场由栈的底部开始的一场赌博,从管理员干的事情开始,而不是顶部开发人员的所处的位置开始。举个例子:用容器化技术作为一种对系统本身进行更新的方法,因此如果需要的话对系统的更新可以进行回滚。红帽的容器认证和容器注册项目试图解决容器的可验证性和完整性在企业版本的设置中 --本质上,是处理一个持续的低级容器问题,而不是一种更高层次的容器问题。

随着OpenShift 3和 Atomic Enterprise平台正逐步被开发者接受,两者都将成为红帽创建一个完全开放的开源和开放标准的企业混合云解决方案的核心

红帽的最初计划是通过OpenStack来做,但是这一战略已经涉及到了容器化技术,OpenShift,可能还包括CloudForms 管理层方面的东西。以上三者已经可以从红帽找到一个统一的产品,红帽承诺随着时间的推移,他们之间会有更加紧密的整合。

原文链接:With OpenShift 3, Red Hat puts container power in developers' hands(翻译:王辉 校对:李颖杰)

安装 openshift/origin v3 异常

回复

hzchenkj 回复了问题 • 2 人关注 • 1 个回复 • 1296 次浏览 • 2018-12-14 09:52 • 来自相关话题

openshift pod 用户权限设定

回复

zhanglianx111 回复了问题 • 5 人关注 • 4 个回复 • 6315 次浏览 • 2016-06-08 17:45 • 来自相关话题

openshift/origin v3 安装 make clean build 停滞

回复

东方星痕 回复了问题 • 3 人关注 • 3 个回复 • 3248 次浏览 • 2016-03-20 23:01 • 来自相关话题

DockOne微信分享(一九五):智融集团基于OpenShift的容器化PaaS平台实践

大卫 发表了文章 • 1 个评论 • 1887 次浏览 • 2018-12-07 16:32 • 来自相关话题

【编者的话】当前,是不是使用容器已经不是一个被讨论的重点;热点已然成为企业如何高效使用容器、如何利用容器给企业带来切实的收益。从底层的Docker到优秀的容器编排Kubernetes,都给我们带来了令人心动的基础。今天分享的OpenShift实践(基于Dock ...查看全部
【编者的话】当前,是不是使用容器已经不是一个被讨论的重点;热点已然成为企业如何高效使用容器、如何利用容器给企业带来切实的收益。从底层的Docker到优秀的容器编排Kubernetes,都给我们带来了令人心动的基础。今天分享的OpenShift实践(基于Docker和Kubernetes的容器PaaS云平台),希望可以给大家基于Docker和Kubernetes构建自己的容器云带来一点儿思路。
#概述与实践背景
OpenShift是红帽的云开发平台即服务(PaaS)。 通过OpenShift,企业可以快速搭建稳定、安全、高效的容器应用平台。在这个平台上:

* 可以构建企业内部的容器应用市场,为开发人员快速提供应用开发所依赖的中间件、数据库等服务。
* 通过自动化的流程,开发人员可以快速进行应用的构建、容器化及部署。
* 通过OpenShift,用户可以贯通从应用开发到测试,再到上线的全流程,开发、测试和运维等不同的角色可以在一个平台上进行协作。
* 支持LDAP用户权限管理,支持细粒度的权限资源管理。
* OpenShift可以提高应用从研发到上线的效率和速度,缩短产品上市的时间,可以有效地帮助企业推进DevOps,提高资源利用率,提升生产效率。

当前公司业务和产品处于快速迭代和扩展阶段,每个月平均新增10多个新服务,迭代速度越来越快,同时废弃的老服务服务也越来越多。新技术发展也比较快,在旧方式的基础上我们尝试新技术的成本也比较高,维护成本也比较高。如何提高我们的生产效率,如何提高我们适应新时代的能力,越来越重要。

在此背景下,我们先列出以下几个比较明显的问题:

  1. 阿里云上的资源利用率比较低
  2. 临时扩容比较慢,在运营活动的时候经常资源不足,需要升级配置,活动结束后资源利用率回归较低水平
  3. 新服务增加快,新机器、SLB等越来越多,越来越不好维护
  4. 微服务治理的重要性越来越突出

为了解决这些问题,我们调研了多个解决方案,最终选择了OpenShift作为PaaS平台;截止目前已有70多个项目、100多个服务、上千个pod运行在PaaS平台里(包括Python27、Python36、Java、Golang、Nodejs等类型服务)。
#OpenShift结构
1.png

2.png

3.png

4.png

5.png

OpenShift层级自底而上,分为:基础架构层、容器引擎层、容器编排层、PaaS服务层、界面及工具层。
##Docker
优势:构建一个隔离的、稳定的、安全的、高性能的容器运行环境;很多开源项目都做了自己的官方镜像,使用和维护更方便。
##Kubernetes
容器编排。

* 容器调度:按业务的需求快速部署容器到指定的目标
* 弹性伸缩:按业务的需求快速扩展或收缩容器的运行实例数量
* 异常自愈:当容器实例运行异常,集群能自动感知、处理并恢复服务状态
* 持久化卷:为散布在集群不同机器上的容器提供持久化的智能对接
* 服务发现:为业务为服务化提供服务发现及负载均衡等功能
* 配置管理:为业务应用提供灵活的配置管理及分发规则

##OpenShift
容器云,在Docker和Kubernetes的基础上提供了各种功能,以满足业务应用、研发用户及运维用户在生产效率上的诉求。

1)应用开发框架及中间件

OpenShift提供了丰富的开箱即用的编程开发框架及中间件,如Java、PHP、Ruby、Python、JBossEAP、Tomcat、MySQL、MongoDB及JBoss系列中间件等。

2)应用及服务目录

OpenShift提供了如软件市场式的服务及应用目录,可以实现用户一键部署各类应用及服务,比如一键部署Hadoop集群和Spark集群。

3)自动化流程及工具

OpenShift内置了自动化流程工具S2I(SourcetoImage),帮助用户自动化完成代码的编译、构建及镜像发布。

4)软件自定义网络

通过OpenVSwitch,OpenShift为用户提供了灵活强健的软件定义网络。实现跨主机共享网络及多租户隔离网络模式。

5)性能监控及日志管理

OpenShift提供了开箱可用的性能监控及日志管理的组件。通过平台,业务能快速获取运行状态指标,对业务日志进行收集及分析。

6)多用户接口

OpenShift提供了友好的Web用户界面、命令行工具及RESTfulAPI。

7)自动化集群部署及管理

OpenShift通过Ansible实现了集群的自动化部署,为集群的自动化扩容提供了接口。
#OpenShift核心流程
6.png

##一、应用构建
第1步,部署应用。流程的开始是用户通过OpenShift的Web控制台或命令行ocnew-app创建应用。根据用户提供的源代码仓库地址及Builder镜像,平台将生成构建配置(BuildConfig)、部署配置(DeploymentConfig)、Service及Route等对象。

第2步,触发构建。应用相关的对象创建完毕后,平台将触发一次S2I构建。

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

第4步,生成镜像。构建成功后将生成一个可供部署的应用容器镜像。平台将把此镜像推送到内部的镜像仓库组件Registry中。

第5步,更新ImageStream。镜像推送至内部的仓库后,平台将创建或更新应用的ImageStream的镜像信息,使之指向最新的镜像。
##二、应用部署
第6步,触发镜像部署。当ImageStream的镜像信息更新后,将触发平台部署S2I构建生成的镜像。

第7步,实例化镜像部署。DeploymentConfig对象记录了部署的定义,平台将依据此配置实例化一次部署,生成一个Deploy对象跟踪当次部署的状态。

第8步,生成ReplicationController。平台部署将实例化一个ReplicationController,用以调度应用容器的部署。

第9步,部署容器。通过ReplicationController,OpenShift将Pod及应用容器部署到集群的计算节点中。
##三、请求处理
第10步,用户访问。用户通过浏览器访问Route对象中定义的应用域名。

第11步,请求处理并返回。请求到Router组件后,Router根据Route定义的规则,找到请求所含域名相关联的Service的容器,并将请求转发给容器实例。容器实例除了请求后返回数据,还会通过Router将数据返回给调用的客户端。
##四、应用更新
在应用更新时,平台将重复上述流程的第1步至第9步。平台将用下载更新后的代码构建应用,生成新的镜像,并将镜像部署至集群中。值得注意的是,OpenShit支持滚动更新。在第9步时,平台将通过滚动更新的方式,保证应用在新老实例交替时服务不间断。
#部署结构
7.png

8.png

OpenShift集群:

  1. 所有服务组件都部署在阿里云上,即VPC网络的ECS机器上,分布在多个可用区
  2. 3个Master节点,3个etcd节点,1个Harbor节点,N个Node节点
  3. Node节点分布,根据业务类型打tag标签区分,分别为:

* Base集群,主要运行Router服务和Registry镜像仓库
* Inf集群,主要运行内部PyPI的Python内部源、Nexus服务(主要作为Maven的内部源)和Trace系统等
* Gateway集群,主要部署OpenResty的API网关服务,作为流量从公网域名进入集群的入口
* Service集群,主要部署公司的业务应用
* Bigdata集群,主要部署公司内部大数据服务

  1. 变更平台,通过调用OpenShift的API,来完成build镜像、通过Deployment的模板来更新容器,并记录当前Pod的代码版本。
回滚时选择代码版本对应的一个Deployment配置文件来进行重新部署(注意:Deployment是使用的img的对应hash值,启动参数主要通过ConfigMap、配置中心Apollo来管理)。

#服务镜像自动化及部署模板
SourcetoImage(S2I),S2I是OpenShit的一个重要功能。容器镜像是容器云的应用交付格式。容器镜像中包含了应用及其所依赖的运行环境。通常的做法必须基于外部的基础镜像构建包含企业自身开发代码的应用。这个镜像的构建过程是必须的,要么由企业的IT人员手工完成,要么使用某种工具实现自动化。

作为一个面向应用的平台,OpenShift提供了S2I的流程,使得企业内容器的构建变得标准化和自动化,从而提高了软件从开发到上线的效率。

一个典型的S2I流程包含了以下几个步骤。

  1. 用户输入源代码仓库的地址。
  2. 用户选择S2I构建的基础镜像(又称为Builder镜像)。Builder镜像中包含了操作系统、编程语言、框架等应用所需的软件及配置。OpenShift默认提供了多种编程语言的Builder镜像,如Java、PHP、Ruby、Python、Perl等。用户也可以根据自身需求定制自己的Builder镜像,并发布到服务目录中供用户选用。
  3. 用户或系统触发S2I构建。OpenShift将实例化S2I构建执行器。
  4. S2I构建执行器将从用户指定的代码仓库下载源代码。
  5. S2I构建执行器实例化Builder镜像。代码将会被注入Builder镜像中。
  6. Builder镜像将根据预定义的逻辑执行源代码的编译、构建并完成部署。
  7. S2I构建执行器将完成操作的Builder镜像并生成新的Docker镜像。
  8. S2I构建执行器将新的镜像推送到OpenShift内部的镜像仓库。
  9. S2I构建执行器更新该次构建相关的ImageStream信息。

S2I构建完成后,根据用户定义的部署逻辑,OpenShift将把镜像实例化部署到集群中。

除了接受源代码仓库地址作为输入外,S2I还接受Dockerfile以及二进制文件作为构建的输入。甚至可以完全自定义构建逻辑来满足特殊的需求。
#部署模板
通过自定义模板,可以创建我们自己应用发布时的快速部署模板。包括应用所需的多个Build Config、Deployment Config、Service及Route等对象。OpenShift的Template还有一个重要的特性就是参数化。用户可以在Template中定义参数,这些参数在用户部署模板时将显示在Web控制台的界面上,供用户输入。用户的输入最终以环境变量的方式传递到容器内部。一个OpenShift的Template在结构上主要分为三个组成部分:元信息、对象列表及参数列表。
9.png

10.png

#服务访问拓扑
11.png

容器里的服务都是阿里云VPC的网络,旧服务都是部署在阿里云经典网络的ECS机器上,互相访问时经典网络SLB是一个问题。需要将原来经典网络的SLB换成VPC的SLB。

* 容器互访:通过Hostname+Port方式,后端是Service Ip
* 容器访问经典网络服务:将经典网络的SLB,替换成VPC的SLB
* 经典网络的服务访问容器:a、HTTP服务,通过内网域名;b、RPC服务,通过映射的Node+Port
* 外网访问容器里的服务:外网域名——>外网SLB——>API网关——>容器service的HOSTNAME——>Pod

#踩过的坑

日志搜集:因为Pod来回漂移,我们采用了阿里云的NAS存储(NFS)作为PV;日志文件会被共享,并被抢占文件句柄,导致日志里有乱码;我们的处理方式是日志中间加一个hostname,例如:interface.paris-interface-service-c-44-bx52z.debug,日志收集时flume匹配interface..debug。
* 监控:OpenShift自身的监控是使用Hawkular+Cassandra+Heapster,只有容器的CPU、内存和网络的监控。Pod版本的一致性、events监控(包括imge的生命周期、Pod的生命周期、服务健康检查等)、PV的监控等,都需要通过API来获取并加入到自建的监控平台
* CronJob不太稳定,crash时间比较长的情况下,Job下一次执行时间延迟过久,导致不再执行。这种情况只能删除后重新部署Job。
* 版本的控制,Deployment需要通过配置里的image的hash值,找到对应的build(build信息里记录了代码版本信息,包括commit、commit message、更新人和提交时间等);回滚的时候要选择代码版本对应的build,然后再找到build对应的Deployment配置进行部署,这些都通过自建的变更平台来管理。

#我们正在做的事情

  1. 将API网关替换成Kong,目前已经测试了大部分的功能,但Docker版本的稳定性比较差,QPS在很小的情况下也有很多5XX的错误。解决方案:在Kubernetes的Node节点上部署
  2. 由于现在对外业务发展比较快,计划通过模板、环境变量和配置中心来创建一整套的Applications,类似于SaaS服务。
  3. 打通QA,研发人员从提交代码转换到交付一个线上可用的image

#OpenShift对容器化PaaS平台实践的借鉴意义

  1. S2I自动构建服务镜像,实现自动化的流程。
  2. 部署模板,根据某一类型服务进行快速部署deployment(例如Python的模板、Golang的模板等);在每一个版本的deployment配置中,img会使用镜像的hash值(例如:docker-registry.default.svc:5000/XXX/XXX@sha256:899007bXXXXXXXX95f561203cba);回滚时选择对应版本的deployment配置进行重新部署即可达到回滚的目的。
  3. Router服务使用haproxy(支持AB部署,及流量分配),Router根据Route定义的规则,找到请求所含域名相关联的Service的容器,并将请求转发给容器实例。容器实例除了请求后返回数据,还会通过Router将数据返回给调用的客户端。
  4. LDAP用户管理等多用户管理,通过clusterrole/clusterrolebinding(全局资源权限)、role/rolebinding、serviceaccount、oauthaccesstoken等来实现权限分配。

#Q&A
Q:请问通过OpenShift怎么做高可用?

A:OpenShift的高可用是跟Kubernetes的高可用一致的,就是延用了Kubernetes的Service IP来做服务发现和负载均衡;另外,前面还有DNS服务,(例如:Hostname: service.abc-platform.svc,后端就是指向的Cluster IP)。



Q:image的版本管理怎么做?配置的版本管理怎么做?

A:OpenShift本身会创建一个内部的Registry,S2I会根据Build来更新image,image的版本是通过sha256的值对应Build的版本号(例如:Build版本#24对应registry.intra.XXXXX.com/abc/abc-platform@sha256:b41c8XXXXX)。



Q:OpenShift的网络方案性能方面有多少损耗?与Kubernetes的其他网络方案如Calico相比如何?

A:OpenShift的网络方案是选择的OVS,性能上肯定是不如Calico,但对于我们的服务足够了。目前跑了有1000+个常驻Pod,没有出现网络瓶颈。



Q:如果要做多机房部署,OpenShift能搞吗?

A:多机房,只要网络能通并比较稳定,是没有问题的。我们的部署方案就是在阿里云上的多个可用区部署的。而且,实际部署中也是鼓励多机房部署的,服务容灾要求一个服务要分布在多个可用区(或者机房)。



Q:你们平台性能怎么测试的,如何集成?

A:平台性能测试分两部分。一个是在集群内部不经过Nginx直接压测SVC,另外一个是在集群外部压测内网域名或者公网域名。



Q:我们之前用自己的镜像仓库,会报tls认证错误,不知道你们有没有遇到过?

A:我们自己创建的镜像仓库是用Harbor来建的,只是存储了内部服务的基础镜像。OpenShift集群内的镜像仓库有自己的认证,是通过Secrets管理对应权限的token(用户级别的权限是通过LDAP统一认证的)。S2I创建服务镜像的时候就是通过这个来获取权限的。还是比较稳定的,没有出现过tls的问题。



Q:生产环境的节点磁盘是怎么分区分配的?

A:生产环境的节点磁盘分为两类,一个是系统盘(40G),另一个是数据盘(100G);Docker的所有镜像、实例等存储都是放在数据盘,服务的日志是挂载的PV,PV是使用的阿里云的NAS存储。



Q:我看你们在集群访问前加了个API Gateway,用了OpenResty,这个能详细介绍下不?

A:可以简单理解为一个自建的Ingress,底层是Nginx+Lua实现的;我们已经在逐渐使用Kong来代替OpenResty了。Kong底层是Nginx+Lua+PostgreSQL,界面管理是使用的Konga。选择Kong和Konga的理由是:1、Kong的管理和插件比较方便,还支持自定义插件;2、Konga的界面更友好,管理、更新都比较方面。



Q:看到你们是3 Master高可用,请问Kubernetes–Master挂了一台,OpenShift如何恢复,etcd是否有做备份?

A:我们做过演练,Master挂一台或者两台,甚至三台全挂,也不会影响已经运行的服务。只要etcd没有问题,重启Master或者新增一台Master都可以快速恢复集群。etcd的备份是做的全量备份,上传到我们的备份存储中。



Q:内网域名是怎么跨VPC和经典网络的?

A:内网域名的DNSPod解析是解析到VPC网络的一台ECS机器上,通过Nginx来管理。经典网络的ECS服务集群可以在前面挂载一个VPC网络的SLB,Nginx的对应Server Name的Upstream设为这个VPC的SLB即可。



Q:监控和告警好像没有提到,感觉OC这块似乎缺失?
A:OpenShift本身有很多服务状态,image的Build、pull、push等状态,以及Pod的各种状态;另外还有强大events。我们是通过OpenShift的API来获取这些信息,同步到Open-Falcon上来处理报警信息的。

Q:日志统一生产、采集用了什么方式能介绍一下吗?

A:日志是一个比较疑难的问题;我们的解决方案是,所有的日志都打到同一个NaS盘上,服务名来做该服务的日志路径。多个Pod打同一个日志的时候,会出现乱码。我们的解决方案是在日志中间加hostname,例如haadeers.melon-crm-b-9-nb9d5.log,melon-crm-b-9-nb9d5为Pod的hostname。日志收集,是几台ECS机器挂载改NAS盘,启动flume来收集。



Q:业务应用的自动化发布你们是怎么设计的,能否详细说说思路?

A:我们是自己定义了S2I的流程。Build镜像统一在代码仓库里加一个Makefile,定义一个assemble的role,来做初始化(依赖包的安装、nodejs的Build等等,这里还是比较灵活的)。自动发布:我们是自定义了部署模板,分享的内容里有(只需要简单的几步就可以部署一套服务)。



以上内容根据2018年12月4日晚微信群分享内容整理。分享人徐义义,智融集团容器化PaaS平台负责人,从事云计算开发、设计6年(其中4年私有云,2年公有云<京东云>),目前负责公司内部PaaS平台的建设与应用的变更交付。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesd,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

Openshift使用openvswitch实现多租户网络隔离的理解

yekaifeng 发表了文章 • 0 个评论 • 1515 次浏览 • 2018-09-29 20:06 • 来自相关话题

前 言 我们在使用openshift的过程中,当调整网络,偶尔会遇到曾经能通的namespace突然间不通了。这个问题一直困扰着我们。后来通过调研,发现这是网络隔离方式导致的。这篇文章希望能对遇到类似困惑的同学带来帮助。 ...查看全部
前 言

我们在使用openshift的过程中,当调整网络,偶尔会遇到曾经能通的namespace突然间不通了。这个问题一直困扰着我们。后来通过调研,发现这是网络隔离方式导致的。这篇文章希望能对遇到类似困惑的同学带来帮助。

正 文

当前我们使用openshift提供的ovs-multitenant plugin作为网络解决方案。它具备基于VXLAN的多租户隔离能力。每个项目(projects)被分配一个唯一的ID,对应VXLAN的VNID, 缺省项目之间的POD是不能互相通信的。用户可以通过oc adm pod-network 子命令对租户网络进行管理。

pic1.jpg


每个project对应一个NETID (即VXLAN VNID),创建新project也会分配一个新的VNID。

    # oc get netnamespaces
NAME NETID EGRESS IPS
project1 5910689 []
project2 4296943 []
project3 8988879 []


要打通两个project之间的网络连接,使用join-projects 子命令。执行后它们的NETID设置成相同, project1 的NETID被改成project3的。这样具备相同VNID的两个项目就可以互相通信了。

pic2.jpg


原来
    # oc adm pod-network join-projects --to=project2 project1
# oc get netnamespaces|grep project
project1 4296943 []
project2 4296943 []
project3 8988879 []


改变后
    # oc adm pod-network join-projects --to=project3 project1
# oc get netnamespaces|grep project
project1 8988879 []
project2 4296943 []
project3 8988879 []


然而, 这种处理方式有可能导致原来与project1互通的项目,无法继续通信了。比如project1与project2原来是互通的,有一样的NETID。如果让project1加入project3, 后果是1跟2变成不通了。解决办法是把2也加入3。也许我们本来并不希望2, 3之间能够相互可见。所以在操作时,必须对这种组网方式有所了解。在规划网络时,应该以环境类型作为隔离标准,把具有相互通信需求的项目规划使用相同的NETID, 区分 开发/测试/预发布/正式环境。

要把两个project重新隔离起来, 使用isolate-projects 子命令。执行后 project3 NETID不变, project1 被分配新的NETID。假如还有一个或多个project在使用这个NETID,那么project1, project3都被分配新的NETID。

    # oc adm pod-network isolate-projects project1 project3
# oc get netnamespaces|grep project
project1 14486461 []
project2 4296943 []
project3 8988879 []


假如某个项目(例如日志),需要被所有项目访问,那么使用 make-projects-global 子命令。执行后把项目的NETID设成0, 相当于可被VXLAN无条件转发。

pic3.jpg


    # oc adm pod-network make-projects-global project1
# oc get netnamespaces|grep project
project1 0 []
project2 4296943 []
project3 8988879 []


集群服务基础组件适用这种场景,例如日志,统计,调用链,等等。

当红炸子鸡区块链,如何实现企业级部署?

博云BoCloud 发表了文章 • 0 个评论 • 839 次浏览 • 2018-05-25 15:50 • 来自相关话题

随着区块链技术从概念到落地的发展趋势,从2017年开始区块链技术已经有了初步的与现实商业场景结合的可能,这个热度在2018年升到顶端,区块链技术在全球开始部署应用。而2018年的中国,舆论和资本在区块链领域双管齐下,各行业掀起了一场区块链技术应用的创新运动。 ...查看全部
随着区块链技术从概念到落地的发展趋势,从2017年开始区块链技术已经有了初步的与现实商业场景结合的可能,这个热度在2018年升到顶端,区块链技术在全球开始部署应用。而2018年的中国,舆论和资本在区块链领域双管齐下,各行业掀起了一场区块链技术应用的创新运动。


目前,众多区块链应用企业用户的选择之一是基于Docker+Kubernetes来实现 Hyperledge 应用部署,然而搭建一套 Hyperledge 的应用环境其实并不是一件轻松的事情。

在这个搭建过程中,工程师要面临一系列繁琐的工作。首先,环境的搭建要进行 Kubernetes 环境配置,其次包括 Hyperledge Fabric 应用的下载、文件合并、NFS 等服务器的搭建,并配置 Template 以及 Pythony 等应用的安装,以及 Samples 测试的应用部署也是同样需要应用文件准备、修改环境变量、应用安装、实例化操作、数据初始化等等一系列的操作,都是极为繁琐的工作内容,所以当工程师一步步在敲击命令的过程中一不小心就可能出错。

为支持各行业企业基于区块链的创新业务场景,BoCloud博云容器云平台BeyondContainer 1.7.1版本提供了基于 helm 的 Chart 实例部署,支持企业级区块链业务部署和管理,助力企业实现业务创新。

容器云平台BeyondContainer 1.7.1同样基于 Docker+Kubernetes 实现 Hyperledger 应用部署。并且以上那些繁琐的工作,平台已经帮用户进行了简化,把工程师从繁琐枯燥的命令行敲击中解脱出来。BoCloud博云容器云平台的应用商店内嵌入了区块链 Chart 类型应用,即IBM的开源区块链项目 Hyperledge Fabric,同时平台内也嵌入了镜像列表供用户选择。



BeyondContainer 1.7.1版本通过 helm 方式简化 Kubernetes 应用部署,支持一键式区块链应用部署、支持编排 helm 文件执行自定义部署、支持区块链应用的集群化部署。同时,可视化图形界面提供的自动化部署也大大简化了用户操作,平台提供给用户可替换的变量如镜像的地址和版本、Orderer 信息、NFS 信息、端口等。应用部署的整个执行过程流水线式自动完成,避免误操作带来的各种困扰,并且支持实时查看执行日志,这无疑是更高效、更安全的选择。

同时,BeyondContainer 1.7.1版本在支持 GPU 资源调度、用户管理、应用生命周期管理、企业流程管理、用户认证、系统可靠及安全性功能等方面提升了产品能力。

BeyondContainer

BeyondContainer是BoCloud博云容器云产品,产品基于微服务思想和 DevOps 理念,利用 Docker 基础平台、DevOps 组件、微服务 PaaS 中间件等功能模块提供对应用的构建、测试、部署、更新、运维管理的应用全生命周期管理功能。实现基于容器的应用持续集成和快速部署、秒级弹性伸缩、业务可用性保障等关键特性,以帮助企业应对互联网时代业务快速发展的需要。

BeyondContainer 1.7.1

BeyondContainer 1.7.1版技术底层基于OpenShift 3.6版本,同时兼容支持Kubernetes 1.8及以上版本、Docker 17之后更新的社区版。新版本在用户管理方面提供了更友好的操作界面,提供多 OpenShift 集群管理能力,使用户能够基于一个统一管理界面对多集群进行管理,简化平台日常运维工作,实现容器平台的统一管理。在应用生命周期管理方面,基于 Openshift 的 Templates 模板能力实现了复杂多模块的应用的可视化编排部署,实现了系统级(而非容器级)的应用管理能力。

作为企业级的容器云平台,BeyondContainer通过产品业务价值,帮助企业提升其核心业务的商业效率。BeyondContainer具备平台运维、平台运营、应用运维三大方面的能力,可满足企业应用运维、持续集成、微服务开发运维等多种应用场景需求。产品主要功能包括:


  • 支持区域->租户->项目->应用的层级管理
  • 基于角色的仪表盘
  • 基于 Jenkins 的 CI/CD 的流水线
  • 资源管理及对存储、集群、主机、仓库的全方位可视化管理
  • 集群,主机,容器,应用,审计,操作级日志
  • 集群,主机,组件,容器监控告警
  • 基于租户,项目的配额和计量计费
  • 全方位的镜像,容器,应用管理
  • 支持区块链应用
  • 支持 GPU 资源管理


BoCloud博云容器云平台BeyondContainer已在金融、电力、能源等行业中落地,帮助企业大幅缩短应用迭代周期,实现应用全生命周期管理,提升资源利率,产品可靠性、稳定性、安全性久经考验。博云同时为企业客户提供 DevOps 及微服务咨询服务,从开发、测试、部署、运维,帮助企业实现 DevOps 开发运维一体化,响应企业客户对业务需求和IT系统的快速升级的需求。


推荐阅读

BoCloud博云@2018 F5应用服务高峰论坛,容器云解决方案升高赋能
助力AI:博云容器云平台BeyondContainer支持GPU
QCon2018 | 微服务落地关键:设计比技术框架更重要

如何用EOS和OpenShift部署一个BaaS系统

alex_wang2 发表了文章 • 0 个评论 • 1712 次浏览 • 2018-05-21 22:00 • 来自相关话题

现在区块链技术很是热门,很多大公司都纷纷开辟了自己关于区块链的相关业务部门,比如数字金融,产品溯源等,那么什么是区块链技术呢?区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。在区块链技术的迭代过程中,大家公认比特币是区块链1. ...查看全部
现在区块链技术很是热门,很多大公司都纷纷开辟了自己关于区块链的相关业务部门,比如数字金融,产品溯源等,那么什么是区块链技术呢?区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。在区块链技术的迭代过程中,大家公认比特币是区块链1.0版本的应用,以太坊是2.0版本,现在在链圈炒的最为火热的就是自称是区块链3.0版本的EOS。它以BFT+DPos的公式机制和石墨烯网络为基础,号称公网上线以后能够达到3000-4000左右的TPS(比特币为6-7笔,以太坊是30-40笔,但是笔者亲测EOS的3.0版本是1000tps左右,可能是受环境和主机性能所限吧)。越来越多的人开始关注区块链技术,越来越多的企业级应用开始搬上区块链,现在我就带领大家搭建一套最简单的BaaS系统(block chain as a service:区块链即服务)。

当人们一说到某某即服务的时候总是想到PaaS、SaaS或者IaaS,没错!这次搭建的BaaS也和前面几个类似采用PaaS的设计架构,即用容器的方式搭建BaaS平台。提到容器,就不得不想到现在最流行的Docker+Kubernetes的方式,Docker提供了单机条件下的容器运行环境,Kubernetes提供了一套高可用的容器编排管理系统。本次使用的就是在代码级对Kubernetes做了升级和改造的,由Red Hat公司推出的OpenShift,它使用Kuberenetes作为其内核,对它网络功能做了补充,增加了Web界面,又对原有的Ingress做了优化和升级,所以我们选择它作为本次BaaS的基础平台。

BaaS平台部署图如下图所示:
server.png

1、首先开始制作EOS镜像

1)下面是EOS代码获取方法:
git clone https://github.com/EOSIO/eos --recursive

然后进入代码目录执行:
cd eos
./eosio_build.sh

2)在代码的EOS目录下会生成一个build目录,把EOS的二进制文件NodeOS、keosd、cleos等复制到要制作镜像的子目录下,下面是Dockerfile文件的内容,其中基础镜像选用了CentOS,这个系统的版本好要和刚才编译EOS的一致,否则可能会出现NodeOS无法运行的错误。
From docker.io/centos
ADD cleos /cleos
ADD nodeos /nodeos
ADD genesis.json /genesis.json
ADD config.ini /config.ini
ADD nodeosd.sh /opt/eosio/bin/
USER 0
CMD /opt/eosio/bin/nodeosd.sh

3)除了有必要的二进制文件外,我们还要为NodeOS启动配置必要的启动脚本和两个配置文件。

下面是启动脚本:
#!/bin/sh
mkdir -p /opt/eosio/config-dir
mkdir -p /opt/eosio/data-dir
mkdir -p /opt/eosio/bin
cp /config.ini /opt/eosio/config-dir
cp /genesis.json /opt/eosio/config-dir
cp /cleos /opt/eosio/bin
cp /nodeos /opt/eosio/bin
cp -r /contracts /opt/eosio/bin

CONFIG_DIR="--config-dir=/opt/eosio/config-dir"
DATA_DIR="--data-dir=/opt/eosio/data-dir"
#replace agent name
sed -i 's/AGENT-NAME/'$HOSTNAME'/g' /opt/eosio/config-dir/config.ini

#replace producer name
sed -i 's/EOSIO/'$BP'/g' /opt/eosio/config-dir/config.ini
#replace p2p-peer-address
sed -i 's/EOSIO/'$BP'/g' /opt/eosio/config-dir/config.ini

#replace p2p-server-address
sed -i 's/EOSIO/'$BP'/g' /opt/eosio/config-dir/config.ini

sed -i 's/P2P_PEER_ADDRESS/'${BP_LIST}'/g' /opt/eosio/config-dir/config.ini

sed -i 's/P2P_SERVER_ADDRESS/'${HOSTNAME}'/g' /opt/eosio/config-dir/config.ini

#replace keys
sed -i 's/PRIVATE-KEY/'${PRIVATE_KEY}'/g' /opt/eosio/config-dir/config.ini
sed -i 's/PUBLIC-KEY/'${PUBLIC_KEY}'/g' /opt/eosio/config-dir/config.ini

exec /opt/eosio/bin/nodeos -e $CONFIG_DIR $DATA_DIR $@

genesis.json文件内容:
# cat genesis.json
{
"initial_timestamp": "2018-03-02T12:00:00.000",
"initial_key": "EOSxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"initial_configuration": {
"max_block_net_usage": 1048576,
"target_block_net_usage_pct": 1000,
"max_transaction_net_usage": 524288,
"base_per_transaction_net_usage": 12,
"net_usage_leeway": 500,
"context_free_discount_net_usage_num": 20,
"context_free_discount_net_usage_den": 100,
"max_block_cpu_usage": 100000,
"target_block_cpu_usage_pct": 500,
"max_transaction_cpu_usage": 50000,
"min_transaction_cpu_usage": 100,
"max_transaction_lifetime": 3600,
"deferred_trx_expiration_window": 600,
"max_transaction_delay": 3888000,
"max_inline_action_size": 4096,
"max_inline_action_depth": 4,
"max_authority_depth": 6,
"max_generated_transaction_count": 16
},
"initial_chain_id": "0000000000000000000000000000000000000000000000000000000000000000"
}

# cat config.ini
# File to read Genesis State from (eosio::chain_plugin)
genesis-json = "genesis.json"

# override the initial timestamp in the Genesis State file (eosio::chain_plugin)
# genesis-timestamp =

# the location of the block log (absolute path or relative to application data dir) (eosio::chain_plugin)
block-log-dir = "blocks"

# Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints. (eosio::chain_plugin)
# checkpoint =

# Override default WASM runtime (eosio::chain_plugin)
# wasm-runtime =

# Maximum size MB of database shared memory file (eosio::chain_plugin)
shared-memory-size-mb = 1024

# Track only transactions whose scopes involve the listed accounts. Default is to track all transactions. (eosio::history_plugin)
# filter_on_accounts =

# The local IP and port to listen for incoming http connections; set blank to disable. (eosio::http_plugin)
http-server-address = 0.0.0.0:8888

# The local IP and port to listen for incoming https connections; leave blank to disable. (eosio::http_plugin)
# https-server-address =

# Filename with the certificate chain to present on https connections. PEM format. Required for https. (eosio::http_plugin)
# https-certificate-chain-file =

# Filename with https private key in PEM format. Required for https (eosio::http_plugin)
# https-private-key-file =

# Specify the Access-Control-Allow-Origin to be returned on each request. (eosio::http_plugin)
# access-control-allow-origin =

# Specify the Access-Control-Allow-Headers to be returned on each request. (eosio::http_plugin)
# access-control-allow-headers =

# Specify if Access-Control-Allow-Credentials: true should be returned on each request. (eosio::http_plugin)
access-control-allow-credentials = false

# The actual host:port used to listen for incoming p2p connections. (eosio::net_plugin)
p2p-listen-endpoint = 0.0.0.0:9876

# An externally accessible host:port for identifying this node. Defaults to p2p-listen-endpoint. (eosio::net_plugin)
p2p-peer-address = P2P_PEER_ADDRESS:9876

# The public endpoint of a peer node to connect to. Use multiple p2p-peer-address options as needed to compose a network. (eosio::net_plugin)
p2p-server-address = P2P_SERVER_ADDRESS

# The name supplied to identify this node amongst the peers. (eosio::net_plugin)
agent-name = "AGENT-NAME"

# Can be 'any' or 'producers' or 'specified' or 'none'. If 'specified', peer-key must be specified at least once. If only 'producers', peer-key is not required. 'producers' and 'specified' may be combined. (eosio::net_plugin)
allowed-connection = any

# Optional public key of peer allowed to connect. May be used multiple times. (eosio::net_plugin)
# peer-key =

# Tuple of [PublicKey, WIF private key] (may specify multiple times) (eosio::net_plugin)
# peer-private-key =

# Maximum number of clients from which connections are accepted, use 0 for no limit (eosio::net_plugin)
max-clients = 25

# number of seconds to wait before cleaning up dead connections (eosio::net_plugin)
connection-cleanup-period = 30

# True to require exact match of peer network version. (eosio::net_plugin)
network-version-match = 0

# number of blocks to retrieve in a chunk from any individual peer during synchronization (eosio::net_plugin)
sync-fetch-span = 100

# maximum sizes of transaction or block messages that are sent without first sending a notice (eosio::net_plugin)
max-implicit-request = 1500

# Enable block production, even if the chain is stale. (eosio::producer_plugin)
enable-stale-production = false

# Limits the maximum time (in milliseconds) that is allowed a pushed transaction's code to execute before being considered invalid (eosio::producer_plugin)
max-transaction-time = 30

# Percent of producers (0-100) that must be participating in order to produce blocks (eosio::producer_plugin)
required-participation = 33

# ID of producer controlled by this node (e.g. inita; may specify multiple times) (eosio::producer_plugin)
producer-name = EOSIO

# Tuple of [public key, WIF private key] (may specify multiple times) (eosio::producer_plugin)
private-key = ["PUBLIC-KEY","PRIVATE-KEY"]

# Lag in number of blocks from the head block when selecting the reference block for transactions (-1 means Last Irreversible Block) (eosio::txn_test_gen_plugin)
txn-reference-block-lag = 0

# The path of the wallet files (absolute path or relative to application data dir) (eosio::wallet_plugin)
wallet-dir = "."

# Timeout for unlocked wallet in seconds (default 900 (15 minutes)). Wallets will automatically lock after specified number of seconds of inactivity. Activity is defined as any wallet command e.g. list-wallets. (eosio::wallet_plugin)
unlock-timeout = 900

# eosio key that will be imported automatically when a wallet is created. (eosio::wallet_plugin)
# eosio-key =

# Plugin(s) to enable, may be specified multiple times
# plugin =
plugin = eosio::producer_plugin
plugin = eosio::chain_api_plugin
plugin = eosio::wallet_api_plugin
plugin = eosio::http_plugin
plugin = eosio::history_api_plugin
plugin = eosio::history_plugin

4)运行下面命令生成image并推送到私有的Registry。
docker build . -t registryhost:5000/eos/eos
docker push registryhost:5000/eos/eos

2、EOS节点部署

1)当镜像推送到Registry后,我们就可以进行Yaml文件的编写并且配置了,下面是Yaml文件的内容:
at nodeos.yaml 
apiVersion: v1
kind: Template
metadata:
name: nodeos
annotations:
openshift.io/display-name: nodeos
description: nodeos of eos.
iconClass: icon-wildfly
tags: nodeos

objects:
[list]
[*]apiVersion: v1[/*]
[/list] kind: Service
metadata:
annotations:
description: Exposes and load balances the application pods
name: ${BP}
spec:
ports:
- name: http-8888-tcp
port: 8888
protocol: TCP
targetPort: 8888
- name: admin-9876-tcp
port: 9876
protocol: TCP
targetPort: 9876
selector:
name: ${BP}
type: ClusterIP

[list]
[*]apiVersion: v1[/*]
[/list] kind: Route
metadata:
name: nodeos-http
spec:
host: nodeos
to:
kind: Service
name: ${BP}
port:
targetPort: http-8888-tcp

[list]
[*]apiVersion: v1[/*]
[/list] kind: DeploymentConfig
metadata:
annotations:
description: Defines how to deploy the nodeos
name: ${BP}
spec:
replicas: 1
selector:
name: ${BP}
strategy:
type: Rolling
template:
metadata:
labels:
name: ${BP}
spec:
containers:
- image: "192.168.20.2:5000/eosio/eos"
env:
[list]
[*]name: BP[/*]
[/list] value: ${BP}
[list]
[*]name: PUBLIC_KEY[/*]
[/list] value: ${PUBLIC_KEY}
[list]
[*]name: PRIVATE_KEY[/*]
[/list] value: ${PRIVATE_KEY}
[list]
[*]name: BP_LIST[/*]
[/list] value: ${BP_LIST}

imagePullPolicy: Always
name: nodeos-bp
ports:
- containerPort: 8888
protocol: "TCP"
- containerPort: 9876
protocol: "TCP"
resources:
requests:
memory: 4096Mi
cpu: 2000m
dnsPolicy: ClusterFirst
restartPolicy: Always
securityContext:
- runAsUser: 0

parameters:
[list]
[*]name: BP[/*]
[/list] description: Block Producer
displayName: Block Producer
value: eosio
[list]
[*]name: PUBLIC_KEY[/*]
[/list] description: PUBLIC_KEY.
displayName: PUBLIC_KEY
value: EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
[list]
[*]name: PRIVATE_KEY[/*]
[/list] description: PRIVATE_KEY.
displayName: PRIVATE_KEY
value: 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
[list]
[*]name: BP_LIST[/*]
[/list] description: A IP list of Block Producer
displayName: A IP list of Block Producer
value: bp's hostname and port

2)有了Yaml文件,我们就可以把我们的Yaml文件推送到OpenShift中当成模版,然后通过模版进行EOS节点的实例化,这里面有一个问题需要注意,所有节点的公私钥不可能一样,需要通过模版的参数在实例化的时候传入容器里,然后启动NodeOS的时候,调用NodeOS启动脚本把传入的变量(每一个传入变量都会生成一个环境变量)替换到config.ini文件中,然后启动EOS的主进程NodeOS。当然,这里还有一个问题也需要注意,那就是4.1的EOS配置文件里需要配置两个参数,一个是自己自身的IP(p2p-server-address),和对端IP(p2p-peer-address)第一个地址可以用这个Pod的hostname代替,第二个可以通过指定之前节点的hostname来填充。

3)使用`oc create -f eos.yaml` 进行模版的部署,然后通过Web界面,选择刚才的模版,输入变量的内容,最后点击生成,如下图所示:
eos_template.png

3、既然环境已经设置完成,现在我们来初始化整个EOS的环境。

1)首先通过cleos wallet create创建一个默认的钱包,如果-n可以指定钱包的名字,这个钱包默认的存储路径是/root/eosio-wallet/目录下,看下面的输出:
# cleos wallet create 
"/usr/local/bin/keosd" launched
Creating wallet: default
Save password to use in the future to unlock this wallet.
Without password imported keys will not be retrievable.
"PW5J2SGNDmhZFiVnbZY6V669XLEksGs13KnhCnkuqQZScj5ieNV5j"

其中下面的是这个钱包的密码,需要记住,因为现在4.1版本的钱包900秒后就会自动上锁,需要这个密码来解锁。

2)创建一对公私钥,创建账户,并部署合约。

创建私钥公钥:
# cleos create key 
"/usr/local/bin/keosd" launched Private key: 5Kgh744izqDCNyuRZcBu4nMjgJbBwWBdqQZuQi53qPB9L69Cj9X Public key: EOS6CYWY1q4AAsVV3rzcT1GGQmZcg7yDnA6jZx2KUjHvmZWPv8DQg

创建代币账户:
#cleos -u http://nodeos:80 create account eosio eosio.token EOS6CYWY1q4AAsVV3rzcT1GGQmZcg7yDnA6jZx2KUjHvmZWPv8DQg EOS6CYWY1q4AAsVV3rzcT1GGQmZcg7yDnA6jZx2KUjHvmZWPv8DQg

部署代币合约:
#cleos -u http://nodeos:80  set contract eosio.token  eos/build/contracts/eosio.token/
Reading WAST/WASM from /eos_code/dawn-v4.1.0/eos/build/contracts/eosio.token/eosio.token.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 9ff87dffe3eb2eeac29a4afd8eec8faa563f6be43cf5142e71cd61d0c4b66244 8072 bytes 976 us
# eosio <= eosio::setcode {"account":"eosio.token","vmtype":0,"vmversion":0,"code":"0061736d01000000017f1560037f7e7f0060057f7e...
# eosio <= eosio::setabi {"account":"eosio.token","abi":"0e656f73696f3a3a6162692f312e30010c6163636f756e745f6e616d65046e616d65...
warning: transaction executed locally, but may not be confirmed by the network yet

创建代币:
#cleos -u http://nodeos:80  push action eosio.token create '["eosio","1000000000.0000 SYS",0,0,0]' -p eosio.token
executed transaction: 8d7e472fa7309abfa051eed367c552e3e3e8f283530dc3375761d7da2cb298e9 120 bytes 453 us
# eosio.token <= eosio.token::create {"issuer":"eosio","maximum_supply":"1000000000.0000 SYS"}
[quote]> warning: transaction executed locally, but may not be confirmed by the network yet

发布代币:
#cleos -u http://nodeos:80 push action eosio.token issue '["eosio","1000000000.0000 SYS","issue"]' -p eosio
cleos -u http://nodeos:80 push action eosio.token issue '["eosio","1000000000.0000 SYS","issue"]' -p eosio
executed transaction: bdf099816554de5849f030a1d26cc40ade29b74c87aff4180796c7c26507d677 128 bytes 343 us
# eosio.token <= eosio.token::issue {"to":"eosio","quantity":"1000000000.0000 SYS","memo":"issue"}
warning: transaction executed locally, but may not be confirmed by the network yet

查询代币余额:
#cleos -u http://nodeos:80 get currency balance eosio.token eosio
1000000000.0000 SYS

创建账号:
#cleos -u http://nodeos:80  create account eosio alex EOS6CYWY1q4AAsVV3rzcT1GGQmZcg7yDnA6jZx2KUjHvmZWPv8DQg EOS6CYWY1q4AAsVV3rzcT1GGQmZcg7yDnA6jZx2KUjHvmZWPv8DQg

EOS转账 :
#cleos -u http://nodeos:80  push action eosio.token transfer '["eosio", "alex", "10000.0000 SYS", ""]' -p eosio
[/quote]

3)创建BP,所谓BP就是EOS上的出块节点(block producer),到6月2日 EOS主网上线,全网应该一共21个BP节点,每个节点都要经过EOS的投票选举产生,现在我们的环境里还没有一个被选举的BP节点,所以需要通过抵押EOS的方式进行投票,当投票超过150M个EOS的时候,得票最多的前21个节点就是BP节点了,当然我们的BaaS受部署规模限制,不一样定要到21个节点,所以我们选举4个BP节点,执行如下命令:
部署系统合约
# cleos -u http://nodeos:80 set contract eosio eos/build/contracts/eosio.system
Reading WAST/WASM from ./build/contracts/eosio.system/eosio.system.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: cd1975d3628c1306f1fb58e49d5474d61c829bad42bffb4aeca7488bb041bd4d 40488 bytes 3969 us
# eosio <= eosio::setcode {"account":"eosio","vmtype":0,"vmversion":0,"code":"0061736d0100000001ba022f60027f7e0060067f7e7e7f7f...
# eosio <= eosio::setabi {"account":"eosio","abi":"0e656f73696f3a3a6162692f312e30050c6163636f756e745f6e616d65046e616d650f7065...
[quote]> warning: transaction executed locally, but may not be confirmed by the network yet

创建账户bp1作为出块节点:
cleos -u http://nodeos:80 system newaccount eosio bp1 EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH --stake-net '500.00 SYS' --stake-cpu '500.00 SYS'
1697946ms thread-0 main.cpp:419 create_action ] result: {"binargs":"0000000000ea3055000000000000423d00200000"} arg: {"code":"eosio","action":"buyrambytes","args":{"payer":"eosio","receiver":"bp1","bytes":8192}}
1697948ms thread-0 main.cpp:419 create_action ] result: {"binargs":"0000000000ea3055000000000000423d404b4c00000000000453595300000000404b4c0000000000045359530000000000"} arg: {"code":"eosio","action":"delegatebw","args":{"from":"eosio","receiver":"bp1","stake_net_quantity":"500.0000 SYS","stake_cpu_quantity":"500.0000 SYS","transfer":false}}
executed transaction: bbdeb7aa13953746e93c47b53729ce36ca2d89f16d87b872ebbacd71c2f4fc6b 336 bytes 1831 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"bp1","owner":{"threshold":1,"keys":[{"key":"EOS7FNpU6P3yhT7bkf3fs7aNUxb3y...
>>
# eosio <= eosio::buyrambytes {"payer":"eosio","receiver":"bp1","bytes":8192}
>>
# eosio <= eosio::delegatebw {"from":"eosio","receiver":"bp1","stake_net_quantity":"500.0000 SYS","stake_cpu_quantity":"500.0000 ...
>> warning: transaction executed locally, but may not be confirmed by the network yet

导入刚才bp1的私钥:
# cleos -u http://nodeos:80 wallet import 5Je4PsFWinBPXgWQasz7usbv8MLu5AbkcjU41JQr4Suhh34yKPu
imported private key for: EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH

注册bp1为bp节点:
# cleos -u http://nodeos:80 system regproducer bp1 EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH http://bp1:8888
2155333ms thread-0 main.cpp:419 create_action ] result: {"binargs":"000000000000423d000336d1c7309f8c40f9a2d7bf11b6ba3f6b1c699a67d2ad26c003e5313ff1ce1c940f687474703a2f2f6270313a383838380000"} arg: {"code":"eosio","action":"regproducer","args":{"producer":"bp1","producer_key":"EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH","url":"http://bp1:8888","location":0}}
executed transaction: 294568630bd6c733db853ba2aac2245ffeb199d47767b80ddadc1cbf421c6d46 152 bytes 623 us
# eosio <= eosio::regproducer {"producer":"bp1","producer_key":"EOS7FNpU6P3yhT7bkf3fs7aNUxb3yNbMXPbn4nsYgh3ZkEhtchEAH","url":"http...
>> warning: transaction executed locally, but may not be confirmed by the network yet

eosio抵押EOS,投票给bp1:
#cleos -u http://nodeos:80 system delegatebw bp1 bp1 '100000000.0000 SYS' '50000000.0000 SYS' --transfer[/quote]


460701ms thread-0 main.cpp:1083 operator() ] act_payload: {"from":"eosio","receiver":"bp1","stake_net_quantity":"100000000.0000 SYS","stake_cpu_quantity":"50000000.0000 SYS","transfer":true}
2460702ms thread-0 main.cpp:419 create_action ] result: {"binargs":"0000000000ea3055000000000000423d0010a5d4e800000004535953000000000088526a74000000045359530000000001"} arg: {"code":"eosio","action":"delegatebw","args":{"from":"eosio","receiver":"bp1","stake_net_quantity":"100000000.0000 SYS","stake_cpu_quantity":"50000000.0000 SYS","transfer":true}}
executed transaction: 98314189243cf8a38619e9f887c5430d9285f19a6a2977eccc07d838611dd17a 144 bytes 676 us
# eosio <= eosio::delegatebw {"from":"eosio","receiver":"bp1","stake_net_quantity":"100000000.0000 SYS","stake_cpu_quantity":"500...
[quote]> warning: transaction executed locally, but may not be confirmed by the network yet

用抵押的EOS投票给bp1:
#cleos -u http://nodeos:80 system voteproducer prods bp1 bp1
2531150ms thread-0 main.cpp:419 create_action ] result: {"binargs":"000000000000423d000000000000000001000000000000423d"} arg: {"code":"eosio","action":"voteproducer","args":{"voter":"bp1","proxy":"","producers":["bp1"]}}
executed transaction: 15c7cd6bd19f41654abb2d4f52f23953f80fd76b2698ef2a713d67c6fa022ee7 120 bytes 905 us
# eosio <= eosio::voteproducer {"voter":"bp1","proxy":"","producers":["bp1"]}
>> warning: transaction executed locally, but may not be confirmed by the network yet

到这一步一个最最基本的EOS BaaS就算部署完毕了。[/quote]

此时你就可以在你的终端上看到类似如下输出:
3302000ms thread-0   producer_plugin.cpp:585       block_production_loo ] Produced block 00000d279a442987... #3367 @ 2018-05-22T15:55:02.000 signed by bp1 [trxs: 0, lib: 3366, confirmed: 0]

hello-openshift origin

mgnetwork 发表了文章 • 0 个评论 • 1281 次浏览 • 2018-03-15 21:27 • 来自相关话题

环境 : 三台centos 7.4 mast 4G 内存 4 VCPU 200.com node 2G 内存 2vcpu 201.com 202.com systemc ...查看全部
环境 :
三台centos 7.4
mast 4G 内存 4 VCPU 200.com
node 2G 内存 2vcpu 201.com 202.com

systemctl disable firewalld
systemctl stop firewalld
环境1.png


环境2.png


环境3.png


安装ansible 、ssh互信、配置ansible hosts 、etcd安装

yum install -y ansible
ssh互信 (三台centos一样)
ssh-keygen -t srt
ssh-copy-id 200.com
ssh-copy-id 201.com
ssh-copy-id 202.com
etcd安装
yum install -y etcd
systemctl enable etcd.service
systemctl start etcd.service
我这里是只有一台etcd,多台请用源码安装


配置ansible hosts
vim /etc/ansible/hosts
[abcd]
# 这个我一做测试的
200.com
201.com
202.com

[OSEv3:children]
masters
nodes
etcd

[OSEv3:vars]
ansible_ssh_user=root
openshift_deployment_type=origin
openshift_disable_check=disk_availability,docker_storage,memory_availability,docker_image_availability,package_version
#disable-check这里是不检查项目,mast要求默认16G内存、node8G内存,现在docker要1.13版本,我#这里把检测关了,所以还是用1.12.6版本

[masters]
200.com

[nodes]
200.com openshift_schedulable=True openshift_node_labels="{'region': 'infra'}"
201.com openshift_node_labels="{'region': 'infra', 'zone': 'east'}"
202.com openshift_node_labels="{'region': 'infra', 'zone': 'west'}"

[etcd]
# etcd主机
200.com
保存退出

测试ansible

测试1.png


我使用的openshift-ansible-3.7.0-0.198.1.tar.gz 这个包 ,可以在github 可以找到
tar -zxvf openshift-ansible-3.7.0-0.198.1.tar.gz

运行安装脚本
ansible-playbook /abcd/openshift-ansible-openshift-ansible-3.7.0-0.198.1/playbooks/byo/config.yml

这里会自动安装,如果有问题会 飘红 (我安装时没有问题,有问题也是 检测问题,如果是可以在ansible hosts 里把那个关了),必须连网哦。

成功后 查看一下集群

成功1.png


https://200.com:8443
前提 能正常解析到200.com这个地址。不能就用ip或者在windos hosts文件里加一条

web登录 用户名密码 dev

创建一个项目



项目名.png



项目名2.png



项目3.png





项目4.png



这里我们可以看到 运行详细信息 如地址和端口

直接在某台宿主机上访问


成功1.png



Hello OpenShift!












DockOne微信分享(一五七):一个可供参考的企业应用容器化实践案例

alex_wang2 发表了文章 • 2 个评论 • 2978 次浏览 • 2018-01-10 21:37 • 来自相关话题

【编者的话】本次分享分为两个部分,第一部分比较常规,介绍如何用OpenShift搭建自动化测试、开发环境。第二部分介绍了在容器使用过程中遇到的问题,以及应对方案。作者在原有的OpenShift Router仅支持7层协议的基础上,对其进行支持4层协议的扩充,联 ...查看全部
【编者的话】本次分享分为两个部分,第一部分比较常规,介绍如何用OpenShift搭建自动化测试、开发环境。第二部分介绍了在容器使用过程中遇到的问题,以及应对方案。作者在原有的OpenShift Router仅支持7层协议的基础上,对其进行支持4层协议的扩充,联合CoreDNS,让平台的使用者不用记录灵活多变的IP地址,仅需自己定义自己熟悉的hostname(或者由平台自动生成)就访问对应资源,让使用者几乎没有感知的使用容器还是物理机或者虚拟机。

在这里跟大家分享一下容器使用方面的经验,说的不好,肯定有不对的地方,还希望大家多多批评、指正。
#OpenShift简介
我们都知道在容器编排领域,有3个最著名的编排系统,它们分别是Docker Swarm,Mesos和Kubernetes。其中Docker Swarm是Docker公司自己推出的容器管理和编排系统,由于起步较晚,目前大规模的应用尚不多见。

Mesos是Apache下的开源分布式资源管理框架,它被称为是分布式系统的内核。Mesos最初是由加州大学伯克利分校的AMPLab开发的,后在Twitter得到广泛使用。

Kubernetes和Mesos同样是起源于Google的Borg,是现在应用最广,用户数和社区活跃度最高的容器编排系统。OpenShift也是出于这个考虑,最终选择Kubernetes作为底层架构。OpenShift是一个开源容器云平台,他是基于主流的容器技术Docker和Kubernetes构建的云平台,作为一个开源项目,他已经有5年的历史,其最早的定位是一个应用云平台(Platform as a Service)。在Docker时代来临之前,各个厂商和社区项目倾向构建自己的容器标准,比如Cloud Foundry的Warden、OpenShift的Gear,但是在Docker成为主流及社区的技术发展方向之后,OpenShift快速的拥抱了Docker,并推出了市场上第一个基于Docker及Kubernetes的容器PaaS解决方案。当然很多人会有疑问,OpenShift与Docker及Kubernetes的关系究竟是什么

OpenShift是基于容器技术构建的一个云平台。这里所指的容器技术即包含Docker及Kubernetes。如下图所示:
93045234accd1ea8fcaa59f8776f326f.png

OpenShift底层以Docker作为容器引擎驱动,以Kubernetes作为容器编排引擎组件。OpenShift提供了开发语言、中间件、自动化流程工具及界面等元素,提供了一套完整的基于容器的应用云平台。OpenShift有三个版本,分别是企业版,社区版和在线版,我们这里使用社区版(origin版)用做我们企业内部的部署
#部署介绍
我们的架构比较典型,前端有2个负载均衡器(HAProxy),两个负载均衡通过Heartbeat共享一个VIP,后面连接3个master node,每个master node上分别运行了一个etcd,后面连接了N个slave node,因为集群中所有的状态都会持久化到etcd中,所以api server基本上是无状态运行,当有节点需要和master node打交道时,先访问这个VIP,然后连接到vip后面的一个haproxy上,haproxy再选择其中一个master node上的api server进行与etcd通信,下面是我的HAProxy的配置文件`haproxy.conf`:
backend atomic-openshift-api
balance source
mode tcp
server master0 10.10.xx.xx:8443 check
server master1 10.10.xx.xx:8443 check
server master2 10.10.xx.xx:8443 check

同时,其实我们如果集群里面有其他的高可用的需求,比如我们使用了Harbor做为私有镜像仓库,三个镜像仓库配置了Replication规则,我们通过VIP推送一个镜像到3个中的任意一个仓库,其他两个仓库也会存在我们的同样镜像,从而实现高可用。下面是Haproxy的配:
`harpoxy.conf`

backend harbor-proxy
balance source
mode tcp
server harbor1 10.10.xx.xx:80 check
server harbor2 10.10.xx.xx:80 check
server harbor3 10.10.xx.xx:80 check

#运行流程
第一个是每天定时的作业,这个比较简单,我们快速过一下:首先在Jenkins里做定时任务,时间定为0:30,从代码下载代码,在编译之前进行代码风格检查(snoar)和执行单元测试(junit),然后进行编译,把编译出的二进制包和事先写好的`Dockerfile`进行docker build,然后把输出的docker image push到Harbor的镜像仓库,同时这个编译的jenkins job会触发,测试部署job,测试部署job执行时会触发OpenShift里的test project,我们把PullPolicy设置成alway是,每次执行从新部署,都会触发新的镜像的拉去,从而达到从新部署的目的,测试项目分为两个阶段,自动化测试和压力测试,自动化测试采用selenume和robot结合的方式,在测试结束之后生成测试报告。压力测试使用jmeter。结束之后也会以邮件的方式发送给订阅者。如果所有测试通过之后,测试job会先打给这个image打一个tag,然后push到Harbor的ready项目中。同时发送通知邮件。

待第二天测试人员需要测试的时候,它们会通过我们的release manager去拉去ready项目中最新的image,然后部署到它们自己的project里,进行特定功能的手工测试。整体流程大概如此。

第二个是针对开发测试环境的,由于资源有限,我们的开发人员也用容器的方式部署每个人的开发环境,这里OpenShift/Kubernetes多租户的优势就体现出来了。在我们的容器平台上,每一个开发人员对应一个账号,每一个账号就是我们OpenShift里的一个project,由于每一个project都可以做资源限制,所以只要给大家按照需求分配好固定份额,就可以做到资源的共享和互不干涉。举个例子,比如说,我们有一个项目,它分为前端和后端。前端人员开发的时候,他只关心前端代码,后端代码基本仅仅是调用而不做修改,那么这个时候我们就可以把前端打成一个docker image,后端打成一个docker image(这里是举个例子,实际情况可能比较复杂,可能前后端都不止一个image),然后在前端开发人员写完前端代码后,把自己的代码通过NFS共享给Release Manager,(NFS挂在可以是实现挂载好的,Windows、Linux、MacOS均可以,代码就可以直接存在上面),然后点击Relea se Manager上面的前端发布按钮,就会触发Jenkins中的一个front-end job,它会完成代码编译,打包和镜像推送,最后在这个开发人员对应的project里从新部署前端代码的整个过程,接着这个开发人员就可以通过url(比如http://test/frontend-dev1)去访问它刚刚部署的页面。如果是后端人员也会有同样的流程,具体就不在赘述了。具体说明一点,由于OpenShift集成了Router功能,也就是类似于Kubernetes里的Ingress,但是它是用Haproxy实现的,并且能够在Yaml文件中对Router进行配置,刚才引用的url我们可以配置成http://{集群名}/{项目名},所有整个集群都是用7层协议通过Router对外提供访问。

生产和预发布环境,这个环境的配置除了要求高可用之外,就是环境的硬件配置,一般意义上来将,预发布的环境因该不高于正式生产环境。当决定对测试通过的镜像版本进行上线时,首先会用docker tag把它换成tag换成pre-release,经过压力测试和手工最后的verify就可以发布到正式环境中了。
#调试问题的解决
下面我们着重讲一下在实际部署和使用容器过程中,遇到了哪些问题,以及是如何去解决:

1、有一个问题也许很多刚刚使用容器的公司都会遇到,就是开发人员喜欢把容器当成虚拟机用,在遇到程序bug的时候,很多开发者都喜欢SSH到容器里,亲自看看log,或者是尝试替换一下它们程序的debug版(这里的所谓debug版,就是开发人员在代码里加入一些调试信息或者print一下log),然后重新启动应用。这里我们不推荐给容器内部安装SSHD,因为首先容器的IP是临时分配的,我们无法确定的告诉开发者它这一次的IP地址是多少,即使告诉了他也不一定能够访问的到(我们的容器系统网络层和外界不是一个网段),那么如何解决这个问题呢?我们开始也是尽量说服开发者,学会用log去debug,因为我们前面已经把log通过ES进行了收集,用Kabina可以去查看,但是没有办法,有些开发人员还是习惯自己去cat或者vim打开日志文件。所以这里我们就用到了OpenShift里提供的一个oc子命令oc get pod和oc exec,前者用来得到当前用户所在的项目中的Pod列表,后在类似docker exec命令可以直接跳进容器里(Kubernetes中也提供类似的命令),当开发人员需要把里面的日志文件拷贝出来,后者是拷贝一个debug版本的程序到容器里去运行时,可以用oc cp(同样这个在Kubernetes里也有类似的命令)。虽然这几个命令据我观察有一些bug,比如拷贝的目标目录不太准确,而且对容器里的tar命令有一些依赖。但这都是小问题,不影响使用,如果觉得几条命令结合起来使用有些麻烦,可以自己用Python脚本进行一个简单的封装,这样当开发人员使用的时候会更加简单。

2、每一个系统几乎都或多或少的使用了一些第三方工具,比如MySQL、MyCAT、Redis、ZooKeeper,这些组件我们都把它们进行了容器化,以便实现资源的整合和方便部署。那么这就引发了另一个问题,开发人员在碰到程序bug的时候,往往需要直接去连接这些第三方组件,去改修改和查看里面的信息。比如他需要查看Redis的键值是否存在,查看数据是否写入到了数据库里。这个时候,由于所有组件在容器中,你不知道它的准确IP,你可能很容易想到用OpenShift提供的router功能去像刚才的url那样提供外界的访问,但是由于这些中间件是4层的协议,而现有OpenShift的Router功能仅仅支持7层协议,所以我们为了解决这个问题就必须实现OpenShift的4层代理功能。通过修改Openshift的源代码haproxy-templte.conf和router部分的相关代码,然后通过yaml route的annotation段,定义一个规则,把对应的端口传进router的配置文件,让后端的第三方应用程序通过router节点对应的端口(Haproxy里的mode tcp)从而实现router代理4层协议的目的,但是这会导致另一个问题,因为router的每一个端口只能映射给一个后台应用,比如3306端口,只能映射给一个MySQL,如果有两个开发人员都要调试MySQL,那第二个开发者的MySQL的映射端口肯定就得用除了3306以外的端口(比如3307、第三个人3308等)那么就会产生一个问题router的映射端口如何告诉开发人员呢?这个问题有两个解决办法,第一个是通过一个Web UI,去显示的告诉开发人员他所有的资源对应的router节点的端口号,但是这有一个不方便的地方,如果的资源对应的Pod被重置了,那么他的端口号也就会被改变,即使端口号不改变,记起来也比较麻烦(大型的项目可能要用到5、6个中间件产品,每一个开发人员都要记自己的那套环境的端口号,还要学会如何用自己的工具去修改默认的端口号显然给开发者添加了许多不必要的麻烦。那么基于此问题的考虑,我们使用了第二种方法,即我们内部实现了一个DNS,开发者每个人的资源的IP都可以用DNS查到,比如developer1,他有Redis、MySQL、MyCAT、ZooKeeper、PostgreSQL等资源,那他的这些资源对应的DNS域名为:developer1.redis developer1.mysql developer1.mycat developer1.zookeeper等,但是由于DNS只能返回IP地址,无法返回端口号,我们还是得不到router节点对应资源的端口号。为了解决这个问题,我们可以用Haproxy加别名的方式,比如运行命令ifconfig eth0:1 10.10.xx.xx意思就是给eth0的NIC,加上了一个虚拟的IP10.10.xx.xx,那么此时,这个网卡eth0就有两2个IP,并且此时都能ping通。(当然实际实现的时候,我们不可能用是ifconfig命令完成这个网卡别名的方式,那样太low,也太不可靠了,我查看了ifconfig的源代码,把其中设置IP地址的代码集成到了我们修改过的OpenShift里)。然后router的haproxy.conf做bind的时候就需要指定这个IP,端口号还用原来这个应用默认的端口号如下图所示,这样开发人员不用每次都记住不同的端口号,仅仅配置一个DNS,不管环境发生了什么样的改变,都可以用默认端口和hostname去连接自己的资源进行调试。

3、配置Docker容器的时候,默认使用的是DeviceMapper方式,然而这种方式有众多的限制,可以参考https://docs.openshift.org/3.6/install_config/install/host_preparation.html#configuring-docker-storage中的详细配置说明,在生产环境中我们采用的是第2种方式。

#Q&A
Q:所有开发人员都是用一套OpenShift集群测试吗?CI/CD也是同一套环境吗?

A:我们是按业务分的,原则上,一套业务线(一个业务部门)用一套系统,这样成本上也好分摊。



Q:OpenShift也是用Go编写的?

A:是的,OpenShift用的是源码级别的和Kubernetes的集成,不是通过client-go或者REST API的方式,所以我们可以看到,Kubernetes发型的版本总是比OpenShift的快1 到2个版本。



Q:对于OpenShift比较适合多大规模的团队?

A:这个怎么说呢,其实引入DevOps或者CI/CD的流程就是为了给企业减少人员成本,让有些能够自动化的东西通过计算机运行起来。所以因该是人员越少越好,但是人员如果少,就要求每个人的技术能里比较强,开源的东西往往用起来不难,但是真到出了问题的时候就需要看代码去解决了。所以如果人少的话,可能每个人就要求懂得就比较多一些。



Q:router本身是否具备HAProxy?

A:OpenShift的Router就是用HAProxy实现的,当然我现在用的是3.6.1的版本,我不知道以后会不会支持Nginx或者其他别的LB,因为我看到代码里已经有关于Nginx的相关配置了,但是没有激活。OpenShift使用HAProxy的大致流程就是通过一个Yaml或者jason文件,配置一条route信息,然后通过api-server持久化到etcd中,router的代码启动一个goroutine,去通过api-server watch etcd,然后根据配置信息和环境变量,通过haproxy-template模版,去生成 haproxy.conf,然后去动态reload。



Q:OpenShift的project和Kubernetes的namespace是一对一的关系么?project可以设置资源配额么?怎么设的?

A:是一对一关系,当然有一些namespace 是有一些特殊意义的,不建议在里面跑应用。project可以设置资源配额,具体怎么设置就比较复杂了,建议参考一下官方文档,简单的说就是可以根据CPU内存做资源的限定,这个和Kubernetes是一样的。



Q:OpenShift中原生性能指标监控服务的Pod总挂有没有相应的解决办法?

A:解决Pod总挂的问题就得具体问题具体分析了,我记得它做性能监控的那个Pod比较吃资源,其实可以对他进行一下限定,比如:oc env rc hawkular-cassandra-1 MAX_HEAP_SIZE=1024M -n openshift-infra。



Q:OpenShift中的router默认情况下是跑在Pod里的,那么当service特别多,route规则也特别多的时候,如何解决router服务的性能问题的?

A:这是一个好问题,但其实我觉得这个和HAProxy有很大的关系,跟在不在Pod中关系不大,因为router这个容器其实用的是主机网络,那么这个问题其实就转化成了如何提升HAProxy的性能,这种情况其实有很多可以参考的方案,比如前面在加一层LVS负载,或者用DNS做域名解析的时候进行一定的负载功能。



以上内容根据2018年1月9日晚微信群分享内容整理。 分享人王晓轩,某传统互联网公司基础技术部技术经理,关注容器技术及DevOps PaaS领域,2014年关注容器技术,深度接触过Cloud Foundry等开源PaaS产品, 现主要研究OpenShift及周边技术。 DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesa,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

如何用S2I创建一个builder image

alex_wang2 发表了文章 • 0 个评论 • 5258 次浏览 • 2017-12-08 00:25 • 来自相关话题

【编者的话】众所周知,在容器编排领域,Kubernetes的市场占有率遥遥领先。但编者认为Kubernetes是面向运维人员的分布式系统,所以Redhat公司在Kubernetes的基础上推出了OpenShift,除了支持Kubernetes原有的所有功能外, ...查看全部
【编者的话】众所周知,在容器编排领域,Kubernetes的市场占有率遥遥领先。但编者认为Kubernetes是面向运维人员的分布式系统,所以Redhat公司在Kubernetes的基础上推出了OpenShift,除了支持Kubernetes原有的所有功能外,针对Paas平台的特点,对Kubernetes进行了深度开发。文中介绍的S2I就是OpenShift之所以区别于Kubernetets,能成为PaaS平台的实现中一个主要环节。通过选择对应的image,编写assmble和run的脚本,最终生成包含依靠源代码生成的可以执行文件的docker image,并利用OpenShift现有的体系架构(BC和DC)直接发布,完成所有的应用的生命周期管理,让开发者真正仅仅关注与代码本身,这才是一个真正的Paas平台需要实现的。
openshift.png

Source-To-Image(S2I)是一个在创建builder image时可以独立运行的常用的工具。在OpenShiftV3里,它还是一个创建应用程序的主要手段。这是因为它具备如下优势:

- 速度快 —— 用S2I在应用程序编译的过程中可以在不创建新的layer的情况下执行大量的复杂操作,导致编译速度加快。
- 可以打补丁 —— 比如当你发现你的image存在一个安全问题时,S2I可以帮助你持续不断的rebuild你的应用程序。
- 用户效率高 —— S2I防止开发人员随意的用yum对image进行安装操作,因为这种操作会拖慢开发迭代的效率。
- 生态性 —— S2I鼓励共享一个生态系统的image,你可以利用它去运行你的应用程序。

这篇文档就是关于如何去创建一个简单的S2I的builder image。

总的来说,S2I利用源代码和builder Docker image去生成一个新的Docker image。S2I这个项目本身其实已经包含了一些常用的Docker builder image,比如用来创建PythonRuby的builder image,当然你也可以根据需要去扩展它。

在讨论如何去创建一个builder image之前,我们先来解释一下S2I是如何工作的以及build image过程中builder image的角色。S2I(Source-To-Image),从名字上可以知道,它其实是负责把你应用程序源代码转换成可以在OpenShiftV3或者用`docker run`运行的Docker Image。所以这个Builder image包含了一些能够生成可执行镜像的所需的能力(也成为:build artifact)。整个的build过程包含对生成的image的调用,分几个步骤完成。在这边文章里我们用一个最简单的流程说明:

1. 下载S2I脚本(或者用builder image 里面的)
2. 下载你的应用程序代码
3. S2I stream脚本们和应用程序代码到builder image的容器里
4. 在Build image里执行定于好的编译脚本(这个脚本就是上一步stream进去的)
5. 保存这个容器到一个新的image

当然,为了让builder image完成所有这些工作,我们还需给它一些传入一些content。首先,因为builder image 负责实际意义上的build 你的源代码,所以它必须包含build和运行这个应用程序所依赖的libray和tools。比如,一个tomcat的builder image需要包含Tomcat,JDK和Maven。一个Python builder image包含Gunicorn或者cherry.py,SciPy的二进制包和pip。其次,它还需要包含一些执行build和run操作的逻辑脚本。这部分被下面两个S2I的脚本调用:

  • `assemble`——负责build一个应用程序
  • `run`——负责运行一个应用程序

下面的我将介绍如何利用lighttpd server的docker image构造一个builder image去运行一个静态的html页面。

当然,这其中还包括了用GitHub存储创建过程中产生的文件。

现在,我们开始我的旅程,首先从https://github.com/openshift/source-to-image/releases/ 获取最近版本的S2I(作者在写此篇文章时的最新版是1.0.9,译者翻译时是1.1.8)
#步骤 1
S2I 是一个独立的命令,能够创建一个builder image所需要的目录结构,从这里可以得到更多关于它的信息,现在我们用下面命令创建出所必须的结构:
s2i create lighttpd-centos7 s2i-lighttpd

我调用了`s2i create`命令传入了我想要的builder image的名字(`lighttpd-centos7`)它的目录结构也已经被创建完成(`s2i-lighttpd`)。如果这个目录不存在,这个命令就会为你创建它。所以,运行上面那个命令你将得到下面这个目录结构:
s2i-lighttpd/
├── Dockerfile
├── Makefile
├── README.md
├── s2i
│ └── bin
│ ├── assemble
│ ├── run
│ ├── save-artifacts
│ └── usage
└── test
├── run
└── test-app
└── index.html


#步骤 2
为了定制builder image,我们需要修改那个刚刚用S2I命令生成的`Dockerfile`。
# We are basing our builder image on openshift base-centos7 image
FROM openshift/base-centos7

# Inform users who's the maintainer of this builder image
MAINTAINER Maciej Szulik

# Inform about software versions being used inside the builder
ENV LIGHTTPD_VERSION=1.4.35

# Set labels used in OpenShift to describe the builder images
LABEL io.k8s.description="Platform for serving static HTML files" \
io.k8s.display-name="Lighttpd 1.4.35" \
io.openshift.expose-services="8080:http" \
io.openshift.tags="builder,html,lighttpd"

# Install the required software, namely Lighttpd and
RUN yum install -y lighttpd && \
# clean yum cache files, as they are not needed and will only make the image bigger in the end
yum clean all -y

# Defines the location of the S2I
# Although this is defined in openshift/base-centos7 image it's repeated here
# to make it clear why the following COPY operation is happening
LABEL io.openshift.s2i.scripts-url=image:///usr/local/s2i
# Copy the S2I scripts from ./.s2i/bin/ to /usr/local/s2i when making the builder image
COPY ./.s2i/bin/ /usr/local/s2i

# Copy the lighttpd configuration file
COPY ./etc/ /opt/app-root/etc

# Drop the root user and make the content of /opt/app-root owned by user 1001
RUN chown -R 1001:1001 /opt/app-root

# Set the default user for the image, the user itself was created in the base image
USER 1001

# Specify the ports the final image will expose
EXPOSE 8080

# Set the default CMD to print the usage of the image, if somebody does docker run
CMD ["usage"]

#步骤 3
一旦`Dockerfile`修改完成,我们就可以填充builder image的剩余部分,也就是S2I的那些脚本。首先我们从`assemble`脚本开始, 它是负责build我们的应用。在这个例子里,它仅仅负责拷贝源代码到Lighttpd server的目录下:
#!/bin/bash -e
#
# S2I assemble script for the 'lighttpd-centos7' image.
# The 'assemble' script builds your application source ready to run.
#
# For more information refer to the documentation:
# https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md
#

echo "---> Installing application source"
cp -Rf /tmp/src/. ./

默认情况下,`s2i build`命令将源代码放在`/tmp/src`目录下,在build过程中这个目录下的源代码文件和其他相关文件会被使用。当然你也可以通过修改`io.openshift.s2i.destination`这个label或者传入`--destination`这个参数去修改它,如果是那样的话源代码会被放在你指定的目录下的`src`子目录下。在相面cp命令中的第二个参数`./`是用来设置在`openshift/base-centos7`中的工作目录,也就是`/opt/app-root/src`。

好,现在让我去处理第二个脚本文件`run`,故名思义它负责运行我们的应用程序。在我们的例子中,它会启动lighttd server:
#!/bin/bash -e
#
# S2I run script for the 'lighttpd-centos7' image.
# The run script executes the server that runs your application.
#
# For more information see the documentation:
# https://github.com/openshift/source-to-image/blob/master/docs/builder_image.md
#

exec lighttpd -D -f /opt/app-root/etc/lighttpd.conf

在上面的命令中我们用`exec`去`run`lighttpd的进程。这是为了所有由Docker发送给lighttpd的信号都会输出到`stdout`和`stderr`。

因为在我们的例子中,我们不关注增量build,所以我们可以去掉`save-artifacts`这个脚本。最后我们写一些关于如何使用这个build image的方法在`usage`脚本中:
#!/bin/bash -e

cat < This is the lighttpd-centos7 S2I image:
To use it, install S2I: https://github.com/openshift/source-to-image

Sample invocation:

s2i build https://github.com/soltysh/sti-lighttpd.git --context-dir=test/test-app/ lighttpd-centos7 sample-app

You can then run the resulting image via:
docker run -p 8080:8080 sample-app
EOF

注意:修改这个脚本的执行权限,推荐为`755`。
#步骤 4
我们builder image还剩最后的一部分,一个关于lighttpd的配置文件。这个配置文件会被`Dockerfile`和`run`脚本去使用。如果没有这个配置文件,lighttpd恐怕不能够正常启动。我用下面的配置为lighttpd生成配置文件,将其保存在`s2i-lighttpd/etc/lighttpd.conf`。
# directory where the documents will be served from
server.document-root = "/opt/app-root/src"

# port the server listens on
server.port = 8080

# default file if none is provided in the URL
index-file.names = ( "index.html" )

# configure specific mimetypes, otherwise application/octet-stream will be used for every file
mimetype.assign = (
".html" => "text/html",
".txt" => "text/plain",
".jpg" => "image/jpeg",
".png" => "image/png"
)

这是一个关于lighttpd的最简单配置文件,它仅仅设置了:

* lighttpd处理的源代码路径(`/opt/app-root/src`)
* lighttpd监听的端口号(`8080`)
* 默认文件的路径(`index.html`)
* mimetype影射属性

到这步,我们的builder image已经做完了。我们能够确定的是`s2i-lighttpd`目录下的`make`命令会在适当的时候去build它,当然你已经发现了这是因为里面有一个`Makefile`,里面调用了`docker build`命令。
#步骤 5
现在我们用一个简单的应用程序去测试这个builder image。我创建了一个`index.html`在`s2i-lighttpd/test/test-app`目录下:(译者注:在现在的1.18版本中,这个测试文件会被自动创建)



test-app


Hello from lighttpd served index.html!



有了这个文件,我们现在可以运行s2i build了。在`s2i-lighttpd`目录下执行下面的命令:
s2i build test/test-app/ lighttpd-centos7 sample-app

我们从`test/test-app`目录下创建了一个应用程序,用了我们刚刚的build image(`lighttpd-centos7`)。生成了一个叫`sample-app`的image。默认的S2I build 会打印出所有的由`assemble`产生的输出,所以你会看到下面的输出在你的终端上(你可以忽略关于用本地builder image和非git目录产生的warning):
---> Installing application source

现在我们实际的测试一下生成的image。运行这个image,暴露容器的8080端口到主机上:
docker run -p 8080:8080 sample-app

现在你可以通过http://localhost:8080/访问到`index.html`的内容了。
#步骤 6
这里面还有一些需要补充的部分:tests,大多数情况下可以用`s2i-lighttpd/test/run`这个脚本去处理,假设你在`Dockerfile`里选择默认的端口。在这个情况下,你可以在`s2i-lighttpd`目录下运行下面这个命令:
make test

注意:设置test/run的执行权限,建议设置成700。

这个脚本会运行`s2i build`(请却保`s2i`在你的环境变量path里)用`test-app`作为应用程序源代码,并且执行一些测试以确保image的可用性。这些测试包括:

* 检查`s2i build`运行没有出错
* 检查`usage`脚本运行正确
* 测试最后生成的image
* 检查运行应用程序的容器有正确的响应

在这个例子里我没有增加对`test/run`的说明,因为那个文件太长了。感兴趣的读者可以去看一下https://github.com/soltysh/sti-lighttpd/。提交的历史可以准确地显示本文中给出的每一个步骤。

现在祝贺你,已经有了一个可以使用的S2I builder image,使用S2I(Source Build strategy in OpenShift),处理一个在git代码仓库里的html文件,并且生成一个新的image去对外提供访问。总的来说,通过修改`assemble`和`run`这两个脚本可以很容易的定义其他类型的builder image,然后用相应的image去运行对应类型的应用程序。

原文连接:How to Create an S2I Builder Image (翻译:王晓轩)

DockOne微信分享(一四二):容器云在万达的落地经验

qianggezhishen 发表了文章 • 3 个评论 • 7635 次浏览 • 2017-09-22 11:39 • 来自相关话题

【编者的话】容器生态是现在非常火热的技术生态之一,个人认为它主要囊括着四个方面的技术栈:一是容器核心技术栈(包括 Docker、rkt 及第三方公司自主研发的容器 Engine 等);二是容器基础技术栈(包括容器网络、存储、安全及服务发现等);三是容器编排技术 ...查看全部
【编者的话】容器生态是现在非常火热的技术生态之一,个人认为它主要囊括着四个方面的技术栈:一是容器核心技术栈(包括 Docker、rkt 及第三方公司自主研发的容器 Engine 等);二是容器基础技术栈(包括容器网络、存储、安全及服务发现等);三是容器编排技术栈(包括 Mesos/Marathon、Swarm、Kubernetes 及 OpenShift 等);四是容器应用技术栈(主要包括 CI/CD、监控、日志及微服务框架等)。而 Docker 和 Kubernetes 分别又是目前容器与容器编排界的小宠儿,所以我将带着小宠从这四方面分享一些容器云在万达的落地经验。

万达容器云平台支持快速部署、弹性伸缩、负载均衡、灰度发布、高效运维及微服务等特性。用户可以基于 Dashboard 简单方便地上线和管理着各自业务。目前在我们的容器云平台上,平稳高效运行着近 400 款包括支付、酒店等核心业务,管理着公司上万个容器。经历住了双旦、618 及双 11 等大型活动的考验。
# 一、容器云的平台高可用架构与部署
“经济基础决定上层建筑”,对于整个容器云平台来说,Kubernetes 平台就是我们的“经济基础”,所以在建设之初我们为了保证平台的稳定、高可用还是做了不少工作。先来看看 Kubernetes 平台的建设涉及到的几个技术点:

* 组件的高可用性如何保证?
* 组件以何种方式部署安装?
* 集群以何种方式快速扩缩容?
* 如何实现环境的批量配置及组件的批量升级?

为了较好的描述这些,我画了一张我们容器云中 Kubernetes 各组件高可用架构与部署图,请见:
1-K8S_组件高可用架构与部署.png

Kubernetes 所有组件都容器化了,而不采用二进制安装的方式。这样对于组件的部署、配置更新和升级等都比较容易,只需要先安装好 Docker,提前做好镜像,然后直接 `docker run --restart=always --name xxx_component xxx_component_image:xxx_version` 就可以了,这里 `--restart=always` 非常重要,用来保证主机重启后或由于突发状况引起的非错误性退出后组件服务自动恢复。

注意在升级过程中,为了减少组件服务的宕机时间,需要提前下载好新制作的镜像版本,因为如果镜像挺大的话,在 `docker restart` 进行更新前会耗费一些时间在 `docker pull` 上面。

在批量部署方面,我们采用 `Ansible` 工具。由于 Kubernetes 集群部署的文档网上蛮多的,所以这里就简要介绍一下组件的高可用部分。我们都知道 Kubernetes 实现了应用的高可用,而针对 Kubernetes 自身的 HA,主要还是在 Etcd 和 Kubernetes Master 组件上面。
##1、 Etcd 的高可用性部署
Etcd 使用的是 V3(3.0.3)的版本,比 V2 版本性能强很多。Etcd 组件作为一个高可用强一致性的服务发现存储仓库,它的 HA 体现在两方面:一方面是 Etcd 集群自身需要以集群方式部署,以实现 Etcd 数据存储的冗余、备份与高可用;另一方面是 Etcd 存储的数据需要考虑使用可靠的存储设备。

为了展示一下组件容器化在`部署方面`带来的一些便利效果,以 Etcd 的部署为例,先在 Ansible `inventory` 文件中规划好 Etcd 的分组信息,一般是采用 3 台做为集群即可。例如:
2-etcd-host.png

然后编写好类似如下的 `template` 文件,就可以用 Ansible Playbook 实现一键秒级的部署 Etcd 集群了,注意我这里使用了主机 IP 作为 Etcd 的名字。
3-template.png

如果需要升级则只需要修改一下 `etcd_version` 并提前拉取新版本镜像,重新跑一下 `ansible-playbook --limit=etcds -i hosts/cd-prod k8s.yaml --tags etcd` 即可完成秒级升级,这样一般显得简洁易用。当然 Etcd 在升级过程中还涉及到数据的迁移备份等,这个可以参考官方文档进行。
## 2、Kubernetes Master 的高可用性部署
Kubernetes 的版本我们更新的不频繁,目前使用的是 V1.6.6。Master 的三个组件是以 `Static Pod` 的形式启动并由 `kubelet` 进行监控和自动重启的,而 `kubelet` 自身也以容器的方式运行。

对于 `kube-apiserver` 的 HA,我们会配置一个 `VIP`,通过 `haproxy` 和 `keepalived` 来实现,其中 `haproxy` 用于负载均衡,而 `keepalived` 负责对 `haproxy` 做监控以保证它的高可用性。后面就可以通过 `VIP:Port` 来访问 `apiserver`。

对于 `kube-controller-manager` 和 `kube-scheduler` 的 HA,由于它们会修改集群的状态,所以对于这两个组件,高可用不仅要保证有多个实例,还需要保证在多个实例间实现 `leader` 的选举,因此在启动参数中需要分别设置 `--leader-elect=true`。

对于 Kubernetes 集群的扩缩容,只需要采用 Ansible 进行批量操作或者 `kubectl` 命令(一般需要用到 `drain`, `cordon` , `uncordon` 等)完成即可。另外我们还需要注意一下图中标注的各组件启动时依赖的参数配置,这些比较容易搞混。
# 二、容器云的技术架构介绍
目前使用的容器云架构方案如图所示,我将其分为四个部分,每个部分包含对应的容器技术栈,下面对于主流的技术介绍如下:
4-云容器技术架构.png

##1、存储方案
后端存储主要采用 `ceph` 驱动,这里主要介绍它在`有状态服务`和 `docker-registry` 两方面的应用。

`有状态`是应用程序的高级需求,它们需要在 Pod 挂了特别是飘移到其他 Node 后,可以持续的访问后端存储。因此,Kubernetes 通过网络存储提供了丰富的 `Persistent Volume` 支持,比如:GCE 的 pd,AWS 的 ebs,还有 GlusterFS,Ceph 等先进的分布式文件系统。我们目前使用了 Ceph 的 `rbd` 来支持有状态服务。Kubernetes 集群环境中,由于所有的组件采用容器化部署,因此除了在主机上安装 `ceph-common` 之外,还需要在 `kubelet`、`kube-controller-manager` 容器中安装它,而且在启动时挂载如下三个 volume,其他的与二进制方式差不多:
5-rbd-volume.png

具体 ceph rbd 配置,详见:Persistent Volume Provisioning 官网

rbd 支持动态供应,支持单节点读写,多节点读,但不支持多节点写。如果有业务需要多节点写的话,rbd 就比较受限制。目前由于只有 `GlusterFS` 既允许动态供应,又支持单节点和多节点读写,所以我们也正在调研其相关使用。

`docker-registry` 做为容器的核心部分,起初我们采用 `Swift` 作为后端存储,为了提高 `push/pull` 的效率,采用 `Redis` 作为 Metadata 缓存,然后直接以容器的方式运行官方提供的镜像,比如:
6-docker-regsitry.png

具体的 `config.yml` 配置,详见:docker-registry 官方配置。但后来为了保证 `docker-registry` 的高可用,我们采用 `Harbor` 做 HA,并以 `pod` 的形式运行在 Kubernetes 集群上,镜像数据以及 `Harbor-db` 全部通过 `Ceph` 的 `PV` 来挂载,这样就保证在 `Harbor` 主机挂了或者 `Pod` 故障后,`Harbor` 也可以 HA 了,同时我们也不需要额外维护 Swift 了。

另外注意一个问题,由于 `PV`,`StorageClass` 都局限于单个 `Namespace` 下,所以对于想通过 `Namespace` 来区分多租户使用动态存储的情况目前是不满足的。
##2、网络方案
底层容器网络我们最初使用的是官方推荐的 `Flannel`,现在部分集群已经由 `Flannel` 切换成了 `OVS` 。 `Flannel` 可以很容易的实现 Pod 跨主机通信,但不能实现多租户隔离,也不能很好的限制 Pod 网络流量,所以我们网络同事开发了 `K8S-OVS` 组件来满足这些需求。它是一个使用 `Open VSwitch` 为 Kubernetes 提供 SDN 功能的组件。该组件基于 `OpenShift SDN` 的原理进行开发。由于 OpenShift 的 SDN 网络方案和 OpenShift 自身的代码耦合在一起,无法像 Flannel 和 Calico 等网络方案以插件的方式独立的为 Kubernetes 提供服务,所以开发了 K8S-OVS 插件,它拥有 OpenShift 优秀的 SDN 功能,又可以独立为 Kubernetes 提供服务。

K8S-OVS 支持单租户模式和多租户模式,主要实现了如下功能:

* 单租户模式直接使用 Openvswitch+Vxlan 将 Kubernetes 的 Pod 网络组成一个大二层,所有 Pod 可以互通。
* 多租户模式也使用 Open vSwitch+Vxlan 来组建 Kubernetes 的 Pod 网络,但是它可以基于 Kubernetes 中的 Namespace 来分配虚拟网络从而形成一个网络独立的租户,一个 Namespace 中的 Pod 无法访问其他 Namespace 中的 Pod 和 Service。
* 多租户模式下可以对一些 Namespace 进行设置,使这些 Namespace 中的 Pod 可以和其他所有 Namespace 中的 Pods 和 Services 进行互访。
* 多租户模式下可以合并某两个 Namespace 的虚拟网络,让他们的 Pods 和 Services 可以互访。
* 多租户模式下也可以将上面合并的 Namespace 虚拟网络进行分离。
* 单租户和多租户模式下都支持 Pod 的流量限制功能,这样可以保证同一台主机上的 Pod 相对公平的分享网卡带宽,而不会出现一个 Pod 因为流量过大占满了网卡导致其他 Pod 无法正常工作的情况。
* 单租户和多租户模式下都支持外联负载均衡。

下面举例解释一下:

合并是指两个不同租户的网络变成一个虚拟网络从而使这两个租户中的所有 Pod 和 Service 能够互通;分离是指针对合并的两个租户,如果用户希望这两个租户不再互通了则可以将他们进行分离;全网化是指有一些特殊的服务需要能够和其他所有的租户互通,那么通过将这种特殊的租户进行全网化操作就可以实现。

不同租户的网络隔离是通过为每个 Kubernetes 命名空间分配一个 VNI(VXLAN 中的概念)来实现的,在 VXLAN 中不同的 VNI 可以隔离不同的网络空间。K8S-OVS 将具体的 Kubernetes 命名空间和 VNI 的对应关系存储在 Etcd 中,如下:
7-network1.png

这是在我们通过 Kubernetes 创建 Namespace 时,K8S-OVS 自动检测并为我们创建的。其中 `NetName` 是指租户的 Kubernetes 命名空间;`NetID` 是指为该租户分配的VNI;`Action` 是指可以对该租户网络进行的操作,它包括 `join` :合并, `isolate` :分离, `global` :全网化,其中 `join` 需要指定上面的第四个参数 Namespace,用于表示需要和哪个租户进行合并,其他两个操作则不需要设置 Namespace。
8-network2.png

合并之后观察 helloworld1 和 helloworld2 ,发现两个租户的 `NetID` 变为相同的了。这样两个网络的 Pod 和 Service 就可以相互访问了。其他场景一样,通过 etcdctl update 来控制 Action 从而改变 NetID 来实现租户的隔离与互通。这里不过多演示。

在应用方面,我们实现了 LVS 的四层,Ingress(基于 Nginx+Ingress Controller) 的七层负载均衡,各 Kubernetes 集群环境中陆续弃用了 Kubernetes 自带的 kube-proxy 及 Service 方案。
##3、CI/CD 方案
CI/CD(持续集成与部署)模块肩负着 DevOps 的重任,是开发与运维人员的桥梁,它实现了业务(应用)从代码到服务的自动上线,满足了开发过程中一键的持续集成与部署的需求。

我们采用了前端基于 Opads(一个比较成熟的在线打包平台)和后端 Pluto(一个将 Kubernetes APIServer 的接口进行封装,并提供 RestfulAPI 服务的库项目)的方案。目前通过该平台上线的业务有近 400 款。其架构图大致如下:
9-CICD.png

业务代码从公司内部的 GitLab/Gerrit 中拉取后,用户选择不同的分支版本(比如:sit/uat/prod)经过 Jenkins 构建后,生成相应的镜像并 Push 到相应的镜像仓库中,底层 `CD` 模块从相应的仓库上线到相应的 Kubernetes 集群(sit/uat/prod)中,而不需要 care 底层平台实现。

在整个 CI/CD 过程中,基础镜像的制作比较关键,我们按不同的业务分类提前制作不同的应用镜像(如:Tomcat、PHP、Java、NodeJS等)并打上所需的版本号,后面源代码既可以 `mount` 到相应的容器目录,也可以利用在 Dockerfile 的 `ONBUILD` 命令完成业务代码的加载。挂载的方式优点是比较灵活,也不需要重复构建,上线效率高;缺点是对于基础镜像环境的依赖较高,如果一个业务的基础镜像一直未改动,但代码中又有了对新组件库的调用或者依赖,则很容易失败。onbuild 的方式和 mount 方式恰好相反,它每次都进行 build,解决了环境的依赖,后面版本回滚也方便,缺点是需要每次进行镜像构建,效率低。这些看业务自己的选择。

我们提供的基础镜像版本在业务上线的时候,业务开发者可以提前搜索确定基础镜像是否存在,如果不存在需要提 Jira 单子后,交由我们进行制作,部分截图如下:
10-opads1.png

之后,业务可以通过选择代码分支、版本等上线服务。之后就可以看到上线的业务详情了,包括 Pod 副本个数,存活时间,名字,创建时间,所在的 Kubernetes 节点及 node selector 等。也可以通过基于 Gotty 实现的 Web console 登录查看业务容器,如图所示。
11-opads2.png

我们还实现了服务扩缩容,弹性伸缩(HPA)、负载均衡、灰度发布等,也加入了代码质量检查(Sonar)、自动化测试及性能测试插件等,这些都是 CI/CD PaaS 平台的重要组成部分。
##4、容器监控与告警方案
容器监控的对象主要包括 Kubernetes 集群(各组件)、应用服务、Pod、容器及网络等。这些对象主要表现为以下三个方面:

  • Kubernetes 集群自身健康状态监控(5 个基础组件、Docker、Etcd、Flannel/OVS 等)
  • 系统性能的监控,比如:CPU、内存、磁盘、网络、filesystem 及 processes 等;
  • 业务资源状态监控,主要包括:rc/rs/deployment、Pod、Service 等;

Kubernetes 组件相关的监控,我们写了相关的 shell 脚本,通过 crond 开启后监控各组件状态。

容器相关的监控,我们采用传统的 Heapster+InfluxDB+Grafana 方案:
12-监控与告警架构.png

Heapster 首先从 Kubernetes Master 获取集群中所有 Node 的信息,每个 Node 通过 `kubelet` 调用 `cAdvisor API` 来采集所有容器的数据信息(资源使用率和性能特征等)。这样既拿到了 Node 级别的资源使用状况信息,又拿到了容器级别的信息,它可以通过标签来分组这些信息,之后聚合所有监控数据,一起 `sink` 到 `Heapster` 配置的后端存储中(`Influxdb`),通过 `Grafana` 来支持数据的可视化。所以需要为 Heapster 设置几个重要的启动参数,一个是 `--source` 用来指定 Master 的 URL 作为数据来源,一个是 `--sink` 用来指定使用的后端存储系统(InfluxDB),还有就是 `--metric_resolution` 来指定性能指标的精度,比如:`30s` 表示将过去 30 秒的数据进行聚合并存储。

这里说一下 Heapster 的后端存储,它有两个,一个是 `metricSink`,另一个是 `influxdbSink`。metricSink 是存放在本地内存中的 metrics 数据池,会默认创建,当集群比较大的时候,内存消耗会很大。Heapster API 获取到的数据都是从它那获取的。而 influxdbSink 接的是我们真正的数据存储后端,在新版本中支持多后端数据存储,比如可以指定多个不同的 influxDB 。

通过 Grafana, 用户可以使用各种正则表达式或者选项查看自己的业务监控详情,还可以按系统性能(CPU、内存、磁盘、网络、filesystem)进行排序查看等等。
13-监控示例.png

当监控到一定的数量级,超过某个阈值时,将产生告警。虽然 Grafana 目前支持邮件进行一些简单的告警,但我们还是通过制定一些监控点、告警机制、告警等级等,然后接入公司内部现有 Zabbix 平台来进行告警。

邮件告警示例如下:
14-邮件告警.png

##5、日志方案:
容器平台的日志系统一般包括:Kubernetes 组件的日志,资源的事件日志及容器所运行的应用的日志。所以一个好的日志系统至少需要 cover 到这几块。

日志这块一直是我们头痛的地方,之前我们主要关心容器中的业务日志,所以是直接将其日志对接到公司的统一日志平台(Hippo)中。他们需要我们采用 Flume 来进行日志采集,每个业务以 Pod 的方式运行 Flume ,在 Flume 中配置好 `source`, `channel` 和 `sink` 参数。`source` 用来指定业务挂载的日志目录,然后通过 `channel` 进行输送,最后 `sink` 到后端的 Hippo 所使用的 Kafka 中。业务需要查看日志,则需要登录到 Hippo 平台中。

这个方案有一些不如意的地方,比如:每个业务需要单独额外运行一个 Flume 的 Pod,浪费资源;业务需要登录到另一个系统查看,由于不是对接到我们的平台中,不方便;另外由于 Hippo 是针对公司的统一的日志平台,不是容器云专用的,经常会在高峰期响应慢,很难处理过来。所以我们决定设计一个全新的平台,采用的方案是 `Kubernetes` 官方推荐的(Fluentd+Kafka+ES+ 自定义界面),具体架构如下:
15-日志方案.png

容器中输出的日志都会以 `*-json.log` 的命名方式保存在 `/var/lib/docker/containers/` 中,系统日志都在 `/var/log` 中。Fluentd 以 `Daemon Set` 运行于所有的 Node 中进行数据采集,为了保证性能,引进了 Kafka 作为消息队列及日志转发,由于不想维护多个组件,中间转发不用 Logstash ,所以需要引入了两个 Fluentd 的插件 `fluentd-kafka` 及 `fluentd-es`,前者用于推送数据到 Kafka,后者用于将数据推到 ElasticSearch 中。最后实现一个 `Log API Engine` 用于供上层 `Log GUI` 调用。这个 Engine 封装了实时日志、历史日志下载和查询、应用日志占比、日志等级占比等 Restful API。下面是我们的部分截图:
16-日志示例.png

# 三、容器云的填坑实践
下面挑几个坑说一下,并分享一下解决方法。
## Docker 相关
早期由于混合使用了 Deployment 和 RC,此时如果使用了同名 Pod label 则会产生冲突,RC 会删除 Deployment 后创建的 Pod,造成 Pod 的反复创建与删除,最终导致 Node 上的 Docker daemon 挂僵掉。原因是 Docker device mapper 互锁而触发 Linux kernel bug(特别是有大量的 outbound traffic 时),解决方法是升级内核到 3.10.0-327.22.2,或者添加内核补丁
## Kubernetes 相关
业务应用在升级过程中,如果 Docker 删除出错, 偶偶会导致 device mapper busy,则会显示 Pod 一直在销毁,但其实它的 Deployment 已经被删除了,这种我们没有找到很好的处理方法,现有 workaround 是先重启 docker daemon,如果不能解决,再 reboot 主机。一般的做法是先 `drain` 掉所有的 pod,然后待重启解决后,再 `uncordon` 回来。

在使用容器的方式部署 kubelet 时,我们发现删除 Pod 时,在 apiserver log 中一直会出现 `UnmountVolume TearDown secrect` 资源失败的错误。其中是当时在挂载 `/var/lib/kubelet` 时采用了 `rw` 的方式,这个问题困扰我们很久了,解决方法是加上 `shared` 即 `--volume=/var/lib/kubelet:/var/lib/kubelet:rw,shared`。
## 存储相关
当某一 Pod 挂载 Ceph rbd 的 Volume 时,如果删除 Pod,再重新创建,由于 PVC 被 lock 导致无法挂载,会出现 volume 死锁问题。由于我们的 kubelet 是容器部署,而 ceph 组件是以挂载的方式开启的,所以猜测可能是由于 kubelet 容器部署引起,但后面改用二进制方式部署并升级到 `V1.6.6` 版本后就可以解决。当时的版本是 V1.5.2。
## 网络相关
在业务上线过程中,一定要进行规范化约束,比如,当时将 Flannel 升级到 K8S-OVS 网络升级过程中出现有些业务采用 Service 进行负载均衡的情况,这种依赖于 kube-dns,由于 Flannel 和 OVS 的网段不一样,Service 层又没有打通,导致以 Service 运行并通过 kube-dns 解析时会出问题,且网络不通,本可以采用新建一个以 OVS 同网段的 kube-dns 来完成不同网段的兼容,但最后面发现该业务是根本不使用 Service,而是直接利用了 Pod,这样非规范化上线的业务很容易导致升级相关的故障出现,猜测可能当时平台建设初期手动上过业务,其实这方面我们也可以加些监控。

网络不稳定的时候,偶偶发现业务访问突然就慢起来了,然后发现其 `TIME_WAIT` 会出现过高,这个是没有对网络内核进行优化处理,此时需要设置 `net.ipv4.tcp_tw_recycle = 1` 和 `net.ipv4.tcp_tw_reuse = 1` ,前者表示开启 TCP 连接中 `TIME-WAIT Sockets` 的快速回收,后者表示允许将 `TIME-WAIT Sockets` 重新用于新的 TCP 连接。当然还有其他网络内核优化。
## 告警相关
CPU load 是通过每个核的 `running queue`(待运行进程队列)计算出来的,某些情况下 running queue 会变成 -1 也就是 4294967295。由于在 cpu 过载的时候,我们设置了告警,所以会被触发,但其实这时的 CPU 负载是正常的。此时,如果通过 `sar -q ,top,w,uptime` 等看到的 running queue 都是有问题后的值,只有用 vmstat 查看才是真实的。解决方法是重启 Node,因为这个值只有在重启后才会重置为零,或者升级内核补丁
#Q&A
Q:Grafana 是实时显示数据的,请问他如何能做到告警?就是 grafana 达到一定告警阈值时做告警?

A:Grafana 新版本中添加了简单的告警功能,在 Notification Channels 页面有个新建通道,在里面设置一下,具体可以看下官方的文档。



Q:请问如何实现容器限速的?

A:你是说容器的网络限速吗?流量限制功能我们是通过在 pod 的 annotations 字段设置 kubernetes.io/ingress-bandwidth (设置输入流量带宽)和 kubernetes.io/egress-bandwidth (设置输出流量带宽)来实现。



Q:请问使用什么操作系统部署 Kubernetes,有什么考虑?

A:用的 CentOS 7,企业一般的用法,还有就是它稳定,不容易出问题,Kubernetes 和 Docker 的支持比较好。



Q:如何把所有告警信息全部递给 Zabbix,Zabbix 自身是否也获取了监控值信息了?

A:全部推送压力大,先将 APIserver、Heapster 中相关的信息放 MySQL,中间做个数据库。



Q:etcd 3 的容灾和安全做了吗?

A:etcd 非常关键,我们会在升级和定期用 etcdctl 做 backup。升级时需将 --initial-cluster-state 改为 existing ,安全方面还没有。



Q:做灰度发布或 HPA 自动扩容时,实现了不影响正在提供服务的服务吗?

A:灰度发布不会影响服务,我们使用了 Ingress + Nginx 来保证 Pod 的变化不受影响。HPA 这块我们不敢上线,功能完成了,但没有经过大量测试。



Q:使用 rbd 作为后端存储,当 pod 发生迁移到另外一个节点后如何再次挂载这个 rbd?

A:将 PVC 的 volume.beta.kubernetes.io/storage-class 和 StorageClass 的 name 名字一样就可。不需要管后面 Pod。



Q:etcd 3 在哪些方面不如 etcd 2?

A:没有去做对比,etcd 3 是通过搜集了 etcd 2 用户的反馈和实际扩展 etcd 2 的经验基础上全新设计了 API 的产品。etcd 3 在效率,可靠性和并发控制上改进比较多。etcd 2 支持多语言客户端驱动,etcd 3 由于采用 gRPC,很多需要自己实现驱动。



Q:请问有状态的 pod 迁移,使用 ceph pv 是怎么保证分到同一个 volume?

A:我们用的是 StorageClass,在 PVC 时指定和 StorageClass 名字一样就可。通过 volume.beta.kubernetes.io/storage-class 来指定该名字。



Q:请问运行在不同的 Node 上面的 Pod 如何共享 Volume 存储,比如要共享一份代码?

A:不同 Node 间的 Pod 卷共享方式比较多,但一般需要网络存储,比如:NFS,GlusterFS,CephFS,Ceph rbd,当然还包括很多大厂如:GCE 的 pd,AWS 的 ebs 等。甚至可以使用 ConfigMap 来共享,然后 mount 到相应的目录即可。



Q:请问有没有对比过共有的容器云和私有的容器云的优缺点?

A:公有云比较难做,我们之前是做私有云(物理资源隔离,用户数据更安全可控;独占资源,不受干扰;自行规划灵活调整资源复用比例,成本更优),公有云(公有云弹性,自如应对业务变化;具备跨机房、跨地区的容灾能力)我们也在做,正在和 IBM 合作。



Q:请教多 Master 下,当某个 Master down 掉,default/kubernetes endpoints 中的 IP 没更新的问题,你们是如何处理的?

A:这个主要是 Endpoints 控制器负责 Endpoints 对象的创建,更新。新 leader master 掌管后,Kubernetes 会用 checkLeftoverEndpoints 来删除 没有响应的服务的 endpoints,重启一下 kube-controller-manager 试试。



Q:做过集群联盟吗?

A:有测试过,但目前 Kubernetes 可以支持达 1000 节点了,能满足我们目前的需求,所以还没有上。



Q:HPA不是Kubernetes支持的吗?你们对其做了哪些二次开发?支持蓝绿部署吗?

A:对的,目前是支持 CPU 还有一些应用程序提供的 metrics 了,之前在社区还没有的时候,我们有自己开发,主要是通过 heapster 监控 qps 还提供自定义的一些度量来完成 HPA。但 HPA 这个一旦出问题会不可控,所以暂时还不敢上线。蓝绿部署比较耗硬件资源,相当于要多一新版本备份,目前我们还不支持蓝绿部署。



Q:如果想看日志文件有没有好的办法,感觉在ES重被切割了不友好?

A:日志文件可以通过在启动的时候新建一个以应用名字命名的目录挂载到本地或者网络存储中,然后应用的标准或错误输出会直接输出到 docker daemon 的日志目录下,如果应用有自己的专门的文件输出方式,则可以用 tail -f 方式进行转发与 docker daemon 对接。



Q:还有就是基础容器是用的CentOS镜像吗?它默认就接近200m。开发语言用的Go的话有啥优化容器的地方?

A:基础容器一般 CentOS 的多些,有些会直接采用 docker hub 提供的原始镜像,然后做些自定义组件添加并重打包。一般的会比较大一些,镜像可以对 Dockerfile 进行优化来变小。可以用 pprof 来分析 Go 代码性能,容器的优化还主要在 Dockerfile。



Q:请问你们对于用户体验方面是如何监控的? 比如每个点击在不同服务层面上的延时各是多少,超时报警等?

A:这是个不错的想法,我们还没有做这块,不过可以通过应用提供的url,对其监控HTTP get 的 response 时间来控制。



Q:前端基于 Opads和后端 Pluto实现CI,有具体的文档可以参考吗?

A:这两个都是自己内部开发的模块,一个基于 PHP,一个基于 Python,文档不方便分享。



Q:目前大规模落地云平台是建议上容器云吗?

A:建议上。



Q:服务启动依赖和应用版本控制如何做的?

A:这块我们做的不大好,一般可以将每个服务注册到发现服务,然后记录它们的依赖,在启动时进行服务发现及启动,这个在微服务框架中有些。我们在应用程序版本控制方面有自己的约束规范,但后面会有 helm 来试试。



Q:etcd 集群为什么不直接用Compose启动?

A:这个我们为了ansible部署方便



Q:Node 节点采用虚拟机还是物理机部署的?

A:物理机。



以上内容根据2017年09月21日晚微信群分享内容整理。 分享人陈强,万达网络资深工程师,毕业于华东师范大学。目前在万达网络科技集团云公司基础架构部负责Kubernetes与Docker的落地与实践工作。曾先后就职于Intel、IBM和爱奇艺。在云计算领域长年搬砖,对Mesos/Kubernetes/Docker等有较深入的研究。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesa,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

PaaS平台OpenShift企业部署的“脑图”

范彬 发表了文章 • 0 个评论 • 3324 次浏览 • 2017-08-17 14:33 • 来自相关话题

【编者的话】这篇文章是来自红帽,是关于OpenShift企业部署的“蓝图”,通过“脑图”帮助客户实现企业级部署OpenShift。 【烧脑式Kubernetes实战训练营】本次培训理论结合实践,主要包括:Kubernetes架构和资源 ...查看全部
【编者的话】这篇文章是来自红帽,是关于OpenShift企业部署的“蓝图”,通过“脑图”帮助客户实现企业级部署OpenShift。

【烧脑式Kubernetes实战训练营】本次培训理论结合实践,主要包括:Kubernetes架构和资源调度原理、Kubernetes DNS与服务发现、基于Kubernetes和Jenkins的持续部署方案 、Kubernetes网络部署实践、监控、日志、Kubernetes与云原生应用、在CentOS中部署Kubernetes集群、Kubernetes中的容器设计模式、开发Kubernetes原生应用步骤介绍等。

如下是构建高分布式平台的大部分依赖:

  • 战略目标 (Strategy)
  • 存储计划(Storage)
  • 业务操作(Operation)
  • 业务连续性弹性与灾难恢复 (BCR&DR)
  • 应用开发 (AppDev)
  • 安全(Security)
  • 自动化(Automation)
  • 网络(Networking)
  • 提供(Provisioning)
  • 外部依赖 (External dependencies)
其中每类都包含多个子区域。下面我们将逐一说明。即使你熟悉这个话题,你也会发现脑图的价值。如果你想要更多,点击放大图片或查看https://github.com/mangirdaz/ocp-mindmap
appcoup-pod-150-150-768x1159.png
# 策略目标(Strategy)开始采用新平台之前,你的策略应该是你想到的第一件事情。你需要知道谁将使用你建立的这个平台。利益相关者是谁,最后谁负责?确定利益相关者通常是构建策略的最简单的部分。建立实践社区(CoP)将有助于制定战略和方向。一个精心设计和维护的CoP可以帮助您利用自己组织力量推动实现“正确解决方案”的进程。封闭式解决方案往往会错过组织需求。考虑对CoP进行一些研究。其次,你需要“微服务标准化”策略。在将服务放在新平台之前,强烈建议您定义一些标准。这里的挑战是旧的标准不适用于这个新的世界。尝试“复制 粘贴”,某些功能可能无法正常工作。# 存储计划(Storage)计划构建一个成功的平台,存储或持久性是关键考虑。你不仅需要简单的存储,而且需要可以高扩展的存储空间。你将需要为平台内部组件提供存储,如:日志、度量和容器镜像仓库。当然,你可以根据将要构建的解决方案选择不同的实现方式,如:对象存储、基于消息传递的日志服务,有时可能不需要考虑存储,但你无法完全消除对存储的需求。一旦我们开始说“持久性”,就意味着存储。开始思考如何做,谁将为你提供,以及你将拥有的存储空间(SSD,Magnetic,SAN,NAS等)。# 业务操作(Operation)操作部分与本文的下一节紧密相连。业务主要由以下两个领域组成:通常和无计划的行为(BCR&DR)。我们将在下一节中介绍无计划的行为。通常业务操作包括出口(egress)路由器配置、平台维护、补丁、调度规则创建、平台管理以及对平台上发生的事件的主动反应等内容。所有这一切,你将需要一个非常好的日志和监视。没有日志和监控,你将会“失明”,在高分布式系统中变得盲目。在分布式系统中,事情可能会从坏到更坏变得非常快,与“独立服务器”的基础架构相比,系统变得更糟糕。例如,如果出现故障,你的容器将重新调度(假设你没有自动伸缩功能,即使可以自动伸缩,你也可能花费巨大的费用)。在当前配置中重新调度=高密度,将意味着会遇到滚动失败情况。因此,你需要为运营定义标准运行手册,创建仪表盘,确保你了解你的环境,这是运营中最重要的事情之一。# 业务连续性弹性与灾难恢复(BCR&DR)我将业务连续性和弹性(BCR)和灾难恢复(DR)从业务中分开,它是非常重要的。即使在提供生产流量之前,我建议你知道你的“失败域”,及如何失败恢复。你需要制定一个计划。例如:如果你在分布式、可靠的、键值存储集群(如:在OpenShift / Kubernetes的etcd)中丢失了仲裁,或者知道外部DNS或存储提供发生故障。同时,存在多种不同的场景,如:不同的环境有所不同,某些内部/外部依赖关系具有较少或更成熟的提供,这些都取决于你所在的组织。# 应用开发(AppDev)你也需要考虑开发者。一些组织忘记了平台将主要由开发人员使用。只有开发者才知道他们想要在一个平台上运行什么。尽管事实上你将有很多工具,但你可能希望对模式和蓝图进行标准化。你应该考虑如下:
  • 应用开发人员将如何监控应用程序和节点性能?
  • 他们将如何推广和部署?
  • CI / CD管道(现有和新的)将如何与平台集成。
  • 你将如何推广来自环境的镜像?
  • 如何进行配置推广?
  • 最后,开发人员将会使用哪些开发工具?
开发人员的经验非常重要。得到这个权利,你的平台将被使用。错了,没有人会想使用它。# 自动化(Automation)这个特定的领域与其他领域有许多关系。你将需要使用CI / CD工具自动化你的应用程序、进行镜像测试和推广等。此外,你的基础设施应该被视为与你的应用程序相同 - 自动化无处不在。为此,你可以使用配置管理工具或仅依赖于部署工具。但是,如果你从一开始就以自动化的思想开始构建,那么即使在开始时它看起来很慢,它会变得想要的更快。# 网络(Networking)你需要考虑如何访问应用程序、出口(egress)和入口(ingress)流量,以及如何配置负载平衡器。决定是否将运行 “主动-被动” 或 “主动-主动”,及如何负载均衡所有的堆栈。容器是“网络中的第一个公民”或依赖SDN抽象?如何处理DNS?是否需要相互SSL?你的集群在2 - 5年内有多大?一个节点上运行多少个容器?所有这些问题都将定义你的平台的网络设计。# 安全(Security)尽管平台构建要考虑是安全的,但你仍然会有很多公开的问题。例如,你如何管理你的私密凭据(密码,证书)以及如何更新它们?你将如何将你的应用程序暴露给外界?最后,最重要的是,如何验证你正在运行的镜像?运行多租户、基于微服务的应用程序,镜像扫描和生命周期是关键问题。# 提供(Provisioning)提供非常广泛,与你的组织中的如下有很高的联系:
  • 配置管理:使用哪一个配置管理,以及它如何与平台支持的工具一起使用?一旦你确定了所使用的配置管理后,需要选择使用什么工具?你是否需要什么额外的工具?你是否需要红帽的订阅和生命周期管理吗?是否需要CloudForms控制台、能力和主动管理?需要什么可视化提供商及其功能?
  • 基础设施本身:你是否使用容器化部署或基于包/ RPM?如何满足你组织的补丁策略?例如,如果你的组织是基于RPM的,你选择了基于容器化/OSTree的平台,你的运维工程师如何知道它们的生命周期?
最后,你需要为平台做什么定制(预先及后期操作),使其符合你的需求?# 外部依赖(External dependencies)你会有很多外部依赖,请确保这些外部依赖的弹性和它们提供的SLA。外部依赖将包括如下:
  • 记录和监控:日志如何归档?集成哪些外部日志和监控解决方案?提供哪些元数据?
  • 存储:你将如何使用它?是否足够快?每秒输入/输出操作(IOPS)?如何扩缩?
  • 容器镜像仓库:如果你计划运行全局分布式集群,确定你的镜像的一致性?镜像仓库是否需要外部存储?如果是,什么格式?
  • 身份验证和授权:你将如何验证你的用户和应用程序?首选的身份验证提供商?如何进行基于角色的访问控制(RBAC)?
  • ITSM / CMDB:你需要在配置管理数据库中注册你的应用程序吗?如何自动化(或自动化解决方案?)。如何考虑变化?

# 架构
最后,一旦你知道你想去哪里(策略),你需要开始考虑更广泛的架构,例如基础设施密度、数据中心和可用性区域。已经覆盖的大部分领域与平台本身的架构有关。你需要在开始时做出正确的选择,因为分布式平台(OpenShift / Kubernetes等)构建和使用数百甚至数千个应用程序,难以修改。如果你网络错误,以后再进行更改是不可能的。如果平台不适应外部依赖关系与平台扩展的能力,将遇到瓶颈。

至此,我们介绍了这个旅程,你需要考虑什么。需要明白一点:公共云不能解决所有这些问题。这些可能看起来很难、不值得。但是,最终的结果就是全云不可知、水平和垂直可扩展的自助服务基础设施 ,这些将使开发人员开心。

原文出处:Enterprise OpenShift Deployment: What do you need to know?(翻译:范彬)

===============================================================
译者介绍:范彬,从事微服务、Docker和Kubernetes容器技术等方面的工作。可以关注译者的微信公众号:范范米饭。

在OpenShift中运行容器

alex_wang2 发表了文章 • 0 个评论 • 6240 次浏览 • 2017-05-18 22:35 • 来自相关话题

【编者的话】本文不是一篇关于如何在OpenShift中使用和部署容器的指导性文章,也没有具体介绍OpenShift的原理组成,关于如何使用OpenShift可以参考另一篇《OpenShift V3 应用发布部署的简单场景演示》。 【深 ...查看全部
【编者的话】本文不是一篇关于如何在OpenShift中使用和部署容器的指导性文章,也没有具体介绍OpenShift的原理组成,关于如何使用OpenShift可以参考另一篇《OpenShift V3 应用发布部署的简单场景演示》。

【深入浅出学习 etcd】etcd为分布式系统提供可靠、高效的配置管理服务,在Docker、Kubernetes、Mesos等平台中扮演了越来越重要的角色。作为2013年开始的项目,它还很年轻,官方文档中缺乏实现上全面、系统的介绍,本课程深入浅出地介绍了etcd的实现,并为运维和二次开发提供了系统的指导和建议。

本文的关注点在于OpenShift中的容器安全性问题,相信很多用过容器的读者都会遇到在容器里以root用户启动应用的情况,这篇文章着重介绍了OpenShift中我们如何处理这种情况,以及这样处理带来的副作用和其中的原因。
openshift.png

OpenShift 是Red Hat公司推出的一个基于Kubernetes的容器应用平台。并且很简捷,没错,它就是一个PaaS。新的V3版本的OpenShift做了一次重大的改变,所有的组件用go语言重新编写,并且和现在的Kubernetes有了很好的结合。当你使用OpenShift的时候,你其实得到了一个Red Hat推出的基于Kubernetes的分布式系统,OpenShift的功能是围绕着代码部署,自动化构建等等,是一个典型的PaaS平台。

Openshift的优点是什么呢?Red Hat经常引以为豪的就是它的安全性

我不是一个安全领域的专家,但是当你考虑使用Kubernetes并且如果你不想围绕secrets争论的时候,你会发现,Kubernetes有很多关于安全性的功能。RBAC就是一个当你用`kubeadm`去部署一个集群环境时默认的设置。可以通过强制的网络规则进行网络隔离,pod也可以用安全策略控制非常严格。加上身份验证机制,为所有组建间的通信提供TSL加密,权限控制,至少对我来说,这是一个非常强大和安全系统。

但是对早期的使用者来说这些安全设置是有代价的。在OpenShift中,使用Kubernetes默认的Pod安全策略,他们被SCC(Security Context Constraints)调用。默认使用SCC最明显的地方是在OpenShift的容器中,进程不能用`ROOT`用户运行。(译者注:表现为权限不够)

所以如果你在`minishift`里,没有指定一个非`ROOT`用户去运行一个Docker镜像,这肯定会失败。这意味着,非常遗憾,我们的Bitnami镜像现在还不能运行在OpenShift上。我们正在修复解决这个问题,也就是说在Dockerfile中用一个非特权用户,我将向您展示如何暂时规避这个问题。
# 开始使用OpenShift
使用OpenShift可以从minishift开始,他是一个定制的`minikube`,也就是说,它可以运行在你桌面系统的虚拟机里(就像k8s)。客户端`oc`(译者注:oc是OpenShift的主要命令)可以被非常容易的配置,因为他实际上是在`kubectl`外面包装了一层。

当你下载后,使用它感觉就像在使用`minikube`一样:(除了它默认的驱动是xhyve,当然如果你用的是Virtual Box......):
minishift start — vm-driver virtualbox

把`oc`添加到你的PATH中:
$ minishift oc-env
export
PATH=”/Users/sebgoa/.minishift/cache/oc/v1.5.0:$PATH”
# Run this command to configure your shell:
# eval $(minishift oc-env)
$ eval $(minishift oc-env)

# 修改SCC
为了修改SCC,你需要用admin用户登录到OpenShift中:
oc login -u sysadmin:admin

这时修改`scc`:
oc edit scc anyuid

如果你不使用`admin`用户登录,那么RBAC不会让你修改安全约束(security constraints),这时增加user会像这样:
users:
[list]
[*]system:serviceaccount:default:ci[/*]
[*]system:serviceaccount:ci:default[/*]
[*]system:serviceaccount:myproject:default
[/*]
[/list]基本上,你在你自己的项目里(myproject)可以用默认的服务帐号(service account)以任何`uid`包括`ROOT`用户去在运行一个容器。需要注意的是,你自己创建的项目(myproject)是一个可以用被你自己访问的名字空间。它是默认被minishift创建的,你可以用`oc config view`在k8s中找到它。

作为普通用户切换回minishift( `oc config` just like `kubectl config` ):
oc config use-context minishift

# 用容器创建一个应用程序,并以ROOT身份运行一个进程
你现在可以用容器去创建一个应用程序了,并且这个应用程序的进程是`ROOT`用户运行的。你解除了pod的安全规则。但是这部太符合最小权限运行原则。`oc new-app`看起来很像`kubectl run`
oc new-app --name foobar \
--docker-image bitnami/mariadb \
--env MARIADB_ROOT_PASSWORD=root

现在你已经运行起来了bitnami mariadb在OpenShift里了,( `oc logs` like `kubectl logs` :
$ oc logs foobar-1-zft17
Welcome to the Bitnami mariadb container
Subscribe to project updates by watching
https://github.com/bitnami/bitnami-docker-mariadb
Submit issues and feature requests at
https://github.com/bitnami/bitnami-docker-mariadb/issues
Send us your feedback at containers@bitnami.com
nami INFO Initializing mariadb
mariadb INFO ==> Configuring permissions…
mariadb INFO ==> Validating inputs…
mariadb INFO ==> Initializing database…
...

# 结论
虽然在OpenShift里可以绕过默认的安全约束策略(SCC)但其实这并不是一个理想状态。这就是为什么在Bitnami中,我们现在去修改我们的容器应用模板去harden它们并且让它讷讷个够和运行在OpenShift中。

原文连接:Running Containers in OpenShift(翻译:王晓轩)