Home | 簡體中文 | 繁體中文 | 雜文 | 知乎專欄 | Github | OSChina 博客 | 雲社區 | 雲棲社區 | Facebook | Linkedin | 視頻教程 | 打賞(Donations) | About
知乎專欄多維度架構 | 微信號 netkiller-ebook | QQ群:128659835 請註明“讀者”

10.10. Spring Cloud with Kubernetes

10.10.1. Config

Spring Cloud 使用 Kubernetes 提供的 Config Maps 作為配置中心。這樣的好處是我們無需再啟動一個 config 服務。

10.10.1.1. Maven 依賴

父項目

		
<?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>
		
		
			

10.10.1.2. Spring Cloud 配置檔案

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		
		
			

10.10.1.3. 程序檔案

10.10.1.3.1. SpringBootApplication 啟動檔案
			
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);
	}
}
			
			
				
10.10.1.3.2. 配置類
			
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;
	}
}
			
			
				
10.10.1.3.3. 控製器
			
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()) + "]";
	}
}			
			
				

10.10.1.4. Kubernetes 編排腳本

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		
		
			

10.10.1.5. 測試

編譯並構建 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]		
		
			

配置刷新成功

10.10.2. 註冊發現

有了 Kubernetes 註冊發現,我們就可以拋棄 Eureka Server。

10.10.2.1. Maven 父項目

			
<?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>			
			
			

10.10.2.2. provider

10.10.2.2.1. Maven 依賴
				
<?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>
				
				
				
10.10.2.2.2. Springboot 啟動類

注意:這裡必須使用 @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);
	}
}				
				
				
10.10.2.2.3. 控製器
				
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") 可以返回註冊中心已經註冊的服務。

10.10.2.2.4. application.properties 配置檔案

src/main/resource/application.properties 配置檔案

				
spring.application.name=provider
server.port=8080		
				
				

Pod 啟動後會以 spring.application.name 設置的名字註冊到註冊中心,Openfeign 將改名字訪問微服務。

10.10.2.2.5. Kubernetes provider 編排腳本
				
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"				
				
				

10.10.2.3. consumer

10.10.2.3.1. Maven 依賴
				
<?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>
				
				
				
10.10.2.3.2. Springboot 啟動類
				
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);
	}
}				
				
				
10.10.2.3.3. 控製器
				
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);
	}
}
				
				
				
10.10.2.3.4. FeignClient 介面
				
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";
	}
}
				
				
				
10.10.2.3.5. application.properties 配置檔案

src/main/resource/application.properties 配置檔案

				
spring.application.name=consumer
server.port=8080

provider.ribbon.KubernetesNamespace=default
provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
				
				
10.10.2.3.6. Kubernetes consumer 編排腳本
				
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
				
				
				

10.10.2.4. 測試

編譯,打包,構建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			
			
			

10.10.2.5. 

			
spring.cloud.kubernetes.discovery.enabled=false