1. Pourquoi utiliser Istio ?
Actuellement, pour la pile technologique Java, le meilleur choix pour créer des microservices est Spring Boot, et Spring Boot est généralement utilisé avec le framework de microservices Spring Cloud, qui a de nombreux cas d'implémentation.
Spring Cloud semble parfait, mais après le développement réel, il est facile de constater que Spring Cloud présente les problèmes sérieux suivants :
La logique liée à la gouvernance des services existe dans les SDK tels que Spring Cloud Netflix, qui est liée à l'entreprise. le code est étroitement couplé.
Le SDK est trop intrusif dans le code métier. Lorsque le SDK est mis à niveau et ne peut pas être rétrocompatible, le code métier doit être modifié pour s'adapter à la mise à niveau du SDK - même si la logique métier ne change pas du tout.
Les différents composants sont éblouissants, et la qualité est également inégale. Le coût d'apprentissage est trop élevé et il est difficile de réutiliser complètement le code entre les composants. Apprendre le SDK uniquement pour implémenter la logique de gouvernance n'est pas un bon choix.
est lié à la pile technologique Java. Bien que d'autres langages puissent être connectés, la logique liée à la gouvernance des services doit être implémentée manuellement, ce qui n'est pas conforme au principe des microservices « pouvant être développés dans plusieurs langages ».
Spring Cloud n'est qu'un framework de développement et n'implémente pas la planification des services, l'allocation des ressources et d'autres fonctions nécessaires aux microservices. Ces exigences doivent être remplies à l'aide de plateformes telles que Kubernetes. Spring Cloud et Kubernetes ont des fonctionnalités qui se chevauchent, et des fonctionnalités contradictoires créent des difficultés pour une collaboration fluide entre les deux.
Existe-t-il des alternatives à Spring Cloud ? avoir! C'est Istio.
Istio sépare complètement la logique de gouvernance du code métier et met en œuvre un processus indépendant (Sidecar). Lors du déploiement, Sidecar et le code métier coexistent dans le même Pod, mais le code métier ignore totalement l'existence de Sidecar. Cela permet d'obtenir une intrusion nulle de la logique de gouvernance dans le code métier : en fait, non seulement le code n'est pas intrusif, mais il n'y a aucun couplage entre les deux au moment de l'exécution. Cela permet de développer différents microservices en utilisant différents langages et piles technologiques sans avoir à se soucier des problèmes de gouvernance des services. On peut dire que c'est une solution très élégante.
Ainsi, la question « Pourquoi utiliser Istio » est facilement résolue - car Istio résout les problèmes des microservices traditionnels tels que le couplage de la logique métier et de la logique de gouvernance des services, et l'incapacité de bien implémenter plusieurs langages, et c'est très facile à utiliser. Apprendre à utiliser Istio n’est pas difficile une fois que vous maîtrisez Kubernetes.
1.1. Pourquoi utiliser gRPC comme framework de communication ?
Dans l'architecture des microservices, la communication entre les services est un problème relativement important et est généralement implémentée à l'aide de l'API RPC ou RESTful.
Spring Boot peut utiliser RestTemplate pour appeler des services distants, mais cette méthode n'est pas intuitive et le code est compliqué. La communication multilingue est également un gros problème ; gRPC est plus léger que les frameworks Java RPC courants tels que Dubbo et est facile à utiliser. utilisation. Il est également très pratique à utiliser, le code est très lisible et il peut être bien intégré à Istio et Kubernetes. Avec le support de Protobuf et HTTP2, les performances ne sont pas mauvaises, donc cette fois, gRPC a été choisi pour résoudre le problème. problème de communication entre les microservices Spring Boot. De plus, bien que gRPC ne dispose pas de fonctionnalités de découverte de services, d'équilibrage de charge et autres, Istio est très puissant à cet égard, et les deux forment une relation parfaitement complémentaire.
Considérant que divers grpc-spring-boot-starters peuvent avoir des effets secondaires inconnus sur l'intégration de Spring Boot et d'Istio, je n'ai utilisé aucun grpc-spring-boot-starter cette fois, mais je l'ai écrit directement à la main Intégration de gRPC et Spring Boot. Si vous ne souhaitez pas utiliser de framework tiers pour intégrer gRPC et Spring Boot, vous pouvez vous référer à ma méthode d'implémentation simple.
1.2. Écrivez du code commercial
Utilisez d'abord Spring Initializr pour établir le projet parent spring-boot-istio et introduire les dépendances gRPC. Le fichier pom est le suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http:
xmlns:xsi="http:
xsi:schemaLocation="http:
<modelVersion>4.0.0</modelVersion>
<modules>
<module>spring-boot-istio-api</module>
<module>spring-boot-istio-server</module>
<module>spring-boot-istio-client</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/>
</parent>
<groupId>site.wendev</groupId>
<artifactId>spring-boot-istio</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-istio</name>
<description>Demo project for Spring Boot With Istio.</description>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-all</artifactId>
<version>1.28.1</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
|
Copier après la connexion
Créez ensuite le module de dépendances public spring-boot-istio-api. Le fichier pom est le suivant, principalement certaines dépendances de gRPC :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http:
xmlns:xsi="http:
xsi:schemaLocation="http:
<parent>
<artifactId>spring-boot-istio</artifactId>
<groupId>site.wendev</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-boot-istio-api</artifactId>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-all</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.11.3:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.28.1:exe:${os.detected.classifier}</pluginArtifact>
<protocExecutable>/Users/jiangwen/tools/protoc-3.11.3/bin/protoc</protocExecutable>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
|
Copier après la connexion
Créez le dossier src/main/proto. créez hello sous ce dossier .proto définit l'interface entre les services comme suit :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | syntax = "proto3";
option java_package = "site.wendev.spring.boot.istio.api";
option java_outer_classname = "HelloWorldService";
package helloworld;
service HelloWorld {
rpc SayHello (HelloRequest)
returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
|
Copier après la connexion
C'est très simple, il suffit d'envoyer un nom et de renvoyer un message avec le nom.
Générez ensuite les codes serveur et client et placez-les dans le dossier java. Pour cette partie, veuillez vous référer à la documentation officielle de gRPC.
Une fois le module API disponible, les fournisseurs de services (serveurs) et les consommateurs de services (clients) peuvent être développés. Ici, nous nous concentrons sur la façon d'intégrer gRPC et Spring Boot.
1) Le code métier
côté serveur est très simple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@Slf4j
@Component
public class HelloServiceImpl extends HelloWorldGrpc.HelloWorldImplBase {
@Override
public void sayHello(HelloWorldService.HelloRequest request,
StreamObserver<HelloWorldService.HelloResponse> responseObserver) {
HelloWorldService.HelloResponse response = HelloWorldService.HelloResponse
.newBuilder()
.setMessage(String.format("Hello, %s. This message comes from gRPC.", request.getName()))
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
log.info("Client Message Received:[{}]", request.getName());
}
}
|
Copier après la connexion
En plus du code métier, nous devons également démarrer le serveur gRPC en même temps que l'application démarre. Tout d'abord, écrivez la logique de démarrage, d'arrêt et autre côté serveur :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
@Slf4j
@Componentpublic class GrpcServerConfiguration {
@Autowired
HelloServiceImpl service;
@Value("${grpc.server-port}")
private int port;
private Server server;
public void start() throws IOException {
log.info("Starting gRPC on port {}.", port);
server = ServerBuilder.forPort(port).addService(service).build().start();
log.info("gRPC server started, listening on {}.", port);
Runtime.getRuntime().addShutdownHook( new Thread(() -> {
log.info("Shutting down gRPC server.");
GrpcServerConfiguration.this.stop();
log.info("gRPC server shut down successfully.");
}));
}
private void stop() {
if (server != null) {
server.shutdown();
}
}
public void block() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
}
|
Copier après la connexion
Après avoir défini la logique de démarrage, d'arrêt et autre de gRPC, vous pouvez utiliser CommandLineRunner pour l'ajouter au démarrage de Spring Boot :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Component
public class GrpcCommandLineRunner implements CommandLineRunner {
@Autowired
GrpcServerConfiguration configuration;
@Override
public void run(String... args) throws Exception {
configuration.start();
configuration.block();
}
}
|
Copier après la connexion
Nous allons nous inscrire la logique de gRPC Il devient un Spring Bean car vous devez obtenir son instance et effectuer les opérations correspondantes.
De cette façon, au démarrage de Spring Boot, grâce à l'existence de CommandLineRunner, le serveur gRPC peut être démarré ensemble.
2) Le code entreprise du client
est également très simple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@RestController
@Slf4j
public class HelloController {
@Autowired
GrpcClientConfiguration configuration;
@GetMapping("/hello")
public String hello(@RequestParam(name = "name", defaultValue = "JiangWen", required = false) String name) {
.newBuilder()
.setName(name)
.build();
HelloWorldService.HelloResponse response = configuration.getStub().sayHello(request);
log.info("Server response received: [{}]", response.getMessage());
return response.getMessage();
}
}
|
Copier après la connexion
在启动客户端时,我们需要打开gRPC的客户端,并获取到channel和stub以进行RPC通信,来看看gRPC客户端的实现逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
@Slf4j
@Component
public class GrpcClientConfiguration {
@Value("${server-host}")
private String host;
@Value("${server-port}")
private int port;
private ManagedChannel channel;
private HelloWorldGrpc.HelloWorldBlockingStub stub;
public void start() {
channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
stub = HelloWorldGrpc.newBlockingStub(channel);
log.info("gRPC client started, server address: {}:{}", host, port);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
log.info("gRPC client shut down successfully.");
}
public HelloWorldGrpc.HelloWorldBlockingStub getStub() {
return this.stub;
}
}
|
Copier après la connexion
比服务端要简单一些。
最后,仍然需要一个CommandLineRunner把这些启动逻辑加入到Spring Boot的启动过程中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
@Component
@Slf4j
public class GrpcClientCommandLineRunner implements CommandLineRunner {
@Autowired
GrpcClientConfiguration configuration;
@Override
public void run(String... args) {
configuration.start();
Runtime.getRuntime().addShutdownHook( new Thread(() -> {
try {
configuration.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
}
}
|
Copier après la connexion
1.3、 编写Dockerfile
业务代码跑通之后,就可以制作Docker镜像,准备部署到Istio中去了。
在开始编写Dockerfile之前,先改动一下客户端的配置文件:
1 2 3 4 5 | server:
port: 19090
spring:
application:
name: spring-boot-istio-clientserver-host: ${server-host}server-port: ${server-port}
|
Copier après la connexion
接下来编写Dockerfile:
1) 服务端:
1 2 3 4 5 | FROM openjdk:8u121-jdk
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo 'Asia/Shanghai' >/etc/timezone
ADD /target/spring-boot-istio-server-0.0.1-SNAPSHOT.jar /ENV
SERVER_PORT="18080" ENTRYPOINT java -jar /spring-boot-istio-server-0.0.1-SNAPSHOT.jar
|
Copier après la connexion
主要是规定服务端应用的端口为18080,并且在容器启动时让服务端也一起启动。
2) 客户端:
1 2 3 | FROM openjdk:8u121-jdk
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo 'Asia/Shanghai' >/etc/timezoneADD /target/spring-boot-istio-client-0.0.1-SNAPSHOT.jar /ENV GRPC_SERVER_HOST="spring-boot-istio-server"ENV GRPC_SERVER_PORT="18888"ENTRYPOINT java -jar /spring-boot-istio-client-0.0.1-SNAPSHOT.jar \ --server-host= $GRPC_SERVER_HOST \ --server-port= $GRPC_SERVER_PORT
|
Copier après la connexion
可以看到这里添加了启动参数,配合前面的配置,当这个镜像部署到Kubernetes集群时,就可以在Kubernetes的配合之下通过服务名找到服务端了。
同时,服务端和客户端的pom文件中添加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<dependencies>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
<executions>
<execution>
<id> default </id>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<configuration>
<repository>wendev-docker.pkg.coding.net/develop/docker/${project.artifactId}</repository>
<tag>${project.version}</tag>
<buildArgs>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
|
Copier après la connexion
这样执行mvn clean package时就可以同时把docker镜像构建出来了。
2. 编写部署文件
有了镜像之后,就可以写部署文件了:
1) 服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | apiVersion: v1
kind:
Servicemetadata:
name: spring-boot-istio-server
spec:
type: ClusterIP
ports:
- name: http
port: 18080
targetPort: 18080
- name: grpc
port: 18888
targetPort: 18888
selector:
app: spring-boot-istio-server
---apiVersion: apps/v1
kind:
Deploymentmetadata:
name: spring-boot-istio-server
spec:
replicas: 1
selector:
matchLabels:
app: spring-boot-istio-server
template:
metadata:
labels:
app: spring-boot-istio-server
spec:
containers:
- name: spring-boot-istio-server
image: wendev-docker.pkg.coding.net/develop/docker/spring-boot-istio-server:0.0.1-SNAPSHOT
imagePullPolicy: Always
tty: true
ports:
- name: http
protocol: TCP
containerPort: 18080
- name: grpc
protocol: TCP
containerPort: 18888
|
Copier après la connexion
主要是暴露服务端的端口:18080和gRPC Server的端口18888,以便可以从Pod外部访问服务端。
2) 客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | apiVersion: v1
kind:
Servicemetadata:
name:
spring-boot-istio-client
spec:
type: ClusterIP
ports:
- name: http
port: 19090
targetPort: 19090
selector:
app: spring-boot-istio-client
---apiVersion: apps/v1
kind:
Deploymentmetadata:
name: spring-boot-istio-client
spec:
replicas: 1
selector:
matchLabels:
app: spring-boot-istio-client
template:
metadata:
labels:
app: spring-boot-istio-client
spec:
containers:
- name: spring-boot-istio-client
image: wendev-docker.pkg.coding.net/develop/docker/spring-boot-istio-client:0.0.1-SNAPSHOT
imagePullPolicy: Always
tty: true
ports:
- name: http
protocol: TCP
containerPort: 19090
|
Copier après la connexion
主要是暴露客户端的端口19090,以便访问客户端并调用服务端。
如果想先试试把它们部署到k8s可不可以正常访问,可以这样配置Ingress:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | apiVersion: networking.k8s.io/v1beta1
kind:
Ingressmetadata:
name: nginx-web
annotations:
kubernetes.io/ingress. class : "nginx"
nginx.ingress.kubernetes.io/ use -reges: "true"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: dev.wendev.site
http:
paths:
- path: /
backend:
serviceName: spring-boot-istio-client
servicePort: 19090
|
Copier après la connexion
Istio的网关配置文件与k8s不大一样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | apiVersion: networking.istio.io/v1alpha3
kind:
Gatewaymetadata:
name: spring-boot-istio-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---apiVersion: networking.istio.io/v1alpha3
kind: Virtual
Servicemetadata:
name: spring-boot-istio
spec:
hosts:
- "*"
gateways:
- spring-boot-istio-gateway
http:
- match:
- uri:
exact: /hello
route:
- destination:
host: spring-boot-istio-client
port:
number: 19090
|
Copier après la connexion
主要就是暴露/hello这个路径,并且指定对应的服务和端口。
3. 部署应用到Istio
首先搭建k8s集群并且安装istio。我使用的k8s版本是1.16.0,Istio版本是最新的1.6.0-alpha.1,使用istioctl命令安装Istio。建议跑通官方的bookinfo示例之后再来部署本项目。
注:以下命令都是在开启了自动注入Sidecar的前提下运行的
我是在虚拟机中运行的k8s,所以istio-ingressgateway没有外部ip:
1 2 3 4 | $ kubectl get svc istio-ingressgateway -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
AGEistio-ingressgateway NodePort 10.97.158.232 <none> 15020:30388/TCP,80:31690/TCP,443:31493/TCP,15029:32182/TCP,15030:31724/TCP,15031:30887/TCP,15032:30369/TCP,31400:31122/TCP,15443:31545/TCP 26h
|
Copier après la connexion
所以,需要设置IP和端口,以NodePort的方式访问gateway:
1 2 3 | export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
export INGRESS_HOST=127.0.0.1export GATEWAY_URL= $INGRESS_HOST : $INGRESS_PORT
|
Copier après la connexion
这样就可以了。
接下来部署服务:
1 2 3 | $ kubectl apply -f spring-boot-istio-server.yml
$ kubectl apply -f spring-boot-istio-client.yml
$ kubectl apply -f istio-gateway.yml
|
Copier après la connexion
必须要等到两个pod全部变为Running而且Ready变为2/2才算部署完成。
接下来就可以通过
Copier après la connexion
访问到服务了。如果成功返回了Hello, JiangWen. This message comes from gRPC.的结果,没有出错则说明部署完成。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!