Prerequisites
It’s recommended to have following skills before starting :
- Basic knowledge of Java, Gradle, Docker and Kubernetes
- Components’ version :
- JDK 8 or higher
- Please ensure you have a JDK installed and not just a JRE
- Docker installed
- Kubectl installed
- IDE (IntelliJ, Eclipse, VSCode)
Dockerize
Makes original app build and run via Docker |
---|
Spring Initializr
Details : Spring initializr ()
Clone project and add to IDE
1 2 3 4 5 6 7 8 9 10 11 12
curl https://start.spring.io/starter.tgz \ -d language=java \ -d javaVersion=1.8 \ -d type=gradle-project \ -d baseDir=springk8s \ -d bootVersion=2.4.2 \ -d packageName=com.example.springk8s\ -d groupId=com.example \ -d artifactId=springk8s\ -d packaging=jar \ -d dependencies=web,actuator,devtools,lombok,configuration-processor \ | tar -xzvf -
delete application.properties and add application.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
server: port: 8080 shutdown: "graceful" spring: application: name: ${info.app.name} lifecycle: timeout-per-shutdown-phase: "20s" management: endpoint: health: show-details: always probes: enabled: true metrics: tags: application: ${info.app.name} info: app: name: SpringK8s description: Spring Boot actuator for k8s cloud stack demo version: 1.0.0 encoding: UTF-8 java.version: 1.8
gradle build projects
run and test
curl -ivL http://localhost:8080/actuator/health
Result :
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
{ "status":"UP", "components":{ "diskSpace":{ "status":"UP", "details":{ "total":498851688448, "free":244886433792, "threshold":10485760, "exists":true } }, "livenessState":{ "status":"UP" }, "ping":{ "status":"UP" }, "readinessState":{ "status":"UP" } }, "groups":[ "liveness", "readiness" ] }
Containerize Spring Boot App
The first step in running the app on Kubernetes is to write a Dockerfile and producing a container for the app we can then deploy to Kubernetes
Dockerfile
add Dockerfile to root project
1 2 3 4 5 6 7 8 9
FROM openjdk:8-jre-alpine WORKDIR /usr/app/ COPY build/libs/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","app.jar"]
Build image:
docker build -t image/springk8s .
Running image:
docker run -d -p 8080:8080 image/springk8s
Test container:
curl http://localhost:8080/actuator/info
Putting The Container In A Registry:
docker push / docker pull
Kubernetes
Makes Docker image deploy to Kubernetes |
---|
Deploying To Kubernetes
Kubernetes uses YAML files to provide a way of describing how the app will be deployed to the platform
You can write these by hand using the Kubernetes documentation as a reference
Or you can have Kubernetes generate it for you using kubectl
The –dry-run flag allows us to generate the YAML without actually deploying anything to Kubernetes
Generate by command line :
1 2
mkdir k8s kubectl create deployment k8s-demo-app --image localhost:5000/apps/demo -o yaml --dry-run=client > k8s/deployment.yaml
Generate by yaml example as shown below
K8s resources config best practice
Externalized Configuration
Transfer application.yaml and active profiles to configmap
Create ConfigMap using existing files
DEPLOYMENT_ENV: dev
kubectl create configmap springk8s-app-config --from-file=application.yaml
Mount to container
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
spec: containers: - name: springk8s image: image/springk8s imagePullPolicy: IfNotPresent args: - '--spring.profiles.active=$(DEPLOYMENT_ENV_KEY)' - '--spring.config.location=/usr/config/' ports: - containerPort: 8080 volumeMounts: - name: springk8s-app-config mountPath: /usr/config readOnly: true volumes: - name: springk8s-app-config configMap: name: springk8s-profile-cm items: - key: application.yaml path: application.yaml
Lifecycle
- Liveness and Readiness Probes
- Graceful Shutdown
- Handling In Flight Requests
Liveness and Readiness Probes
application.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13
management: endpoints: web: exposure: include: "*" endpoint: health: show-details: always probes: enabled: true metrics: tags: application: ${info.app.name}
deployment.yaml
1 2 3 4 5 6 7 8 9 10
livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 8 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 8
Graceful Shutdown
deployment.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
apiVersion: apps/v1 kind: Deployment metadata: ... name: k8s-demo-app spec: ... template: ... spec: containers: ... lifecycle: preStop: exec: command: ["sh", "-c", "sleep 10"]
Handling In Flight Requests
application.yaml
1 2 3
server: port: 8085 shutdown: "graceful"
Resource Manage
Manage resource in 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
spec: imagePullSecrets: - name: regcred containers: - name: springk8s image: image/springk8s imagePullPolicy: IfNotPresent args: - '--spring.profiles.active=$(DEPLOYMENT_ENV_KEY)' - '--spring.config.location=/usr/config/' ports: - containerPort: 8080 env: - name: DEPLOYMENT_ENV_KEY valueFrom: configMapKeyRef: name: springk8s-profile-cm key: DEPLOYMENT_ENV resources: requests: memory: "256Mi" cpu: "300m" limits: memory: "512Mi" cpu: "500m"
Resources : memory limits + Java heap size?
Service Discovery
Kubernetes makes it easy to make requests to other services
Each service has a DNS entry in the container of the other services allowing you to make requests to that service using the service name
For example, if there is a service called k8s-workshop-name-service deployed we could make a request from the k8s-demo-app just by making an HTTP request to http://k8s-workshop-name-service
Testing
kubectl port-forward service/k8s-demo-app 8080:80
curl http://localhost:8080
Istio :
Configuration-watcher (bp)
HA : LoadBalancer
watch -n 1 kubectl get all