有關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的微服務結合使用,有幾點須要注意: