Building a Spring Boot gRPC Microservice Demo Deployed on Istio with Kubernetes
This article walks through creating a simple Spring Boot microservice using gRPC, packaging it with Maven, containerizing it with Docker, and deploying both the server and client to a Kubernetes cluster with Istio sidecar injection, demonstrating how Istio removes service‑mesh logic from business code and enables seamless multi‑language microservice communication.
As a fan of Service Mesh and cloud‑native technologies, the author builds a minimal Spring Boot microservice demo that uses gRPC for communication and deploys it on Istio. The demo sends a string and returns a string, illustrating how Istio isolates service‑governance logic into a sidecar.
Initially the author considered Spring MVC but switched to Spring Boot for simplicity.
Why use Istio? Istio moves governance logic out of the business code into an independent sidecar process, eliminating code coupling, supporting multiple languages, and integrating smoothly with Kubernetes.
Why choose gRPC? gRPC offers lightweight, high‑performance RPC with Protobuf and HTTP/2, integrates well with Istio, and avoids the complexity of Spring Cloud’s SDKs.
Project Setup
Use Spring Initializr to create a parent project spring-boot-istio and add gRPC dependencies. The pom.xml for the parent module includes modules for API, server, and client, and manages the grpc-all dependency.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" ...>
<modules>
<module>spring-boot-istio-api</module>
<module>spring-boot-istio-server</module>
<module>spring-boot-istio-client</module>
</modules>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-all</artifactId>
<version>1.28.1</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>The API module defines the protobuf service:
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; }Server Implementation
Implement the service logic:
/**
* Service implementation
*/
@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());
}
}Configure and start the gRPC server as a Spring bean:
@Slf4j
@Component
public 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();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("Shutting down gRPC server.");
stop();
}));
}
private void stop() { if (server != null) server.shutdown(); }
public void block() throws InterruptedException { if (server != null) server.awaitTermination(); }
}Hook the server lifecycle into Spring Boot using CommandLineRunner :
@Component
public class GrpcCommandLineRunner implements CommandLineRunner {
@Autowired GrpcServerConfiguration configuration;
@Override public void run(String... args) throws Exception {
configuration.start();
configuration.block();
}
}Client Implementation
Expose a REST endpoint that forwards requests to the gRPC server:
@RestController
@Slf4j
public class HelloController {
@Autowired GrpcClientConfiguration configuration;
@GetMapping("/hello")
public String hello(@RequestParam(name = "name", defaultValue = "JiangWen") String name) {
HelloWorldService.HelloRequest request = HelloWorldService.HelloRequest.newBuilder()
.setName(name).build();
HelloWorldService.HelloResponse response = configuration.getStub().sayHello(request);
log.info("Server response received: [{}]", response.getMessage());
return response.getMessage();
}
}Client configuration creates the channel and stub:
@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); }
public HelloWorldGrpc.HelloWorldBlockingStub getStub() { return stub; }
}Register the client lifecycle with another CommandLineRunner that starts the client and adds a shutdown hook.
Docker Images
Build Dockerfiles for server and client, exposing ports 18080 (HTTP) and 18888 (gRPC) for the server, and 19090 for the client.
# Server Dockerfile
FROM openjdk:8u121-jdk
RUN 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 # Client Dockerfile
FROM openjdk:8u121-jdk
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' > /etc/timezone
ADD /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_PORTKubernetes & Istio Deployment
Create Service and Deployment YAMLs for both server and client, exposing the necessary ports, and apply them with kubectl apply -f . Then define an Istio Gateway and VirtualService to route /hello to the client service.
# Server Service & Deployment (excerpt)
apiVersion: v1
kind: Service
metadata:
name: spring-boot-istio-server
spec:
ports:
- name: http
port: 18080
targetPort: 18080
- name: grpc
port: 18888
targetPort: 18888
selector:
app: spring-boot-istio-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
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
ports:
- name: http
containerPort: 18080
- name: grpc
containerPort: 18888Similar YAML is provided for the client.
# Istio Gateway & VirtualService (excerpt)
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: spring-boot-istio-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
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: 19090After deploying, obtain the Istio ingress NodePort, set GATEWAY_URL , and test with curl http://${GATEWAY_URL}/hello . A successful response like Hello, JiangWen. This message comes from gRPC. confirms the deployment.
All source code is available on GitHub at https://github.com/WenDev/spring-boot-istio-demo .
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.