关于spring-cloud-kubernetes
spring-cloud-kubernetes是springcloud官方推出的开源项目,用于将Spring Cloud和Spring Boot应用运行在kubernetes环境,并且提供了通用的接口来调用kubernetes服务,主要提供了应用程序使用k8s本身功能:服务注册发现、客户端负载均衡、从Kubernetes ConfigMap和Secrets加载应用程序属性 。 ConfigMap或Secret更改时,重新加载应用程序属性。GitHub上官方地址是:https://github.com/spring-cloud/spring-cloud-kubernetes
官网文档地址:https://cloud.spring.io/spring-cloud-kubernetes/1.1.x/reference/html/
入门程序
环境和版本:
linux系统
k8s集群
kubectl
idea
jdk8
maven
spring-boot工程
pom.xml
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xxx.bolingcavalry</groupId>
<artifactId>springcloudk8sdiscovery</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloudk8sdiscovery</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.1.1.RELEASE</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<type>pom</type>
<scope>import</scope>
<version>${spring-boot.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.1.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
配置文件
spring.application.name=springcloudk8sdiscovery
1
启动类
package com.xxx.cloud.k8s.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class Springcloudk8sdiscoveryApplication {
public static void main(String[] args) {
SpringApplication.run(Springcloudk8sdiscoveryApplication.class, args);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
接口
package com.xxx.cloud.k8s.demo.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;
/**
* @date: 2020/4/21 9:02
* @author: lhl
* @version: V0.1
* */
@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());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
服务部署
打包spring-boot工程
mvn clean -DskipTests=true package
1
在targe\目录下生成可执行jar :springcloudk8sdiscovery-0.0.1-SNAPSHOT.jar
上传到linux服务器
创建Dockerfile文件
#基础镜像,如果本地仓库没有,会从远程仓库拉取
FROM 私服/jdk8版本
#容器中创建目录
RUN mkdir -p /usr/local/cloudk8s-demo
#编译后的jar包copy到容器中创建到目录内
COPY springcloudk8sdiscovery-0.0.1-SNAPSHOT.jar /usr/local/cloudk8s-demo/app.jar
#指定容器启动时要执行的命令
ENTRYPOINT ["java","-jar","/usr/local/cloudk8s-demo/app.jar"]
1
2
3
4
5
6
7
8
创建 kubernetes.yml文件
apiVersion: v1
kind: Service
metadata:
labels:
app: springcloudk8sdiscovery
name: springcloudk8sdiscovery
namespace: default
spec:
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: springcloudk8sdiscovery
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: springcloudk8sdiscovery
name: springcloudk8sdiscovery
spec:
replicas: 1
selector:
matchLabels:
app: springcloudk8sdiscovery
template:
metadata:
labels:
app: springcloudk8sdiscovery
spec:
containers:
- image: 私服/test/cloudk8s-demo
imagePullPolicy: IfNotPresent
name: springcloudk8sdiscovery
ports:
- containerPort: 8080
name: http
protocol: TCP
securityContext:
privileged: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
创建 start.sh 脚本文件
#!/bin/bash
imageVersion=2
docker build -t 私服/test/cloudk8s-demo:${imageVersion} . docker push私服/test/cloudk8s-demo:${imageVersion} kubectl delete -f kubernetes.yml kubectl create -f kubernetes.yml
docker image 版本修改相应需要修改 kubernetes.yml 中的镜像版本
1
2
3
4
修改start.sh为可执行 chmod +x start.sh
执行脚本 sh start.sh
查看服务是否启动
kubectl get pods
1
READY下面的前后数一样则启动成功 1/1,且STATUS为Running状态,如果为0/1则启动失败,启动失败,通过命令:kubectl describe pod podName查看服务详情或者kubectl logs -f podName 查看日志
通过 kubectl get svc 查看服务端口,通过ip:端口访问服务查询服务列表
入门程序部署完成
通过feign调用k8s启动的服务的工程
pom.xml
<?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>com.xxx</groupId>
<artifactId>cloud-k8s-feign</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<springcloud.kubernetes.version>1.0.1.RELEASE</springcloud.kubernetes.version>
<springcloud.version>2.0.3.RELEASE</springcloud.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>
<version>${springcloud.version}</version>
</dependency>
<dependency>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<groupId>org.springframework.cloud</groupId>
</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>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</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>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
配置文件
spring:
application:
name: k8s-feign
product-infra-service:
ribbon:
KubernetesNamespace: default
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: 11101
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
启动类
package com.xxx.cloud.k8s.feign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @date: 2020/4/21 16:02
* @author: lhl
* @version: V0.1
* */
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class K8sFeignApplication {
public static void main(String[] args) {
SpringApplication.run(K8sFeignApplication.class, args);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
接口
package com.xxx.cloud.k8s.feign.controller;
import com.xxx.cloud.k8s.feign.feign.TestClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @date: 2020/4/21 16:05
* @author: lhl
* @version: V0.1
*
*/
@RestController
public class TestController {
@Autowired
private TestClient testClient;
@GetMapping("test1")
public String test1() {
return testClient.services();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
feign 接口
package com.xxx.cloud.k8s.feign.feign;
import com.xxx.cloud.k8s.feign.hystrix.TestClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @date: 2020/4/21 16:02
* @author: lhl
* @version: V0.1
*
*/
@FeignClient(name = "${springcloudk8sdiscovery:springcloudk8sdiscovery}", fallback = TestClientFallback.class)
public interface TestClient {
@RequestMapping("/services")
String services();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
hystrix fallback类
package com.xxx.cloud.k8s.feign.hystrix;
import com.xxx.cloud.k8s.feign.feign.TestClient;
import org.springframework.stereotype.Component;
/**
* @date: 2020/4/21 16:03
* @author: lhl
* @version: V0.1
*
*/
@Component
public class TestClientFallback implements TestClient {
@Override
public String services() {
return "hystrix call back";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
其他步骤和入门程序差不多
start.sh文件
start.sh
#!/bin/bash
imageVersion=2
docker build -t 私服/test/cloud-k8s-feign:${imageVersion} .
docker push 私服/test/cloud-k8s-feign:${imageVersion}
kubectl delete -f kubernetes.yml
kubectl create -f kubernetes.yml
1
2
3
4
5
6
7
8
Dockerfile
#基础镜像,如果本地仓库没有,会从远程仓库拉取
FROM 私服/oraclejdk8
#容器中创建目录
RUN mkdir -p /usr/local/cloudk8s-feign-demo
#编译后的jar包copy到容器中创建到目录内
COPY cloud-k8s-feign-1.0-SNAPSHOT.jar /usr/local/cloudk8s-feign-demo/app.jar
#指定容器启动时要执行的命令
ENTRYPOINT ["java","-jar","/usr/local/cloudk8s-feign-demo/app.jar"]
1
2
3
4
5
6
7
8
修改start.sh文件的docker镜像版本需要也需要改kubernetes.yml的docker镜像版本
kubernetes.yml
apiVersion: v1
kind: Service
metadata:
labels:
app: k8s-feign
name: k8s-feign
namespace: default
spec:
ports:
- name: http
port: 11101
protocol: TCP
targetPort: 11101
selector:
app: k8s-feign
type: NodePort
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: k8s-feign
name: k8s-feign
spec:
replicas: 3
selector:
matchLabels:
app: k8s-feign
template:
metadata:
labels:
app: k8s-feign
spec:
containers:
- image: 私服/test/cloud-k8s-feign:1
imagePullPolicy: IfNotPresent
name: k8s-feign
ports:
- containerPort: 11101
name: http
protocol: TCP
securityContext:
privileged: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
运行脚本文件start.sh
产看服务是否运行
查看端口号
通过接口 test1访问服务
Ip:port/test1
可以通过接口test1,以feign调用的方式访问到入门程序的services接口
k8s安全设计
生产环境k8s服务发现会报错:
Message: Forbidden!Configured service account doesn't have access. Service account may have been revoked. services is forbidden: User "system:serviceaccount:default:default" cannot list resource "services" in API group "" in the namespace "default"
1
通过官方发布,为权限问题
在生产级别环境,可能需要给用户角色授权
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: YOUR-NAME-SPACE
name: namespace-reader
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["configmaps", "pods", "services", "endpoints", "secrets"]
verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: namespace-reader-binding
namespace: YOUR-NAME-SPACE
subjects:
- kind: ServiceAccount
name: default
apiGroup: ""
roleRef:
kind: Role
name: namespace-reader
apiGroup: ""