spring-cloud-kubernetes实践

服务发现

1、利用idea创建一个springboot(version:2.4.3)项目;
2、在pom.xml文件中加入

    <properties>
        <spring-cloud.version>2020.0.1</spring-cloud.version>
        <spring.cloud.k8s.version>2.0.1</spring.cloud.k8s.version>
        ...
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-kubernetes-dependencies</artifactId>
                <version>${spring.cloud.k8s.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-client</artifactId>
        </dependency>
        ...
    </dependencies>

    <!-- 设置跳过测试 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
            ...
        </plugins>
    </build>

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.<init>(SSLExtensions.java:90) ~[na:na]
        at java.base/sun.security.ssl.CertificateRequest$T13CertificateRequestMessage.<init>(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添加配置如下

<plugin>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-maven-plugin</artifactId>
     <configuration>
         <layers>
             <enabled>true</enabled>
         </layers>
     </configuration>
 </plugin>

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"]

获取k8s的configMap内容

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;
    ...
}

获取k8s的secrets内容

官方文档写的有点不清不楚的,最后参考官方文档提供的例子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

开启configMap和secret的热更新

bootstrap.yaml添加配置,该配置方式很方便,但是新的版本已经废弃

spring:
  cloud:
    kubernetes:
      reload:
        # 自动更新配置的开关设置为打开
        enabled: true
        # 更新配置信息的模式:polling是主动拉取,event是事件通知
        mode: polling
        # 主动拉取的间隔时间是500毫秒
        period: 5000

开启热更新以后,发现了一个问题,secret修改的时候,并没有热更新,只有configMap修改的时候,才会进行热更新,并且把secret的变更也热更新了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值