译者 | 李睿
审校 | 孙淑娟
为了说明如何使用Kubernetes,建议考虑本文介绍的区块链案例,并探讨为加密货币市场开发的应用程序。这个应用程序使用的技术是实用的,也可以用于其他项目。换句话说,技术任务是一个非常通用的解决方案,主要针对Kubernetes进行了专门调整,也可以用于其他行业。
使用的技术
该项目是一个启动项目,预算有限。而开发人员将定期为投资者提供演示,并定期展示开发新功能的进展情况。因此决定使用以下技术:
- Node JS(NestJS 框架)
- Postgre SQL数据库
- Kafka JS
- Kubernetes(k8s)+Helm图表
- Flutter
- React
开发过程
对于第一阶段,开发人员的主要目的是将其应用程序拆分为微服务。在这一案例中,决定创建6个微服务。
(1)管理微服务
(2)核心微服务
(3)支付微服务
(4)邮件和通知服务
(5)Cron任务服务
(6)Webhooks微服务
值得一提的是,虽然技术堆栈是实用的,并且可以在各种情况下使用而无需更改,但上述微服务却不是,它们是专门为这一项目中所需的功能而创建的。因此可以使用相同的技术,但必须根据需要设计新的微服务。
以下了解如何在NestJS上制作这些微服务。需要为Kafka消息代理进行配置选项。因此,为所有微服务的常用模块和配置创建了一个共享资源文件夹。
微服务配置选项
import { ClientProviderOptions, Transport } from '@nestjs/microservices';
import CONFIG from '@application-config';
import { ConsumerGroups, ProjectMicroservices } from './microservices.enum';
const { BROKER_HOST, BROKER_PORT } = CONFIG.KAFKA;
export const PRODUCER_CONFIG = (name: ProjectMicroservices): ClientProviderOptions => ({
name,
transport: Transport.KAFKA,
options: {
client: {
brokers: [`${BROKER_HOST}:${BROKER_PORT}`],
},
}
});
export const CONSUMER_CONFIG = (groupId: ConsumerGroups) => ({
transport: Transport.KAFKA,
options: {
client: {
brokers: [`${BROKER_HOST}:${BROKER_PORT}`],
},
consumer: {
groupId
}
}
});
以消费者模式将管理面板微服务连接到Kafka。它将允许捕获和处理来自主题的事件。
使应用程序在微服务模式下工作,以便能够使用事件:
app.connectMicroservice(CONSUMER_CONFIG(ConsumerGroups.ADMIN)); await app.startAllMicroservices();
可以注意到消费者配置包含groupId。这是一个重要的选项,它将允许来自同一组的消费者从主题中获取事件,并将它们分发给彼此以更快地处理它们。
例如,假设微服务接收事件的速度快于处理它们的速度。在这种情况下,可以进行自动扩展以生成额外的pod以在它们之间共享负载,并使该过程加快两倍。
为了实现这一点,消费者应该在组中,并且在扩展之后,生成的pod也将在同一个组中。因此,他们将能够共享加载,而不是处理来自不同Kafka分区的相同主题事件。
以下了解一个如何在NestJS中捕获和处理Kafka事件的示例:
消费者控制器
import { Controller } from '@nestjs/common';
import { Ctx, KafkaContext, MessagePattern, EventPattern, Payload } from '@nestjs/microservices';
@Controller('consumer')
export class ConsumerController {
@MessagePattern('hero')
readMessage(@Payload() message: any, @Ctx() context: KafkaContext) {
return message;
}
@EventPattern('event-hero')
sendNotif(data) {
console.log(data);
}
}
消费者可以在两种模式下工作。它接收事件并处理它们而不返回任何响应(EventPattern
decorator),或者在处理事件后将响应返回给生产者(MessagePattern
decorator)。EventPattern更好,如果可能的话应该是首选,因为它不包含任何额外的源代码层来提供请求/响应功能。
生产者呢?
对于连接生产者,需要为负责发送事件的模块提供生产者配置。
生产者连接
import { Module } from '@nestjs/common';
import DatabaseModule from '@shared/database/database.module';
import { ClientsModule } from '@nestjs/microservices';
import { ProducerController } from './producer.controller';
import { PRODUCER_CONFIG } from '@shared/microservices/microservices.config';
import { ProjectMicroservices } from '@shared/microservices/microservices.enum';
@Module({
imports: [
DatabaseModule,
ClientsModule.register([PRODUCER_CONFIG(ProjectMicroservices.ADMIN)]),
],
controllers: [ProducerController],
providers: [],
})
export class ProducerModule {}
基于事件的生产者
import { Controller, Get, Inject } from '@nestjs/common';
import { ClientKafka } from '@nestjs/microservices';
import { ProjectMicroservices } from '@shared/microservices/microservices.enum';
@Controller('producer')
export class ProducerController {
constructor(
@Inject(ProjectMicroservices.ADMIN)
private readonly client: ClientKafka,
) {}
@Get()
async getHello() {
this.client.emit('event-hero', { msg: 'Event Based'});
}
}
基于请求/响应的生产者
import { Controller, Get, Inject } from '@nestjs/common';
import { ClientKafka } from '@nestjs/microservices';
import { ProjectMicroservices } from '@shared/microservices/microservices.enum';
@Controller('producer')
export class ProducerController {
constructor(
@Inject(ProjectMicroservices.ADMIN)
private readonly client: ClientKafka,
) {}
async onModuleInit() {
// Need to subscribe to a topic
// to make the response receiving from Kafka microservice possible
this.client.subscribeToResponseOf('hero');
await this.client.connect();
}
@Get()
async getHello() {
const responseBased = this.client.send('hero', { msg: 'Response Based' });
return responseBased;
}
}
每个微服务可以同时在两种模式(生产者/消费者)或两种模式(混合)下工作。通常情况下,微服务使用混合模式来达到负载平衡的目的,为主题生成事件并均匀地使用它们,共享负载。
基于Helm图表模板的Kubernetes配置,针对每个微服务实现。
Helm图表描述的Admin API微服务组件及其结构
该模板由几个配置文件组成:
以下来看每个配置文件(没有Helm模板)
Admin-API部署
apiVersion: apps/v1
kind: Deployment
metadata:
name: admin-api
spec:
replicas: 1
selector:
matchLabels:
app: admin-api
template:
metadata:
labels:
app: admin-api
spec:
containers:
- name: admin-api
image: xxx208926xxx.dkr.ecr.us-east-1.amazonaws.com/project-name/stage/admin-api
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 250m
memory: 512Mi
ports:
- containerPort: 80
env:
- name: NODE_ENV
value: production
- name: APP_PORT
value: "80"
部署可以包含更多的精简配置,如资源限制、健康检查配置、更新策略等。但是提供了一个基本配置示例,可以根据任何其他项目的需要进行扩展。
Admin-API服务
--
apiVersion: v1
kind: Service
metadata:
name: admin-api
spec:
selector:
app: admin-api
ports:
- name: admin-api-port
port: 80
targetPort: 80
protocol: TCP
type: NodePort
需要将服务暴露给外界才能使用它。通过负载均衡器公开的应用程序,并提供SSL配置以使用安全的HTTPS连接。
需要在集群上安装一个负载均衡控制器。这是最流行的解决方案:AWS负载均衡器控制器。
然后,需要使用以下配置创建入口:
Admin-API入口控制器
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: default
name: admin-api-ingress
annotations:
alb.ingress.kubernetes.io/load-balancer-name: admin-api-alb
alb.ingress.kubernetes.io/ip-address-type: ipv4
alb.ingress.kubernetes.io/tags: Environment=production,Kind=application
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-2:xxxxxxxx:certificate/xxxxxxxxxx
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/healthcheck-protocol: HTTPS
alb.ingress.kubernetes.io/healthcheck-path: /healthcheck
alb.ingress.kubernetes.io/healthcheck-interval-seconds: '15'
alb.ingress.kubernetes.io/ssl-redirect: '443'
alb.ingress.kubernetes.io/group.name: admin-api
spec:
ingressClassName: alb
rules:
- host: example.com
http:
paths:
- path: /*
pathType: ImplementationSpecific
backend:
service:
name: admin-api
port:
number: 80
应用这一配置后,将创建一个新的alb负载均衡器,需要使用在“host”参数中提供的名称创建一个域,并将流量从该主机路由到负载均衡器。
Admin-API自动扩展配置
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: admin-api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: admin-api
minReplicas: 1
maxReplicas: 2
metrics:
- type: Resource
resource:
name: cpu
targetAverageUtilization: 90
Helm呢?
当想要降低K8s基础设施的复杂性时,Helm变得非常有用。如果没有这个工具——需要编写很多yml文件才能在集群上运行它。
此外,应该记住应用程序、标签、名称等之间的关系。但是,可以使用Helm使一切变得更简单。它的工作方式类似于包管理器,允许创建应用程序的模板,然后使用简单的命令准备和运行它。
使用Helm来制作模板:
Admin-API 部署(Helm图表)
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.global.appName }}
spec:
selector:
app: {{ .Values.global.appName }}
ports:
- name: {{ .Values.global.appName }}-port
port: {{ .Values.externalPort }}
targetPort: {{ .Values.internalPort }}
protocol: TCP
type: NodePort
Admin-API 服务(Helm图表)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: default
name: ingress
annotations:
alb.ingress.kubernetes.io/load-balancer-name: {{ .Values.ingress.loadBalancerName }}
alb.ingress.kubernetes.io/ip-address-type: ipv4
alb.ingress.kubernetes.io/tags: {{ .Values.ingress.tags }}
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/certificate-arn: {{ .Values.ingress.certificateArn }}
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
alb.ingress.kubernetes.io/healthcheck-protocol: HTTPS
alb.ingress.kubernetes.io/healthcheck-path: {{ .Values.ingress.healthcheckPath }}
alb.ingress.kubernetes.io/healthcheck-interval-seconds: {{ .Values.ingress.healthcheckIntervalSeconds }}
alb.ingress.kubernetes.io/ssl-redirect: '443'
alb.ingress.kubernetes.io/group.name: {{ .Values.ingress.loadBalancerGroup }}
spec:
ingressClassName: alb
rules:
- host: {{ .Values.adminApi.domain }}
http:
paths:
- path: {{ .Values.adminApi.path }}
pathType: ImplementationSpecific
backend:
service:
name: {{ .Values.adminApi.appName }}
port:
number: {{ .Values.adminApi.externalPort }}
Admin-API自动扩展配置(Helm图表)
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "ks.fullname" . }}
labels:
{{- include "ks.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "ks.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}
模板的值位于“values.yml”、“values-dev.yml”和“values-stage.yml”文件中。使用哪一个取决于环境。检查一下dev
env的一些值的示例。
Admin-API Helm Values-Stage.yml File
env: stage
appName: admin-api
domain: admin-api.xxxx.com
path: /*
internalPort: '80'
externalPort: '80'
replicas: 1
image:
repository: xxxxxxxxx.dkr.ecr.us-east-2.amazonaws.com/admin-api
pullPolicy: Always
tag: latest
ingress:
loadBalancerName: project-microservices-alb
tags: Environment=stage,Kind=application
certificateArn: arn:aws:acm:us-east-2:xxxxxxxxx:certificate/xxxxxx
healthcheckPath: /healthcheck
healthcheckIntervalSeconds: '15'
loadBalancerGroup: project-microservices
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
env:
- name: NODE_ENV
value: stage
- name: ADMIN_PORT
value: "80"
要在集群上应用配置,需要升级图表并重新启动部署。
以下检查负责此操作的GitHub Actions步骤。
在GitHub操作中应用Helm配置
env: stage
appName: admin-api
domain: admin-api.xxxx.com
path: /*
internalPort: '80'
externalPort: '80'
replicas: 1
image:
repository: xxxxxxxxx.dkr.ecr.us-east-2.amazonaws.com/admin-api
pullPolicy: Always
tag: latest
ingress:
loadBalancerName: project-microservices-alb
tags: Environment=stage,Kind=application
certificateArn: arn:aws:acm:us-east-2:xxxxxxxxx:certificate/xxxxxx
healthcheckPath: /healthcheck
healthcheckIntervalSeconds: '15'
loadBalancerGroup: project-microservices
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
env:
- name: NODE_ENV
value: stage
- name: ADMIN_PORT
value: "80"
结语
最终,本文研究了如何在特定案例中使用Kubernetes构建微服务。显然跳过了其他必备步骤和组件,将代码示例转换为成熟的工作应用程序。但是,上述源代码足以展示和解释Kubernetes微服务是如何构建的。
原文标题:Blockchain Case Using Kubernetes,作者:Tetiana Stoyk