Kubernetes 部署 SpringCloud 网关 Zuul 1.x + Eureka 动态路由

目录[-]


系统环境

  • Redis 版本:5.0.5
  • Kubernetes 版本:1.5.3
  • SpringBoot 版本:2.1.8.RELEASE
  • SpringCloud 版本:Greenwich.SR2
  • SpringCloud Kubernetes 版本:1.0.2.RELEASE
  • spring-cloud-zuul-ratelimit 版本:2.2.5.RELEASE

参考地址

一、Zuul 概念简介

1、简单介绍下 Zuul

Zuul 是 Netflix 开源的一个网关服务, 本质上是一个 Web Servlet 应用。主要是作用在服务最外层充当网关角色,还包括下面功能:

  • 动态路由: 以动态方式根据需要将请求路由至不同后端集群处。
  • 压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。
  • 安全验证: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
  • 负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
  • 静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。
  • 审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。

2、为什么选择 Zuul 1.x 版本

现在很多 SpringCloud 组件都在往 Kubernetes 中迁移,SpringBoot Admin 作为我们微服务监控经常使用的组件,也要将它迁移到 Kuberntes 环境下。虽然现在 Netflix 的组件以及不再维护,但是它的很多组件大多数都是经历过生产环境下验证过得,尤其是网关 Zuul。

现在很多公司部署网关还是依旧优先选择使用 Zuul 1.x 版本部署。Zuul 2.x 版本和现在 SpringCloud 新推出的 Gateway 使用 Netty 作为底层,提供异步接口,不过这也间接增加了代码复杂性和缺少生产环境验证,所以现在生产环境还是推荐使用 Zuul 1.x 版本,这里记录在 Kuberntes 下部署 Zuul 1.x 的构建及部署过程。

二、Zuul 配置简介

1、Zuul 的路由配置

(1)、自动路由配置

Zuul 中引入 Eureka 依赖时并开启服务发现后,Zuul 会自动将注册中心中服务列表中的服务生成路由规则。例如,注册中心存在 service-A,service-B 两个服务实例,Zuul 会自动为其配置路由规则:

  • 访问地址 /service-A/** 来访问 service-A 服务。
  • 访问地址 /service-B/** 来访问 service-B 服务。

(2)、自定义路由配置

当然,我们也可以手动设置服务的路由规则,手动配置一般是在配置文件中设置服务路由规则,规则如下:

  • zuul.routes..path: 服务的监听地址,可以通过访问此地址访问到对应的服务信息。
  • zuul.routes..serviceId: 注册中心中服务实例名称。

例如,我们有一个 demo 的服务,该服务注册到注册中心实例名称也同样为 demo,那么可以按下配置:

zuul:
  routes:
    demo:
      path: /api/**
      serviceId: demo

路由配置中的 path 就是用来匹配请求 url 中的路径,可以通过访问该地址从而访问到服务。path通常需要使用通配符,如下:

通配符说明举例
?匹配单个字符/api/?
*匹配任意数量字符,但不支持多级目录/api/*
**匹配任意数量字符,支持多级目录/api/**

如果有一个可以同时满足多个 path 的匹配的情况,此时匹配结果取决于路由规则的定义顺序。

(3)、忽略默认自动路由配置

Zuul 中引入 Eureka 依赖时并开启服务发现后,Zuul 会自动将注册中心中服务列表中的服务生成路由规则,默认的 path 为注册中心中服务名称,如果想忽略该服务,可以配置 zuul.ignored-services 参数。

  • 如果参数为服务名称,则 Zuul 不自动配置该服务路由。
  • 如果参数为 “*” ,则 Zuul 不自动配置全部服务。
#忽略某个服务路由配置
zuul:
  ignored-services: myService

#忽略全部服务自动路由配置
zuul:
  ignored-services: "*"

默认情况下,Zuul 在请求路由时会过滤掉 HTTP 请求头信息中的一些敏感信息,防止这些敏感的头信息传递到下游外部服务器。但是如果我们使用安全框架,如 Spring Security、Apache Shiro 等,需要使用 Cookie 做登录和鉴权,这时可以配置 zuul.sensitiveHeaders 参数,该参数相当于配置一个黑名单,配置的值都是属于程序的敏感 header 信息,不允许传递到外部服务器。

sensitiveHeaders 设置的敏感头的默认值:

zuul:
  sensitiveHeaders: Cookie,Set-Cookie,Authorization

如果想自定义这些敏感头信息,可以设置如下:

#全局配置
zuul:
  sensitiveHeaders: Cookie

#针对某条路由配置
zuul:
  routes:
    myServiceId:
      path: /api/**
      serviceId: myService
      sensitiveHeaders: Cookie,Authorization

如果认为 header 信息都不敏感,希望下游服务器能够获取全部 header 信息,则可以设置 zuul.sensitiveHeaders 参数为空:

#全局配置
zuul:
  sensitiveHeaders: 
  
#针对某条路由配置
zuul:
  routes:
    myServiceId:
      path: /api/**
      serviceId: myService
      sensitiveHeaders: 

3、忽略 Headers 配置

除了能设置敏感 header 过滤外,还可以设置 zuul.ignoredHeaders 参数来忽略某些 header 信息。

zuul:
  ignoredHeaders: customHeader1,customHeader2

这里的 ignoredHeaders 参数和上面的 sensitiveHeaders 参数类似,都是不向下游传递某些 header 信息,不过理念不同:

  • zuul.sensitiveHeaders: 主要思想是防止数据泄漏。例如 Zuul 连接到外部下游应用程序时,不应将敏感标头(如授权令牌)发送到外部服务。
  • zuul.ignoredHeaders: 主要思想是删除特定的标头。Zuul可以充当两方之间的匿名者,并且完全忽略可能泄露有关系统数据的标头。

4、Zuul 常用配置参数

zuul 有一些常用参数需要配置,例如路由配置等,常用参数如下:

zuul:                                       #---zuul配置---
  prefix: /v1                               #为路由添加统一前缀
  retryable: false                          #是否开启重试机制
  add-host-header: true                     #在请求路由转发前为请求设置Host头信息
  ignored-services: a-service,b-service     #忽略的服务
  ignored-patterns: /**/api/**              #通配符表达式方式来忽略多个请求路径
  sensitiveHeaders:                         #全局配置是为空来覆盖默认敏感头信息,默认情况下zuul不会将Cookie,Set-Cookie,Authorization这三个头
                                            #传递到下游服务,如果需要传递,就需要忽略这些敏感头
  routes:                                   #---路由策略配置---
    myServiceId:                            #自定义zuul路由服务ID
      path: /api/**                         #自定义服务监听地址
      serviceId: myService                  #注册中心中的服务名称
      stripPrefix: true                     #是否移除前缀,例如设置为ture时,访问/api/aa那么将代理到服务的/aa接口 
      customSensitiveHeaders:               #是否对指定路由开启自定义敏感头

5、Hystrix 和 Ribbon 支持

设置 Ribbon 超时时间

ribbon:
  ReadTimeout: 15000
  ConnectTimeout: 5000
  SocketTimeout: 15000

设置 Hystrix 断路器超时时间

hystrix:
  command:
    myusers-service:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 15000

使用 ribbon.ConnectTimeout 参数创建请求连接的超时时间,当 ribbon.ConnectTimeout 的配置值小于 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 的配置值时,若出现请求超时的时候,会自动进行重试路由请求,如果依然失败,Zuul 会返回如下 JSON 信息给外部调用方。

如果 ribbon.ConnectTimeout 的配置值大于 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 的配置值时,当出现请求超时的时候不会进行重试,直接超时处理返回 timeout 的错误信息。

6、Zuul 中的 Hystrix 服务降级

Zuul 中默认引入了 Hystrix(断路器),当连接服务超时时候会进行服务降级,执行降级操作。这个服务降级可以指定只针对某些服务或者全部服务。

指定某服务降级设置:

class MyFallbackProvider implements FallbackProvider {

    @Override
    public String getRoute() {
        // 可以单独设置为 ServiceID 来指定只生效某个服务
        return "customers";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    private ClientHttpResponse response(final HttpStatus status) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return status;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return status.value();
            }

            @Override
            public String getStatusText() throws IOException {
                return status.getReasonPhrase();
            }

            @Override
            public void close() {
            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("fallback".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

全部服务降级设置:

class MyFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        // 设置为 “*” 即指定为全部服务
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable throwable) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("fallback".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

三、Zuul 对服务限制流量

Zuul 作为网关,经常要做一些限流策略来防止被恶意攻击。由于 Zuul 自身并没有提供限流功能,我们可以依赖第三方组件帮我们完成限流工作。

Zuul 限流组件有很多,比较常用是 spring-cloud-zuul-ratelimit 组件,由于 Zuul 是分布式的,所以多个实例间共享限流数据,ratelimit 该组件提供了多种存储访问数据量的方法,如将访问次数和量存入 Redis、Consul 或者 数据库以及 Bucket4j JCache 中,嘴常用的就是存入 Redis 中,所以这里以使用 Redis 作为介绍。

1、Maven 引入其依赖

一般情况下,我们不光会使用其 redis 作为值存储,所以共需要引入两个依赖:

<!--ratelimit-->
<dependency>
    <groupId>com.marcosbarbero.cloud</groupId>
    <artifactId>spring-cloud-zuul-ratelimit</artifactId>
    <version>LATEST</version>
</dependency>
<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、配置文件中设置限流配置

限流需要配置一些全局和针对某个服务的限流策略,更具体可以访问 spring-cloud-zuul-ratelimit 的 github 地址 了解更多信息,下面部分是常用参数:

zuul:
  ratelimit:                    #---zuul 限流配置---
    enabled: true               #是否启用限流
    key-prefix: your-prefix     #限流数据 key 前缀
    repository: redis           #限流数据存储方式
    behind-proxy: true          #
    add-response-headers: true  #
    default-policy-list:        #---全局默认限流策略---
      - limit: 10               #单位时间内允许访问的次数
        quota: 100              #单位时间内全部访问时间总和的限制(全部访问时间不能超过该时间)单位(s)
        refresh-interval: 10    #单位时间设置,超过该时间后刷新限制,默认60s,单位(s)
    policy-list:                #---针对某个服务单独设置限流策略---
      myService:              #zuul路由策略中配置的路由ID,不是 ServiceId
        - limit: 20             #该服务的单位时间内允许访问的次数
          quota: 100            #该服务单位时间内全部访问时间总和的限制
          refresh-interval: 30  #单位时间设置

上面就是 Zuul-Ratelimit 中对服务限速的设置,设置了全局限速设置为 10s 内每个服务只能访问 10 次。另外还单独对 myService 服务限制 30s 内只能访问 20 次。

四、Kubernetes 部署 Zuul 需要的环境

Kubernetes 下部署 Zuul 网关,需要在该环境下提前部署注册中心、配置中心等组件:

  • 注册中心: Eureka
  • 配置中心: SpringCloud Kubernetes Config
  • Redis: Rdis 缓存

1、注册中心

在 SpringCloud 微服务框架中,注册中心是一个必不可少的组件,全部微服务会注册到其中。Eureka 是我们常用的注册中心,Zuul 会从注册中心中拉取一份服务列表,并且动态配置路由规则。所以我们需要在 Kubernetes 环境下,提前布置好注册中心。如何在 Kubernetes 部署 Eureka 注册中心 在之前博博客中已经写过,这里不过多描述。

在 Kuberenetes 内部已经部署的 Eureka 的地址环境如下:

2、配置中心

在 SpringCloud 微服务框架中,我们需要一个配置中心来管理整个微服务的配置,并且实现动态刷新配置。我们常用的做法是部署 SpringCloud Config 组件,但是由于 SpringCloud Config 需要将配置存放于 Git 或者 SVN 中,这两种存储仓库都会有不可读取或者读取超时问题,动态通知可能还需要部署一个消息队列动来充当消息总线,还要保证队列的稳定性从而不影响全部服务的可用性,故而比较麻烦和需要保证可用性,这里不太推荐。

由于 SpringCloud 各个组件和服务都是部署在 Kubernetes 环境下,而 Kubernetes 自己也有个配置管理的 ConfigMap。考虑到这点 SpringCloud 也有 SpringCloud Kubernetes Config项目来完成从 ConfigMap 动态读取配置信息,只要 Kubernetes 整个集群不宕机,那么配置就可以得到保障,这是目前比较推荐的组件。

关于 如何使用 SpringCloud Config 完成动态配置 之前文章中也有写,需要了解的可以查看下。当然,如果你使用的是 Apollo 配置中心也是可以。

3、Redis 缓存

由于分布式环境,限速需要将访问数据共享给各个客户端,将这些数据存入 Redis 中,所以需要在提前部署 Redis 在 Kuberntes 集群内或者集群外都可。

五、Zuul 示例项目

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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
    </parent>

    <groupId>club.mydlq</groupId>
    <artifactId>springcloud-zuul-demo</artifactId>
    <version>0.0.1</version>
    <name>springcloud-zuul-demo</name>
    <description>springboot zuul 1.x demo project</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--eureka-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--zuul-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <!--rate limit-->
        <dependency>
            <groupId>com.marcosbarbero.cloud</groupId>
            <artifactId>spring-cloud-zuul-ratelimit</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <!--redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <!--springcloud kubernetes config-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-config</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <!--SpringCloud 版本管理-->
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2、服务降级

Zuul 中默认引入了 Hystrix 断路器,当服务熔断时会进行服务降级,这里可以配置服务降级类。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.hystrix.exception.HystrixTimeoutException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class ServiceFallbackProvider implements FallbackProvider {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String getRoute() {
        // 可以单独指定 ServiceID,也可以设置“*”全部服务
        return "springboot-helloworld";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    private ClientHttpResponse response(final HttpStatus status) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() {
                return status;
            }

            @Override
            public int getRawStatusCode() {
                return status.value();
            }

            @Override
            public String getStatusText() {
                return status.getReasonPhrase();
            }

            @Override
            public void close() {
            }

            @Override
            public InputStream getBody() throws JsonProcessingException {
                Map<String,String> map = new HashMap<>();
                map.put("code", "501");
                map.put("message", "后台服务异常");
                String json = objectMapper.writeValueAsString(map);
                return new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

3、配置文件

这里需要配置 application.yml 和 bootstrap.yml 两个文件,SpirngBoot 在加载时候会先加载 bootstrap.yml 内容然后再加载 application.yml。

在这里 application.yml 和 bootstrap.yml 两个文件分别有不同的作用:

  • application: 这里一般用于 Redis、Log、Eureka、Hystrix、ribbon、ZuulZuul-Ratelimit 等的配置,在将 Zuul 部署到 Kubernetes 前,会将此配置文件提前存入 Kubernetes 的 ConfigMap 中。
  • bootstrap: 设置 SpringCloud Kubernetes Config 配置参数,动态读取上面配置的 Kubernetes 下的 ConfigMap 的参数值。

在 applicaion.yml 中还存在路由规则的配置,不过一般情况下路由规则配置会存入 Kubernetes 下的 ConfigMap 中,我们可以配合 SpringCloud Kubernetes Config 组件对其进行动态配置,使路由规则快速生效,这里为了演示方便,下面将配置一条路由规则,在本人 Kubernetes 集群中已经存在的一个供测试的 HelloWorld 服务,这里便设置路由规则来代理该服务。

application.yml

#Redis
spring:
  redis:
    #host: 192.168.2.11
    #port: 30379
    host: redis-master.mydlqcloud
    port: 6379
    password: 123456

#Log Config
logging:
  path: /opt/logs/

#Eureka Config
eureka:
  client:
    service-url:
      #defaultZone: http://192.168.2.11:31011/eureka/
      defaultZone: http://eureka-0.eureka.mydlqcloud:8080/eureka/,http://eureka-1.eureka.mydlqcloud:8080/eureka/,http://eureka-2.eureka.mydlqcloud:8080/eureka/

#Ribbon Timeout Config
ribbon:
  ReadTimeout: 2000
  ConnectTimeout: 5000
  SocketTimeout: 2000

#Hystrix Config
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10000

#Zuul Config
zuul:
  retryable: false
  add-host-header: false
  prefix: /v1
  routes:
    helloworld:
      path: /helloworld/**
      serviceId: springboot-helloworld
  ratelimit:
    enabled: true
    key-prefix: retelimit
    repository: redis
    behind-proxy: true
    add-response-headers: true
    # globle retelimit config
    default-policy-list:
      - limit: 10
        quota: 100
        refresh-interval: 10
    # single service config
    policy-list:
      helloworld:
        - limit: 5
          quota: 100
          refresh-interval: 10

bootstrap.yml

#Base Config
server:
  port: 8080
spring:
  application:
    name: springcloud-zuul-demo
  cloud:
    kubernetes:
      reload:
        enabled: true
        mode: polling
        period: 5000
        strategy: refresh
        monitoring-secrets: true
      config:
        enabled: true
        enableApi: true
        sources:
          - namespace: mydlqcloud
            name: springcloud-zuul-config
#Actuator Config
management:
  server:
    port: 8080
  endpoint:
    restart:
      enabled: true
    health:
      show-details: always
  endpoints:
    web:
      exposure:
        include: "*"

4、启动类

启动类上需要加上俩个注解来启用 Zuul 和服务发现:

  • @EnableZuulProxy: 启用 Zuul。
  • @EnableDiscoveryClient: 启用 SpringCloud 服务发现,这里会开启 Eureka。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

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

}

六、构建 Docker 镜像

1、执行 Maven 命令将 Zuul 打包成 Jar

首先执行 Maven 命令,将项目编译成一个可执行 JAR。

$ mvn clean install

2、准备 Dockerfile

创建构建 Docker 镜像需要的 Dockerfile 文件,放置到项目 根目录 中。脚本会在执行构建镜像时,将 Maven 编译的 JAR 复制到镜像内部。

Dockerfile:

FROM openjdk:8u222-jre-slim
VOLUME /tmp
ADD target/*.jar app.jar
RUN sh -c 'touch /app.jar'
ENV JAVA_OPTS="-Duser.timezone=Asia/Shanghai"
ENV APP_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ]

上面设置了两个变量,分别是:

  • JAVA_OPTS: Java JVM 启动参数变量,这里需要在这里加一个时区参数。
  • APP_OPTS: Spring 容器启动参数变量,方便后续操作时能通过此变量配置 Spring 参数。

3、构建与推送镜像

执行 Docker Build 命令构建 Docker 镜像,构建完成支线 Docker push 命令将镜像推送到镜像仓库。

# 构建镜像
$ docker build -t mydlqclub/springcloud-zuul:0.0.1 .

# 推送镜像
$ docker push mydlqclub/springcloud-zuul:0.0.1

七、Kubernetes 部署 Zuul

1、创建应用权限 RBAC

由于程序需要读取 ConfigMap 中的配置,需要一定的权限,这里提前创建一个 RBAC 对象来供程序绑定以获取读取的权限,下面是 RBAC 对象的部署文件。

  • 注意:需要修改下面的全部 Namespace 参数为你自己 Kubernetes 集群的 Namespace 名称。

zuul-rbac.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: springcloud-zuul
  namespace: mydlqcloud
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: springcloud-zuul
subjects:
  - kind: ServiceAccount
    name: springcloud-zuul
    namespace: mydlqcloud
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

在 Kubernetes 中部创建 RBAC 对象

  • -n:创建应用到指定的 Namespace 中。
$ kubectl apply -f zuul-rbac.yaml -n mydlqcloud

2、ConfigMap 应用配置文件

创建 application-configmap.yaml 配置文件,将示例项目中 application.yml 配置文件中的配置项复制到 ConfigMap 中,应用中会通过 SpringCloud Kubernetes Config 组件读取 ConfigMap 中的配置。

application-configmap.yaml

kind: ConfigMap
apiVersion: v1
metadata:
  name: springcloud-zuul-config
data:
  application.yaml: |-
    #Redis
    spring:
      redis:
        #host: 192.168.2.11
        #port: 30379
        host: redis-master.mydlqcloud
        port: 6379
        password: 123456
    
    #Log Config
    logging:
      path: /opt/logs/
    
    #Eureka Config
    eureka:
      client:
        service-url:
          #defaultZone: http://192.168.2.11:31011/eureka/
          defaultZone: http://eureka-0.eureka.mydlqcloud:8080/eureka/,http://eureka-1.eureka.mydlqcloud:8080/eureka/,http://eureka-2.eureka.mydlqcloud:8080/eureka/
    
    #Ribbon Timeout Config
    ribbon:
      ReadTimeout: 2000
      ConnectTimeout: 5000
      SocketTimeout: 2000
    
    #Hystrix Config
    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 10000
    
    #Zuul Config
    zuul:
      retryable: false
      add-host-header: false
      prefix: /v1
      routes:
        helloworld:
          path: /helloworld/**
          serviceId: springboot-helloworld
      ratelimit:
        enabled: true
        key-prefix: retelimit
        repository: redis
        behind-proxy: true
        add-response-headers: true
        # globle retelimit config
        default-policy-list:
          - limit: 10
            quota: 100
            refresh-interval: 10
        # single service config
        policy-list:
          helloworld:
            - limit: 5
              quota: 100
              refresh-interval: 10

在 Kubernetes 中部署应 ConfigMap 对象

  • -n:创建应用到指定的 Namespace 中。
$ kubectl apply -f application-configmap.yaml -n mydlqcloud

3、Kubernetes 应用部署文件

Zuul 要部署在 Kubernetes 环境下,需要部署为无状态应用,即需要将 Zuul 创建一个 Deployment 对象,下面是 Zuul 的部署文件。

springcloud-zuul.yaml

apiVersion: v1
kind: Service
metadata:
  name: springcloud-zuul
spec:
  type: NodePort
  ports:
    - name: server
      nodePort: 31085
      port: 8080
      targetPort: 8080
    - name: management
      nodePort: 31086
      port: 8081
      targetPort: 8081
  selector:
    app: springcloud-zuul
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: springcloud-zuul
  labels:
    app: springcloud-zuul
spec:
  replicas: 1
  selector:
    matchLabels:
      app: springcloud-zuul
  template:
    metadata:
      name: springcloud-zuul
      labels:
        app: springcloud-zuul
    spec:
      restartPolicy: Always
      containers:
        - name: springcloud-zuul
          image: mydlqclub/springcloud-zuul:0.0.1
          #imagePullPolicy: Always
          ports:
            - name: server
              containerPort: 8080
            - name: management
              containerPort: 8081
          resources:
            limits:
              memory: 2048Mi
              cpu: 2000m
            requests:
              memory: 2048Mi
              cpu: 2000m
          readinessProbe:
            initialDelaySeconds: 20
            periodSeconds: 5
            timeoutSeconds: 10
            failureThreshold: 5
            httpGet:
              path: /actuator/health
              port: 8081
          livenessProbe:
            initialDelaySeconds: 60
            periodSeconds: 5
            timeoutSeconds: 5
            failureThreshold: 3
            httpGet:
              path: /actuator/health
              port: 8081
          volumeMounts:
            - name: log
              mountPath: /opt/logs
      volumes:
        - name: log
          hostPath:
            type: DirectoryOrCreate
            path: /data/apps/logs

在 Kubernetes 中部署 Zuul 应用

  • -n:创建应用到指定的 Namespace 中。
$ kubectl apply -f springcloud-zuul.yaml -n mydlqcloud

八、测试部署的应用接口

将 Zuul 部署到 Kubernetes 后,我们可以通过 Kubernetes 集群地址 192.168.2.11 加上配置的 Service 的 NodePort 端口 31085 访问 Zuul 中 helloworld 服务,所以这里输入地址 http://192.168.2.11:31085 加上设置的前缀 /v1 与测试服务 helloworld 的服务名 helloworld 进行测试。

1、测试网关路由

输入地址 http://192.168.2.11:31085/v1/helloworld 访问 springboot-helloworld 服务,返回结果如下:

HelloWorld!

可以看到 Zuul 网关生效,能成功代理 springboot-helloworld 服务接口。

2、测试限流

Zuul 中引用了 zuul-ratelimit 组件用于限速,上面配置 helloworld 服务,在 10s 内限制访问 5 次,连续访问 5 次地址 http://192.168.2.11:31085/v1/helloworld 访问 springboot-helloworld 服务,返回结果如下:

HelloWorld!

然后输入第 6 次时,返回信息如下:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sat Oct 05 20:39:11 CST 2019
There was an unexpected error (type=Too Many Requests, status=429).
429 TOO_MANY_REQUESTS

可以看到抛出限速异常以及状态码 426,说明限速已经生效。

3、测试动态路由

改变之前存入 ConfigMap 中的路由配置:

zuul:
  prefix: /v1
  routes:
    helloworld:
      path: /test/**
      serviceId: springboot-helloworld

等待几秒钟后,再次输入地址 http://192.168.2.11:31085/v1/test 访问 springboot-helloworld 服务,查看改变后是否能生效,返回结果如下:

HelloWorld!

可以看到,可以成功的访问到 springboot-helloworld 服务,动态路由配置能及时生效。


好了,到此 Kubernetes 部署 Zuul 结束,在 Kubernetes 中集群外提供服务一般我们会部署 Ingress Controller 来代理 Kubernetes 内部服务。有了 Ingress Controller 就可以喷子 Zuul 的 Ingress 路由规则,让外部流量流入 Zuul 服务。由于不同的 Ingress Controller 有不同的 Ingress 路由配置,这里就不过多描述。

—END—

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
微服务是什么?微服务是用于构建应用程序的架构风格,一个大的系统可由一个或者多个微服务组成,微服务架构可将应用拆分成多个核心功能,每个功能都被称为一项服务,可以单独构建和部署,这意味着各项服务在工作和出现故障的时候不会相互影响。为什么要用微服务?单体架构下的所有代码模块都耦合在一起,代码量大,维护困难,想要更新一个模块的代码,也可能会影响其他模块,不能很好的定制化代码。微服务中可以有java编写、有Python编写的,他们都是靠restful架构风格统一成一个系统的,所以微服务本身与具体技术无关、扩展性强。大型电商平台微服务功能图为什么要将SpringCloud项目部署到k8s平台?SpringCloud只能用在SpringBoot的java环境中,而kubernetes可以适用于任何开发语言,只要能被放进docker的应用,都可以在kubernetes上运行,而且更轻量,更简单。SpringCloud很多功能都跟kubernetes重合,比如服务发现,负载均衡,配置管理,所以如果把SpringCloud部署到k8s,那么很多功能可以直接使用k8s原生的,减少复杂度。Kubernetes作为成熟的容器编排工具,在国内外很多公司、世界500强等企业已经落地使用,很多中小型公司也开始把业务迁移到kubernetes中。kubernetes已经成为互联网行业急需的人才,很多企业都开始引进kubernetes技术人员,实现其内部的自动化容器云平台的建设。对于开发、测试、运维、架构师等技术人员来说k8s已经成为的一项重要的技能,下面列举了国内外在生产环境使用kubernetes的公司: 国内在用k8s的公司:阿里巴巴、百度、腾讯、京东、360、新浪、头条、知乎、华为、小米、富士康、移动、银行、电网、阿里云、青云、时速云、腾讯、优酷、抖音、快手、美团等国外在用k8s的公司:谷歌、IBM、丰田、iphone、微软、redhat等整个K8S体系涉及到的技术众多,包括存储、网络、安全、监控、日志、DevOps、微服务等,很多刚接触K8S的初学者,都会感到无从下手,为了能让大家系统地学习,克服这些技术难点,推出了这套K8S架构师课程。Kubernetes的发展前景 kubernetes作为炙手可热的技术,已经成为云计算领域获取高薪要掌握的重要技能,在招聘网站搜索k8s,薪资水平也非常可观,为了让大家能够了解k8s目前的薪资分布情况,下面列举一些K8S的招聘截图: 讲师介绍:  先超容器云架构师、IT技术架构师、DevOps工程师,曾就职于世界500强上市公司,拥有多年一线运维经验,主导过上亿流量的pv项目的架构设计和运维工作;具有丰富的在线教育经验,对课程一直在改进和提高、不断的更新和完善、开发更多的企业实战项目。所教学员遍布京东、阿里、百度、电网等大型企业和上市公司。课程学习计划 学习方式:视频录播+视频回放+全套源码笔记 教学服务:模拟面试、就业指导、岗位内推、一对一答疑、远程指导 VIP终身服务:一次购买,终身学习课程亮点:1. 学习方式灵活,不占用工作时间:可在电脑、手机观看,随时可以学习,不占用上班时间2.老师答疑及时:老师24小时在线答疑3. 知识点覆盖全、课程质量高4. 精益求精、不断改进根据学员要求、随时更新课程内容5. 适合范围广,不管你是0基础,还是拥有工作经验均可学习:0基础1-3年工作经验3-5年工作经验5年以上工作经验运维、开发、测试、产品、前端、架构师其他行业转行做技术人员均可学习课程部分项目截图   课程大纲 k8s+SpringCloud全栈技术:基于世界500强的企业实战课程-大纲第一章 开班仪式老师自我介绍、课程大纲介绍、行业背景、发展趋势、市场行情、课程优势、薪资水平、给大家的职业规划、课程学习计划、岗位内推第二章 kubernetes介绍Kubernetes简介kubernetes起源和发展kubernetes优点kubernetes功能kubernetes应用领域:在大数据、5G、区块链、DevOps、AI等领域的应用第三章  kubernetes中的资源对象最小调度单元Pod标签Label和标签选择器控制器Replicaset、Deployment、Statefulset、Daemonset等四层负载均衡器Service第四章 kubernetes架构和组件熟悉谷歌的Borg架构kubernetes单master节点架构kubernetes多master节点高可用架构kubernetes多层架构设计原理kubernetes API介绍master(控制)节点组件:apiserver、scheduler、controller-manager、etcdnode(工作)节点组件:kube-proxy、coredns、calico附加组件:prometheus、dashboard、metrics-server、efk、HPA、VPA、Descheduler、Flannel、cAdvisor、Ingress     Controller。第五章 部署多master节点的K8S高可用集群(kubeadm)第六章 带你体验kubernetes可视化界面dashboard在kubernetes部署dashboard通过token令牌登陆dashboard通过kubeconfig登陆dashboard限制dashboard的用户权限在dashboard界面部署Web服务在dashboard界面部署redis服务第七章 资源清单YAML文件编写技巧编写YAML文件常用字段,YAML文件编写技巧,kubectl explain查看帮助命令,手把手教你创建一个Pod的YAML文件第八章 通过资源清单YAML文件部署tomcat站点编写tomcat的资源清单YAML文件、创建service发布应用、通过HTTP、HTTPS访问tomcat第九章  kubernetes Ingress发布服务Ingress和Ingress Controller概述Ingress和Servcie关系安装Nginx Ingress Controller安装Traefik Ingress Controller使用Ingress发布k8s服务Ingress代理HTTP/HTTPS服务Ingress实现应用的灰度发布-可按百分比、按流量分发第十章 私有镜像仓库Harbor安装和配置Harbor简介安装HarborHarbor UI界面使用上传镜像到Harbor仓库从Harbor仓库下载镜像第十一章 微服务概述什么是微服务?为什么要用微服务?微服务的特性什么样的项目适合微服务?使用微服务需要考虑的问题常见的微服务框架常见的微服务框架对比分析第十二章 SpringCloud概述SpringCloud是什么?SpringCloudSpringBoot什么关系?SpringCloud微服务框架的优缺点SpringCloud项目部署到k8s的流程第十三章 SpringCloud组件介绍服务注册与发现组件Eureka客户端负载均衡组件Ribbon服务网关Zuul熔断器HystrixAPI网关SpringCloud Gateway配置中心SpringCloud Config第十四章 将SpringCloud项目部署到k8s平台的注意事项如何进行服务发现?如何进行配置管理?如何进行负载均衡?如何对外发布服务?k8s部署SpringCloud项目的整体流程第十五章 部署MySQL数据库MySQL简介MySQL特点安装部署MySQL在MySQL数据库导入数据对MySQL数据库授权第十六章 将SpringCLoud项目部署到k8s平台SpringCloud的微服务电商框架安装openjdk和maven修改源代码、更改数据库连接地址通过Maven编译、构建、打包源代码在k8s中部署Eureka组件在k8s中部署Gateway组件在k8s中部署前端服务在k8s中部署订单服务在k8s中部署产品服务在k8s中部署库存服务第十七章 微服务的扩容和缩容第十八章 微服务的全链路监控什么是全链路监控?为什么要进行全链路监控?全链路监控能解决哪些问题?常见的全链路监控工具:zipkin、skywalking、pinpoint全链路监控工具对比分析第十九章 部署pinpoint服务部署pinpoint部署pinpoint agent在k8s中重新部署带pinpoint agent的产品服务在k8s中重新部署带pinpoint agent的订单服务在k8s中重新部署带pinpoint agent的库存服务在k8s中重新部署带pinpoint agent的前端服务在k8s中重新部署带pinpoint agent的网关eureka服务Pinpoint UI界面使用第二十章 基于Jenkins+k8s+harbor等构建企业级DevOps平台第二十一章 基于Promethues+Alert+Grafana搭建企业级监控系统第二十二章 部署智能化日志收集系统EFK 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值