17

Spring Boot Development in Kubernetes for Lazy Developers (like me)

 4 years ago
source link: https://itnext.io/spring-boot-development-in-kubernetes-for-lazy-developers-like-me-bb6e7b08f13f?source=friends_link&gi=2ccede4cef50
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Spring Boot Development in Kubernetes for Lazy Developers (like me)

I’ve been away from the Spring Boot community for a while, so I decided to check out some samples to see what was new, and here’s what I found.

When I created my project using Spring Initializr, I wanted to try some integrations that I’ve never used with Spring Boot before, like Google PubSub or Flyway. I also added Postgres and Jooq as a dependency.

So in total, I had two dependencies: PubSub and Postgres, meaning that I would need to run them locally to test the integration.

PubSub

I know three options to run PubSub locally:

gcloud components install pubsub-emulator
...
gcloud beta emulators pubsub start --project=PUBSUB_PROJECT_ID
  • Run a containerized PubSub emulator. You can pass topics and subscriptions as environment variables and they will be created when the container starts.
docker run --rm -ti -p 8681:8681 messagebird/gcloud-pubsub-emulator:latest
apiVersion: apps/v1
kind: Deployment
metadata:
name: pubsub
labels:
app: pubsub
spec:
replicas: 1
selector:
matchLabels:
app: pubsub
template:
metadata:
labels:
app: pubsub
spec:
containers:
- name: pubsub
image: messagebird/gcloud-pubsub-emulator:latest
env:
- name: PUBSUB_PROJECT1
value: "PROJECTID,TOPIC1:SUBSCRIPTION1,TOPIC2:SUBSCRIPTION2"
ports:
- containerPort: 8681

And here’s the service:

apiVersion: v1
kind: Service
metadata:
name: pubsub
labels:
app: pubsub
spec:
externalName: pubsub
type: NodePort
ports:
- name: pubsub
port: 8681
nodePort: 30100
selector:
app: pubsub

Postgres

I’ll mention at least three options to run PubSub locally:

docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres
  • Run Postgres in Kubernetes: This one is a little bit more complex since I don’t want my DB to lose all the data when I restart a pod, right? So that’s why I need to define a volume and storage.

Why Kubernetes?

Running N different containers with their IPs and Ports, in N terminals and making sure they all point to the correct address and port was way too painful. That’s why I decided to go with Kubernetes instead of Docker: when you deploy in Kubernetes you have access to a built-in DNS service or environment variables with addresses and ports for all the services that you deployed.

Skaffold

Now that I have all the services in Kubernetes, I’m that lazy, that I don’t want to have to apply multiple Kubernetes files all the time, and make sure that I cleaned the environment up (delete deployments, etc. ) when I’m done.

I have no idea how I found Skaffold, it’s such a great tool, built by Google, and actually fixes that problem for me. How does it work?

There are two use cases:

1) Developing your services locally in Kubernetes with hot reload: you make a change and Skaffold will apply the corresponding changes in your cluster

2) Deploying your changes in prod: in this case, Skaffold works as a pipeline and you can deploy your changes in a remote cluster

I focused on the first use case since I don’t have anything to deploy in production 😅

Yet another DSL using YAML

I think it makes sense that Skaffold uses YAML for its configuration as most of the configuration files in Kubernetes are YAML based as well.

This is how a Skaffold configuration looks like:

apiVersion: skaffold/v2alpha3
kind: Config
deploy:
kubectl:
manifests:
- kubernetes/postgres/**
- kubernetes/pubsub/**

What Skaffold will do is to apply all the Kubernetes files that are present in those two directories. To do that, you need to run in a terminalskaffold dev and when you cancel the process, all the resources are deleted.

Skaffold Support on IntelliJ

Now it’s time to do it with one click! There’s an IntelliJ plugin called Cloud Code, that allows you to 1) Manage your Kubernetes resources and 2) Run your Kubernetes configuration from a Skaffold file.

Kubernetes Explorer on IntelliJ
Kubernetes Explorer on IntelliJ
Kubernetes Resources Explorer on IntelliJ
Run Skaffold configuration on IntelliJ
Run Skaffold configuration on IntelliJ
Skaffold run configuration on IntelliJ

Once you create the IntelliJ Cloud Code Kubernetes Configuration, when you run it you’ll see something like:

Listing files to watch...
Generating tags...
Checking cache...
Tags used in deployment:
Starting deploy...
- serviceaccount/admin-user created
- configmap/postgres-config created
- deployment.extensions/postgres created
- service/postgres created
- persistentvolume/postgres-pv-volume created
- persistentvolumeclaim/postgres-pv-claim created
- deployment.apps/pubsub created
- service/pubsub created
Port forwarding service/postgres in namespace default, remote port 5432 -> address 127.0.0.1 port 5432
Port forwarding service/pubsub in namespace default, remote port 8681 -> address 127.0.0.1 port 8681

If you change something, like DB name, PubSub topics, ports, etc. just redeploy with IntelliJ using the redeploy button and you’ll see all the changes applied. When you’re done, stop the Run and Skaffold will clean up all the Kubernetes Resources for you:

Watching for changes...
Cleaning up...
- serviceaccount "admin-user" deleted
- configmap "postgres-config" deleted
- deployment.extensions "postgres" deleted
- service "postgres" deleted
- persistentvolume "postgres-pv-volume" deleted
- persistentvolumeclaim "postgres-pv-claim" deleted
- deployment.apps "pubsub" deleted
- service "pubsub" deleted

Use your Kubernetes Resources in Spring Boot

This is how everything started, trying to use PubSub and Flyway in Spring Boot! So let’s continue with that.

As I said before, there’s a built-in DNS already in Kubernetes, so in order to deploy in Kubernetes, this Spring Boot properties file is enough:

# https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/#environment-variables
# DNS doesn't work for pubsub service for some reason
spring.cloud.gcp.pubsub.emulatorHost: ${PUBSUB_SERVICE_HOST}:${PUBSUB_SERVICE_PORT}
spring.cloud.gcp.pubsub.projectId: PROJECTID
spring.datasource.url: jdbc:postgresql://postgres/postgresdb
spring.datasource.username: postgresadmin
spring.datasource.password: admin123
spring.datasource.driverClassName: org.postgresql.Driver

If you want to debug something locally, then you’ll need to point to the port that you used in the Kubernetes Service:

spring.cloud.gcp.pubsub.emulatorHost: localhost:30100
spring.cloud.gcp.pubsub.projectId: PROJECTID
spring.datasource.url: jdbc:postgresql://localhost:30000/postgresdb
spring.datasource.username: postgresadmin
spring.datasource.password: admin123
spring.datasource.driverClassName: org.postgresql.Driver

Going back to the example, by running the Spring Boot app you can see in the logs something like:

2020-03-20 13:03:17.291  INFO 58272 --- [  restartedMain] o.f.c.internal.license.VersionPrinter    : Flyway Community Edition 6.0.8 by Redgate
2020-03-20 13:03:17.427 INFO 58272 --- [ restartedMain] o.f.c.internal.database.DatabaseFactory : Database: jdbc:postgresql://localhost:30000/postgresdb (PostgreSQL 12.2)
2020-03-20 13:03:17.468 INFO 58272 --- [ restartedMain] o.f.core.internal.command.DbValidate : Successfully validated 2 migrations (execution time 00:00.022s)

Meaning that Flyway was executed. In the example there’s a REST endpoint that we can call to try the app:

curl localhost:8080/hello

There’s no response body, but in the logs you can see something like:

com.example.demo.DemoService: User id 1 username ariel com.example.demo.DemoService: User id 2 username alejandro com.example.demo.DemoService: Success! 
com.example.demo.DemoService: Success!
com.example.demo.DemoService: 1-ariel

In the migration script, I inserted some rows. Then in DemoService, I query the table and for each row, I print it in the logs and publish a PubSub Message (therefore two success messages), and finally, I read the next message in the PubSub Subscription.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK