运满满自开始微服务改造以来,线上线下已有数千个微服务的 Java 实例在运行中。这些 Java 实例部署在数百台云服务器或虚机上,除少数访问量较高的关键应用外,大部分实例均混合部署。
这些实例的管理,采用自研平台结合开源软件的方式,已实现通过平台页面按钮菜单执行打包、部署、启动、停止以及回滚指定的版本等基本功能,取得了不错的效果。但仍然存在如下几个痛点:
运满满飞速发展的业务,对系统稳定性的要求越来越高,我们急需解决如上问题。
最初吸引我们的是容器技术良好的隔离和水平扩展等特性,而 Docker 的口碑以及几年前参与的一些 Docker 项目经验,使得采用 Docker 容器技术成了我们的不二选择。
但我们仍然需要一套容器编排系统,来实现自动化管理 Docker 容器,大致了解下来有 3 个选项:Kubetnetes(K8S)、swarm、mesos
这 3 个我们都不熟悉,而这个项目的节奏很紧迫,不允许我们对这 3 个系统深入了解后再做选择。好在 Github 有一个统计功能,我们在 Github 上查到了这 3 个开源项目的一些基本情况,如下图:
根据这份统计数据,以及拥有 Google 公司的光环,我们在很短的时间内确定了使用 K8S 作为容器编排管理系统。K8S,这个开源项目号称可以自动部署、扩展和管理容器应用,并且能解决如下核心问题:
负载均衡- 一个应用运行多个同样的容器,内部 Service 提供了统一的访问定义,以负载均衡的方式来提供访问。
服务发现- Service 和 Kube-DNS 结合,只需要通过固定的 Service 名称就可以访问到对应的容器,不需要独立寻找使用服务发现组件。
高可用- K8S 会检查服务的健康状态,发现异常时会自动尝试重新启动服务,保障正常运行。
滚动升级- 在升级过程中 K8S 会有规划的挨个容器滚动升级,把升级带来的影响降低到最小。
自动伸缩- 可以配置策略当容器资源使用较高会自动增加新的容器来分担压力, 当资源使用率降低会回收容器。
快速部署- 编写好对应的编排脚本, 可以在极短的时间部署一套环境。
资源限制- 对程序限制最大资源使用量避免抢占资源遇到事故或压力也能从容保障基础服务不受影响。
进一步深入了解 K8S 之后,我们大致确定了会用到如下组件、相关技术和系统:
从下图中可以看到 2 个明显的变化:
当前业务应用主要分为 2 种,仅供内部应用调用的 RPC 服务(Pigeon 框架)和对外提供服务的 REST API,REST API 可进一步细分为 2 种,已接入 API 网关和未接入 API 网关。其中 RPC 服务和已接入 API 网关的应用均有自己的注册中心,迁移步骤相对简单,在 K8S 集群中启动对应的应用即可。未接入 API 网关的应用采用 K8S Ingress 插件提供对外服务入口,需要一些配置。系统架构如下图,最终目标是要实现将图中下方的两个框内的应用全部迁入 K8S 集群中。
由于公有云的限制,我们主要结合服务商提供的 SLB 来实现,示意图如下:
由于集群内 POD 的 IP 地址动态变化,我们采用 Traefik+Ingress+Nginx+SLB 的方式,来提供一个对外服务的统一入口。Traefik 根据 HTTP 请求的域名和路径路由到不同的应用服务,Nginx 则执行一些复杂的诸如 rewrite 等操作,SLB 提供高可用。架构示意图如下:
为了实现同一个镜像可以兼容运行在 DEV、QA、Production 等各种环境,必须编写一个初始化脚本,该脚本被存放在镜像中。当容器启动时,从 Env 变量中读取当前所在的环境,并创建一系列软链到各环境对应的配置文件以及设置日志目录等其他初始化操作,随后 fork 一个新进程用于检测和设置该容器内应用是否已完成正常启动(配合容器 readiness 探针使用),同时调用应用启动脚本。
下图为容器内通过软链指向不同的环境配置文件:
下图为容器内通过软链设置日志目录:
当前应用日志均以文件形式存放,且单个实例对应多个日志文件,无法采用 K8S 官方推荐的日志方案。同时由于容器的无状态化,我们必须另想其他办法保存日志。目前采用的是将 Node 上的固定目录作为存储卷挂载到容器内,在容器启动时通过初始化脚本按照应用名 + 容器 IP 生成该容器特定的日志路径。为了便于查看日志,我们提供 3 种途径:
下图为 Node 服务器上的日志目录结构:
下图为 Node 服务器上共享的日志下载路径:
采用 Heapster+InfluxDB+Grafana 组合,需要注意的是其中 InfluxDB 用于存放监控数据,需要将数据持久化。在 Grafana 上制作了不同维度的 dashboard,可根据 Namespace、Node、应用名进行检索,可按照 CPU、内存、网络带宽、硬盘使用量筛选应用,方便故障排查和日常优化。(当然,更好的监控系统是 Prometheus,已经在上线的路上。)
下图为监控大盘:
下图为监控菜单:
下图为某应用的监控图:
Harbor 我们目前采用的是一主多从结构,主库与打包 Jenkins 都在线下网络中,镜像上传到主库后会被自动同步到线下另一个从库以及线上的从库中,如下图所示:
我们的规划是构建一颗镜像树,所有的应用都基于这颗树上的基础镜像来构建应用镜像,各应用构建时选择最相似的基础镜像,再增加应用的特殊需求即可。基于此镜像树,我们 95% 以上的应用均无需在 Gitlab 里放置 Dockerfile,Dockerfile 在打包时根据变量自动生成即可,例如:
下图为脚本自动生成的某应用 Dockerfile:
镜像树结构示意图如下:
容器化:DEV/QA 环境的应用已完成 Docker 化,产品环境中应用约 98% 已完成 Docker 化。
系统自愈:应用 OOM 或其他 Crash 时,系统能够自动拉起新的节点以替换故障节点,高级健康检查暂未开启(需其他方面配合)。
弹性伸缩:关键应用全部开启弹性伸缩,访问量高峰期观察到的效果很好。
滚动发布:可按指定的比例分批次部署更新应用版本,先更新一批,成功后销毁一批,依次滚动。
快速回滚:当前仅支持单应用快速回滚,后期如需要增加事务级回滚能力,采用 K8S 的 rollout 功能可以方便实现。
作者简介
王春林,就职于运满满技术保障部,关注容器、DevOPS 等领域。