苹果宣布加入云原生计算基金会

大卫 发表了文章 • 0 个评论 • 278 次浏览 • 2019-06-13 08:27 • 来自相关话题

云原生计算基金会(简称CNCF)作为Kubernetes等顶级开源项目的运营方,今天宣布苹果公司即将以最高级白金用户的身份正式加盟。凭借这位新成员,CNCF如今已经拥有89位最终用户成员,其中还包括阿迪达斯、Atlassian、Box、GitHub、纽约 ...查看全部

云原生计算基金会(简称CNCF)作为Kubernetes等顶级开源项目的运营方,今天宣布苹果公司即将以最高级白金用户的身份正式加盟。凭借这位新成员,CNCF如今已经拥有89位最终用户成员,其中还包括阿迪达斯、Atlassian、Box、GitHub、纽约时报、Reddit、Spotify以及沃尔玛等。

与以往的作风一样,苹果公司并没有对此次公告做出评论。但CNCF方面指出,最终用户资格主要面向那些“对开源云原生技术高度依赖”且希望回馈整个社区的重度用户。作为CNCF最终用户成员,苹果公司实际上也正式加入了Linux基金会。

作为成员关系的一部分,苹果公司还在CNCF管理委员会当中获得一个席位,苹果方面的高级工程技术经理Tomer Doron将出任这一职位。

云原生计算基金会CTO Chris Aniszczyk表示,“吸引到苹果这样一家拥有丰富经验与庞大业务规模的企业作为最终用户成员,充分证明了云原生计算在未来基础设施与应用程序开发方面的可观潜力。我们很高兴能够获得苹果公司的支持,并期待着未来能够为更为广泛的云原生项目社区做出更多贡献。”

虽然很多朋友可能不会把苹果公司与开源参与企业联系起来,但事实上该公司确实已经开放了从Darwin操作系统的XNU内核到Swift编程语言等一系列内部成果。话虽如此,苹果方面一般不会参与开源云基础设施社区,而这次的举动可能预示着这位消费电子巨头正在迎来转变。苹果公司肯定拥有自己的数据中心,因此其确实有可能高度依赖着各类开源基础设施项目——当然,按照苹果的一贯风格,其对这类话题往往避而不谈。

原文链接:Apple joins the open-source Cloud Native Computing Foundation

DockOne微信分享(二一一):基于Actor模型的CQRS/ES解决方案分享

JetLee 发表了文章 • 0 个评论 • 325 次浏览 • 2019-06-05 16:17 • 来自相关话题

【编者的话】2017年加密货币比较流行,我曾有幸在加密货币交易所参与开发工作,为了应对交易系统高性能、高并发、高可用的要求,我们使用基于Actor模型的Orleans技术,采用CQRS/ES架构结合Service Fabric开展了富有挑战性的工作。本次分享将 ...查看全部
【编者的话】2017年加密货币比较流行,我曾有幸在加密货币交易所参与开发工作,为了应对交易系统高性能、高并发、高可用的要求,我们使用基于Actor模型的Orleans技术,采用CQRS/ES架构结合Service Fabric开展了富有挑战性的工作。本次分享将从不同视角为大家介绍Actor模型、CQRS/ES架构以及Service Fabric在高并发场景中的考量和应用。

最近一段时间我一直是这个话题的学习者、追随者,这个话题目前生产环境落地的资料少一些,分享的内容中有一些我个人的思考和理解,如果分享的内容有误、有疑问欢迎大家提出,希望通过分享这种沟通方式大家相互促进,共同进步。
# 引言
本话题由三部分组成:

  • Actor模型&Orleans:在编程的层面,从细粒度-由下向上的角度介绍Actor模型;
  • CQRS/ES:在框架的层面,从粗粒度-由上向下的角度介绍Actor模型,说明Orleans技术在架构方面的价值;
  • Service Fabric:从架构部署的角度将上述方案落地上线。
群里的小伙伴技术栈可能多是Java和Go体系,分享的话题主要是C#技术栈,没有语言纷争,彼此相互学习。比如:Scala中,Actor模型框架有akka,CQRS/ES模式与编程语言无关,Service Fabric与Kubernetes是同类平台,可以相互替代,我自己也在学习Kubernetes。# Actor模型&Orleans(细粒度)##共享内存模型多核处理器出现后,大家常用的并发编程模型是共享内存模型。
1.png
这种编程模型的使用带来了许多痛点,比如:
  • 编程:多线程、锁、并发集合、异步、设计模式(队列、约定顺序、权重)、编译
  • 无力:单系统的无力性:①地理分布型、②容错型
  • 性能:锁,性能会降低
  • 测试:
- 从坑里爬出来不难,难的是我们不知道自己是不是在坑里(开发调试的时候没有热点可能是正常的) - 遇到bug难以重现。有些问题特别是系统规模大了,可能运行几个月才能重现问题
  • 维护:
- 我们要保证所有对象的同步都是正确的、顺序的获取多个锁。 - 12个月后换了另外10个程序员仍然按照这个规则维护代码。简单总结:
  • 并发问题确实存在
  • 共享内存模型正确使用掌握的知识量多
  • 加锁效率就低
  • 存在许多不确定性
##Actor模型Actor模型是一个概念模型,用于处理并发计算。Actor由3部分组成:状态(State)+行为(Behavior)+邮箱(Mailbox),State是指actor对象的变量信息,存在于actor之中,actor之间不共享内存数据,actor只会在接收到消息后,调用自己的方法改变自己的state,从而避免并发条件下的死锁等问题;Behavior是指actor的计算行为逻辑;邮箱建立actor之间的联系,一个actor发送消息后,接收消息的actor将消息放入邮箱中等待处理,邮箱内部通过队列实现,消息传递通过异步方式进行。
2.png
Actor是分布式存在的内存状态及单线程计算单元,一个Id对应的Actor只会在集群种存在一个(有状态的 Actor在集群中一个Id只会存在一个实例,无状态的可配置为根据流量存在多个),使用者只需要通过Id就能随时访问不需要关注该Actor在集群的什么位置。单线程计算单元保证了消息的顺序到达,不存在Actor内部状态竞用问题。举个例子:多个玩家合作在打Boss,每个玩家都是一个单独的线程,但是Boss的血量需要在多个玩家之间同步。同时这个Boss在多个服务器中都存在,因此每个服务器都有多个玩家会同时打这个服务器里面的Boss。如果多线程并发请求,默认情况下它只会并发处理。这种情况下可能造成数据冲突。但是Actor是单线程模型,意味着即使多线程来通过Actor ID调用同一个Actor,任何函数调用都是只允许一个线程进行操作。并且同时只能有一个线程在使用一个Actor实例。##Actor模型:OrleansActor模型这么好,怎么实现?可以通过特定的Actor工具或直接使用编程语言实现Actor模型,Erlang语言含有Actor元素,Scala可以通过Akka框架实现Actor编程。C#语言中有两类比较流行,Akka.NET框架和Orleans框架。这次分享内容使用了Orleans框架。特点:Erlang和Akka的Actor平台仍然使开发人员负担许多分布式系统的复杂性:关键的挑战是开发管理Actor生命周期的代码,处理分布式竞争、处理故障和恢复Actor以及分布式资源管理等等都很复杂。Orleans简化了许多复杂性。优点:
  • 降低开发、测试、维护的难度
  • 特殊场景下锁依旧会用到,但频率大大降低,业务代码里甚至不会用到锁
  • 关注并发时,只需要关注多个actor之间的消息流
  • 方便测试
  • 容错
  • 分布式内存
缺点:
  • 也会出现死锁(调用顺序原因)
  • 多个actor不共享状态,通过消息传递,每次调用都是一次网络请求,不太适合实施细粒度的并行
  • 编程思维需要转变
3.png
第一小节总结:上面内容由下往上,从代码层面细粒度层面表达了采用Actor模型的好处或原因。# CQRS/ES(架构层面)##从1000万用户并发修改用户资料的假设场景开始
4.png
[list=1]
  • 每次修改操作耗时200ms,每秒5个操作
  • MySQL连接数在5K,分10个库
  • 5 5k 10=25万TPS
  • 1000万/25万=40s
  • 5.png
    在秒杀场景中,由于对乐观锁/悲观锁的使用,推测系统响应时间更复杂。##使用Actor解决高并发的性能问题
    6.png
    1000万用户,一个用户一个Actor,1000万个内存对象。
    7.png
    200万件SKU,一件SKU一个Actor,200万个内存对象。
    • 平均一个SKU承担1000万/200万=5个请求
    • 1000万对数据库的读写压力变成了200万
    • 1000万的读写是同步的,200万的数据库压力是异步的
    • 异步落盘时可以采用批量操作
    总结:由于1000万+用户的请求根据购物意愿分散到200万个商品SKU上:每个内存领域对象都强制串行执行用户请求,避免了竞争争抢;内存领域对象上扣库存操作处理时间极快,基本没可能出现请求阻塞情况;从架构层面彻底解决高并发争抢的性能问题。理论模型,TPS>100万+……##EventSourcing:内存对象高可用保障Actor是分布式存在的内存状态及单线程计算单元,采用EventSourcing只记录状态变化引发的事件,事件落盘时只有Add操作,上述设计中很依赖Actor中State,事件溯源提高性能的同时,可以用来保证内存数据的高可用。
    8.png
    9.png
    ##CQRS上面1000万并发场景的内容来自网友分享的PPT,与我们实际项目思路一致,就拿来与大家分享这个过程,下图是我们交易所项目中的架构图:
    10.png
    开源版本架构图:
    11.png
    开源项目GitHub:https://github.com/RayTale/Ray第二小节总结:由上往下,架构层面粗粒度层面表达了采用Actor模型的好处或原因。# Service Fabric 系统开发完成后Actor要组成集群,系统在集群中部署,实现高性能、高可用、可伸缩的要求。部署阶段可以选择Service Fabric或者Kubernetes,目的是降低分布式系统部署、管理的难度,同时满足弹性伸缩。交易所项目可以采用Service Fabric部署,也可以采用K8S,当时K8S还没这么流行,我们采用了Service Fabric,Service Fabric 是一款微软开源的分布式系统平台,可方便用户轻松打包、部署和管理可缩放的可靠微服务和容器。开发人员和管理员不需解决复杂的基础结构问题,只需专注于实现苛刻的任务关键型工作负荷,即那些可缩放、可靠且易于管理的工作负荷。支持Windows与Linux部署,Windows上的部署文档齐全,但在Linux上官方资料没有。现在推荐Kubernetes。第三小节总结:[list=1]
  • 借助Service Fabric或K8S实现低成本运维、构建集群的目的。
  • 建立分布式系统的两种最佳实践:

  • - 进程级别:容器+运维工具(Kubernetes/Service Fabric)
    - 线程级别:Actor+运维工具(Kubernetes/Service Fabric)

    上面是我对今天话题的分享。

    CQRS/ES部分内容参考:《领域模型 + 内存计算 + 微服务的协奏曲:乾坤(演讲稿)》 2017年互联网应用架构实战峰会。
    #Q&A
    Q:单点故障后,正在处理的Cache数据如何处理的,例如,http、tcp请求……毕竟涉及到钱?
    A:actor有激活和失活的生命周期,激活的时候使用快照和Events来恢复最新内存状态,失活的时候保存快照。actor框架保证系统中同一个key只会存在同一个actor,当单点故障后,actor会在其它节点重建并恢复最新状态。

    Q:event ID生成的速度如何保证有效的scale?有没有遇到需要后期插入一些event,修正前期系统运行的bug?有没有遇到需要把前期已经定好的event再拆细的情况?有遇到系统错误,需要replay event的情况?
    A:当时项目中event ID采用了MongoDB的ObjectId生成算法,没有遇到问题;有遇到后期插入event修正之前bug的情况;有遇到将已定好的event修改的情况,采用的方式是加版本号;没有,遇到过系统重新迁移删除快照重新replay event的情况。

    Q:数据落地得策略是什么?还是说就是直接落地?
    A:event数据直接落地;用于支持查询的数据,是Handler消费event后异步落库。

    Q:actor跨物理机器集群事务怎么处理?
    A:结合事件溯源,采用最终一致性。

    Q:Grain Persistence使用Relational Storage容量和速度会不会是瓶颈?
    A:Grain Persistence存的是Grain的快照和event,event是只增的,速度没有出现瓶颈,而且开源版本测试中PostgreSQL性能优于MongoDB,在存储中针对这两个方面做了优化:比如分表、归档处理、快照处理、批量处理。

    Q7:开发语言是erlang吗?Golang有这样的开发模型库支持吗?
    A:开发语言是C#。Golang我了解的不多,proto.actor可以了解一下:https://github.com/AsynkronIT/protoactor-go。

    Q:每个Pod的actor都不一样,如何用Kubernetes部署actor,失败的节点如何监控,并借助Kubernetes自动恢复?
    A:actor是无状态的,失败恢复依靠重新激活时事件溯源机制。Kubernetes部署actor官方有支持,可以参考官方示例。在实际项目中使用Kubernetes部署Orleans,我没有实践过,后来有同事验证过可以,具体如何监控不清楚。

    Q:Orleans中,持久化事件时,是否有支持并发冲突的检测,是如何实现的?
    A:Orleans不支持;工作中,在事件持久化时做了这方面的工作,方式是根据版本号。

    Q:Orleans中,如何判断消息是否重复处理的?因为分布式环境下,同一个消息可能会被重复发送到actor mailbox中的,而actor本身无法检测消息是否重复过来。
    A:是的,在具体项目中,通过框架封装实现了幂等性控制,具体细节是通过插入事件的唯一索引。

    Q:同一个actor是否会存在于集群中的多台机器?如果可能,怎样的场景下可能会出现这种情况?
    A:一个Id对应的Actor只会在集群种存在一个。

    以上内容根据2019年6月4日晚微信群分享内容整理。分享人郑承良,上海某科技公司架构师,对高并发场景下的分布式金融系统拥有丰富的实战经验,曾为澳大利亚、迪拜多家交易所提供技术支持。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiese,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

    使用Docker高效搭建开发环境

    齐达内 发表了文章 • 0 个评论 • 368 次浏览 • 2019-06-04 23:00 • 来自相关话题

    【编者的话】Docker作为轻量级的基于容器的解决方案,它对系统侵入性低,容易移植,天生就适合做复杂业务部署和开发环境搭建,今天给大家带来的是奇虎360开发是如何使用Docker高效搭建开发环境的。 作为一个平时喜欢折腾的开发人员,我 ...查看全部
    【编者的话】Docker作为轻量级的基于容器的解决方案,它对系统侵入性低,容易移植,天生就适合做复杂业务部署和开发环境搭建,今天给大家带来的是奇虎360开发是如何使用Docker高效搭建开发环境的。

    作为一个平时喜欢折腾的开发人员,我喜欢尝试各种环境,使用感兴趣的各种开源软件。

    同时,我也是有一些相对的小洁癖,很喜欢Linux中权限最小化原则,我也不喜欢自己的环境中有太多不知道的东西。

    做了多年的Web开发,我接触到的环境大致如下:

    1. 操作系统从CentOS 5到CentOS 7;
    2. Web Server从Apache到Nginx;
    3. 开发语言从最初的PHP 5.2到PHP 7,又到现在主要使用Go,马上还会开始接触C++;
    4. 数据库从MySQL 5.1到现在的5.7,前阵子又开始折腾MariaDB;
    5. Cache选型从Memcache到Redis;
    6. 队列用过Kafka,去年开始大量使用NSQ;

    公司虽然有专门负责部署、运维这些服务的同学,但我在开发的时候,还是喜欢自己来搭建这些东西,因为这样通常可以对使用到的服务有更多的认识,也能帮助自己使用的更好。如果你想和更多Docker技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

    今天我就来和大家分享下我是如何高效的搭建好自己的开发环境的。
    #搭建前说明
    这里先说明一点,对每个开源软件,我几乎都是自己编译部署的,而不会使用类似yum install这种方式,也很少直接下载官方编译好的二进制包,这都是为了能多深入了解用到的开源软件。

    但一些依赖的动态库文件,如zlib等,还有编译工具,如GCC、make等,我都是通过方便的yum install这种方式直接安装的,否则会累死。
    #传统做法
    我在很长的一段时间内,都是把每个软件的编译、安装过程写成一个脚本,之后再需要用的时候直接运行脚本即可,但这样的方式,通常会遇到下面这些问题:

    1. 脚本只能在我当时的操作系统环境下运行。记得当时购买过不同服务商的VPS,虽然不同VPS我都使用同样的Linux发行版,但脚本通常都不能一键跑完。这也是没办法,因为每个VPS服务商都会制作自己的操作系统镜像版本。
    2. 操作系统升级,如CentOS 5 - 6,或是换为Ubuntu,这样基本上脚本都跑不了。
    3. 软件升级,如MySQL 5.2 - 5.6,构建工具改为CMake,依赖库改变或升级。
    4. 如果某个软件依赖的公共库版本和其它软件不同,且公共库升级后和旧版不兼容,那你就只能为这个软件单独编译公共库了,如果只是普通的公共库还好,但如果是所需要的编译工具版本不同,那可就惨了。

    上面这些问题,如果你想每个发行版维护一个脚本,那会累死,因为一旦你每次想升级一个软件,难道每个发行版都要编译一遍吗?这就变成了收获价值很低的体力劳动了。

    由于喜欢折腾的个性,我对操作系统的升级以及软件包版本的升级又经常发生,所以一直以来,我都在寻找一个好方法,能很方便的维护好自己的开发环境,尽量做到只=新东西只为它工作一次,最后我找到了Docker,目前我都是用它来搭建自己的开发环境的。
    #Docker做法
    先概括介绍下我的方法:

    1. 让每个软件运行在容器中,因为运行的容器环境是可以固定下来的,所以编译安装脚本写一个就可以了。
    2. 代码使用数据卷的方式加载到需要的容器中。
    3. 因为是开发环境,所以网络方面使用最简单的--net=host。
    4. 将镜像的创建、容器的启动维护在Git项目中,并抽象出统一的构建过程,很方面的做到新软件接入,新机器部署。

    下面用实例来说明把:
    ##示例Nginx环境构建
    我将构建过程放到Git中:https://gitee.com/andals/docker-nginx

    Readme中记录了构建所需要执行的脚本命令,大家访问上面的网址就可以看到,这里我简单介绍下项目的结构:
    ├── Dockerfile        //创建镜像的Dockerfile
    ├── pkg //编译好的二进制包,可以直接使用,此外软件运行的一些配置文件或第三方包也放在这里
    │ ├── conf
    │ │ ├── fastcgi.conf
    │ │ ├── http.d
    │ │ ├── include
    │ │ ├── koi-utf
    │ │ ├── koi-win
    │ │ ├── logrotate.conf
    │ │ ├── logrotate.d
    │ │ ├── mime.types
    │ │ ├── Nginx.conf
    │ │ ├── scgi_params
    │ │ ├── uwsgi_params
    │ │ └── win-utf
    │ ├── luajit-2.0.3.tar.gz
    │ └── Nginx-1.8.1.tar.gz
    ├── README.md
    ├── script //里面放构建脚本
    │ ├── build_image.sh //构建镜像使用
    │ ├── build_pkg.sh //编译软件包时使用
    │ ├── init.sh //容器启动时执行
    │ └── pre_build.sh //软件依赖的共享库,编译和构建时都会用到
    └── src //编译时需要的软件源码
    ├── modules
    │ ├── ngx_devel_kit-0.2.19.tgz
    │ ├── ngx_echo-0.53.tgz
    │ └── ngx_lua-0.9.7.tgz
    ├── Nginx-1.8.1.tar.gz
    └── openssl-1.0.2h.tar.gz

    ##DockerFile说明
    Dockerfile结构如下:
    FROM andals/CentOS:7
    MAINTAINER ligang

    LABEL name="Nginx Image"
    LABEL vendor="Andals"

    COPY pkg/ /build/pkg/
    COPY script/ /build/script/

    RUN /build/script/build_image.sh

    CMD /build/script/init.sh

    整个构建框架为:

    1. 把构建需要的包(PKG目录中)放到镜像中
    2. 把构建脚本放到镜像中
    3. 执行构建脚本
    4. 容器启动时,执行init.sh,里面启动相应的服务

    Readme.md中记录了执行构建的命令和容器运行命令,示例运行如下:
    ligang@vm-xUbuntu16 ~/devspace/dbuild $ git clone git@gitee.com:andals/docker-Nginx.git Nginx
    Cloning into 'Nginx'...
    ......

    ligang@vm-xUbuntu16 ~/devspace/dbuild $ cd Nginx/
    ligang@vm-xUbuntu16 ~/devspace/dbuild/Nginx $ ngxVer=1.8.1
    ligang@vm-xUbuntu16 ~/devspace/dbuild/Nginx $ docker build -t andals/Nginx:${ngxVer} ./
    Sending build context to Docker daemon 30.7MB
    Step 1/8 : FROM andals/CentOS:7
    ......
    Successfully built ea8147743031
    Successfully tagged andals/Nginx:1.8.1

    ligang@vm-xUbuntu16 ~/devspace/dbuild/Nginx $ docker run -d --name=Nginx-${ngxVer} --volumes-from=data-home -v /data/Nginx:/data/Nginx --net=host andals/Nginx:${ngxVer}
    dbf3c0617eb34c4b1b4ea54c2961989612d5474db3b1acd1d717221e6e5cb516

    说明:

    `--volumes-from=data-home`这个就是我放置代码的数据卷,我喜欢把代码放到$HOME下面,所以这个卷的相关信息如下:
    ligang@vm-xUbuntu16 ~ $ docker ps -a
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    578912a08ea7 andals/CentOS:7 "echo Data Volumn Ho…" 9 days ago Exited (0) 9 days ago data-home
    ......

    ligang@vm-xUbuntu16 ~ $ docker inspect 578912a08ea7
    ......
    "Mounts": [
    {
    "Type": "bind",
    "Source": "/home",
    "Destination": "/home",
    "Mode": "",
    "RW": true,
    "Propagation": "rprivate"
    }
    ......

    `/data/Nginx`中放置Nginx的conf、log等,每个软件运行时的conf、log、data等我都统一放置在`/data`下面,如:
    ligang@vm-xUbuntu16 ~ $ tree -d /data/ -L 1
    /data/
    ├── mariadb
    ├── Nginx
    └── redis

    ligang@vm-xUbuntu16 ~ $ tree -d /data/Nginx/
    /data/Nginx/
    ├── conf
    │ ├── http.d
    │ ├── include
    │ └── logrotate.d
    └── logs

    启动容器时使用`--net=host`,作为开发环境简单实用

    我就是通过这种方法完成了开发环境的构建,不再有多余的重复工作,并且新机器部署开发环境效率极高。

    我目前用到的容器环境如下:
    ligang@vm-xUbuntu16 ~ $ docker ps -a
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    dbf3c0617eb3 andals/Nginx:1.8.1 "/bin/sh -c /build/s…" 33 minutes ago Up 33 minutes Nginx-1.8.1
    3e31ef433298 andals/php:7.1.9 "/bin/sh -c /build/s…" 8 hours ago Up 8 hours php-7.1.9
    360f94bf9c43 andals/jekyll:latest "/bin/sh -c /build/s…" 9 days ago Up 10 hours jekyll-latest
    0a7d58d1ca5e andals/redis:4.0.8 "/bin/sh -c /build/s…" 9 days ago Up 10 hours redis-4.0.8
    fdaa655b4a11 andals/samba:4.4.16 "/bin/sh -c /build/s…" 9 days ago Up 10 hours samba-4.4.16
    6ad00a69befd andals/mariadb:10.2.14 "/bin/sh -c /build/s…" 9 days ago Up 10 hours mariadb-10.2.14
    578912a08ea7 andals/CentOS:7 "echo Data Volumn Ho…" 9 days ago Exited (0) 9 days ago data-home

    ##辅助工具dockerbox
    使用Docker环境后,有个问题,就是没有办法很方便的和软件交互。

    这是因为软件都执行在容器中,比如重启Nginx吧,需要下面这几步:

    1. 找到Nginx这个容器
    2. 进入Nginx这个容器
    3. 在容器里面再执行reload

    也可以是:

    1. 找到Nginx这个容器
    2. 使用docker exec

    但无论哪种方式,都比原先直接执行命令麻烦的多。

    另外,有时也需要进入容器中,查看服务的运行情况。

    为了方便的做这些事情,我开发了一个工具dockerbox,可以很方便的做到这些事情。

    dockerbox的详情及使用方法请见:https://github.com/ligang1109/dockerbox
    #配置开机运行
    最后再说下如何配置开机启动。

    我使用虚拟机搭建的开发环境,所以配置这个会省事好多,我使用用了systemd:
    ligang@vm-xUbuntu16 ~ $ ll /lib/systemd/system/dstart.service
    -rw-r--r-- 1 root root 229 4月 3 21:35 /lib/systemd/system/dstart.service

    ligang@vm-xUbuntu16 ~ $ cat /lib/systemd/system/dstart.service
    [Unit]
    Description=Docker Container Starter
    After=network.target docker.service
    Requires=docker.service

    [Service]
    ExecStart=/usr/local/bin/dbox -dconfPath=/home/ligang/.dconf.json start all

    [Install]
    WantedBy=multi-user.target

    dbox请参考dockerbox的使用方法。
    #结束语
    上面说的是我现在使用的开发环境搭建方法,有兴趣爱折腾的同学不妨试试看,如果你有更好的方法,也希望能分享给我。

    原文链接:使用Docker高效搭建开发环境

    什么是服务网格?

    grace_shi 发表了文章 • 0 个评论 • 223 次浏览 • 2019-06-04 22:29 • 来自相关话题

    服务网格 是一个可配置的低延迟的基础设施层,目的是通过API(应用程序编程接口)处理应用程序服务之间的大量基于网络的进程间通信。服务网络确保容器化的短暂存在的应用程序的基础结构服务之间的通信快速,可靠和安全。网格提供关键功能,包括服务发现,负载平衡,加密,可观 ...查看全部
    服务网格 是一个可配置的低延迟的基础设施层,目的是通过API(应用程序编程接口)处理应用程序服务之间的大量基于网络的进程间通信。服务网络确保容器化的短暂存在的应用程序的基础结构服务之间的通信快速,可靠和安全。网格提供关键功能,包括服务发现,负载平衡,加密,可观察性,可追溯性,身份验证和授权,以及对断路器模式【1】的支持。

    服务网格是如何实现的呢?它通常会为每个服务实例提供一个称为边车(sidecar)的代理实例。这些边车会处理服务间的通信,监控和安全相关的问题, 以及任何可以从各个服务中抽象出来的东西。这样,开发人员就可以专注于服务中应用程序代码的开发,支持和维护,而运维团队可以负责维护服务网格以及运行应用程序。

    Istio,由Google,IBM和Lyft支持,是目前最著名的服务网格架构。Kubernetes,最初由Google设计,是目前Istio支持的唯一容器编排框架。供应商正在寻求商业版本的Istio。如果Istio能添加到开源项目中,将会带来更大的价值。

    Istio并不是唯一的选择,其他服务网格实现也在开发中。目前边车代理模式是最受欢迎的,例如Buoyant,HashiCorp,Solo.io等项目都使用了这种模式。与此同时,Netflix的技术套件使用了一种替代架构,他们使用了应用程序库(包含Ribbon,Hysterix,Eureka,Archaius)来提供服务网格功能,而Azure Service Fabric等平台在应用程序框架中嵌入了类似服务网格的功能。 服务网格包含一些专业术语来描述组件服务和功能:

    • 容器编排框架。随着越来越多的容器被添加到应用程序的基础架构中,用于监视和管理容器组的容器编排框架变得至关重要。
    • 服务和实例(Kubernetes Pod)。实例是微服务的单个运行副本。有时实例是一个容器;在Kubernetes中,一个实例由一小组相互依赖的容器(称为Pod)组成。客户端很少直接访问实例或Pod,通常他们会访问服务,服务通常是一组可扩展且具有容错性的实例或Pod(副本)。
    • 边车代理。 边车代理与单个实例或Pod一起运行。 边车代理的目的是路由或者代理从容器发出或者接收的流量。 边车与其他边车代理进行通信,编排框架会管理这些边车。许多服务网格的实现会使用边车代理来拦截和管理实例或Pod的所有进出流量。
    • 服务发现。当实例需要与不同的服务进行交互时,它需要找到 - 发现 - 其他服务的健康的,可用的实例。通常,这个实例会执行DNS查找来寻找其他服务的实例。容器编排框架保留实例列表(这些实例都可以正常接收请求),并且框架会提供DNS查询的接口。
    • 负载均衡。大多数编排框架已经提供了第4层(传输层)的负载均衡。服务网络实现了更复杂的第7层(应用层)负载均衡,具有更丰富的算法以及更强大的流量管理。同时可以通过API修改负载均衡的参数,从而可以编排蓝绿部署【2】或金丝雀部署【3】。
    • 加密。服务网格可以加密和解密请求和响应,因此每个服务不需要额外对请求进行加密,减少了负担。服务网格还可以通过优先重用现有的持久连接来提高性能,这减少新连接的创建(创建新连接十分耗费计算资源)。一般实现加密流量都是用双向TLS(mTLS),其中公钥架构(PKI,public key infrastructure)生成并分发证书和密钥,提供给边车代理使用。
    • 身份验证和授权。服务网格可以授权和验证从应用程序外部和内部发出的请求,仅向实例发送经过验证的请求。
    • 支持断路器模式【1】。服务网格可以支持断路器模式,这可以隔离不健康的实例,然后在安全的情况下逐渐将它们恢复并加入到健康的实例池中。

    服务网格应用中管理实例之间的网络流量的的部分称为数据平面。另外有一个独立的控制平面负责生成和部署数据平面的配置(这个配置可以控制数据平面的行为)。控制平面通常包含(或被设计为连接到)一个API,命令行界面和用于管理App的图形用户界面。

    *服务网格中的控制平面在数据平面中边车代理上分发配置*

    服务网格架构的一个常见用例是在使用容器和微服务时解决非常苛刻的操作问题。微服务领域的先驱包括Lyft,Netflix和Twitter等公司,这些公司任何时间都能为全球数百万用户提供强大的服务。 (请参阅我们对Netflix面临的一些架构挑战的深入描述。)如果应用程序对这方面要求比较低,那么更简单的架构就足够了。

    服务网格架构不可能解决所有应用程序操作和交付问题。架构师和开发人员可以选择多种工具,这些工具之中,有些是锤子,可以解决不同类型的问题,而另一些可能是钉子。例如,NGINX微服务参考架构包括几种不同的模型,这些模型提供了使用微服务来解决问题的一系列方法。 服务网格架构中的组成部分 - 例如:NGINX,容器,Kubernetes,以及微服务(作为架构方法),可以在非服务网格架构实现中高效地使用。例如,Istio是作为一个完整的服务网格架构开发的,但其模块化的设计意味着开发人员可以自由选择他们需要的部分组件技术。考虑到这一点,即使您不确定是否以及何时会完全实现服务网格应用程序,也值得深入了解一下服务网格概念。 

    注释: 

    【1】断路器模式: 一种设计模式。用以侦测错误,并避免不断地触发相同的错误(如维护时服务不可用、暂时性的系统问题或是未知的系统错误)。https://zh.wikipedia.org/wiki/斷路器設計模式 
    【2】蓝绿部署(blue‑green deployment):蓝绿部署是保留老版本,同时部署新版本然后进行测试,测试通过后,将流量切换到新版本,然后老版本同时也升级到新版本。 
    【3】金丝雀部署(canary deployment):又叫做灰度部署,即选择部分部署新版本,将部分流量引入到新版本,新老版本同时提供服务。等待灰度的版本测试完毕,之后全量覆盖老版本。 

    原文链接:What Is a Service Mesh? 

    ============================================================================== 
    译者介绍:Grace,程序员,研究生毕业于SUNY at Stony Brook,目前供职于Linktime Cloud Company,对大数据技术以及数据可视化技术感兴趣。

    Kubernetes IN Docker - local clusters for testing Kubernetes

    老马 发表了文章 • 0 个评论 • 212 次浏览 • 2019-06-04 21:52 • 来自相关话题

    ##Intro to Kind Kubernetes IN Docker - local clusters for testing Kubernetes ##Brief Kind(Kubernetes IN Docker)是 ...查看全部
    ##Intro to Kind
    Kubernetes IN Docker - local clusters for testing Kubernetes
    ##Brief
    Kind(Kubernetes IN Docker)是一个用来快速创建和测试Kubernetes的工具,Kind把环境的依赖降低到了最小,仅需要机器安装了Docker即可。

    Kind 可以做什么

    • 快速创建一个或多个`Kubernetes`集群(几分钟)
    • 支持`ha master`部署高可用的`Kubernetes`集群
    • 支持从源码构建并部署一个`Kubernetes`集群
    • 可以快速低成本体验一个最新的`Kubernetes`集群,并支持`Kubernetes`的绝大部分功能
    • 支持本地离线运行一个多节点集群
    Kind 有哪些优势
    • 最小的安装依赖,仅需要安装`Docker`即可
    • 使用快速简单,使用`kind cli`工具即可快速创建集群
    • 使用`container`来 mock`kubernetes node`
    • 内部使用`kubeadm`的官方主流部署工具
    • 使用了`containerd`
    • 通过了`CNCF`官方的`k8s conformance`测试
    ##Usage
    GO111MODULE="on" go get sigs.k8s.io/kind@v0.3.0 && kind create cluster
    ##How it workKind 使用一个 container 来模拟一个 node,在 container 里面跑了 systemd ,并用 systemd 托管了 kubelet 以及 containerd,然后容器内部的 kubelet 把其他 Kubernetes 组件,比如 kube-apiserver,etcd,cni 等组件跑起来。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态。可以通过配置文件的方式,来通过创建多个 container 的方式,来模拟创建多个 Node,并以这些 Node 来构建一个多节点的 Kubernetes 集群。Kind 内部使用了 kubeadm 这个工具来做集群的部署,包括 ha master 的高可用集群,也是借助 kubeadm 提供的aplha特性提供的。同时,在 ha master 下,额外部署了一个 nginx 用来提供负载均衡 vip。##Build ImagesKind 的镜像分为两个,一个 Node 镜像,一个 Base 镜像。Node 镜像Node 镜像的构建比较复杂,目前是通过运行 Base 镜像,并在 Base 镜像内执行操作,再保存此容器内容为镜像的方式来构建的,包含的操作有:
    • 构建 Kubernetes 相关资源(比如二进制文件和镜像)
    • 运行一个用于构建的容器
    • 把构建的 Kubernetes 相关资源复制到容器里
    • 调整部分组件配置参数,以支持在容器内运行
    • 预先拉去运行环境需要的镜像
    • 通过 docker commit 方式保存当前的构建容器为 node 镜像
    具体的逻辑,可以参考node.goBase 镜像Base 镜像目前使用了 ubuntu:19.04 作为基础镜像,做了下面的调整:
    • 安装 systemd 相关的包,并调整一些配置以适应在容器内运行
    • 安装 Kubernetes 运行时的依赖包,比如 conntrack,socat,cni
    • 安装容器运行环境,比如 containerd,crictl
    • 配置自己的 ENTRYPOINT 脚本,以适应和调整容器内运行的问题
    具体的逻辑,可以参考构建的Dockerfile。##Create ClusterKind 创建集群的基本过程为:[list=1]
  • 根据传入的参数,来创建 container,分为 control node 和 worker node 两种(如果是 ha master,还有一个 loadbalancer node)
  • 如果需要,配置 loadbalancer 的配置,主要是 nginx 配置文件
  • 生成 kubeadm 配置
  • 对于第一个控制节点,使用 kubeadm init 初始化单节点集群
  • 配置安装 cni 插件
  • 配置存储(实际是安装了一个使用 hostpath 的 storageclass)
  • 其他的控制节点,通过kubeadm join --experimental-control-plane
  • 的方式来扩容控制节点[list=1]
  • 通过 kubeadm join 扩容其他的工作节点
  • 等待集群创建完成
  • 生成访问配置,打印使用帮助具体的创建流程,可以参考代码create.go
  • 这里关于每个容器,是如何作为 node 跑起来的,可以简单讲解些原理:根据不同的角色,调用不同的函数创建节点:
    [url=https://github.com/kubernetes-sigs/kind/blob/master/pkg/cluster/internal/create/nodes.go#L196]nodes.go[/url]
    // TODO(bentheelder): remove network in favor of []cri.PortMapping when that is infunc (d [i]nodeSpec) Create(clusterLabel string) (node [/i]nodes.Node, err error) {  // create the node into a container (docker run, but it is paused, see createNode)  // TODO(bentheelder): decouple from config objects further  switch d.Role {  case constants.ExternalLoadBalancerNodeRoleValue:    node, err = nodes.CreateExternalLoadBalancerNode(d.Name, d.Image, clusterLabel, d.APIServerAddress, d.APIServerPort)  case constants.ControlPlaneNodeRoleValue:    node, err = nodes.CreateControlPlaneNode(d.Name, d.Image, clusterLabel, d.APIServerAddress, d.APIServerPort, d.ExtraMounts)  case constants.WorkerNodeRoleValue:    node, err = nodes.CreateWorkerNode(d.Name, d.Image, clusterLabel, d.ExtraMounts)  default:    return nil, errors.Errorf("unknown node role: %s", d.Role)  }  return node, err} 
    节点(容器)创建时,通过配置 --privileged,挂载 tmpfs,修改主机名等,来运行节点create
    func createNode(name, image, clusterLabel, role string, mounts []cri.Mount, extraArgs ...string) (handle *Node, err error) {  runArgs := []string{    "-d", // run the container detached    "-t", // allocate a tty for entrypoint logs    // running containers in a container requires privileged    // NOTE: we could try to replicate this with --cap-add, and use less    // privileges, but this flag also changes some mounts that are necessary    // including some ones docker would otherwise do by default.    // for now this is what we want. in the future we may revisit this.    "--privileged",    "--security-opt", "seccomp=unconfined", // also ignore seccomp    "--tmpfs", "/tmp", // various things depend on working /tmp    "--tmpfs", "/run", // systemd wants a writable /run    // some k8s things want /lib/modules    "-v", "/lib/modules:/lib/modules:ro",    "--hostname", name, // make hostname match container name    "--name", name, // ... and set the container name    // label the node with the cluster ID    "--label", clusterLabel,    // label the node with the role ID    "--label", fmt.Sprintf("%s=%s", constants.NodeRoleKey, role),  }  // pass proxy environment variables to be used by node's docker deamon  proxyDetails := getProxyDetails()  for key, val := range proxyDetails.Envs {    runArgs = append(runArgs, "-e", fmt.Sprintf("%s=%s", key, val))  }  // adds node specific args  runArgs = append(runArgs, extraArgs...)  if docker.UsernsRemap() {    // We need this argument in order to make this command work    // in systems that have userns-remap enabled on the docker daemon    runArgs = append(runArgs, "--userns=host")  }  err = docker.Run(    image,    docker.WithRunArgs(runArgs...),    docker.WithMounts(mounts),  )  // we should return a handle so the caller can clean it up  handle = FromName(name)  if err != nil {    return handle, errors.Wrap(err, "docker run error")  }  return handle, nil} 
    ##MoreKind是一个比较简单有趣的项目,Kind的scope定的比较明确和具体,也定的比较小,其实借助 Kind 或者 Kind 的思想,可以做更多的事情,比如:
    • 在单节点部署自己的上层平台
    • 借助容器 mock 节点的方式,优化现有的测试方案
    • 自动化的部署测试
    • 自动化的 e2e 测试

    原文链接:https://mp.weixin.qq.com/s/Vhr0ml1wI1BIoxKqKGTXRg

    58集团云平台架构实践与演进

    玻璃樽 发表了文章 • 0 个评论 • 353 次浏览 • 2019-06-04 19:23 • 来自相关话题

    【编者的话】在17年底,我们分享了《高可用Docker容器云在58集团的实践》这篇文章,对整个容器云基础环境搭建与应用选型进行了详细介绍,本文是在该文章基础之上的进阶篇,是针对具体业务场景的落地解决方案。如果对基础环境选型比较感兴趣,可以查看上篇文章,在本文的 ...查看全部
    【编者的话】在17年底,我们分享了《高可用Docker容器云在58集团的实践》这篇文章,对整个容器云基础环境搭建与应用选型进行了详细介绍,本文是在该文章基础之上的进阶篇,是针对具体业务场景的落地解决方案。如果对基础环境选型比较感兴趣,可以查看上篇文章,在本文的最后会附上相关文章的链接。对于上篇文章讨论过的内容,本文将不再进行详细讨论。后续每个月,云团队都会选择平台中某一具体领域的相关工作进行详细讨论与分享,欢迎大家关注。大家想了解哪方面的实现方案与细节,可进行相应留言。
    #背景

    通过容器化技术,58云计算平台主要解决以下几个问题:

    1. 资源利用率低:通过云化技术可以将资源利用率提升至原有的3-4倍,甚至更高。
    2. 服务扩容效率低:将传统扩容的时间从小时级别降低为分钟级别。
    3. 上线流程不规范:基于同一的镜像模板,约束整个上线过程。

    为了解决上述问题,云团队通过技术选型与反复论证最终决定基于Docker与Kubernetes体系构建整个容器云环境。

    云计算平台的发展历程如下:
    1.png

    #整体架构

    58云计算平台的整体架构如下:
    2.png

    所有容器云的架构都是相似的,这里不做赘述,具体可查看上篇文章。

    云计算平台承载了集团90%以上的业务流量,作为核心的服务管理与上线系统,它并不是独立运作的,为了保证整个上线流程的一致性与流畅度,满足业务日常管理与维护的通用需求,云平台与集团内部多个核心系统与组件进行了相应的对接与联动。
    3.jpg

    在项目管理方面,云平台与代码管理系统、项目管理等系统进行了内部对接,实现了代码从编译到生成镜像,再到环境部署的完全自动化。

    在运维方面,云平台与CMDB、服务树、监控系统等多个运维系统进行了打通与整合,保证整个运维体系的完整性与用户体验的一致性。

    在基础组件方面,集团内部的服务治理体系与常用的中间件系统都针对云平台进行了相应的改造,以适配云平台的工作模式,同时云平台也集成了现有的数据收集组件,保证数据流与用户习惯在云化前后是一致的。

    云平台的使用方面,平台定义了四套环境,四套环境基于唯一的镜像仓库,这使得同一个代码版本可以在不同的环境之间进行流转,在保证代码唯一性的同时也使得相关环境的变更不会传递到下一个环境。
    4.png

    #架构演进

    构建一个适配多种业务场景的容器云有很多细节需要考虑,云团队做了很多工作,由于篇幅关系,这里主要和大家分享58云计算平台在“网络架构”和“服务发现”两个核心组件上的架构实践与演进。
    ##网络架构

    在网络架构方面,通过对比常用的六种容器组网模型,云计算平台选择“bridge+vlan”的方式作为基础网络模型。
    5.png

    原生bridge网络模型存在两个明显的缺点:IP利用率低、缺少网络限速。

    为了解决IP地址利用率低的问题,云平台基于docker的CNM接口开发了网络插件,支持多个宿主间共享同一个容器网段,也支持IP地址在不同的宿主间复用。
    6.png

    这种网络模式下,分配给业务实例的IP地址是随机的,实例每次重启IP地址都可能会发生变更。在推进业务云化的早期,这种模式被业务所接受,但是随着云化进程的不断深入,业务方面提出了更高的要求:固定IP。

    业务需求来源于真实的案例:有些服务要求IP不能发生变化,特别是某些依赖于第三方外部接口的服务。同时集团内部很多系统都是以IP固定为前提,如果IP随机分配,这些现有系统将不可用或很难用,极大的影响用户体验。如果IP固定的需求不能被满足,业务云化将很难推进下去。所以在18年4月份,云平台对网络架构进行了升级,支持了固定IP模式。网络架构的升级很好的保证了业务云化的进程。
    7.png

    固定IP的网络架构基于Kubernetes的CNI接口实现,增加了IP控制器模块,业务每次扩缩容时,都会与IP控制器交互,进行IP的变更。在业务正常升级流程中,归属于业务的IP将不会发生变化。依托于腾讯机房的网络支撑,平台将容器网段的路由规则下发到交换机,实现了容器的全网漂移,而不仅仅局限于固定的交换机。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

    针对网络限速的需求,云平台结合容器网络虚拟化的特性,基于自研监控与tc工具实现了容器网络限速的能力。
    8.jpg

    标准的tc工具只支持单向限速,即出口流量限速。单方向限速无法满足业务的实际需求。在深入研究容器网络虚拟化的原理后,我们发现,容器在做虚拟化时,会创建一个网卡对,他们是对等的网络设备,在宿主机上体现为veth,在容器内部体现为eth0。基于这个特性,我们实现了双向限速:即对两块网卡同时做出口流量限速。由于是对等网卡,所以对veth做出口流量限速就相当于对eth0做入口流量限速。

    在网络限速的基础上,云平台又进行了多维度的完善,分别支持动态限速、秒级限速与弹性限速等网络应用场景,极大的实现带宽复用。
    ##服务发现

    服务发现是容器云平台中非常核心的服务组件,是流量的入口,也是服务对外暴露的接口。云平台中IP动态分配,节点弹性伸缩,需要有一套自动变更负载均衡器的方式。对于后端服务,集团有成熟的服务治理框架与体系,在云化过程中,中间件团队对服务治理体系进行了改造使其可以适配不断变化的云环境。对于前端服务,集团是基于Nginx做负载均衡,并且没有一套支持IP自动变更的架构。为了实现前端服务的云化,云平台团队与运维团队共同设计了全新的服务发现架构。

    在调研阶段,云团队也调研了Kubernetes自带的服务发现机制,这一机制无法提供复杂的负载均衡策略,并且无法满足业务在特殊场景下需要对部分节点摘除流量的需求,所以最终我们否定了这一方案。
    9.png

    这是业界典型的服务发现架构。服务端和负载均衡器通过Consul进行解耦。服务注册在Consul中,负载均衡器通过Watch Consul中目录的变化来实时感知节点变更。在云化的早期,为了满足快速上线的需求,云平台对集团内部的Java Web框架进行了修改,使得其以心跳的方式自动注册到Consul中。这很好的解决了负载均衡器自动感知业务变更的问题以及云化过程中的流量灰度问题。

    这一架构也引入了新的问题:调试问题与多语言扩展问题。由于是Java框架代理业务注册到Consul中,这使得活跃节点无法被下掉流量从而进行调试,但实际场景中很多故障调试需要下掉流量后才能进行。对Java语言的支持是通过修改框架来完成的,而集团中还有很多其他类型语言,比如PHP、Node.js和Go等。对每种接入语言或框架都需要以修改代码的方式才能接入到云平台中来,开发成本高并且对业务不友好。Consul被直接暴露给服务方,也增加了Consul的安全风险。

    基于此,在18年4月,云平台对整个服务发现架构进行了升级。
    10.png

    新的服务发现架构中,业务与Consul中间增加了Proxy代理层,代理层通过Watch Kubernetes事件实时感知业务节点信息的变化,配合健康检查功能可以保证业务节点在变更时流量无损。基于代理层,任意语言的程序不需要做任何修改,仅通过简单配置即可接入到云平台。服务的注册与发现托管到云平台,Consul组件对业务透明,开发人员使用更友好。
    #复盘与反思

    回顾这两年来的容器云架构演进过程与业务云化历程,复盘遇到的棘手问题及其解决方案,与大家共同探讨。

    在整个云平台的设计之初,我们有如下的设计考量:
    11.png

    容器云都会面临一个问题:对于业务来说,是容器还是虚拟机?虚拟机是业务习惯的使用模式,业务更容易接受。容器是以服务为核心,服务存在即存在,服务关闭即销毁,不再是虚拟机以机器为核心的模式。思虑再三,最终决定以容器的模式来定位,全新的平台提供全新的服务模式与体验。

    虽然是以容器为核心,但容器中可运行单进程也可运行多进程。我们规范容器中只运行一个业务进程,防止由于运行多进程而导致业务之间相互干扰情况的发生,这类问题很难进行排查与定位。

    去Agent化:在传统物理机模式下,一台物理机上可能会有多个Agent:运维管控Agent、监控Agent、业务自定义Agent等。云化后面临一个现实的问题:我们需要在每个容器中都安装这些Agent么?如果每个容器中都集成这些Agent,对云平台来说是简单的,这没有任何工作量。但是一台物理机上可能会运行上百个容器,这些Agent数量也会大量膨胀,带来系统负载的增加的问题。基于此,云平台投入大量的精力来实现容器的无Agent化,即在所有功能正常运行的前提下,Agent只运行在宿主上,不会运行在容器中。

    业务云化过程中云团队遇到了很多问题案例,并形成自己独特的解决方案。这里选择了几个有代表性的案例,与大家进行分享。
    12.png

    服务启动耗CPU过高是云化早期时遇到的棘手问题。这个问题产生的原因很多:Java的语言特性导致JVM是在运行中逐步进行优化的;内部的很多开发框架都是在流量过来时才初始化链接;某些业务资源也是在流量过来时才进行初始化。当流量分发过来时,多种资源同时初始化导致服务需要大量的CPU资源,超过了平台为其分配的CPU配额,服务出现大量超时与抛弃。这一问题的简单解法是多分配CPU资源,但从长远角度来看这会导致资源利用无法有效把控,也会影响弹性调度的效果。最终,我们从两个维度解决这一问题:在调用方增加预热策略,流量逐步分发过来,CPU资源使用更平滑;在服务方增加预热方法,默认初始化链接等资源,同时引导用户进行自定义的初始化。

    在容器监控维度方面,由于默认的监控数据是基于随机采样的分钟级数据,导致当服务出现短期秒级的CPU波动时,监控系统无法捕获,从而影响问题的排查与定位。针对这一问题,云平台对监控维度进行了深化,增加了容器级别秒级监控,实时采集每分钟的最大值上报,便于问题的排查与跟踪。
    13.png

    由于Cgroups只对CPU和内存资源进行了隔离与限速,并没有对系统负载进行隔离,如果容器中运行进程或线程数过多,会导致宿主机整体的负载波动,进而对上面的所有服务都会造成影响。

    云平台增加了两个维度的控制策略:容器级别的最大线程数控制,防止由于单个容器线程数过多会对宿主造成影响。宿主级别的过载保护,当宿主上的负载过高时,过载保护策略会自动触发宿主上的容器进行漂移,直至负载降至合理的范围。

    云平台必须关闭swap交换分区,这是一个深刻的经验教训。在云化的早期,云平台的交换分区没有关闭,部分服务经常出现由于使用交换分区而导致的耗时随机抖动,这类问题很难排查,耗费了大量的精力。

    58云计算平台核心软件版本变迁过程如下:
    14.png

    #后记

    容器云在58集团的实践与探索过程中,云团队做了很多技术选型与优化工作,也进行了很多技术方案的架构演进工作,究其根本原因是58集团是发展了十多年的互联网公司,它有自己独特的业务场景和成熟的开发体系与基础组件,容器云在集团内部起步较晚,内部系统很难为云平台做适配,只能云平台通过不断演进来适配不停发展的业务场景。这也是在较有规模的互联网公司做基础架构与创业公司的区别和挑战所在。所幸在团队的共同努力下,我们用1年多的时间完成了集团流量服务的云化工作。目前,集团内部还有很多服务需要云化,未来的挑战更大。

    原文链接:https://mp.weixin.qq.com/s/gJM5-DByvMH54QhVsJ5HEA

    详解Eureka缓存机制

    阿娇 发表了文章 • 0 个评论 • 216 次浏览 • 2019-06-04 14:45 • 来自相关话题

    【编者的话】Eureka是Netflix开源的、用于实现服务注册和发现的服务。Spring Cloud Eureka基于Eureka进行二次封装,增加了更人性化的UI,使用更为方便。但是由于Eureka本身存在较多缓存,服务状态更新滞后,最常见的状况是:服务下 ...查看全部
    【编者的话】Eureka是Netflix开源的、用于实现服务注册和发现的服务。Spring Cloud Eureka基于Eureka进行二次封装,增加了更人性化的UI,使用更为方便。但是由于Eureka本身存在较多缓存,服务状态更新滞后,最常见的状况是:服务下线后状态没有及时更新,服务消费者调用到已下线的服务导致请求失败。本文基于Spring Cloud Eureka 1.4.4.RELEASE,在默认region和zone的前提下,介绍Eureka的缓存机制。
    #一、AP特性

    从CAP理论看,Eureka是一个AP系统,优先保证可用性(A)和分区容错性(P),不保证强一致性(C),只保证最终一致性,因此在架构中设计了较多缓存。
    1.jpg

    Eureka高可用架构
    #二、服务状态

    Eureka服务状态enum类:com.netflix.appinfo.InstanceInfo.InstanceStatus
    2.png

    #三、Eureka Server

    在Eureka高可用架构中,Eureka Server也可以作为Client向其他server注册,多节点相互注册组成Eureka集群,集群间相互视为peer。Eureka Client向Server注册、续约、更新状态时,接受节点更新自己的服务注册信息后,逐个同步至其他peer节点。如果你想和更多Spring Cloud技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

    【注意】如果server-A向server-B节点单向注册,则server-A视server-B为peer节点,server-A接受的数据会同步给server-B,但server-B接受的数据不会同步给server-A。
    ##缓存机制

    Eureka Server存在三个变量:(registry、readWriteCacheMap、readOnlyCacheMap)保存服务注册信息,默认情况下定时任务每30s将readWriteCacheMap同步至readOnlyCacheMap,每60s清理超过90s未续约的节点,Eureka Client每30s从readOnlyCacheMap更新服务注册信息,而UI则从registry更新服务注册信息。
    3.jpg

    三级缓存:
    4.jpg

    缓存相关配置:
    5.jpg

    关键类:
    6.jpg

    #四、Eureka Client

    Eureka Client存在两种角色:服务提供者和服务消费者,作为服务消费者一般配合Ribbon或Feign(Feign内部使用Ribbon)使用。Eureka Client启动后,作为服务提供者立即向Server注册,默认情况下每30s续约(renew);作为服务消费者立即向Server全量更新服务注册信息,默认情况下每30s增量更新服务注册信息;Ribbon延时1s向Client获取使用的服务注册信息,默认每30s更新使用的服务注册信息,只保存状态为UP的服务。

    二级缓存:
    7.jpg

    缓存相关配置:
    8.jpg

    关键类:
    9.jpg

    #五、默认配置下服务消费者最长感知时间

    10.jpg

    考虑如下情况:

    * 0s时服务未通知Eureka Client直接下线;
    * 29s时第一次过期检查evict未超过90s;
    * 89s时第二次过期检查evict未超过90s;
    * 149s时第三次过期检查evict未续约时间超过了90s,故将该服务实例从registry和readWriteCacheMap中删除;
    * 179s时定时任务从readWriteCacheMap更新至readOnlyCacheMap;
    * 209s时Eureka Client从Eureka Server的readOnlyCacheMap更新;
    * 239s时Ribbon从Eureka Client更新。

    因此,极限情况下服务消费者最长感知时间将无限趋近240s。
    11.jpg

    #六、应对措施

    服务注册中心在选择使用Eureka时说明已经接受了其优先保证可用性(A)和分区容错性(P)、不保证强一致性(C)的特点。如果需要优先保证强一致性(C),则应该考虑使用ZooKeeper等CP系统作为服务注册中心。分布式系统中一般配置多节点,单个节点服务上线的状态更新滞后并没有什么影响,这里主要考虑服务下线后状态更新滞后的应对措施。
    ##Eureka Server

    1、缩短readOnlyCacheMap更新周期。缩短该定时任务周期可减少滞后时间。
    eureka.server.responsecCacheUpdateIntervalMs: 10000  # Eureka Server readOnlyCacheMap更新周期

    2、关闭readOnlyCacheMap。中小型系统可以考虑该方案,Eureka Client直接从readWriteCacheMap更新服务注册信息。
    eureka.server.useReadOnlyResponseCache: false        # 是否使用readOnlyCacheMap

    ##Eureka Client

    1、服务消费者使用容错机制。如Spring Cloud Retry和Hystrix,Ribbon、Feign、Zuul都可以配置Retry,服务消费者访问某个已下线节点时一般报ConnectTimeout,这时可以通过Retry机制重试下一个节点。

    2、服务消费者缩短更新周期。Eureka Client和Ribbon二级缓存影响状态更新,缩短这两个定时任务周期可减少滞后时间,例如配置:
    eureka.client.registryFetchIntervalSeconds: 5        # Eureka Client更新周期
    ribbon.ServerListRefreshInterval: 2000 # Ribbon更新周期

    3、服务提供者保证服务正常下线。服务下线时使用kill或kill -15命令,避免使用kill -9命令,kill或kill -15命令杀死进程时将触发Eureka Client的shutdown()方法,主动删除Server的registry和readWriteCacheMap中的注册信息,不必依赖Server的evict清除。

    4、服务提供者延迟下线。服务下线之前先调用接口使Eureka Server中保存的服务状态为DOWN或OUT_OF_SERVICE后再下线,二者时间差根据缓存机制和配置决定,比如默认情况下调用接口后延迟90s再下线服务即可保证服务消费者不会调用已下线服务实例。
    #七、网关实现服务下线实时感知

    在软件工程中,没有一个问题是中间层解决不了的,而网关是服务提供者和服务消费者的中间层。以Spring Cloud Zuul网关为例,网关作为Eureka Client保存了服务注册信息,服务消费者通过网关将请求转发给服务提供者,只需要做到服务提供者下线时通知网关在自己保存的服务列表中使该服务失效。为了保持网关的独立性,可实现一个独立服务接收下线通知并协调网关集群。下篇文章将详细介绍网关如何实现服务下线实时感知,敬请期待!

    原文链接:https://mp.weixin.qq.com/s/zwoIDzX8WouYVrBfMJMpJQ

    服务端高并发分布式架构演进之路

    翔宇 发表了文章 • 0 个评论 • 208 次浏览 • 2019-06-04 11:16 • 来自相关话题

    【编者的话】本文以淘宝作为例子,介绍从一百个并发到千万级并发情况下服务端的架构的演进过程,同时列举出每个演进阶段会遇到的相关技术,让大家对架构的演进有一个整体的认知,文章最后汇总了一些架构设计的原则。 #基本概念 在介绍架构之前,为 ...查看全部
    【编者的话】本文以淘宝作为例子,介绍从一百个并发到千万级并发情况下服务端的架构的演进过程,同时列举出每个演进阶段会遇到的相关技术,让大家对架构的演进有一个整体的认知,文章最后汇总了一些架构设计的原则。
    #基本概念

    在介绍架构之前,为了避免部分读者对架构设计中的一些概念不了解,下面对几个最基础的概念进行介绍:
    ##分布式

    系统中的多个模块在不同服务器上部署,即可称为分布式系统,如Tomcat和数据库分别部署在不同的服务器上,或两个相同功能的Tomcat分别部署在不同服务器上。
    ##高可用

    系统中部分节点失效时,其他节点能够接替它继续提供服务,则可认为系统具有高可用性。
    ##集群

    一个特定领域的软件部署在多台服务器上并作为一个整体提供一类服务,这个整体称为集群。如Zookeeper中的Master和Slave分别部署在多台服务器上,共同组成一个整体提供集中配置服务。在常见的集群中,客户端往往能够连接任意一个节点获得服务,并且当集群中一个节点掉线时,其他节点往往能够自动的接替它继续提供服务,这时候说明集群具有高可用性。
    ##负载均衡

    请求发送到系统时,通过某些方式把请求均匀分发到多个节点上,使系统中每个节点能够均匀的处理请求负载,则可认为系统是负载均衡的。
    ##正向代理和反向代理

    系统内部要访问外部网络时,统一通过一个代理服务器把请求转发出去,在外部网络看来就是代理服务器发起的访问,此时代理服务器实现的是正向代理;当外部请求进入系统时,代理服务器把该请求转发到系统中的某台服务器上,对外部请求来说,与之交互的只有代理服务器,此时代理服务器实现的是反向代理。简单来说,正向代理是代理服务器代替系统内部来访问外部网络的过程,反向代理是外部请求访问系统时通过代理服务器转发到内部服务器的过程。
    #架构演进

    ##单机架构

    1.png

    以淘宝作为例子。在网站最初时,应用数量与用户数都较少,可以把Tomcat和数据库部署在同一台服务器上。浏览器往www.taobao.com发起请求时,首先经过DNS服务器(域名系统)把域名转换为实际IP地址10.102.4.1,浏览器转而访问该IP对应的Tomcat。如果你想和更多Tomcat技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

    随着用户数的增长,Tomcat和数据库之间竞争资源,单机性能不足以支撑业务
    ##第一次演进:Tomcat与数据库分开部署

    2.png

    Tomcat和数据库分别独占服务器资源,显著提高两者各自性能。

    随着用户数的增长,并发读写数据库成为瓶颈
    ##第二次演进:引入本地缓存和分布式缓存

    3.png

    在Tomcat同服务器上或同JVM中增加本地缓存,并在外部增加分布式缓存,缓存热门商品信息或热门商品的html页面等。通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。其中涉及的技术包括:使用memcached作为本地缓存,使用Redis作为分布式缓存,还会涉及缓存一致性、缓存穿透/击穿、缓存雪崩、热点数据集中失效等问题。

    缓存抗住了大部分的访问请求,随着用户数的增长,并发压力主要落在单机的Tomcat上,响应逐渐变慢
    ##第三次演进:引入反向代理实现负载均衡

    4.png

    在多台服务器上分别部署Tomcat,使用反向代理软件(Nginx)把请求均匀分发到每个Tomcat中。此处假设Tomcat最多支持100个并发,Nginx最多支持50000个并发,那么理论上Nginx把请求分发到500个Tomcat上,就能抗住50000个并发。其中涉及的技术包括:Nginx、HAProxy,两者都是工作在网络第七层的反向代理软件,主要支持http协议,还会涉及session共享、文件上传下载的问题。

    反向代理使应用服务器可支持的并发量大大增加,但并发量的增长也意味着更多请求穿透到数据库,单机的数据库最终成为瓶颈
    ##第四次演进:数据库读写分离

    5.png

    把数据库划分为读库和写库,读库可以有多个,通过同步机制把写库的数据同步到读库,对于需要查询最新写入数据场景,可通过在缓存中多写一份,通过缓存获得最新数据。其中涉及的技术包括:Mycat,它是数据库中间件,可通过它来组织数据库的分离读写和分库分表,客户端通过它来访问下层数据库,还会涉及数据同步,数据一致性的问题。

    业务逐渐变多,不同业务之间的访问量差距较大,不同业务直接竞争数据库,相互影响性能
    ##第五次演进:数据库按业务分库

    6.png

    把不同业务的数据保存到不同的数据库中,使业务之间的资源竞争降低,对于访问量大的业务,可以部署更多的服务器来支撑。这样同时导致跨业务的表无法直接做关联分析,需要通过其他途径来解决,但这不是本文讨论的重点,有兴趣的可以自行搜索解决方案。

    随着用户数的增长,单机的写库会逐渐会达到性能瓶颈
    ##第六次演进:把大表拆分为小表

    7.png

    比如针对评论数据,可按照商品ID进行hash,路由到对应的表中存储;针对支付记录,可按照小时创建表,每个小时表继续拆分为小表,使用用户ID或记录编号来路由数据。只要实时操作的表数据量足够小,请求能够足够均匀的分发到多台服务器上的小表,那数据库就能通过水平扩展的方式来提高性能。其中前面提到的Mycat也支持在大表拆分为小表情况下的访问控制。

    这种做法显著的增加了数据库运维的难度,对DBA的要求较高。数据库设计到这种结构时,已经可以称为分布式数据库,但是这只是一个逻辑的数据库整体,数据库里不同的组成部分是由不同的组件单独来实现的,如分库分表的管理和请求分发,由Mycat实现,SQL的解析由单机的数据库实现,读写分离可能由网关和消息队列来实现,查询结果的汇总可能由数据库接口层来实现等等,这种架构其实是MPP(大规模并行处理)架构的一类实现。

    目前开源和商用都已经有不少MPP数据库,开源中比较流行的有Greenplum、TiDB、Postgresql XC、HAWQ等,商用的如南大通用的GBase、睿帆科技的雪球DB、华为的LibrA等等,不同的MPP数据库的侧重点也不一样,如TiDB更侧重于分布式OLTP场景,Greenplum更侧重于分布式OLAP场景,这些MPP数据库基本都提供了类似Postgresql、Oracle、MySQL那样的SQL标准支持能力,能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,最终由数据库本身汇总数据进行返回,也提供了诸如权限管理、分库分表、事务、数据副本等能力,并且大多能够支持100个节点以上的集群,大大降低了数据库运维的成本,并且使数据库也能够实现水平扩展。

    数据库和Tomcat都能够水平扩展,可支撑的并发大幅提高,随着用户数的增长,最终单机的Nginx会成为瓶颈
    ##第七次演进:使用LVS或F5来使多个Nginx负载均衡

    8.png

    由于瓶颈在Nginx,因此无法通过两层的Nginx来实现多个Nginx的负载均衡。图中的LVS和F5是工作在网络第四层的负载均衡解决方案,其中LVS是软件,运行在操作系统内核态,可对TCP请求或更高层级的网络协议进行转发,因此支持的协议更丰富,并且性能也远高于Nginx,可假设单机的LVS可支持几十万个并发的请求转发;F5是一种负载均衡硬件,与LVS提供的能力类似,性能比LVS更高,但价格昂贵。由于LVS是单机版的软件,若LVS所在服务器宕机则会导致整个后端系统都无法访问,因此需要有备用节点。可使用keepalived软件模拟出虚拟IP,然后把虚拟IP绑定到多台LVS服务器上,浏览器访问虚拟IP时,会被路由器重定向到真实的LVS服务器,当主LVS服务器宕机时,keepalived软件会自动更新路由器中的路由表,把虚拟IP重定向到另外一台正常的LVS服务器,从而达到LVS服务器高可用的效果。

    此处需要注意的是,上图中从Nginx层到Tomcat层这样画并不代表全部Nginx都转发请求到全部的Tomcat,在实际使用时,可能会是几个Nginx下面接一部分的Tomcat,这些Nginx之间通过keepalived实现高可用,其他的Nginx接另外的Tomcat,这样可接入的Tomcat数量就能成倍的增加。

    由于LVS也是单机的,随着并发数增长到几十万时,LVS服务器最终会达到瓶颈,此时用户数达到千万甚至上亿级别,用户分布在不同的地区,与服务器机房距离不同,导致了访问的延迟会明显不同
    ##第八次演进:通过DNS轮询实现机房间的负载均衡

    9.png

    在DNS服务器中可配置一个域名对应多个IP地址,每个IP地址对应到不同的机房里的虚拟IP。当用户访问www.taobao.com时,DNS服务器会使用轮询策略或其他策略,来选择某个IP供用户访问。此方式能实现机房间的负载均衡,至此,系统可做到机房级别的水平扩展,千万级到亿级的并发量都可通过增加机房来解决,系统入口处的请求并发量不再是问题。

    随着数据的丰富程度和业务的发展,检索、分析等需求越来越丰富,单单依靠数据库无法解决如此丰富的需求
    ##第九次演进:引入NoSQL数据库和搜索引擎等技术

    10.png

    当数据库中的数据多到一定规模时,数据库就不适用于复杂的查询了,往往只能满足普通查询的场景。对于统计报表场景,在数据量大时不一定能跑出结果,而且在跑复杂查询时会导致其他查询变慢,对于全文检索、可变数据结构等场景,数据库天生不适用。因此需要针对特定的场景,引入合适的解决方案。如对于海量文件存储,可通过分布式文件系统HDFS解决,对于key value类型的数据,可通过HBase和Redis等方案解决,对于全文检索场景,可通过搜索引擎如ElasticSearch解决,对于多维分析场景,可通过Kylin或Druid等方案解决。

    当然,引入更多组件同时会提高系统的复杂度,不同的组件保存的数据需要同步,需要考虑一致性的问题,需要有更多的运维手段来管理这些组件等。

    引入更多组件解决了丰富的需求,业务维度能够极大扩充,随之而来的是一个应用中包含了太多的业务代码,业务的升级迭代变得困难
    ##第十次演进:大应用拆分为小应用

    11.png

    按照业务板块来划分应用代码,使单个应用的职责更清晰,相互之间可以做到独立升级迭代。这时候应用之间可能会涉及到一些公共配置,可以通过分布式配置中心Zookeeper来解决。

    不同应用之间存在共用的模块,由应用单独管理会导致相同代码存在多份,导致公共功能升级时全部应用代码都要跟着升级
    ##第十一次演进:复用的功能抽离成微服务

    12.png

    如用户管理、订单、支付、鉴权等功能在多个应用中都存在,那么可以把这些功能的代码单独抽取出来形成一个单独的服务来管理,这样的服务就是所谓的微服务,应用和服务之间通过HTTP、TCP或RPC请求等多种方式来访问公共服务,每个单独的服务都可以由单独的团队来管理。此外,可以通过Dubbo、SpringCloud等框架实现服务治理、限流、熔断、降级等功能,提高服务的稳定性和可用性。

    不同服务的接口访问方式不同,应用代码需要适配多种访问方式才能使用服务,此外,应用访问服务,服务之间也可能相互访问,调用链将会变得非常复杂,逻辑变得混乱
    ##第十二次演进:引入企业服务总线ESB屏蔽服务接口的访问差异

    13.png

    通过ESB统一进行访问协议转换,应用统一通过ESB来访问后端服务,服务与服务之间也通过ESB来相互调用,以此降低系统的耦合程度。这种单个应用拆分为多个应用,公共服务单独抽取出来来管理,并使用企业消息总线来解除服务之间耦合问题的架构,就是所谓的SOA(面向服务)架构,这种架构与微服务架构容易混淆,因为表现形式十分相似。个人理解,微服务架构更多是指把系统里的公共服务抽取出来单独运维管理的思想,而SOA架构则是指一种拆分服务并使服务接口访问变得统一的架构思想,SOA架构中包含了微服务的思想。

    业务不断发展,应用和服务都会不断变多,应用和服务的部署变得复杂,同一台服务器上部署多个服务还要解决运行环境冲突的问题,此外,对于如大促这类需要动态扩缩容的场景,需要水平扩展服务的性能,就需要在新增的服务上准备运行环境,部署服务等,运维将变得十分困难。
    ##第十三次演进:引入容器化技术实现运行环境隔离与动态服务管理

    14.png

    目前最流行的容器化技术是Docker,最流行的容器管理服务是Kubernetes(K8S),应用/服务可以打包为Docker镜像,通过K8S来动态分发和部署镜像。Docker镜像可理解为一个能运行你的应用/服务的最小的操作系统,里面放着应用/服务的运行代码,运行环境根据实际的需要设置好。把整个“操作系统”打包为一个镜像后,就可以分发到需要部署相关服务的机器上,直接启动Docker镜像就可以把服务起起来,使服务的部署和运维变得简单。

    在大促的之前,可以在现有的机器集群上划分出服务器来启动Docker镜像,增强服务的性能,大促过后就可以关闭镜像,对机器上的其他服务不造成影响(在3.14节之前,服务运行在新增机器上需要修改系统配置来适配服务,这会导致机器上其他服务需要的运行环境被破坏)。

    使用容器化技术后服务动态扩缩容问题得以解决,但是机器还是需要公司自身来管理,在非大促的时候,还是需要闲置着大量的机器资源来应对大促,机器自身成本和运维成本都极高,资源利用率低
    ##第十四次演进:以云平台承载系统

    15.png

    系统可部署到公有云上,利用公有云的海量机器资源,解决动态硬件资源的问题,在大促的时间段里,在云平台中临时申请更多的资源,结合Docker和K8S来快速部署服务,在大促结束后释放资源,真正做到按需付费,资源利用率大大提高,同时大大降低了运维成本。

    所谓的云平台,就是把海量机器资源,通过统一的资源管理,抽象为一个资源整体,在之上可按需动态申请硬件资源(如CPU、内存、网络等),并且之上提供通用的操作系统,提供常用的技术组件(如Hadoop技术栈,MPP数据库等)供用户使用,甚至提供开发好的应用,用户不需要关系应用内部使用了什么技术,就能够解决需求(如音视频转码服务、邮件服务、个人博客等)。在云平台中会涉及如下几个概念:

    * IaaS:基础设施即服务。对应于上面所说的机器资源统一为资源整体,可动态申请硬件资源的层面;
    * PaaS:平台即服务。对应于上面所说的提供常用的技术组件方便系统的开发和维护;
    * SaaS:软件即服务。对应于上面所说的提供开发好的应用或服务,按功能或性能要求付费。

    至此,以上所提到的从高并发访问问题,到服务的架构和系统实施的层面都有了各自的解决方案,但同时也应该意识到,在上面的介绍中,其实是有意忽略了诸如跨机房数据同步、分布式事务实现等等的实际问题,这些问题以后有机会再拿出来单独讨论。
    #架构设计总结

    ##架构的调整是否必须按照上述演变路径进行?

    不是的,以上所说的架构演变顺序只是针对某个侧面进行单独的改进,在实际场景中,可能同一时间会有几个问题需要解决,或者可能先达到瓶颈的是另外的方面,这时候就应该按照实际问题实际解决。如在政府类的并发量可能不大,但业务可能很丰富的场景,高并发就不是重点解决的问题,此时优先需要的可能会是丰富需求的解决方案。
    ##对于将要实施的系统,架构应该设计到什么程度?

    对于单次实施并且性能指标明确的系统,架构设计到能够支持系统的性能指标要求就足够了,但要留有扩展架构的接口以便不备之需。对于不断发展的系统,如电商平台,应设计到能满足下一阶段用户量和性能指标要求的程度,并根据业务的增长不断的迭代升级架构,以支持更高的并发和更丰富的业务。
    ##服务端架构和大数据架构有什么区别?

    所谓的“大数据”其实是海量数据采集清洗转换、数据存储、数据分析、数据服务等场景解决方案的一个统称,在每一个场景都包含了多种可选的技术,如数据采集有Flume、Sqoop、Kettle等,数据存储有分布式文件系统HDFS、FastDFS,NoSQL数据库HBase、MongoDB等,数据分析有Spark技术栈、机器学习算法等。总的来说大数据架构就是根据业务的需求,整合各种大数据组件组合而成的架构,一般会提供分布式存储、分布式计算、多维分析、数据仓库、机器学习算法等能力。而服务端架构更多指的是应用组织层面的架构,底层能力往往是由大数据架构来提供。
    #有没有一些架构设计的原则?


    * N+1设计。系统中的每个组件都应做到没有单点故障;
    * 回滚设计。确保系统可以向前兼容,在系统升级时应能有办法回滚版本;
    * 禁用设计。应该提供控制具体功能是否可用的配置,在系统出现故障时能够快速下线功能;
    * 监控设计。在设计阶段就要考虑监控的手段;
    * 多活数据中心设计。若系统需要极高的高可用,应考虑在多地实施数据中心进行多活,至少在一个机房断电的情况下系统依然可用;
    * 采用成熟的技术。刚开发的或开源的技术往往存在很多隐藏的bug,出了问题没有商业支持可能会是一个灾难;
    * 资源隔离设计。应避免单一业务占用全部资源;
    * 架构应能水平扩展。系统只有做到能水平扩展,才能有效避免瓶颈问题;
    * 非核心则购买。非核心功能若需要占用大量的研发资源才能解决,则考虑购买成熟的产品;
    * 使用商用硬件。商用硬件能有效降低硬件故障的机率;
    * 快速迭代。系统应该快速开发小功能模块,尽快上线进行验证,早日发现问题大大降低系统交付的风险;
    * 无状态设计。服务接口应该做成无状态的,当前接口的访问不依赖于接口上次访问的状态。

    原文链接:https://segmentfault.com/a/1190000018626163

    微服务化后缓存怎么做

    大卫 发表了文章 • 0 个评论 • 174 次浏览 • 2019-06-03 15:41 • 来自相关话题

    【编者的话】最近接手的代码中遇到几个缓存的问题,存在一些设计原则的问题,这里总结一下,希望可以对你有帮助。 #问题 问题1: 店铺数据的获取,将用户关注的数据放在店铺信息一起返回。 对外提供的接口: ...查看全部
    【编者的话】最近接手的代码中遇到几个缓存的问题,存在一些设计原则的问题,这里总结一下,希望可以对你有帮助。
    #问题

    问题1: 店铺数据的获取,将用户关注的数据放在店铺信息一起返回。

    对外提供的接口:
    List getPageShop(final Query query,final Boolean cache);

    返回的店铺信息:
    public class Shop {

    public static final long DEFAULT_PRIORITY = 10L;

    /**
    * 唯一标识
    */
    private Long id;
    //省略了店铺其他信息
    /**
    * 用户关注
    */
    private ShopAttention attention;
    }

    当调用方设置cache为true时,因为有缓存的存在,获取不到用户是否关注的数据。

    问题2: 统计店铺的被关注数导致的慢SQL,导致数据库cpu飙高,影响到了整个应用。

    SQL:
    SELECT shop_id, count(user_Id) as attentionNumber
    FROM shop_attention
    WHERE shop_id IN

    #{shopId}

    GROUP BY shopId

    这两种代码的写法都是基于一个基准。

    不同的地方的缓存策略不一样,比如我更新的地方,查找数据时不能缓存,页面展示的查找的地方需要缓存。 既然服务提供方不知道该不该缓存,那就不管了,交给调用方去管理。如果你想和更多微服务技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

    这种假设本身没什么问题,但是忽略了另外一个原则,服务的内聚性。不应该被外部知道的就没必要暴露给外部。

    无论是面向过程的C,还是面向对象的语言,都强调内聚性,也就是高内聚,低耦合。单体应用中应当遵循这个原则,微服务同样遵循这个原则。但是在实际过程中,我们发现做到高内聚并不简单。我们必须要时时刻刻审视方法/服务的边界,只有确定好职责边界,才能写出高内聚的代码。
    #问题分析

    第一个问题,从缓存的角度来看,是忽略了数据的更新频繁性以及数据获取的不同场景。

    对于店铺这样一个大的聚合根,本身包含的信息很多,有些数据可能会被频繁更改的,有些则会很少更新的。那么不同的修改频率,是否缓存/缓存策略自然不同,使用同一个参数Boolean cache来控制显然不妥

    第二个问题,这种统计类的需求使用SQL统计是一种在数据量比较小的情况下的权宜之计,当数据规模增大后,必须要使用离线计算或者流式计算来解决。它本身是一个慢SQL,所以必须要控制号调用量,这种统计的数据量的时效性应该由服务方控制,不需要暴露给调用方。否则就会出现上述的问题,调用方并不清楚其中的逻辑,不走缓存的话就会使得调用次数增加,QPS的增加会导致慢SQL打垮数据库。
    #解法

    缓存更新本身就是一个难解的问题,在微服务化后,多个服务就更加复杂了。涉及到跨服务的多级缓存一致性的问题。

    所以对大部分的业务,我们可以遵循这样的原则来简单有效处理。

    对数据的有效性比较敏感的调用都收敛到服务内部(领域内部应该更合适),不要暴露给调用方。

    领域内部做数据的缓存失效控制。

    缓存预计算(有些页面的地方不希望首次打开慢)的逻辑也应该放在领域内控制,不要暴露给调用方。

    在领域内部控制在不同的地方使用不同的缓存策略,比如更新数据的地方需要获取及时的数据。比如商品的价格,和商品的所属类目更新频次不同,需要有不同的过期时间。

    跨服务调用为了减少rpc调用,可以再进行一层缓存。因为这些调用可以接受过期的数据,再进行一层缓存没问题,expired time叠加也没多大影响(expire time在这边主要是影响缓存的命中数)

    以上述店铺查询问题改造为例
    1.png

    扩展:如果后续有case在跨服务的调用时,对数据的过期比较敏感,并且在调用方也做了缓存,那就是跨服务的多级缓存一致性的问题。那就需要服务方告知调用方缓存何时失效,使用消息队列or其他方式来实现。

    作者:方丈的寺院
    原文:https://fangzhang.blog.csdn.net/article/details/89892575

    闲聊我心中的运维开发

    aoxiang 发表了文章 • 0 个评论 • 191 次浏览 • 2019-06-03 12:21 • 来自相关话题

    #前言 在我入职上家公司的运维部之前,我所以为的运维工程师只是修修电脑,拉拉网线,布布机器。 诸不知,运维所涉及的知识面、专业点非常广,对从业人员素质也要求非常高,运维工作在大型互联网公司的重要性不比业务开发差。且分类繁多 ...查看全部
    #前言
    在我入职上家公司的运维部之前,我所以为的运维工程师只是修修电脑,拉拉网线,布布机器。

    诸不知,运维所涉及的知识面、专业点非常广,对从业人员素质也要求非常高,运维工作在大型互联网公司的重要性不比业务开发差。且分类繁多:

    * 桌面运维工程师
    * 业务运维工程师
    * DBA工程师
    * 配置工程师
    * 运维开发工程师
    * 以及其它....

    1.png

    原本准备写篇前端眼中的运维开发,恰巧前组长写了两篇结合自身六七年开发经验写的体会。用他的文章来阐述再合适不过了。以下来自其投稿以及穿插一些知识普及。
    #DevOps:打破协作壁垒
    来自维基百科:
    2.png

    DevOps(Development和Operations的组合詞)是一种重视「软件开发人员(Dev)」和「IT运维技术人员(Ops)」之间沟通合作的文化、运动或慣例。如果你想和更多DevOps技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

    透过自动化「软件交付」和「架构变更」的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。

    传统的软件组织将开发、IT运营和质量保障设为各自分离的部门,在这种环境下如何采用新的开发方法(例如敏捷软件开发),是一个重要的课题。

    按照从前的工作方式,开发和部署,不需要IT支持或者QA深入的跨部门的支持;

    而现在却需要极其紧密的多部门协作。而DevOps考虑的还不止是软件部署,它是一套针对这几个部门间沟通与协作问题的流程和方法。
    3.png

    具体来说,就是在 软件交付和部署过程中提高沟通与协作的效率,旨在更快、更可靠的的发布更高质量的产品。
    #运维开发的价值
    从岗位职责来看,运维开发要做的工作是:

    通过开发技能帮助运维实现运维工作的自动化。说白了就是“辅助”,或者说是运维的臂膀,需要把运维中遇到的问题提供平台查询,或者把一些常见的重复操作给抽象出来做成工具,减少运维的人工介入。
    4.png

    运维服务伴随并支撑着业务发展的整个生命周期。

    而DevOps将运维服务的执行方式升级为更加软件工程化的手段,减少人肉操作,DevOps 强调自动化、拉动式来提高团队交付效率与质量。

    而传统的运维需要谋求技术转型,从原来只关注操作系统层面的技术已经不够了,还要增加对程序代码的性能调优、持续交付、容器化等软件基础架构方面的技能提升,也需要持续关注整个业务、应用、服务的生命周期管理。

    简单来说,就是把过去传统的黑盒运维的思维方式抛弃,进入白盒运维的时代,我们必须更加深入代码、深入业务运营,让整个线上服务运行于更优质高效的状态。
    #运维开发是什么?
    要建设运维自动化或者实践 DevOps 离不开运维开发工程师的参与,但要怎样才能更好地发挥运维开发的作用呢?

    我曾作为运维开发经理的角色和各种类型的运维开发一起协作过,团队中有本来就做运维开发的,也有本来做其他业务(电商、平台)的开发转来协助运维团队的,还有原本是做业务运维后来转型做运维开发的。
    和他们协作一段日子后,总体感觉如下:

    运维开发首先是一个程序员,不是运维工程师。

    一个好的运维开发需要具备 「运维理解」+「开发能力」:

    * 对「开发能力」的技术要求低于其他业务形态(如游戏、电商、搜索等)。
    * 对运维业务的理解难度会低于电商、游戏等业务形态,即对「运维理解」的要求不高。
    * 对运维相关技术栈的掌握程度要求高,如Python/PHP/Go/Shell、 Linux、Git、Nginx、Zabbix、Docker、Kubernetes等。

    5.png

    综上所述,运维开发是一个深度不算太深的职业分支,而现在之所以对运维开发需求量热起来了,主要由于老一辈的资深运维普遍研发能力有限,而这是有历史原因的。等到业界提出 DevOps的时候,他们往往已经专注于团队管理、容量规划、架构调优、运维服务质量等高级范畴,所以基本不太可能抽出大块的时间来重新学习编码并开发自动化系统。

    所以,当我们有自动化系统的建设需求时,需要更专业的程序员来协助。但一般的非专职运维开发的程序员做出来的系统对于运维来说往往不太好使,这时候有部分年轻的运维工程师升级了研发技能,转型运维开发,把好使的运维系统做出来了,赢得了运维团队的好评,大家都为「运维开发」点赞。

    所以,大家将 「好使的运维系统」 和 「运维开发」 等价起来,以为我们只要招来一个运维开发,那么一套完美的运维平台就能自动诞生出来,这是个很大的误区。
    #打造「好使的DevOps系统」
    其实「好使的DevOps系统」真正等价于「运维理解」+「开发能力」,这两种能力也是可以分离的,不一定要强加在运维开发工程师一个人的身上。

    类似其他业务形态的开发过程,需要产品经理和程序员两种角色分离,企业也不会说要招聘既会写代码、又会出需求的程序员。

    所以,当运维能把运维自动化的需求细致地文档化下来,把自动化系统的设计、架构等关键环节确立下来,这就是最好的「运维理解」。这时把这份靠谱、好使、细致的需求文档交给具备强「开发能力」的程序员,最终就可以得到「好使的运维系统」。

    当然, 一般企业不会专门为运维开发配备「产品经理」,所以运维开发想要再往高级发展的话,也可以替代运维出需求,升级为运维产品经理,以程序员的思维角度来解决运维服务的工程效率和质量问题,我认为这也是类似 Google 所提倡的 SRE 文化。
    ##DevOps平台
    编者补充描述。

    光说不练假把戏,编者在上家公司的主职就是将DevOps操作界面化。
    其中的核心模块:应用部署发布监控。

    图为DevOps应用部署发布监控界面图:
    6.png

    我们组在做上图的DevOps系统时,面临的情况是:无产品、无设计、需求也是靠业务运维和开发们的口头描述。

    其中的核心功能:应用部署界面,在参考其它同类产品后,发现都不适合业务场景,要么功能太分散,要么就仅是流程控制。于是前端功能里,我们做了这些:

    * 区分不同环境下的包,实现有序管理。
    * 应用的状态可以通过界面做启停、查看配置等任务。
    * Jenkins服务操作可通过界面完成,简化配置工程师的工作。
    * 业务运维与开发团队的日常发包工作界面化。

    此时一个优秀的运维开发需具备以下技能:产品规划、产品设计、面向对象、需求模型、领域模型、设计模型、设计原则、设计模式、产品工具和文档能力等。

    所以,当运维需求被理解、分析得足够透彻,以及运维开发获得了「产品经理」能力后,运维开发就是一种普通的开发分支,按需求文档编码即可。
    #优秀的运维开发
    从事DevOps平台开发相关工作已有六七年了,自身经历总结,觉得一个优秀的运维开发工程师应当具备以下能力和素质。
    ##提高运维意识。
    从下到上,从上到下的工作都要做好,对上运维工作的价值和含金量可以得到认可,对下我们的工作能够提高效率解放运维。

    运维意识是很重要,并不是你技术很牛,学的技术很多很熟,就不代表你不需要运维意识。

    其实领导很看重运维意识的,例如有没有做好备份,权限分配问题,平台测试情况,故障响应时间等,这些都是意识,而不是你学了很多技术自认大牛了,平台发现故障你又没什么大不子,以为很简单的问题喜欢处理就处理,不需要向其它部门反馈等,领导不是看你的技术如何,而是看你的运维意识如何,你没运维意识,技术再牛也没用,只会让其它部门的人跟你不协调。
    ##了解业务场景
    DevOps平台最终服务于运维部和开发测试部同事,因此只有熟悉了解了每一项业务的运维场景,才能更好去设计功能与代码开发,熟悉业务场景才能方方面面考虑周全,开发出来的代码才能满足各类场景应用。
    ##拒绝重复犯错
    人难免会犯错,这是无法避免的,我们应当根据已有的犯错经验,总结犯错的原因,以及如何避免同类情况再次发生,甚至可以把一些典型的错误在团队中分享,把一个人的错误得到的经验传播于整个团队。
    ##凡事有备份,可回退
    运维工作中经常有一些发布,迁移,备份等复杂操作,因此,在研发DevOPs平台的时候,要做好全面的操作计划,思考每一步可能的回退与备份。
    ##平台操作尽量简化
    DevOps平台目的就是为了能够提高运维的工作效率,解放运维,因此在设计与开发的时候,应当保持操作简单,不要让事情变得太复杂,能点一下到位的,尽量不要让人点五六下才能完成操作。
    ##注重优化用户体验
    DevOps开发是一个迭代的过程,虽然我们常说以功能开发为主,但是用户体验也同等重要,试想一下,纵使你开发的功能众多,如果体验不友好,用户便失去了再次使用的欲望,如果用户拒绝,抵触使用平台,做再多功能也是失败,最终平台推广失败。因此,在研发的过程中,我们应当深入体验自己开发的产品,把自己当成用户去体验平台操作。尽可能的去优化用户体验。这是一个优秀的运维开发工程师必需要懂的。

    在设计与开发的过程中经常会碰到复杂,繁琐的场景,这个时候我们很容易失去耐心,我们要时刻提醒自己,必须严格履行自己的工作职责,端正自己的工作态度,做一件事,要么不做,既然做了就要做好:当你想要放弃的时候,想想当初为什么要开始。
    #总结
    本文是我个人对运维开发以及其职业发展的一些浅薄理解,总的来说,运维开发还是一个比较有意思且有良好发展的职业分支,虽然偶尔也要背黑锅,但也欢迎更多努力、聪明、有才华的同学加入运维开发行业。

    链接:https://juejin.im/post/5cf29a6ae51d45778f076cd2