容器调度 • Docker网络 • 持续交付 • 动态运行应用程序 部署的多元化

《英雄联盟》在线服务运维之道 - InfoQ https://www.infoq.cn/article/running-online-services-riot/

第一章 简 介

我是Jonathan McCaffrey,来自Riot公司的基础设施团队。在讲述我们 如何进行后端应用的部署和运维之前,首先要先了解一下我们是如何看待 我们的应用开发的。游戏玩家的价值对于Riot来说是至高无上的,我们的 开发团队经常与玩家社区互动,为了给玩家们提供最佳的游戏体验,我们 必须具备根据玩家反馈快速做出变更的能力。基础设施团队的使命就是为 开发人员提供支持,我们提供支持的力度越大,玩家们就能越快体验到最 新的功能。

当然,说起来容易做起来难!因为部署的多元化,我们面临着很多 挑战。我们有公有云服务器,也有本地数据中心,还要与腾讯和Garena 合作。这些环境的复杂性给开发团队增加了不少负担,于是就有了基 础设施团队。我们使用基于容器的内部云来简化部署,这个云叫作 “rCluster”。在这篇文章里,我将介绍Riot从手动部署到使用rCluster进 行部署的演变过程。为了更好地描述rCluster,我将以海克斯工匠系统 (Hextech Crafting System)作为例子。

历史背景简述

我在七年前加入Riot,当时我们并没有那么多部署流程和服务器管理 流程,我们还只是一个怀揣大梦想的初创公司。我们的预算很有限,做什 么事情都要求快。我们为《英雄联盟》构建了一套基础设施,努力满足游 戏不断增长的需求,为开发团队提供支持,同时还要支持区域团队拓展新 的疆域。我们通过手动配置服务器和应用程序,没有太多指南和战略规划 之类的东西。

后来,我们使用Chef来完成常见的部署和基础设施任务,使用公有云 进行大数据计算和提供Web服务。在这一过程中,我们多次重新设计了我 们的网络,也更换过供应商,甚至重组了整个团队结构。

我们的数据中心有数千台服务器,每新增一个应用程序就要添加新的 服务器。新服务器被接入手动创建的虚拟局域网,并手动配置路由和防火 墙规则来提升安全性。虽然这样可以提升安全性,但费时又费力。因为这 个痛点的存在,当时的新功能都被设计成小型的Web服务,导致《英雄联 盟》生态系统的服务数量大肆膨胀。

除此之外,我们的开发团队对应用程序的测试环节缺乏信心,在进行 部署时经常出现一些配置或网络连接问题。因为应用程序与物理基础设施 的耦合太过紧密,以致于生产环境与测试环境、Staging环境和公测环境 难以保持一致。

我们的应用程序数量一直在增长,我们也在经历着手动配置服务器 和网络的艰难过程。与此同时,Docker开始在我们的开发团队中流行起 来,用于解决配置和开发环境一致性问题。我们开始意识到,我们可以用 Docker做更多的事情,它可以在基础设施方面扮演一个更重要的角色。

2016 全明星赛及其他

基础设施团队的目标是为游戏玩家、开发团队和2016全明星赛提供支 持。在2015年底,我们从手动部署转向了自动部署,比如海克斯工匠系统 8 InfoQ 中文站 就使用了自动部署。我们开发了rCluster——一个全新的系统,它使用了 Docker和微服务风格的SDN(Software Defined Networking)。rCluster解 决了不一致性问题和部署流程问题,让产品团队可以集中在他们的产品研 发上。

接下来我们将深入探讨rCluster是如何支持海克斯工匠系统的。

海克斯工匠系统在我们内部被称为“战利品(Loot)”,由三个核心 组件组成。

• 战利品服务——一个Java应用程序,通过基于HTTP/JSON的REST API处理战利品请求。

• 战利品缓存——基于Memcached的缓存集群,并使用了一个Go语 言开发的小型边车(sidecar)来监控、配置、启动和关闭缓存集 群。

• 战利品数据库——一个MySQL集群,包含了一个主数据库和多个 从数据库。

