Spring Cloud 使用 k8s 作为注册中心 开发环境 和 生产环境

因为 k8s 本身就有拥有注册中心,和配置中心的功能。如果还是用 Nacos、Eureka、Consul 之类的注册中心组件,就有点冗余了。当然这些组件还是可以继续用的。

所以,本教程,教授 Spring Cloud 使用 k8s 的注册中心。在开发环境和生产环境 的教程!

下面以一个最简单的 服务消费者 使用 OpenFeign 调用 服务提供者 的案例

源代码地址(我也不想用gitee,但是github太慢了):

https://gitee.com/thousmile/k8s-demo1

k8s-demo1 的 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <groupId>com.xaaef.k8sdemo</groupId>
    <artifactId>k8s-demo1</artifactId>
    <version>1.1</version>
    <name>k8s-demo1</name>
    <description>k8s-demo1</description>

    <modules>
        <module>k8s-provider</module>
        <module>k8s-consumer</module>
    </modules>

    <properties>
        <java.version>17</java.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <maven-compiler.version>3.10.1</maven-compiler.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <hutool.version>5.8.9</hutool.version>
        <spring-boot.version>3.0.6</spring-boot.version>
        <spring-cloud.version>2022.0.2</spring-cloud.version>
    </properties>

    <dependencies>

        <!-- 生产环境用 kubernetes 注册中心 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-fabric8</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-bom</artifactId>
                <version>${hutool.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

服务提供者 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>com.xaaef.k8sdemo</groupId>
        <artifactId>k8s-demo1</artifactId>
        <version>1.1</version>
    </parent>

    <artifactId>k8s-provider</artifactId>
    <name>k8s-provider</name>

    <description>
        管理服务
    </description>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

服务提供者 application.yml

server:
  port: 18831

spring:
  application:
    name: k8s-provider
  profiles:
    active: dev

服务提供者 application-dev.yml

开发环境,禁用 k8s 的注册中心。就算启用,也没啥用,在本地idea中开发时,根本获取不到k8s注册中心的服务列表

spring:
  cloud:
    kubernetes:
      discovery:
        enabled: false

服务提供者 application-prod.yml

生产环境 启动k8s的注册中心,并且获取 “ sc ” 这个命名空间下所有的服务

spring:
  cloud:
    kubernetes:
      discovery:
        enabled: true
        namespaces:
          - "sc"

服务提供者 java代码


package com.xaaef.k8sdemo.provider;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.net.InetAddress;
import java.net.UnknownHostException;

@RequestMapping
@RestController
@EnableDiscoveryClient
@SpringBootApplication
public class ProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }

    @GetMapping()
    public String index() {
        return StrUtil.format("provider ===> : {}", IdUtil.fastSimpleUUID());
    }

    @GetMapping("hello")
    public String hello(@RequestParam("name") String name) throws UnknownHostException {
        var hostAddress = InetAddress.getLocalHost().getHostAddress();
        return StrUtil.format("provider -> {} : hello {}", hostAddress, name);
    }

}

服务提供者 Dockerfile (一定要根 src 同一个目录)

FROM eclipse-temurin:17-jre-alpine
MAINTAINER Wang Chen Chen<932560435@qq.com>
ENV VERSION 1.1
# 复制打包 完成后的jar文件,名字修改成 app.jar
COPY ./target/k8s-provider-1.1.jar app.jar
# 设置时区为上海
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
# 设置编码
ENV LANG C.UTF-8
# JVM参数
ENV JVM_OPTS="-server -XX:+DisableExplicitGC -XX:+HeapDumpOnOutOfMemoryError"
# 服务暴露端口PORT
EXPOSE 18831
# 启动 Spring Boot App 命令
ENTRYPOINT java ${JVM_OPTS} -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai -Djava.security.egd=file:/dev/./urandom -jar /app.jar

服务消费者 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>com.xaaef.k8sdemo</groupId>
        <artifactId>k8s-demo1</artifactId>
        <version>1.1</version>
    </parent>

    <artifactId>k8s-consumer</artifactId>
    <name>k8s-consumer</name>

    <description>
        管理服务
    </description>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
		
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

消费者 application.yml

server:
  port: 18841

spring:
  application:
    name: k8s-consumer
  profiles:
    active: dev
  cloud:
    openfeign:
      client:
        config:
          default:
            connect-timeout: 1000
            read-timeout: 1000
            logger-level: full
      compression:
        request:
          enabled: true
        response:
          enabled: true
      circuitbreaker:
        enabled: true
        alphanumeric-ids:
          enabled: true
      okhttp:
        enabled: true

消费者 application-dev.yml

在本地idea 开发环境中,无法获取k8s注册中心的服务,所以需要自定义的服务实例信息。

原理请看 SimpleDiscoveryClient 和 SimpleDiscoveryProperties 配置类

spring:
  cloud:
    kubernetes:
      discovery:
        enabled: false
    discovery:
      client:
        simple:
          instances:
            k8s-provider:
              - instanceId: k8s-provider-${random.int}
                serviceId: k8s-provider
                host: localhost
                port: 18831

消费者 application-prod.yml

生产环境 启动k8s的注册中心,并且获取 “ sc ” 这个命名空间下所有的服务

spring:
  cloud:
    kubernetes:
      discovery:
        enabled: true
        namespaces:
          - "sc"

服务消费者 java 代码 ConsumerApplication.java

package com.xaaef.k8sdemo.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;

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }

}

服务消费者 java 代码 RpcProviderService.java

package com.xaaef.k8sdemo.consumer;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "k8s-provider")
public interface RpcProviderService {

    @GetMapping("hello")
    String hello(@RequestParam("name") String name);
    
}

服务消费者 java 代码 IndexController.java

package com.xaaef.k8sdemo.consumer;

import lombok.AllArgsConstructor;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RequestMapping
@RestController
@AllArgsConstructor
public class IndexController {

    private final RpcProviderService providerService;

    private final DiscoveryClient discoveryClient;

	// 获取 注册中心 中所有的 服务ID
    @GetMapping("services")
    public List<String> services() {
        return discoveryClient.getServices();
    }
	
	// 根据 服务ID 获取服务实例
    @GetMapping("instances")
    public List<ServiceInstance> getInstances(@RequestParam String serviceId) {
        return discoveryClient.getInstances(serviceId);
    }

	// 调用 消费者服务
    @GetMapping("hello")
    public String hello() {
        return providerService.hello("consumer");
    }

}

服务消费者 Dockerfile (一定要根 src 同一个目录)

FROM eclipse-temurin:17-jre-alpine
MAINTAINER Wang Chen Chen<932560435@qq.com>
ENV VERSION 1.1
# 复制打包 完成后的jar文件,名字修改成 app.jar
COPY ./target/k8s-consumer-1.1.jar app.jar
# 设置时区为上海
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
# 设置编码
ENV LANG C.UTF-8
# JVM参数
ENV JVM_OPTS="-server -XX:+DisableExplicitGC -XX:+HeapDumpOnOutOfMemoryError"
# 服务暴露端口PORT
EXPOSE 18841
# 启动 Spring Boot App 命令
ENTRYPOINT java ${JVM_OPTS} -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai -Djava.security.egd=file:/dev/./urandom -jar /app.jar

test.http 测试

### 消费者  获取所有服务名
GET http://localhost:18841/services

### 消费者  根据服务名获取详情
GET http://localhost:18841/instances?serviceId=k8s-provider

### 消费者  调用提供者。
GET http://localhost:18841/hello

## 结果 provider -> 192.168.0.167 : hello consumer

项目打包成 docker 镜像

在这里插入图片描述

服务提供者 docker 构建镜像

docker build -t k8s-provider:1.1 ./

在这里插入图片描述

服务消费者 docker 构建镜像

docker build -t k8s-consumer:1.1 ./

在这里插入图片描述

到这里有两个选择,1.将镜像推送到 阿里云docker仓库中,然后在k8s集群中再拉下来、

2.将 镜像导出成 .tar 文件。然后手动上传到 k8s 集群的容器中。在这里使用第二种,我的网络不太好

## 将 消费者和提供者 保存到 k8s-demo1.tar 文件中。
docker save -o k8s-demo1.tar k8s-consumer:1.1 k8s-provider:1.1

## k8s 的容器如果是 docker 。切记集群有几个工作节点,就要导入几次。
docker load -i k8s-demo1.tar

## k8s 的容器如果是 containerd
ctr -n k8s.io image import k8s-demo1.tar

k8s 服务 yaml

apiVersion: v1
kind: Namespace
metadata:
  name: sc
  labels:
    name: spring-cloud-k8s
---
### 创建一个账号 然后关联集群管理员的权限。否则 消费者 无法获取到 “sc” 命名空间中的 服务
apiVersion: v1
kind: ServiceAccount
metadata:
  name: k8s-demo1
  namespace: sc
  labels:
    app: sc-k8s-demo1
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: k8s-demo1
  namespace: sc
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: k8s-demo1
    namespace: sc

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-consumer
  namespace: sc
  labels:
    app: k8s-consumer
spec:
  replicas: 3
  template:
    metadata:
      name: k8s-consumer
      labels:
        app: k8s-consumer
    spec:
      serviceAccountName: k8s-demo1
      containers:
        - name: k8s-consumer
          image: k8s-consumer:1.1
          imagePullPolicy: IfNotPresent
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: prod
      restartPolicy: Always
  selector:
    matchLabels:
      app: k8s-consumer

---
apiVersion: v1
kind: Service
metadata:
  name: k8s-consumer
  namespace: sc
  labels:
    app: k8s-consumer
spec:
  type: ClusterIP
  selector:
    app: k8s-consumer
  ports:
    - port: 18841
      targetPort: 18841

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-provider
  namespace: sc
  labels:
    app: k8s-provider
spec:
  replicas: 3
  template:
    metadata:
      name: k8s-provider
      labels:
        app: k8s-provider
    spec:
      serviceAccountName: k8s-demo1
      containers:
        - name: k8s-provider
          image: k8s-provider:1.1
          imagePullPolicy: IfNotPresent
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: prod
      restartPolicy: Always
  selector:
    matchLabels:
      app: k8s-provider

---
apiVersion: v1
kind: Service
metadata:
  name: k8s-provider
  namespace: sc
  labels:
    app: k8s-provider
spec:
  type: ClusterIP
  selector:
    app: k8s-provider
  ports:
    - port: 18831
      targetPort: 18831

---
################## ingress myapp ##################
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: k8s-demo1
  namespace: sc
  labels:
    app: k8s-demo1
spec:
  ingressClassName: nginx
  rules:
    - host: consumer.example.com
      http:
        paths:
          - path: "/"
            pathType: Prefix
            backend:
              service:
                name: k8s-consumer
                port:
                  number: 18841

    - host: provider.example.com
      http:
        paths:
          - path: "/"
            pathType: Prefix
            backend:
              service:
                name: k8s-provider
                port:
                  number: 18831

启动 消费者 和 提供者 服务

kubectl apply -f k8s-demo1.yaml

1.编辑 C:\Windows\System32\drivers\etc\hosts

### 192.168.2.33 即是k8s集群中 ingress 的 ip
192.168.2.33 provider.example.com
192.168.2.33 consumer.example.com

test.http 测试

### 消费者  获取所有服务名
GET http://consumer.example.com/services

### 消费者  根据服务名获取详情
GET http://consumer.example.com/instances?serviceId=k8s-provider

### 消费者  调用提供者。
GET http://consumer.example.com/hello

## 结果 provider -> 10.233.76.8 : hello consumer

2.使用 kt-connect 的VPN能力,直接访问k8s集群内部的 service

官方文档 https://alibaba.github.io/kt-connect/#/

ktctl connect

test.http 测试

### 消费者  获取所有服务名
GET http://k8s-consumer.sc.svc:18841/services

### 消费者  根据服务名获取详情
GET http://k8s-consumer.sc.svc:18841/instances?serviceId=k8s-provider

### 消费者  调用提供者。
GET http://k8s-consumer.sc.svc:18841/hello

## 结果 provider -> 10.233.76.8 : hello consumer

至此,开发环境使用配置的服务列表。生产环境k8s的注册中心。不需要修改任何代码。只需要切换spring.profiles.active 的环境接口。如果有人知道更方便的方式,可以留言给我!

spring boot 、mybatis-plus、mysql 的 schema 多租户 项目 请大家去点个赞

https://github.com/thousmile/molly-multi-tenant

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