转载 | 从应用到平台 - 云服务架构的演进过程

本文转载自SegmentFault同名文章。作者系力谱宿云 LeapCloud 团队_云服务负责人:秦鹏。 首发地址:https://blog.maxleap.cn/archives/940

介绍

   MaxLeap早期是一家研发、运营移动应用和手机游戏公司,发展过程中积累了很多通用组件。这些组件很大程度帮公司在移动研发过程中节省了时间和成本,有没有可能以云服务的方式开放出去,创造更大的价值?延续这个思路,公司成立了云服务部门,尝试服务的商业化。

   从对内提供接口服务到对外提供云服务,经历了三个阶段发展:1.0时代,定位对内服务,为公司研发的几十款应用提供服务端功能,推送、统一用户管理等API接口,可以说是非常普通的接口服务;2.0时代,定位对外服务,除了支撑公司的移动研发以外,同时对外开放服务,提供更多的功能接口,也参考了行业的通用做法,形成了针对移动研发加速和提高运营效率的BaaS云平台;3.0时代,定位基础研发平台,工具链逐渐完整,从研发、上线、运维到运营,提供应用管理、支付、IM、推送等SaaS功能,也提供托管、发布、监控、日志等PaaS功能,逐步形成SaaS + PaaS的研发平台。

云服务概念

   常见的云服务有几种方式。

   IaaS(Infrastructure as a Service),基础设施即服务。提供云端的基础设施为主,比如提供主机、存储、网络、CDN、域名解析等功能,知名厂商有阿里云、AWS、Azure等。

   PaaS(Platform),平台即服务。提供云端的发布、数据库服务、文件存储、缓存服务、容器管理等基础存储和管理组件,自动化了程序的配置、发布、管理–Heroku、Google App Engine、Force.com等。

   SaaS(Software as a Service),软件即服务。提供云端的应用服务,ERP、HR、CRM等在线系统,每个账户或者每家公司有独立的数据存储,通过账户进行权限和访问隔离,知名厂商有Salesforce、Successfactor、Zendesk等。

   BaaS(Backen as a Service),后端即服务,起初专指针对移动端的研发提供的云服务,降低移动研发的复杂度,让开发者关注与移动端开发即可。流行的服务有几大类:综合类,Parse、Kinvey;分析类,友盟、TalkingData、神策数据;支付类,Beecloud、Ping++;IM类,环信、网易;消息类,极光、个推等。

   我们在2.0时代把自己定位于BaaS,随着功能的不断演进3.0着眼于PaaS和SaaS。

1.0 单应用架构

##背景 当时公司有几十款App需要研发和运营,每个应用功能各异,种类包括浏览器、音视频工具、社交工具、清理大师、图片存储类和手游等,门类很多、很杂。如何提高研发效率,实现一套统一的研发、管理和运营体系,是当时的主要诉求。对主要功能进行梳理之后发现,各类应用共同需要依赖的组件包括,用户体系、云参数体系、推送服务、数据存储和广告服务。

图1 1.0业务模型

   需求基本明确了,目标是快速上线,然后小版本迭代。

设计

   当时4个后端研发人员,Java出身,人少但是技术精干。结合团队情况和产品需求,决定采用如下架构,简单但给力。

图2 1.0架构

   典型的web应用架构方式,使用Nginx做反向代理和负载均衡,后面跟了多个JVM实例。每个JVM实例由Jetty作为应用服务器,提供REST接口,服务层实现具体的逻辑。DAL层对DB和缓存进行封装,提供统一的数据访问接口。Redis作为缓存方案,支持多个shard水平扩容,TPS高、性能好。Cassandra作为数据存储引擎,无中心、可水平扩展、易维护,没有专门的运维人员,对研发人员非常友好,由于没有事务场景,NoSQL完全满足当时的需求。RabbitMQ作为消息中间件方案,不同进程间通信,支持HA,支持持久化。Zookeeper用于存储基础配置信息。

小结

   这种简单的设计,有效支持了公司几十款应用的运行,日访问量达数十亿级别。统一后台基础服务和移动端SDK后,提高了移动应用的研发和上线速度,研发了用户管理、推送这些基础功能,在移动端几行代码搞定。通过控制台,能有效管理应用和配置信息。其实对于多数十多个研发人员的公司来讲,这样的单应用架构性价比最高,解决商业上的问题才是关键。

   也有不少需要改进的地方,Cassandra作为业务数据的存储,查询非常不灵活,依赖设计时对row key和composit key的精确把握,扩展非常困难,再加上对翻页、排序等支持有限,在数据层做了很多特殊处理。整个系统没有脱单,Redis、Nginx还有单点问题,脱单是高可用系统中首要需要解决的问题。所有服务部署在一起,出问题时相互影响,项目耦合度高,扩展困难。开发效率低,发布新功能时相互依赖等,这些都是单用架构设计最明显的问题。

2.0 服务化架构

背景

   随着业务不断发展以及新的产品定位,单应用架构的弊端不断暴露出来,要求我们在新的规划中,重新设计整个后端架构。

   来看新的需求:

定位公有云,服务标准化 多租户支持,用户数据需要隔离,每个公司都有自己的后台管理和账号管理,不同工作人员区分权限职责,允许同时有多款应用,应用在逻辑上相互独立,每个应用可以使用所有服务 更灵活的存储和查询服务 提供基础数据分析功能,提供代码托管等更多云服务

图3 2.0业务模型

设计

   服务规模扩大后,大家解决单应用弊端的方法也都类似,以独立运行的业务为单位,对服务纵向拆分,提高单个服务的聚合程度,降低服务间的相互依赖,从而解决降低研发、测试、上线过程中各个服务相互依赖、相互影响的问题。

   除此以外,我们对数据存储和网络层也进行了改进。平台所支持的两类服务,分别是云服务和云计算。在新的设计中,云服务部分的重点是重构、支持在新架构上快速研发新服务,云计算部分的重点是,构建起来一个稳定、可靠、高效的基础架构。

云服务部分

图4 2.0云服务架构

   网络,最外层增加了ELB,IP地址直接暴露在公网是非常危险的方式,通过DNS配置CNAME指向域名,降低了被DDOS的风险,提高了Nginx的可用性。ELB本身会在访问增加时,自动伸缩,配合IaaS厂商提供的AutoScalling服务,可以抵御多数DDOS攻击。通过Nginx作为反向代理和负载均衡,不同服务指向不同的upstram地址和端口。服务间禁止相互依赖。

   容器,每个服务都运行在Docker中,服务之间环境和资源隔离,能够快速迁移和部署,统一了交付和发布流程。每个容器无状态,服务迁移、扩容、宕机等问题迎刃而解,在服务化过程中起到很关键的作用。

   数据,NoSQL部分主要依赖MongoDB,Wired Tiger + SSD,由于MongoDB的Schema-less特性,开发者可以根据自己的需求随时调整实体的定义。天生为分布式设计,支持复制集高可用方案,支持多Shard水平扩展。关系型数据库采用MySQL,用于实现支付、订单、配置信息等需要事务支持的业务,我们基于MHA实现MySQL的高可用和读写分离。不同服务所依赖的库必须分离,从数据着手保障用户资源的隔离。

   存储/缓存,块数据依赖AWS的S3,权限控制、分桶策略比较实用,可用性有保障;缓存依然使用Redis,为了解决单点问题,对原有版本进行了升级,采用官方的3.0集群方案,支持分片和高可用,当然也有其它方案可选,比如codis等代理的方案。

云计算部分 图5 2.0云分析架构

   数据收集部分采用REST + Kafka方式,其中很重要的一部分是DataTransform,用于数据标准化、过滤、流控等用途,基于vert.x实现。

   计算部分参考Lambda架构实现,分为Speed Layer、Batch Layer、Serving Layer三层。

   Speed Layer,基于Storm实现,引擎监听Kafka中收集到的新数据,把一些对实时性要求高的指标计算完成,写入展示层。Batch Layer,基于Hadoop生态实现,通过开源项目Camus,把Kafka中监听到的数据存储在HDFS中,然后通过HIVE或者M/R的Job,计算各种指标,工作流通过oozie来调度,最终结果也写入展示层,并且覆盖掉实时计算的结果。结果数据根据不同指标,按天、周、月分别存储在Cassandra中,有很高的写速度,无限水平扩展。展示层,数据同时用antlr4j实现了SQL解析,访问Cassandra中的计算结果。

小结

   相比较1.0时期,架构上更合理,扩展性、维护性、鲁棒性都有很明显提升。功能上,完成移动应用研发大部分组件(统计分析、支付、IM、社交、在线参数、推送、营销、云数据、云存储、云代码)。应用的研发、上线、运维、运营形成闭环,顺利完成从对内服务到公共BaaS平台的升级。团队上,1-3人一个服务的研发,测试、上线的节奏可以自己把控。

   当然,业务在演进,也有新的问题需要去解决。从功能角度,Nginx只能支持静态方式设置反向代理,然后reload,而平台有服务对应的后端服务和端口是有动态调整需求。从架构角度,在相对成熟的系统中,日志、监控这些基础组件需要统一收集、管理、处理。数据访问层、RPC等基础服务也要标准化。

3.0 平台化

背景

图6 3.0业务模型

   对于创业公司而言,业务随时会有调整,作为技术人员,需要能够应对这种变化,架构上为这些变化做准备。产品向3.0演进由新的业务需求和架构自身的调整需求共同推动。新的业务需要支撑一个全网营销的SaaS产品线,产品支持配置操作生成App + 微信商城 + 手机网站,以及营销,就是需要本来专注于移动端研发定位的BaaS,向PaaS化和SaaS前进。架构上是基础组件需要进行升级,数据访问层、RPC、日志、监控系统等。于是我们提出一个目标,就是平台化,数据访问的组件以PaaS形式提供给开发者和内部团队,标准化API网关、日志、监控系统。

设计

   设计上依然延续稳定、成熟、解决问题的思路。网关服务作为所有请求的入口,稳定、性能、水平扩展是考虑的要素。数据访问层,就像一个项目的大动脉,所有的访问压力、瓶颈、安全问题会在这里汇合,如何构建一条数据访问的高速公路是我们的目标。日志和监控,虽然没有业务系统那么核心,但是关键时刻出现问题需要排查时他们很大程度会影响解决问题的效率,好的监控系统能第一时间把正确的信号通知到正确的人,而烂的监控只会为运维带来困扰。

网关

   我们设计网关的初衷是替代掉Nginx,在自研的网关上控制规则转发、主机注册、容器状态检查、负载均衡、服务拒绝、限速等功能。Nginx是非常优秀的一个软件,也满足一部分网关的功能,但是无法满足我们不断进化的需求。

   整个网关系统由规则控制组件和容器管理组件两部分组成。后的服务在启动后,向Hydra-容器管理组件,注册自己的服务、IP地址和端口信息,Hydra随后接管容器的生命周期管理,进行健康检查,Failover处理,并把相关的信息保存在Zookeeper和MySQL中。规则控制组件在请求到达后,进行规制匹配、路由转发、限制、限速等处理。

   目前网关用在了大多数服务中,下步计划是所有服务统一由网关进行管理,下图是网关系统的最终形态。

图7 3.0网关架构

数据访问

   结构化的业务数据主要存储在MySQL和MongoDB中,设计上我们增加了数据代理层,代理层完全兼容MySQL和MongoDB的数据访问协议,让开发人员对代理完全无感,表面上是在访问特定的数据库,实际上连接上数据代理后,由代理对数据操作做解析和路由。

   当然,如果是仅仅实现代理的功能,有很多开源项目可以使用比如MyCat、MySQL Proxy等,我们对代理有这些需求,数据访问路由和配置变更,数据访问鉴权和处理,日志采集发送,流控和服务拒绝。

图8 3.0公共组件架构

日志系统

   设计之初也有考虑自己研发,通过调研后发现ElasticSearch + Logstash + Kibana完全满足我们的需求,并且ES的生态和社区非常活跃。通过早期几个服务的试水,最终决定整个平台基于ELK构建日志系统。

   我们是这样做的,在每台主机上部署Logstash Angent采集格式化的日志数据,向Logstash Server发送日志。为了降低运维成本,我们把Logstash Forward做成Docker镜像,通过Salt来管理所有的Agent。Logstash Server在拿到日志信息后,不做任何规则上的处理,将数据保存在ES中。其实Logstash Server自身有日志解析和格式化的功能,但这样做会严重影响它的吞吐量,于是我们的方案是把日志标准的流程控制在源头。接下来,在Kibana中建立各种服务的面板和视图,便可以进行浏览和检索了,还可以周期对日志进行rotate、分析。

图9 3.0日志系统

监控系统

   基于日志系统的基础架构,不同服务将Metrics输出到文件系统,通过LogStash和ES收集。在Kibana中定义视图,Kibana使用ES作为自己的存储引擎。比如新增一个视图后,Kibana会在ES的.kibana这个索引中创建一份视图的定义。

   有了Metrics数据和视图的展示,下一步是报警。利用Nagios的报警机制,基于JNRPE实现报警的逻辑。报警逻辑通过读取ES中.kibana某一个视图的定义,和对应的阈值设置,通过比较当前值和阈值,返回某一个服务对应特定指标的监控状态。Nagios拿到状态后,决定是否报警。

图10 3.0监控系统

小结

   相比较2.0时期,统一了主要的基础组件,降低了不同服务之间重复劳动部分。通过日志系统也提升了定位问题的效率,接下来还会考虑实现一套自己的请求trace系统,希望能够跟踪整个请求的生命周期,为寻找问题和定位性能瓶颈做参考。

总结

   从应用到平台,公司业务和产品定位在不断的演进,架构也随之发生了天翻地覆的变化。有一点是我们研发人员时刻要提醒自己的,不能脱离业务去谈技术,满足业务是原动力,一切抛开业务去空谈架构的做法都是耍流氓。

   成熟简单的技术就是最合适的,踩坑在所难免,但是如何把有限的资源用于做对的事情,不仅对商业和产品适合,对研发也同样适合。互联网公司多数都有由小团队、小想法、小产品,一步步发展起来的,在这个过程中能够通过设计和决策,在正确的时间做正确的事情,让产品能够快速迭代,快速上线,才是架构人员最需要考虑的事情。

Docker 新手上路(后记)

这个系列一共分为5篇,是我初次接触 Docker 的总结。

这几篇是一个很粗略的新手入门。并不足以了解全貌,不过可以让你短时间内了解 Docker 为何物以及一些简单的操作,可以一看。

从 Docker 安装到使用,渊源等了解之后,对 Docker 有了比较全面的认识。在后记里着重于 Docker 生态圈的记录以及技术选型。

集群管理

随着要管理的容器越来越多,容器的集群管理平台就成了刚需。目前主流的容器管理平台包括Swarm,Kubernetes,Mesos。

Docker Swarm

Swarm是Docker公司在2014年12月初新发布的容器集群管理工具。它可以把多个主机变成一个虚拟的Docker主机来管理。Swarm使用Go语言开发,并且开源,在github上可以找到它的全部source code。Swarm使用标准的Docker API,给Docker用户带来无缝的集群使用体验。2016年7月, Swarm已经被整合进入Docker Engine。

  1. 特点

Docker Swarm的特点是配置和架构都很简单,使用Docker原生的API,可以很好的融合Docker的生态系统。

  1. 功能

Docker Swarm提供API 和CLI来在管理运行Docker的集群,它的功能和使用本地的Docker并没有本质的区别。但是可以通过增加Node带来和好的扩展性。理论上,你可以通过增加节点(Node)的方式拥有一个无限大的Docker主机。 Swarm并不提供UI,需要UI的话,可以安装UCP,不过这个UCP是收费的!!!

相关资料:

Kubernetes

Kubernetes是Google开发的一套开源的容器应用管理系统,用于管理应用的部署,维护和扩张。利用Kubernetes能方便地管理跨机器运行容器化的应用。Kubernetes也是用Go语言开发的,在github上可以找到源代码。Kubernetes 源于谷歌公司的内部容器管理系统Borg,经过了多年的生产环境的历炼,所以功能非常强大。

  1. 功能
  • 使用Docker对应用程序包装(package)、实例化(instantiate)、运行(run)。
  • 以集群的方式运行、管理跨机器的容器。
  • 解决Docker跨机器容器之间的通讯问题。
  • Kubernetes的自我修复机制使得容器集群总是运行在用户期望的状态。
  • 应用的高可用和靠扩展
  • 支持应用的在线升级(Rolling Update)
  • 支持跨云平台(IaaS)的部署
  1. 特点

    Kubernetes提供了很多应用级别的管理能力,包括高可用可高扩展,当然为了支持这些功能,它的架构和概念都比较复杂。

相关资料:

Apache Mesos

Mesos是为软件定义数据中心而生的操作系统,跨数据中心的资源在这个系统中被统一管理。Mesos的初衷并非管理容器,只是随着容器的发展,Mesos加入了容器的功能。Mesos可以把不同机器的计算资源统一管理,就像同一个操作系统,用于运行分布式应用程序。

Mesos的起源于Google的数据中心资源管理系统Borg。你可以从WIRED杂志的这篇文章中了解更多关于Borg起源的信息及它对Mesos影响。

  1. 功能

