首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >K8s 集群使用 ConfigMap 优雅加载 Spring Boot 配置文件

K8s 集群使用 ConfigMap 优雅加载 Spring Boot 配置文件

作者头像
哎_小羊
发布2019-05-25 19:47:05
5.1K0
发布2019-05-25 19:47:05
举报
文章被收录于专栏:哎_小羊哎_小羊哎_小羊

文章目录
  • 1、Spring Boot 加载配置介绍
  • 2、环境、软件准备
  • 3、Spring Boot 示例工程 Demo
  • 4、K8s ConfigMap 加载工程配置文件
    • 4.1、直接加载环境的配置文件
    • 4.2、配置要加载的环境属性

1、Spring Boot 加载配置介绍

我们知道 Spring Boot 工程默认的配置文件名称为 application.properties,SpringApplication 将从以下位置加载 application.properties 文件,并把它们添加到 Spring Environment 中:

  • 当前目录下的 /config 子目录
  • 当前目录
  • 一个 Classpath 下的 /config
  • Classpath 根路径

如果我们运行时想指定运行哪个环境的配置文件,可以有三种方式:

  1. 在工程 resources 文件夹下 application.properties 文件中配置 spring.profiles.active=dev 指定加载的环境类型
  2. 启动 jar 时,指定 --spring.profiles.active=prod 加载的环境类型
  3. 启动 jar 时,指定 --spring.config.location=target/application.properties加载配置文件位置

至于在工程中如何获取这些配置文件值,这里就不在描述了,这个不是本次演示的重点。

2、环境、软件准备

本次演示环境,我是在本机 MAC OS 上操作,以下是安装的软件及版本:

  • Docker: 17.09.0-ce
  • Java: 1.8.0_211
  • Spring-boot: 2.1.4
  • Oracle VirtualBox: 5.1.20 r114628 (Qt5.6.2)
  • Minikube: v0.28.2
  • Kubernetes: v1.10.0
  • Kubectl:
    • Client Version: v1.10.0
    • Server Version: v1.10.0

注意:这里 Kubernetes 集群搭建使用 Minikube 来完成,Minikube 启动的单节点 k8s Node 实例是需要运行在本机的 VM 虚拟机里面,所以需要提前安装好 VM,这里我选择 Oracle VirtualBox。k8s 运行底层使用 Docker 容器,所以本机需要安装好 Docker 环境,这里忽略 Docker、VirtualBox、Minikube、Kubectl 的安装过程,可以参考之前文章 Minikube & kubectl 升级并配置,这里结合代码着重介绍下在 K8s 集群中如何使用 ConfigMap 优雅加载 Spring Boot 工程配置文件。

3、Spring Boot 示例工程 Demo

首先我们使用 IDEA 创建一个 Spring Boot 项目,项目名为 demo,为了好演示加载不同配置文件展示效果,这里添加 swagger-ui 依赖,然后新建 Controller 类 DemoController,通过读取配置文件中的 key 值并返回,代码如下:

package com.example.demo.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/demo")
@PropertySource("classpath:mysql.properties")
@Api(tags = "DemoController", description = "测试读取不同资源文件")
public class DemoController {

    @Value("${env}")
    private String env;
    @Value("${msg}")
    private String msg;
    @Value("${mysql.hostname}")
    private String mysl_url;
    @Value("${mysql.port}")
    private String mysql_port;

    @ApiOperation(value = "获取配置文件变量")
    @RequestMapping(value = "", method = RequestMethod.GET)
    public Map<String, Object> getDemoKey() {
        Map<String, Object> map = new HashMap<>();
        try {
            map.put("env", env);
            map.put("msg", msg);
            map.put("mysql_url", mysl_url);
            map.put("mysql_port", mysql_port);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }
}

然后在 resources 目录下,分别新建不同环境的配置文件 application.properties,以及其他配置文件 mysql.properties 如下:

# application-dev.properties
env=dev
msg=this is dev env properteis.

# application-prod.properties
env=prod
msg=this is prod env properteis.

# application-test.properties
env=test
msg=this is test env properteis.

# mysql.properties
mysql.hostname=127.0.0.1
mysql.port=3306

同时在 application.properties 配置文件下指定加载的环境为 dev,毕竟本地开发,还是使用开发环境配置比较多。

# application.properties
spring.profiles.active=dev

其他代码文件这里不再贴出来了,源码已经上传到 Github 上 spring-k8s-configmap-demo 。接下来,我们本地启动一下,看下能否正确读取到 dev 环境配置文件吧!

$ mvn clean package
$ java -jar demo-0.0.1-SNAPSHOT.jar

启动完毕,本地浏览器访问 http://127.0.0.1:8080/swagger-ui.html 页面,测试一下 http://127.0.0.1:8080/demo 接口,可以看到正确加载到 application-dev.propertiesmysql.properties 配置文件内容。

spring-boot-demo
spring-boot-demo

OK, 工程启动没有问题,接下来我们来创建 Dockerfile 来构建工程镜像,方便后边部署到 K8s 集群中,新建 Dockerfile 如下:

$ vim Dockerfile
FROM huwanyang168/centos7_jdk8:v1.2

MAINTAINER huwanyang168 <huwanyang168@163.com>

ADD demo-0.0.1-SNAPSHOT.jar /opt

WORKDIR /opt

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "demo-0.0.1-SNAPSHOT.jar"]

简单说明一下,我们是基于 centos7_jdk8 环境,添加并启动编译后的 jar,最后构建镜像并 push 到镜像仓库(这里我推送到个人 Docker Hub 仓库)。

$ docker build -t huwanyang168/demo:0.0.1 -f Dockerfile .
$ docker push huwanyang168/demo:0.0.1

启动一下,也是妥妥没有问题的,这里就不在演示了。

4、K8s ConfigMap 加载工程配置文件

接下来,我们创建一个可以在 K8s 集群中运行该镜像的资源类型 yaml 文件,该文件主要包含 NamespaceConfigMapDeploymentService 四种资源类型,这里我使用两种方式加载配置文件,对比下二者的好处和弊端。

4.1、直接加载环境的配置文件

yaml 文件如下:

apiVersion: v1
kind: Namespace
metadata:
  name: wanyang3
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-configmap
  namespace: wanyang3
data:
  application.properties: |
    env=local
    msg=this is local env properteis.
  mysql.properteis: |
    mysql.hostname=10.10.10.10
    mysql.port=3333  
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-hwy
  namespace: wanyang3
  labels:
    app: demo-hwy
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: demo-hwy
  template:
    metadata:
      labels:
        app: demo-hwy
    spec:
      containers:
      - name: demo
        image: huwanyang168/demo:0.0.1
        imagePullPolicy: IfNotPresent
        args: ["--spring.config.location=application.properties,mysql.properties"]
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: demo-config
          mountPath: /opt/application.properties
          subPath: application.properties
        - name: demo-config
          mountPath: /opt/mysql.properties 
          subPath: mysql.properties   
      volumes:
      - name: demo-config
        configMap:
          name: demo-configmap
          items:
            - key: application.properties
              path: application.properties 
            - key: mysql.properties
              path: mysql.properties  
---
apiVersion: v1
kind: Service
metadata:
  name: demo-hwy
  namespace: wanyang3
  labels:
    app: demo-hwy
spec:
  type: NodePort 
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
      nodePort: 32123
  selector:
    app: demo-hwy     

说明一下:

  • 创建一个名称为 wanyang3 的 Namespace,下边其他资源部署在该命名空间下。
  • 创建一个 ConfigMap 来配置下边容器启动时需要使用到的对应环境的配置文件,一般会有多个环境配置,例如 devtestprod,这里部署需要使用到哪个环境的配置文件,就配置哪个。
  • 创建一个 Deployment 用来部署上边的 demo 镜像,开启 8080 端口并挂载 ConfigMap 指定的环境配置文件到指定位置。
  • 创建一个 Service 来代理上边的 Deployment 并使用 NodePort 方式对外暴露 32123端口来方便访问。

这里有个地方需要注意:就是 K8s 中 commandargs 和 Dockerfile 中的 ENTRYPOINTCMD 之间的关系,下边详细介绍一下二者的关系。

K8s 中 command、args 和 Dockerfile 中的 ENTRYPOINT、CMD 之间的关系

我们知道,K8s 中有 command、args 可以指定镜像启动命令和参数,而 Dockerfile 中 ENTRYPOINT、CMD 同样可以指定镜像启动命令和参数,在 K8s 中当用户同时写了 command 和 args 的时候,是可以覆盖 Dockerfile 中 ENTRYPOINT 的命令行和 CMD 参数,但对于一些其他情况,比如仅仅写了 command 或者 args 的时候,二者的覆盖关系又是怎样呢?我们参照 这里 获得完整的情况分类如下:

  • 如果 command 和 args 均没有配置,那么使用 Dockerfile 默认的配置。
  • 如果 command 配置,args 没有配置,那么 Dockerfile 默认的配置会被忽略而且仅仅执行 yaml 文件中的 command(不带任何参数)。
  • 如果 command 没有配置,args 配置了,那么 Dockerfile 默认配置的 ENTRYPOINT 的命令行会被执行,但是调用的参数是 yaml 中的 args。
  • 如果 command 和 args 都配置了,那么 Dockerfile 默认的配置被忽略,使用 yaml 的配置。

Image Entrypoint

Image Cmd

Container command

Container args

Command run

[/ep-1]

[foo bar]

[ep-1 foo bar]

[/ep-1]

[foo bar]

[/ep-2]

[ep-2]

[/ep-1]

[foo bar]

[zoo boo]

[ep-1 zoo boo]

[/ep-1]

[foo bar]

[/ep-2]

[zoo boo]

[ep-2 zoo boo]

这里我们使用将 ConfigMap 配置的对应环境配置文件挂载到容器指定位置(跟 jar 包在同一目录),然后通过 1、Spring Boot 加载配置介绍 中的第三种方式,它会在当前目录自动查找指定的配置文件,从而达到启动服务时能够加载正确的配置文件的目的。这种方式好处就是,我们构建时可以不包含不同环境的配置文件(当然打包含进去也是没问题的,会覆盖),这样 jar 包就是一个纯净的不带任何配置文件的应用,该 jar 包在任何环境均可使用,只需要启动时加载包含了对应环境的配置文件的 ConfigMap 即可,其次就是如果仅仅是配置文件需要修改,那么可以在不需要重新构建镜像的情况下,直接修改 ConfigMap 即可。坏处就是每次配置 ConfigMap 时要将对应环境的配置文件配置到 yaml 文件中,稍显复杂。

最后,在集群内部署一下该 yaml,部署成功后,通过访问 http://<k8s_cluster_ip>:32123 地址,查看下是否能够正确读取到配置吧,测试没有问题。

spring-boot-demo
spring-boot-demo

4.2、配置要加载的环境属性

yaml 文件如下:

apiVersion: v1
kind: Namespace
metadata:
  name: wanyang3
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-configmap-1
  namespace: wanyang3
data:
  DEPLOYMENT_ENV: test
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-hwy-1
  namespace: wanyang3
  labels:
    app: demo-hwy-1
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: demo-hwy-1
  template:
    metadata:
      labels:
        app: demo-hwy-1
    spec:
      containers:
      - name: demo
        image: huwanyang168/demo:0.0.1
        imagePullPolicy: IfNotPresent
        args: ["--spring.profiles.active=$(DEPLOYMENT_ENV_KEY)"]
        ports:
        - containerPort: 8080
        env:
        - name: DEPLOYMENT_ENV_KEY
          valueFrom:
            configMapKeyRef:
              name: demo-configmap-1
              key: DEPLOYMENT_ENV
---
apiVersion: v1
kind: Service
metadata:
  name: demo-hwy-1
  namespace: wanyang3
  labels:
    app: demo-hwy-1
spec:
  type: NodePort 
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
      nodePort: 32124
  selector:
    app: demo-hwy-1     

说明一下:

  • 创建一个名称为 wanyang3 的 Namespace,下边其他资源部署在该命名空间下。
  • 创建一个 ConfigMap 用来配置一下 DEPLOYMENT_ENV: test 的 Key-Value 值,主要用来为下边启动容器时指定激活那个环境的配置,一般分为 devtestprod 等环境配置。
  • 创建一个 Deployment 用来部署上边的 demo 镜像,开启 8080 端口并指定加载 ConfigMap指定的环境配置。
  • 创建一个 Service 来代理上边的 Deployment 并使用 NodePort 方式对外暴露 32124 端口来方便访问。

这里有个地方需要注意:就是 Deploymentcommond 命令中使用 ConfigMap 定义的环境变量方式。

Deployment 在 commond 命令中使用 ConfigMap 定义的环境变量

我们可以使用该方式从 ConfigMap中获取指定的 Key 值,并设置为 env 环境变量的形式,可参考 这里 查看使用示例。

env:
- name: DEPLOYMENT_ENV_KEY
  valueFrom:
    configMapKeyRef:
      name: demo-configmap-1
      key: DEPLOYMENT_ENV

然后就可以在 command 或者 args 命令时,直接通过 $(DEPLOYMENT_ENV_KEY) 方式获取 env 的值啦!

为什么要强调这点呢,因为在 4.1、直接加载环境的配置文件 中我们通过挂载 volume 的方式将 ConfigMap 中的文件或者值挂载到容器指定位置,这里我们使用 Deployment 在 commond 命令中使用 ConfigMap 定义的环境变量,通过这种方式将要激活的环境属性传递到启动参数中,这样在启动容器时,就可以动态加载指定的环境配置文件啦(这里使用 1、Spring Boot 加载配置介绍 中的第二种方式)。对比上边那种方式,好处就是部署时不需要每次将对应环境的配置文件写到 ConfigMap 中,而是简单的指定激活的环境属性即可(前提是构建时包含所有环境的配置文件),非常的方便。坏处就是如果配置文件改变,每次都得重新构建镜像,重新走一遍部署流程。

最后,在集群内部署一下该 yaml,部署成功后,通过访问 http://<k8s_cluster_ip>:32124 地址,查看下是否能够正确读取到配置吧,测试妥妥没有问题的。

spring-boot-demo
spring-boot-demo

当然,除了上边两种方式外,我们也可以直接在 Dockerfile 中指定激活的环境配置文件,这样的话,我们部署到不同环境时,需要分别构建镜像,这样 K8s 部署时就可以不需要指定 ConfigMap 了,个人认为此方式对应迭代不频繁的项目可以采用,毕竟不需要重复构建不同环境配置文件的镜像,但是对于迭代频繁的项目,建议采用 ConfigMap 方式,这样我们就可以避免重复构建不同环境的镜像啦,一个镜像搞定所有环境!

参考资料

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年05月12日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1、Spring Boot 加载配置介绍
  • 2、环境、软件准备
  • 3、Spring Boot 示例工程 Demo
  • 4、K8s ConfigMap 加载工程配置文件
    • 4.1、直接加载环境的配置文件
      • 4.2、配置要加载的环境属性
      相关产品与服务
      容器镜像服务
      容器镜像服务(Tencent Container Registry,TCR)为您提供安全独享、高性能的容器镜像托管分发服务。您可同时在全球多个地域创建独享实例,以实现容器镜像的就近拉取,降低拉取时间,节约带宽成本。TCR 提供细颗粒度的权限管理及访问控制,保障您的数据安全。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档