在项目开发中, 大都会为不同的运行环境编写不同的配置文件, 比如生产环境使用application-prod.yml, 开发环境使用application-dev.yml.
在spring boot 应用部署到k8s之前, 配置文件的挂载一般都会在打jar包时包含在内, 或者把配置文件的内容保存在nacos之类的中间件中在启动应用时读取配置.
在应用k8s之后, ?配置文件可以在通过ConfigMap资源剥离出来单独部署, ?本次记录spring boot框架的java应用结合k8s的ConfigMap的实现外部配置文件挂载及配置热更新的一次实践。
一、配置自动更新,需要引入 kubernetes-config、健康检测依赖
1、项目依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo-v6</artifactId>
<version>0.0.1</version>
<name>demo-v6</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-config</artifactId>
<version>1.1.8.RELEASE</version>
</dependency>
<!-- 用于健康监测自动刷新 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、使用到配置项的代码
@RefreshScope
@RestController
public class HelloController {
@Value("${name:null}")
private String name;
@Value("${age:0}")
private int age;
@GetMapping("/hello")
public Object hello () {
return name + ": " + age;
}
}
3、Dockerfile
FROM openjdk:8-jdk-alpine
RUN mkdir -p /app/
COPY ./demo-v6-0.0.1.jar /app/
RUN touch /app/demo-v6-0.0.1.jar
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
4、添加账号角色绑定demo-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: config-reader
namespace: demo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: demo
rules:
- apiGroups:
- ""
resources:
- endpoints
- pods
- configmaps
verbs:
- list
- get
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-reader
namespace: demo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: pod-reader
subjects:
- kind: ServiceAccount
name: config-reader
namespace: demo
5、configmap
apiVersion: v1
kind: ConfigMap
metadata:
name: demo-configmap
namespace: demo
labels:
spring.cloud.kubernetes.config: "true"
data:
application.yml: |-
server:
port: 8989
spring:
application:
name: demo-configmap
cloud:
kubernetes:
config:
sources:
- name: demo-configmap
namespace: demo
reload:
#自动更新配置的开关设置为打开
enabled: true
monitoring-config-maps: true
monitoring-secrets: true
mode: polling
strategy: refresh
management:
endpoints:
web:
exposure:
include: refresh,health
endpoint:
restart:
enabled: true
health:
enabled: true
info:
enabled: true
age: 18
name: Tom
6、后端yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
namespace: demo
labels:
app: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
serviceAccountName: config-reader
imagePullSecrets:
- name: harbor-secret
containers:
- name: demo
image: 192.168.1.6:8037/demo:0.0.1
imagePullPolicy: Always
command: ["java", "-jar", "/app/demo-v6-0.0.1.jar"]
args: ["--spring.config.location=/app/config/application.yml", "--spring.profiles.active=dev"]
ports:
- containerPort: 8001
volumeMounts:
- name: host-time
mountPath: /etc/localtime
- name: demo-config
mountPath: /app/config
volumes:
- name: host-time
hostPath:
path: /etc/localtime
- name: demo-config
configMap:
name: demo-configmap
---
apiVersion: v1
kind: Service
metadata:
name: demo
namespace: demo
labels:
app: demo
spec:
type: ClusterIP
ports:
- port: 8001
targetPort: 8001
selector:
app: demo
效果:
configmap修改前:
?configmap修改后:
?二、手动触发滚动更新,无须引入任何依赖
1、configmap
apiVersion: v1
kind: ConfigMap
metadata:
name: demo-configmap
namespace: demo
labels:
spring.cloud.kubernetes.config: "true"
data:
application.yml: |-
server:
port: 8989
spring:
application:
name: demo-configmap
age: 18
name: Tom
?2、后端yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
namespace: demo
labels:
app: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
imagePullSecrets:
- name: harbor-secret
containers:
- name: demo
image: 192.168.1.6:8037/demo:0.0.1
imagePullPolicy: Always
command: ["java", "-jar", "/app/demo-v6-0.0.1.jar"]
args: ["--spring.config.location=/app/config/application.yml", "--spring.profiles.active=dev"]
ports:
- containerPort: 8001
volumeMounts:
- name: host-time
mountPath: /etc/localtime
- name: demo-config
mountPath: /app/config
volumes:
- name: host-time
hostPath:
path: /etc/localtime
- name: demo-config
configMap:
name: demo-configmap
---
apiVersion: v1
kind: Service
metadata:
name: demo
namespace: demo
labels:
app: demo
spec:
type: ClusterIP
ports:
- port: 8001
targetPort: 8001
selector:
app: demo
3、利用patch更新annotation的值,来触发pod滚动更新
kubectl patch deployment -n demo --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "20220614" }}}}}'
三、Secret保存账号密码信息
思路:jasypt加密 -> ENC(加密后的字符串) -> base64编码 -> Secret
1、jasypt加密
2、base64编码
?
?3、secret
?4、后端引用yaml
?5、修改configmap值后,发现程序中的值不会自动更新,采用手动方式触发刷新
curl ‘ip:port/actuator/refresh’
?
?
?
|