知乎專欄 | 多維度架構 | | | 微信號 netkiller-ebook | | | QQ群:128659835 請註明“讀者” |
Spring Cloud 使用 Kubernetes 提供的 Config Maps 作為配置中心。這樣的好處是我們無需再啟動一個 config 服務。
父項目
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.netkiller</groupId> <artifactId>kubernetes</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>kubernetes</name> <url>http://www.netkiller.cn</url> <description>Spring Cloud with Kubernetes</description> <organization> <name>Netkiller Spring Cloud 手札</name> <url>http://www.netkiller.cn</url> </organization> <developers> <developer> <name>Neo</name> <email>netkiller@msn.com</email> <organization>Netkiller Spring Cloud 手札</organization> <organizationUrl>http://www.netkiller.cn</organizationUrl> <roles> <role>Author</role> </roles> </developer> </developers> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring-cloud.version>Hoxton.SR8</spring-cloud.version> <docker.registry>registry.netkiller.cn:5000</docker.registry> <docker.registry.name>netkiller</docker.registry.name> <docker.image>mcr.microsoft.com/java/jre:15-zulu-alpine</docker.image> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath /> </parent> <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> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <modules> <module>service</module> <module>ConfigMaps</module> </modules> </project>
項目模組
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.netkiller</groupId> <artifactId>kubernetes</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>cn.netkiller</groupId> <artifactId>configmaps</artifactId> <version>0.0.1-SNAPSHOT</version> <name>configmaps</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-kubernetes-config</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>1.2.2</version> <configuration> <imageName>${docker.registry}/${docker.registry.name}/${project.artifactId}</imageName> <baseImage>${docker.image}</baseImage> <maintainer>netkiller@msn.com</maintainer> <volumes>/tmp</volumes> <workdir>/srv</workdir> <exposes>8080</exposes> <env> <JAVA_OPTS>-server -Xms128m -Xmx256m</JAVA_OPTS> </env> <entryPoint>["sh", "-c", "java ${JAVA_OPTS} -jar /srv/${project.build.finalName}.jar ${SPRING_OPTS}"]</entryPoint> <resources> <resource> <targetPath>/srv</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> <!-- <image>${docker.image.prefix}/${project.artifactId}</image> --> <!-- <newName>${docker.image.prefix}/${project.artifactId}:${project.version}</newName> --> <!-- <serverId>docker-hub</serverId> --> <registryUrl>http://${docker.registry}/v2/</registryUrl> <imageTags> <!-- <imageTag>${project.version}</imageTag> --> <imageTag>latest</imageTag> </imageTags> </configuration> </plugin> </plugins> </build> </project>
src/main/resources/bootstrap.yml
spring: application: name: spring-cloud-kubernetes-configmaps profiles: active: dev cloud: kubernetes: reload: enabled: true mode: polling period: 5000 config: sources: - name: ${spring.application.name} group: cn.netkiller namespace: default management: security: enabled: false #context-path: / endpoints: web: exposure: include: refresh
package cn.netkiller.config; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String[] args) { System.out.println("Hello World!"); SpringApplication.run(App.class, args); } }
package cn.netkiller.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @Configuration @ConfigurationProperties(prefix = "greeting") public class KeyValueConfig { private String message = "This is a default message"; public String getMessage() { return this.message; } public void setMessage(String message) { this.message = message; } }
package cn.netkiller.config; import java.text.SimpleDateFormat; import java.util.Date; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController @EnableConfigurationProperties(KeyValueConfig.class) @RefreshScope public class TestController { @Autowired private KeyValueConfig keyValueConfig; @GetMapping("/") public String index() { return "Hello world\r\n"; } @GetMapping("/hello") public String hello() { return keyValueConfig.getMessage() + " [" + new SimpleDateFormat().format(new Date()) + "]"; } }
config.yaml
apiVersion: v1 kind: Service metadata: annotations: fabric8.io/git-commit: 729badc5e8578b67c1f9387ac0d1949b0646a991 fabric8.io/git-branch: master fabric8.io/git-url: https://netkiller@github.com/netkiller/spring-cloud-kubernetes.git fabric8.io/scm-url: https://github.com/spring-projects/spring-boot/kubernetes/ConfigMaps fabric8.io/scm-tag: HEAD prometheus.io/port: "9779" prometheus.io/scrape: "true" labels: expose: "true" app: ConfigMaps provider: fabric8 version: 0.0.1-SNAPSHOT group: cn.netkiller name: config spec: ports: - name: http port: 8080 protocol: TCP targetPort: 8080 selector: app: ConfigMaps provider: fabric8 group: cn.netkiller type: NodePort --- kind: ConfigMap apiVersion: v1 metadata: name: spring-cloud-kubernetes-configmaps data: application.yml: |- greeting: message: Say Hello to the World farewell: message: Say Goodbye --- spring: profiles: development greeting: message: Say Hello to the Developers farewell: message: Say Goodbye to the Developers --- spring: profiles: production greeting: message: Say Hello to the Ops --- apiVersion: apps/v1 kind: Deployment metadata: annotations: fabric8.io/git-commit: 729badc5e8578b67c1f9387ac0d1949b0646a991 fabric8.io/git-branch: master fabric8.io/git-url: https://netkiller@github.com/netkiller/spring-cloud-kubernetes.git fabric8.io/scm-url: https://github.com/spring-projects/spring-boot/kubernetes/ConfigMaps fabric8.io/scm-tag: HEAD labels: app: ConfigMaps provider: fabric8 version: 0.0.1-SNAPSHOT group: cn.netkiller name: config spec: replicas: 1 revisionHistoryLimit: 2 selector: matchLabels: app: ConfigMaps provider: fabric8 group: cn.netkiller template: metadata: annotations: fabric8.io/git-commit: 729badc5e8578b67c1f9387ac0d1949b0646a991 fabric8.io/git-branch: master fabric8.io/scm-tag: HEAD fabric8.io/git-url: https://netkiller@github.com/netkiller/spring-cloud-kubernetes.git fabric8.io/scm-url: https://github.com/spring-projects/spring-boot/kubernetes/ConfigMaps labels: app: ConfigMaps provider: fabric8 version: 0.0.1-SNAPSHOT group: cn.netkiller spec: containers: - env: - name: KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace image: registry.netkiller.cn:5000/netkiller/configmaps:latest #imagePullPolicy: IfNotPresent name: spring-boot ports: - containerPort: 8080 name: http protocol: TCP - containerPort: 9779 name: prometheus protocol: TCP - containerPort: 8778 name: jolokia protocol: TCP securityContext: privileged: false
編譯並構建 Docker 鏡像
iMac:ConfigMaps neo$ mvn package docker:build
查看鏡像是否正確產生
iMac:ConfigMaps neo$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE registry.netkiller.cn:5000/netkiller/configmaps latest 93280aec434f 4 minutes ago 219MB
上傳鏡像到私有庫
iMac:ConfigMaps neo$ docker push registry.netkiller.cn:5000/netkiller/configmaps The push refers to repository [registry.netkiller.cn:5000/netkiller/configmaps] f56e553c8b82: Pushed 7c1edc21f93f: Layer already exists 50644c29ef5a: Layer already exists latest: digest: sha256:3ef48e858254ee4d578fe1737fd948b2679c33d28d0dc573cf1e8076d0a054a1 size: 952
開啟 Spring cloud 訪問 Kubernetes Config Maps 的權限。
kubectl create clusterrolebinding permissive-binding \ --clusterrole=cluster-admin \ --user=admin \ --user=kubelet \ --group=system:serviceaccounts
部署鏡像
iMac:ConfigMaps neo$ kubectl create -f config.yaml service/config created configmap/spring-cloud-kubernetes-configmaps created deployment.apps/config created
查看服務地址
iMac:ConfigMaps neo$ minikube service list |----------------------|---------------------------|--------------|----------------------------| | NAMESPACE | NAME | TARGET PORT | URL | |----------------------|---------------------------|--------------|----------------------------| | default | config | http/8080 | http://192.168.64.12:30662 | | default | kubernetes | No node port | | kube-system | kube-dns | No node port | | kubernetes-dashboard | dashboard-metrics-scraper | No node port | | kubernetes-dashboard | kubernetes-dashboard | No node port | |----------------------|---------------------------|--------------|----------------------------|
訪問地址,從 Config Maps 中獲取配置項。
iMac:ConfigMaps neo$ curl http://192.168.64.12:30662/hello Say Hello to the World [10/7/20, 11:25 AM]
修改配置增加了=*=,然後使用 kubectl apply -f config.yaml 刷新
iMac:ConfigMaps neo$ curl http://192.168.64.12:30662/hello Say Hello to the World=*= [10/7/20, 11:26 AM]
配置刷新成功
有了 Kubernetes 註冊發現,我們就可以拋棄 Eureka Server。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.netkiller</groupId> <artifactId>kubernetes</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>kubernetes</name> <url>http://www.netkiller.cn</url> <description>Spring Cloud with Kubernetes</description> <organization> <name>Netkiller Spring Cloud 手札</name> <url>http://www.netkiller.cn</url> </organization> <developers> <developer> <name>Neo</name> <email>netkiller@msn.com</email> <organization>Netkiller Spring Cloud 手札</organization> <organizationUrl>http://www.netkiller.cn</organizationUrl> <roles> <role>Author</role> </roles> </developer> </developers> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring-cloud.version>Hoxton.SR8</spring-cloud.version> <docker.registry>registry.netkiller.cn:5000</docker.registry> <docker.registry.name>netkiller</docker.registry.name> <docker.image>mcr.microsoft.com/java/jre:15-zulu-alpine</docker.image> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath /> </parent> <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> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> <modules> <module>service</module> <module>ConfigMaps</module> <module>provider</module> <module>consumer</module> </modules> </project>
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.netkiller</groupId> <artifactId>kubernetes</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>cn.netkiller</groupId> <artifactId>provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>provider</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-kubernetes</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>1.2.2</version> <configuration> <imageName>${docker.registry}/${docker.registry.name}/${project.artifactId}</imageName> <baseImage>${docker.image}</baseImage> <maintainer>netkiller@msn.com</maintainer> <volumes>/tmp</volumes> <workdir>/srv</workdir> <exposes>8080</exposes> <env> <JAVA_OPTS>-server -Xms128m -Xmx256m</JAVA_OPTS> </env> <entryPoint>["sh", "-c", "java ${JAVA_OPTS} -jar /srv/${project.build.finalName}.jar ${SPRING_OPTS}"]</entryPoint> <resources> <resource> <targetPath>/srv</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> <!-- <image>${docker.image.prefix}/${project.artifactId}</image> --> <!-- <newName>${docker.image.prefix}/${project.artifactId}:${project.version}</newName> --> <!-- <serverId>docker-hub</serverId> --> <registryUrl>http://${docker.registry}/v2/</registryUrl> <imageTags> <!-- <imageTag>${project.version}</imageTag> --> <imageTag>latest</imageTag> </imageTags> </configuration> </plugin> </plugins> </build> </project>
注意:這裡必須使用 @EnableDiscoveryClient 註解,不能使用 @EnableEurekaClient。
他們的區別是 @EnableEurekaClient 只能註冊到 Eureka Server,而 @EnableDiscoveryClient 不僅可以註冊進 Eureka Server 還能註冊到 ZooKeeper,Consul,Kubernetes 等等......
package cn.netkiller.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class ProviderApplication { public static void main(String[] args) { System.out.println("Hello World!"); SpringApplication.run(ProviderApplication.class, args); } }
package cn.netkiller.provider.controller; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.List; 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 lombok.extern.slf4j.Slf4j; @RestController @Slf4j public class ProviderController { @Autowired private DiscoveryClient discoveryClient; @GetMapping("/") public String index() { return "Hello world!!!"; } @GetMapping("/ping") public String ping() { try { return InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { return "Pong"; } } @GetMapping("/services") public List<String> services() { return this.discoveryClient.getServices(); } }
@GetMapping("/services") 可以返回註冊中心已經註冊的服務。
src/main/resource/application.properties 配置檔案
spring.application.name=provider server.port=8080
Pod 啟動後會以 spring.application.name 設置的名字註冊到註冊中心,Openfeign 將改名字訪問微服務。
apiVersion: v1 kind: Service metadata: name: provider labels: app.kubernetes.io/name: provider spec: type: ClusterIP ports: - port: 8080 targetPort: 8080 protocol: TCP name: http selector: app.kubernetes.io/name: provider --- apiVersion: apps/v1 kind: Deployment metadata: name: provider labels: app.kubernetes.io/name: provider spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: provider template: metadata: labels: app.kubernetes.io/name: provider app.kubernetes.io/instance: sad-markhor spec: containers: - name: provider image: registry.netkiller.cn:5000/netkiller/provider #imagePullPolicy: IfNotPresent ports: - name: http containerPort: 8080 protocol: TCP env: - name: "KUBERNETES_NAMESPACE" valueFrom: fieldRef: fieldPath: "metadata.namespace"
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.netkiller</groupId> <artifactId>kubernetes</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>cn.netkiller</groupId> <artifactId>consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>consumer</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-kubernetes</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>1.2.2</version> <configuration> <imageName>${docker.registry}/${docker.registry.name}/${project.artifactId}</imageName> <baseImage>${docker.image}</baseImage> <maintainer>netkiller@msn.com</maintainer> <volumes>/tmp</volumes> <workdir>/srv</workdir> <exposes>8080</exposes> <env> <JAVA_OPTS>-server -Xms128m -Xmx256m</JAVA_OPTS> </env> <entryPoint>["sh", "-c", "java ${JAVA_OPTS} -jar /srv/${project.build.finalName}.jar ${SPRING_OPTS}"]</entryPoint> <resources> <resource> <targetPath>/srv</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> <!-- <image>${docker.image.prefix}/${project.artifactId}</image> --> <!-- <newName>${docker.image.prefix}/${project.artifactId}:${project.version}</newName> --> <!-- <serverId>docker-hub</serverId> --> <registryUrl>http://${docker.registry}/v2/</registryUrl> <imageTags> <!-- <imageTag>${project.version}</imageTag> --> <imageTag>latest</imageTag> </imageTags> </configuration> </plugin> </plugins> </build> </project>
package cn.netkiller.consumer; 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 public class ConsumerApplication { public static void main(String[] args) { System.out.println("Hello World!"); SpringApplication.run(ConsumerApplication.class, args); } }
package cn.netkiller.consumer.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import cn.netkiller.consumer.feign.ProviderClient; import lombok.extern.slf4j.Slf4j; @RestController @Slf4j public class ConsumerController { @Autowired private DiscoveryClient discoveryClient; @Autowired private ProviderClient providerClient; @GetMapping("/") public String index() { return "Consumer OK\r\n"; } @GetMapping("/service") public Object getClient() { return discoveryClient.getServices(); } @GetMapping("/instance") public List<ServiceInstance> getInstance(String instanceId) { return discoveryClient.getInstances(instanceId); } @GetMapping("/ping") public String ping() { return OperationResponse.builder().status(true).data(providerClient.ping()).build(); } }
@GetMapping("/ping") 將經過註冊中心,獲取到可用的服務,運行後從微服務返回結果。
package cn.netkiller.consumer.controller; public class OperationResponse { public boolean status = false; public String data = ""; public static OperationResponse builder() { // TODO Auto-generated method stub return new OperationResponse(); } public OperationResponse status(boolean status) { this.status = status; return this; } public OperationResponse data(String data) { this.data = data; return this; } public String build() { return String.format("Status: %s, Data: %s", this.status, this.data); } }
package cn.netkiller.consumer.feign; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; @FeignClient(name = "provider", fallback = ProviderClientFallback.class) public interface ProviderClient { @GetMapping("/ping") String ping(); } @Component class ProviderClientFallback implements ProviderClient { @Override public String ping() { return "Error"; } }
src/main/resource/application.properties 配置檔案
spring.application.name=consumer server.port=8080 provider.ribbon.KubernetesNamespace=default provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
apiVersion: v1 kind: Service metadata: name: consumer labels: app.kubernetes.io/name: consumer spec: type: NodePort ports: - port: 8080 nodePort: 30080 protocol: TCP name: http selector: app.kubernetes.io/name: consumer --- apiVersion: apps/v1 kind: Deployment metadata: name: consumer labels: app.kubernetes.io/name: consumer spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: consumer template: metadata: labels: app.kubernetes.io/name: consumer spec: containers: - name: consumer image: registry.netkiller.cn:5000/netkiller/consumer #imagePullPolicy: IfNotPresent ports: - name: http containerPort: 8080 protocol: TCP
編譯,打包,構建Docker鏡像
iMac:provider neo$ mvn package docker:build iMac:consumer neo$ mvn package docker:build
推送鏡像
iMac:spring-cloud-kubernetes neo$ docker images | grep registry.netkiller.cn registry.netkiller.cn:5000/netkiller/consumer latest 63921dc9b81d 27 seconds ago 238MB registry.netkiller.cn:5000/netkiller/provider latest fbcc6b3a91ef 4 minutes ago 221MB registry.netkiller.cn:5000/netkiller/configmaps latest 93280aec434f 23 hours ago 219MB iMac:spring-cloud-kubernetes neo$ docker push registry.netkiller.cn:5000/netkiller/consumer The push refers to repository [registry.netkiller.cn:5000/netkiller/consumer] 51c839989a66: Pushed 7c1edc21f93f: Pushed 50644c29ef5a: Pushed latest: digest: sha256:2591686cfc63888f3b97ca1e950205b58a47b3d3c618242465baa0796cf7e056 size: 952 iMac:spring-cloud-kubernetes neo$ docker push registry.netkiller.cn:5000/netkiller/provider The push refers to repository [registry.netkiller.cn:5000/netkiller/provider] 47eb1c86b415: Pushed 7c1edc21f93f: Mounted from netkiller/consumer 50644c29ef5a: Mounted from netkiller/consumer latest: digest: sha256:42cdeb63cd67a5edf9e769538878a0c8f18048bcde1ef9ba22d58766afa2d52d size: 952 iMac:spring-cloud-kubernetes neo$ curl -s http://registry.netkiller.cn:5000/v2/_catalog | jq { "repositories": [ "netkiller/configmaps", "netkiller/consumer", "netkiller/provider" ] }
將 provider 和 consumer 應用部署到 Kubernetes
iMac:spring-cloud-kubernetes neo$ kubectl create -f provider/src/main/kubernetes/provider.yaml service/provider created deployment.apps/provider created iMac:spring-cloud-kubernetes neo$ kubectl create -f consumer/src/main/kubernetes/consumer.yaml service/consumer created deployment.apps/consumer created
查看 consumer 連接埠
iMac:spring-cloud-kubernetes neo$ minikube service list |----------------------|---------------------------|--------------|----------------------------| | NAMESPACE | NAME | TARGET PORT | URL | |----------------------|---------------------------|--------------|----------------------------| | default | config | http/8080 | http://192.168.64.12:30662 | | default | consumer | http/8080 | http://192.168.64.12:30080 | | default | kubernetes | No node port | | default | provider | No node port | | kube-system | kube-dns | No node port | | kubernetes-dashboard | dashboard-metrics-scraper | No node port | | kubernetes-dashboard | kubernetes-dashboard | No node port | |----------------------|---------------------------|--------------|----------------------------|
測試 provider 是否工作正常
$ curl -s http://10.10.0.121:8080/ping provider-54875bf44-4gf49 $ curl -s http://10.10.0.121:8080/services |jq [ "config", "kubernetes", "provider" ]
查看 consumer 是否已經註冊成功
iMac:spring-cloud-kubernetes neo$ curl -s http://192.168.64.12:30080/service |jq [ "config", "consumer", "kubernetes", "provider" ] iMac:spring-cloud-kubernetes neo$ curl http://192.168.64.12:30080/ping Status: true, Data: provider-54875bf44-4gf49
增加 provider 節點,然後反覆請求可以看到返回不同節點的主機名
iMac:spring-cloud-kubernetes neo$ kubectl scale deployment provider --replicas=3 deployment.apps/provider scaled iMac:spring-cloud-kubernetes neo$ curl -s http://192.168.64.12:30080/ping Status: true, Data: provider-54875bf44-8vs72 iMac:spring-cloud-kubernetes neo$ curl -s http://192.168.64.12:30080/ping Status: true, Data: provider-54875bf44-4gf49i
測試完畢銷毀服務
iMac:spring-cloud-kubernetes neo$ kubectl delete -f consumer/src/main/kubernetes/consumer.yaml service "consumer" deleted deployment.apps "consumer" deleted iMac:spring-cloud-kubernetes neo$ kubectl delete -f provider/src/main/kubernetes/provider.yaml service "provider" deleted deployment.apps "provider" deleted