k8s以及云原生相关概念近年来一直比较火热,阿丸最近搞了个相关项目,小结一下。java
本文将重点分享阿里开源项目otter适配k8s部署的改造过程,其中的改造过程和技巧应该适用于将大多数开源项目改造到k8s进行部署。node
1.背景
otter是阿里开源的分布式数据库同步系统,基于数据库增量日志解析,并准实时同步到本机房或异地机房的mysql/oracle数据库(相关内容能够参考https://github.com/alibaba/otter,本文不作过多赘述)。mysql
为了充分利用物理资源、快速扩容同步节点、拥抱云原生,决定使用k8s部署otter。git
otter的项目总体上自成一体,出于改形成本考虑,尽可能在项目已有基础上,作一些适配,不改动源代码。github
本文将重点分享对于otter适配k8s部署的改造过程,有不当之处,还请多多指教。web
涉及到几个核心内容:sql
otter基本架构docker
dockerfile编写数据库
deployment编写centos
启动脚本改造
K8s中固定ip/port访问
2.otter的基本架构
典型管理系统架构,manager(web管理)+node(工做节点)
manager运行时推送同步配置到node节点
node节点将同步状态反馈到manager上
基于zookeeper,解决分布式状态调度的,容许多node节点之间协同工做
基于Canal开源产品,获取数据库增量日志数据。固然,otter采用了canal的嵌入式模式,不是独立的canal节点。
基于以上部署架构,咱们只须要将otter-manager和otter-node部署到k8s上。
尤为是otter-node,须要利用k8s实现节点快速水平扩展、计算性能弹性扩缩容。
2.Dockerfile编写
2.1 otter-manager的Dockerfile
otter-manager比较简单,包括几个步骤:
使用一个centos的基础镜像
设置时区
建立目录
拷贝下载好的manager.deployer-4.2.18到指定目录
使用启动startup-new.sh脚本启动(这里对本来的启动脚本作了一些改造,后文进行详述)
具体以下所示:
2.2 otter-node的Dockerfile
otter-node稍微有所不一样,根据官方文档说明,须要安装aria2来作文件传输。
注意注意,因为aria2安装很是慢,所以,咱们须要先安装aria2做为一个新的基础镜像,而后在新的基础镜像上构建otter-node镜像,能大大提升后续镜像构建速度。
新的基础镜像以下,命名为 registry.xxx.com/xxx/otter-node-base:1.0。
而后在此基础上构建新的otter-node镜像。
注意,otter-node的配置方式比较特殊,须要先在otter-admin上获取一个nid,而后才能运行一个otter-node。
因此,咱们在dockerfile中以ARG 声明一个nid,而后在后续构建镜像的时候,经过 --docker-arg 传入nid具体的值。
固然,若是把nid看做一个配置文件的话,也能够用下文提到的ConfigMap的形式在Deployment中挂载进去
3.Deployment编写
什么是Deployment?
k8s的一个Deployment控制器为 Pods 和 ReplicaSets 提供声明式的更新能力。咱们经过编写Deployment描述指望的目标状态,而后 Deployment 控制器更改Pods或者ReplicaSets的实际状态, 使其变为指望状态。
具体关于Deployment的知识不展开说明,能够参考k8s官方文档。
咱们须要部署测试环境与生产环境两套集群,而不管是otter-manager仍是otter-node,都依赖于读取 conf/otter.properties 做为配置。
所以,咱们须要根据环境,修改不一样的otter.properties。
那么,对于k8s部署来讲,能够采用同一份镜像,而后在不一样环境(k8s的不一样namespace)中将otter.properties做为ConfigMap写入,最后经过volume的形式挂载到pod的指定路径上。
这里对几个名词作简单介绍,详细内容能够参考k8s官方文档。
ConfigMap:一种 API 对象,用来将非机密性的数据保存到键值对中。使用时,Pods能够将其用做环境变量、命令行参数或者存储卷中的配置文件。(若是想存储的数据是机密的,可使用 Secret,而不是ConfigMap)
Volume:卷的核心是包含一些数据的一个目录,Pod 中的容器能够访问该目录。所采用的特定的卷类型将决定该目录如何造成的、使用何种介质保存数据以及目录中存放 的内容。使用卷时, 在 .spec.volumes 字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置。
因此,首先在指定环境(namespace中)建立configmap,以otter.properties做为key,以文件内容做为value。
具体命令以下
kubectl create configmap otter-manager-dev-config --from-file=otter.properties=conf/otter-dev.properties -n otter-system
在namespace为otter-system中建立一个名字为otter-manager-dev-config的ConfigMap,其中,以otter.properties做为key,以文件内容做为value。
产生的ConfigMap以下图所示
最后,将这个ConfigMap在Deployment中用volume进行引用,而后经过volumeMounts挂载到指定目录,Deployment具体以下所示。
这里须要特别注意volumeMounts的路径覆盖问题,须要在volumeMounts中配置subPath为具体文件名。
4.启动脚本改造
Otter包括两个部分,管理控制台manager和工做运行节点node,正常状况下都是用各自的启动脚本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进程意外退出了,那么方案二就能使得1号进程也结束,而后pod就能自动重启了。而方案一的话,因为startup.sh脚本仍然在执行tail,因此即便java进程退出,1号进程也不会结束。
具体修改以下:
最终pod中的进程如图所示
1号进程是启动脚本
1号进程的子进程是otter的java应用进程(前台进程)
4.2 虚拟机大小自定义配置
因为otter项目中,将jvm的启动参数配置在了start.sh中,不方便进行手动配置。
所以,将start.sh的配置jvm参数的逻辑注释掉,采用本身配置的环境变量JAVA_OPTIONS进行注入。
这个环境变量的注入方式也比较简单,就是在Deployment中的env配置的(蓝色框部分),方便之后手动修改jvm参数大小而不用修改镜像。
5.k8s上固定IP/Port访问
otter-node的部署中,有个比较特殊的地方。
不一样于普通的微服务的无状态扩展,otter-node的部署必须指定nid、ip、port,这种设计听说是为解决单机部署多实例而设计的,容许单机多node指定不一样的端口(具体能够参考官方wiki,https://github.com/alibaba/otter/wiki/Node_Quickstart,这里不展开说明)。
仍是直接看看如何在k8s上进行适配吧。
这里采用了k8s的NodePort进行处理。
NodePort 服务是引导外部流量到你的服务的最原始方式。NodePort,正如这个名字所示,在全部节点(虚拟机)上开放一个特定端口,任何发送到该端口的流量都被转发到对应服务。以下图所示。
在上面的配置中,可使用IP1:3000 或者 IP2:3000 或者 IP3:3000 访问service。
固然,为了保证不绑定特定KVM的IP,咱们在前面挂一个SLB服务,经过访问SLB的 虚拟IP:PORT 的形式访问。
对于otter部署来讲,otter-manager须要两组 IP:PORT、每一个node须要三组 IP:PORT。
注意,因为otter部署中,每一个node须要暴露的port都是不一样的,因此每次新增一个otter-node,都须要新增三组 IP:PORT。
咱们以otter-node为例,来看下NodePort类型的Service的yml文件吧。
kind为service
type为NodePort
配置了三组端口。port/targetport都是应用暴露端口,而nodePort是对外访问端口。
6.总结
通过这样的改造,咱们就能用k8s的部署otter-manager和otter-node了,而且可以快速扩容节点、弹性使用机器资源。
咱们回顾一下其中的关键问题和技巧:
Dockerfile编写中,能够把环境相关依赖打成一个新的基础镜像,提升后续应用镜像的构建速度。
Dockerfile中,能够经过ARG定义一些构建过程当中的变量,进行替换。
对于不一样环境的配置文件,能够在不一样环境(k8s的namespace)下配置不一样的ConfigMap,而后在Deployment文件中经过volumeMounts的方式挂载进去。
对于后台进程,须要改造为前台进程使得pod可以保持
对于一些特定的环境变量,能够在Deployment中经过env进行传入。
其余开源项目若是有须要上k8s的,这些技巧应该都能用上。
若是你有更好的一些方式和技巧分享,欢迎留言交流~~~
往期热门笔记合集推荐:
原创:阿丸笔记(微信公众号:aone_note),欢迎 分享,转载请保留出处。
扫描下方二维码能够关注我哦~
本文分享自微信公众号 - 阿丸笔记(aone_note)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。