spring-cloud-kubernetes-feign实战

易书
2023-12-01

关于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: ""

 类似资料: