天天看点

传统 Java 网站如何实现容器化?看看 Cars.com 如何玩转 Docker!

背景

传统 Java 网站如何实现容器化?看看 Cars.com 如何玩转 Docker!

Cars.com 是美国的汽车门户网站,为用户提供各种购车相关的信息和资讯,类似于国内的汽车之家。

他们之前的技术栈是比较传统的 Java Web 结构:Java 7,  Websphere/Weblogic, Oracle Linux, IBM 硬件,Oracle 数据库 。开发语言包含 Java,NodeJs,Go。随着网站宣传的投入,Cars.com 的流量也变大了。

·      3000万访问者 / 月

·      470万车辆信息

·      每天处理200万张图片请求

·      每天服务端处理1亿次请求

问题来了,流量变大之后,传统的架构显得力不从心。同时,软件交付流程也成为了瓶颈,来看看他们的痛点。

痛点

·      两套操作系统和中间件

操作系统和中间件的选型不同,各自分别包含Websphere, Weblogic, Oracle Linux, Redhat Linux, AIX等等。

·      每套运行环境需要5份

包含开发者环境,功能测试环境,集成测试环境,性能测试环境,生产环境。

·      10个 Web 应用,100多个服务需要部署。

由于环境的差异性,上线流程的复杂性,导致 Cars.com 的团队经常碰到的问题是:为什么代码在这个环境能运行,在另外一个环境不行?久而久之为团队带来大量重复的定位环境问题的工作,严重影响了开发进度。

 Cars.com 总结了他们的诉求 – 实现不可变基础设施

·      统一上线流程。

·      具备实时扩展的能力,应对高峰期流量。

·      当服务出现问题,能够自动回滚,自动恢复。

使用 Docker 实现不可变基础设施

为什么选择 Docker?

·      提供一致性,可预期的环境。

使用 Docker 可以屏蔽环境的差异,解决维护5份环境的痛点。

·      实现微服务部署。

Cars.com 底层有100多个服务,服务如果不能单独部署,会有造成构建时间长,上线周期长,排除问题困难等问题。 实现微服务的独立部署,可以加速软件上线速度,降低问题排除难度。

·      秒级故障恢复。

Docker 作为服务的载体,具备快速销毁,快速上线,快速回滚的能力。一旦线上服务出问题,可以快速回滚到上一个版本。

·      实现蓝绿部署,金丝雀发布。

快速发布环境,让一部分用户体验某些新功能,待功能被快速验证得到积极的反馈之后,再进行大面积部署。

构建一个通用 Docker 镜像

为什么需要构建通用的 Docker 镜像?

·      公司内部有很多镜像,当有新的环境需要搭建时,要把镜像从仓库一个一个都拷贝到开发者的机器。

·      多语言开发团队,包括 JavaScript,Java,Go 等等,每种语言打包的镜像体积过大,导致镜像构建,下载速度慢,影响开发效率。

经过一系列实践,Cars.com 总结出来了通用镜像的方案:

·      采用 Alpine Linux 作为基础镜像

Alpine Linux 系统的文件大小非常小,可以达到5MB。

·      轻量级的容器

    - 删除 Package Manager 里面的所有Source。

    - 在软件安装完之后,删除安装包,删除没有用到的依赖,删除临时文件。

    - 剪裁官方镜像。剪裁 Node 镜像,Node 官方镜像最新版达到了649MB,他们将这个镜像缩减到55MB。JDK 缩减到122MB,Tomcat 从357MB 缩减到181MB。

在裁剪镜像大小的实践中,Artifactory 中的 Docker info 提供了帮助,可以在 Docker info 中看到每次打包的镜像 Layer 的实际大小,通过裁剪每层 Layer 的大小,最终达到缩小镜像的目的。

传统 Java 网站如何实现容器化?看看 Cars.com 如何玩转 Docker!

仓库的需求

每个团队都有管理包的需求,开发者要管理 Jar,War,静态文件,系统 Admin 要管理不同的 Linux 发行版,每种语言都用不同的包管理系统,维护这5,6种二进制包仓库非常麻烦,他们使用了 Artifactory 作为一站式包管理平台。理由:

·      本地搭建的仓库

大大提升所有语言构建,打包的速度。

·      支持 NPM modules,Docker 注册中心,Maven,Go。

·      支持 Linux 的包管理工具(RPM,Debian)。

·      和 Jenkins/Bamboo/MSBuild/TFS 等工具集成,收集构建信息。

·      元数据支持,并且提供 Rest API。

Cars.com 通过为 Docker 镜像,War 包设置元数据,使得任何包都可以通过 Artifactory Query Language  根据元数据,过滤出符合上线条件的包,打包到镜像里,部署到环境上。

封装 Docker API – PowerTrain

      为什么要封装Docker API?

·      需要即时响应快速上线,回滚,支持滚动升级,动态绑定端口。

·      保证开发者环境里能跑的应用,在生产环境里也能跑,实现不可变基础设施。

·      声明式配置管理。

PowerTrain 的配置文件 powertrain.mk 解决的问题是将不同环境中变化的部分,作为变量,存在文件里,当需要生成某个环境时,只需将需要的变量传递给 PowerTrain,即可获取对应环境所需的依赖,实现按需搭建环境。

传统 Java 网站如何实现容器化?看看 Cars.com 如何玩转 Docker!

PowerTrain 也封装了一些 Docker 的常用操作,例如 Docker build,pull,push 等等,在这基础之上增加的一些定制化的信息,来适应灵活的镜像构建和部署。利用 Docker 的轻量级部署特性,实现快速搭建环境,快速上线服务。PowerTrain 的具体的实现可以在 Carsdotcom 的 Github 上找到源代码。

持续集成流水线

传统 Java 网站如何实现容器化?看看 Cars.com 如何玩转 Docker!

·      开发提交代码到 Bitbucket。

·      Commit 触发 Jenkins 的 Webhook, 执行构建。

·      Jenkins 执行 PowerTrain build。

·      从 Artifactory 找到对应的 War 文件,基础镜像,构建出新的镜像。

·      快速搭建测试环境,使用 Docker 作为载体运行 war 包,进行测试(功能,接口,性能) 。

持续交付流水线

传统 Java 网站如何实现容器化?看看 Cars.com 如何玩转 Docker!

·      镜像通过测试,会升级到 Artifactory 的生产仓库。

·      通过 Jenkins 触发 PowerTrain 命令,部署到生产环境。

要部署到不同的环境,只需要执行不同的 powertrain.mk 文件,就能拉取不同的依赖,部署到不同的环境。

总结

Cars.com 通过一系列的实践,完成了从传统  Java Web 应用到容器化的转型。得到的收获:

·      使用 Docker 屏蔽之前5份环境的差异,实现一次构建,处处运行,大大减少运维中的重复性工作。使用  PowerTrain 的配置文件,让上线流程脚本化,实现不可变基础设施。

·      使用 Artifactory 作为多语言的本地仓库,大大提高获取第三方依赖,和管理自研件的效率。