睿云智合 | 开源项目Breeze成为Kubernetes认证安装工具

wise2c 发表了文章 • 0 个评论 • 204 次浏览 • 2019-06-14 11:46 • 来自相关话题

近日,睿云智合开源的Kubernetes图形化部署工具Breeze项目通过Kubernetes一致性认证成为认证Kubernetes安装工具。目前Breeze已经列入 Cloud Native Landscape 生态图谱的认证安装工具板块。 ...查看全部

近日,睿云智合开源的Kubernetes图形化部署工具Breeze项目通过Kubernetes一致性认证成为认证Kubernetes安装工具。目前Breeze已经列入 Cloud Native Landscape 生态图谱的认证安装工具板块。


Beeze项目是睿云智合(Wise2C)2018年5月所开源的Kubernetes图形化部署工具,能大大简化Kubernetes部署的步骤,支持全离线环境的部署,而且可以不需要翻墙获取Google的相应资源包,尤其适合中国金融、运营商、国有企业等不便访问互联网的私有数据中心部署环境场景。目前支持最新版Kubernetes、Etcd、Docker、Harbor以及Prometheus等云原生工具部署,同时支持Istio部署 (内置Kiali, Jaeger, Prometheus, Grafana)。推出至今获得开源技术社区用户的一致好评目前在Github上已获600余星。



功能亮点:

易于运行:

Breeze将您需要的所有资源(比如Kubernetes组件镜像以及用于部署Kubernetes集群的 ansible playbooks)组合为一个Docker镜像(wise2c/playbook)。它还可以作为本地yum存储库服务器工作。您只需要安装了Docker引擎的Linux服务器来运行Breeze。



简化了kubernetes集群部署的过程:


通过几个简单的命令,您可以轻而易举地运行,然后通过图形界面完成所有其他部署过程。



支持离线部署:

在部署服务器上加载了4个镜像(playbook、yum repo、pagoda、deploy ui)后,就可以在不接入互联网的情况下设置kubernetes集群。Breeze作为一个yum存储库服务器工作,部署一个本地的Harbor镜像仓库,并使用kubeadm来设置kubernetes集群。所有docker镜像将从本地镜像仓库提取。



支持多集群:

Breeze支持多个Kubernetes集群部署。

支持高可用架构:

通过Breeze,您可以设置Kubernetes集群,其中3台主服务器和3台ETCD服务器与Haproxy和Keepalived相结合。所有工作节点将使用虚拟浮动IP地址与主服务器通信。



Breeze项目是100%开源的云原生部署工具


GitHub项目地址:

https://github.com/wise2c-devops/breeze

安装过程演示视频在此:

 https://pan.baidu.com/s/1X0ZYt48wfYNrSrH7vvEKiw

常见排错说明在此:

https://github.com/wise2c-devops/breeze/blob/master/TroubleShooting-CN.md

PDF手册请在此处下载:

https://github.com/wise2c-devops/breeze/raw/master/manual/BreezeManual-CN.pdf

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

大卫 发表了文章 • 0 个评论 • 262 次浏览 • 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

基于Jenkins Pipeline自动化部署

尼古拉斯 发表了文章 • 0 个评论 • 346 次浏览 • 2019-06-05 23:53 • 来自相关话题

最近在公司推行Docker Swarm集群的过程中,需要用到Jenkins来做自动化部署,Jenkins实现自动化部署有很多种方案,可以直接在jenkins页面写Job,把一些操作和脚本都通过页面设置,也可以在每个项目中直接写Pipeline脚本,但像我那么追 ...查看全部
最近在公司推行Docker Swarm集群的过程中,需要用到Jenkins来做自动化部署,Jenkins实现自动化部署有很多种方案,可以直接在jenkins页面写Job,把一些操作和脚本都通过页面设置,也可以在每个项目中直接写Pipeline脚本,但像我那么追求极致的程序员来说,这些方案都打动不了我那颗骚动的心,下面我会跟你们讲讲我是如何通过Pipeline脚本实现自动化部署方案的,并且实现多分支构建,还实现了所有项目共享一个Pipeline脚本。
#使用Jenkins前的一些设置

为了快速搭建Jenkins,我这里使用Docker安装运行Jenkins:
$ sudo docker run -it -d \
--rm \
-u root \
-p 8080:8080 \
-v jenkins-data:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$HOME":/home \
--name jenkins jenkinsci/blueocean

初次使用Jenkins,进入Jenkins页面前,需要密码验证,我们需要进入Docker容器查看密码:
$ sudo docker exec -it jenkins /bin/bash
$ vi /var/jenkins_home/secrets/initialAdminPassword

Docker安装的Jenkins稍微有那么一点缺陷,shell版本跟CentOS宿主机的版本不兼容,这时我们需要进入Jenkins容器手动设置shell:
$ sudo docker exec -it jenkins /bin/bash
$ ln -sf /bin/bash /bin/sh

由于我们的Pipeline还需要在远程服务器执行任务,需要通过SSH连接,那么我们就需要在Jenkins里面生成SSH的公钥密钥:
$ sudo docker exec -it jenkins /bin/bash
$ ssh-keygen -C "root@jenkins"

在远程节点的~/.ssh/authorized_keys中添加jenkins的公钥(id_rsa.pub)。

还需要安装一些必要的插件:

  1. Pipeline Maven Integration
  2. SSH Pipeline Steps

安装完插件后,还需要去全局工具那里添加Maven:
1.png

这里后面Jenkinsfile有用到。
#mutiBranch多分支构建

由于我们的开发是基于多分支开发,每个开发环境都对应有一条分支,所以普通的Pipeline自动化构建并不能满足现有的开发部署需求,所以我们需要使用Jenkins的mutiBranch Pipeline。如果你想和更多Jenkins技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

首先当然是新建一个mutiBranch多分支构建Job:
2.jpg

接着设置分支源,分支源就是你项目的Git地址,选择Jenkinsfile在项目的路径:
3.png

接下来Jenkins会在分支源中扫描每个分支下的Jenkinsfile,如果该分支下有Jenkinsfile,那么就会创建一个分支Job:
4.jpg

该Job下的分支Job如下:
5.png

这里需要注意的是,只有需要部署的分支,才加上Jenkinsfile,不然Jenkins会将其余分支也创建一个分支Job。
#通用化Pipeline脚本

到这里之前,基本就可以基于Pipeline脚本自动化部署了,但如果你是一个追求极致,不甘于平庸的程序员,你一定会想,随着项目的增多,Pipeline脚本不断增多,这会造成越来越大的维护成本,业务的极速增长难免会在脚本中修改东西,这就会牵扯太多Pipeline脚本的改动,而且这些脚本基本都相同,那么对于我这么优秀的程序员,怎么会想不到这个问题呢,我第一时间就想到通用化Pipeline脚本。所幸,Jenkins已经看出了我不断骚动的心了,Jenkins甩手就给我一个Shared Libraries。

Shared Libraries是什么呢?顾名思义,它就是一个共享库,它的主要作用是用于将通用的Pipeline脚本放在一个地方,其它项目可以从它那里获取到一个全局通用化的Pipeline脚本,项目之间通过不通的变量参数传递,达到通用化的目的。

接下来我们先创建一个用于存储通用Pipeline脚本的Git仓库:
6.png

仓库目录就不能随便乱添加了,Jenkins有一个严格的规范,下面是官方说明:
7.jpg

官方已经讲得很清楚了,大概意思就是vars目录用于存储通用Pipeline脚本,resources用于存储非Groovy文件。所以我这里就把Pipeline需要的构建脚本以及编排文件都集中放在这里,完全对业务工程师隐蔽,这样做的目的就是为了避免业务工程师不懂瞎几把乱改,导致出bug。

创建完Git仓库后,我们还需要在Jenkins的Manage Jenkins » Configure System » Global Pipeline Libraries中定义全局库:
8.jpg

这里的name,可以在jenkinsfile中通过以下命令引用:
@Library 'objcoding-pipeline-library'

下面我们来看通用Pipeline脚本的编写规则:
#!groovy

def getServer() {
def remote = [:]
remote.name = 'manager node'
remote.user = 'dev'
remote.host = "${REMOTE_HOST}"
remote.port = 22
remote.identityFile = '/root/.ssh/id_rsa'
remote.allowAnyHosts = true
return remote
}

def call(Map map) {

pipeline {
agent any

environment {
REMOTE_HOST = "${map.REMOTE_HOST}"
REPO_URL = "${map.REPO_URL}"
BRANCH_NAME = "${map.BRANCH_NAME}"
STACK_NAME = "${map.STACK_NAME}"
COMPOSE_FILE_NAME = "" + "${map.STACK_NAME}" + "-" + "${map.BRANCH_NAME}" + ".yml"
}

stages {
stage('获取代码') {
steps {
git([url: "${REPO_URL}", branch: "${BRANCH_NAME}"])
}
}

stage('编译代码') {
steps {
withMaven(maven: 'maven 3.6') {
sh "mvn -U -am clean package -DskipTests"
}
}
}

stage('构建镜像') {
steps {
sh "wget -O build.sh https://git.x-vipay.com/docker/jenkins-pipeline-library/raw/master/resources/shell/build.sh"
sh "sh build.sh ${BRANCH_NAME} "
}
}

stage('init-server') {
steps {
script {
server = getServer()
}
}
}

stage('执行发版') {
steps {
writeFile file: 'deploy.sh', text: "wget -O ${COMPOSE_FILE_NAME} " +
" https://git.x-vipay.com/docker/jenkins-pipeline-library/raw/master/resources/docker-compose/${COMPOSE_FILE_NAME} \n" +
"sudo docker stack deploy -c ${COMPOSE_FILE_NAME} ${STACK_NAME}"
sshScript remote: server, script: "deploy.sh"
}
}
}
}
}


  1. 由于我们需要在远程服务器执行任务,所以定义一个远程服务器的信息其中remote.identityFile就是我们上面在容器生成的密钥的地址;
  2. 定义一个call()方法,这个方法用于在各个项目的Jenkinsfile中调用,注意一定得叫call;
  3. 在call()方法中定义一个Pipeline;
  4. environment参数即是可变通用参数,通过传递参数Map来给定值,该Map是从各个项目中定义的传参;
  5. 接下来就是一顿步骤操作啦,“编译代码”这步骤需要填写上面我们在全局工具类设置的maven,“构建镜像”的构建脚本巧妙地利用wget从本远程仓库中拉取下来,”执行发版“的编排文件也是这么做,“init-server”步骤主要是初始化一个server对象,供“执行发版使用”。

从脚本看出来Jenkins将来要推崇的一种思维:配置即代码。

写完通用Pipeline脚本后,接下来我们就需要在各个项目的需要自动化部署的分支的根目录下新建一个Jenkinsfile脚本了:
9.png

接下来我来解释一下Jenkinsfile内容:
#!groovy

// 在多分支构建下,严格规定Jenkinsfile只存在可以发版的分支上

// 引用在jenkins已经全局定义好的library
library 'objcoding-pipeline-library'
def map = [:]

// 远程管理节点地址(用于执行发版)
map.put('REMOTE_HOST','xxx.xx.xx.xxx')
// 项目gitlab代码地址
map.put('REPO_URL','https://github.com/objcoding/docker-jenkins-pipeline-sample.git')
// 分支名称
map.put('BRANCH_NAME','master')
// 服务栈名称
map.put('STACK_NAME','vipay')

// 调用library中var目录下的build.groovy脚本
build(map)


  1. 通过library 'objcoding-pipeline-library'引用我们在Jenkins定义的全局库,定义一个map参数;
  2. 接下来就是将项目具体的参数保存到map中,调用build()方法传递给通用Pipeline脚本。

Shared Libraries共享库极大地提升了Pipeline脚本的通用性,避免了脚本过多带来的问题,也符合了一个优秀程序员的审美观,如果你是一个有追求的程序员,你一定会爱上它。

附上一张价值连城的手稿图:
10.jpg


原文链接:https://mp.weixin.qq.com/s/2M2RQN-_2wmWf4OslIpuaA

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

JetLee 发表了文章 • 0 个评论 • 310 次浏览 • 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 个评论 • 353 次浏览 • 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 个评论 • 216 次浏览 • 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 个评论 • 201 次浏览 • 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 个评论 • 347 次浏览 • 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

    API网关如何实现对服务下线实时感知

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

    上篇文章《Eureka 缓存机制》介绍了Eureka的缓存机制,相信大家对Eureka有了进一步的了解,本文将详细介绍API网关如何实现服务下线的实时感知。 #一、前言 在基于云的微服务应用中,服务实例的网络位置都是动态分配的。而且 ...查看全部
    上篇文章《Eureka 缓存机制》介绍了Eureka的缓存机制,相信大家对Eureka有了进一步的了解,本文将详细介绍API网关如何实现服务下线的实时感知。
    #一、前言

    在基于云的微服务应用中,服务实例的网络位置都是动态分配的。而且由于自动伸缩、故障和升级,服务实例会经常动态改变。因此,客户端代码需要使用更加复杂的服务发现机制。

    目前服务发现主要有两种模式:客户端发现和服务端发现。

    * 服务端发现:客户端通过负载均衡器向服务注册中心发起请求,负载均衡器查询服务注册中心,将每个请求路由到可用的服务实例上。
    * 客户端发现:客户端负责决定可用服务实例的网络地址,并且在集群中对请求负载均衡, 客户端访问服务登记表,也就是一个可用服务的数据库,然后客户端使用一种负载均衡算法选择一个可用的服务实例然后发起请求。

    客户端发现相对于服务端发现最大的区别是:客户端知道(缓存)可用服务注册表信息。如果Client端缓存没能从服务端及时更新的话,可能出现Client 与 服务端缓存数据不一致的情况。
    #二、网关与Eureka结合使用

    Netflix OSS 提供了一个客户端服务发现的好例子。Eureka Server 为注册中心,Zuul 相对于Eureka Server来说是Eureka Client,Zuul 会把 Eureka Server 端服务列表缓存到本地,并以定时任务的形式更新服务列表,同时 Zuul 通过本地列表发现其它服务,使用 Ribbon 实现客户端负载均衡。如果你想和更多Spring Cloud技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态
    1.jpg

    正常情况下,调用方对网关发起请求即刻能得到响应。但是当对生产者做缩容、下线、升级的情况下,由于Eureka这种多级缓存的设计结构和定时更新的机制,LoadBalance 端的服务列表B存在更新不及时的情况(由上篇文章《Eureka 缓存机制》可知,服务消费者最长感知时间将无限趋近240s),如果这时消费者对网关发起请求,LoadBalance 会对一个已经不存在的服务发起请求,请求是会超时的。
    #三、解决方案

    ##实现思路

    生产者下线后,最先得到感知的是 Eureka Server 中的 readWriteCacheMap,最后得到感知的是网关核心中的 LoadBalance。但是 loadBalance 对生产者的发现是在 loadBalance 本地维护的列表中。

    所以要想达到网关对生产者下线的实时感知,可以这样做:首先生产者或者部署平台主动通知 Eureka Server,然后跳过 Eureka 多级缓存之间的更新时间,直接通知 Zuul 中的 Eureka Client,最后将 Eureka Client 中的服务列表更新到 Ribbon 中。

    但是如果下线通知的逻辑代码放在生产者中,会造成代码污染、语言差异等问题。

    借用一句名言:计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。
    2.jpg

    Gateway-SynchSpeed 相当于一个代理服务,它对外提供REST API来负责响应调用方的下线请求,同时会将生产者的状态同步到 Eureka Server 和 网关核心,起着 状态同步 和 软事物 的作用。

    思路:在生产者做 缩容、下线、升级 前,spider 平台(spider为容器管理平台)会主动通知 Gateway-SynchSpeed 某个生产者的某个实例要下线了,然后 Gateway-SynchSpeed 会通知 Eureka Server 生产者的某个实例下线了;如果Eureka Server 下线成功,Gateway-SynchSpeed 会直接通知 网关核心。

    设计特点:

    * 无侵入性、方便使用。不用关心调用方的基于何种语言实现,调用者只要对 Gateway-SynchSpeed 发起一个http rest请求即可,真正的实现逻辑不用侵入到调用方而是交给这个代理来实现。
    * 原子性。调用方先在Eureka Server下线,然后在所有相关网关核心中下线为最小工作执行单元,Gateway-SynchSpeed 相当于一个"软事物",保证服务下线的某种程度上原子特性。

    ##实现步骤

    3.jpg

    步骤说明:

    第一步:在生产者做 缩容、下线、升级 前,spider平台会以http请求的形式通知到 Gateway-SynchSpeed 服务,通知的粒度为服务实例所在的容器IP。

    第二步:Gateway-SynchSpeed 接受到请求后,先校验IP的可用性,然后通知Eureka Server。

    第三步:Eureka Server 将 Producer 置为失效状态,并返回处理结果(Eureka 下线形式分为两种,一种是直接从服务注册列表直接剔除,第二种是状态下线,即是将 Producer 的状态置为OUT_OF_SERVICE。 如果是以第一种形式下线,Spider平台发出下线请求后,不能保证Producer进程立刻被kill,如果这期间 Producer 还有心跳同步到 Eureka Server,服务会重新注册到 Eureka Server)。

    第四步:Gateway-SynchSpeed 得到上一步结果,如果结果为成功,则执行下一步;反之,则停止。

    第五步:Gateway-SynchSpeed 为Eureka Client。Gateway-SynchSpeed 通过 IP 到本地服务注册列表中得到 Producer 的 Application-Name。

    第六步:Gateway-SynchSpeed 通过 Application-Name 到网关核心库中查询所有与下线服务相关的 网关组名字。

    第七步:Gateway-SynchSpeed 通过 网关组名字 到本地服务列表中查找网关组下所有的服务地址 ipAddress(ip : port)。

    第八步:Gateway-SynchSpeed 异步通知所有相关网关节点。

    第九步:Gateway-Core 收到通知后,对 Producer 做状态下线,同时记录所有状态下线成功的实例信息到缓存 DownServiceCache 中。

    第十步:Gateway-Core 更新本地 Ribbon 服务列表。
    #四、补偿机制

    Eureka 提供了一种安全保护机制。Eureka Client 从 Eureka Server 更新服务列表前,会校验相关Hash值是否改变(Client 服务列表被修改,hash值会改变),如果改变,更新方式会从增量更新变成全量更新,(由《Eureka 缓存机制》可知这30s内 readOnlyCacheMap 和 readWriteCacheMap 的数据可能存在差异),如果Client端缓存列表被readOnlyCacheMap 覆盖,最终会导致 Ribbon 端服务列表与 readWriteCacheMap 数据不一致。
    4.jpg

    针对 Eureka 这种机制,引入监听器 EurekaEventListener 作为补偿机制,它会监听 Eureka Client 全量拉取事件,对于缓存中未超过30s的服务,将其状态重新设置成 OUT_OF_SERVICE 。
    #五、API安全设计

    考虑到系统的安全性问题,如果被人恶意访问,可能会使生产者在Eureka Server中无故下线,导致消费者无法通过 Eureka Server 来发现生产者。

    使用黑白名单做安全过滤,基本流程如下:

    * 对 Gateway-Synchspeed 中设置白名单网段(IP网段)。
    * 在 Gateway-Synchspeed 加入过滤器,对下线请求方进行IP校验,如果请求端IP在网段中,则放行;反之,过滤。

    #六、日志回溯

    由于 Gateway-SynchSpeed 和 Gateway-Core 是部署在 Docker 容器中,如果容器重启,会导致日志文件全部丢失。所以需要将 Gateway-SynchSpeed 和 Gateway-Core 中相关日志写入到 Elasticsearch ,最终由 Kibana 负责查询 Elasticsearch 的数据并以可视化的方式展现。
    #七、代码片段展示

    Gateway-SynchSpeed 做状态同步。
    5.jpg

    EurekaEventListener 处理缓存数据。
    6.jpg

    #八、 补充说明

    目前网关实现对服务下线的实时感知中,使用的 Zuul 和 Eureka 版本为 Spring Cloud Zuul 1.3.6.RELEASE、Spring Cloud Eureka 1.4.4.RELEASE。

    目前网关实现的是对网关下游服务的实时感知,而且需满足以下条件:

    * 生产者需部署在 kubernetes 容器管理平台 。
    * 生产者做正常的下线、升级或者缩容操作。如果是由于容器资源不足,导致服务异常宕机等非正常下线,不支持。

    网关服务下线实时感知是网关对业务方提供的一种可选的解决方案,在 spider 平台中默认是没有开启此功能,是否开启此功能由业务方根据本身系统要求决定,具体如何配置可参考 API网关接入指南 中 《网关实时感知在spider上配置文档说明》。

    原文链接:http://college.creditease.cn/detail/256

    详解Eureka缓存机制

    阿娇 发表了文章 • 0 个评论 • 207 次浏览 • 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