前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用Kubernetes和Spring Boot从头开始构建弹性微服务

用Kubernetes和Spring Boot从头开始构建弹性微服务

作者头像
February
修改2018-11-29 15:03:33
2K0
修改2018-11-29 15:03:33
举报
文章被收录于专栏:技术翻译技术翻译

除了最近关于Kubernetes的所有讨论以及你是否应该使用Docker化数据库之外,今天我想向您展示为什么当可扩展性和弹性是您的架构中的一个重要要求时,这两件事可能是很好的解决方案。

这里的秘诀很简单:在Kubernetes上部署应用程序和数据库,以及NoSQL和Spring Data的组合。

为什么选择NoSQL和Spring数据?

使用文档数据库,可以避免大量不必要的连接,因为整个结构存储在单个文档中。因此,随着数据的增长,它自然会比关系模型执行得更快。

如果您使用的是任何JVM语言,那么Spring Data可能是您非常熟悉的。因此,即使没有任何先前的知识,您也可以快速启动NoSQL。

为何选择Kubernetes?

Kubernetes允许您在与云无关的环境中扩展和缩小无状态应用程序。在最近的几个版本中,K8还增加了运行状态应用程序(如数据库)的能力,这也是现在如此热门话题的原因之一。

我在之前的博客文章中展示了如何在K8上部署Couchbase,以及如何通过轻松扩展和缩小来使其“弹性”。如果您还没有阅读,请花几分钟时间浏览视频记录,因为这是我们将要讨论的重要部分。

创建用户配置文件微服务

在大多数系统中,用户(以及所有相关实体)是最常访问的数据。因此,随着数据的增长,系统的第一部分必须经过某种优化。

添加缓存层是我们可以想到的第一种优化类型。但是,它还不是“最终解决方案”。如果您有数千个用户,或者您需要将用户相关实体也存储在内存中,事情可能会变得更复杂一些。

管理大量用户配置文件是众所周知的适合文档数据库的。例如,只需看一下Pokémon Go用例。因此,构建高度可扩展且具有弹性的用户配置文件服务似乎是一个足以证明如何设计高度可扩展的微服务的挑战。

你需要什么

  • Couchbase
  • JDK和Lombok的EclipseIntellij插件
  • Maven
  • Kubernetes集群 - 我在AWS上的3个节点上运行此示例(我不建议使用minikube)。如果您不知道如何设置,请观看此视频

代码

您可以在此处克隆整个项目。

让我们从创建名为User的主要实体开始:

代码语言:javascript
复制
@Document
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class User extends BasicEntity {
    @NotNull
    @Id
    private String id;
    @NotNull
    @Field
    private String name;
    @Field
    private Address address;
    @Field
    private List<Preference> preferences = new ArrayList<>();
    @Field
    private List<String> securityRoles = new ArrayList<>();
}

在这个实体中,我们有两个重要的属性:

  • securityRoles:用户可以在系统中播放的所有角色。
  • 首选项:用户可能具有的所有可能首选项,例如语言,通知,货币等。

现在,让我们来看看我们的存储库。在我们使用Spring Data时,您可以在此处使用它的所有功能:

代码语言:javascript
复制
@N1qlPrimaryIndexed
@ViewIndexed(designDoc = "user")
public interface UserRepository extends CouchbasePagingAndSortingRepository<User, String> {
    List<User> findByName(String name);
}

我们还实现了另外两种方法:

代码语言:javascript
复制
@Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and ANY preference IN " +
            " preferences SATISFIES preference.name = $1 END")
List<User> findUsersByPreferenceName(String name);
@Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and meta().id = $1 and ARRAY_CONTAINS(securityRoles, $2)")
User hasRole(String userId, String role);
  • hasRole:检查用户是否具有指定的角色:
  • findUsersByPreferencyName:如名称所示,它查找包含给定首选项的所有用户。

请注意,我们在上面的代码中使用了N1QL语法,因为它使查询比使用普通JQL更简单。

此外,您可以运行所有测试以确保一切正常:

不要忘记使用数据库的正确凭据更改应用程序属性:

代码语言:javascript
复制
spring.couchbase.bootstrap-hosts=localhost
spring.couchbase.bucket.name=test
spring.couchbase.bucket.password=couchbase
spring.data.couchbase.auto-index=true

为了测试我们的微服务,我添加了一些Restful端点:

代码语言:javascript
复制
@RestController
@RequestMapping("/api/user")
public class UserServiceController {
    @Autowired
    private UserService userService;
    @RequestMapping(value = "/{id}", method = GET, produces = APPLICATION_JSON_VALUE)
    public User findById(@PathParam("id") String id) {
        return userService.findById(id);
    }
    @RequestMapping(value = "/preference", method = GET, produces = APPLICATION_JSON_VALUE)
    public List<User> findPreference(@RequestParam("name") String name) {
        return userService.findUsersByPreferenceName(name);
    }
    @RequestMapping(value = "/find", method = GET, produces = APPLICATION_JSON_VALUE)
    public List<User> findUserByName(@RequestParam("name") String name) {
        return userService.findByName(name);
    }
    @RequestMapping(value = "/save", method = POST, produces = APPLICATION_JSON_VALUE)
    public User findUserByName(@RequestBody User user) {
        return userService.save(user);
    }
}

Docker化您的微服务

首先,更改application.properties以从环境变量获取连接凭据:

代码语言:javascript
复制
spring.couchbase.bootstrap-hosts=${COUCHBASE_HOST}
spring.couchbase.bucket.name=${COUCHBASE_BUCKET}
spring.couchbase.bucket.password=${COUCHBASE_PASSWORD}
spring.data.couchbase.auto-index=true

现在我们可以创建我们的Dockerfile:

代码语言:javascript
复制
FROM openjdk:8-jdk-alpine
VOLUME /tmp
MAINTAINER Denis Rosa <denis.rosa@couchbase.com>
ARG JAR_FILE
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

然后,我们在Docker Hub上构建并发布我们的图像:

  • 创建你的形象:
代码语言:javascript
复制
./mvnw install dockerfile:build -DskipTests
  • 从命令行docker login 登录Docker Hub
  • 让我们抓住最近创建的 docker images 图像的imageId:
  • 使用imageId创建新标记:
代码语言:javascript
复制
//docker tag YOUR_IMAGE_ID YOUR_USER/REPO_NAME
docker tag 3f9db98544bd deniswsrosa/kubernetes-starter-kit

最后,推送你的图像docker push deniswsrosa/kubernetes-starter-kit

您的图像现在应该可以在Docker Hub上获得:

配置数据库

在这里写了一篇关于它的文章,但为了简短起见,只需在kubernetes目录中运行以下命令即可

代码语言:javascript
复制
./rbac/cluster_role.sh
kubectl create -f secret.yaml
kubectl create -f operator.yaml
kubectl create -f couchbase-cluster.yaml

过了一会儿,我们数据库的所有3个实例都应该运行:

让我们将Web控制台的端口转发到本地计算机:

代码语言:javascript
复制
kubectl port-forward cb-example-0000 8091:8091

现在我们可以访问http:// localhost:8091的Web控制台。您可以使用用户名Administrator和密码密码登录

使用以下属性转到安全性 - >添加用户

  • 用户名: couchbase-sample
  • 全名: couchbase-sample
  • 密码: couchbase-sample
  • 验证密码: couchbase-sample
  • 角色:根据下图:

OBS:在生产环境中,请不要将您的应用程序添加为管理员。

部署您的微服务

首先,让我们创建一个Kubernetes秘密,我们将存储密码以连接到我们的数据库:

代码语言:javascript
复制
apiVersion: v1
kind: Secret
metadata:
  name: spring-boot-app-secret
type: Opaque
data:
apiVersion: v1
kind: Secret
metadata:
  name: spring-boot-app-secret
type: Opaque
data:
  bucket_password: Y291Y2hiYXNlLXNhbXBsZQ== #couchbase-sample in base64

运行以下命令以创建密码:

代码语言:javascript
复制
kubectl create -f spring-boot-app-secret.yaml

spring-boot-app.yaml文件负责部署我们的应用程序。我们来看看它的内容:

代码语言:javascript
复制
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: spring-boot-deployment
spec:
  selector:
    matchLabels:
      app: spring-boot-app
  replicas: 2 # tells deployment to run 2 pods matching the template
  template: # create pods using pod definition in this template
    metadata:
      labels:
        app: spring-boot-app
    spec:
      containers:
      - name: spring-boot-app
        image: deniswsrosa/kubernetes-starter-kit
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
          name: server
        - containerPort: 8081
          name: management
        env:
        - name: COUCHBASE_PASSWORD
          valueFrom:
            secretKeyRef:
              name: spring-boot-app-secret
              key: bucket_password
        - name: COUCHBASE_BUCKET
          value: couchbase-sample
        - name: COUCHBASE_HOST
          value: cb-exampleapiVersion : apps / v1beta1

我想强调一下这个文件的一些重要部分:

  • 副本:2 - > Kubernetes将启动我们的应用程序的2个实例
  • image:deniswsrosa / kubernetes-starter-kit - >我们之前创建的码头图像。
  • containers:name: - >这是我们定义运行应用程序的容器的名称的地方。每当要定义应运行的实例数,自动扩展策略,负载平衡等时,您将在Kubernetes中使用此名称。
  • env: - >这是我们定义应用程序的环境变量的地方。请注意,我们也指的是我们之前创建的秘密。

运行以下命令来部署我们的应用程序:

代码语言:javascript
复制
kubectl create -f spring-boot-app.yaml

几秒钟后,您会注意到应用程序的两个实例都已在运行:

最后,让我们将微服务暴露给外界。如何做到有几十种不同的可能性。在我们的例子中,让我们简单地创建一个Load Balancer:

代码语言:javascript
复制
apiVersion: v1
kind: Service
metadata:
  name: spring-boot-load-balancer
spec:
  ports:
  - port: 8080
    targetPort: 8080
    name: http
  - port: 8081
    targetPort: 8081
    name: management
  selector:
    app: spring-boot-app
  type: LoadBalancer

运行kubectl create -f spring-boot-load-balancer.yaml 命令以创建负载均衡器:

负载均衡器需要几分钟才能启动并将流量重定向到我们的pod。您可以运行以下命令来检查其状态:

代码语言:javascript
复制
kubectl describe service spring-boot-load-balancer

如上图所示,我们的负载均衡器可在ad84a916d65ad11e884a20266aaa53c9-1223617270.us-west-2.elb.amazonaws.com上访问,而targetPort 8080将流量重定向到两个端点:10.2.1.6:8080和10.2. 2.7:8080

最后,我们可以访问我们的应用程序并开始向它发送请求:

  • 插入新用户:
  • 正在搜索用户:

弹性怎么样?

这是事情变得非常有趣的地方。如果我们需要扩展整个微服务怎么办?假设黑色星期五即将到来,我们需要准备我们的基础设施,以支持大量用户访问我们的网站。那么,这是一个容易解决的问题:

  • 要扩展我们的应用程序,我们只需要更改spring-boot-app.yaml 文件中的副本数量。
代码语言:javascript
复制
...
spec:
  selector:
    matchLabels:
      app: spring-boot-app
  replicas: 6 # tells deployment to run 6 pods matching the template
  template: # create pods using pod definition in this template
    metadata:
      labels:
...

然后,运行以下命令:

代码语言:javascript
复制
kubectl replace -f spring-boot-app.yaml

有什么遗漏吗?是的。我们的数据库怎么样?我们也应该扩展它:

  • 更改couchbase-cluster.yaml文件中的size属性:
代码语言:javascript
复制
...
enableIndexReplica: false
  servers:
    - size: 6
      name: all_services
      services:
        - data
        - index
...

最后,运行以下命令:

代码语言:javascript
复制
kubectl replace -f couchbase-cluster.yaml

我该如何缩小它?

按比例缩小就像放大一样简单; 你只需要更改couchbase-cluster.yamlspring-boot-app.yaml

  • couchbase-cluster.yaml
代码语言:javascript
复制
...
      enableIndexReplica: false
  servers:
    - size: 1
      name: all_services
      services:
        - data
        - index
...
  • spring-boot-app.yaml
代码语言:javascript
复制
...
spec:
  selector:
    matchLabels:
      app: spring-boot-app
  replicas: 1 
  template:
    metadata:
      labels:
...

并运行以下命令:

代码语言:javascript
复制
kubectl replace -f couchbase-cluster.yaml
kubectl replace -f spring-boot-app.yaml

Kubernetes上的Auto-Scaling微服务

我将在本文的第2部分深入探讨这个主题。在此期间,您可以查看有关pod autoscaling的视频。

排除Kubernetes部署故障

如果您的Pod无法启动,有很多方法可以解决问题。在下面的情况中,两个应用程序都无法启动:

由于它们是部署的一部分,让我们描述部署以尝试了解正在发生的事情:

代码语言:javascript
复制
kubectl describe deployment spring-boot-deployment

嗯,在这种情况下没有什么是真正相关的。让我们看看其中一个pod的日志:

代码语言:javascript
复制
kubectl log spring-boot-deployment-74649f869d-74sq8

明白了!应用程序没有启动,因为我们忘了在Couchbase上创建用户。只需创建用户,pod就会在几秒钟内启动:

结论

数据库是有状态的应用程序,扩展它们并不像扩展无状态应用程序那样快(可能永远不会),但是如果你需要建立一个真正有弹性的架构,你应该计划扩展基础架构的所有组件。否则,你只是在其他地方制造瓶颈。

在本文中,我试图展示一个关于如何使Kubernetes上的应用程序和数据库具有弹性的小介绍。但是,它还不是一个可用于生产的架构。还有很多其他事情要考虑,我将在即将发表的文章中讨论其中一些问题。

原文标题《Building Elastic Microservices With Kubernetes and Spring Boot From the Ground Up》

作者:enis W S Rosa

译者:February

不代表云加社区观点,更多详情请查看原文链接

本文系外文翻译,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系外文翻译前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么选择NoSQL和Spring数据?
  • 为何选择Kubernetes?
  • 创建用户配置文件微服务
    • 你需要什么
    • 代码
      • Docker化您的微服务
        • 配置数据库
          • 部署您的微服务
            • 弹性怎么样?
              • 我该如何缩小它?
              • Kubernetes上的Auto-Scaling微服务
                • 排除Kubernetes部署故障
                • 结论
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档