1、利用idea创建一个springboot(version:2.4.3)项目;
2、在pom.xml文件中加入
2020.0.1
2.0.1
...
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.cloud
spring-cloud-kubernetes-dependencies
${spring.cloud.k8s.version}
pom
import
org.springframework.cloud
spring-cloud-starter-kubernetes-client
...
org.apache.maven.plugins
maven-surefire-plugin
2.22.2
true
...
3、主程序中添加注解
@SpringBootApplication
@EnableDiscoveryClient
//官方文档中说:
//Spring Cloud Kubernetes can also watch the Kubernetes service catalog for changes and update the DiscoveryClient implementation accordingly.
//In order to enable this functionality you need to add @EnableScheduling on a configuration class in your application.
@EnableScheduling
4、创建ServiceController,测试通过DiscoveryClient获取所有的服务。
5、打包成jar
6、编写Dockerfile。
FROM openjdk:14-jdk-alpine
ENV PORT 8080
EXPOSE 8080
COPY target/*.jar /opt/app.jar
WORKDIR /opt
# JDK 11 onwards have support for TLS 1.3 which can cause the error extension (5) should not be presented in certificate_request.
# Add -Djdk.tls.client.protocols=TLSv1.2 to the JVM args to make it use 1.2 instead!!!
CMD ["java", "-jar", "-Djdk.tls.client.protocols=TLSv1.2", "app.jar"]
刚开始,Dockerfile的FROM写的是 FROM openjdk:8-jdk-alpine
,即使用的是java8镜像,但是我项目编译使用的是java11,导致在k8s(使用ubuntu的microk8s)上运行的时候报错,通过kubectl logs命令发现是由于代码不能在高版本编译,在低版本运行导致的。
改成 FROM openjdk:14-jdk-alpine
后pod启动成功,但是kubectl logs查看日志,发现下面报错
2021-02-27 15:23:10.810 ERROR 1 --- [s.V1Endpoints-0] i.k.c.informer.cache.ReflectorRunnable : class io.kubernetes.client.openapi.models.V1Endpoints#Reflector loop failed unexpectedly
java.lang.IllegalStateException: javax.net.ssl.SSLHandshakeException: extension (5) should not be presented in certificate_request
at io.kubernetes.client.util.generic.GenericKubernetesApi.executeCall(GenericKubernetesApi.java:733) ~[client-java-10.0.0.jar!/:na]
at io.kubernetes.client.util.generic.GenericKubernetesApi.list(GenericKubernetesApi.java:335) ~[client-java-10.0.0.jar!/:na]
at io.kubernetes.client.informer.SharedInformerFactory$2.list(SharedInformerFactory.java:209) ~[client-java-10.0.0.jar!/:na]
at io.kubernetes.client.informer.cache.ReflectorRunnable.run(ReflectorRunnable.java:74) ~[client-java-10.0.0.jar!/:na]
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) ~[na:na]
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]
Caused by: javax.net.ssl.SSLHandshakeException: extension (5) should not be presented in certificate_request
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131) ~[na:na]
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117) ~[na:na]
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:311) ~[na:na]
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:267) ~[na:na]
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:258) ~[na:na]
at java.base/sun.security.ssl.SSLExtensions.(SSLExtensions.java:90) ~[na:na]
at java.base/sun.security.ssl.CertificateRequest$T13CertificateRequestMessage.(CertificateRequest.java:819) ~[na:na]
at java.base/sun.security.ssl.CertificateRequest$T13CertificateRequestConsumer.consume(CertificateRequest.java:923) ~[na:na]
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396) ~[na:na]
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444) ~[na:na]
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:422) ~[na:na]
at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:181) ~[na:na]
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:167) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1462) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1370) ~[na:na]
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:437) ~[na:na]
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:336) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:300) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:185) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.java:224) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.java:108) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.java:88) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.connection.Transmitter.newExchange(Transmitter.java:169) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:41) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:88) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) ~[okhttp-3.14.9.jar!/:na]
at io.kubernetes.client.util.credentials.TokenFileAuthentication.intercept(TokenFileAuthentication.java:72) ~[client-java-10.0.0.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:229) ~[okhttp-3.14.9.jar!/:na]
at okhttp3.RealCall.execute(RealCall.java:81) ~[okhttp-3.14.9.jar!/:na]
at io.kubernetes.client.openapi.ApiClient.execute(ApiClient.java:882) ~[client-java-api-10.0.0.jar!/:na]
at io.kubernetes.client.util.generic.GenericKubernetesApi.executeCall(GenericKubernetesApi.java:729) ~[client-java-10.0.0.jar!/:na]
... 9 common frames omitted
在这个网页找到解决方法:
https://stackoverflow.com/questions/60790118/java-kubernetes-client-sslhandshakeexception-extension-5-should-not-be-present
Which version of Java are you using?
JDK 11 onwards have support for TLS 1.3 which can cause the error extension (5) should not be presented in certificate_request.
Add -Djdk.tls.client.protocols=TLSv1.2 to the JVM args to make it use 1.2 instead.
k8s 的 rc配置文件如下
apiVersion: v1
kind: ReplicationController
metadata:
name: spring-cloud-k8s-test
spec:
replicas: 1
selector:
app: spring-cloud-k8s-test
template:
metadata:
labels:
app: spring-cloud-k8s-test
spec:
containers:
- name: spring-cloud-k8s-test
image: docker.io/library/spring-cloud-k8s-test:v1.0
ports:
- name: general-port
containerPort: 8080
hostPort: 6080
springboot 从2.3.0开始,采用了新的分层打包镜像的方式。只需要下面两步即可
1、spring-boot-maven-plugin添加配置如下
org.springframework.boot
spring-boot-maven-plugin
true
2、编写Dockerfile如下
FROM openjdk:14-jdk-alpine as builder
WORKDIR application
COPY target/*.jar application.jar
RUN java -Djarmode=layertools -jar application.jar extract
RUN ls -al
FROM openjdk:14-jdk-alpine
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
# JDK 11 onwards have support for TLS 1.3 which can cause the error extension (5) should not be presented in certificate_request.
# Add -Djdk.tls.client.protocols=TLSv1.2 to the JVM args to make it use 1.2 instead!!!
ENTRYPOINT ["java", "-Djdk.tls.client.protocols=TLSv1.2", "org.springframework.boot.loader.JarLauncher"]
1、将application.yaml改成bootstrap.yaml,加入下面配置
spring:
application:
name: spring-cloud-kubernetes-test
cloud:
kubernetes:
config:
name: cm-kubernetes-test #the value must same with configMap.
2、创建名称为cm-kubernetes-test的configMap
kind: ConfigMap
apiVersion: v1
metadata:
name: cm-kubernetes-test
data:
application.yaml: |-
greeting:
message: Say Hello to the World
3、添加configMap的配置类Greeting
@Component
@ConfigurationProperties(prefix = "greeting")
public class Greeting {
private String message;
...
}
官方文档写的有点不清不楚的,最后参考官方文档提供的例子spring-boot-camel-config,终于可以使用
1、首先,secret配置是这样的,在k8s中创建该secret
apiVersion: v1
kind: Secret
metadata:
name: secret-kubernetes-test
type: Opaque
data:
user.username: bXl1c2VyCg==
user.password: bXl1c2VyCg==
user是前缀。
2、在bootstrap.yaml中添加
spring:
cloud:
kubernetes:
reload:
secrets:
paths: /etc/secrets/secret-kubernetes-test
3、其次创建对应的java配置类
@Component
@ConfigurationProperties(prefix = "user")
public class UserInfo {
private String username;
private String password;
...
}
4、k8s的rc配置文件添加secret挂载配置
apiVersion: v1
kind: ReplicationController
metadata:
name: spring-cloud-k8s-test
spec:
replicas: 1
selector:
app: spring-cloud-k8s-test
template:
metadata:
labels:
app: spring-cloud-k8s-test
spec:
containers:
- name: spring-cloud-k8s-test
image: docker.io/library/spring-cloud-k8s-test:v1.0
ports:
- name: general-port
containerPort: 8080
hostPort: 6080
volumeMounts:
- name: secret-kubernetes-test
mountPath: "/etc/secrets/secret-kubernetes-test"
readOnly: true
volumes:
- name: secret-kubernetes-test
secret:
secretName: secret-kubernetes-test
bootstrap.yaml添加配置,该配置方式很方便,但是新的版本已经废弃
spring:
cloud:
kubernetes:
reload:
# 自动更新配置的开关设置为打开
enabled: true
# 更新配置信息的模式:polling是主动拉取,event是事件通知
mode: polling
# 主动拉取的间隔时间是500毫秒
period: 5000
开启热更新以后,发现了一个问题,secret修改的时候,并没有热更新,只有configMap修改的时候,才会进行热更新,并且把secret的变更也热更新了。