loop in codes

Kevin Lynx BLOG

Kubernete APIServer Sample

kubernetes从apiserver中独立出来了一个项目:apiserver,可以基于这个库快速实现一个类似kubernetes apiserver的服务。

Generic library for building a Kubernetes aggregated API server.

如果直接阅读kubenetes的apiserver源码,会发现很多实现都位于这个项目中。kubenetes源码目录下还有个sample-apiserver,是用于示例如何使用这个库的。从这个sample可以更快速地了解kubernetes apiserver的实现,以及如何使用。

简单来说,这个apiserver库做了很多抽象,基本上,用户只需要描述自己的资源结构是怎样的,就可以构建出一个类似kubernetes的apiserver,具备资源多版本兼容能力,对外提供json/yaml的http restful接口,并持久化到etcd中。接下来主要讲下大概的用法以及apiserver中的主要概念。

apiserver简介

apiserver简单来说,可以理解为一个基于etcd,并提供HTTP接口的对象(资源)系统。其提供了针对多种资源的操作,例如CRUD、列表读取、状态读取。kubernetes中POD、Deployment、Service,都是资源,可以说kubernetes所有组件都是围绕着资源运作的。apiserver库本身是不提供任何资源的,它做了很多抽象,使得应用层可以根据自己需要添加各种资源。同时,apiserver支持相同资源多个版本的存在。

为了更容易地理解apiserver的设计,可以先自己思考如何实现出这样一个通用的资源服务框架,例如,可能需要解决以下问题:

  • HTTP接口层,根据资源名映射出不同的URI,如何统一地从HTTP请求中创建出不同类型的资源
  • 不同的资源支持的操作不同,如何区分
  • 资源的多版本如何实现
  • 资源如何统一地序列化存储到etcd中

kubernetes网络相关总结

要理解kubernetes的网络模型涉及到的技术点比较多,网络上各个知识点讲得细的有很多,这里我就大概梳理下整个架构,方便顺着这个脉络深入。本文主要假设kubernetes使用docker+flannel实现。

整体上,了解kubernetes的网络模型,涉及到以下知识:

  • linux网络及网络基础
  • docker网络模型
  • kubernetes网络需求,及flannel网络实现

最后大家就可以结合实例对照着学习。

Linux网络

先看几个概念,引用自Kubernetes网络原理及方案:

  • 网络命名空间

Linux在网络栈中引入网络命名空间,将独立的网络协议栈隔离到不同的命令空间中,彼此间无法通信;docker利用这一特性,实现不同容器间的网络隔离

  • 网桥

网桥是一个二层网络设备,通过网桥可以将linux支持的不同的端口连接起来,并实现类似交换机那样的多对多的通信

  • Veth设备对

Veth设备对的引入是为了实现在不同网络命名空间的通信

  • 路由

Linux系统包含一个完整的路由功能,当IP层在处理数据发送或转发的时候,会使用路由表来决定发往哪里

借图以关联上面的概念:

dnet.png

使用Kubeadm在CentOS部署Kubernets 1.8.7

主要参考:

建议都大致浏览下。这里我也是简单地记录,估计每个人遇到的细节问题不一样。

环境准备

我拿到手的环境docker已经ready:

  • docker (alidocker-1.12.6.22)
  • CentOS 7

上面博客提到的一些系统设置可以先做掉:

1
2
3
4
5
cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl -p /etc/sysctl.d/k8s.conf

其他一些设置:

  • 防火墙最好关闭
  • swap最好关闭
  • setenforce 0

基于Yarn的分布式应用调度器Slider

Apache Hadoop Map-Reduce 框架为了解决规模增长问题,发展出了yarn。而yarn不仅解决Map-Reduce调度问题,还成为了一个通用的分布式应用调度服务。yarn中的一个创新是把各种不同应用的调度逻辑拆分到了一个称为Application Manager(以下简称AM)的角色中,从而让yarn自己变得更通用,同时解决调度性能问题。Apache Slider就是这其中的一个AM具体实现。但Slider进一步做了通用化,可以用于调度长运行(long-running)的分布式应用。

为了更好地理解Slider/Yarn,需要思考这样一个问题:在不用Slider/Yarn这种自动部署并管理应用的软件时,我们如何在一个网络环境中部署一个分布式应用?

  • 可能需要在目标物理机上创建虚拟容器,指定容器所用的CPU核数、内存数
  • 到容器中下载或复制应用运行所需的所有软件包
  • 可能需要改写应用所需的各种配置
  • 运行应用,输入可能很长的命令行参数

注意这些操作需要在所有需要运行的容器中执行,当然现在也有很多自动部署的工具可以解决这些问题。但是,当应用首次部署运行起来后,继续思考以下问题:

  • 某台机器物理原因关机,对应的应用实例不可服务,如何自动发现故障并迁移该实例
  • 应用有突发流量,需要基于当前运行中的版本做扩容
  • 应用需要更新

架构

看一下yarn的总体架构:

Python协程greenlet实现原理

greenletstackless Python中剥离出来的一个项目,可以作为官方CPython的一个扩展来使用,从而支持Python协程。gevent正是基于greenlet实现。

协程实现原理

实现协程主要是在协程切换时,将协程当前的执行上下文保存到协程关联的context中。在c/c++这种native程序中实现协程,需要将栈内容和CPU各个寄存器的内容保存起来。在Python这种VM中则有些不同。例如,在以下基于greenlet协程的python程序中:

1
2
3
4
5
6
7
8
9
10
11
12
13
def foo():
    bar()

def bar():
    a = 3 + 1
    gr2.switch()

def func():
    pass

gr1 = greenlet(foo)
gr2 = greenlet(func)
gr1.switch()

bargr2.switch切换到gr2时,协程库需要保存gr1协程的执行上下文。这个上下文包括:

  • Python VM的stack
  • Python VM中解释执行的上下文

写了一个棋牌游戏服务器框架

最近业余时间写了一个棋牌游戏服务端框架:pigy。对于棋牌游戏服务端框架,我的定义是:

  • 分布式的
  • 包含网络棋牌游戏中包括登陆、大厅、游戏框架、数据持久化等基础组件
  • 提供具体游戏框架,游戏逻辑程序员可以基于这个框架focus在游戏的开发上

写得差不多的时候,我在网上搜索了下,发现棋牌游戏源码已经烂大街,自己精力有限,也没有心思和动力去研究现有实现的优缺点而做出一个更好的替代。所以我这份实现仅作为一个demo放出来让大家开心下好了。

pigy基于skynet实现。之所以选择skynet是看中其中已经有不少网络游戏基础组件可以使用,结合开发下来稍微花点业余时间就可以完成雏形。除此之外,部分源码也参考/复制了metoo项目。

协程并发模型及使用感受

协程可以简单理解为更轻量的线程,但有很多显著的不同:

  • 不是OS级别的调度单元,通常是编程语言或库实现
  • 可能需要应用层自己切换
  • 由于切换点是可控制的,所以对于CPU资源是非抢占式的
  • 通常用于有大量阻塞操作的应用,例如大量IO

协程与actor模式的实现有一定关系。由于协程本身是应用级的并发调度单元,所以理论上可以大量创建。在协程之上做队列及通信包装,即可得到一个actor框架,例如python-actor

最近1年做了一个python项目。这个项目中利用gevent wsgi对外提供HTTP API,使用gevent greelet来支撑上层应用的开发。当可以使用协程后,编程模型会得到一定简化,例如相对于传统线程池+队列的并发实现,协程可以抛弃这个模型,直接一个协程对应于一个并发任务,例如网络服务中一个协程对应一个socket fd。

但是python毕竟是单核的,这个项目内部虽然有大量IO请求,但随着业务增长,CPU很快就到达了瓶颈。后来改造为多进程结构,将业务单元分散到各个worker进程上。

python gevent中的协议切换是自动的,在遇到阻塞操作后gevent会自动挂起当前协程,并切换到其他需要激活的协程。阻塞操作完成,对应的协程就会处于待激活状态。

在这个项目过程中,我发现协程也存在很多陷阱。

实现一个memcache proxy

通常我们会使用多台memcached构成一个集群,通过客户端库来实现缓存数据的分片(replica)。这会带来2个主要问题:

  • memcached机器连接数过多
  • 不利于做整体的服务化;缺少可运维性。例如想对接入的客户端做应用级隔离;或者对缓存数据做多区域(机房)的冗余

实现一个memcache proxy,相对于减少连接数来说,主要可以提供更多的扩展性。目前已经存在一些不错的memcache proxy,例如twitter的twemproxy,facebook的mcrouter。稍微调研了下,发现twemproxy虽然轻量,但功能较弱;mcrouter功能齐全,类似多区域多写的需求也满足。处于好玩的目的,之前又读过网络库xnio源码,我还是决定自己实现一个。

Xmemcached源码阅读

Xmemcached 是一个memcached客户端库。由于它提供的是同步API,而我想看下如何增加异步接口。所以就大致浏览了下它的源码。

主要结构

针对memcache客户端的实现,主要结构如下:

  • XMemcachedClient 是应用主要使用的类,所有针对memcache的接口都在这里
  • Command 用于抽象二进制协议或文本协议下各个操作,这里称为Command。CommandFactory 用于创建这些command
  • MemcachedSessionLocator 用于抽象不同的负载均衡策略,或者说数据分布策略。在一个memcached集群中,数据具体存放在哪个replica中,主要就是由这个类的实现具体的,例如KetamaMemcachedSessionLocator 实现了一致性哈希策略
  • MemcachedConnector 包装了网络部分,与每一个memcached建立连接后,就得到一个Session。command的发送都在MemcachedConnector中实现
  • 各个Session类/接口,则涉及到Xmemcached使用的网络库yanf4j。这个库也是Xmemcached作者的。

XNIO源码阅读

XNIO是JBoss的一个IO框架。最开始我想找个lightweight servlet container库,于是看到了undertow,发现其网络部分使用的就是XNIO。所以干脆就先把XNIO的源码读下。

XNIO文档非常匮乏,能找到都是3.0的版本,而且描述也不完全。Git上已经出到3.5.0。我读的是3.3.6.Final。

使用方式

可以参考SimpleEchoServer.java,不过这个例子使用的API已经被deprecated,仅供参考。使用方式大致为:

  • 创建服务,提供acceptListener
  • 在acceptListener中accept新的连接,并注册连接listener
  • 在连接listener回调中完成IO读写