cfsslwget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
chmod +x cfssl_linux-amd64
mv cfssl_linux-amd64 /usr/local/bin/cfssl
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
chmod +x cfssljson_linux-amd64
mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
chmod +x cfssl-certinfo_linux-amd64
mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo
export PATH=/usr/local/bin:$PATH
找到 kubernetes 的根证书 ca.pem, ca-key.pem, ca-config.json
生成证书请求配置文件, 可以替换 usage 为其他名字, 替换 usage.common.name 为服务器域名
cat > usage.json <<EOF
{
"CN": "usage.common.name",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "System"
}
],
"ca": {
"expiry": "87600h"
}
}
EOF
cfssl 工具签名服务端证书cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes usage.json | cfssljson -bare usage
$ ls usage*
usage.csr usage.json usage-key.pem usage.pem
openssl 签名客户端证书openssl -in usage.pem -out usage.client.pem
usage.pem usage-key.pem usage.client.pem 愉快的玩耍参考资料:
istio 是通过给每个生产 pod 注入一个 envoy sidecar 进行流量劫持的。注入后,流入 pod 的流量和 pod 请求的流量都会经过 sidecar 进行路由。
这样的流量劫持是通过 sidecar pod 和生产 pod 共享一个网络命名空间,然后设置 iptable 规则实现的。
PS1:下面的步骤前提是在一个 k8s 集群内操作,并且已经安装了 istio) PS2:实验步骤是基于宋大的博文上实验的,可以先看看宋大的两篇博文:用 vagrant 和 virtualbox 搭建一个 k8s 集群 理解 Istio Service Mesh 中 Envoy 代理 Sidecar 注入及流量劫持
比如我们要部署一个服务,首先要编写一个关于 service 和 deployment 的 yaml 文件(拿 istio 官网的 bookinfo 为例子):
---
apiVersion: v1
kind: Service
metadata:
name: productpage
labels:
app: productpage
spec:
ports:
- port: 9080
name: http
selector:
app: productpage
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: productpage-v1
spec:
replicas: 1
template:
metadata:
labels:
app: productpage
version: v1
spec:
containers:
- name: productpage
image: istio/examples-bookinfo-productpage-v1:1.5.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
这个是 productpage 服务的 yaml 的内容。那么调用 istioctl kube-inject -f productpage.yaml 后会发生什么呢?
本文翻译自:https://cloudnativelabs.github.io/post/2017-05-10-kube-network-service-proxy/
服务是 Kubernetes 的一个抽象概念。服务将一组逻辑上的、提供相同功能的 pod 组合在一起。一个在 Kubernetes 内的服务可以是多种类型的,比如 ‘ClusterIP’ 和 ‘NodePort’ 类型实现服务发现和负载均衡。这些服务的类型的实现都需要一个运行在各个 Kubernetes 集群节点的服务代理。Kubernetes 有一种名为 ‘kube-proxy’ 的代理,是基于 iptable 实现的。虽然,kube-proxy 提供了一种开箱即用的代理解决方案,它并不是一种最优的方案。在这篇文章里,我们会深入研究另一种 Kubernetes 里网络服务代理的实现:kube-router,它是基于 Liunx IPVS 的。我们还会讨论基于 iptables 的 kube-proxy 的好处与不足,以及将它与基于 IPVS/LVS 实现的 Kubernetes 网络服务代理进行对比。
请看下面的 demo,感受 IPVS 实现的 kube-router 作为 Kubernetes 服务代理的效果。
(此处查看原文可以看到 asciinema demo,如果不能访问,asciinema 地址在这里)
下面我们来深入研究一下细节。
在服务的生命周期里,每个 ‘ClusterIP’ 和 ‘NodePort’ 服务会被分配一个唯一的 IP 地址(称为 ClusterIP)。Kubernetes 会配置 Pods 通过 ClusterIP 与服务进行交互,而且 pods 发送至 service 的请求会被自动地负载均衡到 service 的成员 pods 上。另外,Kubernetes master 节点会为 ‘NodePort’ 类 service 分配一个标志配置范围(默认是 30000 至 23767)的端口,然后每个 node 节点会将该 service 的请求代理到这个端口上。
本文简单概括了 kubernetes 里有什么组件,以及它们是如何工作的。
一个 kubernetes 集群包含两部分:master 节点和 node 节点。master 节点负责接收管理员对于 kubernetes 资源对象的修改,并根据这些修改,对 node 节点进行配置和调度。node 节点是 kubernetes 集群内的计算节点,负责给部署在上面的业务应用提供计算以及储存能力。
kubernetes 里有以下组件:Kubernetes API Server、Controll Manager、Scheduler、kubelet 和 kube-proxy。
其中,Kubernetes API Server、Controll Manager、Scheduler 运行在 master节点上。而 kubelet 和 kube-proxy 运行在 node 节点上。
这些组件分别负责 kubernetes 运行的不同职责:
Kubernetes API Server:kubernetes 集群的管理入口,它提供 HTTP REST 接口给 kubernetes 用户进行集群的配置,这些配置将会保存在 master 节点运行的 etcd 里面,供其他组件查阅。
Controll Manager:负责监听 etcd 变化并作出对应的动作。Controll Manager 是一系列 manager 的统称,其中有以下这些manager:
Scheduler:负责接收 Replication Controller 的指挥,对要部署在 node 上的 pod 进行调度。Scheduler 会根据调度算法以及调度策略,调度 pod 在哪个 node 上部署。
@(PHP) ###PHP扩展分享 ###什么时候需要写php扩展
###php扩展简介
php提供一个工具用来生成php扩展的代码,类似代码生成器,工具目录source/to/php_source_code/ext/ext_skel,这个是供类unix平台调用的脚本,php源码也提供了一个供windows平台调用的脚本,用来生成php扩展代码.该脚本是个php脚本,在source/to/php_source_code/ext/ext_skel_win32.php
php将扩展代码统一放到source/to/php_source_code/ext/下,这样当开发者按照工具生成对应格式的php扩展源码,并统一放到这个目录下,php源码就会自动识别,并可以选在在编译时加上开发者的扩展.
###先找个题目
总所周知php在连接字符串会进行多次初始化临时变量,和内存复制.假设有一个变态的需求需要重复一个字符串’a’ n次,并且在系统中多次出现,由于n已知,为了提高性能,可以考虑用扩展实现
###动手
####1. 编写扩展原型文件,使用工具生成扩展代码
php没有规定原型文件的文件后缀,但有规定原型文件的格式.
如果你的扩展需要暴露出接口函数供php代码调用,原型文件只需包含接口函数的原型.接口函数原型包括函数输入参数的类型和名字,还有函数返回的类型.其中类型支持php的变量类型.如bool,int,string,float等等..
根据上面的需求,创建一个名为repeatString的函数,返回类型为string,接受一个string类型的$inputString字符串,和一个int类型的$repeatCount,表示输出$inputString被重复$repeatCount次的字符串.
首先进入ext目录
cd path/to/php_source_code/ext && \
vim repeatString.proto
编写内容
string repeatString(string inputString, int repeatCount)
保存,使用ext_skel工具生成扩展框架,可以指定扩展名和扩展的原型文件
./ext_skel --extname=repeatString --proto=repeatString.proto
你会看到ext目录下会多了一个repeatString的文件夹,里面放的就是你扩展的所有所需文件
需要留意的有几个文件:
扩展的 config.m4 文件告诉 UNIX 构建系统哪些扩展 configure 选项是支持的,你需要哪些扩展库,以及哪些源文件要编译成它的一部分。对所有经常使用的 autoconf 宏,包括 PHP 特定的及 autoconf 内建的,请查看 Zend Engine 2 API 参考 章节。
####2. 编写核心代码 进入repeatString.c文件,可以看到这个文件都做了些什么:
前些天在研究分布式锁的方案,突发奇想,在云原生时代,像 php、c、go、java 这种原来用特定编程语言开发的程序,已经通过打包、编排,变成了云原生操作系统里的一个工作进程。而像是网络这种原本的操作系统基础设施,也在云原生操作系统里面有了实现(从微服务发展到 service mesh)。
那么,锁是不是也可以看作是基础设施,提供给云原生操作系统的工作进程使用呢?
云原生开发者在编写工作进程逻辑的时候,无需考虑引入哪个分布式锁的包/库,就像是调用系统调用一样,将资源锁住,读写资源,然后再调用系统调用进行解锁。而云原生操作系统负责调度容器进行资源的访问,防止容器争抢资源。这样做的好处是,可以屏蔽掉不同语言的分布式锁之间的实现差异,让业务逻辑开发者专注业务逻辑的优化、思考业务应该使用哪种锁,以及锁的粒度等业务相关的工作。
其实不止是锁,也许还可以思考一下,原生操作系统的基础设施有哪些可以对应到云原生操作系统上的。
且不论叫它 lock mesh 是否合理吧:)
有这个想法后,我在 github、google 和 CNCF landscape 上搜索了一番,没有找到相关的项目或者资料。后续我会再进行一些搜索,确定我是不是想别人已经想到的东西。
如果你也觉得这个想法可行,不妨跟我联系哦:)
有了想法,然后呢?
我列了一些 todo 项:
这周花了点时间看了 skynet 关于定时器功能的源码,以及扩展的了解了一下定时器实现的其他方案,写一遍博客总结一下.内容会分为下面几部分:

首先确定定时器里面的几个概念:
tick: 指 skynet timer 线程每一次处理事件的循环.由于这个循环每隔 0.00025 秒会执行一次,很像时钟滴答滴答的走,很多书籍都称为 tickskynet 启动时间戳: 顾名思义,比如 1528613417 时间戳(北京时间 2018/6/10 14:50:17)代表 skynet 是这个时间点启动的,而且这个时间是不会变的.这个比较容易理解系统启动时间数: 单位 0.01 秒. 顾名思义,但是为什么说是时间数呢?因为这是一个时间的量,比如300,表示系统启动了3秒. skynet timer 线程在每一次 tick 都会去同步这个时间量skynet 启动时间数: 单位 0.01 秒. 与系统启动时间数类似,如 300,表示 skynet 启动了 3 秒,每次 tick skynet timer 线程也会去更新这个时间量首先需要说明的是, skynet 定时器的时间刻度是百分秒(0.01秒).意味着 skynet 最小可以定义事件 0.01 秒后执行.这个精度对于游戏领域可以说是够用了.
skynet 初始化时会创建5个定时器的事件槽,分别是 Near,level0-3.每个定时器事件是按照触发时间(这个触发时间是以 skynet 启动时间数作为参照的.比如触发时间为 300,事件注册时 skynet 创建时间量为 100,表示注册这个事件在 2 秒后执行)为索引插入到对应的槽里面的.用索引的方式安排这些事件保证了查询和插入的性能( 时间复杂度为O(1) ).
这个星期以来,由于工作需要使用skynet,于是看了5天skynet的源码。收获很多,于是周末写篇博文总结一下这周以来的学习。
这5天的学习过程可以概括为5句话:(因为5天嘛)
看了5天后回过头来发现,其实了解的还停留在skynet的核心部分,还没有走出核心。因为skynet还包含很多其他内容,比如他的消息机制的各种api、socket支持、loginserver gateserver、datacenter、harbor等等。。。
不过问题也不大,毕竟核心比较重要,后续还要继续学习。
那下面就来总结一下这5天都看到来什么内容。有可能因为我了解尚浅,写出来的和实际的skynet有出入。不过没关系,后续会回过头来再看看。
下面的总结会按照这个结构:
那么,开始吧。
可以用一个段落来说明
$ ll
drwxr-xr-x 26 root staff 884 6 1 06:30 .
drwxr-xr-x 5 root staff 170 6 2 07:07 ..
drwxr-xr-x 12 root staff 408 6 1 07:10 .git
-rw-r--r-- 1 root staff 94 6 1 05:36 .gitignore
-rw-r--r-- 1 root staff 96 6 1 05:36 .gitmodules
drwxr-xr-x 8 root staff 272 6 1 22:25 .idea
drwxr-xr-x 6 root staff 204 6 1 05:36 3rd -- 第三方c库目录,如lua jmalloc
-rw-r--r-- 1 root staff 12384 6 1 05:58 CMakeLists.txt
-rw-r--r-- 1 root staff 11953 6 1 05:36 HISTORY.md
-rw-r--r-- 1 root staff 1085 6 1 05:36 LICENSE
-rw-r--r-- 1 root staff 3484 6 1 05:36 Makefile
-rw-r--r-- 1 root staff 1085 6 1 05:36 README.md
drwxr-xr-x 7 root staff 238 6 1 05:58 cmake-build-debug
drwxr-xr-x 10 root staff 340 6 1 05:39 cservice -- 可被skynet核心加载的so动态库服务
drwxr-xr-x 33 root staff 1122 6 1 05:36 examples -- skynet提供的一些lua逻辑的例子
drwxr-xr-x 14 root staff 476 6 1 05:39 luaclib -- 可被lua加载的skynet so库
drwxr-xr-x 12 root staff 408 6 1 07:10 lualib -- lua skynet库
drwxr-xr-x 22 root staff 748 6 1 05:55 lualib-src -- ./luaclib 里的so库的源文件
-rw-r--r-- 1 root staff 876 6 1 05:36 platform.mk
drwxr-xr-x 23 root staff 782 6 1 06:57 service -- 可被skynet加载的lua服务
drwxr-xr-x 8 root staff 272 6 1 05:36 service-src - ./cservice 里的so文件的源文件
-rwxr-xr-x 1 root staff 343772 6 1 05:38 skynet -- skynet可执行文件
drwxr-xr-x 41 root staff 1394 6 1 05:36 skynet-src -- ./skynet 的源文件
drwxr-xr-x 3 root staff 102 6 1 05:38 skynet.dSYM
drwxr-xr-x 38 root staff 1292 6 1 05:36 test -- 单元测试文件
从文件目录分析可以看出,skynet这个框架的内容既包含了c语言的实现(主要是c语言),也包含了lua逻辑(一些配置部分,或者可以被替代或者修改的逻辑,如服务加载器launcher.lua的逻辑, 或skynet启动的逻辑bootstrap.lua)。
spring的内容有很多,而springBoot对于spring的优化的内容也有非常多方面可讲。这次主要分享的是spring和springBoot中的IoC和DI,以及我所看到的在我们java项目中的体现。
控制反转即反转控制类实例的方式。 从控制反转为被控制 从自己生成/销毁反转为别人生成/销毁 控制反转的好处在于:
依赖注入是控制反转的一种实现方法。“别人"管理类实例的生成/销毁,并将业务逻辑需要用到的类实例注入进来。
一个Spring应用有一个应用上下文(ApplicationContext),充当类实例的容器,管理着类实例的生成和销毁,并在应用需要时返回/注入对应的类实例。 Spring按类实例的种类管理容器中的类实例(种类有唯一的种类id:beanId)。每一种类实例称为Bean。 与很多IoC框架类似,类实例的注入方式有很多种(单例/非单例),Spring中类实例存在方式可以是:
以下是spring注册Bean的其中某些特性:
另外,spring的IoC和DI利用了java的注解实现,也是面向切面编程的一个体现。在加载应用的时候,切换到应用bean管理切面,扫描类并注册Bean,在需要注入时,再次切换到bean管理切面,返回需要的bean。
springBoot简化了spring的配置部分 spring通过xml配置文件扫描/注解方式注册类实例 springBoot默认通过注解方式注册类实例,并默认扫描application包以及子包的所有类实例。默认以注解方式标记类实例。 springBoot可以沿用spring注解配置Bean时的规则
要说明这点,先要说一下什么是用户程序,什么是系统程序。
为了限制用户程序的权限,和将一些系统操作抽象出来,计算机系统将一些高等操作称为系统程序,如申请内存,读取内存信息到寄存器/读取磁盘信息到内存,从一个外部设备读取信息到磁盘,等。
同时这样分离还有一个好处,就是提高系统的吞吐率。众所周知,cpu寄存器-一级缓存-二级缓存-内存-磁盘-网络存储设备,越往后读取的速度越慢,而让cpu盲目的等待IO的数据到位,然后再执行后续的程序,是极其浪费cpu计算资源的。而另一方面,由于计算机系统的多进程特性,一个用户程序在一个进程调用系统程序,进行耗时的IO时,完全可以对这个进程进行挂起,让cpu资源去处理其他进程的逻辑,待IO完毕,给挂起的程序一个通知,让挂起的程序继续进行IO后的逻辑。
这种通知进程的行为,就是异常,而这个通知,就是后面要讲到的信号。
计算机系统的异常有4种:中断,陷阱,故障,终止
中断
中断通常发生在用户程序和硬件交互。中断的过程是异步的,即发生中断时用户程序被挂起(被中断了),待硬件返回后,用户程序继续发生中断代码后续的命令。
陷阱
陷阱和中断类似,只不过陷阱是同步的。陷阱像是一种故意的异常,发生在软件程序上的调用,如用户程序向系统程序申请fock一个子进程,或者执行另外一个程序。所以实现系统程序的封装和系统程序和用户程序的隔离,接口化,使用的也是陷阱这种异常。
故障
故障是由于错误引起的。故障发生时,程序的控制转移到故障处理程序,待故障处理程序逻辑结束后,程序计数器会尝试回到故障出现的行命令。一个典型的故障就是内存缺页故障。当程序读取内存的某一个虚拟地址,发现读取的内存页面不在内存里面,于是引起缺页故障,处理程序将数据从磁盘读取到内存对应的页面,故障处理结束,程序再次回到读取内存页面的那行,这时候,读取的代码再次执行,就可以正常运行了。
终止
终止发生在程序出现致命错误的时候,系统在执行完故障处理程序后,会停止该程序。如:内存损坏时。
信号有很多种类型,系统给不同的信号定义了不同的意义,其中也保留了用户可以自定义的信号

(图出自<<Computer Systems Third edition»)
比如在当前进程按下ctrl+c..实际上就是键盘通过操作系统给当前进程发送了一个2信号
通过kill命令,传入信号和进程id,可以给进程发送一个信号。常见的nginx通过kill命令重启nginx,平滑重新加载配置,就是用了这个特性。
在程序逻辑里,可以调用c的kill函数给进程id传入信号。如果进程id大于0,向进程id发送信号;如果进程id为0,则为自己和自己所在的进程组每个进程发送信号;如果进程id小于0,则为进程id的绝对值以及进程id绝对值所在的进程组所有的进程都发送信号。

(图出自<<Computer Systems Third edition»)
在程序逻辑里面,用户也可以注册信号的handler函数,当进程被传入信号时,程序控制就会交给handler函数。处理完后,控制交还给故障发生时的后一句命令,程序继续执行。

(图出自<<Computer Systems Third edition»)
c语言提供setjump和longjump方法。setjmp方法保存当前进程的运行状态(包括程序计数器,栈指针和通用目的寄存器),而longjmp根据之前保存的状态还原程序执行的状态,继续执行setjmp处后面的逻辑。
高级语言的异常捕捉trycatch,或者说php的yield,实际上是setjmp longjmp的封装:在catch处setjmp,然后执行try里面的代码段,throw时调用longjmp。