k8s和cloud-native相关的概念这几年比较火。Amaru最近启动了一个相关项目。让我总结一下。本文将重点分享阿里巴巴开源项目otter适配k8s部署的改造过程。转换过程和技术应该适用于大多数开源项目转换到k8s进行部署。一、背景Otter是阿里开源的分布式数据库同步系统,基于数据库增量日志分析,准实时同步到本地或远程机房的mysql/oracle数据库(相关内容可参考到https://github.com/alibaba/otter,本文不再赘述)。为了充分利用物理资源,快速扩容同步节点容量,拥抱云原生,决定使用k8s部署otter。Otter的项目作为一个整体是独立的。为了改造成本,尽量在项目已有的基础上做一些适配,不改源码。本文将重点分享otter适配k8s部署的改造过程。如有不当之处,敬请指教。涉及几个核心内容:otter基本结构,编写dockerfile,编写启动脚本,改造K8s中固定ip/端口访问2.otter基本结构,典型管理系统架构,manager(web管理)+node(工作节点)managerrunningpush同步配置到node节点node节点基于zookeeper将同步状态反馈给manager,解决分布式状态调度,让多个节点基于Canal开源产品协同工作,获取数据库增量日志数据。当然,otter采用的是canal嵌入的方式,并不是一个独立的canal节点。基于上述部署架构,我们只需要将otter-manager和otter-node部署到k8s中即可。尤其是otter-node,需要借助k8s来实现节点的快速横向扩展和计算性能的弹性伸缩。2.Dockerfile编写2.1otter-manager的Dockerfileotter-manager比较简单,包括几个步骤:使用centos基础镜像,设置时区,创建目录将下载的manager.deployer-4.2.18复制到指定目录,开始启动-新的。sh脚本启动(这里对原来的启动脚本做了一些改动,后面会详细介绍)如下:2.2otter-node的Dockerfileotter-node略有不同。根据官方文档,需要安装aria2才能进行文件传输。注意,由于aria2的安装速度很慢,所以我们需要先安装aria2作为新的基础镜像,然后在新的基础镜像上构建otter-node镜像,这样可以大大提高后续镜像构建的速度。新的基础镜像如下,命名为registry.xxx.com/xxx/otter-node-base:1.0。然后在其上构建一个新的otter-node图像。注意otter-node的配置方式比较特殊。在运行otter-node之前,您需要在otter-admin上获得一个nid。因此,我们在dockerfile中声明一个带ARG的nid,然后在后面构建镜像的时候通过--docker-arg传入nid的具体值。当然,如果把nid当成一个配置文件,也可以通过下面提到的ConfigMap的形式挂载到Deployment中。3.Deployment的写法什么是Deployment?k8s的Deploymentcontroller提供了声明式的更新能力。我们通过编写Deployment来描述期望的目标状态,然后Deploymentcontroller改变Pod或ReplicaSets的实际状态,使其成为期望的状态。关于Deployment的具体知识就不解释了,可以参考k8s官方文档。我们需要部署两套集群,测试环境和生产环境,otter-manager和otter-node都依赖读取conf/otter.properties作为配置。因此,我们需要根据环境修改不同的otter.properties。然后,对于k8s的部署,可以使用同一个image,然后在不同的环境(k8s的不同namespace),将otter.properties写成ConfigMap,最后以volume的形式挂载到pod的指定路径。这里简单介绍几个名词。具体可以参考k8s官方文档。ConfigMap:用于将非机密数据存储到键值对中的API对象。使用时,Pod可以将其用作环境变量、命令行参数或存储卷中的配置文件。(如果你要存储的数据是保密的,可以使用Secret代替ConfigMap)Volume:Volume的核心是一个包含一些数据的目录,可以被Pod中的容器访问。使用的特定卷类型将决定目录的形成方式、用于保存数据的介质以及目录中存储的内容。使用卷时,在.spec.volumes字段中设置pod提供的卷,并在.spec.containers[*].volumeMounts字段中声明卷在容器中的安装位置。因此,首先在指定的环境中(命名空间中)创建一个configmap,以otter.properties为key,以文件内容为value。具体命令如下kubectlcreateconfigmapotter-manager-dev-config--from-file=otter.properties=conf/otter-dev.properties-notter-system在命名空间otter中创建一个名为otter-manager-的名字-systemdev-config的ConfigMap,其中otter.properties为key,文件内容为value。生成的ConfigMap如下图所示。最后在Deployment中通过volume引用ConfigMap,然后通过volumeMounts挂载到指定目录。部署如下。这里需要特别注意volumeMounts的路径覆盖,需要将volumeMounts中的subPath配置为具体的文件名。4.启动脚本修改Otter包括管理控制台管理器和工作运行节点节点两部分,正常情况下使用各自的启动脚本startup.sh启动。为了适配k8s,我们需要修改启动脚本。本文以otter-manager的启动脚本为例,otter-node类似。将启动脚本startup.sh改成startup-moon.sh,重点解决两个问题。前台进程一直在运行。jvm参数自定义修改4.1前台进程保持运行。由于容器中entrypoint启动的进程是1号进程,一旦1号执行完毕,容器就会退出。原来的startup.sh中,以java启动后,使用“&”将java进程转为后台进程,所以startup.sh作为1号进程,很快执行完毕,容器自动退出。所以我们需要保留1号进程,不要退出。这里考虑两种解决方案:在Startup.sh脚本中增加一个前台进程保留,比如tail-f/dev/null命令去掉“&”,这样java启动后就一直保留为前台进程。想了想,还是选择了方案二。主要原因是要利用自动pod重启功能。如果Java进程意外退出,方案2可以让进程1也结束,然后自动重启pod。至于方案1,由于startup.sh脚本还在执行tail,即使java进程退出,进程1也不会结束。具体修改如下:最终pod中的流程如图所示。第一个进程是启动脚本。第一个进程的子进程是otter的java应用进程(前台进程)。4.2自定义配置虚拟机大小由于otter项目,jvm的启动参数配置在start.sh中,不方便手动配置。所以将start.sh中配置jvm参数的逻辑注释掉,使用自己配置的环境变量JAVA_OPTIONS进行注入。这个环境变量的注入方式也比较简单,就是配置在Deployment中的env中(蓝框部分),这样可以方便的手动修改jvm参数大小而不用修改image。5、在k8s上部署固定IP/端口访问otter-node有一个特殊的地方。不同于普通微服务的无状态扩展,otter-node的部署必须指定nid、ip、port。这个设计据说是为了解决单机部署多实例的问题,允许单机多个节点指定不同的端口(具体可以参考官方wiki,https://github.com/alibaba/otter/wiki/Node_Quickstart,这里不做解释)。下面就来看看如何在k8s上进行适配吧。这里使用k8s的NodePort进行处理。NodePort服务是将外部流量引导至您的服务的最原始方式。NodePort,顾名思义,在所有节点(虚拟机)上打开一个特定的端口,任何发送到这个端口的流量都会被转发到相应的服务。如下所示。在上面的配置中,你可以使用IP1:3000或者IP2:3000或者IP3:3000来访问服务。当然,为了保证不绑定具体的KVMIP,我们在前面挂了一个SLB服务,通过访问SLB的虚拟IP:PORT来访问。对于otter部署,otter-manager需要两套IP:PORT,每个节点需要三套IP:PORT。注意在otter部署中,每个节点需要暴露不同的端口,所以每次添加一个otter-node,都需要添加三组IP:PORT。下面以otter-node为例,看看NodePort类型Service的yml文件。Kind为服务类型和NodePort配置了三组端口。port/targetport是应用程序暴露的端口,而nodePort是外部访问端口。6.总结经过这样的改造,我们可以使用k8s部署otter-manager和otter-node,可以快速扩展节点,灵活使用机器资源。回顾一下关键问题和技巧:在编写Dockerfile时,可以将环境相关的依赖打包成一个新的基础镜像,以提高后续应用镜像的构建速度。在Dockerfile中,构建过程中的一些变量可以通过ARG来定义和替换。针对不同环境下的配置文件,可以在不同的环境(k8s命名空间)配置不同的ConfigMap,然后通过volumeMounts挂载到Deployment文件中。对于后台进程,需要转化为前台进程,让pod维护一些特定的环境变量,在Deployment中通过env传入。如果其他开源项目需要用到k8s,这些技能应该都有。
