Twelve principles of success for cloud native apps

Twelve principles of success for cloud native apps

In the past, twelve principles have emerged as essential when developing applications that can be quickly scaled, reliably and consistently maintained and released.

These principles are under the term The Twelve Factor App published by Adam Wiggins. Especially when developing applications that are designed for cloud operation, you cannot avoid these principles.

We will introduce them to you below and go into two of them in more detail.

1. Code Base

The first principle states that there should be exactly one versioned code base per application. This code base must contain all artifacts necessary to build, install, operate and monitor the application.

This code base must be accessible to development, testers, the continuous integration/delivery server and IT operations.

If there is more than one code base, then it is not an application but a distributed system. Each component in a distributed system is an application that, taken individually, must comply with the 12 principles.

Multiple applications sharing the same code violate the 12 Factor Principles. The solution is to wrap shared code in libraries that can be included through the dependency manager.

Code Base and Actions

2. Dependencies

The second principle looks at the external dependencies and states that all external dependencies are defined explicitly and separately from the source code. Only the application code that is unique and needed for the purpose of the application and the declaration of dependencies should be managed in the code base. External artifacts such as Node.js packages or Java .jar files do not belong in the codebase.

Code base with books

Maven configuration with dependencies (abbreviated):

3. configuration

The third principle states that configuration must be separate from application code. You can either inject the application's configuration into the runtime environment as an environment variable or declare it in a separate configuration file.

The advantage of this separation is that the configuration can be easily adapted for the different development environments.

This should be illustrated here using a Spring Boot application.

Here is an example of a configuration where the database connection is hardwired in code:

jump:

    data source:

        url: jdbc:mysql://mydbserver:12324/12factors

        username: someuser

        password: highsecuritypassword

Although Spring Boot allows properties overriding, the following application.yaml would be more appropriate for a 12factor app.

spring: datasource: url: ${MYSQL_CONNECTION} username: ${MYSQL_USERNAME} password: ${MYSQL_PASSWORD}

A so-called config map can now be defined in Kubernetes, which contains the above data, such as the MySQL connection data, user name and password.

Kubernetes ConfigMap

apiVersion: v1

child : ConfigMap

metadata:

    name: mysqlconfig

date:

    connections: jdbc:mysql://mydbserver:12324/12factors

    username: someuser

    password: highsecuritypassword

The above example serves to illustrate the concept. In a real application, you would use Kubernetes Secrets for username and password for security reasons.

The last step is then the connection between the application and the ConfigMap. The environment variables are then set here based on the data in the ConfigMap.

apiVersion: v1 kind: Pod metadata: name: 12factorapp spec: containers: - name: 12factorapp image: 12factorapp:latest env: - name: MYSQL_CONNECTION valueFrom: configMapKeyRef: name: mysqlconfig key: connection - name: MYSQL_USERNAME valueFrom: configMapKeyRef: name: mysqlconfig key: username - name: MYSQL_PASSWORD valueFrom: configMapKeyRef: name: mysqlconfig key: password
Configuration

4. External applications (backing services)

The fourth principle defines that external components such as databases, message brokers and the like are treated as attached resources. This approach promotes flexibility and efficiency in the software development process.

5. Build, Release and Run

The fifth principle prescribes the separation of stages: Separate the stages of the installation process into a build, release and run phase.

  • In the build phase, the source code is compiled, built, and loaded into an archive such as Maven Central or Docker Hub.
  • In the release phase, the configuration files are applied.
  • In the run phase, the runtime environment is provisioned and the application is loaded there along with its dependencies.

6. Processes

The sixth principle states that the application should run as a set of multiple stateless processes. In concrete terms, this means that the application should not store any information about the session or process status.

This makes it easier to scale an application and avoids unwanted side effects that are difficult to replicate.

7. Port Binding

The seventh principle states that a service or application should be identifiable to the network by its port number and not by a domain name. The reason is that domain names and associated IP addresses can be assigned on-the-fly through manual manipulation and automated service discovery mechanisms. Therefore, using them as a reference point is unreliable. The principle of port binding is more reliable and easier to manage. In addition, potential problems due to a collision between the private port number assignment to the network and the public use of the same port number by another process can be avoided by port forwarding.

The main idea behind the port binding principle is that consistent use of a port number is the best way to expose a process to the network. For example, the following standards have emerged: port 80 for web servers running HTTP, port 443 for HTTPS, port 22 for SSH, port 3306 is the default port for MySQL and port 27017 for MongoDB.

8. Concurrency

The eighth principle of concurrency justifies that the processes should be organized according to their functional purpose in order to be scalable independently of each other. Concurrency support means that different components of an application can run more often, or less often than other components, independently of other components. Applications that do not support concurrency can only be scaled as a whole.

9. Availability

The ninth principle explains that applications should start and stop properly. In concrete terms, this means that all necessary preparatory work must be carried out before an application is made available to consumers. A proper start ensures, for example, that all access to resources is operational and all configuration work has been completed.

With regard to shutdown, the Principle recommends ensuring that all resource access is gracefully terminated and all shutdown activity is logged.

10. Development-production parity

In short, the tenth principle states that all environments (development, integration, production) should be configured as similarly as possible and that none of the environment-specific phases from point 5 should be skipped.

In concrete terms, this means on the one hand that the deployment pipelines should be almost identical for all environments and on the other hand that the external applications should be as similar as possible in all environments (same database, just different instances for different environments; same message queue, etc. ).

Thirdly, the principle also means that the application must independently run through the environment-specific release and run phases in each environment. The principle would have been violated if the deployed artifacts were copied from the development environment to the integration environment.

11. Logs

The eleventh principle advocates sending log data in a stream that can be accessed by a wide range of interested consumers. The process of forwarding log data must be separate from the processing of it.

For example, one consumer might only be interested in error data, while another consumer might be interested in request/response data. Another consumer might be interested in storing all log data for event archiving.

An added benefit is that even if an app goes down, the log data lives on long afterward.

12. Admin Processes

The final principle of the 12 Factor Apps advocates that the admin processes should be treated as first-class citizens of the software development lifecycle.

This means that the above-mentioned principles 1. code base, 5. and 10. also apply to these processes. They should not be viewed as separate, but as part of the software development life cycle.

The use of these best practices is essential in professional software development in order to program applications in a secure, scalable and maintainable manner.

This leads to greater efficiency in the development and operation of applications, which in turn leads to a better user experience and greater customer satisfaction.

In short, applying the 12 factors ensures better quality and reliability.

These twelve factors represent the foundation for best practices in the development of cloud native applications. Over time, other best practices have emerged, which we will present in a follow-up article.

– A joint contribution from our Karlsruhe location –