
Kubernetes | Kubebuilder
前两天我们提到Nginx Ingress Controller可以通过修改Service和Nginx配置来配置TCP代理。结尾的时候提出来一个问题是,有没有更好地办法。
能这么问,那当然是有啦。那就是利用CRD和Controller的力量。
引
回顾一下之前我们做了什么
我们之前实际上就是做了两件事情:
- 配置暴露Nginx的LoadBalancer Service,多监听一个端口
- 配置Nginx的配置,通过ConfigMap来更新nginx.conf,告诉nginx要代理到一个Service上
这样难道不够好用吗
当然不够好用,我如果不知道可以用ConfigMap来配置tcp代理,就得绕一大圈,知道了有这么个东西,我也可能会记错用法,或者是在配置时手误。而且我还得一个个改资源,很麻烦。作为新时代的懒人,当然不能满足于手工配一配啦。
设计
知道了要做什么,以及做的目的,那么我们来尝试一下用K8S来解决这个问题吧。
验证数据
为了防止写错,我们需要验证数据有效性。
我们前两天看到CRD。CRD是可以设置Schema的,通过Schema就可以限制很多东西。除此之外,K8S的Admission Webhook也可以验证数据的有效性。
自动更新资源
既然我是新时代懒人,那么手工配置好几个地方就显得非常傻。能一个地方写好配置,然后自动配置好所有有关的资源多好。
所以我们就需要监控CRD的变动,更新Service的Spec,完成以后把配置更新到ConfigMap让Nginx把端口代理过去。
工具: Kubebuilder
Kubebuilder是K8S的一个SIG项目,主要是为了方便第三方利用CRD编写k8s api。和我们的目的恰好相近,所以我直接选择了kubebuilder。除此之外还有一个非常常用的operator-sdk,我没有那么多时间一个个学过去,就不赘述了。
Kubebuilder概念
API
在Kubebuilder中,一个CRD称为是一个API。简单说类似于URI,通过(Group,Version,Kind,Namespace,Name)定位一个资源。核心是/api/<version>/
目录下的类型,通过在类型上设置成员和注释属性,来更新CRD的配置。
Webhook
Kubebuilder中的Webhook对应K8S中的Admission Webhook和Conversion Webhook。
这两种Webhook中,Admission Webhook是K8S中用来动态判断一个配置是否合法的工具,而Conversion Webhook是新加入的用来在API不同版本之间切换的。
Reconcile
Reconcile翻译成调和、和解。但是这里的意义显然不大一样。
我们在解释异步是怎么一回事的时候,解释过很多程序,或者说大部分的程序,其实都是处理状态的。只不过我们一开始写程序的时候,只能考虑到状态按照我们思路中转移的过程,只能顺序的让状态向我们期待的方向转移。
在现实中,状态是很难依照自己的意志一步步去转移的,有可能你希望A到B+C到D这样一帆风顺的达到D这个理想状态,但是实际上会遇到B和C的依赖没有满足需要等待的情况,会遇到处于中间状态或者达到理想状态的程序、莫名其妙跑到M状态的情况。所以我们随着写程序积累的量变多,顺序的让状态从A变成B+C变成D是不够的,我们需要处理已知状态向下一个状态迁移的过程,需要检查之前达到理想状态的程序是不是还在理想的状态上,还需要处理未知状态如何处理。
这个驱动状态从一个状态向另一个状态改变的过程,就叫做Reconcile。
用Deployment举个例子
用我们稍微熟悉一些的Deployment举个例子来说明Reconcile。只是我的理解作为一个例子,并不是K8S中的内部逻辑。
- Deployment:
- 获取状态《是否需要创建新的Pod》:根据《Pod数量少于需要的》以及《已有的Pod是否都在Ready状态上》,加上其他的策略,得出是否需要创建新的Pod
- 需要创建新的Pod:创建一个Pod
- 获取状态《需要删除掉的Pod》
- 删除这些Pod
- 获取状态《是否需要创建新的Pod》:根据《Pod数量少于需要的》以及《已有的Pod是否都在Ready状态上》,加上其他的策略,得出是否需要创建新的Pod
- Pod:
- 获取状态《是否满足创建容器的条件》:根据VolumeMount以及类似的条件计算
- 不满足创建容器的条件:退出等待下次Reconcile
- 获取《镜像是否已经在本地》:
- 不在:拉取镜像
- 获取《是否已经创建容器》:
- 未创建:创建容器
- 获取《系统有没有配置好容器》:
- 没有配置好:退出等待下次Reconcile
- 获取《有没有启动容器》:
- 没有启动:启动容器
- 获取《Pod需要不需要重新启动》:根据《重启策略》和《Pod中Container退出的情况》计算
- 需要:启动容器
- ……
- 获取状态《是否满足创建容器的条件》:根据VolumeMount以及类似的条件计算
通过这样一个循环,就可以让Deployment在创建以后,逐步的创建容器、并且满足replication的要求。
具体实现:回到之前Nginx中TCP代理的例子
我们之前提到说Nginx的TCP代理没有合适的资源类型来放,改config map一方面是没有办法单独的管理tcp代理的信息,没有办法分散的在各个地方放配置,另一方面我们还需要更新一些其他的资源比较麻烦。
这个时候kubebuilder就该上场了。我们可以定义一个CRD叫nginx-stream/v1.TCPIngress,然后根据spec更新各种资源,最后修改status。
具体的代码我过两天再贴上来。