Continuing this short series of writing a simple echo service web API along with the docker and k8s requirements, we’re now going to turn our attention to a Java implementation.
Implementation
I’m going to be using JetBrains IntelliJ.
- Create a new Java Project, selecting Maven as the build system
- We’re going to use Sprint Boot, so add the following to the pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.5.5</version> </dependency> </dependencies> - We’re also going to want to use the Spring Boot Maven plugin to generate our JAR and Manifest
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>3.5.5</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> - Let’s delete the supplied Main.java file and replace with one named EchoServiceApplication.java which looks like this
package com.putridparrot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.HashMap; import java.util.Map; @SpringBootApplication public class EchoServiceApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(EchoServiceApplication.class); Map<String, Object> props = new HashMap<>(); props.put("server.port", System.getenv("PORT")); app.setDefaultProperties(props); app.run(args); } }We’re setting the PORT here from the environment variable as this will be supplied via the Dockerfile
- Next add a new file named EchoController.java which will look like this
package com.putridparrot; import org.springframework.web.bind.annotation.*; @RestController public class EchoController { @GetMapping("/echo") public String echo(@RequestParam(name = "text", defaultValue = "") String text) { return String.format("Java Echo: %s", text); } @GetMapping("/readyz") public String readyz() { return "OK"; } @GetMapping("/livez") public String livez() { return "OK"; } }
Dockerfile
Next up we need to create our Dockerfile
FROM maven:3.9.11-eclipse-temurin-21 AS builder WORKDIR /app COPY . . RUN mvn clean package -DskipTests FROM eclipse-temurin:21-jre AS runtime WORKDIR /app COPY --from=builder /app/target/echo_service-1.0-SNAPSHOT.jar ./echo-service.jar ENV PORT=8080 EXPOSE 8080 ENTRYPOINT ["java", "-jar", "echo-service.jar"]
Note: In Linux port 80 might be locked down, hence we use port 8080 – to override the default port in phx we also set the environment variable PORT.
To build this, run
docker build -t putridparrot.echo_service:v1 .
Don’t forget to change the name to your preferred name.
To test this, run
docker run -p 8080:8080 putridparrot.echo_service:v1
Kubernetes
If all wen well we’ve not tested our application and see it working from a docker image, so now we need to create the deployment etc. for Kubernete’s. Let’s assume you’ve pushed you image to Docker or another container registry such as Azure – I’m call my container registry putridparrotreg.
I’m also not going to use helm at this point as I just want a (relatively) simple yaml file to run from kubectl, so create a deployment.yaml file, we’ll store all the configurations, deployment, service and ingress in this one file jus for simplicity.
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo
namespace: dev
labels:
app: echo
spec:
replicas: 1
selector:
matchLabels:
app: echo
template:
metadata:
labels:
app: echo
spec:
containers:
- name: echo
image: putridparrotreg/putridparrot.echo_service:v1
ports:
- containerPort: 8080
resources:
requests:
memory: "100Mi"
cpu: "100m"
limits:
memory: "200Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /livez
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: echo_service
namespace: dev
labels:
app: echo
spec:
type: ClusterIP
selector:
app: echo
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo-ingress
namespace: dev
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: mydomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: echo_service
port:
number: 80
Don’t forget to change the “host” and image to suit, also this assume you created a namespace “dev” for your app. See Creating a local container registry for information on setting up your own container registry.