Mesos的主要功能包括:

  • 高度的可扩展和高可用
  • 可自定义的两级调度
  • 提供API进行应用的扩展
  • 跨平台
  1. 特点

Mesos相比Kubernetes和Swarm更为成熟,但是Mesos主要要解决的是操作系统级别的抽象,并非为了容器专门设计,如果用户出了容器之外,还要集成其它的应用,例如Hadoop,Spark,Kafka等,Mesos更为合适。Mesos是一个更重量级的集群管理平台,功能更丰富,当然很多功能要基于各种Framework。 Mesos的扩展性非常好,最大支持50000节点,如果对扩展性要求非常高的话么,Mesos是最佳选择。

相关资料:

Docker 与 大数据

Docker 与 Hadoop

直接用机器搭建Hadoop集群是一个相当痛苦的过程,尤其对初学者来说。他们还没开始跑wordcount,可能就被这个问题折腾的体无完肤了。而且也不是每个人都有好几台机器对吧。 将Hadoop集群运行在Docker容器中,使Hadoop开发者能够快速便捷地在本机搭建多节点的Hadoop集群。

相关资料:

Docker 与 ELK

相关资料:

Docker 与 TENSOFLOW

相关资料:

Docker 与 Jenkins

Jenkins是被广泛应用的持续集成、自动化测试、持续部署的框架,甚至有些项目组顺便将其用来做流程管理的工具。

相关资料:

Docker 与 Redis

相关资料:

Docker 与 Serverless

Serverless 不意味着没有服务器,而是从应用可以在一个抽象层上忽略它的存在,而只关注在功能实现上和自身的请求处理上;每一个功能实现在不是单纯的业务逻辑处理的代码,相反每个功能调用具有了 Server 的特质,进化成为了一个具有自省、自知和自治的工作负载单元;他们更像是能够衍生出其它新功能单元的生物体。这样整个 Serverless 应用架构之内,每个生命可以衍生下去,子子孙孙无穷匮也。

Docker 与 测试运维

Docker 的实践案例

Docker 与其他

参考资料

补充参考资料:


Docker 新手上路(前传)

这个系列一共分为5篇,是我初次接触 Docker 的总结。

这几篇是一个很粗略的新手入门。并不足以了解全貌,不过可以让你短时间内了解 Docker 为何物以及一些简单的操作,可以一看。

今晚将 CSDN 上所有的 Docker 的文章浏览了一遍(53页,将近523篇文章)。

在学习了基本的 Docker 部署的知识和简单应用后,回过头来从源头开始,再来看看 Docker 这项技术,大有收获。

何为 Docker

Docker 是 PaaS 提供商 dotCloud 开源的一个基于 LXC 的高级容器引擎, 源代码托管在 Github 上, 基于go语言并遵从Apache2.0协议开源。 Docker容器可以封装任何有效负载,几乎可以在任何服务器之间进行一致性运行。换句话说,开发者构建的应用只需一次构建即可多平台运行。运营人员只需配置他们的服务,即可运行所有的应用。

Docker的常用案例包括:

  • 自动打包和部署应用
  • 创建轻量、私有的PaaS环境
  • 自动化测试和持续集成/部署
  • 部署并扩展Web应用、数据库和后端服务器

Docker 的组成

  • Docker Client : Docker提供给用户的客户端。Docker Client提供给用户一个终端,用户输入Docker提供的命令来管理本地或者远程的服务器。
  • Docker Daemon : Docker服务的守护进程。每台服务器(物理机或虚机)上只要安装了Docker的环境,基本上就跑了一个后台程序Docker Daemon,Docker Daemon会接收Docker Client发过来的指令,并对服务器的进行具体操作。
  • Docker Images : 俗称Docker的镜像,这个可难懂了。你暂时可以认为这个就像我们要给电脑装系统用的系统CD盘,里面有操作系统的程序,并且还有一些CD盘在系统的基础上安装了必要的软件,做成的一张 “只读” 的CD。
  • Docker Registry : 这个可认为是Docker Images的仓库,就像git的仓库一样,用来管理Docker镜像的,提供了Docker镜像的上传、下载和浏览等功能,并且提供安全的账号管理可以管理只有自己可见的私人image。就像git的仓库一样,docker也提供了官方的Registry,叫做Dock Hub
  • Docker Container : 俗称Docker的容器,这个是最关键的东西了。Docker Container是真正跑项目程序、消耗机器资源、提供服务的地方,Docker Container通过Docker Images启动,在Docker Images的基础上运行你需要的代码。

Docker 的技术

  • LinuX Containers(LXC). 提供一个共享kernel的 OS 级虚拟化方法,在执行时不用重复加载Kernel, 且container的kernel与host共享,因此可以大大加快container的 启动过程,并显著减少内存消耗。
  • AUFS(chroot) – 用来建立不同的操作系统和隔离运行时的硬盘空间
  • Namespace – 用来隔离Container的执行空间, 其中pid, net, ipc, mnt, uts 等namespace将container的进程, 网络, 消息, 文件系统和hostname 隔离开。
  • Cgroup – 分配不同的硬件资源
  • SELinux – 用来保护linux的网络安全
  • Netlink – 用来让不同的Container之间的进程保持通信

简单说来,Docker = LXC + AUFS。

(AUFS)

其他知识

云计算

云计算,通常被称为云,是指在 Internet 上按需交付计算资源(从应用程序到数据,到硬件、软件,甚至数据中心),并按使用付费。此外,云计算可以包括快速、动态地对 IT 资源进行配置,然后取消配置的能力、自助服务式 IT 方法(而不是让用户通过 IT 部门获取 IT 资源),以及通过广泛共享资源并以非常细粒度的增量提供这些资源来实现业务效率。通过服务模型来划分,可以分为 IaaS, Paas, Saas.

在当今云计算环境当中,IaaS是非常主流的,无论是Amazon EC2还是Linode或者Joyent等,都占有一席之地,但是随着Google的App Engine,Salesforce的Force.com还是微软的Windows Azure等PaaS平台的推出,使得PaaS也开始崭露头角。

IaaS

基础架构即服务:Infrastructure as a service(IaaS) 以自助服务和按使用付费的方式为用户提供基本的计算资源,这些资源包括服务器、网络、存储和数据中心空间。IaaS 通常称为云计算的基础层。在典型的 IaaS 云模型中,提供给用户的基本计算资源要么是裸机 (专用),要么是虚拟化的 (共享)。

PaaS

平台即服务:Platform as a service(PaaS) 构建在 IaaS 之上,提供基础架构和平台软件的组合;这通常是指基于云的应用程序开发、中间件、数据库软件,以及相应的硬件环境。 这种服务模式专门面向应用程序的开发人员、测试人员、部署人员和管理员。谷歌应用引擎(GAE)、新浪SAE也属于PaaS范畴。最大的好处在于免运维,对开发人员是个莫大的吸引力。

SaaS

软件即服务:Software as a service (SaaS) 对在云中运行的应用程序提供基于网络的访问。通常,在 SaaS 解决方案中,许多客户都共享对云交付的软件和数据库的访问。常见的 SaaS 应用包括客户关系管理(CRM)系统、企业资源规划(ERP)系统,Gmail也是一种SaaS,谷歌是提供商,我们大众则是消费者。 有争论的部分

Docker的争议

  • 能否彻底隔离

在超复杂的业务系统中,单OS到底能不能实现彻底隔离,一个程序的崩溃/内存溢出/高CPU占用到底会不会影响到其他容器或者整个系统?很多人对Docker能否在实际的多主机的生产环境中支持关键任务系统还有所怀疑。 注* 就像有人质疑Node.JS单线程快而不稳,无法在复杂场景中应用一样。

  • GO语言还没有完全成熟

Docker由Go语言开发,但GO语言对大多数开发者来说比较陌生,而且还在不断改进,距离成熟还有一段时间。此半git、半包管理的方式让一些人产生不适。

  • 被私有公司控制

Docker是一家叫Dotcloud的私有公司设计的,公司都是以营利为目的,比如你没有办法使用源代码编绎Docker项目,只能使用黑匣子编出的Docker二进制发行包,未来可能不是完全免费的。 目前Docker已经推出面向公司的企业级服务(咨询、支持和培训)。

Docker VS OpenStack

OpenStack和Docker之间是很好的互补关系。Docker的出现能让IaaS层的资源使用得更加充分,因为Docker相对虚拟机来说更轻量,对资源的利用率会更加充分。

Docker主要针对Paas平台,是以应用为中心。OpenStack主要针对Iaas平台,以资源为中心,可以为上层的PaaS平台提供存储、网络、计算等资源。

参考资料:


Docker 新手上路(三)

这个系列一共分为5篇,是我初次接触 Docker 的总结。

这几篇是一个很粗略的新手入门。并不足以了解全貌,不过可以让你短时间内了解 Docker 为何物以及一些简单的操作,可以一看。 前几天新学习了 docker 的基本知识,包括安装、环境配置、获取和运行镜像等最基础的东西。今天看看一些简单的网络配置和相关的开源软件。更加深入的内容暂时就不研究了。毕竟作为新手上路已经足够了。

外部访问容器

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P 或 -p 参数来指定端口映射。

docker run --name web -d -p 1644:80 nginx
docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py
docker logs -f web

使用 docker port 来查看当前映射的端口配置,也可以查看到绑定的地址

docker port web

使用 docker inspect xxx 可以获取特定容器内部网络和 ip 地址所有的变量,-p 标记可以多次使用来绑定多个端口

容器互联

(待续)

参考资料:


Docker 新手上路(二)——Dockerfile

这个系列一共分为5篇,是我初次接触 Docker 的总结。

这几篇是一个很粗略的新手入门。并不足以了解全貌,不过可以让你短时间内了解 Docker 为何物以及一些简单的操作,可以一看。

前几天新学习了 docker 的基本知识,包括安装、环境配置、获取和运行镜像等最基础的东西。今天学习一些 docker 的启动查看定制等操作。

镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。 用过 PHP Laravel 框架的应该马上就理解了(其他例如java kotlin等也是类似),这个就和 composer.json 脚本一样,用来保证环境一致性的配置文件。

定制镜像需要用到下面几个命令

  • FROM 指定基础镜像
  • RUN 执行命令
  • BUILD 构建镜像

Dockerfile 正确的写法应该是这样:

FROM xxx

RUN buildDeps='gcc libc6-dev make' \
    && apt-get update \
    && apt-get install -y $buildDeps \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -rf /var/lib/apt/lists/* \
    && rm redis.tar.gz \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps

然后在该目录执行

$ docker build -t nginx:v3 .

使用 docker build 命令进行镜像构建。其格式为:

docker build [选项] <上下文路径/URL/->

#build
      --no-cache=false Do not use cache when building the image
      -q, --quiet=false Suppress the verbose output generated by the containers
      --rm=true Remove intermediate containers after a successful build
      -t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success
      
docker build -t image_name Dockerfile_path



在这里我们指定了最终镜像的名称 -t nginx:v3。 一般大家习惯性的会使用默认的文件名 Dockerfile,以及会将其置于镜像构建上下文目录中。

Docker Build 的用法

  • Dockerfile docker build -t nginx:v3 .
  • Git repo docker build https://github.com/twang2218/gitlab-ce-zh.git#:8.14
  • tar 压缩包 docker build http://server/context.tar.gz
  • 标准输入中读取 Dockerfile

      docker build - < Dockerfile
      cat Dockerfile | docker build -
    
  • 标准输入中读取上下文压缩包 docker build - < context.tar.gz

Dockerfile

Dockerfile是自动构建docker镜像的配置文件,Dockerfile中的命令非常类似linux shell下的命令,支持以 # 开头的注释行.一般,Dockerfile分为4部分

  • 基础镜像(父镜像)信息
  • 维护者信息
  • 镜像操作命令
  • 容器启动命令

下面是一个CentOS7的Dockerfile实例

FROM       daocloud.io/centos:7
MAINTAINER hanxt <hanxiaotongtong@163.com>

ENV TZ "Asia/Shanghai"
ENV TERM xterm

ADD aliyun-mirror.repo /etc/yum.repos.d/CentOS-Base.repo
ADD aliyun-epel.repo /etc/yum.repos.d/epel.repo

RUN yum install -y curl wget tar bzip2 unzip vim-enhanced passwd sudo yum-utils hostname net-tools rsync man && \
    yum install -y gcc gcc-c++ git make automake cmake patch logrotate python-devel libpng-devel libjpeg-devel && \
    yum install -y --enablerepo=epel pwgen python-pip && \
    yum clean all

RUN pip install supervisor
ADD supervisord.conf /etc/supervisord.conf

RUN mkdir -p /etc/supervisor.conf.d && \
    mkdir -p /var/log/supervisor

EXPOSE 22

ENTRYPOINT ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"]

详细解析

FROM daocloud.io/centos:7
# 基于父镜像构建其他docker镜像,父镜像:可以通过docker pull 命令获得,也可以自己制作

MAINTAINER hanxt <hanxiaotongtong@163.com>
# Dockerfile维护者

ENV TZ "Asia/Shanghai"
# ENV(environment)设置环境变量,一个Dockerfile中可以写多个。以上例子是:设置docker容器的时区为Shanghai

# Dockerfile中有2条指令可以拷贝文件

ADD aliyun-mirror.repo /etc/yum.repos.d/CentOS-Base.repo
# 拷贝本地文件到docker容器里,还可以拷贝URL链接地址下的文件,ADD还具有解压软件包的功能(支持gzip, bzip2 or xz)

COPY test /mydir
# 拷贝本地文件到docker容器


安装linux软件包 RUN yum install -y curl wget….

# RUN命令,非常类似linux下的shell命令 (the command is run in a shell – /bin/sh -c – shell form)
在Dockerfile中每执行一条指令(ENV、ADD、RUN等命令),都会生成一个docker image layer
RUN pip install supervisor

# 安装supervisor进程管理系统,推荐使用
ADD supervisord.conf /etc/supervisord.conf

# 添加supervisor的主配置文件,到docker容器里
RUN mkdir -p /etc/supervisor.conf.d

# 创建存放启动其他服务”supervisor.conf”的目录,此目录下的所有以.conf结尾的文件,在启动docker容器的时候会被加载
端口映射
EXPOSE 22

# 端口映射 EXPOSE <host_port>:<container_port>
# 推荐使用docker run -p <host_port>:<container_port>来固化端口
# 容器启动时执行的命令 ENTRYPOINT [“/usr/bin/supervisord”, “-n”, “-c”, “/etc/supervisord.conf”]
# 一个Dockerfile中只有最后一条ENTRYPOINT生效,并且每次启动docker容器,都会执行ENTRYPOINT

参考资料:


PHP 获取类名、调用者类名方法名

先记一个案例。今天在写一个虚基类 AbstractUuidModel,因为要从 request 里批量将数据通过 Eloquent 保存至 model。而此时有很多 model 都需要这样一个操作,每个 model 都有20个以上的属性,一个一个赋值,真的是能把人写死。

为了提高效率,遂在虚基类中添加一个抽象度比较高的方法。

由 updateByConsole() 这个方法开始进入逻辑:

public function updateByConsole()
{
    $request = request();
    $flag = true;
    foreach ($request->all() as $key => $value) {
    	if(!$flag)continue;
        if(!$request->has($key))continue;
        if(is_string($value))$value = trim($value);
        $flag = $flag && $this->updateProperty($key,$value);
    }
    if($flag) $this->save();

    return $flag;
}

将每一个 input 交给 updateProperty() 这个方法。于是接下来是核心代码:

protected function updateProperty($key,$value){
    if(in_array($key,$this->keyExcept))return true;

    if($this->{$key} == $value) return true;

    $className = get_class($this);
    if(in_array($key,$this->keyUnion)){
        $count = $className::where($key,$value)->count();
        if($count>0)return false;
    }

    if(in_array($key,$this->keySpecific)){
        $this->specific($key,$value);
    }else{
        $this->{$key} = $value;
    }

    return true;
}

可以看出核心逻辑是这样的:

  • 是否有不需要存入的属性,有的话继续循环到一个属性
  • 前后值是否相等,相等则继续循环到一个属性
  • 该属性是否唯一,唯一则判断数据表中是否已有值,有的话继续循环到一个属性(通过 flag 变量做判断,最终的结果是跳过所有循环直到结束,然后上一个 updateByConsole() 方法不保存结果。
  • 该属性是否是特殊属性,是的话子类自己实现 specific() 方法;否则赋值给 this, 然后下一个循环,直到遍历所有的 request。

类方法获取类名

在写上边案例时候印象比较深的就是 php 的这个 get_class() 方法。这个方法可以获得这个类的名字,一般写在 trait 或者 require 里用到。

类静态方法获取类名

虽然 get_class 好用,毕竟是类成员方法才可行。如果是静态方法,有两个办法:

  • CLASS;
  • get_called_class();

获取堆栈信息

以下是我在项目实战中的代码,用来打印方法调用关系。

主要是 stack_trace() + stack_msg_assemble 方法,获得堆栈信息 + 堆栈偏移。

function debug($message = null, array $context = [])
{
    $stack = stack_trace(0);

    if (is_null($message)) {
        $message = '';
    }

    if (is_string($message)) {
        $message = stack_msg_assemble($stack) . "() " . $message;
    }
    return app('log')->debug($message, $context);
}

参考资料: