Springboot应用在k8s集群中解耦配置项

对于传统开发人员来说,k8s集群部署和传统的在linux主机上部署还是有挺大区别的。

主要区别集中在配置文件的处理上面。

传统部署jar包方法,可以再jar包目录下或者config目录下丢一个yml配置文件,springboot在启动时,会优先加载jar包目录下的配置文件,如果没有,才会加载jar包中的配置文件。我们在服务器上,只需要针对性的修改下配置文件就可以实现配置项和jar包的解耦。

Docker部署其实也可以再docker run或者dockerfile里面,讲配置文件目录映射到宿主机,然后通过宿主机配置文件修改参数。

那个k8s呢?集群内随机创建pod对象,文件存储基本也不会使用本地卷,都是用分布式文件系统(FastDFS)或者对象存储(MinIO),好像没有地方放配置文件了啊。那么是不是我上线前就必须把线上配置丢到程序里的配置文件中去呢?

答案那肯定是不用啦。

其实同时做过运维和开发的技术可能会知道,其实springboot的yml文件,其实是支持操作系统环境变量加载的。

例如:

redis:
    port: 16379
    host: 36.134.204.163

上面是一个常见的redis的配置,其实他还可以成下面这样:

redis:
    port: ${REDIS_PORT:16379}
    host: ${REDIS_HOST:36.134.204.163}

本机运行的时候,其实没有任何影响。

那么下面这种写法是什么意思呢?

这里就要说道系统环境变量了。还记得JavaHome怎么配置吗?那其实就是环境变量。

环境变量是系统中配置的全局参数变量,任何程序都可以直接通过系统接口读取到环境变量的值。

上面的写法就是,如果系统中存在REDIS_PORT环境变量参数设置,那么就使用这个设置,如果没有,那么使用16379作为默认值。host也是一个道理。

有兴趣的可以写个main函数,配置一个JAVA_HOME的yml参数,然后控制台打印下看看效果。

于是乎,第一种解决办法就来了。

方案一

1. 将服务中所有需要解耦的配置项全部配置成环境变量+默认值的方式。

2. 将环境变量的列表提供给k8s运维人员,由他按照列表,配置k8s部署的yml文件,通过spec下env增加对应环境变量设置,改为k8s环境下设置。

k8s会吧env配置下的参数,输出到容器的环境变量中,服务直接读取就会生效。从而实现解耦。

其实这个办法是比较推荐的,可以开发组内通过规范的形式,吧mysql、redis、mq、nacos等信息都统一处理上。基本不会影响开发人员本地开发测试工作。同时也不会增加k8s运维人员过多负担。

    spec:
      containers:
      - args: []
        env:
        - name: RUNTIME
          value: 'docker'
        - name: RUNTIME_ENV
          value: 'prd'
        - name: CMIOT_DATASOURCE_IP
          value: '192.168.0.60'
        - name: CMIOT_DATASOURCE_PORT
          value: '3306'
        - name: CMIOT_DATASOURCE_USERNAME
          value: 'root'
        - name: CMIOT_DATASOURCE_PASSWORD    #数据库密码

方案二

有一些自我感觉良好的开发人员会说,我可以通过启动参数动态读取外部配置,从而实现解耦。

好吧,这也是一个办法,就是通过java -jar _DXXXXX的办法,将参数传进去,然后服务中读取应用。

其实也不失为一种办法,就是有点麻烦。

首先需要服务中接受并处理参数;然后需要dockerfile进行配置;然后还需要在deploy的yml文件中进行配置。

任何一个环节出错,都可能导致系统配置信息对不上,直接导致系统出错,排查也挺麻烦的。

不推荐!!!

方案三

其实还有一个办法,经测试,deploy的env的配置,会复写springboot中yml对应key的值。

于是乎我们其实可以直接写成这样:

      env:
        - name: spring.redis.host
          value: '192.168.216.219' 
        - name: spring.redis.port
          value: '6379'
        - name: spring.redis.password
          value: '123456'

spring.redis.host其实就对应这application.yml中的spring.redis.host。这里k8s在创建镜像的时候,其实会复写掉这个值

 于是k8s下启动容器的java服务中,生效的是k8s配置的192.168.216.219。

jar包中原先的配置就被覆盖掉了。

这种方式感觉上其实比方案一要简单,但其实存在些问题。比方说不同项目中yml的层级不同,可能配置项目的名称也不一样,就到只在k8s管理员那里,不太好通过统一的yaml模板部署项目,每次都需要跟研发人员去核对。

当然,如果公司就一个标准框架,或者就一个产品,那么其实直接这么用也没什么问题。

总结

        上述这三种方式,其实都能够达成目的,但是,方案一和方案三比较推荐,首选方案一。定义好一套环境变量名称后,对于k8s运维来说,可以做一个包含所有参数的标准模板,毕竟参数多写几个其实没什么影响。

        有人说nacos可以实现yml参数配置。但是,你nacos的地址总要有地方设置。而且nacos改了参数,如果想要立刻生效,还需要程序单独处理。但是改个yml,只需要kubectl apply一下,然后等会k8s就给你搞定了。(K8s会创建一个新的pod,等新的pod启动后,释放掉老的pod,于是配置就自动更新了)

猜你喜欢

转载自blog.csdn.net/lanwilliam/article/details/125910569