在进入工匠系统时,会发生以下一系列事件

1. 玩家在客户端打开工匠系统的屏幕。

2. 客户端向前端应用程序发起 RPC 调用,前端应用(也就是 “feapp”)就是玩家和后端服务之间的代理。

3. feapp向战利品服务器发起调用请求。 • feapp从“服务发现”(Service Discovery)中找到战利品服务 器的IP地址和端口。 • feapp向战利品服务器发起HTTP GET请求。 • 战利品服务器检查战利品缓存里是否保存着玩家的物品。 • 玩家物品不在缓存里,于是战利品服务向数据库发起请求,并 将返回的结果放进缓存。 • 战利品服务将结果返回给feapp。

4. feapp将RPC响应消息返回给客户端。 通过与战利品团队的合作,我们将服务器和缓存放进了Docker容器, 并使用JSON文件来定义它们的部署配置。 战利品服务器的JSON配置示例:

{
"name": "euw1.loot.lootserver",
"service": {
"appname": "loot.lootserver",
"location": "lolriot.ams1.euw1_loot"
},
"containers": [
{
"image": "compet/lootserver",
"version": "0.1.10-20160511-1746",
"ports": []
}
],
"env": [
"LOOT_SERVER_OPTIONS=-Dloot.regions=EUW1",
"LOG_FORWARDING=true"
],
"count": 12,
"cpu": 4,
"memory": 6144
}

战利品缓存的JSON配置示例:

{
"name": "euw1.loot.memcached",
"service": {
"appname": "loot.memcached",
"location": "lolriot.ams1.euw1_loot"
},
"containers": [
{
"name": "loot.memcached_sidecar",
"image": "rcluster/memcached-sidecar",
"version": "0.0.65",
"ports": [],
"env": [
"LOG_FORWARDING=true",
"RC_GROUP=loot",
"RC_APP=memcached"
]
},
{
"name": "loot.memcached",
"image": "rcluster/memcached",
"version": "0.0.65",
"ports": [],
"env": [
"LOG_FORWARDING=true"
]
}
],
"count": 12,
"cpu": 1,
"memory": 2048
}

 不过要真正部署好它们,我们还需要创建一些集群,它们需要支持南 美、北美、欧洲和亚洲的Docker。我们因此需要解决一大堆问题:

• 容器调度

• Docker网络

• 持续交付

• 动态运行应用程序

后续的章节将会详细介绍这些组件,所以在这里先简要提及。

容器调度

我们自己编写了一款叫作Admiral的软件,并把它用在rCluster系统 里,进行容器调度。Admiral与一组物理机上的Docker后台进程进行通 信,以便了解它们的健康状态。运维人员通过HTTPS发送上述的JSON 请求,Admiral则用它们了解相关容器的状态。Admiral会持续地更新集 群的状态,并在必要的时候采取相应的行动。Admiral会根据实际情况向 Docker后台进程发起请求来启动或停止容器,从而达到预期的状态。 如果容器发生崩溃,Admiral会在另一台主机上启动一个新的容器。 这种灵活的机制让服务器的管理变得十分容易,我们可以无缝地“灭掉” 它们,做一些维护工作,然后再重启它们。 Admiral与开源工具Marathon有点像,我们目前也正打算使用Mesos、 Marathon和DC/OS的替代方案。如果这样可行,我们将会在后续的文章中 分享我们的经验。

Docker 网络

在容器可以运行了之后,我们还要为战利品应用程序和系统的其他 部分提供网络连接。我们使用OpenContrail为每个应用设置私有网络,然 后让开发团队使用托管在GitHub上的JSON文件来配置他们自己的网络策 略。 战利品服务器网络配置示例:

