本文所述内容的背景是,基于Docker容器技术的OpenStack研发、测试、运维及其相关的CI/CD、DevOps等活动。思想是相通的,读者可以取其可用部分用于自己的业务需求中。
IaaS云和容器云不是可有可无、相互竞争的关系,而是相互弥补彼此缺陷的关系。容器改变了应用部署和管理的模式,众所周知,IaaS云通过提供基本的计算、存储和网络来运行虚拟机(VM),在IaaS(基础设施即服务)之上,还有PaaS(平台即服务)、SaaS(软件即服务)、CaaS(容器即服务)。OpenStack作为一个IaaS云的基础设施管理平台,为用户提供了创建VM和其他资源的服务,至于具体怎么用这些虚拟机、怎么运行业务应用,如Hadoop大数据、Docker微服务、CI/CD工具链、Cloud
Foundry等,用户自己可以按需使用。
Docker的整个生命周期阶段,主要分为:
Build——使用Dockerfile文件或手动方式生成镜像。
Push——将构建好的镜像上传到镜像仓库中。
Pull——从镜像仓库中拉取镜像到使用环境中。
Run——启动镜像为容器服务。
Stop/Remove——停止或删除容器、镜像。
Docker适用于CI/CD的场景,主要是基于:
Docker容器是把应用程序和环境打包在一起的,所以是一次构建,处处运行。比如,当开发人员变更代码后,测试人员和运维人员只需要执行停止旧容器、启动新容器这两个步骤即可。因此,Docker最大的好处就是标准化了应用交付,只要系统能运行Docker,就能无差别地运行应用。
Docker镜像统一封装了软件服务及其依赖环境,可以让镜像成为一个标准的软件交付物。
容器比虚拟机运行效率更高,在开发测试工作日益繁重的CI/CD环境中,Job任务运行的速度是至关重要的。
通过容器云平台如Kubernetes,可以方便地实施集群和容器管理。相关技术也日趋成熟,生态环境丰富。
不同于其他软件应用,OpenStack有着自身固有的特点,既有众多优势,也存在对研发测试带来的诸多挑战。如何运用有效的方法,才是解决问题的关键所在。
一、OpenStack和Docker集成现状
Docker是一种集Namespace、Cgroups等技术于一体的容器技术,可以将应用和环境等进行打包,形成一个独立且隔离的环境,让每个应用彼此相互隔离,并在同一台主机上同时运行多个应用,进行更细粒度的资源管理。Docker和VM(虚拟机)的区别如下图所示:
工欲善其事,必先利其器。Docker是一种具体的容器技术,虽然以REST API形式提供服务,但在实际生产环境中管理大规模集群的Docker镜像和容器仍然是一个巨大的挑战。开源软件项目的成功往往根据其催生的生态系统来衡量,使其日臻完善。
1.Kubernetes
谈论Docker管理,自然不能不提及Kubernetes,这是谷歌开发的一款开源的Docker管理工具,用于跨主机集群部署容器。Kubernetes还提供了让容器彼此互通的方法,不需要手动开启网络端口或执行其他操作。它提出两个概念。
Pods:每个Pod都是一个容器集合并部署在同一台主机上,共享IP地址和存储空间,比如将Apache、Redis之类的应用分为一个容器集合。
Labels:提供服务标签,方便Pod容器之间的调用协作。
2.Docker Compose
Docker Compose 是容器编排工具,让用户可以自定义容器的镜像、容器之间的依赖关系等。定义好这些信息之后,只需要一条命令就可以按照顺序启动容器,然后整个应用就部署好了,这对于自动化来说是一个很好的进步。对于应用编排来说,Compose是一个非常不错的选择。
3.Docker Machine
Docker Machine是一个便捷的容器创建工具,即从0到1地安装Docker,极大地简化了安装过程。使用docker-machine
create命令,参数-d指定虚拟化的驱动,即可在相应的平台上创建Docker。
4.Docker Swarm
Docker Swarm 是Docker的集群管理工具,能够通过Master/Slave 的方式管理跨主机的Docker,为Docker集群化部署提供了非常好的支持。
5.Docker Datacenter
Docker Datacenter 是Docker的自动化集群管理工具,并非第三方工具,它其实是一个基于Docker的管理平台。
6.Apache Mesos
Mesos的名气比较大,基于它可以做很好的扩展,开发个性化的Docker集群管理工具。
7.Marathon
Marathon是一个与Mesos结合在一起的Docker集群管理工具,支持在Mesos之外调度容器。
8.Panamax
在众多的集群管理工具面前,如何管理单机的Docker容器也是一个需要解决的问题。因为Docker占用资源少,在单机服务器上部署成百上千个容器也是可能的。Panamax提供了人性化的Web管理界面用来安装软件,让部署变得更简单。并且,Panamax还提供了丰富的容器模板,让在线创建服务成为可能。所有的操作都可以在Web界面上完成,开发者只需要关注开发应用即可。
9.Tutum
Tutum提供了一套非常友好的Dashboard界面,支持创建Docker容器的应用。
10.Harbor
使用Harbor可以管理大规模的Docker镜像,以及基于同步复制功能的HA主备高可用性、细粒度的角色权限和项目管理。
除此之外,还有一些Docker性能监控工具,如Docker Stats、CAdvisor、Scout、Data
Dog以及Sensu等。
11.OpenStack集成Docker项目
OpenStack社区为了集成Docker做了很多努力,如开发了Nova+Docker Driver、Heat+Docker
Driver等模块,但都存在诸多不足。为了能更好地与Docker集成,社区陆续开发了几个新项目,如Magnum、Murano、Solum和Kolla等。
目前,Kolla是OpenStack集成Docker最火热的一个项目,将大部分的OpenStack服务Docker容器化,便于安装部署升级。其他的如Magnum,则提供了CaaS(容器即服务),Solum、Murano和Docker有些关系。但Solum更偏重于CI/CD,可以理解为一个应用软件的持续集成/持续交付环境,Murano是一个App
Store,Solum可以将开发的应用发布到Murano中。
二、基于OpenStack+Docker设计CI/CD
目前,在Docker容器中部署和运行OpenStack云计算服务,已成为主流趋势之一。基于这样的背景,设计和实现OpenStack+Docker环境下的CI/CD应用便成为了必然。
在IT企业中,当我们面对以OpenStack为代表的云计算+以Docker为代表的容器技术这两种可用的基础设施平台时,如何利用二者的关系为产品研发测试带来效益,是至关重要的。对于竞争激烈、快速革新的互联网企业而言,持续创新及快速交付服务是其打造产品核心竞争力的重要因素之一。特别是在产品设计、功能实现、快速发布到可视化管理等方面,无疑都需要自动化测试、敏捷开发、持续集成(CI)和持续交付(CD)等方式进行高效支撑。其中,产品开发和自动化测试等重要环节,对环境的执行能力都提出了更高的要求。
我们深知,仅仅有一些方法和工具是不够的,真正给产品开发带来价值的,是如何完成产品的敏捷创新、降低风险,并根据市场反馈及时变更产品等。在这方面,OpenStack社区的做法是成功的。当然,我们可以借鉴其优势,将其引入到自己的业务环境中。在开发阶段,通过CI对项目进行持续集成、自动化编译和测试,以达到控制代码质量的目的,持续提升开发效率,同时在交付阶段,利用CD帮助在短周期内生产有价值的产品,并且保证能够在任何时间进行发布。
设计与实现基于OpenStack云计算+Docker容器技术的CI/CD服务,其核心是在OpenStack
IaaS云计算平台上创建虚拟机,实现基于OpenStack研发测试业务背景下的CI/CD服务。
这里涉及三个重要组成部分:一是基于原生OpenStack研发的云计算产品,以及基于OpenStack基础设施平台构建的CI/CD服务;二是包括诸如Jenkins、GitLab、Gerrit、Harbor等系统在内的CI/CD应用;三是将OpenStack每个服务容器化,并使用Kolla方式部署。
基于云服务的CI/CD服务,可以实现弹性伸缩和横向扩展等。本节将以案例方式进行实践,如下图所示:
首先在物理服务器上搭建好OpenStack IaaS云平台,以及创建数台可用的虚拟机等,以备后续使用。
建议:在生产环境中,通过创建并挂载云硬盘到虚拟机中运行的服务数据目录下,或者通过rsync同步备份数据的方式,以保证数据的高可用性和完整性。对于CI/CD系统的HA(高可用性)等,可以通过计算节点HA或虚拟机HA的方式实现。
所用到的持续集成和持续交付平台如下表所示:
在实际的企业研发测试和CI/CD环境下,建议使用三个Harbor系统并相互关联,一个是开发系统,用于存放和管理平时开发人员上传的开发Docker镜像,该系统允许存放同一个镜像的不同副本(tag);一个是测试系统,用于存放经过了CI/CD流程验证测试的Docker镜像;一个是生产系统,用于存放发布的Docker镜像,其产出物便是用于交付的最终产品。
2.1 基于Docker的软件持续交付
目前,Docker容器已经在IT软件生产的各个环节中得到了大量的推广和使用,如从软件开发,到持续集成和持续交付,再到生产环境上的微服务应用等。除了Docker技术本身的发展以外,还不断涌现出一系列与容器相关的生态应用,包括容器编排、高可用性、运维监控和日志收集等各个方面。
在基于容器的持续交付实现当中,以镜像为内容传递的单元,通过CI(持续集成)的测试以及验证,完成镜像从开发、测试到可发布的状态转变和软件的交付流程,如下图所示。
开发人员:编写和提交代码,从提交代码起,这一过程均自动化完成,完成所属范围的工作内容,负责输出待测试的镜像。
测试人员:编写测试用例,手动或自动部署环境,执行各种测试,完成所属范围的工作内容,负责输出预发布的镜像。
运维人员:将预发布的镜像部署到预生产环境中,执行验收测试,完成所属范围的工作内容,负责生产环境的部署和运维。
1.持续集成和持续交付流水线
建立持续集成和持续交付流水线的核心问题是如何定义企业的软件交付价值流动。价值流动涉及各个职能团队的高度协同,在以镜像作为不同职能团队之间的价值传递物当中,其基于容器的CI/CD流水线的设计应该遵循如下一些基本原则。
可视化:流水线的运行和停止、成功与失败应对所有人直观可见,以掌握全局变化。
信息反馈:流水线应通过邮件等多种手段,及时将失败信息传达给相关团队成员。
可控制:从开发到生产发布总有一些环节需要人手动验证,比如回归测试、环境配置等,也就需要流水线在某些环节可以暂停,等待手动继续。
门禁:流水线中任何一个环节失败,都应该让流水线停下来,并通知团队解决,比如自动化测试不通过、代码或镜像构建失败等。
持续集成和持续交付(CI/CD)流水线的建立,主要包括开发流水线、测试流水线和生产运维流水线3种,如下图所示:
(1)开发流水线
开发人员:频繁提交代码,通过持续地编译、打包、测试、镜像构建和自动化测试等环节,产生可测试的候选镜像列表(如0.1-dev)。
以源码仓库为起点,开发人员频繁提交代码,每一次代码变更都要立即在流水线中传递;Jenkins持续集成平台支持定时周期触发、代码变更检查以及Webhooks等多种触发方式。
手动测试阶段,当新的代码提交部署到环境中后,开发人员可以快速地进行手动测试,确保新提交的代码在测试环境中是可用的,并且满足相关的功能需求。
自动化测试阶段,该阶段主要是使用JUnit、Pytest、Selenium、Cucumber等自动化测试框架进行单元测试、功能测试等一系列的自动化测试,同时还可以根据需要在持续交付流水线中集成Sonarqube进行代码的质量跟踪和管理。
镜像构建,当所提交的代码通过了整个流水线的持续验证后,就会产生相应版本的Docker镜像。
基于流水线中的持续验证和代码质量数据,团队可以快速处理典型的代码质量问题,避免累积。总而言之,开发流水线可以帮助团队频繁地进行代码集成并通过单元测试、代码静态分析、自动化测试等,有效帮助开发人员及时发现和解决问题,最终输出待测试的镜像列表。
(2)测试流水线
测试人员:从候选测试镜像列表中选择需要测试的目标镜像,标记为测试版本(如将0.1-dev标记为0.1-test),并且将待测试镜像自动部署到验收测试环境中进行测试,对于测试通过的镜像标记为预发布版本(如将0.1-test
标记为 0.1-beta)。
测试阶段,从功能交付断言整个系统是能够满足需求设计或客户规范的,该阶段会进行一系列的手动或自动化测试,同时基于容器技术解决异构环境下的依赖问题。
对于测试人员而言,流水线的起点则变为待测试的镜像列表,基于Jenkins创建类似的流水线,可以支持测试人员快速创建测试环境,并且运行相关的自动化测试脚本,同时满足手动探索性测试的需求。
(3)生产运维流水线
运维人员:从预发布镜像列表中选择镜像部署到预发布环境中,在验证通过后标记为release版本(如将0.1-beta
标记为 0.1-release),最后发布到生产环境中。
与自动化测试流水线相同,运维人员可以建立独立的部署流水线,从待发布的镜像列表中选择镜像发布到生产环境Registry中,并且设置流水线的自动或者手动触发,实现预生产环境的一键部署。
2.2 基于OpenStack+Docker的应用部署
Docker产生应用镜像有两种办法:一种是启动一个基础镜像(比如基于CentOS的Linux镜像),然后在容器中执行各种命令来安装相应的软件包,进行配置后,再通过Docker
commit命令把已经更新的容器保存为相应的镜像;另一种方法则是通过编写Dockerfile文件,然后使用Docker
build命令自动化构建相应的镜像。
相比第一种手工方式,通过Dockerfile文件的方式可以更好地维护镜像,并将Dockerfile
提交到版本库进行管理。并且通过CI/CD系统的自动化build、push、pull、run等工作流,可以与研发测试业务更紧密地结合起来。维护的不再是一条条零散的命令,而是一个文件集合。这样,Dockerfile文件便如同代码一样,做到随时维护修改和协作开发。
将所有的源码存放在私有仓库GitLab中,Docker镜像则托管在私有Registry中,并使用诸如Ansible之类的自动化工具将容器部署到服务器环境中,这些都是一个完全自动化的过程。Docker典型的应用场景包括:
1.测试
Docker很适合用于标准化场景中,将Docker封装后直接提供给测试人员进行环境搭建与部署,不再需要与运维、开发人员进行协商。同时,还可以很容易地将测试数据与应用服务分离,根据不同的测试场景随时进行镜像—容器切换。
2.开发
开发人员使用同一个Docker镜像,同时修改的源代码都挂载到本地磁盘上,然后映射到容器中。不再为因为环境的不同而造成不同的程序行为而伤透脑筋,同时新人到岗时也能迅速建立起开发、编译环境。
3.PaaS云服务
Docker可以支持命令行封装与编程,通过自动加载与服务自发现,可以很方便地将封装于Docker镜像中的服务扩展成云服务,根据业务请求的规模随时增加或减少容器的运行数量,实现弹性伸缩和横向扩展。
2.3 基于OpenStack+Docker的CI/CD流程设计
构建基于OpenStack+Docker云平台研发测试用的CI/CD系统,其核心组件包括:Jenkins持续集成系统、GitLab代码仓库管理系统、Harbor私有镜像仓库管理系统、Gerrit代码评审系统等。
基于OpenStack+Docker的CI/CD任务分解和流程设计步骤如下图所示。
①开发者准备好一个单节点环境,将开发工具链接到远程开发目录,并使用Git将代码提交到代码评审系统Gerrit中,目的是通过协作发现一些明显的问题,减少把Bug带到软件中的概率。
②当Jenkins持续集成系统检测到Gerrit系统的代码提交事件后,触发相关的Job任务,自动化执行代码编译、打包、构建、部署和测试等工作流。相应地,会执行如下任务:
执行源码编译、打包,如RPM、WAR包等。
构建Docker镜像。
部署环境,如使用Kolla自动化部署OpenStack。
自动化运行测试,如单元测试、集成测试等。测试结果有两种,一种是测试失败,流程返回到步骤1;一种是测试成功,流程继续。
③根据测试结果和其他信息综合决定此次开发人员提交的代码是否合并,这样保证只有通过了测试和审核的代码才能合并到GitLab仓库中。
④ GitLab的Webhooks会触发Jenkins系统中的两个构建任务,一个是源码编译、打包任务;一个是源码打包后的Docker镜像构建任务。Docker镜像构建后,Jenkins系统会自动将镜像推送到私有Registry仓库中。整个流程如下图所示:
围绕以Jenkins系统为中心的自动化测试、集成和发布平台,开发人员可以经常集成自己开发的代码,而每次集成都要通过自动化构建来验证,包括自动化编译和测试,从而尽快地发现集成错误。
同时,以GitLab作为自托管的Git项目仓库平台,配合代码评审系统Gerrit,在代码合并前进行审查。一个团队内的参与者,可以相互审阅各自修改后的代码,决定是否能够提交,或者退回继续修改。
这些应用系统的实现可以满足OpenStack研发测试的日常需求,这样,开发人员和QA测试人员就不需要关心从何处获取代码,寻找环境来做软件打包、环境部署、验证测试等了。相关人员只需要分别编写好各自负责的那部分代码,然后关联到相应的环境中即可。
首先,以Jenkins为核心的任务调度系统,自动触发代码编译、构建和打包;然后,GitLab和Gerrit协作完成代码及软件仓库的管理。由于OpenStack环境的部署对相关资源服务依赖性较强,因此,需要使用自动化部署管理工具。这里选择使用Kolla方式,实现高效的配置管理和持续部署,从代码打包、Docker镜像构建、部署实施到验证测试等环节,自动化实现代码持续集成的一系列任务。
Docker是一种标准化的开发、测试、部署和运维方式,通过Docker镜像,我们可以在开发、测试、线上等各个阶段无差别地使用服务,可以在本地端,也可以在云端运行程序。
很多企业内部都有一套标准化规范,在这套规范中定义了开发所使用的语言、框架、软件包版本及依赖环境等,这样做可以统一开发环境并解决因差异化带来的其他问题。基于此,我们可以根据不同的服务进行逻辑或物理上的分组,如下图所示:
在上图中,把Docker镜像分为了三层:基础镜像层、服务镜像层和应用镜像层。上层镜像的构建依赖于下层镜像,越下层的镜像越稳定,也越不会经常变更。
基础镜像层:负责安装最基本的、所有镜像都需要的软件及环境,例如操作系统等。
服务镜像层:负责构建符合企业标准化规范的镜像,这一层很像SaaS,例如Python环境、某个项目的公共软件包等。
应用镜像层:负责部署和运行应用程序,这个阶段是CI的产出物,例如rpm包、Python源文件等。
分层后,由于下层镜像已经提供了应用所需要的全部依赖环境,因此可以显著加快应用镜像层构建的速度。对于容器而言,它只操作变更的部分,已经存在且没变化的内容则不做任何操作。
典型地,在Dockerfile文件中使用FROM命令构建基础镜像层,如选择CentOS 7作为所有容器的基础操作系统;依据标准化规范,安装Nova计算项目的公共软件包,构建服务镜像层;最后安装指定的软件包,构建应用镜像层。
glance-base镜像的Dockerfile文件内容如下:
FROM {{ namespace
}}/{{ image_prefix }}OpenStack-base:{{ tag }}
MAINTAINER {{ maintainer }}
{% block glance_base_header %}{% endblock %}
{% import "macros.j2" as macros with
context %}
{% if install_type == 'binary' %}
{% if base_distro in ['centos', 'fedora', 'oraclelinux',
'rhel'] %}
{% set glance_base_packages = [
'OpenStack-glance',
'Python-rbd',
'Python-rados'
] %}
{% elif base_distro in ['ubuntu'] %}
{% set glance_base_packages = [
'glance',
'Python-rbd',
'Python-rados'
] %}
{% endif %}
{{ macros.install_packages(glance_base_packages
| customizable("packages")) }}
{% elif install_type == 'source' %}
{% set glance_base_packages = [
'Python-rbd',
'Python-rados'
] %}
{{ macros.install_packages(glance_base_packages
| customizable("packages")) }}
ADD glance-base-archive /glance-base-source
RUN ln -s glance-base-source/* glance \
&& useradd --user-group glance \
&& /var/lib/kolla/venv/bin/pip --no-cache-dir
install --upgrade -c requirements/upper-constraints.txt
/glance[cinder] \
&& mkdir -p /etc/glance /var/lib/glance
/home/glance \
&& cp -r /glance/etc/* /etc/glance/
\
&& chown -R glance: /etc/glance /var/lib/glance
/home/glance
{% endif %}
COPY glance_sudoers /etc/sudoers.d/kolla_glance_sudoers
COPY extend_start.sh /usr/local/bin/kolla_extend_start
RUN usermod -a -G kolla glance \
&& chmod 750 /etc/sudoers.d \
&& chmod 440 /etc/sudoers.d/kolla_glance_sudoers
\
&& touch /usr/local/bin/kolla_glance_extend_start
\
&& chmod 755 /usr/local/bin/kolla_extend_start
/usr/local/bin/kolla_glance_extend_start
{% block glance_base_footer %}{% endblock %}
{% block footer %}{% endblock %}
{{ include_footer }} |
glance-api镜像的Dockerfile文件内容如下:
FROM {{ namespace
}}/{{ image_prefix }}glance-base:{{ tag }}
MAINTAINER {{ maintainer }}
{% block glance_api_header %}{% endblock %}
COPY extend_start.sh /usr/local/bin/kolla_glance_extend_start
RUN chmod 755 /usr/local/bin/kolla_glance_extend_start
{% block glance_api_footer %}{% endblock %}
{% block footer %}{% endblock %}
{{ include_footer }}
USER glance |
|