Whether you are a part of an early-stage startup, a midsize company or an enterprise you hear people talking about microservices, containers or Kubernetes. Whether it’s a conversation about software, infrastructure or systems, they are mentioned. But, why?
Cloud has evolved as a strategy for disruption driven continuous delivery
Cloud elasticity enables microservices architectures to scale out quickly, but also roll new updates out at immense speeds
- Monolith applications have slow release cycles and are updated relatively infrequently. Developers would hand over the packaged application to the operations team who would then deploy and monitor it. Migration of the application to other hardware in case of failure was manual as well
- Microservices can be developed, deployed, updated and scaled individually
12-Factor is a software methodology for building scalable microservices applications (Originally created by Heroku)
- Best practices designed to enable applications to be built with portability, resilience, and scalability when deployed to the web
- Make it easier to run, scale, and deploy applications
- Keep parity between development and production
- Provide strict separation between build, release, and run stages
i. Codebase - One codebase tracked in revision control, many deploys
ii. Dependencies - Explicitly declare and isolate dependencies
iii. Config - Store config in the environment
iv. Backing services - Treat backing services as attached resources
v. Build, release, run - Strictly separate build and run stages
vi. Processes - Execute the app as one or more stateless processes
vii. Port binding - Export services via port binding
viii. Concurrency - Scale out via the process model
ix. Disposability - Maximize robustness with fast startup and graceful shutdown
x. Dev/prod parity - Keep development, staging, and production as similar as possible
xi. Logs - Treat logs as event streams
xii. Admin processes - Run admin/management tasks as one-off processes
- Build, release, run
- Parity between dev & prod
- Backing services
- Port binding
- Admin processes
- Small batch changes
- All changes should be incremental and finite. When failures occur, small-batch changes are typically easier to recover than large disruptive changes
- Source control all the things
- A history of all changes is helpful to understand what changes have been made and to identify the cause of regressions in the codebase or configuration
- Production-like environments
- Developers should have access to environments and tools that are representative of production. Production environments typically operate at larger scales than development or quality assurance (QA) and with more complex configuration. The variance can mean that features that work fine in early stages do not work correctly in production—which is the only place it matters
- Shift-left of operational practices
- We should expose behaviors for health management, log collection, change management, and so on earlier in the development process
- Continuous integration of changes
- All changes should be built and deployed together on an ongoing basis to identify when the intermingling of various changes leads to an unforeseen issue or application programming interface (API) incompatibility
- Highly automated testing with continuous feedback
- To manage velocity, you need to automate your testing and validation work so that you can always be testing (ABT)
Containers are the ideal unit of delivery because they encapsulate all aspects of your application, middleware, and operating system (OS) packages into a single package. You can create container images in several ways, and the most popular means of creating an image is by a Dockerfile.
The Dockerfile describes all of the relevant details necessary to package the image.
Compared to Virtual Machines, containers share the OS kernel instead of having a full copy of it – like making multiple VMs in a single host. Although it is possible to put your microservices in multiple VMs, containers are commonly used in this case since it takes up less space and is faster to boot up
Backing up the benefits of using containers for microservices, docker reported a 46% increase in the frequency of software releases by using Docker containers
According to a report by Gartner, by 2022 more than 75% of organizations globally will be running containerized applications
Kubernetes is a container orchestration platform that lets you deploy, scale, and manage all of your containers. It allows you to automate the deployment of your containerized microservices. This makes it easier to manage all of the components and microservices of your application
Kubernetes can run within the public cloud or on-premise environment. Cloud computing service providers like AWS, Microsoft Azure, and GCP provides managed solutions using Kubernetes that enable customers to start up K8s apps fast and operate efficiently
- How to scale?
- How to avoid port conflicts?
- How to manage them on multiple hosts?
- What happens if a host has trouble?
- How to keep them running?
- How to update them?
- Where are my containers?
Kubernetes is an open-source platform for automating deployments, scaling, and operations of application containers across clusters of hosts, providing container-centric infrastructure.
It addresses all the challenges we’ve described above, it’s very portable (can run on most cloud providers, bare-metal, hybrids or a combination of all of the above), it’s configurable, modular, and does features like auto-placement, auto-restart, auto-replication and auto-healing of containers extremely well. By far, the best thing about Kubernetes is the awesome community with its online and in-person meetups in every major city in the world
Master node: Runs multiple controllers that are responsible for the health of the cluster, replication, scheduling, endpoints (linking Services and Pods), Kubernetes API, interacting with the underlying cloud providers etc. Generally it makes sure everything is running as it should be and looks after worker nodes
Worker node (minion): Runs the Kubernetes agent that is responsible for running Pod containers via Docker or rkt, requests secrets or configurations, mounts required Pod volumes, does health checks and reports the status of Pods and the node to the rest of the system
Pod: The smallest and simplest unit in the Kubernetes object model that you can create or deploy. It represents a running process in the cluster. Can contain one or multiple containers
Deployment: Provides declarative updates for Pods (like the template for the Pods), for example, the Docker image(s) to use, environment variables, how many Pod replicas to run, labels, node selectors, volumes, etc
DaemonSet: It’s like a Deployment but instead runs a copy of a Pod (or multiple) on all (or some) nodes. Useful for things like log collection daemons (sumologic, fluentd), node monitoring daemons (datadog) and cluster storage daemons (glusterd)
ReplicaSet: Controller that ensures a specified number of Pod replicas (defined in the Deployment) is running at any given time
Service: An abstraction which defines a logical set of Pods and a policy by which to access them (determined by a label selector). Generally, it’s used to expose Pods to other services within the cluster (using targetPort) or externally (using NodePort or LoadBalancer objects)
ConfigMap: ConfigMaps allow you to decouple configuration artifacts from image content to keep containerized applications portable. It makes configurations easier to change and manage and prevents hardcoding configuration data to Pod specifications. ConfigMaps are useful for storing and sharing non-sensitive, unencrypted configuration information
Secret: Secrets are secure objects which store sensitive data, such as passwords, OAuth tokens, and SSH keys, in your clusters. Storing sensitive data in Secrets is more secure than plaintext ConfigMaps or in Pod specifications. Using Secrets gives you control over how sensitive data is used, and reduces the risk of exposing the data to unauthorized users
Volume: On-disk files in a Container are ephemeral, which presents some problems for non-trivial applications when running in Containers. First, when a Container crashes, kubelet will restart it, but the files will be lost - the Container starts with a clean state. Second, when running Containers together in a Pod it is often necessary to share files between those Containers. The Kubernetes Volume abstraction solves both of these problems