{
"inbound": [
{
"source": "loot.loadbalancer:lolriot.ams1.euw1_loot",
"ports": [
"main"
]
},
{
"source": "riot.offices:globalriot.earth.alloffices",
"ports": [
"main",
"jmx",
"jmx_rmi",
"bproxy"
]
},
{
"source": "hmp.metricsd:globalriot.ams1.ams1",
"ports": [
"main",
"logasaurous"
]
},
{
"source": "platform.gsm:lolriot.ams1.euw1",
"ports": [
"main"
]
},
{
"source": "platform.feapp:lolriot.ams1.euw1",
"ports": [
"main"
]
},
{
"source": "platform.beapp:lolriot.ams1.euw1",
"ports": [
"main"
]
},
{
"source": "store.purchase:lolriot.ams1.euw1",
"ports": [
"main"
]
},
{
"source": "pss.psstool:lolriot.ams1.euw1",
"ports": [
"main"
]
},
{
"source": "championmastery.server:lolriot.ams1.euw1",
"ports": [
"main"
]
},
{
"source": "rama.server:lolriot.ams1.euw1",
"ports": [
"main"
]
}
],
"ports": {
"bproxy": [
"1301"
],
"jmx": [
"23050"
],
"jmx_rmi": [
"23051"
],
"logasaurous": [
"1065"
],
"main": [
"22050"
]
}
}

战利品缓存网络配置示例:

{
"inbound": [
{
"source": "loot.lootserver:lolriot.ams1.euw1_loot",
"ports": [
"memcached"
]
},
{
"source": "riot.offices:globalriot.earth.alloffices",
"ports": [
"sidecar",
"memcached",
"bproxy"
]
},
{
"source": "hmp.metricsd:globalriot.ams1.ams1",
"ports": [
"sidecar"
]
},
{
"source": "riot.las1build:globalriot.las1.buildfarm",
"ports": [
"sidecar"
]
}
],
"ports": {
"sidecar": 8080,
"memcached": 11211,
"bproxy": 1301
}
}

一旦GitHub上的配置文件发生变更,就会启动一个传输作业,调用 Contrail的API来创建和更新应用程序的私有网络策略。 Contrail使用了一种叫作叠加网络(Overlay Network)的技术来实现私 有网络。在我们的系统里,Contrail在计算机主机之间启用了GRE通道, 并使用网关路由器来管理流经通道的流量。OpenContrail系统的灵感来自

 于标准MPLS L3VPN,所以在概念上与之非常相似。

在实现这个系统时,我们必须解决一些关键性挑战:

• 集成Contrail和Docker;

• 允许rCluster之外的网络无缝地访问叠加网络;

• 允许不同集群之间的应用发生交互;

• 在AWS上运行叠加网络;

• 在叠加网络上构建高可用的面向边缘的应用。

持续集成

战利品应用程序的CI流程类似下面这样:

 我们的主要目标是这样的:一旦主仓库发生变化,就会创建一个新的 应用容器,并将其部署到QA环境中。有了这个流程,我们的团队就可以 快速迭代他们的代码,并看到代码实际的运行情况。紧凑的反馈闭环加快 了改进用户体验的速度,这也正是Riot的关键目标——以玩家为中心,为 玩家提供最佳体验。

动态地运行应用程序

到目前为止,我们谈论了我们是如何构建和部署应用程序的,但如果 你曾经在这样的容器环境里工作过的话,你就会知道,我们要面临的挑战 远不止之前提到的那些。

在rCluster里,容器有动态的IP地址,而且一直在启动和关闭。这与之

前提到的静态服务器的部署方式是完全不一样的,所以我们需要新的工具 和流程。

一些关键问题:

• 如何监控容量和端点一直在变化的应用?

• 如果端点一直在变化,那么应用程序如何才能知道其他应用程序 的端点是什么?

• 如果不能通过ssh连接到容器上,而且容器日志在重启之后就会消 失,那么该如何进行问题诊断?

• 如果我们是在构建阶段预热(bake)容器,那么如何配置数据库 密码,或者如何为其他地区的容器配置不同的参数? 为了解决这些问题,我们开发了一个微服务平台,用于解决服务发 现、配置管理和监控问题,我们将在其他详述该平台的细节,以及它为我 们解决了哪些问题。

原文地址:https://www.cnblogs.com/rsapaper/p/13637453.html