Spring boot应用发布到k8s中并挂载外部配置文件

# Spring boot应用发布到k8s中并挂载外部配置文件

在项目开发中, 大都会为不同的运行环境编写不同的配置文件, 比如生产环境使用application-prod.yml, 开发环境使用application-dev.yml.

在spring boot 应用部署到k8s之前, 配置文件的挂载一般都会在打jar包时包含在内, 或着把配置文件的内容保存在nacos之类的中间件中在启动应用时读取配置.

在应用k8s之后, 配置文件可以在通过ConfigMap资源剥离出来单独部署, 本次记录spring boot框架的java应用结合k8s的ConfigMap的实现外部配置文件挂载的一次实践.

  • application.yml位置与读取优先级

spring boot会默认读取jar包同级目录下的application.yml文件, 还会读取jar包同级目录下的config/application.yml文件.

|-- application.yml
|-- config
|   `-- application.yml
`-- springboot-docker-demo-0.0.3-SNAPSHOT.jar
1
2
3
4

在如上的目录层级结构中, spring boot 读取配置文件的优先级是config/application.yml > application.yml > jar包内application.yml.

  • demo测试
@RestController
@SpringBootApplication
public class SpringbootDockerDemoApplication {

    public static void main(String[] args) {

        String path = System.getProperty("user.dir");
        System.out.println("---------- workdir: " + path + " ----------");
        boolean externalProfile = new File(path + File.separator + "application.yml").exists();
        boolean configDirProfile = new File(path + File.separator + "config" + File.separator + "application.yml").exists();
        if (configDirProfile){
            System.out.println("---- 将使用config/下的application.yml");
        }else if (externalProfile){
            System.out.println("---- 将使用jar的同级目录中的application.yml");
        }else {
            System.out.println("---- 将使用jar中的application.yml");
        }
        SpringApplication.run(SpringbootDockerDemoApplication.class, args);
    }

    @Value("${yourName}")
    private String yourName;

    @Value("${myName}")
    private String myName;

    @GetMapping
    public String sayHello(){

        return myName.concat("  hello hello ").concat(yourName);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

此时src/main/resources/application.yml中文件内容

yourName: 嘿嘿
myName: ytg2097
1
2

执行mvn package之后, 在target目录中清理掉除jar包之外的文件, 然后在target目录中加入application.yml和config/application.yml

E:/ytg2097/springboot-docker-demo/target
|-- application.yml
|-- config
|   `-- application.yml
`-- springboot-docker-demo-0.0.3-SNAPSHOT.jar
1
2
3
4
5

application.yml内容

yourName: 哈哈
myName: ytg2097
1
2

config/application.yml内容

yourName: 吼吼
myName: ytg2097
1
2

启动项目后访问localhost:8080返回: ytg2097 hello hello 吼吼. 删除config/application.yml后再次访问返回: ytg2097 hello hello 哈哈. 再删除application.yml后返回: ytg2097 hello hello 嘿嘿.

  • 发布到k8s

发布到已经知道, spring boot可以读取jar之外的主机路径下的文件, 现在我们结合k8s的volumeMounts, ConfigMap实现配置文件挂载到pod的容器中.

  1. 打镜像

    打镜像比较简单, 我使用的dockerfile-maven-plugin插件打包的, demo代码现在已经上传到github (opens new window)

  2. 编写k8s.yml

    kind: ConfigMap
    apiVersion: v1
    metadata:
      name: spring-boot-demo-config
    data:
      application.yml: |-
        yourName: 哈哈
        myName: 阳光大男孩
    ---
    kind: Deployment
    apiVersion: apps/v1
    metadata:
      name: spring-boot-demo
      labels:
        ytg2097.com/java: spring-boot-demo
    spec:
      replicas: 1
      selector:
        matchLabels:
          ytg2097.com/java: spring-boot-demo
      template:
        metadata:
          name: spring-boot-demo
          labels:
            ytg2097.com/java: spring-boot-demo
        spec:
          volumes:
            - name: configmap-demo
              configMap:
                name: spring-boot-demo-config
                items:
                  - key: application.yml
                    path: application.yml
          containers:
            - name: spring-boot-demo
              image: 'ytg2097/springboot-docker-demo:0.0.3-SNAPSHOT'
              resources:
                limits:
                  cpu: 128m
                  memory: '256Mi'
                requests:
                  cpu: 64m
                  memory: '256Mi'
              volumeMounts:
                - name: configmap-demo
                  mountPath: /config
    ---
    kind: Service
    apiVersion: v1
    metadata:
      name: spring-boot-demo-nodeport
      labels:
        ytg2097.com/java: spring-boot-demo
    spec:
      ports:
        - name: spring-boot-demo-nodeport-8080
          protocol: TCP
          port: 8080
          targetPort: 8080
          nodePort: 35015
      selector:
        ytg2097.com/java: spring-boot-demo
      type: NodePort
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63

    volumeMounts中引用了volume对象configmap-demo. configmap-demo中有引用了ConfigMap对象作为volume挂载, 并且在items中声明了使用key为application.yml的value作为文件挂载, 并且文件路径为application.yml, 结合volumeMounts中的mountPath: /config, 最后ConfigMap对象spring-boot-demo-config中的key为application.yml的value被作为文件挂载到了容器的/config/application.yml.

    小知识点: 如果volumeMounts使用subPath来挂载, k8s是不会做热更新的

将yml文件apply到k8s中后测试

$ curl 192.168.58.136:35015
阳光大男孩  hello hello  哈哈
1
2

后续再研究一下有什么办法可以做到spring cloud config那样的热更新配置文件

上次更新: 2022/1/6 15:29:54