孙科 译 分布式实验室
在CodePicnic,Docker很早就成了基础设施的核心部分。从0.11版本开始,我们不但看到了Docker的成长,更看到了一整个相关生态的建立。
与此同时,CodePicnic也在发展,我们有了不同于其他基于容器平台的特性。这也导致我们开始考虑在可扩展并且控制成本的前提下,(使之成为)一个稳定且高效的平台。
我们的应用负责以一种简单高效的方式来管理容器平台。通过一系列规则,节点(装有Docker引擎的AWS EC2实例)被选择来部署新的容器。另一方面,当需求上升时,我们会通过AWS终端来增加节点,并提供给新客户。为此,我们开始寻找一些容器编排系统来替换我们自身的解决方案。
我们首先选择的是Kubernetes,这是一个由Google开发的编排平台。
对于管理Docker集群而言,Kubernetes是一个复杂但是健壮的方案(可以选择自动扩展节点、容器、监视器、日志等)。
Kubernetes使用Pod作为容器的通讯层。这就意味着,我们的应用和Docker通讯的方式发生了改变。这里最大的挑战在于,让我们的架构适应Kubernetes的哲学。这可以用书中的短语(http://kubernetes.io/docs/user-guide/services/)来概括:
Kubernetes Pod终有一死。它们生或死,并不会重生。
我们的平台依赖于持久化组件,这个事实促使我们寻找一个替代方案。
和Kubernetes相比,Docker Swarm是简单的:它为一组Docker引擎提供集群能力,并且它能在单个引擎中管理容器的编排。你可以向Swarm添加自己的Docker基础架构,并且它们的API和Docker的API是兼容的,因此我们并不需要对应用有较大的变动以开始使用它来管理容器。
如此一来,我们就有了独特的关于Swarm Master的接口,而不是管理N个Docker实例。Swarm作为容器化的应用运行,并负责调度在哪里部署新的容器。策略、约束和亲和性共同决定了哪一个节点会被选择。
虽然迁移简单,但是让Docker Swarm管理集群中成千上万的容器和镜像会有性能问题,这使得我们修改Swarm的源码以优化容器的创建时间并避免调度器上的并发锁。在未来的文章中,我们将会继续讨论我们的优化。
编排和调度工作得不错,但是监控和扩展性并不是方案的一部分。
我们的基础架构工作于AWS上,AWS本身已经提供了这些问题的解决方案。因此,我们开始将系统和容器的度量数据发往AWS Cloudwatch。当这些数据超过阈值后,一个警告就会被发往AWS Auto Scaling,它会提供一个或一个以上的新实例。一旦一个实例启动,它便会在Docker Compose的协助下,自动注册到Docker Swarm。
另一个集成进我们架构的Docker特性是多host网络。这个特性使得AWS弹性负载均衡能够和所有容器通信,无论它们在哪个节点上。
Docker v1.1包含了一个嵌入DNS,它允许提供基于容器名和别名的服务发现。在早期实现中,我们需要在host上为新创建的容器开启端口。这样,应用就能和容器通信以告诉终端用户在哪里工作。如此实现存在几个缺点,我们需要记录端口的使用,并且端口数量也受每个节点自身的限制(大约65535或更少一点)。如今,我们有Nginx容器在内部将请求路由到正确的容器和端口,这样就不需要通过节点开放多端口了。
围绕着这个基础设置,我们添加了额外的组件来自动管理任务。我们用来提供给不同终端的所有基础镜像(Ruby、Python、PHP、Swift等)都存储在Docker Registry。
我们使用Jenkins来构建镜像并上传到Registry。我们通过Jenkins使用的Ansible脚本,来更新AWS Auto Scaling提供新实例时所使用的AWS镜像。
关于监控方案,cAdvisor运行在所有的节点中以收集来自容器的状态。我们使用InfluxData来收集度量信息,例如正在运行的容器,执行Docker命令的时间消耗等。这些度量信息反过来由Grafana显示。
想象一下!未来会如何?我们已经尝试了新的Docker发行版,但是恶心的bug使得我们无法升级自己的平台,因此我们目前正在等待Docker v1.12以及未来的发行版。我们开始向自己的Swarm集群添加基于Windows的Docker引擎。不久前,Docker发布了Swarmkit(https://github.com/docker/swarmkit),这是一个用于提供Kubernetes类似特性的编排平台。
正如我们之前说过,Docker和我们一样正在发展。我们将会继续添加最好的工具来改进我们的平台,并向用户尽可能提供更好的体验。