有关Spring Cloud Kubernates(如下简称SCK)详见https://github.com/spring-cloud/spring-cloud-kubernetes,在本文中咱们主要测试三个功能:java
首先,咱们来建立pom文件,注意几点:git
文件以下:github
<?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.0.9.RELEASE</version> <relativePath/> </parent> <groupId>me.josephzhu</groupId> <artifactId>springcloudk8sdemo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springcloudk8sdemo</name> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-kubernetes-all</artifactId> <version>1.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <finalName>k8sdemo</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>1.0.0</version> <configuration> <imageName>zhuye/${project.artifactId}</imageName> <dockerDirectory>src/main/docker</dockerDirectory> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> </configuration> </plugin> </plugins> </build> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.SR4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
接下去在src\main\docker目录下建立Dockerfile文件:web
FROM openjdk:11-jdk-slim VOLUME /tmp ADD k8sdemo.jar app.jar ENTRYPOINT exec java $JAVA_OPTS -jar /app.jar
值得注意的是,JVM参数咱们但愿从环境变量注入。spring
来看看代码,咱们首先定义一个配置类:docker
package me.josephzhu.springcloudk8sdemo; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @Configuration @ConfigurationProperties(prefix = "bean") @Data public class TestConfig { private String message; private String serviceName; }
有了SCK的帮助,配置能够从ConfigMap加载,以后咱们会看到ConfigMap的配置方式。下面咱们定义一个控制器扮演服务端的角色:apache
package me.josephzhu.springcloudk8sdemo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.List; @RestController public class TestServer { @Autowired private DiscoveryClient discoveryClient; @GetMapping("servers") public List<String> servers() { return discoveryClient.getServices(); } @GetMapping public String ip() throws UnknownHostException { return InetAddress.getLocalHost().getHostAddress(); } }
能够看到这里定义了两个接口:bootstrap
接下去定义另外一个控制器扮演客户端的角色:浏览器
package me.josephzhu.springcloudk8sdemo; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.net.InetAddress; import java.net.UnknownHostException; @RestController @Slf4j public class TestClient { @Autowired private RestTemplate restTemplate; @Autowired private TestConfig testConfig; @GetMapping("client") public String client() throws UnknownHostException { String ip = InetAddress.getLocalHost().getHostAddress(); String response = restTemplate.getForObject("http://"+testConfig.getServiceName()+"/", String.class); return String.format("%s -> %s", ip, response); } }
这里就一个接口client接口,访问后经过RestTemplate来访问服务端根路径的接口,而后输出了客户端和服务端的IP地址。mvc
而后咱们定义一个全局的异常处理器,在出错的时候咱们直接看到是什么错:
package me.josephzhu.springcloudk8sdemo; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice @Slf4j public class GlobalAdvice { @ExceptionHandler(Exception.class) public String exception(Exception ex){ log.error("error:", ex); return ex.toString(); } }
最后咱们定义启动程序:
package me.josephzhu.springcloudk8sdemo; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.web.client.RestTemplate; import java.lang.management.ManagementFactory; import java.util.stream.Collectors; @SpringBootApplication @EnableDiscoveryClient @EnableScheduling @Slf4j @RibbonClient(name = "k8sdemo") public class Springcloudk8sdemoApplication { public static void main(String[] args) { log.info("jvm:{}", ManagementFactory.getRuntimeMXBean().getInputArguments().stream().collect(Collectors.joining(" "))); SpringApplication.run(Springcloudk8sdemoApplication.class, args); } @Autowired private TestConfig testConfig; @Scheduled(fixedDelay = 5000) public void hello() { log.info("config:{}", testConfig); } @LoadBalanced @Bean RestTemplate restTemplate() { return new RestTemplate(); } }
在这个启动程序中咱们作了几件事情:
配置文件方面,首先是application.yaml:
spring: application: name: k8sdemo cloud: kubernetes: reload: enabled: true config: sources: - name: ${spring.application.name}
干了三件事情:
再定义一个bootstrap.yaml用于打开actuator的一些端点:
management: endpoint: restart: enabled: true health: enabled: true info: enabled: true
整个代码源码参见 https://github.com/JosephZhu1983/SpringCloudK8S
集群购买过程我就略去了,这些选项均可以勾上,Ingress特别记得须要,咱们以后要在公网上进行测试。
差很少30秒就有了一个K8S集群,这鬼东西要本身从头搭建一套高可用的没一天搞不下来,这里能够看到我买了一个3节点的托管版K8S,所谓托管版也就是K8S的管理节点咱们直接用阿里云本身的,只须要买工做节点,省钱省心。
买好后记得配置下kubeconfig,这样才能经过kubectl访问集群。
注意下,阿里云给出的配置别一股脑直接复制覆盖了原来的配置(好比你可能还有本地集群),也别直接粘贴到文件的最后,文件是有格式的,你须要把cluster、context和user三个配置分别复制到对应的地方。
咱们知道在K8S部署程序不像虚拟机,惟一的交付是镜像,所以咱们须要把镜像上传到阿里云。
首先,本地构建镜像:
mvn package docker:build -DskipTests
完成后查看镜像:
而后在阿里云开通镜像服务,建立本身的仓库:
根据里面的说明,给镜像打上标签后推送镜像到仓库:
docker login --username=【你的帐号】 registry.cn-shanghai.aliyuncs.com docker tag 80026bb476ce registry.cn-shanghai.aliyuncs.com/zhuyedocker/test:v6 docker push registry.cn-shanghai.aliyuncs.com/zhuyedocker/test:v6
完成后在镜像仓库查看镜像:
经过镜像建立无状态应用:
建立的时候注意下面几点:
这里我配置了JVM动态根据容器的资源限制来设置堆内存大小(此特性在部分版本的JDK8上支持,在9之后都支持),这比直接设置死Xms和Xmx好不少(设置死的话不方便进行扩容),这里我设置了50%,不建议设置更高(好比若是是2GB的内存限制,给堆设置为1.5GB显然是不合适的),毕竟Java进程所使用的内存除了堆以外还有堆外、线程栈(线程数*ThreadStackSize)、元数据区等,并且容器自己也有开销。
我这里展现的是编辑界面,建立界面略有不一样可是相似:
建立应用的时候你能够把Service和Ingress一并建立。
完成后能够进入应用详情看到2个节点状态都是运行中:
来到Ingress界面能够看到咱们的公网Ingress记录,能够直接点击访问:
根节点输出的是IP,在以前的截图中咱们能够看到服务运行在1.13和0.137两个IP上:
多刷新几回浏览器能够看到负载均衡的效果。
访问services能够查看到全部K8S的服务:
访问actuator/info能够看到有关K8S的详情(感谢SCK),显然咱们代码里获取到的IIP是PodIP:
接下去咱们来到配置项来配置ConfigMap:
这里配置项的名称须要和配置文件中的对应起来,也就是k8sdemo。而后配置项的Key须要和代码中的对应:
咱们来看看应用的日志:
2019-10-03 11:30:33.442 INFO 1 --- [pool-1-thread-1] m.j.s.Springcloudk8sdemoApplication : config:TestConfig(message=8888, serviceName=k8sdemo-svc)
的确正确获取到了配置,咱们修改下配置项bean.message为9999,随后再来看看日志:
能够看到程序发现了配置的变动,刷新了上下文,而后获取到了最新的配置。
访问client接口能够看到1.13正常从0.137获取到了数据:
多刷新几回:
咱们访问到应用的负载均衡是由Ingress实现的,应用访问服务端的负载均衡是由Ribbon实现的。
还记得吗,咱们在建立应用的时候给的内存是1.4GB,而后咱们设置了JVM使用50%的内存(初始和最大都是50%),如今咱们来看看是否是这样。
首先来看看pod的状况:
而后执行以下命令在Pod内运行jinfo
kubectl exec k8sdemo-7b44d9fbff-c4jkf -- jinfo 1
能够看到以下结果,初始和最大堆是700M左右,说明参数起做用了:
本文咱们简单展现了一下Spring Cloud Kubernetes的使用,以及如何经过阿里云的K8S集群来部署咱们的微服务,咱们看到:
有关K8S和基于Spring Boot/Spring Cloud的微服务结合使用,有几点须要注意: