SpringCloudKubernetes-02 ConfigMap的引用

SpringCloudKubernetes

Posted by Claire on September 13, 2023

此篇文章中,我们将讲述如何从configMap中引入参数配置,如何从挂载文件中引入文件配置。其中文件挂载是应用部署中常见的形式。

组件版本说明:

  • SpringBoot:3.1.0
  • SpringCloud:4.0.4
  • SpringCloudKubernetes:3.0.4
  • JDK17

1、通过 valueRef 引入 ConfigMap 配置信息

1.1: 初始化项目

引入maven依赖,核心依赖:spring-cloud-kubernetes-fabric8-config

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-kubernetes-fabric8-config</artifactId>
            <version>${springcloud-kubernetes-fabric8.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
    </dependencies>

1.2: 定义将外部引入的配置项

在 application.yaml 中定义

spring:
  application:
    name: springboot-cloud-k8s-config-valueref
greeting:
  message: ${GREETING_MESSAGE:nice to meet you}
farewell:
  message: ${FAREWELL_MESSAGE:see you next time}

在ConfigMap中定义对应的参数

apiVersion: v1
kind: ConfigMap
metadata:
  name: springboot-k8s-config-valueref
  labels:
    app: springboot-k8s-config-valueref
data:
  GREETING_MESSAGE: "Say Hello to the World outside"

我们定义两个不同的ConfigMap便于更好地演示

apiVersion: v1
kind: ConfigMap
metadata:
  name: springboot-k8s-config-valueref2
  labels:
    app: springboot-k8s-config-valueref
data:
  farewell.message: "Say Farewell to the World outside"

1.3: 构建镜像 & 发布应用

通过maven指令编译一个镜像

 <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>${docker.maven.plugin.version}</version>
                <executions>
                    <!--如果想在项目打包时构建镜像添加-->
                    <execution>
                        <id>build-image</id>
                        <phase>package</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <images>
                        <image>
                            <name>org/${project.artifactId}:${project.version}</name>
                            <build>
                                <dockerFile>${project.basedir}/Dockerfile</dockerFile>
                            </build>
                        </image>
                    </images>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-deploy-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
</plugins>

可以通过maven指令或IDE的插件

mvn clean package -Dmaven.test.skip=true

执行后,就可以将构建的镜像推送至本地/远程仓库

REPOSITORY                           TAG                IMAGE ID            CREATED             SIZE
org/springboot-k8s-config-valueref   1.0-SNAPSHOT       8902aa877999        6 seconds ago       564MB

通过插件,可以进行应用打包 -> 构建docker镜像 -> 在本地/远程仓库中覆盖更新相同版本的镜像 -> 并清除本地临时文件 接下来,我们可以通过仓库中的镜像,发布一个pod 这边使用 configMapRef & configMapKeyRef 进行引用 :

test.yaml示例文件如下:

apiVersion: v1
kind: List
items:
  - apiVersion: v1
    kind: ConfigMap
    metadata:
      name: springboot-k8s-config-valueref
      labels:
        app: springboot-k8s-config-valueref
    data:
      GREETING_MESSAGE: "Say Hello to the World outside"
  - apiVersion: v1
    kind: ConfigMap
    metadata:
      name: springboot-k8s-config-valueref2
      labels:
        app: springboot-k8s-config-valueref
    data:
      farewell.message: "Say Farewell to the World outside"
  - apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: springboot-k8s-config-valueref
      name: springboot-k8s-config-valueref
    spec:
      type: NodePort
      selector:
        app: springboot-k8s-config-valueref
      ports:
        - nodePort: 30163
          port: 8080
          protocol: TCP
          targetPort: 8080
  - apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: springboot-k8s-config-valueref
      labels:
        app: springboot-k8s-config-valueref
        group: org.example
    spec:
      strategy:
        type: Recreate
      replicas: 1
      selector:
        matchLabels:
          app: springboot-k8s-config-valueref
      template:
        metadata:
          labels:
            app: springboot-k8s-config-valueref
        spec:
          volumes:
            - name: autoconfig
          containers:
            - name: springboot-k8s-config-valueref
              image: org/springboot-k8s-config-valueref:1.0-SNAPSHOT
              imagePullPolicy: IfNotPresent
              ports:
                - containerPort: 8080
              envFrom:
                - configMapRef:
                    name: springboot-k8s-config-valueref
              env:
                - name: FAREWELL_MESSAGE
                  valueFrom:
                    configMapKeyRef:
                      name: springboot-k8s-config-valueref2
                      key: farewell.message

执行情况如下:

$ kubectl apply -f ~/springboot-demo/springboot-config-k8s-valueref/src/main/resources/deploy-valueref.yaml
configmap/springboot-k8s-config-valueref created
configmap/springboot-k8s-config-valueref2 created
service/springboot-k8s-config-valueref created
deployment.apps/springboot-k8s-config-valueref created
$ kubectl get pods
NAME                                              READY   STATUS    RESTARTS   AGE
springboot-k8s-config-valueref-57d464c66c-tg8nw   1/1     Running   0          3s
$ kubectl logs -f springboot-k8s-config-valueref-57d464c66c-tg8nw

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.0)

2023-09-18T12:17:05.206Z  INFO 1 --- [           main] org.example.BootValueRefApplication      : Starting BootValueRefApplication using Java 17.0.8 with PID 1 (/opt/app/springboot-k8s-config-valueref-1.0-SNAPSHOT.jar started by root in /opt/app)
2023-09-18T12:17:05.215Z  INFO 1 --- [           main] org.example.BootValueRefApplication      : The following 1 profile is active: "kubernetes"
2023-09-18T12:17:07.260Z  INFO 1 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=cc47999e-9518-3998-aa7d-05324c2cb413
2023-09-18T12:17:08.015Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-09-18T12:17:08.028Z  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-09-18T12:17:08.029Z  INFO 1 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.8]
2023-09-18T12:17:08.150Z  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-09-18T12:17:08.153Z  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2632 ms
2023-09-18T12:17:09.339Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-09-18T12:17:09.412Z  INFO 1 --- [           main] org.example.BootValueRefApplication      : Started BootValueRefApplication in 6.462 seconds (process running for 7.914)

经过以上步骤,一个简单的应用已经顺利地跑起来了

1.4: 确认配置的引用

这边测试是采用 minikube 在本地搭建 K8S集群。 由于本地环境,需要讲暴露服务来访问,minikube service springboot-k8s-config-valueref --url

minikube service springboot-k8s-config-valueref --url
http://127.0.0.1:51650
❗  Because you are using a Docker driver on darwin, the terminal needs to be open to run it.

请求服务API

$ curl http://127.0.0.1:51650
hello, 'Say Hello to the World outside', goodbye, 'Say Farewell to the World outside'

返回在外部ConfigMap中配置的参数,通过configMapRef 和 configMapKeyRef 均可以获取到参数

2、通过 fileAmount 引入 ConfigMap 配置信息

以下通过文件挂载的常见形式加载服务变量,通常直接挂载到jar的执行目录下的/config目录,作为springboot加载配置文件的第一优先级,无需指定自动读取

2.1: 初始化项目

引入maven依赖,核心依赖:spring-cloud-kubernetes-fabric8-config

main dependency:spring-cloud-kubernetes-fabric8-config

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-kubernetes-fabric8-config</artifactId>
            <version>${springcloud-kubernetes-fabric8.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
    </dependencies>

2.2: 定义将外部引入的配置项

define the properties in application.yaml, properties in ammounted file not defined.

server:
  port: 8080
spring:
  application:
    name: springboot-k8s-config-fileamount
dbuser: ${DB_USERNAME:default}
dbpassword: ${DB_PASSWORD:default}

定义 configmap

apiVersion: v1
kind: ConfigMap
metadata:
  name: springboot-k8s-config-fileamount
  labels:
    app: springboot-k8s-config-fileamount
data:
  application.yaml: |-
    greeting:
      message: "Say Hello to the World outside"
    farewell:
      message: "Say Goodbye to the World outside"

定义配置类:

@Data
@Configuration
public class DbConfig {
    @Value("${dbuser}")
    private String dbUsername;
    @Value("${dbpassword}")
    private String dbPassword;
}

@Data
@Configuration
public class MyConfig {

    @Value("${greeting.message:'default greeting message'}")
    private String greetingMessage;
    @Value("${farewell.message:'default farewell message'}")
    private String farewellMessage;
}

2.3: 构建 & 发布镜像

通过 maven compiler 构建镜像

 <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>${docker.maven.plugin.version}</version>
                <executions>
                    <!--如果想在项目打包时构建镜像添加-->
                    <execution>
                        <id>build-image</id>
                        <phase>package</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <images>
                        <image>
                            <name>org/${project.artifactId}:${project.version}</name>
                            <build>
                                <dockerFile>${project.basedir}/Dockerfile</dockerFile>
                            </build>
                        </image>
                    </images>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-deploy-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
</plugins>

通过指令 或 IDE 插件,将镜像打包

mvn clean package -Dmaven.test.skip=true

将镜像推到仓库

$ docker images
REPOSITORY                                TAG              IMAGE ID       CREATED          SIZE
org/springboot-config-k8s-fileamount      1.0-SNAPSHOT     f93c2e1254d5   42 seconds ago   628MB

根据镜像,发布一个Pod

使用 volumes 引入挂载的文件 使用 volumeMounts 指定挂载的文件 针对secret的数据需要在配置前加密,可以使用指定来获取base64加密后的数据: echo -n root | base64,然后再填充到文件中

$ echo -n root | base64
cm9vdA==

样例文件:test.yaml:

apiVersion: v1
kind: List
items:
  - apiVersion: v1
    kind: Secret
    metadata:
      name: springboot-k8s-config-fileamount-secret
      labels:
        app: springboot-k8s-config-fileamount
    data:
      dbuser: cm9vdA==
      dbpassword: cGFzc3dvcmQ=
  - apiVersion: v1
    kind: ConfigMap
    metadata:
      name: springboot-k8s-config-fileamount
      labels:
        app: springboot-k8s-config-fileamount
    data:
      application.yaml: |-
        greeting:
          message: "Say Hello to the World outside"
        farewell:
          message: "Say Goodbye to the World outside"
  - apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: springboot-k8s-config-fileamount
      name: springboot-k8s-config-fileamount
    spec:
      type: NodePort
      selector:
        app: springboot-k8s-config-fileamount
      ports:
        - nodePort: 30163
          port: 8080
          protocol: TCP
          targetPort: 8080
  - apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: springboot-k8s-config-fileamount
      labels:
        app: springboot-k8s-config-fileamount
        group: org.example
    spec:
      strategy:
        type: Recreate
      replicas: 1
      selector:
        matchLabels:
          app: springboot-k8s-config-fileamount
      template:
        metadata:
          labels:
            app: springboot-k8s-config-fileamount
        spec:
          volumes:
            - name: config-volume
              configMap:
                name: springboot-k8s-config-fileamount
                items:
                  - key: application.yaml
                    path: application.yaml
          containers:
            - name: springboot-k8s-config-fileamount
              image: org/springboot-config-k8s-fileamount:1.0-SNAPSHOT
              imagePullPolicy: IfNotPresent
              ports:
                - containerPort: 8080
              env:
                - name: DB_USERNAME
                  valueFrom:
                    secretKeyRef:
                      name: springboot-k8s-config-fileamount-secret
                      key: dbuser
                - name: DB_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: springboot-k8s-config-fileamount-secret
                      key: dbpassword
              volumeMounts:
                - name: config-volume
                  mountPath: /opt/app/config/application.yaml
                  subPath: application.yaml

使用 kubectl apply -f test.yaml 的方式发布Pod

$ kubectl apply -f ~/springboot-demo/springboot-config-k8s-fileamount/src/main/resources/deploy-fileamount.yaml
secret/springboot-k8s-config-fileamount-secret created
configmap/springboot-k8s-config-fileamount created
service/springboot-k8s-config-fileamount created
deployment.apps/springboot-k8s-config-fileamount created
$ kubectl get pods
NAME                                               READY   STATUS    RESTARTS   AGE
springboot-k8s-config-fileamount-89d6bdfcd-vvkqz   1/1     Running   0          3s
$ kubectl logs springboot-k8s-config-fileamount-89d6bdfcd-vvkqz

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.0)

2023-09-20T10:38:40.477Z  INFO 1 --- [           main] org.example.BootFileamountApplication    : Starting BootFileamountApplication using Java 17.0.8 with PID 1 (/opt/app/springboot-config-k8s-fileamount-1.0-SNAPSHOT.jar started by root in /opt/app)
2023-09-20T10:38:40.480Z  INFO 1 --- [           main] org.example.BootFileamountApplication    : The following 1 profile is active: "kubernetes"
2023-09-20T10:38:42.402Z  INFO 1 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=49502b73-ffac-39f1-a249-dec4d3facce7
2023-09-20T10:38:43.379Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-09-20T10:38:43.402Z  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-09-20T10:38:43.403Z  INFO 1 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.8]
2023-09-20T10:38:43.517Z  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-09-20T10:38:43.520Z  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2845 ms
2023-09-20T10:38:44.714Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-09-20T10:38:44.792Z  INFO 1 --- [           main] org.example.BootFileamountApplication    : Started BootFileamountApplication in 6.37 seconds (process running for 8.022)

应用启动,准备测试

2.4: 确认配置的引用

暴露端口 minikube service springboot-k8s-config-valueref --url

minikube service springboot-k8s-config-fileamount --url
http://127.0.0.1:55369
❗  Because you are using a Docker driver on darwin, the terminal needs to be open to run it.

请求接口,获取注入的参数值

$ curl http://127.0.0.1:55369
hello, 'Say Hello to the World outside', goodbye, 'Say Goodbye to the World outside'. myname: 'root', mypass: 'password' 

参数值获取成功注入的参数