Docker容器技术在Snowball的技术实践
Global是投资者交流的社区。用户可以买卖股票、出售基金和其他金融衍生品。同时还可以通过雪盈证券在上海、深圳、香港等地进行交易。 、美股交易。
为什么要引入Docker
随着业务的发展,影响不同社区业务的可能性越来越大,所以我们期望每个业务不被打扰,又能够利用资源。根据监管要求,机器、甚至网络都进行了不同程度的隔离。
从2014年开始,我们发现容器技术具有镜像小、灵活、启动速度快等特点,其性能更适合我们当时物理机很少的大规模环境。
相比之下,传统虚拟化技术不仅实施成本较高,而且性能损失超过10%。因此,基于对镜像大小、启动速度、性能损失和隔离需求的充分考虑,我们选择了两种容器引擎:LXC和Docker。
我们将MySQL等有状态服务放在LXC中;并将在线业务等无状态服务放入 Docker 中。
如何使用容器
如您所知,Docker 作为一个独立的软件出现。最初的口号是:构建/运输/运行。
因此最初的工作流程(过程)是:
- 首先在主机 Host 上构建 Docker。
- 然后使用 Docker Pull 从镜像仓库拉取镜像。
- 最后,使用 Docker Run,您将拥有一个正在运行的容器。 ?他们之间的网络没有连接。因此,早期谈论最多的就是如何解决网络连接问题。
- 多节点服务部署和更新。推出容器解决方案后,我们发现由于性能损失相对较小,节点数量会出现爆炸式增长。
因此,常常会出现一台物理机上运行几十个节点的情况。容器节点的绝对数量将比物理节点的数量大一个数量级,甚至更多。那么很多节点的服务部署和更新直接导致工作量的增加。 - 监控,同时我们需要针对很多节点的运行状态使用合适的监控方案。
Snowball 中的 Docker 技术培训
网络模式
首先给大家介绍一下最初的网络方案:上图左边,我们默认使用 Docker 的 Bridge 模式。
如您所知,Docker 默认会在物理机上创建一个名为 docker0 的网桥。
当创建一个新的Container时,它会相应地创建一个veth,然后连接到容器的eth0。
同时,每个veth都会被分配一个子网中的IP地址,以维持与同一主机上的各个容器的通信。
因为生产环境中网卡不止一张,所以我们进行了更改。我们生成一个“网卡bond”,即创建了bond0网卡。我们通过创建 br0 桥来替换原来的 docker0 桥。
在bridge br0上,配置的网段与物理机所在网段相同。由于容器和物理机在同一网段,因此核心上行交换机可以看到不同容器和主机的MAC地址。这是第 2 层网络互操作性的解决方案。 ?差别很小。
由于底层网络连接的原因,实现网络隔离也比较复杂。
业务部署
对于业务部署,我们遵循虚拟机的做法。一旦我们启动容器,我们就不会停止。因此:
- 如果需要添加节点,我们只使用Salt来管理机器配置。
- 如果某个节点需要更新,我们使用Capistrano来部署服务,部署多个节点,更改容器中的业务程序。
其中,优点是:
- 与原有基础设施相比,迁移成本非常低。因为我们复用了原来的基础设施,把多个服务直接部署在原来的物理机上,所以我们很容易迁移到容器上。对于开发者来说,他们看不到容器层,就像使用原始的物理引擎一样,没有“违抗感”。
- 与虚拟机相比,启动速度更快,运行过程中不存在虚拟化损失。
- 最重要的是满足我们一定程度的隔离需求。
缺点是:
- 迁移扩展过于复杂。例如:当服务需要扩展时,我们需要有人登录物理机,生成并启动一个空容器,然后将服务附加到上面。这样效率较低。
- 缺乏对多个历史版本进行管理和维护的集成平台。我们需要记录整个机房的容器数量以及每个容器的IP/MAC地址,因此出错的可能性非常高。
- 缺乏流程和权限控制。基本上我们采用原有的管理和控制方法。
自研容器管理平台
面对以上缺点,我们需要开发自己的容器管理平台来管理各种物理机、容器、IP和MAC地址,并进行流程控制。
因此,整个发布流程发生了变化:
- 开发者将代码提交到代码仓库(如Github)。
- 触发Hooks构建镜像,并在构建过程中做一些CI(持续集成),包括静态代码扫描和单元测试。
- 将报告附加到图像信息并保存在图像存储库中。
- 部署测试环境。
- 低流量上线。上线后,运行一些自动 Diff API 测试以确定它是否可用。
- 保持完全在线。
镜像构建
通过容器管理平台,会自动创建镜像构建。与业内其他公司一样,我们也使用基于通用操作系统的镜像。
然后将我们公司专门使用的包添加到镜像中,得到通用的基础镜像,然后添加不同的语言依赖,得到不同的镜像。
每个业务版本发布,将代码插入相应的语言镜像即可得到业务镜像。构建图像时,应注意避免不必要的图层和内容,这将有助于提高存储和传输效率。
系统依赖
我们完整的解决方案包括开源项目和围绕它们的技术:
负载均衡
由于节点经常添加和删除,我们如何调度和调度?自动集成负载均衡的服务发现怎么样?对于非HTTP协议的RPC,如何自动安全地移除节点?
我们这里使用Nginx+Lua(即OpenResty)来实现逻辑并动态更改Upstream。
当节点启动时,我们可以自动注册并加入;并且当节点被破坏时,也可以及时删除。
同时我们内部使用Finagle作为RPC框架,通过ZooKeeper实现服务发现。
日志收集
由于节点数量较多,我们需要收集各种日志。这里,大致可以分为两类收集方式:
- 一类是Nginx,不容易破解代码。我们并不是试图改变日志流向,而是将其直接“打”到物理机的硬盘上。 ,然后使用Flume进行采集并发送给Kafka。
- 另一类是我们自己的业务。我们实现Log4 Appender将日志直接写入Kafka,然后从Kafka传输到ElasticSearch。
网络模式
在这个场景中,我们使用上面提到的更好的Bridge+Host模式。
监控系统
监控系统由上图所示的几个部分组成。它将Collector收集到的不同监控指标的数据传输给Graphite,Grafana可以读取Graphite信息并以图形方式展示。
同时,我们还根据内部业务适配的需要,对卡博特报警组件进行修改和定制。
在这一点上,我们的平台和虚拟机的使用有很大的不同。如上图所示,主要差异出现在编译、环境、分发、节点变更、流程控制、权限控制等方面。我们的使用更加自动化。
由于这是一个自主研发的容器管理平台,它给我们带来的直接好处包括:
- 流程和权限的控制。
- 代码版本控制和环境固化、多版本发布和镜像管理。
- 部署扩容效率提升。
但是它也存在一定的缺点,包括:
- 过程控制逻辑、机器和网络管理以及耦合程度本身都存在缺陷。因此,它并不是一个很好的架构,并不能真正做到“高内聚、低耦合”。
- 由于是自主研发产品,功能并不完善,无法自愈。无法根据新增节点自动选择物理机,也无法自动分配和配置IP地址。
Swarm 简介
2015 年,我们开始改造我们的容器管理平台。由于该平台之前是基于 DockerAPI 构建的。
Swarm 对 Docker 原生 API 提供了极好的支持,所以我们觉得一旦引入 Swarm,事前修改代码的成本将会降到最低。
那么我们如何替换原来的第二层网络方案呢?前面提到,我们所做的就是让容器的IP地址与物理机的IP地址相等。
所以不存在网络故障。同时我们的Redis是直接部署在物理机上的。
所以根据上图中的列表对比,我们觉得Calico方案更适合我们的业务场景。
所以我们上层使用Rolling来操作各个流程,中下层使用Swarm + Calico来管理容器和网络。
Calico 使用 DownwardDefault 模式,该模式使用 BGP 协议在不同机器之间分发路由信息。
默认情况下,Calico 是 Node 之间的 Mesh 方式,即两个 Node 之间存在 BGP 连接。
当我们在物理机上启动容器时,它会向物理机添加包含容器IP地址的路由信息。
由于同一个Mesh中存在多台物理机,每台机器都会学习到路由信息。随着我们系统规模逐渐增大,每台物理机中的路由表也会随之增大,这会影响网络的整体性能。
所以我们需要使用这种Default Downward部署模式,这样每台物理机就不必有完整的路由表,而只需一台交换机就可以继续。
我们知道,BGP会给每台物理机分配一个AS号(自治域是BGP中的一个概念),然后我们就可以为每台物理机分配相同的AS号。
为上行交换机分配另一个AS号,并为核心交换机分配第三个AS号。
采用这种方式,每台物理机只会与其所连接的交换机进行路由分配。然后当一个新节点启动时,我们可以将该路由信息插入该节点的路由表中。 ,然后通知所连接的上行交换机。
上行交换机学习到这条路由后,进一步推送到核心交换机。
总结一下,该模式的特点是:
- 节点不需要从其他物理机获知相关信息,只需要向网关发送数据包即可。因此,一台物理机上的路由表也会大大减少,数量可以维持在“一台机器上的容器数量+常数(自我调节路由)”的水平。
- 每个上行交换机应该只控制自己机架内所有物理机的路由表信息。
- 核心交换机必须保存整个路由表。而这正是性能和功能本身的体现。
当然,这种模式也带来了一些不便,即:对于每个数据流,即使目标IP不在整个网络中,仍然要一步步询问,直到核心交换机。最后,确定包裹是否确实需要丢弃。
后续演变
之后,我们也逐渐将DevOps的思想和模式引入到当前的平台中。具体包括以下三个方面:
- 通过自助流程解放运维。让开发人员创建、改进和监控自己的项目。我们只需要知道平台上每个项目所占用的资源,这样我们就可以更加专注于平台的开发和完善。
- 现在,随着 Kubernetes 基本成为行业标准,我们正在逐步取代之前使用的 Swarm,通过 Kubernetes 实现更好的调度方案。
- 支持多机房、多云环境,实现更高的容灾率,满足业务发展需求,完善集群管理。
上图展示了一种嵌套关系:在我们的每个Project中,可能有多个IDC。
并且每个IDC都有不同的Kubernetes集群。同时,在每个集群中,我们为每个项目定义了一个Namespace。
根据不同的环境,项目的Namespaces会有不同的Deployment。例如,如果我们想将部署和发布分开,我们相应地创建不同的Deployment。不同的价差预示着不同的环境。
默认情况下,流量会定向到第一个 Deployment。一旦第二个 Deployment 部署完毕并启动,我们将立即“切断”那里的流量。
同时,因为我们平台已经有了日志、负载均衡、监控等解决方案。
Kubernetes 本身就是一个比较完整的解决方案,所以我们以降低成本为原则,谨慎地过渡到 Kubernetes,并尽量保持平台兼容,以免给开发者带来“违和感”。
目前,我们只有一千多个容器和大约一百个项目。不过,我们的部署效率的提升还是很显着的。我们几十个开发者每个月可以发布2000多次以上,日交易日志量在1.5T左右。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网