1、所需环境:
- windows 安装 Ubuntu-18.04
- docker
- minikube
2、安装docker
-
安装yum-utils包(提供yum-config-manager 实用程序)并设置稳定的存储库。
sudo yum install -y yum-utils sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
-
安装 Docker 引擎
sudo yum install docker-ce docker-ce-cli containerd.io
-
启动 Docker
sudo /etc/init.d/docker start # wsl2 启动
-
测试安装
docker --version
-
通过运行hello-world 映像来验证 Docker 引擎是否已正确安装。
sudo docker run hello-world
以上docker安装完成
3、安装minikube
-
更新包
sudo apt-get update sudo apt-get install apt-transport-https sudo apt-get upgrade
-
安装Virtual Box(用来跑minikube)
sudo apt install virtualbox virtualbox-ext-pack
-
下载minikube
wget https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 chmod +x minikube-linux-amd64 sudo mv minikube-linux-amd64 /usr/local/bin/minikube
-
验证minikube的版本
minikube version
root@localhost:~$ minikube version minikube version: v1.25.2 commit: 362d5fdc0a3dbee389b3d3f1034e8023e72bd3a7 root@localhost:~$
-
在Ubuntu上安装 kubectl
- 使用命令行下载安装包
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
- 安装kubectl
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
- 测试安装 kubectl version --client 或使用 kubectl version -o json 验证安装是否正确
kubectl version --client
root@localhost:~$ kubectl version --client WARNING: This version information is deprecated and will be replaced with the output from kubectl version --short. Use --output=yaml|json to get the full version. Client Version: version.Info{Major:"1", Minor:"24", GitVersion:"v1.24.0", GitCommit:"4ce5a8954017644c5420bae81d72b09b735c21f0", GitTreeState:"clean", BuildDate:"2022-05-03T13:46:05Z", GoVersion:"go1.18.1", Compiler:"gc", Platform:"linux/amd64"} Kustomize Version: v4.5.4 root@localhost:~$
root@localhost:~$ kubectl version -o json { "clientVersion": { "major": "1", "minor": "24", "gitVersion": "v1.24.0", "gitCommit": "4ce5a8954017644c5420bae81d72b09b735c21f0", "gitTreeState": "clean", "buildDate": "2022-05-03T13:46:05Z", "goVersion": "go1.18.1", "compiler": "gc", "platform": "linux/amd64" }, "kustomizeVersion": "v4.5.4", "serverVersion": { "major": "1", "minor": "23", "gitVersion": "v1.23.3", "gitCommit": "816c97ab8cff8a1c72eccca1026f7820e93e0d25", "gitTreeState": "clean", "buildDate": "2022-01-25T21:19:12Z", "goVersion": "go1.17.6", "compiler": "gc", "platform": "linux/amd64" } } root@localhost:~$
- 使用命令行下载安装包
以上minikube安装完成
4、新建服务发现工程
-
环境信息:
1、minikube
2、java 1.8
3、maven 3.6
4、spring-cloud-kubernetes:1.0.1RELEASE -
开发应用
基于maven创建一个springboot应用,名为spring-cloud-k8s-discovery-demo;
-
pom文件中依赖了spring-cloud-kubernetes的以下两个库,后面才能使用spring-cloud-kubernetes的服务;
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-kubernetes-core</artifactId> <version>1.0.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-kubernetes-discovery</artifactId> <version>1.0.1.RELEASE</version> </dependency>
-
创建DiscoveryController.java类:
package com.example.springcloudk8sdiscoverydemo.controller; import com.alibaba.fastjson.JSON; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.text.SimpleDateFormat; import java.util.Date; @RestController public class DiscoveryController { @Autowired private DiscoveryClient discoveryClient; /** * 探针检查响应类 * * @return */ @RequestMapping("/health") public String health() { return "health"; } /** * 返回远程调用的结果 * * @return */ @RequestMapping("/getservicedetail") public String getservicedetail( @RequestParam(value = "servicename", defaultValue = "") String servicename) { return "Service [" + servicename + "]'s instance list : " + JSON.toJSONString(discoveryClient.getInstances(servicename)); } /** * 返回发现的所有服务 * * @return */ @RequestMapping("/services") public String services() { return this.discoveryClient.getServices().toString() + ", " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); } }
-
启动类:
package com.example.springcloudk8sdiscoverydemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class SpringCloudK8sDiscoveryDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudK8sDiscoveryDemoApplication.class, args); } }
-
编写 application.properties
spring.application.name=spring-cloud-k8s-discovery-demo server.port=8088
-
总结:
-
DiscoveryClient是个接口,对应的实现类是哪个?
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-kubernetes-discovery</artifactId> <version>1.0.1.RELEASE</version> </dependency>
spring容器启动时,会寻找classpath下所有spring.factories文件(包括jar文件中的),spring.factories中配置的所有类都会实例化,我们在开发springboot时常用到的XXX-starter.jar就用到了这个技术,效果是一旦依赖了某个starter.jar很多功能就在spring初始化时候自动执行了 -
discoveryClient.getServices()方法取得了kubernetes的service信息,这背后的机制是什么?java应用是怎样取得所在kubernetes的服务信息的?
@RequestMapping("/services") public String services() { return this.discoveryClient.getServices().toString() + ", " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); }
discoveryClient对应的类是spring-cloud-kubernetes项目的KubernetesDiscoveryClient.java
public List<String> getServices() { String spelExpression = this.properties.getFilter(); Predicate filteredServices; if (spelExpression != null && !spelExpression.isEmpty()) { Expression filterExpr = this.parser.parseExpression(spelExpression); filteredServices = (instance) -> { Boolean include = (Boolean)filterExpr.getValue(this.evalCtxt, instance, Boolean.class); return include == null ? false : include; }; } else { filteredServices = (instance) -> { return true; }; } return this.getServices(filteredServices); } public List<String> getServices(Predicate<Service> filter) { return (List)((ServiceList)((FilterWatchListDeletable)this.kubernetesClientServicesFunction.apply(this.client)).list()).getItems().stream().filter(filter).map((s) -> { return s.getMetadata().getName(); }).collect(Collectors.toList()); }
这段代码的关键在于this.kubernetesClientServicesFunction.apply(this.client).list(),先看KubernetesClientServicesFunction实例的初始化过程,在KubernetesDiscoveryClientAutoConfiguration类中:
@Bean public KubernetesClientServicesFunction servicesFunction( KubernetesDiscoveryProperties properties) { if (properties.getServiceLabels().isEmpty()) { return KubernetesClient::services; } return (client) -> client.services().withLabels(properties.getServiceLabels()); }
KubernetesClientServicesFunction是个lambda表达式,用于KubernetesClient的时候,返回KubernetesClient.services()的结果,如果指定了标签过滤,就用指定的标签来做过滤(也就是kubernetes中的标签选择器的效果)
因此,数据来源其实就是上面的this.client,调用其services方法的返回结果;
最终是调用okhttp的newCall方法向kubernetes的API Server发起http请求,获取service资源的数据列表;
-
5、部署工程
- 官方demo使用插件fabric8-maven-plugin来构建镜像并部署到minikube环境,我这边可能是环境问题没有部署成功,我是将jar包打成镜后部署到minikube;
-
编写dockerfile并执行
FROM openjdk:8u242-jre-slim WORKDIR /app/spring-cloud-k8s-discovery-demo COPY spring-cloud-k8s-discovery-demo-0.0.1-SNAPSHOT.jar /app/spring-cloud-k8s-discovery-demo RUN echo 'Asia/Shanghai' >/etc/timezone EXPOSE 8080 CMD ["java", "-jar", "spring-cloud-k8s-discovery-demo-0.0.1-SNAPSHOT.jar"]
docker build -f spring-cloud-k8s.Dockerfile .
-
镜像打包完成并推送至docker hub
docker push zwzhang2022/spring-cloud-k8s:1.0
-
编写kubenetes.yaml
apiVersion: v1 kind: Service metadata: labels: app: spring-cloud-k8s-discovery-demo name: spring-cloud-k8s-discovery-demo namespace: spring-cloud-k8s-demo spec: type: NodePort ports: - port: 8088 #服务端口 protocol: TCP targetPort: 8088 selector: app: spring-cloud-k8s-discovery-demo --- apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-k8s-discovery-demo-deployment namespace: spring-cloud-k8s-demo spec: selector: matchLabels: app: spring-cloud-k8s-discovery-demo replicas: 1 template: metadata: labels: app: spring-cloud-k8s-discovery-demo spec: containers: - name: spring-cloud-k8s-discovery-demo image: zwzhang2022/spring-cloud-k8s-discovery-demo:2.0 imagePullPolicy: Always ports: - containerPort: 8088
-
创建命名空间并查看
kubectl create namespace spring-cloud-k8s-demo kubectl get namespace
-
创建并查看
kubectl apply -f kubenetes.yaml kubectl -n kubenetes-demo get pods kubectl -n spring-cloud-k8s-demo describe pod <PodName> #查看pod详细信息
-
进入容器
kubectl -n spring-cloud-k8s-demo exec -it spring-cloud-k8s-discovery-demo-deployment-8499cbdfc-ddsvs -c spring-cloud-k8s-discovery-demo -- sh
-
进入容器后 curl 调用接口
/app # curl http://localhost:8080/health health /app # curl http://localhost:8080/services [spring-cloud-k8s-configmap-demo, spring-cloud-k8s-discovery-demo], 2022-05-10 18:28:54 /app # curl http://localhost:8080/getservicedetail?servicename=spring-cloud-k8s-discovery-demo Service [spring-cloud-k8s-discovery-demo]'s instance list : [{"host":"172.17.0.3","instanceId":"d63e413d-a4a3-4bf7-a778-734d7edb3daa","metadata":{"app":"spring-cloud-k8s-discovery-demo","kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"spring-cloud-k8s-discovery-demo\"},\"name\":\"spring-cloud-k8s-discovery-demo\",\"namespace\":\"spring-cloud-k8s-demo\"},\"spec\":{\"ports\":[{\"port\":8088,\"protocol\":\"TCP\",\"targetPort\":8088}],\"selector\":{\"app\":\"spring-cloud-k8s-discovery-demo\"},\"type\":\"NodePort\"}}\n"},"port":8088,"scheme":"http://","secure":false,"serviceId":"spring-cloud-k8s-discovery-demo","uri":"http://172.17.0.3:8088"}]
-
以上服务发现部署完成
6、k8s集成feign
-
引入依赖库
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>spring-cloud-k8s-feign-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-cloud-k8s-feign-demo</name> <description>spring-cloud-k8s-feign-demo</description> <properties> <java.version>1.8</java.version> <springcloud.kubernetes.version>1.0.1.RELEASE</springcloud.kubernetes.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-kubernetes-core</artifactId> <version>${springcloud.kubernetes.version}</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-kubernetes-discovery</artifactId> <version>${springcloud.kubernetes.version}</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId> <version>${springcloud.kubernetes.version}</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-commons</artifactId> </dependency> <dependency> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <groupId>org.springframework.cloud</groupId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> <version>2.1.0.RELEASE</version> </dependency> <dependency> <artifactId>spring-cloud-starter-openfeign</artifactId> <groupId>org.springframework.cloud</groupId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.SR3</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
-
创建application.yml配置文件
spring: application: name: spring-cloud-k8s-feign-demo product-infra-service: ribbon: KubernetesNamespace: spring-cloud-k8s-demo backend: ribbon: eureka: enabled: false client: enabled: true ServerListRefreshInterval: 5000 hystrix: command: BackendCall: execution: isolation: thread: timeoutInMilliseconds: 5000 threadpool: BackendCallThread: coreSize: 5 feign: hystrix: enabled: true server: port: 8082
-
启动类代码
package com.example.springcloudk8sfeigndemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients(basePackages = "com.example.springcloudk8sfeigndemo") public class SpringCloudK8sFeignDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudK8sFeignDemoApplication.class, args); } }
-
feign接口代码
package com.example.springcloudk8sfeigndemo; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; @FeignClient(name = "spring-cloud-k8s-discovery-demo", fallback = TestClientFallback.class) public interface TestClient { @RequestMapping("/services") String services(); }
-
fallback类代码
package com.example.springcloudk8sfeigndemo; import org.springframework.stereotype.Component; @Component public class TestClientFallback implements TestClient { @Override public String services() { return "hystrix call back"; } }
-
controller接口
package com.example.springcloudk8sfeigndemo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @Autowired private TestClient testClient; @GetMapping("test1") public String test1() { return testClient.services(); } }
-
部署到k8s验证 (kubenetes.yaml可以参考服务发现)
/app # curl http://localhost:8082/test1 [spring-cloud-k8s-configmap-demo, spring-cloud-k8s-discovery-demo, spring-cloud-k8s-feign-demo], 2022-05-12 19:38:25 /app #