除了最近关于Kubernetes的所有讨论以及你是否应该使用Docker化数据库之外,今天我想向您展示为什么当可扩展性和弹性是您的架构中的一个重要要求时,这两件事可能是很好的解决方案。
这里的秘诀很简单:在Kubernetes上部署应用程序和数据库,以及NoSQL和Spring Data的组合。
使用文档数据库,可以避免大量不必要的连接,因为整个结构存储在单个文档中。因此,随着数据的增长,它自然会比关系模型执行得更快。
如果您使用的是任何JVM语言,那么Spring Data可能是您非常熟悉的。因此,即使没有任何先前的知识,您也可以快速启动NoSQL。
Kubernetes允许您在与云无关的环境中扩展和缩小无状态应用程序。在最近的几个版本中,K8还增加了运行状态应用程序(如数据库)的能力,这也是现在如此热门话题的原因之一。
我在之前的博客文章中展示了如何在K8上部署Couchbase,以及如何通过轻松扩展和缩小来使其“弹性”。如果您还没有阅读,请花几分钟时间浏览视频记录,因为这是我们将要讨论的重要部分。
在大多数系统中,用户(以及所有相关实体)是最常访问的数据。因此,随着数据的增长,系统的第一部分必须经过某种优化。
添加缓存层是我们可以想到的第一种优化类型。但是,它还不是“最终解决方案”。如果您有数千个用户,或者您需要将用户相关实体也存储在内存中,事情可能会变得更复杂一些。
管理大量用户配置文件是众所周知的适合文档数据库的。例如,只需看一下Pokémon Go用例。因此,构建高度可扩展且具有弹性的用户配置文件服务似乎是一个足以证明如何设计高度可扩展的微服务的挑战。
您可以在此处克隆整个项目。
让我们从创建名为User的主要实体开始:
@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<>();
}
在这个实体中,我们有两个重要的属性:
现在,让我们来看看我们的存储库。在我们使用Spring Data时,您可以在此处使用它的所有功能:
@N1qlPrimaryIndexed
@ViewIndexed(designDoc = "user")
public interface UserRepository extends CouchbasePagingAndSortingRepository<User, String> {
List<User> findByName(String name);
}
我们还实现了另外两种方法:
@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);
请注意,我们在上面的代码中使用了N1QL语法,因为它使查询比使用普通JQL更简单。
此外,您可以运行所有测试以确保一切正常:
不要忘记使用数据库的正确凭据更改应用程序属性:
spring.couchbase.bootstrap-hosts=localhost
spring.couchbase.bucket.name=test
spring.couchbase.bucket.password=couchbase
spring.data.couchbase.auto-index=true
为了测试我们的微服务,我添加了一些Restful端点:
@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);
}
}
首先,更改application.properties以从环境变量获取连接凭据:
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:
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上构建并发布我们的图像:
./mvnw install dockerfile:build -DskipTests
docker login
登录Docker Hubdocker images
图像的imageId://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目录中运行以下命令即可。
./rbac/cluster_role.sh
kubectl create -f secret.yaml
kubectl create -f operator.yaml
kubectl create -f couchbase-cluster.yaml
过了一会儿,我们数据库的所有3个实例都应该运行:
让我们将Web控制台的端口转发到本地计算机:
kubectl port-forward cb-example-0000 8091:8091
现在我们可以访问http:// localhost:8091的Web控制台。您可以使用用户名Administrator和密码密码登录
使用以下属性转到安全性 - >添加用户:
OBS:在生产环境中,请不要将您的应用程序添加为管理员。
首先,让我们创建一个Kubernetes秘密,我们将存储密码以连接到我们的数据库:
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
运行以下命令以创建密码:
kubectl create -f spring-boot-app-secret.yaml
spring-boot-app.yaml文件负责部署我们的应用程序。我们来看看它的内容:
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
我想强调一下这个文件的一些重要部分:
运行以下命令来部署我们的应用程序:
kubectl create -f spring-boot-app.yaml
几秒钟后,您会注意到应用程序的两个实例都已在运行:
最后,让我们将微服务暴露给外界。如何做到有几十种不同的可能性。在我们的例子中,让我们简单地创建一个Load Balancer:
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。您可以运行以下命令来检查其状态:
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
最后,我们可以访问我们的应用程序并开始向它发送请求:
这是事情变得非常有趣的地方。如果我们需要扩展整个微服务怎么办?假设黑色星期五即将到来,我们需要准备我们的基础设施,以支持大量用户访问我们的网站。那么,这是一个容易解决的问题:
...
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:
...
然后,运行以下命令:
kubectl replace -f spring-boot-app.yaml
有什么遗漏吗?是的。我们的数据库怎么样?我们也应该扩展它:
...
enableIndexReplica: false
servers:
- size: 6
name: all_services
services:
- data
- index
...
最后,运行以下命令:
kubectl replace -f couchbase-cluster.yaml
按比例缩小就像放大一样简单; 你只需要更改couchbase-cluster.yaml和spring-boot-app.yaml:
...
enableIndexReplica: false
servers:
- size: 1
name: all_services
services:
- data
- index
...
...
spec:
selector:
matchLabels:
app: spring-boot-app
replicas: 1
template:
metadata:
labels:
...
并运行以下命令:
kubectl replace -f couchbase-cluster.yaml
kubectl replace -f spring-boot-app.yaml
我将在本文的第2部分深入探讨这个主题。在此期间,您可以查看有关pod autoscaling的视频。
如果您的Pod无法启动,有很多方法可以解决问题。在下面的情况中,两个应用程序都无法启动:
由于它们是部署的一部分,让我们描述部署以尝试了解正在发生的事情:
kubectl describe deployment spring-boot-deployment
嗯,在这种情况下没有什么是真正相关的。让我们看看其中一个pod的日志:
kubectl log spring-boot-deployment-74649f869d-74sq8
明白了!应用程序没有启动,因为我们忘了在Couchbase上创建用户。只需创建用户,pod就会在几秒钟内启动:
数据库是有状态的应用程序,扩展它们并不像扩展无状态应用程序那样快(可能永远不会),但是如果你需要建立一个真正有弹性的架构,你应该计划扩展基础架构的所有组件。否则,你只是在其他地方制造瓶颈。
在本文中,我试图展示一个关于如何使Kubernetes上的应用程序和数据库具有弹性的小介绍。但是,它还不是一个可用于生产的架构。还有很多其他事情要考虑,我将在即将发表的文章中讨论其中一些问题。
原文标题《Building Elastic Microservices With Kubernetes and Spring Boot From the Ground Up》
译者:February
不代表云加社区观点,更多详情请查看原文链接
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系外文翻译,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。