大大丶 2017-06-16
随着容器技术的流行,作为线上应用 Docker 的铺垫,喜马拉雅 FM 从 2016 年开始推进测试环境的 Docker 化。本文重点介绍笔者在 Docker 化的过程中如何进行技术选型、环境搭建,和实践中碰到的一些问题及其解决方案。
为什么要Docker化?
1.标准化
2.API 化
通过API接口操作项目的部署(CPU、内存分配、机器分配、实例数管理等),而不是原来物理机环境的的手工命令行操作。
3.自动化
调度系统可以根据API进行一些策略性的反应,比如自动扩容缩容。
上述工作,原有的技术手段不是不可以做,可是太麻烦,可用性和扩展性都不够好。
Docker 化的四个小目标
1.业务之间不互相干扰
2.容器之间、容器与物理机之间可以互通
3.容器编排:健康检查、容器调度等
4.使用方式:通过 yaml / json 来描述任务,通过 API 部署
总结一下,基于 n 台物理机搭建容器环境,整个工作的主线:
一个项目一个主机 ==>物理机资源不够 ==>虚拟化 ==>轻量级虚拟化 ==> Docker ==>针对 Docker 容器带来的网络、存储等问题 ==>集群编排 ==>对 CI / CD 的影响。
虚拟化网络的两种思路
Overlay
通常需要额外的维护物理机以及物理机上容器 IP(段)的映射关系。
Underlay
不准确的说,容器的网卡暴露在物理网络中,直接收发,通常由外部设备(交换机)负责网络的连通性。
经过对比,我们采用了 MacVLAN,主要是因为:
IP 分配问题
对于物理机、KVM 等虚拟机来说,它的生命周期很长,IP 一经分配便几乎不变,因此通常由人工通过命令或 Web 界面手动分配。而对于 Docker 容器来说,尤其是测试环境,容器的创建和销毁非常频繁,这就涉及到频繁的 IP 分配和释放。因此,IP 分配必须是自动的,并且有一个 IP 资源池来管理 IP。
在 Docker 网络中,CNM(Container Network Management)模块通过 IPAM(IP address management)driver 管理 IP 地址的分配。我们基于 TalkingData/Shrike 改写了自己的 IPAM 插件,fix 了在多实例部署模式(一个 Docker host 部署一个 IPAM,以防止单实例模式出现问题时,整个系统不可用的问题)下的重复存取问题。
Docker之外的编排工具
Docker 解决了单机的虚拟化,但当一个新部署任务到达,由集群中的哪一个 Docker 执行呢?因此,Docker 之外,需要一个编排工具,实现集群的资源管理和任务调度。
这些工具均采用 Master / Slave 架构,假设我们将物理机分为 Master 和 Slave,这些工具在 Slave 上运行一个 Agent(任务执行和数据上报),在 Master 上运行一个 Manager(任务分发和数据汇总)。从功能上说,任务分发和容器资源汇总,这些工具可以满足要求。它们的根本区别就是:发展历程的不同。
到目前为止,根据喜马拉雅FM测试环境的实践,发现有以下特点:
因此我们决定使用 Marathon + Mesos 方案。在后面实践的过程中,因为网络和编排工具的选择,IP 变化的问题带来很大的困扰,我们甚至专门开发了几个小工具,参见下文。
image 的组织
Docker 的厉害之处,不在于发明了一系列新技术,而在于整合了一系列老技术,广为人知的阿里、腾讯等大厂在 Cgroups、Namespace 等基础上搞了一套自己的容器工具。对于阿里,使用 Docker 初衷是 Docker 镜像化,也就是其带来的应用环境标准化,而不是容器化。
Docker镜像的实践主要涉及到以下三个问题:
1.搭建私有 imagerepository
2.对 layer 进行组织
3.镜像的分发较慢
4.镜像化带来容器重启
因为镜像是一体的,哪怕只有一点更改,镜像的发布都必须销毁之前的容器,然后按照新镜像创建新容器。耗时是一方面,对以下场景也很不友好:
对于具体的场景,可以有具体的办法规避。对于通用的解决方案,阿里通过改写 Docker,对镜像支持 HotFix 标识,deploy 这类镜像,不再创建新容器,而是更新容器。
我们要对镜像的 layer 进行组织,以最大化的复用 layer。
因为只是在测试环境使用,镜像较慢的矛盾还不是太突出。这里有一个技术之外的问题。阿里对于 docker image feature 的改造,实现了如下优化:
CI
Jenkins 如何跟 Marathon 结合,现成的方案很多。关键是提供几套不同的模板,方便不同业务的使用。
容器变化带来四大问题
使用 Docker 后,容器在物理机之间自由漂移,物理机的角色弱化成了:单纯的提供计算资源。但带来的问题是,影响了许多系统的正常运行。
1.IP 变化
许多系统的正常运行依赖 IP,但 IP 不稳定带来一系列的问题。而解决IP的变化问题主要有以下方案:
既然重启后,IP 会改变,就减少容器重启
服务与 IP 绑定(这个方案非常不优雅)
对于 Web 服务,IP的变化导致要经常更改 Nginx 配置,为此,我们专门改写了一个 Nginx 插件来解决这个问题。
对于 RPC 服务,技术团队有独立开发的服务治理系统,实现服务注册和发现。但该系统有审核机制,对于跨语言调用,因为rpc客户端不通用,仍有很多不便。
2.文件存储
有许多项目会将业务数据存储在文件中,这就意味着项目 deploy 进而容器重启之后,要能找到并访问这些文件。在 Docker 环境下主要有以下两种方案:
我们当下主要采用第一种,将 cluster fs mount 到每台 Docker host 的特定目录(例如 / data),打通 container / data ==> docker host / data == cluster fs / data,任意容器即可共享访问 /data 目录下的数据。
3.日志采集与查看
为了日志持久化存储,技术将容器的日志目录映射到了物理机上。but,一个项目的日志分散在多个物理机中。
原有的日志采集报警系统,负责日志采集、汇总、报警。因此容器化后,并不会有什么影响。但该系统只采集错误日志,导致开发人员要查看日志以调试程序时,比较麻烦。
最初,提供了一个 Web Console 来访问容器,操作步骤为:login ==> findcontainer ==> input console ==> op。
但依然过于繁琐,并且 Web Console 的性能也不理想。而直接为每个容器配置 ssh server,又会对 safe shutdown 等产生不良影响。因此:
当然,日志的问题,也可以通过 elk 解决。
其他问题
1.Base image 的影响
2.Safeshutdown,部分服务退出时要保存状态数据
3.支持 sshd,以方便大家查看日志文件
使用 supervisord(管理 SSH 和 Tomcat),需要通过 supervisord 传导 。SIGTERM 信号给 Tomcat,以确保 Tomcat 可以 safeshutdown。该方法经常发生 supervisord 让 Tomcat 退出,但自己不退出的情况。
每台机器启动跟一个专门的容器,映射一些必要的 volume,以供大家查看日志等文件。
4.Marathon 多机房主备问题
5.容器的漂移对日志采集、分析系统的影响
6.对容器提供 DNS 服务,以使其可以正确解析外部服务的 hostname
7.如何更好的推广与应用的问题
todo
1.日志采集,简化日志搜索
2.一个集中式的 DC
当下,项目部署的各个阶段分散在不同的组件中。呈现出来的使用方式,不是面向用户的。
这样带来的问题是:
对于运维人员来说,一些操作不能固化下来,比如回滚等,手工操作易出错。
对于用户来说,容易想当然的通过 portainer 进行增删改容器的操作,进而引起系统的不一致。
因为是现成系统,很难加入技术自己的逻辑,这使得配置上经常出现一些语义冲突的情况。
李乾坤
喜马拉雅 FM Java 开发工程师