Slide 1

Slide 1 text

#K8SBeyondTheHype @touret_alex Kubernetes & Co, beyond the hype: 10 tips for designers who want to create cloud native apps! Alexandre Touret @touret_alex blog.touret.info www.worldline.com

Slide 2

Slide 2 text

#K8SBeyondTheHype @touret_alex Kubernetes & Co, beyond the hype: 10 tips for designers who want to create cloud native apps Alexandre Touret Architect / Developer #Java #API #CI #Cloud #Software_Craftsmanship

Slide 3

Slide 3 text

#K8SBeyondTheHype @touret_alex SOME CONTEXT

Slide 4

Slide 4 text

#K8SBeyondTheHype @touret_alex

Slide 5

Slide 5 text

#K8SBeyondTheHype @touret_alex Used technologies

Slide 6

Slide 6 text

#K8SBeyondTheHype @touret_alex DO YOU REALLY NEED IT?

Slide 7

Slide 7 text

#K8SBeyondTheHype @touret_alex Few concerns to address Small business application? Controlled scope? Automatized deployments already implemented? Any compliancy constraints?

Slide 8

Slide 8 text

#K8SBeyondTheHype @touret_alex What are the NFRs? Do you have constraining SLOs? (eg. >99% SLA availability) Does your system need to be dynamically scaled?

Slide 9

Slide 9 text

#K8SBeyondTheHype @touret_alex Providing « on demand » platforms • Do you need to deliver environments on demand (and really fast)?

Slide 10

Slide 10 text

#K8SBeyondTheHype @touret_alex Why I considered K8S as a target platform? • For a new app, we had these NFRs: • 99,95% availability SLA level • Need to deal with peak throughputs (e.g., dynamically scaling for the black Friday) • Need to provide “on-demand” platforms (e.g., DRS, sandboxes for our customers ) • We also chose this technology because we already had an available Openshift© shared platform and the associated tooling.

Slide 11

Slide 11 text

#K8SBeyondTheHype @touret_alex DOCKER & CIE BASICS

Slide 12

Slide 12 text

#K8SBeyondTheHype @touret_alex

Slide 13

Slide 13 text

#K8SBeyondTheHype @touret_alex You will have to know

Slide 14

Slide 14 text

#K8SBeyondTheHype @touret_alex

Slide 15

Slide 15 text

#K8SBeyondTheHype @touret_alex On my side… Support Training sessions Getting started GITLABCI templates

Slide 16

Slide 16 text

#K8SBeyondTheHype @touret_alex ORGANISATION

Slide 17

Slide 17 text

#K8SBeyondTheHype @touret_alex “Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure.” M. Conway

Slide 18

Slide 18 text

#K8SBeyondTheHype @touret_alex Responsability Assignment Matrix (RACI) What’s the associated RACI ? Is your organisation compatible?

Slide 19

Slide 19 text

#K8SBeyondTheHype @touret_alex THE STATELESS APPS AND THE OTHERS…

Slide 20

Slide 20 text

#K8SBeyondTheHype @touret_alex Codebase One codebase tracked in revision control, many deploys Dependencies Explicitly declare and isolate dependencies Config Store config in the environment Backing Services Treat backing services as attached resources Build, release, run Strictly separate build and run stages Concurrency Scale out via the process model Processes Execute the app as one or more stateless processes Port Binding Export services via port binding Disposability Maximize robustness with fast startup and graceful shutdown Dev/prod parity Keep development, staging, and production as similar as possible Logs Treat logs as event streams Admin processes Run admin/management tasks as one-off processes

Slide 21

Slide 21 text

#K8SBeyondTheHype @touret_alex For an API Processes Config Concurrency Disposability Port binding Dev prod parity

Slide 22

Slide 22 text

#K8SBeyondTheHype @touret_alex (FAST) APPLICATION STARTUP

Slide 23

Slide 23 text

#K8SBeyondTheHype @touret_alex

Slide 24

Slide 24 text

#K8SBeyondTheHype @touret_alex How to do that with Spring boot? • Checking your requirements first • Activating Lazy initialization spring.main.lazy-initialization=true • Disabling autoconfiguration and use @Import annotation • Activating Graceful shutdown handling server.shutdown=graceful • …

Slide 25

Slide 25 text

#K8SBeyondTheHype @touret_alex CHOOSE WISELY YOUR FRAMEWORKS AND TARGET RUNTIME PLATFORMS

Slide 26

Slide 26 text

#K8SBeyondTheHype @touret_alex

Slide 27

Slide 27 text

#K8SBeyondTheHype @touret_alex Items to check • Fast startup • Shutdown handling • Ability to be integrated into Docker and K8S • Observability • Memory and CPU consumption • Dependency and patch management

Slide 28

Slide 28 text

#K8SBeyondTheHype @touret_alex TOMCAT vs FAT JARS A story of a Production – Development teams trade-off

Slide 29

Slide 29 text

#K8SBeyondTheHype @touret_alex OBSERVABILITY

Slide 30

Slide 30 text

#K8SBeyondTheHype @touret_alex Address this point from design phase • Don’t wait for the production deployment • Expose your system status through endpoints • Be careful to the used FRAMEWORKS

Slide 31

Slide 31 text

#K8SBeyondTheHype @touret_alex Liveness & readiness probes livenessProbe: failureThreshold: 3 httpGet: path: /actuator/health/liveness port: http scheme: HTTP initialDelaySeconds: 30 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 1 readinessProbe: failureThreshold: 3 httpGet: path: /actuator/health/readiness port: http scheme: HTTP initialDelaySeconds: 30 periodSeconds: 30 successThreshold: 1 timeoutSeconds: 1

Slide 32

Slide 32 text

#K8SBeyondTheHype @touret_alex Spring Actuator dependencies { implementation 'org.springframework.boot:spring-boot-starter- actuator' } https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html

Slide 33

Slide 33 text

#K8SBeyondTheHype @touret_alex Spring Actuator management.health.probes.enabled=true management.endpoints.enabled-by-default=true management.health.livenessstate.enabled=true management.health.readinessstate.enabled=true management.endpoint.health.show-details=always management.endpoint.health.probes.enabled=true management.endpoint.health.enabled=true LivenessStateHealthIndicator & ReadinessStateHealthIndicator

Slide 34

Slide 34 text

#K8SBeyondTheHype @touret_alex Overriding a Heath Indicator @Component public class MongoDBActuatorHealthIndicator implements HealthIndicator { [...] @Override public Health health() { // ping database } @Override public Health getHealth(boolean includeDetails) { if (!includeDetails) { return health(); } else { var statuses = mongoDBHealthService.findStatusForAllConfigurations(); return Health.status(checkStatus(statuses)).withDetails(statuses).build(); } } [...] }

Slide 35

Slide 35 text

#K8SBeyondTheHype @touret_alex Monitoring • Micrometer • Prometheus • Grafana

Slide 36

Slide 36 text

#K8SBeyondTheHype @touret_alex Micrometer • Help you exporting metrics (e.g., the TTR of an API using the @Timed annotation) • These metrics could be aggretated into Prometheus dependencies { implementation 'io.micrometer:micrometer-registry-prometheus’ }

Slide 37

Slide 37 text

#K8SBeyondTheHype @touret_alex management: endpoints: enabled-by-default: true web: exposure: include: '*' jmx: exposure: include: '*' endpoint: health: show-details: always enabled: true probes: enabled: true shutdown: enabled: true prometheus: enabled: true metrics: enabled: true health: livenessstate: enabled: true readinessstate: enabled: true datasource: enabled: true metrics: web: client: request: autotime: enabled: true Actuator configuration Metrics configuration

Slide 38

Slide 38 text

#K8SBeyondTheHype @touret_alex CI/CD

Slide 39

Slide 39 text

#K8SBeyondTheHype @touret_alex Two kind of pipelines • Platform provisioning • Application build & deploy

Slide 40

Slide 40 text

#K8SBeyondTheHype @touret_alex Platform provisionning terraform apply

Slide 41

Slide 41 text

#K8SBeyondTheHype @touret_alex Application build & deploy Build • Java application • Docker image • Helm chart Tests • Unit & integration tests • Smoke tests of the new docker image Deployment • Continuous deployment of your develop branch • Release deployment

Slide 42

Slide 42 text

#K8SBeyondTheHype @touret_alex Example Spring Boot, Tomcat, JDK Migration Tested locally → Dev → Staging ⇒ In one day

Slide 43

Slide 43 text

#K8SBeyondTheHype @touret_alex CONFIGURATION

Slide 44

Slide 44 text

#K8SBeyondTheHype @touret_alex How to configure your cloud native app? ▪ Environment variables ▪ Config Maps ▪ Secrets

Slide 45

Slide 45 text

#K8SBeyondTheHype @touret_alex Environment variables spec: containers: - env: - name: JAVA_OPTS value: >- -XX:+UseContainerSupport -XX:MaxRAMPercentage=70.0 -Dfile.encoding=UTF-8 - Djava.security.egd=file:/dev/./urandom

Slide 46

Slide 46 text

#K8SBeyondTheHype @touret_alex Config map apiVersion: v1 kind: ConfigMap metadata: creationTimestamp: 2021-03-11T18:38:34Z name: my-config-map [...] data: JAVA_OPTS: >- -XX:+UseContainerSupport -XX:MaxRAMPercentage=70.0 -Dfile.encoding=UTF-8 -Djava.security.egd=file:/dev/./urandom

Slide 47

Slide 47 text

#K8SBeyondTheHype @touret_alex What about configuration files? • We can specify them as variables in config maps • We can also externalize them

Slide 48

Slide 48 text

#K8SBeyondTheHype @touret_alex Files in Config map volumeMounts: - mountPath: /config name: configuration-volume readOnly: true [...] volumes: - configMap: defaultMode: 420 name: configuration name: configuration-volume apiVersion: v1 kind: ConfigMap [...] data: my.conf: {{- (.Files.Glob "conf/*").AsConfig | nindent 2 }}

Slide 49

Slide 49 text

#K8SBeyondTheHype @touret_alex Limitations Error: UPGRADE FAILED: ConfigMap "my-service" is invalid: data: Too long: must have at most 1048576 characters https://kubernetes.io/docs/concepts/configuration/configmap/ A ConfigMap is not designed to hold large chunks of data. The data stored in a ConfigMap cannot exceed 1 MiB. If you need to store settings that are larger than this limit, you may want to consider mounting a volume or use a separate database or file service.

Slide 50

Slide 50 text

#K8SBeyondTheHype @touret_alex How to externalise values? apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: labels: [...] spec: maxReplicas: {{ .Values.myapp.maxReplicaCount }} minReplicas: {{ .Values.myapp.minReplicaCount }} [...] targetCPUUtilizationPercentage: {{ .Values.myapp.replicationThreesold }}

Slide 51

Slide 51 text

#K8SBeyondTheHype @touret_alex Values.yml myapp: minReplicaCount: "2" maxReplicaCount: "6" replicationThreesold: 80

Slide 52

Slide 52 text

#K8SBeyondTheHype @touret_alex File templates apiVersion: v1 kind: ConfigMap metadata: name: configuration labels: [...] data: application.properties: |- {{ tpl (.Files.Get "conf/application.properties") . | nindent 4}}

Slide 53

Slide 53 text

#K8SBeyondTheHype @touret_alex Templates application.properties logging.level.org.hibernate.stat={{ .Values.configmap.application.org_hi bernate_stat }} logging.level.org.springframework.se curity={{ .Values.configmap.application.org_sp ringframework_security }} values.yml configmap: application: org_hibernate_stat: ERROR org_springframework_security: ERROR

Slide 54

Slide 54 text

#K8SBeyondTheHype @touret_alex Binary files apiVersion: v1 # Definition of secrets kind: Secret [...] type: Opaque # Inclusion of binary configuration files data: my_keystore.jks: {{ .Files.Get "secrets/my_keystore.jks" | b64enc }}

Slide 55

Slide 55 text

#K8SBeyondTheHype @touret_alex EFFICIENT LOGGING

Slide 56

Slide 56 text

#K8SBeyondTheHype @touret_alex Log management In the console: kubectl logs --tail Logs aggregator (e.g., ELK, Google Logs Explorer)

Slide 57

Slide 57 text

#K8SBeyondTheHype @touret_alex Docker containers output stream stdout & stderr It’s useless to setup a file output stream

Slide 58

Slide 58 text

#K8SBeyondTheHype @touret_alex Best pratices Indicate in your LOGS: • Container information (image name, container ID,…) • Kubernetes related information (POD @IP, POD ID, namespace,...) Log4j Docker Support – Log4j Kubernetes Support

Slide 59

Slide 59 text

#K8SBeyondTheHype @touret_alex To go further • You can identify the related informations of the APIs calls: Caller ID, Correlation ID, request data,… zalando/logbook: An extensible Java library for HTTP request and response logging

Slide 60

Slide 60 text

#K8SBeyondTheHype @touret_alex Distributed tracing You can identify and correlate your API calls on your microservices based architecture by implement Opentracing

Slide 61

Slide 61 text

#K8SBeyondTheHype @touret_alex Configuring Opentracing dependencies { implementation 'io.opentracing.contrib:opentracing-spring-jaeger- cloud-starter:3.3.1’ } application.properties opentracing: jaeger: udp-sender: host: localhost port: 6831 enabled: true

Slide 62

Slide 62 text

#K8SBeyondTheHype @touret_alex https://blog.worldline.tech/2021/09/22/enabling_distributed_tracing_in_spring_apps.html

Slide 63

Slide 63 text

#K8SBeyondTheHype @touret_alex On Google Cloud Platform You have by default: • A log explorer (Google Logs Explorer ~ ELK) • An error reporter(~ ELK specific dashboard) • A distributed trace analyser (~ Opentracing/Jaeger)

Slide 64

Slide 64 text

#K8SBeyondTheHype @touret_alex Logging Use Spring Cloud GCP starter dependencies { implementation("com.google.cloud:spring-cloud-gcp-starter- logging") } Configure using Logback as logging framework Set the GOOGLE_CLOUD_PROJECT & GOOGLE_APPLICATION_CREDENTIALS environment variables in your HELM charts

Slide 65

Slide 65 text

#K8SBeyondTheHype @touret_alex Logging

Slide 66

Slide 66 text

#K8SBeyondTheHype @touret_alex Error Reporter By default, it aggregates all your platform’s errors You can also add your business errors (HTTP 500) There are Logback appenders and JUL handlers provided by Google

Slide 67

Slide 67 text

#K8SBeyondTheHype @touret_alex Distributed tracing By using Spring Cloud GCP starter trace dependencies { compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-trace’ }

Slide 68

Slide 68 text

#K8SBeyondTheHype @touret_alex