$30 off During Our Annual Pro Sale. View Details »

[DEVOXX UK 2021] Kubernetes & Co, beyond the hype: 10 tips for designers who want to create cloud native apps

[DEVOXX UK 2021] Kubernetes & Co, beyond the hype: 10 tips for designers who want to create cloud native apps

Kubernetes and cloud technologies are nowadays the new standard to deploy different cloud native applications: api, batchs, microservices and ... monoliths!
These technologies help to solve many issues but with some complexity.
It could be difficult for developers and designers to identify the constraints of such architectures.

In this presentation, you will (re)discover ten tips and pieces of advice I applied and found useful in my last JAVA projects (Spring, JEE).

I will talk about:

Application ecosystem
Choice of technical solutions
Development
K8S design constraints
And more!

Alexandre Touret

November 01, 2021
Tweet

More Decks by Alexandre Touret

Other Decks in Programming

Transcript

  1. #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

    View Slide

  2. #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

    View Slide

  3. #K8SBeyondTheHype @touret_alex
    SOME CONTEXT

    View Slide

  4. #K8SBeyondTheHype @touret_alex

    View Slide

  5. #K8SBeyondTheHype @touret_alex
    Used technologies

    View Slide

  6. #K8SBeyondTheHype @touret_alex
    DO YOU REALLY NEED IT?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. #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.

    View Slide

  11. #K8SBeyondTheHype @touret_alex
    DOCKER & CIE BASICS

    View Slide

  12. #K8SBeyondTheHype @touret_alex

    View Slide

  13. #K8SBeyondTheHype @touret_alex
    You will have to know

    View Slide

  14. #K8SBeyondTheHype @touret_alex

    View Slide

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

    View Slide

  16. #K8SBeyondTheHype @touret_alex
    ORGANISATION

    View Slide

  17. #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

    View Slide

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

    View Slide

  19. #K8SBeyondTheHype @touret_alex
    THE STATELESS APPS AND THE OTHERS…

    View Slide

  20. #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

    View Slide

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

    View Slide

  22. #K8SBeyondTheHype @touret_alex
    (FAST) APPLICATION STARTUP

    View Slide

  23. #K8SBeyondTheHype @touret_alex

    View Slide

  24. #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
    • …

    View Slide

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

    View Slide

  26. #K8SBeyondTheHype @touret_alex

    View Slide

  27. #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

    View Slide

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

    View Slide

  29. #K8SBeyondTheHype @touret_alex
    OBSERVABILITY

    View Slide

  30. #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

    View Slide

  31. #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

    View Slide

  32. #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

    View Slide

  33. #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

    View Slide

  34. #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();
    }
    }
    [...]
    }

    View Slide

  35. #K8SBeyondTheHype @touret_alex
    Monitoring
    • Micrometer
    • Prometheus
    • Grafana

    View Slide

  36. #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’
    }

    View Slide

  37. #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

    View Slide

  38. #K8SBeyondTheHype @touret_alex
    CI/CD

    View Slide

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

    View Slide

  40. #K8SBeyondTheHype @touret_alex
    Platform provisionning
    terraform apply

    View Slide

  41. #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

    View Slide

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

    View Slide

  43. #K8SBeyondTheHype @touret_alex
    CONFIGURATION

    View Slide

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

    View Slide

  45. #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

    View Slide

  46. #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

    View Slide

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

    View Slide

  48. #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 }}

    View Slide

  49. #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.

    View Slide

  50. #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 }}

    View Slide

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

    View Slide

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

    View Slide

  53. #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

    View Slide

  54. #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 }}

    View Slide

  55. #K8SBeyondTheHype @touret_alex
    EFFICIENT
    LOGGING

    View Slide

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

    View Slide

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

    View Slide

  58. #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

    View Slide

  59. #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

    View Slide

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

    View Slide

  61. #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

    View Slide

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

    View Slide

  63. #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)

    View Slide

  64. #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

    View Slide

  65. #K8SBeyondTheHype @touret_alex
    Logging

    View Slide

  66. #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

    View Slide

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

    View Slide

  68. #K8SBeyondTheHype @touret_alex

    View Slide