Spring Boot Development in Kubernetes for Lazy Developers (like me)
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.
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:
- Run gcloud PubSub emulator. Then manually create all the topics and subscriptions that you need
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
- Run PubSub emulator in Kubernetes. It uses the containerized PubSub emulator and it’s wrapped in a Kubernetes Service so that it’s easier to consume.
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:
- Old-school: Install Postgres — Google how to do it, there’s an installer for that.
- Run a containerized Postgres instance:
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.
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.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK