Figure 24-4. Application-scaling levels Application Tuning At the most granular level, there is an application tuning technique we didn’t cover in this chapter, as it is not a Kubernetes-related activity. However, the very first action you can take is to tune the application running in the container to best use allocated resources. This activity is not performed every time a service is scaled, but it must be performed initially before hitting production. For example, for Java runtimes, that is right-sizing thread pools for best use of the available CPU shares the container is get‐ ting. Then tuning the different memory regions such as heap, nonheap, and thread stack sizes. Adjusting these values is typically performed through configuration changes rather than code changes. Container-native applications use start scripts that can calculate good default values for thread counts, and memory sizes for the application based on the allocated con‐ tainer resources rather than the shared full node capacity. Using such scripts is an excellent first step. You can also go one step further and use techniques and libraries such as Netflix’s Adaptive Concurrency Limits library, where the application can dynamically calculate its concurrency limits by self-profiling and adapting. This is a kind of in-app autoscaling that removes the need for manually tuning services. Tuning applications can cause regressions similar to a code change and must be fol‐ lowed by a degree of testing. For example, changing the heap size of an application can cause it to be killed with an  OutOfMemory  error and horizontal scaling won’t be able to help. On the other hand, scaling Pods vertically or horizontally, or provision‐ Solution  |  217